@forgeax/engine-pack 0.1.3 → 0.1.4

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 (95) hide show
  1. package/README.md +51 -3
  2. package/dist/.tsbuildinfo +1 -1
  3. package/dist/__tests__/owner-chain.integration.test.d.ts +2 -0
  4. package/dist/__tests__/owner-chain.integration.test.d.ts.map +1 -0
  5. package/dist/__tests__/package-finalizer.contract.test.d.ts +2 -0
  6. package/dist/__tests__/package-finalizer.contract.test.d.ts.map +1 -0
  7. package/dist/__tests__/runtime.browser.test.d.ts +2 -0
  8. package/dist/__tests__/runtime.browser.test.d.ts.map +1 -0
  9. package/dist/__tests__/scanner-inventory.contract.test.d.ts +2 -0
  10. package/dist/__tests__/scanner-inventory.contract.test.d.ts.map +1 -0
  11. package/dist/artifact-path.mjs +53 -0
  12. package/dist/artifact-path.mjs.map +1 -0
  13. package/dist/atlas/shelf-pack.d.ts.map +1 -1
  14. package/dist/build.d.ts +17 -0
  15. package/dist/build.d.ts.map +1 -0
  16. package/dist/build.mjs +10168 -0
  17. package/dist/build.mjs.map +1 -0
  18. package/dist/builtin.d.ts +20 -0
  19. package/dist/builtin.d.ts.map +1 -1
  20. package/dist/builtin.mjs +141 -0
  21. package/dist/builtin.mjs.map +1 -0
  22. package/dist/catalog-builder.d.ts +43 -0
  23. package/dist/catalog-builder.d.ts.map +1 -0
  24. package/dist/catalog-delta.d.ts +11 -0
  25. package/dist/catalog-delta.d.ts.map +1 -0
  26. package/dist/catalog-projection.d.ts +91 -0
  27. package/dist/catalog-projection.d.ts.map +1 -0
  28. package/dist/cli-asset.mjs +231 -484
  29. package/dist/cli-asset.mjs.map +1 -1
  30. package/dist/index.mjs +83 -1
  31. package/dist/index.mjs.map +1 -1
  32. package/dist/material-cook.d.ts +2 -0
  33. package/dist/material-cook.d.ts.map +1 -0
  34. package/dist/material-cook.mjs +193 -0
  35. package/dist/material-cook.mjs.map +1 -0
  36. package/dist/mesh-bin-contract.mjs +96 -0
  37. package/dist/mesh-bin-contract.mjs.map +1 -0
  38. package/dist/native-cooker-registry.d.ts +19 -0
  39. package/dist/native-cooker-registry.d.ts.map +1 -1
  40. package/dist/native-cooker.mjs +37 -0
  41. package/dist/native-cooker.mjs.map +1 -1
  42. package/dist/package-finalizer.d.ts +121 -0
  43. package/dist/package-finalizer.d.ts.map +1 -0
  44. package/dist/resolve-asset-source.d.ts.map +1 -1
  45. package/dist/resolve-asset-source.mjs +16 -6
  46. package/dist/resolve-asset-source.mjs.map +1 -1
  47. package/dist/runtime-publication.d.ts +44 -0
  48. package/dist/runtime-publication.d.ts.map +1 -0
  49. package/dist/runtime.d.ts +8 -0
  50. package/dist/runtime.d.ts.map +1 -0
  51. package/dist/runtime.mjs +7261 -0
  52. package/dist/runtime.mjs.map +1 -0
  53. package/dist/scanner.d.ts +87 -54
  54. package/dist/scanner.d.ts.map +1 -1
  55. package/dist/scanner.mjs +258 -454
  56. package/dist/scanner.mjs.map +1 -1
  57. package/dist/scriptable-pack-node.d.ts +3 -8
  58. package/dist/scriptable-pack-node.d.ts.map +1 -1
  59. package/dist/scriptable-pack-node.mjs +93 -9
  60. package/dist/scriptable-pack-node.mjs.map +1 -1
  61. package/dist/scriptable-pack-worker.mjs +1 -0
  62. package/dist/scriptable-pack-worker.mjs.map +1 -1
  63. package/dist/scriptable-pack.d.ts +33 -0
  64. package/dist/scriptable-pack.d.ts.map +1 -1
  65. package/dist/scriptable-pack.mjs +91 -2
  66. package/dist/scriptable-pack.mjs.map +1 -1
  67. package/dist/topology.d.ts +1 -1
  68. package/dist/topology.d.ts.map +1 -1
  69. package/package.json +27 -2
  70. package/src/__tests__/mesh-bin-consumer-surface.unit.test.ts +2 -1
  71. package/src/__tests__/owner-chain.integration.test.ts +78 -0
  72. package/src/__tests__/package-finalizer.contract.test.ts +105 -0
  73. package/src/__tests__/runtime.browser.test.ts +15 -0
  74. package/src/__tests__/scanner-inventory.contract.test.ts +167 -0
  75. package/src/__tests__/scanner-inventory.test.ts +16 -12
  76. package/src/__tests__/scriptable-pack.unit.test.ts +42 -0
  77. package/src/atlas/shelf-pack.ts +20 -24
  78. package/src/build.ts +137 -0
  79. package/src/builtin.ts +38 -0
  80. package/src/catalog-builder.ts +313 -0
  81. package/src/catalog-delta.ts +189 -0
  82. package/src/catalog-projection.ts +344 -0
  83. package/src/cli-asset.ts +1 -5
  84. package/src/index.ts +2 -2
  85. package/src/material-cook.ts +13 -0
  86. package/src/native-cooker-registry.ts +72 -0
  87. package/src/package-finalizer.ts +394 -0
  88. package/src/resolve-asset-source.ts +2 -7
  89. package/src/runtime-publication.ts +190 -0
  90. package/src/runtime.ts +23 -0
  91. package/src/scanner.ts +289 -290
  92. package/src/scriptable-pack-node.ts +27 -23
  93. package/src/scriptable-pack-worker.ts +2 -0
  94. package/src/scriptable-pack.ts +123 -1
  95. package/src/topology.ts +2 -1
