@forgeax/engine-import 0.0.0-dev.8d955ade1c79
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.
- package/LICENSE +202 -0
- package/README.md +227 -0
- package/dist/.tsbuildinfo +1 -0
- package/dist/__tests__/decode-image-owner.test-d.d.ts +2 -0
- package/dist/__tests__/decode-image-owner.test-d.d.ts.map +1 -0
- package/dist/__tests__/import-contract-migration.test.d.ts +2 -0
- package/dist/__tests__/import-contract-migration.test.d.ts.map +1 -0
- package/dist/__tests__/import-dependencies.test.d.ts +2 -0
- package/dist/__tests__/import-dependencies.test.d.ts.map +1 -0
- package/dist/__tests__/import-finalizer-digest-retry.test.d.ts +2 -0
- package/dist/__tests__/import-finalizer-digest-retry.test.d.ts.map +1 -0
- package/dist/__tests__/import-local-artifacts.test.d.ts +2 -0
- package/dist/__tests__/import-local-artifacts.test.d.ts.map +1 -0
- package/dist/__tests__/import.unit.test.d.ts +2 -0
- package/dist/__tests__/import.unit.test.d.ts.map +1 -0
- package/dist/__tests__/mesh-bin-contract.test.d.ts +2 -0
- package/dist/__tests__/mesh-bin-contract.test.d.ts.map +1 -0
- package/dist/__tests__/mesh-bin-morph-v3.test.d.ts +2 -0
- package/dist/__tests__/mesh-bin-morph-v3.test.d.ts.map +1 -0
- package/dist/__tests__/mesh-bin.test.d.ts +2 -0
- package/dist/__tests__/mesh-bin.test.d.ts.map +1 -0
- package/dist/__tests__/owner-chain.integration.test.d.ts +2 -0
- package/dist/__tests__/owner-chain.integration.test.d.ts.map +1 -0
- package/dist/__tests__/scriptable-pack-host.unit.test.d.ts +2 -0
- package/dist/__tests__/scriptable-pack-host.unit.test.d.ts.map +1 -0
- package/dist/__tests__/scriptable-pack-output-producers.unit.test.d.ts +2 -0
- package/dist/__tests__/scriptable-pack-output-producers.unit.test.d.ts.map +1 -0
- package/dist/__tests__/scriptable-pack-product.contract.test.d.ts +2 -0
- package/dist/__tests__/scriptable-pack-product.contract.test.d.ts.map +1 -0
- package/dist/__tests__/scriptable-pack-production-producers.unit.test.d.ts +2 -0
- package/dist/__tests__/scriptable-pack-production-producers.unit.test.d.ts.map +1 -0
- package/dist/__tests__/scriptable-pack-staged-snapshot.unit.test.d.ts +2 -0
- package/dist/__tests__/scriptable-pack-staged-snapshot.unit.test.d.ts.map +1 -0
- package/dist/__tests__/scriptable-pack.integration.test.d.ts +2 -0
- package/dist/__tests__/scriptable-pack.integration.test.d.ts.map +1 -0
- package/dist/__tests__/source-overrides-import-context.unit.test.d.ts +2 -0
- package/dist/__tests__/source-overrides-import-context.unit.test.d.ts.map +1 -0
- package/dist/__tests__/source-package-publication.integration.test.d.ts +2 -0
- package/dist/__tests__/source-package-publication.integration.test.d.ts.map +1 -0
- package/dist/browser.d.ts +5 -0
- package/dist/browser.d.ts.map +1 -0
- package/dist/browser.mjs +782 -0
- package/dist/browser.mjs.map +1 -0
- package/dist/build-production.d.ts +48 -0
- package/dist/build-production.d.ts.map +1 -0
- package/dist/catalog-importer-policy.d.ts +13 -0
- package/dist/catalog-importer-policy.d.ts.map +1 -0
- package/dist/catalog-inventory.d.ts +4 -0
- package/dist/catalog-inventory.d.ts.map +1 -0
- package/dist/import-product.d.ts +27 -0
- package/dist/import-product.d.ts.map +1 -0
- package/dist/import-runner.d.ts +136 -0
- package/dist/import-runner.d.ts.map +1 -0
- package/dist/importer-registry.d.ts +48 -0
- package/dist/importer-registry.d.ts.map +1 -0
- package/dist/index.d.ts +19 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.mjs +2849 -0
- package/dist/index.mjs.map +1 -0
- package/dist/mesh-bin.d.ts +19 -0
- package/dist/mesh-bin.d.ts.map +1 -0
- package/dist/mesh-bin.mjs +182 -0
- package/dist/mesh-bin.mjs.map +1 -0
- package/dist/pack-projection.d.ts +5 -0
- package/dist/pack-projection.d.ts.map +1 -0
- package/dist/scriptable-pack-host.d.ts +72 -0
- package/dist/scriptable-pack-host.d.ts.map +1 -0
- package/dist/scriptable-pack-output-producers.d.ts +7 -0
- package/dist/scriptable-pack-output-producers.d.ts.map +1 -0
- package/dist/scriptable-pack-staged-snapshot.d.ts +21 -0
- package/dist/scriptable-pack-staged-snapshot.d.ts.map +1 -0
- package/dist/scriptable-pack.d.ts +141 -0
- package/dist/scriptable-pack.d.ts.map +1 -0
- package/dist/scriptable-source-package.d.ts +14 -0
- package/dist/scriptable-source-package.d.ts.map +1 -0
- package/dist/source-package-errors.d.ts +52 -0
- package/dist/source-package-errors.d.ts.map +1 -0
- package/dist/source-package-publication.d.ts +63 -0
- package/dist/source-package-publication.d.ts.map +1 -0
- package/dist/source-package.d.ts +60 -0
- package/dist/source-package.d.ts.map +1 -0
- package/package.json +69 -0
- package/src/__tests__/decode-image-owner.test-d.ts +53 -0
- package/src/__tests__/import-contract-migration.test.ts +223 -0
- package/src/__tests__/import-dependencies.test.ts +65 -0
- package/src/__tests__/import-finalizer-digest-retry.test.ts +113 -0
- package/src/__tests__/import-local-artifacts.test.ts +102 -0
- package/src/__tests__/import.unit.test.ts +429 -0
- package/src/__tests__/mesh-bin-contract.test.ts +95 -0
- package/src/__tests__/mesh-bin-morph-v3.test.ts +44 -0
- package/src/__tests__/mesh-bin.test.ts +102 -0
- package/src/__tests__/owner-chain.integration.test.ts +37 -0
- package/src/__tests__/scriptable-pack-host.unit.test.ts +86 -0
- package/src/__tests__/scriptable-pack-output-producers.unit.test.ts +190 -0
- package/src/__tests__/scriptable-pack-product.contract.test.ts +47 -0
- package/src/__tests__/scriptable-pack-production-producers.unit.test.ts +488 -0
- package/src/__tests__/scriptable-pack-staged-snapshot.unit.test.ts +290 -0
- package/src/__tests__/scriptable-pack.integration.test.ts +435 -0
- package/src/__tests__/source-overrides-import-context.unit.test.ts +92 -0
- package/src/__tests__/source-package-publication.integration.test.ts +75 -0
- package/src/browser.ts +14 -0
- package/src/build-production.ts +493 -0
- package/src/catalog-importer-policy.ts +58 -0
- package/src/catalog-inventory.ts +24 -0
- package/src/import-product.ts +163 -0
- package/src/import-runner.ts +749 -0
- package/src/importer-registry.ts +103 -0
- package/src/index.ts +175 -0
- package/src/mesh-bin.ts +229 -0
- package/src/pack-projection.ts +21 -0
- package/src/scriptable-pack-host.ts +544 -0
- package/src/scriptable-pack-output-producers.ts +456 -0
- package/src/scriptable-pack-staged-snapshot.ts +172 -0
- package/src/scriptable-pack.ts +626 -0
- package/src/scriptable-source-package.ts +88 -0
- package/src/source-package-errors.ts +201 -0
- package/src/source-package-publication.ts +326 -0
- package/src/source-package.ts +187 -0
|
@@ -0,0 +1,163 @@
|
|
|
1
|
+
import type {
|
|
2
|
+
ArtifactDescriptor,
|
|
3
|
+
AssetRef,
|
|
4
|
+
CookProduct,
|
|
5
|
+
CookReceipt,
|
|
6
|
+
ImportedArtifactBody,
|
|
7
|
+
ImportedAsset,
|
|
8
|
+
ImportProduct,
|
|
9
|
+
Result,
|
|
10
|
+
} from '@forgeax/engine-types';
|
|
11
|
+
import { err, ok } from '@forgeax/engine-types';
|
|
12
|
+
|
|
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;
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
export interface ImportAssetProduct<P = unknown> {
|
|
23
|
+
readonly payload: P;
|
|
24
|
+
readonly refs: readonly AssetRef[];
|
|
25
|
+
readonly artifacts: Readonly<Record<string, ImportedArtifactBody>>;
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
export interface ImportProductContractError {
|
|
29
|
+
readonly code: 'import-product-invalid';
|
|
30
|
+
readonly expected: string;
|
|
31
|
+
readonly hint: string;
|
|
32
|
+
readonly detail: { readonly field: string };
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
function invalidProduct(field: string): Result<never, ImportProductContractError> {
|
|
36
|
+
return err({
|
|
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',
|
|
40
|
+
detail: { field },
|
|
41
|
+
});
|
|
42
|
+
}
|
|
43
|
+
|
|
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');
|
|
53
|
+
}
|
|
54
|
+
if (!Array.isArray(input.receipts)) return invalidProduct('receipts');
|
|
55
|
+
if (!Array.isArray(input.diagnostics)) return invalidProduct('diagnostics');
|
|
56
|
+
return ok({ ...input });
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
async function sha256Hex(bytes: Uint8Array): Promise<string> {
|
|
60
|
+
const subtle = globalThis.crypto?.subtle;
|
|
61
|
+
if (subtle === undefined) throw new Error('Web Crypto API is required for importer digests');
|
|
62
|
+
const owned = new Uint8Array(bytes.byteLength);
|
|
63
|
+
owned.set(bytes);
|
|
64
|
+
const digest = await subtle.digest('SHA-256', owned.buffer);
|
|
65
|
+
return Array.from(new Uint8Array(digest), (byte) => byte.toString(16).padStart(2, '0')).join('');
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
async function artifactDigest(bytes: Uint8Array): Promise<string> {
|
|
69
|
+
return `sha256:${await sha256Hex(bytes)}`;
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
function concatBytes(chunks: readonly (Uint8Array | string)[]): Uint8Array {
|
|
73
|
+
const encoder = new TextEncoder();
|
|
74
|
+
const encoded = chunks.map((chunk) =>
|
|
75
|
+
typeof chunk === 'string' ? encoder.encode(chunk) : chunk,
|
|
76
|
+
);
|
|
77
|
+
const totalLength = encoded.reduce((total, chunk) => total + chunk.byteLength, 0);
|
|
78
|
+
const bytes = new Uint8Array(totalLength);
|
|
79
|
+
let offset = 0;
|
|
80
|
+
for (const chunk of encoded) {
|
|
81
|
+
bytes.set(chunk, offset);
|
|
82
|
+
offset += chunk.byteLength;
|
|
83
|
+
}
|
|
84
|
+
return bytes;
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
async function productDigest(
|
|
88
|
+
asset: ImportedAsset<unknown>,
|
|
89
|
+
artifacts: Readonly<Record<string, ArtifactDescriptor>>,
|
|
90
|
+
): Promise<string> {
|
|
91
|
+
const chunks: (Uint8Array | string)[] = [
|
|
92
|
+
JSON.stringify(projectImportedAssetPayload(asset)),
|
|
93
|
+
JSON.stringify(asset.refs.map((ref) => ref.guid)),
|
|
94
|
+
JSON.stringify(artifacts),
|
|
95
|
+
];
|
|
96
|
+
for (const [key, artifact] of Object.entries(asset.artifacts).sort(([left], [right]) =>
|
|
97
|
+
left.localeCompare(right),
|
|
98
|
+
)) {
|
|
99
|
+
chunks.push(key, artifact.bytes);
|
|
100
|
+
}
|
|
101
|
+
return `sha256:${await sha256Hex(concatBytes(chunks))}`;
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
async function artifactDescriptors(
|
|
105
|
+
artifacts: Readonly<Record<string, ImportedArtifactBody>>,
|
|
106
|
+
): Promise<Readonly<Record<string, ArtifactDescriptor>>> {
|
|
107
|
+
const entries: [string, ArtifactDescriptor][] = [];
|
|
108
|
+
for (const [path, artifact] of Object.entries(artifacts)) {
|
|
109
|
+
entries.push([
|
|
110
|
+
path,
|
|
111
|
+
{
|
|
112
|
+
path,
|
|
113
|
+
mediaType: artifact.mediaType,
|
|
114
|
+
byteLength: artifact.bytes.byteLength,
|
|
115
|
+
integrity: { algorithm: 'sha256' as const, digest: await artifactDigest(artifact.bytes) },
|
|
116
|
+
...(artifact.assetCodec === undefined ? {} : { assetCodec: artifact.assetCodec }),
|
|
117
|
+
},
|
|
118
|
+
]);
|
|
119
|
+
}
|
|
120
|
+
return Object.fromEntries(entries);
|
|
121
|
+
}
|
|
122
|
+
|
|
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;
|
|
128
|
+
if (asset.kind === 'mesh') return { kind: 'mesh' };
|
|
129
|
+
if (asset.kind === 'texture' || asset.kind === 'equirect') {
|
|
130
|
+
const { data: _runtimeBytes, ...metadata } = payload;
|
|
131
|
+
return metadata;
|
|
132
|
+
}
|
|
133
|
+
return payload;
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
/** Convert each importer asset into the shared completed producer product. */
|
|
137
|
+
export function finalizeImportProducts(
|
|
138
|
+
product: ImportProduct<unknown>,
|
|
139
|
+
inputFingerprint: string,
|
|
140
|
+
): Promise<readonly CookProduct[]> {
|
|
141
|
+
return (async () => {
|
|
142
|
+
const products: CookProduct[] = [];
|
|
143
|
+
for (const asset of product.assets) {
|
|
144
|
+
const artifacts = await artifactDescriptors(asset.artifacts);
|
|
145
|
+
const digest = await productDigest(asset, artifacts);
|
|
146
|
+
products.push({
|
|
147
|
+
guid: asset.guid,
|
|
148
|
+
payload: asset.payload,
|
|
149
|
+
refs: asset.refs.map((ref) => ref.guid),
|
|
150
|
+
artifacts,
|
|
151
|
+
digest,
|
|
152
|
+
receipt: {
|
|
153
|
+
guid: asset.guid,
|
|
154
|
+
origin: 'sourceMeta' as const,
|
|
155
|
+
status: 'succeeded' as const,
|
|
156
|
+
inputFingerprint,
|
|
157
|
+
outputDigest: digest,
|
|
158
|
+
},
|
|
159
|
+
});
|
|
160
|
+
}
|
|
161
|
+
return products;
|
|
162
|
+
})();
|
|
163
|
+
}
|