@bitmagic/world-forger 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (105) hide show
  1. package/dist/artifact-types.d.ts +295 -0
  2. package/dist/artifact-types.d.ts.map +1 -0
  3. package/dist/artifact-types.js +17 -0
  4. package/dist/artifact-types.js.map +1 -0
  5. package/dist/forge-constants.d.ts +106 -0
  6. package/dist/forge-constants.d.ts.map +1 -0
  7. package/dist/forge-constants.js +111 -0
  8. package/dist/forge-constants.js.map +1 -0
  9. package/dist/index.d.ts +28 -0
  10. package/dist/index.d.ts.map +1 -0
  11. package/dist/index.js +28 -0
  12. package/dist/index.js.map +1 -0
  13. package/dist/log.d.ts +26 -0
  14. package/dist/log.d.ts.map +1 -0
  15. package/dist/log.js +14 -0
  16. package/dist/log.js.map +1 -0
  17. package/dist/pipeline/archetype-skips.d.ts +57 -0
  18. package/dist/pipeline/archetype-skips.d.ts.map +1 -0
  19. package/dist/pipeline/archetype-skips.js +51 -0
  20. package/dist/pipeline/archetype-skips.js.map +1 -0
  21. package/dist/pipeline/artifact-store.d.ts +4 -0
  22. package/dist/pipeline/artifact-store.d.ts.map +1 -0
  23. package/dist/pipeline/artifact-store.js +10 -0
  24. package/dist/pipeline/artifact-store.js.map +1 -0
  25. package/dist/pipeline/asset-catalog.d.ts +27 -0
  26. package/dist/pipeline/asset-catalog.d.ts.map +1 -0
  27. package/dist/pipeline/asset-catalog.js +47 -0
  28. package/dist/pipeline/asset-catalog.js.map +1 -0
  29. package/dist/pipeline/create-level-archetypes.d.ts +45 -0
  30. package/dist/pipeline/create-level-archetypes.d.ts.map +1 -0
  31. package/dist/pipeline/create-level-archetypes.js +197 -0
  32. package/dist/pipeline/create-level-archetypes.js.map +1 -0
  33. package/dist/pipeline/deps.d.ts +72 -0
  34. package/dist/pipeline/deps.d.ts.map +1 -0
  35. package/dist/pipeline/deps.js +2 -0
  36. package/dist/pipeline/deps.js.map +1 -0
  37. package/dist/pipeline/errors.d.ts +10 -0
  38. package/dist/pipeline/errors.d.ts.map +1 -0
  39. package/dist/pipeline/errors.js +13 -0
  40. package/dist/pipeline/errors.js.map +1 -0
  41. package/dist/pipeline/forged-work-store.d.ts +46 -0
  42. package/dist/pipeline/forged-work-store.d.ts.map +1 -0
  43. package/dist/pipeline/forged-work-store.js +69 -0
  44. package/dist/pipeline/forged-work-store.js.map +1 -0
  45. package/dist/pipeline/index.d.ts +36 -0
  46. package/dist/pipeline/index.d.ts.map +1 -0
  47. package/dist/pipeline/index.js +36 -0
  48. package/dist/pipeline/index.js.map +1 -0
  49. package/dist/pipeline/job-types.d.ts +145 -0
  50. package/dist/pipeline/job-types.d.ts.map +1 -0
  51. package/dist/pipeline/job-types.js +2 -0
  52. package/dist/pipeline/job-types.js.map +1 -0
  53. package/dist/pipeline/level-registry.d.ts +57 -0
  54. package/dist/pipeline/level-registry.d.ts.map +1 -0
  55. package/dist/pipeline/level-registry.js +108 -0
  56. package/dist/pipeline/level-registry.js.map +1 -0
  57. package/dist/pipeline/model-types.d.ts +51 -0
  58. package/dist/pipeline/model-types.d.ts.map +1 -0
  59. package/dist/pipeline/model-types.js +2 -0
  60. package/dist/pipeline/model-types.js.map +1 -0
  61. package/dist/pipeline/persist-level.d.ts +82 -0
  62. package/dist/pipeline/persist-level.d.ts.map +1 -0
  63. package/dist/pipeline/persist-level.js +201 -0
  64. package/dist/pipeline/persist-level.js.map +1 -0
  65. package/dist/pipeline/place-and-persist-level.d.ts +83 -0
  66. package/dist/pipeline/place-and-persist-level.d.ts.map +1 -0
  67. package/dist/pipeline/place-and-persist-level.js +405 -0
  68. package/dist/pipeline/place-and-persist-level.js.map +1 -0
  69. package/dist/pipeline/request-id.d.ts +7 -0
  70. package/dist/pipeline/request-id.d.ts.map +1 -0
  71. package/dist/pipeline/request-id.js +10 -0
  72. package/dist/pipeline/request-id.js.map +1 -0
  73. package/dist/pipeline/spawn-points.d.ts +47 -0
  74. package/dist/pipeline/spawn-points.d.ts.map +1 -0
  75. package/dist/pipeline/spawn-points.js +100 -0
  76. package/dist/pipeline/spawn-points.js.map +1 -0
  77. package/dist/pipeline/starter-scatter.d.ts +34 -0
  78. package/dist/pipeline/starter-scatter.d.ts.map +1 -0
  79. package/dist/pipeline/starter-scatter.js +49 -0
  80. package/dist/pipeline/starter-scatter.js.map +1 -0
  81. package/dist/pipeline/storage-types.d.ts +48 -0
  82. package/dist/pipeline/storage-types.d.ts.map +1 -0
  83. package/dist/pipeline/storage-types.js +19 -0
  84. package/dist/pipeline/storage-types.js.map +1 -0
  85. package/dist/pipeline/transport-types.d.ts +35 -0
  86. package/dist/pipeline/transport-types.d.ts.map +1 -0
  87. package/dist/pipeline/transport-types.js +2 -0
  88. package/dist/pipeline/transport-types.js.map +1 -0
  89. package/dist/pipeline/voxelize-level.d.ts +24 -0
  90. package/dist/pipeline/voxelize-level.d.ts.map +1 -0
  91. package/dist/pipeline/voxelize-level.js +63 -0
  92. package/dist/pipeline/voxelize-level.js.map +1 -0
  93. package/dist/pipeline/world-json-source.d.ts +78 -0
  94. package/dist/pipeline/world-json-source.d.ts.map +1 -0
  95. package/dist/pipeline/world-json-source.js +2 -0
  96. package/dist/pipeline/world-json-source.js.map +1 -0
  97. package/dist/pipeline/world-json-types.d.ts +104 -0
  98. package/dist/pipeline/world-json-types.d.ts.map +1 -0
  99. package/dist/pipeline/world-json-types.js +15 -0
  100. package/dist/pipeline/world-json-types.js.map +1 -0
  101. package/dist/terrain-height-grid.d.ts +45 -0
  102. package/dist/terrain-height-grid.d.ts.map +1 -0
  103. package/dist/terrain-height-grid.js +50 -0
  104. package/dist/terrain-height-grid.js.map +1 -0
  105. package/package.json +72 -0