@@ -0,0 +1,167 @@
1
+ import { mkdtemp, rm, writeFile } from 'node:fs/promises';
2
+ import { tmpdir } from 'node:os';
3
+ import { join } from 'node:path';
4
+ import { afterEach, describe, expect, it } from 'vitest';
5
+ import { AssetGuid } from '../guid.js';
6
+ import { scan, scanInventory } from '../scanner.js';
7
+
8
+ const GUID = '019e3969-1d48-7c3b-ac24-6d68f457065f';
9
+
10
+ function guid(value: string): Uint8Array {
11
+ const parsed = AssetGuid.parse(value);
12
+ if (!parsed.ok) throw parsed.error;
13
+ return parsed.value;
14
+ }
15
+
16
+ describe('engine-pack scanner inventory contract', () => {
17
+ const roots: string[] = [];
18
+
19
+ afterEach(async () => {
20
+ await Promise.all(roots.splice(0).map((root) => rm(root, { recursive: true, force: true })));
21
+ });
22
+
23
+ it('returns one inventory declaration with source identity before finalization', async () => {
24
+ const root = await mkdtemp(join(tmpdir(), 'forgeax-pack-inventory-'));
25
+ roots.push(root);
26
+ await writeFile(
27
+ join(root, 'hero.pack.json'),
28
+ JSON.stringify({
29
+ schemaVersion: '2.0.0',
30
+ kind: 'internal-text-package',
31
+ assets: [
32
+ {
33
+ guid: GUID,
34
+ kind: 'texture',
35
+ payload: { kind: 'texture', width: 1, height: 1 },
36
+ refs: [],
37
+ sourceKey: 'hero/albedo',
38
+ sourceIndex: 0,
39
+ },
40
+ ],
41
+ }),
42
+ );
43
+
44
+ const result = await scanInventory([root]);
45
+
46
+ expect(result.ok).toBe(true);
47
+ if (!result.ok) return;
48
+ expect(result.value.inventory).toHaveLength(1);
49
+ expect(result.value.inventory[0]).toMatchObject({
50
+ guid: GUID,
51
+ sourceKey: 'hero/albedo',
52
+ sourceIndex: 0,
53
+ });
54
+ });
55
+
56
+ it('returns the parsed Meta, Pack, and ScriptablePack declarations from one scan pass', async () => {
57
+ const root = await mkdtemp(join(tmpdir(), 'forgeax-pack-declarations-'));
58
+ roots.push(root);
59
+
60
+ const metaPath = join(root, 'hero.meta.json');
61
+ const packPath = join(root, 'environment.pack.json');
62
+ const scriptablePath = join(root, 'effects.pack.ts');
63
+ const meta = {
64
+ schemaVersion: '1.0.0',
65
+ kind: 'external-asset-package',
66
+ importer: 'image',
67
+ source: 'hero.png',
68
+ importSettings: {},
69
+ subAssets: [
70
+ { guid: '019e3969-1d48-7c3b-ac24-6d68f4570660', sourceIndex: 0, kind: 'texture' },
71
+ ],
72
+ } as const;
73
+ const pack = {
74
+ schemaVersion: '2.0.0',
75
+ kind: 'internal-text-package',
76
+ assets: [
77
+ {
78
+ guid: '019e3969-1d48-7c3b-ac24-6d68f4570661',
79
+ kind: 'texture',
80
+ payload: { kind: 'texture', width: 1, height: 1 },
81
+ refs: [],
82
+ },
83
+ ],
84
+ } as const;
85
+ await writeFile(join(root, 'hero.png'), 'source');
86
+ await writeFile(metaPath, JSON.stringify(meta));
87
+ await writeFile(packPath, JSON.stringify(pack));
88
+ await writeFile(scriptablePath, '// scanner fixture');
89
+
90
+ const scriptablePackageId = guid('019e3969-1d48-7c3b-ac24-6d68f4570662');
91
+ const scriptableAssetGuid = guid('019e3969-1d48-7c3b-ac24-6d68f4570663');
92
+ const result = await scanInventory([root], {
93
+ scriptablePack: {
94
+ executor: {
95
+ load: async () => ({
96
+ default: {
97
+ schemaVersion: '1.0.0',
98
+ packageId: scriptablePackageId,
99
+ assets: { effect: { guid: scriptableAssetGuid, kind: 'scene' } },
100
+ externalAssets: {},
101
+ build: () => ({ ok: true, value: {} }),
102
+ },
103
+ }),
104
+ },
105
+ },
106
+ });
107
+
108
+ expect(result.ok).toBe(true);
109
+ if (!result.ok) return;
110
+ expect(result.value.declarations.size).toBe(3);
111
+ expect(result.value.declarations.get(metaPath)).toMatchObject({
112
+ format: 'meta.json',
113
+ sourcePath: metaPath,
114
+ value: meta,
115
+ });
116
+ expect(result.value.declarations.get(packPath)).toMatchObject({
117
+ format: 'pack.json',
118
+ sourcePath: packPath,
119
+ value: pack,
120
+ });
121
+ const scriptableDeclaration = result.value.declarations.get(scriptablePath);
122
+ expect(scriptableDeclaration).toMatchObject({
123
+ format: 'pack.ts',
124
+ sourcePath: scriptablePath,
125
+ value: {
126
+ importer: 'pack-ts',
127
+ packageId: '019e3969-1d48-7c3b-ac24-6d68f4570662',
128
+ subAssets: [{ sourceKey: 'effect', kind: 'scene' }],
129
+ },
130
+ });
131
+ expect(
132
+ scriptableDeclaration?.format === 'pack.ts' ? scriptableDeclaration.meta : undefined,
133
+ ).toBe(scriptableDeclaration?.format === 'pack.ts' ? scriptableDeclaration.value : undefined);
134
+ });
135
+
136
+ it('releases the ScriptablePack loader for the path-only scan contract', async () => {
137
+ const root = await mkdtemp(join(tmpdir(), 'forgeax-pack-scan-release-'));
138
+ roots.push(root);
139
+ const scriptablePath = join(root, 'effects.pack.ts');
140
+ await writeFile(scriptablePath, '// scanner fixture');
141
+
142
+ const packageId = guid('019e3969-1d48-7c3b-ac24-6d68f4570664');
143
+ const assetGuid = guid('019e3969-1d48-7c3b-ac24-6d68f4570665');
144
+ let disposals = 0;
145
+ const result = await scan([root], {
146
+ scriptablePack: {
147
+ executor: {
148
+ load: async () => ({
149
+ default: {
150
+ schemaVersion: '1.0.0',
151
+ packageId,
152
+ assets: { effect: { guid: assetGuid, kind: 'scene' } },
153
+ externalAssets: {},
154
+ build: () => ({ ok: true, value: {} }),
155
+ },
156
+ }),
157
+ dispose: async () => {
158
+ disposals += 1;
159
+ },
160
+ },
161
+ },
162
+ });
163
+
164
+ expect(result.ok).toBe(true);
165
+ expect(disposals).toBe(1);
166
+ });
167
+ });
@@ -44,28 +44,32 @@ describe('AssetInventory', () => {
44
44
 
45
45
  it('projects the ScriptablePack declaration and owner outputs', () => {
46
46
  const { inventory, sourcePath } = getFixture();
47
- const declaration = inventory.declarationsByPath.get(sourcePath);
47
+ const declaration = inventory.declarations.get(sourcePath);
48
48
  expect(declaration).toMatchObject({
49
+ format: 'pack.ts',
49
50
  sourcePath,
50
- producer: 'scriptable-pack',
51
- outputs: [{ guid, sourceKey: 'mesh', kind: 'mesh', refs: [] }],
52
- externalGuids: [],
53
- diagnostics: [],
51
+ meta: {
52
+ importer: 'pack-ts',
53
+ subAssets: [{ guid, sourceKey: 'mesh', kind: 'mesh' }],
54
+ },
54
55
  });
55
56
  });
56
57
 
57
58
  it('projects source closure evidence', async () => {
58
59
  const { inventory, sourcePath } = getFixture();
59
- const declaration = inventory.declarationsByPath.get(sourcePath);
60
- expect(declaration?.sourceClosure?.map((entry) => entry.path)).toEqual([
61
- await realpath(sourcePath),
62
- ]);
60
+ const declaration = inventory.declarations.get(sourcePath);
61
+ expect(
62
+ declaration?.format === 'pack.ts'
63
+ ? declaration.sourceClosure.map((entry) => entry.path)
64
+ : undefined,
65
+ ).toEqual([await realpath(sourcePath)]);
63
66
  });
64
67
 
65
- it('indexes the output GUID and ScriptablePack metadata', () => {
68
+ it('indexes the ScriptablePack declaration without a second metadata registry', () => {
66
69
  const { inventory, sourcePath } = getFixture();
67
- expect(inventory.sourceByGuid.get(guid)).toBe(sourcePath);
68
- expect(inventory.scriptablePackMeta.get(sourcePath)?.subAssets).toEqual([
70
+ const declaration = inventory.declarations.get(sourcePath);
71
+ expect(inventory.inventory).toEqual([]);
72
+ expect(declaration?.format === 'pack.ts' ? declaration.meta.subAssets : undefined).toEqual([
69
73
  { guid, sourceIndex: 0, sourceKey: 'mesh', kind: 'mesh' },
70
74
  ]);
71
75
  });
@@ -112,6 +112,48 @@ describe('ScriptablePack definition and Meta projection', () => {
112
112
  ]);
113
113
  });
114
114
 
115
+ it('freezes component-token schemas as an ECS-neutral scene projection', () => {
116
+ const source = definition();
117
+ const input = {
118
+ ...source,
119
+ sceneComponents: [
120
+ {
121
+ name: 'MeshFilter',
122
+ fields: { assetHandle: { type: 'shared<MeshAsset>', default: 0 } },
123
+ },
124
+ ],
125
+ };
126
+ const validated = validateScriptablePackDefinition(input, 'scene-components.pack.ts');
127
+ expect(validated.ok).toBe(true);
128
+ if (!validated.ok) return;
129
+
130
+ expect(validated.value.sceneComponents).toEqual([
131
+ { name: 'MeshFilter', fields: { assetHandle: 'shared<MeshAsset>' } },
132
+ ]);
133
+ expect(validated.value.sceneComponents?.[0]).not.toBe(input.sceneComponents[0]);
134
+ });
135
+
136
+ it('rejects duplicate scene component names before build', () => {
137
+ const source = definition();
138
+ const validated = validateScriptablePackDefinition(
139
+ {
140
+ ...source,
141
+ sceneComponents: [
142
+ { name: 'MeshFilter', fields: {} },
143
+ { name: 'MeshFilter', fields: {} },
144
+ ],
145
+ },
146
+ 'duplicate-scene-components.pack.ts',
147
+ );
148
+ expect(validated).toMatchObject({
149
+ ok: false,
150
+ error: {
151
+ code: 'pack-source-definition-invalid',
152
+ detail: { propertyPath: '$.sceneComponents[1].name' },
153
+ },
154
+ });
155
+ });
156
+
115
157
  it('rejects duplicate local GUIDs before build', () => {
116
158
  const source = definition();
117
159
  const duplicate = {
@@ -82,6 +82,24 @@ export type ShelfPackOutcome =
82
82
  | { readonly ok: true; readonly value: ShelfPackResult }
83
83
  | { readonly ok: false; readonly error: ShelfPackError };
84
84
 
85
+ function sizeExceeded(
86
+ image: AtlasImageInput,
87
+ maxAtlasSize: number,
88
+ ): { readonly ok: false; readonly error: ShelfPackError } {
89
+ return {
90
+ ok: false,
91
+ error: {
92
+ code: 'atlas-size-exceeded',
93
+ detail: {
94
+ name: image.name,
95
+ width: image.width,
96
+ height: image.height,
97
+ maxAtlasSize,
98
+ },
99
+ },
100
+ };
101
+ }
102
+
85
103
  function nextPow2(n: number): number {
86
104
  if (n <= 1) return 1;
87
105
  let p = 1;
@@ -104,18 +122,7 @@ export function shelfPack(
104
122
 
105
123
  for (const img of images) {
106
124
  if (img.width > maxAtlasSize || img.height > maxAtlasSize) {
107
- return {
108
- ok: false,
109
- error: {
110
- code: 'atlas-size-exceeded',
111
- detail: {
112
- name: img.name,
113
- width: img.width,
114
- height: img.height,
115
- maxAtlasSize,
116
- },
117
- },
118
- };
125
+ return sizeExceeded(img, maxAtlasSize);
119
126
  }
120
127
  }
121
128
 
@@ -148,18 +155,7 @@ export function shelfPack(
148
155
  currentShelfHeight = img.height;
149
156
  }
150
157
  if (cursorY + img.height > maxAtlasSize) {
151
- return {
152
- ok: false,
153
- error: {
154
- code: 'atlas-size-exceeded',
155
- detail: {
156
- name: img.name,
157
- width: img.width,
158
- height: img.height,
159
- maxAtlasSize,
160
- },
161
- },
162
- };
158
+ return sizeExceeded(img, maxAtlasSize);
163
159
  }
164
160
  regions.push({ name: img.name, x: cursorX, y: cursorY, w: img.width, h: img.height });
165
161
  cursorX += img.width;
package/src/build.ts ADDED
@@ -0,0 +1,137 @@
1
+ // @forgeax/engine-pack/build
2
+ // Node-only catalog, source scanning, package finalization, and build evidence.
3
+
4
+ export type {
5
+ AnimationClip,
6
+ AnimationGraph,
7
+ Asset,
8
+ AudioClipAsset,
9
+ EquirectAsset,
10
+ FontAsset,
11
+ MaterialAsset,
12
+ MeshAsset,
13
+ PackV2,
14
+ PackV2Error,
15
+ ParticleEffectAsset,
16
+ RenderPipelineAsset,
17
+ SamplerAsset,
18
+ SceneAsset,
19
+ SkeletonAsset,
20
+ SkinAsset,
21
+ TextureAsset,
22
+ TilesetAsset,
23
+ VideoAsset,
24
+ } from '@forgeax/engine-types';
25
+ export type { ArtifactPathContext } from './artifact-path.js';
26
+ export { validateArtifactPath } from './artifact-path.js';
27
+ export {
28
+ buildCatalogProjection,
29
+ type CatalogAuthority,
30
+ type CatalogBuildError,
31
+ type CatalogBuildErrorCode,
32
+ type CatalogBuildProjectionOptions,
33
+ type CatalogBuildResult,
34
+ type CatalogImporterPolicy,
35
+ type CatalogProducerVisibility,
36
+ metaPathForGuid,
37
+ } from './catalog-builder.js';
38
+ export { calculateCatalogDelta } from './catalog-delta.js';
39
+ export {
40
+ type CatalogOutputDeclaration,
41
+ type CatalogProducerMeta,
42
+ type CookedPackageProjection,
43
+ catalogProjectionFor,
44
+ currentProjectionFor,
45
+ findReservedAssetKindConflict,
46
+ type PackageCatalogInput,
47
+ projectCookedPackageEntry,
48
+ projectExternalCatalogEntries,
49
+ projectPackageCatalog,
50
+ projectRuntimeCatalogRow,
51
+ type RuntimeCatalogRowInput,
52
+ } from './catalog-projection.js';
53
+ export {
54
+ type CookedMaterialRecord,
55
+ collectMaterialCookRefs,
56
+ createMaterialArtifactDigest,
57
+ type MaterialCookArtifact,
58
+ type MaterialCookIdentityExpectation,
59
+ type MaterialCookReceipt,
60
+ type MaterialCookRecordError,
61
+ type MaterialCookRefs,
62
+ projectCookedMaterialRecord,
63
+ serializeCookedMaterialRecord,
64
+ serializeMaterialCookReceipt,
65
+ validateCookedMaterialRecord,
66
+ validateMaterialCookReceipt,
67
+ } from './evidence/material-cook.js';
68
+ export { buildOfflineAssetEvidence, packageVerification } from './evidence/offline-evidence.js';
69
+ export {
70
+ decodeMeshBinHeader,
71
+ MESH_BIN_DIGEST_BYTES,
72
+ MESH_BIN_HEADER_V4_BYTES,
73
+ MESH_BIN_PROJECTION_VERSION,
74
+ MESH_BIN_VERSION,
75
+ type MeshBinContractError,
76
+ type MeshBinHeaderResult,
77
+ type MeshBinHeaderV4,
78
+ writeMeshBinHeader,
79
+ } from './mesh-bin-contract.js';
80
+ export {
81
+ type AuthoredPackAssetInput,
82
+ type AuthoredPackInput,
83
+ type FinalizedPackageProduct,
84
+ finalizePackageProduct,
85
+ finalizePackageTransportSource,
86
+ type PackageArtifactBody,
87
+ type PackageFinalizePolicy,
88
+ type PackageFinalizeResult,
89
+ type PackageFinalizerError,
90
+ type PackageProduct,
91
+ type PackageProductAsset,
92
+ packageTransportRevision,
93
+ upgradeLegacyAuthoredPack,
94
+ } from './package-finalizer.js';
95
+ export { validateProducerContract, validateProducerOutputs } from './producer-contract.js';
96
+ export { parsePackV2, validateMeta, validatePack, validatePackV2 } from './runtime.js';
97
+ export {
98
+ createRuntimePackPublication,
99
+ type RuntimePackAssetInput,
100
+ type RuntimePackEnvelope,
101
+ type RuntimePackInput,
102
+ type RuntimePackPublication,
103
+ type RuntimePackPublicationInput,
104
+ } from './runtime-publication.js';
105
+ export {
106
+ type InventoryDeclaration,
107
+ type ScanInventory,
108
+ type ScanOptions,
109
+ type ScriptablePackInventoryDeclaration,
110
+ type ScriptablePackScanOptions,
111
+ STANDARD_SCRIPTABLE_PACK_SCAN_OPTIONS,
112
+ scanInventory,
113
+ } from './scanner.js';
114
+ export {
115
+ type AssetReader,
116
+ isScriptablePackAssetKind,
117
+ projectScriptablePackMeta,
118
+ projectScriptablePackSceneComponents,
119
+ SCRIPTABLE_PACK_ASSET_KINDS,
120
+ SCRIPTABLE_PACK_CAPABILITY_MANIFEST,
121
+ type ScriptablePackAssetDeclaration,
122
+ type ScriptablePackAssetDeclarations,
123
+ type ScriptablePackAssetFor,
124
+ type ScriptablePackAssetKind,
125
+ type ScriptablePackDefinition,
126
+ type ScriptablePackError,
127
+ type ScriptablePackExternalAssets,
128
+ type ScriptablePackMetaJson,
129
+ type ScriptablePackOutputs,
130
+ type ScriptablePackPublicationEnvelope,
131
+ type ScriptablePackReadError,
132
+ type ScriptablePackSceneComponent,
133
+ type ScriptablePackSceneComponentInput,
134
+ type ScriptablePackSourceClosureEntry,
135
+ validateScriptablePackDefinition,
136
+ } from './scriptable-pack.js';
137
+ export { calculateTopologyDiff, diffTopology } from './topology.js';
package/src/builtin.ts CHANGED
@@ -77,3 +77,41 @@ export const BUILTIN_HANDLE_TRIANGLE = await deriveSync('HANDLE_TRIANGLE');
77
77
  * Derived from: deriveBuiltin('HANDLE_QUAD') under FORGEAX_NAMESPACE.
78
78
  */
79
79
  export const BUILTIN_HANDLE_QUAD = await deriveSync('HANDLE_QUAD');
80
+
81
+ /** Pre-computed UUIDv5 for the Engine-owned sphere mesh descriptor. */
82
+ export const BUILTIN_HANDLE_SPHERE = await deriveSync('HANDLE_SPHERE');
83
+
84
+ /** Pre-computed UUIDv5 for the Engine-owned nine-slice quad descriptor. */
85
+ export const BUILTIN_HANDLE_NINESLICE_QUAD = await deriveSync('HANDLE_NINESLICE_QUAD');
86
+
87
+ /** Pre-computed UUIDv5 for the Engine-owned cylinder mesh descriptor. */
88
+ export const BUILTIN_HANDLE_CYLINDER = await deriveSync('HANDLE_CYLINDER');
89
+
90
+ export type BuiltinMeshGeometry =
91
+ | 'procedural-cube'
92
+ | 'procedural-triangle'
93
+ | 'procedural-quad'
94
+ | 'procedural-sphere'
95
+ | 'procedural-nine-slice-quad'
96
+ | 'procedural-cylinder';
97
+
98
+ /**
99
+ * The complete Engine-owned procedural mesh catalog.
100
+ *
101
+ * These are Pack descriptors, not runtime payloads: the Geometry decoder
102
+ * expands each `geometry` token into its validated MeshAsset. DevKit uses this
103
+ * list to materialize only the descriptors a standalone project does not
104
+ * already author, keeping the shipped game self-contained without duplicate
105
+ * GUID declarations.
106
+ */
107
+ export const BUILTIN_MESH_ASSETS: ReadonlyArray<{
108
+ readonly guid: string;
109
+ readonly geometry: BuiltinMeshGeometry;
110
+ }> = Object.freeze([
111
+ { guid: BUILTIN_HANDLE_CUBE, geometry: 'procedural-cube' },
112
+ { guid: BUILTIN_HANDLE_TRIANGLE, geometry: 'procedural-triangle' },
113
+ { guid: BUILTIN_HANDLE_QUAD, geometry: 'procedural-quad' },
114
+ { guid: BUILTIN_HANDLE_SPHERE, geometry: 'procedural-sphere' },
115
+ { guid: BUILTIN_HANDLE_NINESLICE_QUAD, geometry: 'procedural-nine-slice-quad' },
116
+ { guid: BUILTIN_HANDLE_CYLINDER, geometry: 'procedural-cylinder' },
117
+ ]);