@forgeax/engine-import 0.1.3 → 0.1.6

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 (79) hide show
  1. package/README.md +6 -5
  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__/scriptable-pack-host.unit.test.d.ts +2 -0
  6. package/dist/__tests__/scriptable-pack-host.unit.test.d.ts.map +1 -0
  7. package/dist/__tests__/scriptable-pack-product.contract.test.d.ts +2 -0
  8. package/dist/__tests__/scriptable-pack-product.contract.test.d.ts.map +1 -0
  9. package/dist/browser.d.ts +5 -0
  10. package/dist/browser.d.ts.map +1 -0
  11. package/dist/browser.mjs +782 -0
  12. package/dist/browser.mjs.map +1 -0
  13. package/dist/build-production.d.ts +48 -0
  14. package/dist/build-production.d.ts.map +1 -0
  15. package/dist/catalog-importer-policy.d.ts +13 -0
  16. package/dist/catalog-importer-policy.d.ts.map +1 -0
  17. package/dist/catalog-inventory.d.ts +4 -0
  18. package/dist/catalog-inventory.d.ts.map +1 -0
  19. package/dist/import-product.d.ts +16 -25
  20. package/dist/import-product.d.ts.map +1 -1
  21. package/dist/import-runner.d.ts +6 -3
  22. package/dist/import-runner.d.ts.map +1 -1
  23. package/dist/importer-registry.d.ts +9 -1
  24. package/dist/importer-registry.d.ts.map +1 -1
  25. package/dist/index.d.ts +11 -2
  26. package/dist/index.d.ts.map +1 -1
  27. package/dist/index.mjs +1380 -155
  28. package/dist/index.mjs.map +1 -1
  29. package/dist/mesh-bin.d.ts +1 -1
  30. package/dist/mesh-bin.d.ts.map +1 -1
  31. package/dist/mesh-bin.mjs +182 -0
  32. package/dist/mesh-bin.mjs.map +1 -0
  33. package/dist/pack-projection.d.ts +5 -0
  34. package/dist/pack-projection.d.ts.map +1 -0
  35. package/dist/scriptable-pack-host.d.ts +72 -0
  36. package/dist/scriptable-pack-host.d.ts.map +1 -0
  37. package/dist/scriptable-pack-output-producers.d.ts +3 -2
  38. package/dist/scriptable-pack-output-producers.d.ts.map +1 -1
  39. package/dist/scriptable-pack-staged-snapshot.d.ts +1 -2
  40. package/dist/scriptable-pack-staged-snapshot.d.ts.map +1 -1
  41. package/dist/scriptable-pack.d.ts +6 -18
  42. package/dist/scriptable-pack.d.ts.map +1 -1
  43. package/dist/scriptable-source-package.d.ts +14 -0
  44. package/dist/scriptable-source-package.d.ts.map +1 -0
  45. package/dist/source-package-errors.d.ts +52 -0
  46. package/dist/source-package-errors.d.ts.map +1 -0
  47. package/dist/source-package-publication.d.ts +63 -0
  48. package/dist/source-package-publication.d.ts.map +1 -0
  49. package/dist/source-package.d.ts +60 -0
  50. package/dist/source-package.d.ts.map +1 -0
  51. package/package.json +13 -7
  52. package/src/__tests__/import-contract-migration.test.ts +33 -0
  53. package/src/__tests__/import-local-artifacts.test.ts +3 -1
  54. package/src/__tests__/owner-chain.integration.test.ts +37 -0
  55. package/src/__tests__/scriptable-pack-host.unit.test.ts +86 -0
  56. package/src/__tests__/scriptable-pack-product.contract.test.ts +47 -0
  57. package/src/__tests__/scriptable-pack-production-producers.unit.test.ts +78 -19
  58. package/src/__tests__/scriptable-pack-staged-snapshot.unit.test.ts +52 -20
  59. package/src/browser.ts +14 -0
  60. package/src/build-production.ts +493 -0
  61. package/src/catalog-importer-policy.ts +58 -0
  62. package/src/catalog-inventory.ts +24 -0
  63. package/src/import-product.ts +37 -69
  64. package/src/import-runner.ts +36 -7
  65. package/src/importer-registry.ts +24 -1
  66. package/src/index.ts +80 -8
  67. package/src/mesh-bin.ts +2 -2
  68. package/src/pack-projection.ts +21 -0
  69. package/src/scriptable-pack-host.ts +544 -0
  70. package/src/scriptable-pack-output-producers.ts +48 -58
  71. package/src/scriptable-pack-staged-snapshot.ts +17 -6
  72. package/src/scriptable-pack.ts +37 -26
  73. package/src/scriptable-source-package.ts +88 -0
  74. package/src/source-package-errors.ts +201 -0
  75. package/src/source-package-publication.ts +295 -0
  76. package/src/source-package.ts +187 -0
  77. package/dist/__tests__/material-import-product.unit.test.d.ts +0 -2
  78. package/dist/__tests__/material-import-product.unit.test.d.ts.map +0 -1
  79. package/src/__tests__/material-import-product.unit.test.ts +0 -56
