summaryrefslogtreecommitdiff
path: root/public/SomhairlesDream.Shared/Artifact.js
diff options
context:
space:
mode:
authorSomhairle H. Marisol <[email protected]>2026-09-21 00:58:15 +0800
committerSomhairle H. Marisol <[email protected]>2026-09-21 00:58:15 +0800
commit7b29dbff7e43e87741476f47df17ad1582b16546 (patch)
tree4c9d0832b4e4edd77e59e9cb0a88abf5e729a9f9 /public/SomhairlesDream.Shared/Artifact.js
parent47add3088d50f12c5f48591f26c53cdb59d8283b (diff)
downloadsomhairles-dream-fsharp-7b29dbff7e43e87741476f47df17ad1582b16546.tar.gz
feat(pipeline): add runnable artifact pipeline increment
[Change Nature] - This commit adds the first runnable increment of the F# artifact pipeline: CLI, server, frontend bundle, stage fixture, and tests. It is feature work, not a bug fix. [New Capability] - CLI `run` executes the three-step Blender fixture through the process bridge and writes GLBs, step logs, events, and a manifest; `verify` validates a manifest without Blender. - Server exposes run start, run-scoped SSE, artifact manifest/file routes, and serves the Fable frontend from its build output. - Frontend starts runs, follows run-scoped SSE progress, and links the artifact manifest on completion. [Implementation] - Shared layer holds domain, run state, replay, and artifact contracts; Modeling holds the pipeline, 120s bridge timeout, and result-file protocol; stage/blender/fixture.py owns geometry in Python (bpy owns geometry, F# owns orchestration). - Fable bundle is regenerated into public/ and copied by the server project; solution includes all projects with warnings-as-errors. - README documents build, Fable, server, interpreter/env, fixture limitations, and current verification status. [Impact] - Verified: 40/40 solution tests pass; Release build 0 warnings/errors; real CLI run with bpy 5.0.1 produced 3 GLBs, 3 logs, and a verify-clean manifest; isolated-port server smoke flow succeeded. - Not yet verified: browser end-to-end behavior and production fidelity. Artifacts and caches are gitignored.
Diffstat (limited to 'public/SomhairlesDream.Shared/Artifact.js')
-rw-r--r--public/SomhairlesDream.Shared/Artifact.js277
1 files changed, 277 insertions, 0 deletions
diff --git a/public/SomhairlesDream.Shared/Artifact.js b/public/SomhairlesDream.Shared/Artifact.js
new file mode 100644
index 0000000..389d39c
--- /dev/null
+++ b/public/SomhairlesDream.Shared/Artifact.js
@@ -0,0 +1,277 @@
+import { Union, Record } from "../fable_modules/fable-library-js.4.24.0/Types.js";
+import { bool_type, array_type, int64_type, union_type, option_type, int32_type, class_type, record_type, string_type } from "../fable_modules/fable-library-js.4.24.0/Reflection.js";
+import { isMatch, create } from "../fable_modules/fable-library-js.4.24.0/RegExp.js";
+import { FSharpResult$2 } from "../fable_modules/fable-library-js.4.24.0/Result.js";
+
+export class RunIds extends Record {
+ constructor(ProjectId, RunId, BaseId, TargetId) {
+ super();
+ this.ProjectId = ProjectId;
+ this.RunId = RunId;
+ this.BaseId = BaseId;
+ this.TargetId = TargetId;
+ }
+}
+
+export function RunIds_$reflection() {
+ return record_type("SomhairlesDream.Shared.RunIds", [], RunIds, () => [["ProjectId", string_type], ["RunId", string_type], ["BaseId", string_type], ["TargetId", string_type]]);
+}
+
+const Ids_resourcePattern = create("^[a-z0-9](?:[a-z0-9-]{0,62}[a-z0-9])?$", 512);
+
+const Ids_objectPattern = create("^[a-z0-9]+(?:[._-][a-z0-9]+)*$", 512);
+
+export function Ids_validateResourceId(value) {
+ if ((value == null) ? true : !isMatch(Ids_resourcePattern, value)) {
+ return new FSharpResult$2(1, ["resource id must contain only lower-case letters, digits, and hyphens"]);
+ }
+ else {
+ return new FSharpResult$2(0, [undefined]);
+ }
+}
+
+export function Ids_validateObjectId(value) {
+ if ((value == null) ? true : !isMatch(Ids_objectPattern, value)) {
+ return new FSharpResult$2(1, ["object id must use the dotted lower-case namespace"]);
+ }
+ else {
+ return new FSharpResult$2(0, [undefined]);
+ }
+}
+
+export class RunStart extends Record {
+ constructor(Ids, At, TotalSteps) {
+ super();
+ this.Ids = Ids;
+ this.At = At;
+ this.TotalSteps = (TotalSteps | 0);
+ }
+}
+
+export function RunStart_$reflection() {
+ return record_type("SomhairlesDream.Shared.RunStart", [], RunStart, () => [["Ids", RunIds_$reflection()], ["At", class_type("System.DateTimeOffset")], ["TotalSteps", int32_type]]);
+}
+
+export class RunHeartbeat extends Record {
+ constructor(Ids, At, Message) {
+ super();
+ this.Ids = Ids;
+ this.At = At;
+ this.Message = Message;
+ }
+}
+
+export function RunHeartbeat_$reflection() {
+ return record_type("SomhairlesDream.Shared.RunHeartbeat", [], RunHeartbeat, () => [["Ids", RunIds_$reflection()], ["At", class_type("System.DateTimeOffset")], ["Message", string_type]]);
+}
+
+export class RunCheckpoint extends Record {
+ constructor(Ids, At, StepId, StepIndex, TotalSteps, Message, ArtifactPath) {
+ super();
+ this.Ids = Ids;
+ this.At = At;
+ this.StepId = StepId;
+ this.StepIndex = (StepIndex | 0);
+ this.TotalSteps = (TotalSteps | 0);
+ this.Message = Message;
+ this.ArtifactPath = ArtifactPath;
+ }
+}
+
+export function RunCheckpoint_$reflection() {
+ return record_type("SomhairlesDream.Shared.RunCheckpoint", [], RunCheckpoint, () => [["Ids", RunIds_$reflection()], ["At", class_type("System.DateTimeOffset")], ["StepId", string_type], ["StepIndex", int32_type], ["TotalSteps", int32_type], ["Message", string_type], ["ArtifactPath", string_type]]);
+}
+
+export class RunComplete extends Record {
+ constructor(Ids, At, ManifestPath) {
+ super();
+ this.Ids = Ids;
+ this.At = At;
+ this.ManifestPath = ManifestPath;
+ }
+}
+
+export function RunComplete_$reflection() {
+ return record_type("SomhairlesDream.Shared.RunComplete", [], RunComplete, () => [["Ids", RunIds_$reflection()], ["At", class_type("System.DateTimeOffset")], ["ManifestPath", string_type]]);
+}
+
+export class RunFail extends Record {
+ constructor(Ids, At, StepId, Message) {
+ super();
+ this.Ids = Ids;
+ this.At = At;
+ this.StepId = StepId;
+ this.Message = Message;
+ }
+}
+
+export function RunFail_$reflection() {
+ return record_type("SomhairlesDream.Shared.RunFail", [], RunFail, () => [["Ids", RunIds_$reflection()], ["At", class_type("System.DateTimeOffset")], ["StepId", option_type(string_type)], ["Message", string_type]]);
+}
+
+export class RunEvent extends Union {
+ constructor(tag, fields) {
+ super();
+ this.tag = tag;
+ this.fields = fields;
+ }
+ cases() {
+ return ["Start", "Heartbeat", "Checkpoint", "Complete", "Fail"];
+ }
+}
+
+export function RunEvent_$reflection() {
+ return union_type("SomhairlesDream.Shared.RunEvent", [], RunEvent, () => [[["Item", RunStart_$reflection()]], [["Item", RunHeartbeat_$reflection()]], [["Item", RunCheckpoint_$reflection()]], [["Item", RunComplete_$reflection()]], [["Item", RunFail_$reflection()]]]);
+}
+
+export class RunEventWire extends Record {
+ constructor(Kind, At, ProjectId, RunId, BaseId, TargetId, TotalSteps, StepId, StepIndex, Message, ArtifactPath, ManifestPath) {
+ super();
+ this.Kind = Kind;
+ this.At = At;
+ this.ProjectId = ProjectId;
+ this.RunId = RunId;
+ this.BaseId = BaseId;
+ this.TargetId = TargetId;
+ this.TotalSteps = TotalSteps;
+ this.StepId = StepId;
+ this.StepIndex = StepIndex;
+ this.Message = Message;
+ this.ArtifactPath = ArtifactPath;
+ this.ManifestPath = ManifestPath;
+ }
+}
+
+export function RunEventWire_$reflection() {
+ return record_type("SomhairlesDream.Shared.RunEventWire", [], RunEventWire, () => [["Kind", string_type], ["At", class_type("System.DateTimeOffset")], ["ProjectId", string_type], ["RunId", string_type], ["BaseId", string_type], ["TargetId", string_type], ["TotalSteps", option_type(int32_type)], ["StepId", option_type(string_type)], ["StepIndex", option_type(int32_type)], ["Message", option_type(string_type)], ["ArtifactPath", option_type(string_type)], ["ManifestPath", option_type(string_type)]]);
+}
+
+function RunEventModule_ids(value) {
+ return [value.ProjectId, value.RunId, value.BaseId, value.TargetId];
+}
+
+export function RunEventModule_toWire(event) {
+ let patternInput;
+ switch (event.tag) {
+ case 1: {
+ const value_1 = event.fields[0];
+ patternInput = ["heartbeat", value_1.Ids, value_1.At, undefined, undefined, undefined, value_1.Message, undefined, undefined];
+ break;
+ }
+ case 2: {
+ const value_2 = event.fields[0];
+ patternInput = ["checkpoint", value_2.Ids, value_2.At, value_2.TotalSteps, value_2.StepId, value_2.StepIndex, value_2.Message, value_2.ArtifactPath, undefined];
+ break;
+ }
+ case 3: {
+ const value_3 = event.fields[0];
+ patternInput = ["complete", value_3.Ids, value_3.At, undefined, undefined, undefined, undefined, undefined, value_3.ManifestPath];
+ break;
+ }
+ case 4: {
+ const value_4 = event.fields[0];
+ patternInput = ["fail", value_4.Ids, value_4.At, undefined, value_4.StepId, undefined, value_4.Message, undefined, undefined];
+ break;
+ }
+ default: {
+ const value = event.fields[0];
+ patternInput = ["start", value.Ids, value.At, value.TotalSteps, undefined, undefined, undefined, undefined, undefined];
+ }
+ }
+ const patternInput_1 = RunEventModule_ids(patternInput[1]);
+ return new RunEventWire(patternInput[0], patternInput[2], patternInput_1[0], patternInput_1[1], patternInput_1[2], patternInput_1[3], patternInput[3], patternInput[4], patternInput[5], patternInput[6], patternInput[7], patternInput[8]);
+}
+
+export class ArtifactChange extends Record {
+ constructor(ObjectId, Kind, Summary) {
+ super();
+ this.ObjectId = ObjectId;
+ this.Kind = Kind;
+ this.Summary = Summary;
+ }
+}
+
+export function ArtifactChange_$reflection() {
+ return record_type("SomhairlesDream.Shared.ArtifactChange", [], ArtifactChange, () => [["ObjectId", string_type], ["Kind", string_type], ["Summary", string_type]]);
+}
+
+export class RenderArtifact extends Record {
+ constructor(ArtifactPath, Sha256, Bytes, ExportedAt) {
+ super();
+ this.ArtifactPath = ArtifactPath;
+ this.Sha256 = Sha256;
+ this.Bytes = Bytes;
+ this.ExportedAt = ExportedAt;
+ }
+}
+
+export function RenderArtifact_$reflection() {
+ return record_type("SomhairlesDream.Shared.RenderArtifact", [], RenderArtifact, () => [["ArtifactPath", string_type], ["Sha256", string_type], ["Bytes", int64_type], ["ExportedAt", class_type("System.DateTimeOffset")]]);
+}
+
+export class StepArtifact extends Record {
+ constructor(StepId, StepIndex, ArtifactPath, LogPath, Sha256, Bytes, ExportedAt, ObjectIds, VerifiedObjectIds, Changes, Render) {
+ super();
+ this.StepId = StepId;
+ this.StepIndex = (StepIndex | 0);
+ this.ArtifactPath = ArtifactPath;
+ this.LogPath = LogPath;
+ this.Sha256 = Sha256;
+ this.Bytes = Bytes;
+ this.ExportedAt = ExportedAt;
+ this.ObjectIds = ObjectIds;
+ this.VerifiedObjectIds = VerifiedObjectIds;
+ this.Changes = Changes;
+ this.Render = Render;
+ }
+}
+
+export function StepArtifact_$reflection() {
+ return record_type("SomhairlesDream.Shared.StepArtifact", [], StepArtifact, () => [["StepId", string_type], ["StepIndex", int32_type], ["ArtifactPath", string_type], ["LogPath", string_type], ["Sha256", string_type], ["Bytes", int64_type], ["ExportedAt", class_type("System.DateTimeOffset")], ["ObjectIds", array_type(string_type)], ["VerifiedObjectIds", array_type(string_type)], ["Changes", array_type(ArtifactChange_$reflection())], ["Render", option_type(RenderArtifact_$reflection())]]);
+}
+
+export class ArtifactManifest extends Record {
+ constructor(SchemaVersion, ProjectId, RunId, BaseId, TargetId, Status, StartedAt, CompletedAt, BlenderVersion, Steps) {
+ super();
+ this.SchemaVersion = (SchemaVersion | 0);
+ this.ProjectId = ProjectId;
+ this.RunId = RunId;
+ this.BaseId = BaseId;
+ this.TargetId = TargetId;
+ this.Status = Status;
+ this.StartedAt = StartedAt;
+ this.CompletedAt = CompletedAt;
+ this.BlenderVersion = BlenderVersion;
+ this.Steps = Steps;
+ }
+}
+
+export function ArtifactManifest_$reflection() {
+ return record_type("SomhairlesDream.Shared.ArtifactManifest", [], ArtifactManifest, () => [["SchemaVersion", int32_type], ["ProjectId", string_type], ["RunId", string_type], ["BaseId", string_type], ["TargetId", string_type], ["Status", string_type], ["StartedAt", class_type("System.DateTimeOffset")], ["CompletedAt", class_type("System.DateTimeOffset")], ["BlenderVersion", string_type], ["Steps", array_type(StepArtifact_$reflection())]]);
+}
+
+export class RunSnapshot extends Record {
+ constructor(ProjectId, RunId, BaseId, TargetId, Status, CurrentStepId, CurrentStepIndex, TotalSteps, LastHeartbeatAt, UpdatedAt, Message, ManifestPath, ManifestUrl, Error$, IsStale) {
+ super();
+ this.ProjectId = ProjectId;
+ this.RunId = RunId;
+ this.BaseId = BaseId;
+ this.TargetId = TargetId;
+ this.Status = Status;
+ this.CurrentStepId = CurrentStepId;
+ this.CurrentStepIndex = (CurrentStepIndex | 0);
+ this.TotalSteps = (TotalSteps | 0);
+ this.LastHeartbeatAt = LastHeartbeatAt;
+ this.UpdatedAt = UpdatedAt;
+ this.Message = Message;
+ this.ManifestPath = ManifestPath;
+ this.ManifestUrl = ManifestUrl;
+ this.Error = Error$;
+ this.IsStale = IsStale;
+ }
+}
+
+export function RunSnapshot_$reflection() {
+ return record_type("SomhairlesDream.Shared.RunSnapshot", [], RunSnapshot, () => [["ProjectId", string_type], ["RunId", string_type], ["BaseId", string_type], ["TargetId", string_type], ["Status", string_type], ["CurrentStepId", option_type(string_type)], ["CurrentStepIndex", int32_type], ["TotalSteps", int32_type], ["LastHeartbeatAt", option_type(class_type("System.DateTimeOffset"))], ["UpdatedAt", class_type("System.DateTimeOffset")], ["Message", string_type], ["ManifestPath", option_type(string_type)], ["ManifestUrl", option_type(string_type)], ["Error", option_type(string_type)], ["IsStale", bool_type]]);
+}
+