package/dist/log.js ADDED
@@ -0,0 +1,14 @@
1
+ function emit(name, level) {
2
+ return (context, message) => {
3
+ console[level](`[${name}] ${message}`, context);
4
+ };
5
+ }
6
+ export function createLogger(name) {
7
+ return {
8
+ info: emit(name, 'info'),
9
+ warn: emit(name, 'warn'),
10
+ error: emit(name, 'error'),
11
+ debug: emit(name, 'debug'),
12
+ };
13
+ }
14
+ //# sourceMappingURL=log.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"log.js","sourceRoot":"","sources":["../src/log.ts"],"names":[],"mappings":"AAyBA,SAAS,IAAI,CAAC,IAAY,EAAE,KAA0C;IACpE,OAAO,CAAC,OAAgC,EAAE,OAAe,EAAQ,EAAE;QACjE,OAAO,CAAC,KAAK,CAAC,CAAC,IAAI,IAAI,KAAK,OAAO,EAAE,EAAE,OAAO,CAAC,CAAC;IAClD,CAAC,CAAC;AACJ,CAAC;AAED,MAAM,UAAU,YAAY,CAAC,IAAY;IACvC,OAAO;QACL,IAAI,EAAE,IAAI,CAAC,IAAI,EAAE,MAAM,CAAC;QACxB,IAAI,EAAE,IAAI,CAAC,IAAI,EAAE,MAAM,CAAC;QACxB,KAAK,EAAE,IAAI,CAAC,IAAI,EAAE,OAAO,CAAC;QAC1B,KAAK,EAAE,IAAI,CAAC,IAAI,EAAE,OAAO,CAAC;KAC3B,CAAC;AACJ,CAAC"}
@@ -0,0 +1,57 @@
1
+ import type { ForgeArchetypeRef } from './job-types.js';
2
+ /**
3
+ * Recorded in `archetype-progress` / `archetype-assets` IN PLACE of the asset record
4
+ * of an archetype that could not be baked. It is a placeholder, not an asset: keeping
5
+ * the array INDEX-ALIGNED with `forge.archetypes` is what makes the resume cache (keyed
6
+ * by that index) and the summary's archetype↔asset pairing stay correct while the module
7
+ * itself disappears from the world. Every consumer must route it through
8
+ * {@link isSkippedArchetype} — it must never be written to world.json as an asset.
9
+ *
10
+ * A type alias rather than an interface so it is assignable to the `Record<string, unknown>`
11
+ * the artifacts are typed as.
12
+ */
13
+ export type SkippedArchetype = {
14
+ skippedArchetype: true;
15
+ name: string;
16
+ /** Why the bake gave up — the browser's error text, or what the artifact lacked. */
17
+ reason: string;
18
+ /** Instances the level loses with it (all of them). */
19
+ instanceCount: number;
20
+ /**
21
+ * A used kit module (exact floor/wall/surround): its skip leaves bare recessed
22
+ * shell where carved stone belonged, so the summary note names that cost and a
23
+ * resume retries the bake. Absent on ordinary prop/landmark skips.
24
+ */
25
+ structural?: true;
26
+ };
27
+ export declare function isSkippedArchetype(entry: Record<string, unknown> | null | undefined): entry is SkippedArchetype;
28
+ /**
29
+ * The forge-advisory line a skipped archetype contributes to the summary.
30
+ */
31
+ export declare function skipNote(skipped: SkippedArchetype): string;
32
+ /**
33
+ * The structural-module variant: the cost is real and visible — the shell behind
34
+ * an exact kit module is intentionally recessed/flattened, so the note must say
35
+ * what now shows there rather than pretend the drop was cosmetic.
36
+ */
37
+ export declare function structuralSkipNote(skipped: SkippedArchetype): string;
38
+ /**
39
+ * The forge-advisory line a module that came back OVER its LOD0 leaf budget contributes.
40
+ * It was KEPT at the kit's pinned voxel size rather than re-baked coarser (see the attempt
41
+ * loop in `createLevelArchetypes`), so the cost is real and worth reporting: the
42
+ * alternative was a wall with no relief at all.
43
+ */
44
+ export declare function moduleOverBudgetNote(name: string, voxelCount: number, budget: number): string;
45
+ /**
46
+ * Deliberately the SAME predicate `forge-world.ts` pins the bake resolution with, imported
47
+ * rather than restated. The two decisions must agree: if this file stopped classifying a
48
+ * real kit piece as a module while forge-world kept pinning it, the piece would be pinned to
49
+ * the finest voxel size AND become eligible for the coarsen-rebake loop again — silently
50
+ * reinstating the exact defect that exemption exists to prevent, with every test still green.
51
+ * (`forge-world.ts`'s other predicate, the prefix-only one, is NOT interchangeable with this:
52
+ * it guards the world-GLB omission and must fail safe by over-matching, the opposite way.)
53
+ */
54
+ export declare const isCladdingModule: (archetype: ForgeArchetypeRef) => boolean;
55
+ /** A used kit module is structural: the exact floor/wall/surround has no visual fallback. */
56
+ export declare const isUsedStructuralModule: (archetype: ForgeArchetypeRef) => boolean;
57
+ //# sourceMappingURL=archetype-skips.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"archetype-skips.d.ts","sourceRoot":"","sources":["../../src/pipeline/archetype-skips.ts"],"names":[],"mappings":"AAQA,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,gBAAgB,CAAC;AAExD;;;;;;;;;;GAUG;AACH,MAAM,MAAM,gBAAgB,GAAG;IAC7B,gBAAgB,EAAE,IAAI,CAAC;IACvB,IAAI,EAAE,MAAM,CAAC;IACb,oFAAoF;IACpF,MAAM,EAAE,MAAM,CAAC;IACf,uDAAuD;IACvD,aAAa,EAAE,MAAM,CAAC;IACtB;;;;OAIG;IACH,UAAU,CAAC,EAAE,IAAI,CAAC;CACnB,CAAC;AAEF,wBAAgB,kBAAkB,CAAC,KAAK,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,IAAI,GAAG,SAAS,GAAG,KAAK,IAAI,gBAAgB,CAE/G;AAED;;GAEG;AACH,wBAAgB,QAAQ,CAAC,OAAO,EAAE,gBAAgB,GAAG,MAAM,CAG1D;AAED;;;;GAIG;AACH,wBAAgB,kBAAkB,CAAC,OAAO,EAAE,gBAAgB,GAAG,MAAM,CAGpE;AAED;;;;;GAKG;AACH,wBAAgB,oBAAoB,CAAC,IAAI,EAAE,MAAM,EAAE,UAAU,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,GAAG,MAAM,CAI7F;AAED;;;;;;;;GAQG;AACH,eAAO,MAAM,gBAAgB,GAAI,WAAW,iBAAiB,KAAG,OAA0C,CAAC;AAE3G,6FAA6F;AAC7F,eAAO,MAAM,sBAAsB,GAAI,WAAW,iBAAiB,KAAG,OACV,CAAC"}
@@ -0,0 +1,51 @@
1
+ /**
2
+ * A legacy archetype that was SKIPPED, and the advisory lines it contributes.
3
+ *
4
+ * Shared by step 3 (which records the skips) and step 5 (which drops their
5
+ * placements and reports them in the summary), so the placeholder shape and
6
+ * the notes that describe its cost have exactly one definition.
7
+ */
8
+ import { isKitModuleArchetype } from '../forge-constants.js';
9
+ export function isSkippedArchetype(entry) {
10
+ return !!entry && entry.skippedArchetype === true;
11
+ }
12
+ /**
13
+ * The forge-advisory line a skipped archetype contributes to the summary.
14
+ */
15
+ export function skipNote(skipped) {
16
+ const n = skipped.instanceCount;
17
+ return `Archetype "${skipped.name}" was dropped (${skipped.reason}), along with its ${n} instance${n === 1 ? '' : 's'}; the level keeps everything else.`;
18
+ }
19
+ /**
20
+ * The structural-module variant: the cost is real and visible — the shell behind
21
+ * an exact kit module is intentionally recessed/flattened, so the note must say
22
+ * what now shows there rather than pretend the drop was cosmetic.
23
+ */
24
+ export function structuralSkipNote(skipped) {
25
+ const n = skipped.instanceCount;
26
+ return `Structural cladding module "${skipped.name}" failed to bake (${skipped.reason}) — its ${n} placement${n === 1 ? '' : 's'} were dropped, leaving bare recessed shell where its carved stone belongs; the level keeps everything else.`;
27
+ }
28
+ /**
29
+ * The forge-advisory line a module that came back OVER its LOD0 leaf budget contributes.
30
+ * It was KEPT at the kit's pinned voxel size rather than re-baked coarser (see the attempt
31
+ * loop in `createLevelArchetypes`), so the cost is real and worth reporting: the
32
+ * alternative was a wall with no relief at all.
33
+ */
34
+ export function moduleOverBudgetNote(name, voxelCount, budget) {
35
+ return `Cladding module "${name}" baked to ${voxelCount} LOD0 leaves, over the ${budget} budget for its role — `
36
+ + 'kept at the kit\'s pinned voxel size instead of re-baked coarser, because coarsening deletes the stone relief '
37
+ + 'the module exists to carry. Its instances are heavier than a standard library asset.';
38
+ }
39
+ /**
40
+ * Deliberately the SAME predicate `forge-world.ts` pins the bake resolution with, imported
41
+ * rather than restated. The two decisions must agree: if this file stopped classifying a
42
+ * real kit piece as a module while forge-world kept pinning it, the piece would be pinned to
43
+ * the finest voxel size AND become eligible for the coarsen-rebake loop again — silently
44
+ * reinstating the exact defect that exemption exists to prevent, with every test still green.
45
+ * (`forge-world.ts`'s other predicate, the prefix-only one, is NOT interchangeable with this:
46
+ * it guards the world-GLB omission and must fail safe by over-matching, the opposite way.)
47
+ */
48
+ export const isCladdingModule = (archetype) => isKitModuleArchetype(archetype);
49
+ /** A used kit module is structural: the exact floor/wall/surround has no visual fallback. */
50
+ export const isUsedStructuralModule = (archetype) => isCladdingModule(archetype) && archetype.instanceCount > 0;
51
+ //# sourceMappingURL=archetype-skips.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"archetype-skips.js","sourceRoot":"","sources":["../../src/pipeline/archetype-skips.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AACH,OAAO,EAAE,oBAAoB,EAAE,MAAM,uBAAuB,CAAC;AA6B7D,MAAM,UAAU,kBAAkB,CAAC,KAAiD;IAClF,OAAO,CAAC,CAAC,KAAK,IAAI,KAAK,CAAC,gBAAgB,KAAK,IAAI,CAAC;AACpD,CAAC;AAED;;GAEG;AACH,MAAM,UAAU,QAAQ,CAAC,OAAyB;IAChD,MAAM,CAAC,GAAG,OAAO,CAAC,aAAa,CAAC;IAChC,OAAO,cAAc,OAAO,CAAC,IAAI,kBAAkB,OAAO,CAAC,MAAM,qBAAqB,CAAC,YAAY,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,GAAG,oCAAoC,CAAC;AAC5J,CAAC;AAED;;;;GAIG;AACH,MAAM,UAAU,kBAAkB,CAAC,OAAyB;IAC1D,MAAM,CAAC,GAAG,OAAO,CAAC,aAAa,CAAC;IAChC,OAAO,+BAA+B,OAAO,CAAC,IAAI,qBAAqB,OAAO,CAAC,MAAM,WAAW,CAAC,aAAa,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,GAAG,6GAA6G,CAAC;AAChP,CAAC;AAED;;;;;GAKG;AACH,MAAM,UAAU,oBAAoB,CAAC,IAAY,EAAE,UAAkB,EAAE,MAAc;IACnF,OAAO,oBAAoB,IAAI,cAAc,UAAU,0BAA0B,MAAM,yBAAyB;UAC5G,gHAAgH;UAChH,sFAAsF,CAAC;AAC7F,CAAC;AAED;;;;;;;;GAQG;AACH,MAAM,CAAC,MAAM,gBAAgB,GAAG,CAAC,SAA4B,EAAW,EAAE,CAAC,oBAAoB,CAAC,SAAS,CAAC,CAAC;AAE3G,6FAA6F;AAC7F,MAAM,CAAC,MAAM,sBAAsB,GAAG,CAAC,SAA4B,EAAW,EAAE,CAC9E,gBAAgB,CAAC,SAAS,CAAC,IAAI,SAAS,CAAC,aAAa,GAAG,CAAC,CAAC"}
@@ -0,0 +1,4 @@
1
+ import type { ForgedWorkStore, ForgeArtifactName } from './forged-work-store.js';
2
+ /** Read a required artifact, or fail the step with a clear message. */
3
+ export declare function requireArtifact<T>(store: ForgedWorkStore, name: ForgeArtifactName): Promise<T>;
4
+ //# sourceMappingURL=artifact-store.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"artifact-store.d.ts","sourceRoot":"","sources":["../../src/pipeline/artifact-store.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,eAAe,EAAE,iBAAiB,EAAE,MAAM,wBAAwB,CAAC;AAGjF,uEAAuE;AACvE,wBAAsB,eAAe,CAAC,CAAC,EAAE,KAAK,EAAE,eAAe,EAAE,IAAI,EAAE,iBAAiB,GAAG,OAAO,CAAC,CAAC,CAAC,CAMpG"}
@@ -0,0 +1,10 @@
1
+ import { ForgeStepError } from './errors.js';
2
+ /** Read a required artifact, or fail the step with a clear message. */
3
+ export async function requireArtifact(store, name) {
4
+ const value = await store.get(name);
5
+ if (value === null) {
6
+ throw new ForgeStepError(`Forged-work artifact "${name}" missing for job ${store.jobId} — cannot continue`);
7
+ }
8
+ return value;
9
+ }
10
+ //# sourceMappingURL=artifact-store.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"artifact-store.js","sourceRoot":"","sources":["../../src/pipeline/artifact-store.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,cAAc,EAAE,MAAM,aAAa,CAAC;AAE7C,uEAAuE;AACvE,MAAM,CAAC,KAAK,UAAU,eAAe,CAAI,KAAsB,EAAE,IAAuB;IACtF,MAAM,KAAK,GAAG,MAAM,KAAK,CAAC,GAAG,CAAI,IAAI,CAAC,CAAC;IACvC,IAAI,KAAK,KAAK,IAAI,EAAE,CAAC;QACnB,MAAM,IAAI,cAAc,CAAC,yBAAyB,IAAI,qBAAqB,KAAK,CAAC,KAAK,oBAAoB,CAAC,CAAC;IAC9G,CAAC;IACD,OAAO,KAAK,CAAC;AACf,CAAC"}
@@ -0,0 +1,27 @@
1
+ /**
2
+ * Compact view of the game's asset library handed to the designer LLM so a
3
+ * forge can REUSE existing assets (HQ-generated landmarks, user imports)
4
+ * instead of minting new placeholders. Built from world.json `assets[]`.
5
+ *
6
+ * Moved from game-play-agent verbatim: the transform itself (records ->
7
+ * catalog entries) is pure. The READ that used to feed it —
8
+ * `readWorkWorldJson().assets` via a monorepo path walk — stayed on the host
9
+ * side; the host calls `buildAssetCatalog` on what it read and hands the
10
+ * result in as `ForgeDeps.assetCatalog`.
11
+ */
12
+ export interface CatalogEntry {
13
+ assetId: string;
14
+ name: string;
15
+ description?: string;
16
+ /** Real-world dimensions in meters (y = height). fitBox wins over boundingBox. */
17
+ dims: {
18
+ x: number;
19
+ y: number;
20
+ z: number;
21
+ };
22
+ /** hq = HQ-regenerated placeholder; placeholder = un-upgraded forge stand-in; library = user import / other finished asset. */
23
+ kind: 'hq' | 'placeholder' | 'library';
24
+ type: 'vxl' | 'glb';
25
+ }
26
+ export declare function buildAssetCatalog(assets: Array<Record<string, unknown>>): CatalogEntry[];
27
+ //# sourceMappingURL=asset-catalog.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"asset-catalog.d.ts","sourceRoot":"","sources":["../../src/pipeline/asset-catalog.ts"],"names":[],"mappings":"AAIA;;;;;;;;;;GAUG;AACH,MAAM,WAAW,YAAY;IAC3B,OAAO,EAAE,MAAM,CAAC;IAChB,IAAI,EAAE,MAAM,CAAC;IACb,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,kFAAkF;IAClF,IAAI,EAAE;QAAE,CAAC,EAAE,MAAM,CAAC;QAAC,CAAC,EAAE,MAAM,CAAC;QAAC,CAAC,EAAE,MAAM,CAAA;KAAE,CAAC;IAC1C,+HAA+H;IAC/H,IAAI,EAAE,IAAI,GAAG,aAAa,GAAG,SAAS,CAAC;IACvC,IAAI,EAAE,KAAK,GAAG,KAAK,CAAC;CACrB;AAMD,wBAAgB,iBAAiB,CAAC,MAAM,EAAE,KAAK,CAAC,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC,GAAG,YAAY,EAAE,CA2BxF"}
@@ -0,0 +1,47 @@
1
+ import { createLogger } from '../log.js';
2
+ const log = createLogger('world-forger-asset-catalog');
3
+ const PLACEABLE_TYPES = new Set(['vxl', 'voxels', 'glb', 'gltf', 'polygon-mesh']);
4
+ const CATALOG_CAP = 200;
5
+ const KIND_ORDER = { hq: 0, library: 1, placeholder: 2 };
6
+ export function buildAssetCatalog(assets) {
7
+ const entries = [];
8
+ for (const a of assets) {
9
+ const type = String(a.type ?? '');
10
+ if (!PLACEABLE_TYPES.has(type))
11
+ continue;
12
+ const dims = deriveDims(a);
13
+ if (!dims)
14
+ continue;
15
+ entries.push({
16
+ assetId: String(a.id),
17
+ name: String(a.name ?? a.id),
18
+ description: typeof a.description === 'string' ? a.description : undefined,
19
+ dims,
20
+ kind: a.hqGeneratedAt ? 'hq' : a.placeholder === true ? 'placeholder' : 'library',
21
+ type: type === 'glb' || type === 'gltf' || type === 'polygon-mesh' ? 'glb' : 'vxl',
22
+ });
23
+ }
24
+ // Stable sort (Node guarantees stability): hq > library > placeholder, so
25
+ // the cap drops the least valuable entries first.
26
+ entries.sort((x, y) => KIND_ORDER[x.kind] - KIND_ORDER[y.kind]);
27
+ if (entries.length > CATALOG_CAP) {
28
+ // No silent caps — dropped entries are invisible to the designer.
29
+ log.warn({ total: entries.length, dropped: entries.length - CATALOG_CAP }, 'Asset catalog over the designer cap — placeholder/library tail dropped');
30
+ }
31
+ return entries.slice(0, CATALOG_CAP);
32
+ }
33
+ function deriveDims(a) {
34
+ const fit = a.fitBox;
35
+ if (fit && fit.x && fit.z && fit.height)
36
+ return { x: fit.x, y: fit.height, z: fit.z };
37
+ const bb = a.boundingBox;
38
+ if (!bb)
39
+ return null;
40
+ const dx = (bb.maxX ?? 0) - (bb.minX ?? 0);
41
+ const dy = (bb.maxY ?? 0) - (bb.minY ?? 0);
42
+ const dz = (bb.maxZ ?? 0) - (bb.minZ ?? 0);
43
+ if (dx <= 0 || dy <= 0 || dz <= 0)
44
+ return null;
45
+ return { x: dx, y: dy, z: dz };
46
+ }
47
+ //# sourceMappingURL=asset-catalog.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"asset-catalog.js","sourceRoot":"","sources":["../../src/pipeline/asset-catalog.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,45 @@
1
+ /**
2
+ * Step 3 — create the archetype LIBRARY ASSETS by voxelizing each uploaded GLB
3
+ * in the browser, through the host's transport seam.
4
+ *
5
+ * Moved from game-play-agent's `pipeline.ts` verbatim, with the one
6
+ * `sendAndWaitForResult` call site turned into `deps.transport.sendAndWait`
7
+ * (same arguments, same `null`-on-timeout contract — see transport-types.ts)
8
+ * and the logger turned into `deps.logger`.
9
+ */
10
+ import type { ForgedWorkStore } from './forged-work-store.js';
11
+ import type { ForgeDeps } from './deps.js';
12
+ /**
13
+ * Minimum gap between archetype-progress checkpoint writes. GCS rate-limits
14
+ * mutations of one object to ~1/s sustained — sub-second archetype bakes used to
15
+ * rewrite the checkpoint faster than that and 429-fail the whole step.
16
+ */
17
+ export declare const ARCHETYPE_CHECKPOINT_MIN_INTERVAL_MS = 2000;
18
+ /**
19
+ * Create the archetype LIBRARY ASSETS by voxelizing each uploaded GLB at fine
20
+ * resolution in the browser. Before the level bake, so an unresponsive browser
21
+ * is caught BEFORE the expensive bake.
22
+ *
23
+ * Resumability: each archetype's engine record is cached (keyed by its index in
24
+ * forge.archetypes) the moment it bakes, so a resume after a partial failure
25
+ * skips the ones already baked instead of re-baking the whole set. Each bake is
26
+ * a SINGLE attempt (maxRetries: 1) — like the level bake, it is an expensive,
27
+ * non-idempotent browser job; retrying just re-fires a fresh bake (new asset id)
28
+ * into an already-struggling browser. A failed/slow archetype is retried only by
29
+ * the orchestrator's resume, which now picks up from the per-archetype cache.
30
+ *
31
+ * A bake FAILURE (the browser answered and rejected this one GLB) never aborts, not
32
+ * even for a used structural dungeon module: the failure is individual and
33
+ * deterministic, so aborting would fail every resume identically and forfeit the
34
+ * whole forge for one archetype. The archetype is dropped with a note — structural
35
+ * modules get a note naming the bare shell left behind — and the forge keeps
36
+ * everything else. A TIMEOUT still aborts resumably: the browser is not answering,
37
+ * so every remaining bake would burn the same timeout, and a resume can succeed.
38
+ * Structural skip markers are removed on resume so those bakes get a second chance.
39
+ *
40
+ * Modules remain exempt from the budget COARSENING below: a module is kept at the
41
+ * voxel size forge-world.ts pinned it to and noted, because doubling that size deletes the
42
+ * relief the kit exists for. Every other archetype coarsens exactly as before.
43
+ */
44
+ export declare function createLevelArchetypes(store: ForgedWorkStore, deps: ForgeDeps): Promise<void>;
45
+ //# sourceMappingURL=create-level-archetypes.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"create-level-archetypes.d.ts","sourceRoot":"","sources":["../../src/pipeline/create-level-archetypes.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AACH,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,wBAAwB,CAAC;AAC9D,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,WAAW,CAAC;AAY3C;;;;GAIG;AACH,eAAO,MAAM,oCAAoC,OAAO,CAAC;AAEzD;;;;;;;;;;;;;;;;;;;;;;;;;GAyBG;AACH,wBAAsB,qBAAqB,CAAC,KAAK,EAAE,eAAe,EAAE,IAAI,EAAE,SAAS,GAAG,OAAO,CAAC,IAAI,CAAC,CAwLlG"}
@@ -0,0 +1,197 @@
1
+ import { requireArtifact } from './artifact-store.js';
2
+ import { ForgeStepError } from './errors.js';
3
+ import { forgeRequestId } from './request-id.js';
4
+ import { isCladdingModule, isSkippedArchetype, isUsedStructuralModule, } from './archetype-skips.js';
5
+ import { ASSET_LOD0_VOXEL_BUDGET } from '../forge-constants.js';
6
+ /** Per-archetype asset voxelization (small objects at fine resolution). */
7
+ const ASSET_CREATE_TIMEOUT_MS = 5 * 60 * 1000;
8
+ /**
9
+ * Minimum gap between archetype-progress checkpoint writes. GCS rate-limits
10
+ * mutations of one object to ~1/s sustained — sub-second archetype bakes used to
11
+ * rewrite the checkpoint faster than that and 429-fail the whole step.
12
+ */
13
+ export const ARCHETYPE_CHECKPOINT_MIN_INTERVAL_MS = 2000;
14
+ /**
15
+ * Create the archetype LIBRARY ASSETS by voxelizing each uploaded GLB at fine
16
+ * resolution in the browser. Before the level bake, so an unresponsive browser
17
+ * is caught BEFORE the expensive bake.
18
+ *
19
+ * Resumability: each archetype's engine record is cached (keyed by its index in
20
+ * forge.archetypes) the moment it bakes, so a resume after a partial failure
21
+ * skips the ones already baked instead of re-baking the whole set. Each bake is
22
+ * a SINGLE attempt (maxRetries: 1) — like the level bake, it is an expensive,
23
+ * non-idempotent browser job; retrying just re-fires a fresh bake (new asset id)
24
+ * into an already-struggling browser. A failed/slow archetype is retried only by
25
+ * the orchestrator's resume, which now picks up from the per-archetype cache.
26
+ *
27
+ * A bake FAILURE (the browser answered and rejected this one GLB) never aborts, not
28
+ * even for a used structural dungeon module: the failure is individual and
29
+ * deterministic, so aborting would fail every resume identically and forfeit the
30
+ * whole forge for one archetype. The archetype is dropped with a note — structural
31
+ * modules get a note naming the bare shell left behind — and the forge keeps
32
+ * everything else. A TIMEOUT still aborts resumably: the browser is not answering,
33
+ * so every remaining bake would burn the same timeout, and a resume can succeed.
34
+ * Structural skip markers are removed on resume so those bakes get a second chance.
35
+ *
36
+ * Modules remain exempt from the budget COARSENING below: a module is kept at the
37
+ * voxel size forge-world.ts pinned it to and noted, because doubling that size deletes the
38
+ * relief the kit exists for. Every other archetype coarsens exactly as before.
39
+ */
40
+ export async function createLevelArchetypes(store, deps) {
41
+ const forge = await requireArtifact(store, 'forge');
42
+ const progress = (await store.get('archetype-progress')) ?? {};
43
+ const structuralNames = new Set(forge.archetypes.filter(isUsedStructuralModule).map((archetype) => archetype.name));
44
+ for (const [index, record] of Object.entries(progress)) {
45
+ const archetype = forge.archetypes[Number(index)];
46
+ const recordedName = typeof record.name === 'string' ? record.name : null;
47
+ // Progress is index-addressed for ordering, but NAME-addressed for identity.
48
+ // A resumed forge may reorder archetypes, so an index hit alone cannot prove
49
+ // that the cached asset belongs to its current occupant.
50
+ const identityMatches = !!archetype && recordedName === archetype.name;
51
+ // A structural skip is retried on resume — the skip was a degrade, and a
52
+ // resume is a free second chance at the exact visual the kit exists for.
53
+ // Match it by the RECORDED name so reordering cannot hide it under a
54
+ // non-structural current occupant.
55
+ const isStructuralSkip = isSkippedArchetype(record)
56
+ && recordedName !== null
57
+ && structuralNames.has(recordedName);
58
+ if (!identityMatches || isStructuralSkip) {
59
+ delete progress[index];
60
+ }
61
+ }
62
+ // A dungeon's glow (rune inlays, crystal veins, torch flames) is authored as
63
+ // PALETTE COLORS, and the fixture/key/door-leaf archetypes are baked from the
64
+ // same palette as the level. The map is passed WHOLE to every archetype of a
65
+ // dungeon level rather than sliced per archetype: matching is exact-color, so
66
+ // a key whose color never appears in that archetype is a silent no-op (it
67
+ // comes back in `emissiveUnmatched`, which is a note, never an error).
68
+ const emissiveByColor = forge.dungeonEmissiveByColor;
69
+ // GCS caps mutations of a SINGLE object at ~1/s; checkpointing after every archetype
70
+ // hammered that limit when small props baked sub-second, 429-failing forges whose bakes
71
+ // had all succeeded. The checkpoint is pure resume state (the in-memory map is
72
+ // authoritative for this run), so writes are throttled AND best-effort: a skipped or
73
+ // failed write only means a resume re-bakes the archetypes recorded since the last one.
74
+ let lastCheckpointAt = 0;
75
+ const checkpoint = async () => {
76
+ if (Date.now() - lastCheckpointAt < ARCHETYPE_CHECKPOINT_MIN_INTERVAL_MS)
77
+ return;
78
+ lastCheckpointAt = Date.now(); // advance even on failure — re-hitting a 429ing object makes the storm worse
79
+ try {
80
+ await store.put('archetype-progress', progress);
81
+ }
82
+ catch (err) {
83
+ deps.logger.warn({ err, jobId: store.jobId }, 'archetype-progress checkpoint write failed — continuing; a resume re-bakes the unrecorded archetypes');
84
+ }
85
+ };
86
+ for (let i = 0; i < forge.archetypes.length; i++) {
87
+ if (progress[String(i)])
88
+ continue; // already baked in a prior attempt — skip
89
+ const archetype = forge.archetypes[i];
90
+ // Reused library asset: no GLB, no voxelize — record a stub that carries
91
+ // the EXISTING asset id so placement/persist resolve instances to it. The
92
+ // asset record itself is never rewritten (it may be an HQ model).
93
+ if (archetype.reuse) {
94
+ progress[String(i)] = { id: archetype.reuse.assetId, name: archetype.reuse.assetName, reused: true };
95
+ await checkpoint();
96
+ deps.logger.info({ jobId: store.jobId, archetype: archetype.name, assetId: archetype.reuse.assetId }, 'Archetype reuses existing library asset — bake skipped');
97
+ continue;
98
+ }
99
+ const isModule = isCladdingModule(archetype);
100
+ const isStructuralModule = isUsedStructuralModule(archetype);
101
+ // Neither a bake nor a reuse target. Even a structural module degrades to a
102
+ // skip: the defect is deterministic (a resume would find the same artifact),
103
+ // so aborting would trade a level that is 99% fine for no level at all.
104
+ if (!archetype.glbUrl || !archetype.assetVoxelOptions) {
105
+ const skipped = {
106
+ skippedArchetype: true,
107
+ name: archetype.name,
108
+ reason: 'the forge artifact carried neither a GLB nor a reuse target for it',
109
+ instanceCount: archetype.instanceCount,
110
+ ...(isStructuralModule ? { structural: true } : {}),
111
+ };
112
+ progress[String(i)] = skipped;
113
+ await checkpoint();
114
+ deps.logger.warn({ jobId: store.jobId, archetype: archetype.name, structural: isStructuralModule }, 'Archetype has no GLB and no reuse target — dropped, the level keeps everything else');
115
+ continue;
116
+ }
117
+ // Bake at the budget-predicted size first (deriveArchetypeVoxelBake); if the
118
+ // result still exceeds the archetype's own LOD0 budget (bbox under-estimated
119
+ // the real surface), re-bake with doubled voxel sizes until it fits. The
120
+ // re-bake upserts the same asset name, so only the final bake survives.
121
+ const leafBudget = archetype.assetVoxelOptions.lod0Budget ?? ASSET_LOD0_VOXEL_BUDGET;
122
+ let opts = { ...archetype.assetVoxelOptions };
123
+ let asset = null;
124
+ let failureReason = null;
125
+ const MAX_COARSEN_REBAKES = 2;
126
+ for (let attempt = 0;; attempt++) {
127
+ const requestId = forgeRequestId('asset');
128
+ const result = await deps.transport.sendAndWait({
129
+ type: 'CREATE_ASSET_FROM_GLB_URL',
130
+ requestId,
131
+ name: archetype.name,
132
+ glbUrl: archetype.glbUrl,
133
+ options: { ...opts, ...(emissiveByColor ? { emissiveByColor } : {}) },
134
+ }, 'ASSET_FROM_GLB_URL_RESULT', ASSET_CREATE_TIMEOUT_MS, { maxRetries: 1 });
135
+ if (!result) {
136
+ // The browser did not answer inside the timeout — infrastructure, not an
137
+ // individual archetype error: with the editor gone, every remaining bake
138
+ // would burn the same timeout. Abort resumably; baked archetypes are
139
+ // checkpointed and the resume retries from here.
140
+ throw new ForgeStepError(`Voxelizing archetype "${archetype.name}" timed out — is the editor open?`, forge.glbUrl);
141
+ }
142
+ if (!result.success || !result.asset) {
143
+ failureReason = result.error ?? 'unknown error';
144
+ break;
145
+ }
146
+ asset = result.asset;
147
+ const voxelCount = typeof asset.voxelCount === 'number' ? asset.voxelCount : 0;
148
+ if (voxelCount <= leafBudget)
149
+ break;
150
+ // A cladding module is EXEMPT from coarsening. Its relief is authored in multiples of
151
+ // MODULE_MIN_VOXEL_M — mortar recesses one voxel deep, protrusions two — which is why
152
+ // forge-world.ts pins the bake there. The first doubling already drops the mortar
153
+ // lines and the chamfers below a voxel; the second takes the protrusions with them and
154
+ // the piece bakes to flat colour, the very surface the kit exists to replace. Nothing
155
+ // downstream can tell: the asset is still produced and every other check still passes.
156
+ // So keep the bake at its pinned size and NOTE it (moduleOverBudgetNote) — heavier
157
+ // instances are a cost the caller can see and act on; a flattened kit is not.
158
+ if (isModule) {
159
+ deps.logger.warn({ jobId: store.jobId, archetype: archetype.name, voxelCount, budget: leafBudget, minVoxelSize: opts.minVoxelSize }, 'Cladding module baked over its voxel budget — kept at the pinned module voxel size and noted; coarsening it would delete the stone relief');
160
+ break;
161
+ }
162
+ if (attempt >= MAX_COARSEN_REBAKES) {
163
+ deps.logger.warn({ jobId: store.jobId, archetype: archetype.name, voxelCount, minVoxelSize: opts.minVoxelSize }, 'Archetype still over the voxel budget after coarsening rebakes — keeping the last bake');
164
+ break;
165
+ }
166
+ opts = { ...opts, minVoxelSize: opts.minVoxelSize * 2, maxVoxelSize: opts.maxVoxelSize * 2 };
167
+ deps.logger.info({ jobId: store.jobId, archetype: archetype.name, voxelCount, budget: leafBudget, nextMinVoxelSize: opts.minVoxelSize }, 'Archetype bake over the voxel budget — re-baking at doubled voxel sizes');
168
+ }
169
+ if (failureReason !== null) {
170
+ // The browser ANSWERED and rejected this one bake — an individual,
171
+ // deterministic error. Aborting would fail every resume identically and
172
+ // forfeit the whole forge for one archetype, so degrade instead: drop it
173
+ // (its placements follow at persist) with a note. For a structural module
174
+ // the cost is bare recessed shell where its carved stone belonged — the
175
+ // note says so, and a resume retries the bake for a second chance.
176
+ const skipped = {
177
+ skippedArchetype: true,
178
+ name: archetype.name,
179
+ reason: failureReason,
180
+ instanceCount: archetype.instanceCount,
181
+ ...(isStructuralModule ? { structural: true } : {}),
182
+ };
183
+ progress[String(i)] = skipped;
184
+ await checkpoint();
185
+ deps.logger.warn({ jobId: store.jobId, archetype: archetype.name, structural: isStructuralModule, reason: failureReason }, 'Archetype bake failed — dropped, the level keeps everything else');
186
+ continue;
187
+ }
188
+ progress[String(i)] = asset;
189
+ await checkpoint(); // persist incrementally for resume (throttled, best-effort)
190
+ deps.logger.info({ jobId: store.jobId, archetype: archetype.name, assetId: asset.id }, 'Archetype asset created');
191
+ }
192
+ // All archetypes baked — write the ordered combined array (the completion
193
+ // marker the runner skips on; index order matches buildSummary's pairing).
194
+ const created = forge.archetypes.map((_, i) => progress[String(i)]);
195
+ await store.put('archetype-assets', created);
196
+ }
197
+ //# sourceMappingURL=create-level-archetypes.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"create-level-archetypes.js","sourceRoot":"","sources":["../../src/pipeline/create-level-archetypes.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,72 @@
1
+ /**
2
+ * ForgeDeps — everything the world-forger pipeline needs from its host, so
3
+ * the SAME implementation runs behind game-play-agent's Mastra web lane and
4
+ * the CLI lane (`bitmagic forge`, split across api-server for steps 1-2 and
5
+ * the CLI's own headless browser for steps 3-5). Modeled on
6
+ * `shared/asset-core/src/types.ts`'s
7
+ * `GenerateDeps` (read that first) — a generator/pipeline takes its host's
8
+ * capabilities as a parameter and owns no framework, filesystem or tenant of
9
+ * its own.
10
+ *
11
+ * Six seams:
12
+ * - `storage` — the StorageEnv shape `createForgedWorkStore(jobId, env)`
13
+ * already accepts (see forged-work-store.ts) and step 2's
14
+ * uploads write through.
15
+ * - `model` — the LLM client the director and designer call.
16
+ * - `transport` — replaces `sendAndWaitForResult`: the browser round-trips
17
+ * steps 3-5 make (archetype bakes, the level bake, instance
18
+ * placement). Not read by steps 1-2.
19
+ * - `worldJson` — reads the game's current world.json for step 5's persist
20
+ * plan. Read-only by design: the step RETURNS modifications
21
+ * and the host applies them (see world-json-source.ts).
22
+ * - `assetCatalog` — optional; absence degrades to "no reuse", never a
23
+ * failure. The host reads world.json and calls
24
+ * `buildAssetCatalog` itself (see asset-catalog.ts).
25
+ * - `hqAssetIds` — optional, defaults to empty. Read by step 5's placeholder
26
+ * purge, and read WHEN that purge runs rather than when these
27
+ * deps were built (see the field).
28
+ */
29
+ import type { ForgeStorageEnv } from './storage-types.js';
30
+ import type { ForgeModelClient } from './model-types.js';
31
+ import type { ForgeTransport } from './transport-types.js';
32
+ import type { ForgeWorldJsonSource } from './world-json-source.js';
33
+ import type { CatalogEntry } from './asset-catalog.js';
34
+ /**
35
+ * The logging surface the pipeline needs. Structurally satisfied by a pino
36
+ * logger (game-play-agent passes its own `createLogger(...)` result
37
+ * directly), same pattern as `shared/asset-core`'s `CoreLogger`. The
38
+ * object-first overload matters for errors: `warn({ err }, msg)` serializes
39
+ * the Error with its stack as a structured field, where
40
+ * `warn(\`${err.message}\`)` throws the stack away.
41
+ */
42
+ export interface ForgeLogger {
43
+ info(message: string, ...rest: unknown[]): void;
44
+ info(context: Record<string, unknown>, message: string): void;
45
+ warn(message: string, ...rest: unknown[]): void;
46
+ warn(context: Record<string, unknown>, message: string): void;
47
+ error(message: string, ...rest: unknown[]): void;
48
+ error(context: Record<string, unknown>, message: string): void;
49
+ }
50
+ export interface ForgeDeps {
51
+ storage: ForgeStorageEnv;
52
+ model: ForgeModelClient;
53
+ transport: ForgeTransport;
54
+ worldJson: ForgeWorldJsonSource;
55
+ /** Existing library assets the designer may reuse. Absent/empty = no reuse
56
+ * candidates (best-effort — this must never fail the job). */
57
+ assetCatalog?: CatalogEntry[];
58
+ /**
59
+ * Asset ids with an HQ regeneration currently in flight, whose placeholder
60
+ * assets step 5's purge must therefore spare. Omitted = none.
61
+ *
62
+ * A FUNCTION, for the same reason `worldJson` above is one: the purge runs at
63
+ * the very end of a 30-60 minute forge (an LLM design, a geometry build, the
64
+ * archetype bakes and a level bake that alone may take 20 minutes), and HQ
65
+ * jobs both start and finish inside that window. A set captured when the host
66
+ * built these deps would miss every job STARTED since — purging the exact
67
+ * placeholders that protection exists for.
68
+ */
69
+ hqAssetIds?: () => Promise<Set<string>>;
70
+ logger: ForgeLogger;
71
+ }
72
+ //# sourceMappingURL=deps.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"deps.d.ts","sourceRoot":"","sources":["../../src/pipeline/deps.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;GA2BG;AACH,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,oBAAoB,CAAC;AAC1D,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,kBAAkB,CAAC;AACzD,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,sBAAsB,CAAC;AAC3D,OAAO,KAAK,EAAE,oBAAoB,EAAE,MAAM,wBAAwB,CAAC;AACnE,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,oBAAoB,CAAC;AAEvD;;;;;;;GAOG;AACH,MAAM,WAAW,WAAW;IAC1B,IAAI,CAAC,OAAO,EAAE,MAAM,EAAE,GAAG,IAAI,EAAE,OAAO,EAAE,GAAG,IAAI,CAAC;IAChD,IAAI,CAAC,OAAO,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EAAE,OAAO,EAAE,MAAM,GAAG,IAAI,CAAC;IAC9D,IAAI,CAAC,OAAO,EAAE,MAAM,EAAE,GAAG,IAAI,EAAE,OAAO,EAAE,GAAG,IAAI,CAAC;IAChD,IAAI,CAAC,OAAO,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EAAE,OAAO,EAAE,MAAM,GAAG,IAAI,CAAC;IAC9D,KAAK,CAAC,OAAO,EAAE,MAAM,EAAE,GAAG,IAAI,EAAE,OAAO,EAAE,GAAG,IAAI,CAAC;IACjD,KAAK,CAAC,OAAO,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EAAE,OAAO,EAAE,MAAM,GAAG,IAAI,CAAC;CAChE;AAED,MAAM,WAAW,SAAS;IACxB,OAAO,EAAE,eAAe,CAAC;IACzB,KAAK,EAAE,gBAAgB,CAAC;IACxB,SAAS,EAAE,cAAc,CAAC;IAC1B,SAAS,EAAE,oBAAoB,CAAC;IAChC;mEAC+D;IAC/D,YAAY,CAAC,EAAE,YAAY,EAAE,CAAC;IAC9B;;;;;;;;;;OAUG;IACH,UAAU,CAAC,EAAE,MAAM,OAAO,CAAC,GAAG,CAAC,MAAM,CAAC,CAAC,CAAC;IACxC,MAAM,EAAE,WAAW,CAAC;CACrB"}
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=deps.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"deps.js","sourceRoot":"","sources":["../../src/pipeline/deps.ts"],"names":[],"mappings":""}
@@ -0,0 +1,10 @@
1
+ /**
2
+ * A pipeline step failed. Carries `glbUrl` when the source GLB already exists
3
+ * so the caller can tell the user it can be voxelized manually from the
4
+ * Assets tab (the original tool's timeout affordance).
5
+ */
6
+ export declare class ForgeStepError extends Error {
7
+ readonly glbUrl?: string;
8
+ constructor(message: string, glbUrl?: string);
9
+ }
10
+ //# sourceMappingURL=errors.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"errors.d.ts","sourceRoot":"","sources":["../../src/pipeline/errors.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AACH,qBAAa,cAAe,SAAQ,KAAK;IACvC,QAAQ,CAAC,MAAM,CAAC,EAAE,MAAM,CAAC;gBACb,OAAO,EAAE,MAAM,EAAE,MAAM,CAAC,EAAE,MAAM;CAK7C"}
@@ -0,0 +1,13 @@
1
+ /**
2
+ * A pipeline step failed. Carries `glbUrl` when the source GLB already exists
3
+ * so the caller can tell the user it can be voxelized manually from the
4
+ * Assets tab (the original tool's timeout affordance).
5
+ */
6
+ export class ForgeStepError extends Error {
7
+ constructor(message, glbUrl) {
8
+ super(message);
9
+ this.name = 'ForgeStepError';
10
+ this.glbUrl = glbUrl;
11
+ }
12
+ }
13
+ //# sourceMappingURL=errors.js.map