@@ -2,92 +2,58 @@ import type {
2
2
  ArtifactDescriptor,
3
3
  AssetRef,
4
4
  CookProduct,
5
+ CookReceipt,
5
6
  ImportedArtifactBody,
6
7
  ImportedAsset,
7
8
  ImportProduct,
8
- MaterialAsset,
9
9
  Result,
10
10
  } from '@forgeax/engine-types';
11
11
  import { err, ok } from '@forgeax/engine-types';
12
12
 
13
- export interface MaterialImportRefs {
14
- readonly parent: readonly string[];
15
- readonly textures: readonly string[];
16
- readonly samplers: readonly string[];
17
- readonly modules: readonly string[];
13
+ export interface TerminalImportProduct<P = unknown> extends ImportProduct<P> {
14
+ readonly refs: readonly AssetRef[];
15
+ readonly artifacts: Readonly<Record<string, ImportedArtifactBody>>;
16
+ readonly receipts: readonly CookReceipt[];
17
+ readonly diagnostics: readonly unknown[];
18
+ readonly sourceRevision: string;
19
+ readonly sourceKey?: string;
18
20
  }
19
21
 
20
- export interface MaterialSourceEvidence {
21
- readonly inputFingerprint: string;
22
- readonly importerVersion: string;
22
+ export interface ImportAssetProduct<P = unknown> {
23
+ readonly payload: P;
24
+ readonly refs: readonly AssetRef[];
25
+ readonly artifacts: Readonly<Record<string, ImportedArtifactBody>>;
23
26
  }
24
27
 
25
- export interface MaterialImportProductInput {
26
- readonly guid: string;
27
- readonly sourcePath: string;
28
- readonly material: MaterialAsset;
29
- readonly refs: MaterialImportRefs;
30
- readonly sourceEvidence: MaterialSourceEvidence;
31
- }
32
-
33
- export interface MaterialImportProduct {
34
- readonly asset: ImportedAsset<MaterialAsset>;
35
- readonly sourcePath: string;
36
- readonly sourceEvidence: MaterialSourceEvidence;
37
- }
38
-
39
- export interface MaterialImportProductError {
40
- readonly code: 'material-import-product-invalid';
28
+ export interface ImportProductContractError {
29
+ readonly code: 'import-product-invalid';
41
30
  readonly expected: string;
42
31
  readonly hint: string;
43
32
  readonly detail: { readonly field: string };
44
33
  }
45
34
 
46
- function invalid(field: string): Result<never, MaterialImportProductError> {
35
+ function invalidProduct(field: string): Result<never, ImportProductContractError> {
47
36
  return err({
48
- code: 'material-import-product-invalid',
49
- expected: 'an authored MaterialAsset with complete dependency references',
50
- hint: 'declare every material dependency before the product enters the cook pipeline',
37
+ code: 'import-product-invalid',
38
+ expected: 'a complete terminal import product with source identity',
39
+ hint: 'preserve refs, artifacts, receipts, diagnostics, and source revision at the product boundary',
51
40
  detail: { field },
52
41
  });
53
42
  }
54
43
 
55
- function distinct(values: readonly string[]): readonly string[] {
56
- return [...new Set(values)];
57
- }
58
-
59
- export function createMaterialImportProduct(
60
- input: MaterialImportProductInput,
61
- ): Result<MaterialImportProduct, MaterialImportProductError> {
62
- if (!input.guid || !input.sourcePath) return invalid('guid/sourcePath');
63
- if (input.material.kind !== 'material') return invalid('material.kind');
64
- if (!input.sourceEvidence.inputFingerprint || !input.sourceEvidence.importerVersion) {
65
- return invalid('sourceEvidence');
44
+ export function createImportProduct<P = unknown>(
45
+ input: TerminalImportProduct<P>,
46
+ ): Result<TerminalImportProduct<P>, ImportProductContractError> {
47
+ if (!Array.isArray(input.assets)) return invalidProduct('assets');
48
+ if (!Array.isArray(input.sourceDependencies)) return invalidProduct('sourceDependencies');
49
+ if (input.sourceRevision.trim().length === 0) return invalidProduct('sourceRevision');
50
+ if (!Array.isArray(input.refs)) return invalidProduct('refs');
51
+ if (input.artifacts === null || typeof input.artifacts !== 'object') {
52
+ return invalidProduct('artifacts');
66
53
  }
67
- const refs = distinct([
68
- ...input.refs.parent,
69
- ...input.refs.textures,
70
- ...input.refs.samplers,
71
- ...input.refs.modules,
72
- ]);
73
- return ok({
74
- sourcePath: input.sourcePath,
75
- sourceEvidence: input.sourceEvidence,
76
- asset: {
77
- guid: input.guid,
78
- kind: 'material',
79
- payload: input.material,
80
- refs: refs.map((guid): AssetRef => ({ guid })),
81
- artifacts: {},
82
- },
83
- });
84
- }
85
-
86
- export function materialImportProductReady(
87
- product: MaterialImportProduct,
88
- availableRefs: ReadonlySet<string>,
89
- ): boolean {
90
- return product.asset.refs.every((reference) => availableRefs.has(reference.guid));
54
+ if (!Array.isArray(input.receipts)) return invalidProduct('receipts');
55
+ if (!Array.isArray(input.diagnostics)) return invalidProduct('diagnostics');
56
+ return ok({ ...input });
91
57
  }
92
58
 
93
59
  async function sha256Hex(bytes: Uint8Array): Promise<string> {
@@ -123,7 +89,7 @@ async function productDigest(
123
89
  artifacts: Readonly<Record<string, ArtifactDescriptor>>,
124
90
  ): Promise<string> {
125
91
  const chunks: (Uint8Array | string)[] = [
126
- JSON.stringify(digestPayload(asset)),
92
+ JSON.stringify(projectImportedAssetPayload(asset)),
127
93
  JSON.stringify(asset.refs.map((ref) => ref.guid)),
128
94
  JSON.stringify(artifacts),
129
95
  ];
@@ -154,15 +120,17 @@ async function artifactDescriptors(
154
120
  return Object.fromEntries(entries);
155
121
  }
156
122
 
157
- function digestPayload(asset: ImportedAsset<unknown>): unknown {
158
- if (Object.keys(asset.artifacts).length === 0) return asset.payload;
123
+ export function projectImportedAssetPayload(
124
+ asset: ImportedAsset<unknown>,
125
+ ): Record<string, unknown> {
126
+ const payload = asset.payload as Record<string, unknown>;
127
+ if (Object.keys(asset.artifacts).length === 0) return payload;
159
128
  if (asset.kind === 'mesh') return { kind: 'mesh' };
160
129
  if (asset.kind === 'texture' || asset.kind === 'equirect') {
161
- const payload = asset.payload as Record<string, unknown>;
162
130
  const { data: _runtimeBytes, ...metadata } = payload;
163
131
  return metadata;
164
132
  }
165
- return asset.payload;
133
+ return payload;
166
134
  }
167
135
 
168
136
  /** Convert each importer asset into the shared completed producer product. */
@@ -40,7 +40,11 @@ import {
40
40
  ImportError,
41
41
  validateSourceOverrideMap,
42
42
  } from '@forgeax/engine-types';
43
- import { finalizeImportProducts } from './import-product.js';
43
+ import {
44
+ createImportProduct,
45
+ finalizeImportProducts,
46
+ type TerminalImportProduct,
47
+ } from './import-product.js';
44
48
  import type { ImporterRegistry } from './importer-registry.js';
45
49
 
46
50
  /** Reserved `meta.importer` key consumed by vite-plugin-shader, not the import runner. */
@@ -122,7 +126,7 @@ export type RunImportProductResult =
122
126
  readonly value:
123
127
  | { readonly skipped: 'shader' }
124
128
  | {
125
- readonly product: ImportProduct;
129
+ readonly product: TerminalImportProduct;
126
130
  readonly cookProducts: readonly CookProduct[];
127
131
  };
128
132
  }
@@ -136,7 +140,7 @@ export type RunImportProductResult =
136
140
  export type RunImportOk =
137
141
  | { readonly skipped: 'shader' }
138
142
  | {
139
- readonly product: ImportProduct;
143
+ readonly product: TerminalImportProduct;
140
144
  readonly cookProducts: readonly CookProduct[];
141
145
  readonly pack: DdcPack;
142
146
  };
@@ -166,6 +170,8 @@ export interface DdcPack {
166
170
  export interface RunImportMeta {
167
171
  readonly importer: string;
168
172
  readonly source: string;
173
+ /** Scanner-owned source declaration revision; avoids reopening validated Meta. */
174
+ readonly sourceRevision?: string;
169
175
  readonly packageId?: string;
170
176
  readonly provenance?: ProviderProvenance;
171
177
  readonly revision?: ResourceRevision;
@@ -669,10 +675,35 @@ export async function runImport(
669
675
  }),
670
676
  );
671
677
  }
678
+ const terminalProduct = createImportProduct({
679
+ ...productWithDependencies,
680
+ refs: productWithDependencies.assets.flatMap((asset) => asset.refs),
681
+ artifacts: Object.fromEntries(
682
+ productWithDependencies.assets.flatMap((asset) =>
683
+ Object.entries(asset.artifacts).map(([key, artifact]) => [
684
+ `${asset.guid}/${key}`,
685
+ artifact,
686
+ ]),
687
+ ),
688
+ ),
689
+ receipts: cookProducts.map((product) => product.receipt),
690
+ diagnostics: meta.diagnostics ?? [],
691
+ sourceRevision: inputFingerprint,
692
+ });
693
+ if (!terminalProduct.ok) {
694
+ return errResult(
695
+ new ImportError({
696
+ code: 'import-internal-error',
697
+ expected: 'the import product to retain complete terminal producer facts',
698
+ hint: 'preserve source identity and producer evidence when returning the import product',
699
+ detail: { reason: terminalProduct.error.detail.field },
700
+ }),
701
+ );
702
+ }
672
703
  if (meta.buildPack === false) {
673
704
  return {
674
705
  ok: true,
675
- value: { product: productWithDependencies, cookProducts },
706
+ value: { product: terminalProduct.value, cookProducts },
676
707
  };
677
708
  }
678
709
 
@@ -710,9 +741,7 @@ export async function runImport(
710
741
  return {
711
742
  ok: true,
712
743
  value: {
713
- product: {
714
- ...productWithDependencies,
715
- },
744
+ product: terminalProduct.value,
716
745
  cookProducts,
717
746
  pack,
718
747
  },
@@ -19,7 +19,7 @@
19
19
  // idempotent on a repeated key (last write wins, no throw) so re-wiring a
20
20
  // registry across build invocations is safe.
21
21
 
22
- import type { Importer } from '@forgeax/engine-types';
22
+ import type { Importer, ImporterCapabilities, ImportSubAsset } from '@forgeax/engine-types';
23
23
 
24
24
  /**
25
25
  * Injectable `meta.importer` -> {@link Importer} table held by the import
@@ -77,4 +77,27 @@ export class ImporterRegistry {
77
77
  registeredImporters(): readonly string[] {
78
78
  return [...this.importers.keys()];
79
79
  }
80
+
81
+ /** Project the first registered producer capability into the runner context. */
82
+ contextCapabilities(): ImporterCapabilities {
83
+ for (const importer of this.importers.values()) {
84
+ const decoder = importer.capabilities?.decodeImage;
85
+ if (decoder !== undefined) return { decodeImage: decoder };
86
+ }
87
+ return {};
88
+ }
89
+
90
+ /** Ask the registered producer whether a declaration has a Catalog product. */
91
+ shouldPublishCatalog(input: {
92
+ readonly importer: string;
93
+ readonly importSettings: Readonly<Record<string, unknown>>;
94
+ readonly subAssets: readonly ImportSubAsset[];
95
+ }): boolean {
96
+ return (
97
+ this.get(input.importer)?.capabilities?.catalog?.publish?.({
98
+ importSettings: input.importSettings,
99
+ subAssets: input.subAssets,
100
+ }) ?? true
101
+ );
102
+ }
80
103
  }
package/src/index.ts CHANGED
@@ -46,14 +46,26 @@ export {
46
46
  type ImportTransport,
47
47
  } from '@forgeax/engine-types';
48
48
  export {
49
- createMaterialImportProduct,
49
+ type BuildProductionFailure,
50
+ type BuildProductionFile,
51
+ type BuildProductionOptions,
52
+ type BuildProductionSink,
53
+ produceBuildAssets,
54
+ type ScriptableBuildPackage,
55
+ } from './build-production.js';
56
+ export {
57
+ type CatalogImporterDisposition,
58
+ type CatalogImporterPolicy,
59
+ catalogImporterPolicy,
60
+ DEFAULT_CATALOG_IMPORTER_KEYS,
61
+ } from './catalog-importer-policy.js';
62
+ export { buildCatalogResult } from './catalog-inventory.js';
63
+ export {
64
+ createImportProduct,
50
65
  finalizeImportProducts,
51
- type MaterialImportProduct,
52
- type MaterialImportProductError,
53
- type MaterialImportProductInput,
54
- type MaterialImportRefs,
55
- type MaterialSourceEvidence,
56
- materialImportProductReady,
66
+ type ImportAssetProduct,
67
+ type ImportProductContractError,
68
+ type TerminalImportProduct,
57
69
  } from './import-product.js';
58
70
  export {
59
71
  type DdcPack,
@@ -71,6 +83,7 @@ export {
71
83
  type MeshBinEncodeError,
72
84
  packMeshBinV4,
73
85
  } from './mesh-bin.js';
86
+ export { projectImportProductForBuild } from './pack-projection.js';
74
87
  export {
75
88
  type AssetOutputInput,
76
89
  type AssetOutputPayload,
@@ -90,10 +103,29 @@ export {
90
103
  type ScriptablePackStagedOutput,
91
104
  } from './scriptable-pack.js';
92
105
  export {
106
+ type AuthoredPackTransport,
107
+ type CookedAuthoredPack,
108
+ canonicalScriptableSourcePath,
109
+ declaredPackExternalOutputs,
110
+ materializePreparedScriptablePack,
111
+ type PreparedScriptablePack,
112
+ prepareAuthoredPackTransport,
113
+ produceScriptablePackProducts,
114
+ projectScriptablePackPublication,
115
+ readCookedAuthoredPack,
116
+ type ScriptablePackExternalImportOptions,
117
+ type ScriptablePackInput,
118
+ type ScriptablePackPublicationFacts,
119
+ type ScriptablePackTransportPaths,
120
+ type ScriptablePackTransportPolicy,
121
+ type ScriptablePackTransportSink,
122
+ scriptablePackInputs,
123
+ } from './scriptable-pack-host.js';
124
+ export {
125
+ createSceneAssetOutputProducer,
93
126
  createStandardAssetOutputProducerRegistry,
94
127
  materialAssetOutputProducer,
95
128
  meshAssetOutputProducer,
96
- sceneAssetOutputProducer,
97
129
  } from './scriptable-pack-output-producers.js';
98
130
  export {
99
131
  createScriptablePackStagedAssetSnapshotSource,
@@ -101,3 +133,43 @@ export {
101
133
  type ScriptablePackStagedOwner,
102
134
  type ScriptablePackStagedSnapshotOptions,
103
135
  } from './scriptable-pack-staged-snapshot.js';
136
+ export {
137
+ produceScriptableSourcePackage,
138
+ type ScriptableSourcePackageProduct,
139
+ type ScriptableSourcePackageResult,
140
+ } from './scriptable-source-package.js';
141
+ export {
142
+ finalizeSourcePackage,
143
+ type ProducerReadiness,
144
+ type ProducerReadinessError,
145
+ type ProducerReadinessResult,
146
+ parseProducerReadiness,
147
+ produceSourcePackage,
148
+ type SourcePackageClosureDetail,
149
+ type SourcePackageClosureError,
150
+ type SourcePackageProducerInput,
151
+ type SourcePackageProducerResult,
152
+ type SourcePackageProduct,
153
+ sourcePackageAssetsByGuid,
154
+ } from './source-package.js';
155
+ export {
156
+ normalizeSourcePackageError,
157
+ type SourcePackageError,
158
+ type SourcePackageErrorCode,
159
+ type SourcePackageErrorContext,
160
+ type SourcePackageErrorDetail,
161
+ type SourcePackageErrorStage,
162
+ sourcePackageError,
163
+ } from './source-package-errors.js';
164
+ export {
165
+ commitImportPublication,
166
+ discardImportPublication,
167
+ type ImportPublicationError,
168
+ type ImportPublicationInput,
169
+ type ImportPublicationResult,
170
+ publishImportPublication,
171
+ restoreImportPublication,
172
+ type StagedImportPublication,
173
+ type StagedImportPublicationResult,
174
+ stageImportPublication,
175
+ } from './source-package-publication.js';
package/src/mesh-bin.ts CHANGED
@@ -1,11 +1,11 @@
1
1
  import { deriveVertexLayoutProjection } from '@forgeax/engine-geometry';
2
+ import { AssetGuid } from '@forgeax/engine-pack/guid';
2
3
  import {
3
4
  MESH_BIN_HEADER_V4_BYTES,
4
5
  type MeshBinContractError,
5
6
  type MeshBinHeaderV4,
6
7
  writeMeshBinHeader,
7
- } from '@forgeax/engine-pack';
8
- import { AssetGuid } from '@forgeax/engine-pack/guid';
8
+ } from '@forgeax/engine-pack/mesh-bin-contract';
9
9
  import {
10
10
  err,
11
11
  type MeshAsset,
@@ -0,0 +1,21 @@
1
+ import type { ImportProduct } from '@forgeax/engine-types';
2
+ import { projectImportedAssetPayload } from './import-product.js';
3
+ import { type DdcPack, normaliseForPack } from './import-runner.js';
4
+
5
+ /** Project one validated import product into the canonical Pack v2 payload. */
6
+ export function projectImportProductForBuild(
7
+ product: Pick<ImportProduct<unknown>, 'assets'>,
8
+ ): DdcPack {
9
+ return {
10
+ schemaVersion: '2.0.0',
11
+ kind: 'internal-text-package',
12
+ assets: product.assets.map((asset) => ({
13
+ guid: asset.guid,
14
+ kind: asset.kind,
15
+ ...(asset.name === undefined ? {} : { name: asset.name }),
16
+ payload: normaliseForPack(projectImportedAssetPayload(asset)) as Record<string, unknown>,
17
+ refs: asset.refs.map((reference) => reference.guid),
18
+ artifacts: asset.artifacts,
19
+ })),
20
+ };
21
+ }