@forgeax/engine-pack 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 +429 -0
- package/dist/.tsbuildinfo +1 -0
- package/dist/__tests__/ai-recovery.unit.test.d.ts +2 -0
- package/dist/__tests__/ai-recovery.unit.test.d.ts.map +1 -0
- package/dist/__tests__/artifact-path.test.d.ts +2 -0
- package/dist/__tests__/artifact-path.test.d.ts.map +1 -0
- package/dist/__tests__/asset-evidence-error-hints.test.d.ts +2 -0
- package/dist/__tests__/asset-evidence-error-hints.test.d.ts.map +1 -0
- package/dist/__tests__/asset-evidence-lifecycle.test.d.ts +2 -0
- package/dist/__tests__/asset-evidence-lifecycle.test.d.ts.map +1 -0
- package/dist/__tests__/asset-evidence-schema.test.d.ts +2 -0
- package/dist/__tests__/asset-evidence-schema.test.d.ts.map +1 -0
- package/dist/__tests__/atlas-cli-region-mismatch.test.d.ts +2 -0
- package/dist/__tests__/atlas-cli-region-mismatch.test.d.ts.map +1 -0
- package/dist/__tests__/atlas-error-code-owner.test-d.d.ts +2 -0
- package/dist/__tests__/atlas-error-code-owner.test-d.d.ts.map +1 -0
- package/dist/__tests__/cli-asset-evidence.integration.test.d.ts +2 -0
- package/dist/__tests__/cli-asset-evidence.integration.test.d.ts.map +1 -0
- package/dist/__tests__/cli.unit.test.d.ts +2 -0
- package/dist/__tests__/cli.unit.test.d.ts.map +1 -0
- package/dist/__tests__/evidence-source-inventory.unit.test.d.ts +2 -0
- package/dist/__tests__/evidence-source-inventory.unit.test.d.ts.map +1 -0
- package/dist/__tests__/guid-collision.unit.test.d.ts +2 -0
- package/dist/__tests__/guid-collision.unit.test.d.ts.map +1 -0
- package/dist/__tests__/guid-validator-surface.unit.test.d.ts +2 -0
- package/dist/__tests__/guid-validator-surface.unit.test.d.ts.map +1 -0
- package/dist/__tests__/load-asset-config.test.d.ts +2 -0
- package/dist/__tests__/load-asset-config.test.d.ts.map +1 -0
- package/dist/__tests__/material-cook-dependencies.unit.test.d.ts +2 -0
- package/dist/__tests__/material-cook-dependencies.unit.test.d.ts.map +1 -0
- package/dist/__tests__/material-cook-receipt.unit.test.d.ts +2 -0
- package/dist/__tests__/material-cook-receipt.unit.test.d.ts.map +1 -0
- package/dist/__tests__/material-cook-schema.unit.test.d.ts +2 -0
- package/dist/__tests__/material-cook-schema.unit.test.d.ts.map +1 -0
- package/dist/__tests__/mesh-bin-consumer-surface.unit.test.d.ts +2 -0
- package/dist/__tests__/mesh-bin-consumer-surface.unit.test.d.ts.map +1 -0
- package/dist/__tests__/mesh-bin-contract.unit.test.d.ts +2 -0
- package/dist/__tests__/mesh-bin-contract.unit.test.d.ts.map +1 -0
- package/dist/__tests__/meta-schema-optional.test.d.ts +2 -0
- package/dist/__tests__/meta-schema-optional.test.d.ts.map +1 -0
- package/dist/__tests__/meta-source-overrides.unit.test.d.ts +2 -0
- package/dist/__tests__/meta-source-overrides.unit.test.d.ts.map +1 -0
- package/dist/__tests__/native-cooker-registry.test.d.ts +2 -0
- package/dist/__tests__/native-cooker-registry.test.d.ts.map +1 -0
- package/dist/__tests__/offline-evidence-artifact-verification-owner.test-d.d.ts +2 -0
- package/dist/__tests__/offline-evidence-artifact-verification-owner.test-d.d.ts.map +1 -0
- package/dist/__tests__/offline-evidence.integration.test.d.ts +2 -0
- package/dist/__tests__/offline-evidence.integration.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__/pack-v2-fixture-migration.test.d.ts +2 -0
- package/dist/__tests__/pack-v2-fixture-migration.test.d.ts.map +1 -0
- package/dist/__tests__/pack-v2-schema.test.d.ts +2 -0
- package/dist/__tests__/pack-v2-schema.test.d.ts.map +1 -0
- package/dist/__tests__/pack.unit.test.d.ts +2 -0
- package/dist/__tests__/pack.unit.test.d.ts.map +1 -0
- package/dist/__tests__/package-finalizer.contract.test.d.ts +2 -0
- package/dist/__tests__/package-finalizer.contract.test.d.ts.map +1 -0
- package/dist/__tests__/producer-contract-schema.unit.test.d.ts +2 -0
- package/dist/__tests__/producer-contract-schema.unit.test.d.ts.map +1 -0
- package/dist/__tests__/resolve-asset-source.test.d.ts +2 -0
- package/dist/__tests__/resolve-asset-source.test.d.ts.map +1 -0
- package/dist/__tests__/runtime-projection.unit.test.d.ts +2 -0
- package/dist/__tests__/runtime-projection.unit.test.d.ts.map +1 -0
- package/dist/__tests__/runtime.browser.test.d.ts +2 -0
- package/dist/__tests__/runtime.browser.test.d.ts.map +1 -0
- package/dist/__tests__/scanner-inventory.contract.test.d.ts +2 -0
- package/dist/__tests__/scanner-inventory.contract.test.d.ts.map +1 -0
- package/dist/__tests__/scanner-inventory.test.d.ts +2 -0
- package/dist/__tests__/scanner-inventory.test.d.ts.map +1 -0
- package/dist/__tests__/scanner-mount-cycle.test.d.ts +2 -0
- package/dist/__tests__/scanner-mount-cycle.test.d.ts.map +1 -0
- package/dist/__tests__/scriptable-pack-cli.integration.test.d.ts +2 -0
- package/dist/__tests__/scriptable-pack-cli.integration.test.d.ts.map +1 -0
- package/dist/__tests__/scriptable-pack-ddc-key.unit.test.d.ts +2 -0
- package/dist/__tests__/scriptable-pack-ddc-key.unit.test.d.ts.map +1 -0
- package/dist/__tests__/scriptable-pack.test-d.d.ts +2 -0
- package/dist/__tests__/scriptable-pack.test-d.d.ts.map +1 -0
- package/dist/__tests__/scriptable-pack.unit.test.d.ts +2 -0
- package/dist/__tests__/scriptable-pack.unit.test.d.ts.map +1 -0
- package/dist/__tests__/topology-actions.unit.test.d.ts +2 -0
- package/dist/__tests__/topology-actions.unit.test.d.ts.map +1 -0
- package/dist/__tests__/topology.unit.test.d.ts +2 -0
- package/dist/__tests__/topology.unit.test.d.ts.map +1 -0
- package/dist/artifact-path.d.ts +9 -0
- package/dist/artifact-path.d.ts.map +1 -0
- package/dist/artifact-path.mjs +53 -0
- package/dist/artifact-path.mjs.map +1 -0
- package/dist/atlas/run-atlas.d.ts +8 -0
- package/dist/atlas/run-atlas.d.ts.map +1 -0
- package/dist/atlas/shelf-pack.d.ts +55 -0
- package/dist/atlas/shelf-pack.d.ts.map +1 -0
- package/dist/bridge.d.ts +2 -0
- package/dist/bridge.d.ts.map +1 -0
- package/dist/bridge.mjs +3 -0
- package/dist/bridge.mjs.map +1 -0
- package/dist/build.d.ts +19 -0
- package/dist/build.d.ts.map +1 -0
- package/dist/build.mjs +10203 -0
- package/dist/build.mjs.map +1 -0
- package/dist/builtin.d.ts +44 -0
- package/dist/builtin.d.ts.map +1 -0
- package/dist/builtin.mjs +141 -0
- package/dist/builtin.mjs.map +1 -0
- package/dist/catalog-builder.d.ts +43 -0
- package/dist/catalog-builder.d.ts.map +1 -0
- package/dist/catalog-delta.d.ts +11 -0
- package/dist/catalog-delta.d.ts.map +1 -0
- package/dist/catalog-projection.d.ts +91 -0
- package/dist/catalog-projection.d.ts.map +1 -0
- package/dist/cli-asset.d.ts +22 -0
- package/dist/cli-asset.d.ts.map +1 -0
- package/dist/cli-asset.mjs +9697 -0
- package/dist/cli-asset.mjs.map +1 -0
- package/dist/config.d.ts +6 -0
- package/dist/config.d.ts.map +1 -0
- package/dist/config.mjs +29 -0
- package/dist/config.mjs.map +1 -0
- package/dist/deriveAssetName.d.ts +15 -0
- package/dist/deriveAssetName.d.ts.map +1 -0
- package/dist/errors.d.ts +30 -0
- package/dist/errors.d.ts.map +1 -0
- package/dist/errors.mjs +19 -0
- package/dist/errors.mjs.map +1 -0
- package/dist/evidence/material-cook.d.ts +92 -0
- package/dist/evidence/material-cook.d.ts.map +1 -0
- package/dist/evidence/offline-evidence.d.ts +25 -0
- package/dist/evidence/offline-evidence.d.ts.map +1 -0
- package/dist/evidence/source-inventory.d.ts +29 -0
- package/dist/evidence/source-inventory.d.ts.map +1 -0
- package/dist/guid.d.ts +45 -0
- package/dist/guid.d.ts.map +1 -0
- package/dist/guid.mjs +94 -0
- package/dist/guid.mjs.map +1 -0
- package/dist/index.d.ts +15 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.mjs +8612 -0
- package/dist/index.mjs.map +1 -0
- package/dist/material/artifact-writer.d.ts +14 -0
- package/dist/material/artifact-writer.d.ts.map +1 -0
- package/dist/material-cook.d.ts +2 -0
- package/dist/material-cook.d.ts.map +1 -0
- package/dist/material-cook.mjs +248 -0
- package/dist/material-cook.mjs.map +1 -0
- package/dist/mesh-bin-contract.d.ts +36 -0
- package/dist/mesh-bin-contract.d.ts.map +1 -0
- package/dist/mesh-bin-contract.mjs +96 -0
- package/dist/mesh-bin-contract.mjs.map +1 -0
- package/dist/name.mjs +22 -0
- package/dist/name.mjs.map +1 -0
- package/dist/native-cooker-registry.d.ts +47 -0
- package/dist/native-cooker-registry.d.ts.map +1 -0
- package/dist/native-cooker.d.ts +3 -0
- package/dist/native-cooker.d.ts.map +1 -0
- package/dist/native-cooker.mjs +142 -0
- package/dist/native-cooker.mjs.map +1 -0
- package/dist/package-finalizer.d.ts +121 -0
- package/dist/package-finalizer.d.ts.map +1 -0
- package/dist/producer-contract.d.ts +19 -0
- package/dist/producer-contract.d.ts.map +1 -0
- package/dist/resolve-asset-source.d.ts +4 -0
- package/dist/resolve-asset-source.d.ts.map +1 -0
- package/dist/resolve-asset-source.mjs +109 -0
- package/dist/resolve-asset-source.mjs.map +1 -0
- package/dist/runtime-projection.d.ts +14 -0
- package/dist/runtime-projection.d.ts.map +1 -0
- package/dist/runtime-publication.d.ts +44 -0
- package/dist/runtime-publication.d.ts.map +1 -0
- package/dist/runtime.d.ts +8 -0
- package/dist/runtime.d.ts.map +1 -0
- package/dist/runtime.mjs +7261 -0
- package/dist/runtime.mjs.map +1 -0
- package/dist/scanner.d.ts +113 -0
- package/dist/scanner.d.ts.map +1 -0
- package/dist/scanner.mjs +8819 -0
- package/dist/scanner.mjs.map +1 -0
- package/dist/schema-compiled.d.ts +12 -0
- package/dist/schema-compiled.d.ts.map +1 -0
- package/dist/schema.mjs +7369 -0
- package/dist/schema.mjs.map +1 -0
- package/dist/scriptable-pack-node.d.ts +62 -0
- package/dist/scriptable-pack-node.d.ts.map +1 -0
- package/dist/scriptable-pack-node.mjs +1674 -0
- package/dist/scriptable-pack-node.mjs.map +1 -0
- package/dist/scriptable-pack-worker.d.ts +2 -0
- package/dist/scriptable-pack-worker.d.ts.map +1 -0
- package/dist/scriptable-pack-worker.mjs +224 -0
- package/dist/scriptable-pack-worker.mjs.map +1 -0
- package/dist/scriptable-pack.d.ts +558 -0
- package/dist/scriptable-pack.d.ts.map +1 -0
- package/dist/scriptable-pack.mjs +672 -0
- package/dist/scriptable-pack.mjs.map +1 -0
- package/dist/topology.d.ts +10 -0
- package/dist/topology.d.ts.map +1 -0
- package/package.json +154 -0
- package/schema/meta.schema.json +173 -0
- package/schema/pack.schema.json +232 -0
- package/src/__tests__/ai-recovery.unit.test.ts +114 -0
- package/src/__tests__/artifact-path.test.ts +41 -0
- package/src/__tests__/asset-evidence-error-hints.test.ts +46 -0
- package/src/__tests__/asset-evidence-lifecycle.test.ts +104 -0
- package/src/__tests__/asset-evidence-schema.test.ts +86 -0
- package/src/__tests__/atlas-cli-region-mismatch.test.ts +134 -0
- package/src/__tests__/atlas-error-code-owner.test-d.ts +23 -0
- package/src/__tests__/cli-asset-evidence.integration.test.ts +172 -0
- package/src/__tests__/cli.unit.test.ts +448 -0
- package/src/__tests__/evidence-source-inventory.unit.test.ts +37 -0
- package/src/__tests__/guid-collision.unit.test.ts +112 -0
- package/src/__tests__/guid-validator-surface.unit.test.ts +20 -0
- package/src/__tests__/load-asset-config.test.ts +121 -0
- package/src/__tests__/material-cook-dependencies.unit.test.ts +112 -0
- package/src/__tests__/material-cook-receipt.unit.test.ts +148 -0
- package/src/__tests__/material-cook-schema.unit.test.ts +152 -0
- package/src/__tests__/mesh-bin-consumer-surface.unit.test.ts +22 -0
- package/src/__tests__/mesh-bin-contract.unit.test.ts +32 -0
- package/src/__tests__/meta-schema-optional.test.ts +116 -0
- package/src/__tests__/meta-source-overrides.unit.test.ts +71 -0
- package/src/__tests__/native-cooker-registry.test.ts +64 -0
- package/src/__tests__/offline-evidence-artifact-verification-owner.test-d.ts +64 -0
- package/src/__tests__/offline-evidence.integration.test.ts +106 -0
- package/src/__tests__/owner-chain.integration.test.ts +78 -0
- package/src/__tests__/pack-v2-fixture-migration.test.ts +87 -0
- package/src/__tests__/pack-v2-schema.test.ts +142 -0
- package/src/__tests__/pack.unit.test.ts +2218 -0
- package/src/__tests__/package-finalizer.contract.test.ts +105 -0
- package/src/__tests__/producer-contract-schema.unit.test.ts +92 -0
- package/src/__tests__/resolve-asset-source.test.ts +121 -0
- package/src/__tests__/runtime-projection.unit.test.ts +87 -0
- package/src/__tests__/runtime.browser.test.ts +15 -0
- package/src/__tests__/scanner-inventory.contract.test.ts +167 -0
- package/src/__tests__/scanner-inventory.test.ts +76 -0
- package/src/__tests__/scanner-mount-cycle.test.ts +232 -0
- package/src/__tests__/scriptable-pack-cli.integration.test.ts +47 -0
- package/src/__tests__/scriptable-pack-ddc-key.unit.test.ts +63 -0
- package/src/__tests__/scriptable-pack.test-d.ts +131 -0
- package/src/__tests__/scriptable-pack.unit.test.ts +789 -0
- package/src/__tests__/topology-actions.unit.test.ts +94 -0
- package/src/__tests__/topology.unit.test.ts +85 -0
- package/src/artifact-path.ts +69 -0
- package/src/atlas/run-atlas.ts +358 -0
- package/src/atlas/shelf-pack.ts +172 -0
- package/src/atlas/upng-ambient.d.ts +13 -0
- package/src/bridge.ts +2 -0
- package/src/build.ts +139 -0
- package/src/builtin.ts +117 -0
- package/src/catalog-builder.ts +313 -0
- package/src/catalog-delta.ts +189 -0
- package/src/catalog-projection.ts +344 -0
- package/src/cli-asset.ts +586 -0
- package/src/config.ts +36 -0
- package/src/deriveAssetName.ts +42 -0
- package/src/errors.ts +47 -0
- package/src/evidence/material-cook.ts +411 -0
- package/src/evidence/offline-evidence.ts +90 -0
- package/src/evidence/source-inventory.ts +193 -0
- package/src/guid.ts +99 -0
- package/src/index.ts +108 -0
- package/src/material/artifact-writer.ts +26 -0
- package/src/material-cook.ts +15 -0
- package/src/mesh-bin-contract.ts +150 -0
- package/src/native-cooker-registry.ts +235 -0
- package/src/native-cooker.ts +2 -0
- package/src/package-finalizer.ts +394 -0
- package/src/producer-contract.ts +277 -0
- package/src/resolve-asset-source.ts +88 -0
- package/src/runtime-projection.ts +100 -0
- package/src/runtime-publication.ts +190 -0
- package/src/runtime.ts +23 -0
- package/src/scanner.ts +715 -0
- package/src/schema/material-cook.schema.json +119 -0
- package/src/schema-compiled.ts +238 -0
- package/src/scriptable-pack-node.ts +1394 -0
- package/src/scriptable-pack-worker.ts +308 -0
- package/src/scriptable-pack.ts +1006 -0
- package/src/topology.ts +157 -0
|
@@ -0,0 +1,193 @@
|
|
|
1
|
+
import { createHash } from 'node:crypto';
|
|
2
|
+
import { readdir, readFile } from 'node:fs/promises';
|
|
3
|
+
import { basename, dirname, join, relative, resolve, sep } from 'node:path';
|
|
4
|
+
import type { SourceDeclarationEvidence } from '@forgeax/engine-types';
|
|
5
|
+
import { SCANNER_BLACKLIST, scan } from '../scanner.js';
|
|
6
|
+
import { projectScriptablePackMeta } from '../scriptable-pack.js';
|
|
7
|
+
import { loadScriptablePack } from '../scriptable-pack-node.js';
|
|
8
|
+
|
|
9
|
+
/** Project root and GUID used to locate an authoritative source declaration. */
|
|
10
|
+
export interface SourceInventoryRequest {
|
|
11
|
+
readonly projectRoot: string;
|
|
12
|
+
readonly guid: string;
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
export interface ProducerSemanticIdentityInput {
|
|
16
|
+
readonly producerRoot: string;
|
|
17
|
+
readonly sourcePath: string;
|
|
18
|
+
readonly sourceDigest: string;
|
|
19
|
+
readonly schemaVersion: string;
|
|
20
|
+
readonly importer: string;
|
|
21
|
+
readonly codec: string;
|
|
22
|
+
readonly settings: unknown;
|
|
23
|
+
readonly producer: string;
|
|
24
|
+
readonly profile: string;
|
|
25
|
+
readonly declaredGuids?: readonly string[];
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
export interface ProducerRelativeDdcIdentity {
|
|
29
|
+
readonly logicalPath: string;
|
|
30
|
+
readonly sourceDigest: string;
|
|
31
|
+
readonly key: string;
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
function stableValue(value: unknown): unknown {
|
|
35
|
+
if (Array.isArray(value)) return value.map(stableValue);
|
|
36
|
+
if (value !== null && typeof value === 'object') {
|
|
37
|
+
return Object.fromEntries(
|
|
38
|
+
Object.entries(value as Record<string, unknown>)
|
|
39
|
+
.sort(([left], [right]) => left.localeCompare(right))
|
|
40
|
+
.map(([key, entry]) => [key, stableValue(entry)]),
|
|
41
|
+
);
|
|
42
|
+
}
|
|
43
|
+
return value;
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
export function producerRelativeLogicalPath(producerRoot: string, sourcePath: string): string {
|
|
47
|
+
const root = resolve(producerRoot);
|
|
48
|
+
const source = resolve(sourcePath);
|
|
49
|
+
const path = relative(root, source);
|
|
50
|
+
if (
|
|
51
|
+
path.length === 0 ||
|
|
52
|
+
path === '..' ||
|
|
53
|
+
path.startsWith(`..${sep}`) ||
|
|
54
|
+
resolve(root, path) !== source
|
|
55
|
+
) {
|
|
56
|
+
throw new Error('producer source must be inside the injected producer root');
|
|
57
|
+
}
|
|
58
|
+
return path.split(sep).join('/');
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
export function producerRelativeDdcKey(input: ProducerSemanticIdentityInput): string {
|
|
62
|
+
const logicalPath = producerRelativeLogicalPath(input.producerRoot, input.sourcePath);
|
|
63
|
+
const semantic = {
|
|
64
|
+
schemaVersion: input.schemaVersion,
|
|
65
|
+
logicalPath,
|
|
66
|
+
sourceDigest: input.sourceDigest,
|
|
67
|
+
importer: input.importer,
|
|
68
|
+
codec: input.codec,
|
|
69
|
+
settings: stableValue(input.settings),
|
|
70
|
+
producer: input.producer,
|
|
71
|
+
profile: input.profile,
|
|
72
|
+
declaredGuids: [...(input.declaredGuids ?? [])].sort(),
|
|
73
|
+
};
|
|
74
|
+
return createHash('sha256').update(JSON.stringify(semantic)).digest('hex');
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
export function producerRelativeDdcIdentity(
|
|
78
|
+
input: ProducerSemanticIdentityInput,
|
|
79
|
+
): ProducerRelativeDdcIdentity {
|
|
80
|
+
return {
|
|
81
|
+
logicalPath: producerRelativeLogicalPath(input.producerRoot, input.sourcePath),
|
|
82
|
+
sourceDigest: input.sourceDigest,
|
|
83
|
+
key: producerRelativeDdcKey(input),
|
|
84
|
+
};
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
function fingerprint(meta: string, source: Uint8Array | undefined): string {
|
|
88
|
+
const hash = createHash('sha256').update(meta);
|
|
89
|
+
if (source !== undefined) hash.update(source);
|
|
90
|
+
return hash.digest('base64');
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
function sourceFromMeta(
|
|
94
|
+
metaPath: string,
|
|
95
|
+
meta: {
|
|
96
|
+
readonly source?: unknown;
|
|
97
|
+
readonly inputFingerprint?: unknown;
|
|
98
|
+
readonly importSettings?: unknown;
|
|
99
|
+
readonly subAssets?: readonly { readonly guid?: unknown }[];
|
|
100
|
+
},
|
|
101
|
+
guid: string,
|
|
102
|
+
raw: string,
|
|
103
|
+
): SourceDeclarationEvidence | undefined {
|
|
104
|
+
if (
|
|
105
|
+
!meta.subAssets?.some(
|
|
106
|
+
(asset) => typeof asset.guid === 'string' && asset.guid.toLowerCase() === guid.toLowerCase(),
|
|
107
|
+
)
|
|
108
|
+
) {
|
|
109
|
+
return undefined;
|
|
110
|
+
}
|
|
111
|
+
const source =
|
|
112
|
+
typeof meta.source === 'string' ? meta.source : basename(metaPath).replace(/\.meta\.json$/, '');
|
|
113
|
+
const sourcePath = resolve(dirname(metaPath), source);
|
|
114
|
+
const inputFingerprint =
|
|
115
|
+
typeof meta.inputFingerprint === 'string' ? meta.inputFingerprint : fingerprint(raw, undefined);
|
|
116
|
+
return { origin: 'sourceMeta', sourcePath, inputFingerprint };
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
async function collectDeclarationPaths(root: string): Promise<string[]> {
|
|
120
|
+
const paths: string[] = [];
|
|
121
|
+
async function visit(dir: string): Promise<void> {
|
|
122
|
+
const entries = await readdir(dir, { withFileTypes: true }).catch(() => []);
|
|
123
|
+
for (const entry of entries) {
|
|
124
|
+
const path = join(dir, entry.name);
|
|
125
|
+
if (entry.isDirectory()) {
|
|
126
|
+
if (!SCANNER_BLACKLIST.has(entry.name)) await visit(path);
|
|
127
|
+
continue;
|
|
128
|
+
}
|
|
129
|
+
if (
|
|
130
|
+
entry.isFile() &&
|
|
131
|
+
(entry.name.endsWith('.meta.json') ||
|
|
132
|
+
entry.name.endsWith('.pack.json') ||
|
|
133
|
+
entry.name.endsWith('.pack.ts'))
|
|
134
|
+
) {
|
|
135
|
+
paths.push(path);
|
|
136
|
+
}
|
|
137
|
+
}
|
|
138
|
+
}
|
|
139
|
+
await visit(root);
|
|
140
|
+
return paths;
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
/** Read `.meta.json` or `.pack.json` source declarations without runtime imports. */
|
|
144
|
+
export async function readSourceInventory(
|
|
145
|
+
request: SourceInventoryRequest,
|
|
146
|
+
): Promise<SourceDeclarationEvidence | undefined> {
|
|
147
|
+
const result = await scan([request.projectRoot]);
|
|
148
|
+
const paths = result.ok ? result.value : await collectDeclarationPaths(request.projectRoot);
|
|
149
|
+
const guid = request.guid.toLowerCase();
|
|
150
|
+
let authored: SourceDeclarationEvidence | undefined;
|
|
151
|
+
for (const path of paths) {
|
|
152
|
+
if (path.endsWith('.pack.ts')) {
|
|
153
|
+
const loaded = await loadScriptablePack(path, { metadataOnly: true });
|
|
154
|
+
if (!loaded.ok) continue;
|
|
155
|
+
const meta = projectScriptablePackMeta(loaded.value, path);
|
|
156
|
+
if (meta.subAssets.some((asset) => asset.guid.toLowerCase() === guid)) {
|
|
157
|
+
return { origin: 'sourceMeta', sourcePath: path };
|
|
158
|
+
}
|
|
159
|
+
continue;
|
|
160
|
+
}
|
|
161
|
+
const raw = await readFile(path, 'utf8').catch(() => undefined);
|
|
162
|
+
if (raw === undefined) continue;
|
|
163
|
+
let parsed: {
|
|
164
|
+
readonly assets?: readonly { readonly guid?: unknown }[];
|
|
165
|
+
readonly source?: unknown;
|
|
166
|
+
readonly inputFingerprint?: unknown;
|
|
167
|
+
readonly importSettings?: unknown;
|
|
168
|
+
readonly subAssets?: readonly { readonly guid?: unknown }[];
|
|
169
|
+
};
|
|
170
|
+
try {
|
|
171
|
+
parsed = JSON.parse(raw) as typeof parsed;
|
|
172
|
+
} catch {
|
|
173
|
+
continue;
|
|
174
|
+
}
|
|
175
|
+
if (path.endsWith('.pack.json')) {
|
|
176
|
+
if (
|
|
177
|
+
parsed.assets?.some(
|
|
178
|
+
(asset) =>
|
|
179
|
+
typeof asset.guid === 'string' &&
|
|
180
|
+
(asset.guid === request.guid || asset.guid.toLowerCase() === guid),
|
|
181
|
+
)
|
|
182
|
+
) {
|
|
183
|
+
authored = { origin: 'authoredPack', sourcePath: path };
|
|
184
|
+
}
|
|
185
|
+
continue;
|
|
186
|
+
}
|
|
187
|
+
if (path.endsWith('.meta.json')) {
|
|
188
|
+
const source = sourceFromMeta(path, parsed, request.guid, raw);
|
|
189
|
+
if (source !== undefined) return source;
|
|
190
|
+
}
|
|
191
|
+
}
|
|
192
|
+
return authored;
|
|
193
|
+
}
|
package/src/guid.ts
ADDED
|
@@ -0,0 +1,99 @@
|
|
|
1
|
+
import { uuidv7obj } from 'uuidv7';
|
|
2
|
+
import { PackError } from './errors.js';
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* 16-byte UUID branded ABI. Prevents assignment from plain Uint8Array or string.
|
|
6
|
+
* Brand field mirrors the declaration in @forgeax/engine-types for cross-package
|
|
7
|
+
* structural compatibility (both use __guidBrand: 'AssetGuid').
|
|
8
|
+
*/
|
|
9
|
+
export type AssetGuid = Uint8Array & { readonly __guidBrand: 'AssetGuid' };
|
|
10
|
+
|
|
11
|
+
/** Minimal Result alias for this module (structurally compatible with ScanResult). */
|
|
12
|
+
export type GuidResult<T, E> =
|
|
13
|
+
| { readonly ok: true; readonly value: T }
|
|
14
|
+
| { readonly ok: false; readonly error: E };
|
|
15
|
+
|
|
16
|
+
function brand(bytes: Uint8Array): AssetGuid {
|
|
17
|
+
return bytes as AssetGuid;
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
const HEX_BYTE = Array.from({ length: 256 }, (_, i) => i.toString(16).padStart(2, '0'));
|
|
21
|
+
|
|
22
|
+
function bytesToDashForm(bytes: Uint8Array): string {
|
|
23
|
+
const h = HEX_BYTE;
|
|
24
|
+
// biome-ignore lint/style/noNonNullAssertion: length guaranteed 16
|
|
25
|
+
return `${h[bytes[0]!]}${h[bytes[1]!]}${h[bytes[2]!]}${h[bytes[3]!]}-${h[bytes[4]!]}${h[bytes[5]!]}-${h[bytes[6]!]}${h[bytes[7]!]}-${h[bytes[8]!]}${h[bytes[9]!]}-${h[bytes[10]!]}${h[bytes[11]!]}${h[bytes[12]!]}${h[bytes[13]!]}${h[bytes[14]!]}${h[bytes[15]!]}`;
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
const UUID_RE = /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i;
|
|
29
|
+
|
|
30
|
+
export function isValidAssetGuidString(value: unknown): value is string {
|
|
31
|
+
return typeof value === 'string' && UUID_RE.test(value);
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
function dashFormToBytes(dashForm: string): Uint8Array {
|
|
35
|
+
const hex = dashForm.replace(/-/g, '');
|
|
36
|
+
const bytes = new Uint8Array(16);
|
|
37
|
+
for (let i = 0; i < 16; i++) {
|
|
38
|
+
bytes[i] = parseInt(hex.slice(i * 2, i * 2 + 2), 16);
|
|
39
|
+
}
|
|
40
|
+
return bytes;
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
/**
|
|
44
|
+
* Utilities for the AssetGuid branded 16-byte UUID type.
|
|
45
|
+
* Declare the type via `import type { AssetGuid } from '@forgeax/engine-pack/guid'`
|
|
46
|
+
* or `import type { AssetGuid } from '@forgeax/engine-types'`.
|
|
47
|
+
*/
|
|
48
|
+
export const AssetGuid = {
|
|
49
|
+
/**
|
|
50
|
+
* Parse a 36-char RFC 4122 dash-form UUID string into an AssetGuid.
|
|
51
|
+
* Returns Ok(AssetGuid) on success or Err(PackError) with code 'pack-guid-malformed' on failure.
|
|
52
|
+
* Never throws for expected failures (requirements §4.2 / §14 / charter proposition 4).
|
|
53
|
+
*/
|
|
54
|
+
parse(dashForm: string): GuidResult<AssetGuid, PackError> {
|
|
55
|
+
if (!isValidAssetGuidString(dashForm)) {
|
|
56
|
+
return {
|
|
57
|
+
ok: false,
|
|
58
|
+
error: new PackError({
|
|
59
|
+
code: 'pack-guid-malformed',
|
|
60
|
+
expected: '36-char RFC 4122 dash-form UUID',
|
|
61
|
+
hint: 'use AssetGuid.random() or a UUIDv7 generator; all GUID fields must be 36-char RFC 4122 dash-form',
|
|
62
|
+
detail: {
|
|
63
|
+
raw: dashForm,
|
|
64
|
+
reason: 'expected 36-char RFC 4122 dash-form UUID',
|
|
65
|
+
},
|
|
66
|
+
}),
|
|
67
|
+
};
|
|
68
|
+
}
|
|
69
|
+
return { ok: true, value: brand(dashFormToBytes(dashForm)) };
|
|
70
|
+
},
|
|
71
|
+
|
|
72
|
+
/**
|
|
73
|
+
* Format an AssetGuid as a 36-char RFC 4122 lowercase dash-form string.
|
|
74
|
+
*/
|
|
75
|
+
format(guid: AssetGuid): string {
|
|
76
|
+
return bytesToDashForm(guid);
|
|
77
|
+
},
|
|
78
|
+
|
|
79
|
+
/**
|
|
80
|
+
* Test byte-by-byte equality between two AssetGuids.
|
|
81
|
+
*/
|
|
82
|
+
equals(a: AssetGuid, b: AssetGuid): boolean {
|
|
83
|
+
for (let i = 0; i < 16; i++) {
|
|
84
|
+
if (a[i] !== b[i]) return false;
|
|
85
|
+
}
|
|
86
|
+
return true;
|
|
87
|
+
},
|
|
88
|
+
|
|
89
|
+
/**
|
|
90
|
+
* Mint a new time-ordered UUIDv7 as an AssetGuid.
|
|
91
|
+
* Works in both Node.js and browser environments.
|
|
92
|
+
*/
|
|
93
|
+
random(): AssetGuid {
|
|
94
|
+
const uuid = uuidv7obj();
|
|
95
|
+
const bytes = new Uint8Array(16);
|
|
96
|
+
bytes.set(uuid.bytes);
|
|
97
|
+
return brand(bytes);
|
|
98
|
+
},
|
|
99
|
+
} as const;
|
package/src/index.ts
ADDED
|
@@ -0,0 +1,108 @@
|
|
|
1
|
+
// @forgeax/engine-pack
|
|
2
|
+
// Disk schema, GUID tools, and browser-safe asset contracts.
|
|
3
|
+
// Node-only catalog/build APIs live under @forgeax/engine-pack/build.
|
|
4
|
+
|
|
5
|
+
export {
|
|
6
|
+
type CookedMaterialRecord,
|
|
7
|
+
collectMaterialCookRefs,
|
|
8
|
+
createMaterialArtifactDigest,
|
|
9
|
+
createMaterialCookIdentity,
|
|
10
|
+
type MaterialCookArtifact,
|
|
11
|
+
type MaterialCookIdentity,
|
|
12
|
+
type MaterialCookIdentityExpectation,
|
|
13
|
+
type MaterialCookIdentityInput,
|
|
14
|
+
type MaterialCookReceipt,
|
|
15
|
+
type MaterialCookRecordError,
|
|
16
|
+
type MaterialCookRefs,
|
|
17
|
+
type MaterialCookWasmProvenance,
|
|
18
|
+
projectCookedMaterialRecord,
|
|
19
|
+
serializeCookedMaterialRecord,
|
|
20
|
+
serializeMaterialCookReceipt,
|
|
21
|
+
validateCookedMaterialRecord,
|
|
22
|
+
validateMaterialCookReceipt,
|
|
23
|
+
} from './evidence/material-cook.js';
|
|
24
|
+
export { buildOfflineAssetEvidence, packageVerification } from './evidence/offline-evidence.js';
|
|
25
|
+
export {
|
|
26
|
+
decodeMeshBinHeader,
|
|
27
|
+
MESH_BIN_DIGEST_BYTES,
|
|
28
|
+
MESH_BIN_HEADER_V4_BYTES,
|
|
29
|
+
MESH_BIN_PROJECTION_VERSION,
|
|
30
|
+
MESH_BIN_VERSION,
|
|
31
|
+
type MeshBinContractError,
|
|
32
|
+
type MeshBinHeaderResult,
|
|
33
|
+
type MeshBinHeaderV4,
|
|
34
|
+
writeMeshBinHeader,
|
|
35
|
+
} from './mesh-bin-contract.js';
|
|
36
|
+
export { validateProducerContract, validateProducerOutputs } from './producer-contract.js';
|
|
37
|
+
export {
|
|
38
|
+
type AssetReader,
|
|
39
|
+
isScriptablePackAssetKind,
|
|
40
|
+
projectScriptablePackMeta,
|
|
41
|
+
SCRIPTABLE_PACK_ASSET_KINDS,
|
|
42
|
+
type ScriptablePackAssetDeclaration,
|
|
43
|
+
type ScriptablePackAssetDeclarations,
|
|
44
|
+
type ScriptablePackAssetFor,
|
|
45
|
+
type ScriptablePackAssetKind,
|
|
46
|
+
type ScriptablePackDefinition,
|
|
47
|
+
type ScriptablePackError,
|
|
48
|
+
type ScriptablePackExternalAssets,
|
|
49
|
+
type ScriptablePackMetaJson,
|
|
50
|
+
type ScriptablePackOutputs,
|
|
51
|
+
type ScriptablePackPublicationEnvelope,
|
|
52
|
+
type ScriptablePackReadError,
|
|
53
|
+
validateScriptablePackDefinition,
|
|
54
|
+
} from './scriptable-pack.js';
|
|
55
|
+
export { calculateTopologyDiff, diffTopology } from './topology.js';
|
|
56
|
+
|
|
57
|
+
import { err, ok, type PackV2, type PackV2Error, type Result } from '@forgeax/engine-types';
|
|
58
|
+
|
|
59
|
+
export type { ArtifactPathContext } from './artifact-path.js';
|
|
60
|
+
export { validateArtifactPath } from './artifact-path.js';
|
|
61
|
+
|
|
62
|
+
import { validatePackV2 } from './schema-compiled.js';
|
|
63
|
+
|
|
64
|
+
export type {
|
|
65
|
+
AnimationClip,
|
|
66
|
+
AnimationGraph,
|
|
67
|
+
Asset,
|
|
68
|
+
AudioClipAsset,
|
|
69
|
+
EquirectAsset,
|
|
70
|
+
FontAsset,
|
|
71
|
+
MaterialAsset,
|
|
72
|
+
MeshAsset,
|
|
73
|
+
PackV2,
|
|
74
|
+
PackV2Error,
|
|
75
|
+
ParticleEffectAsset,
|
|
76
|
+
RenderPipelineAsset,
|
|
77
|
+
SamplerAsset,
|
|
78
|
+
SceneAsset,
|
|
79
|
+
SkeletonAsset,
|
|
80
|
+
SkinAsset,
|
|
81
|
+
TextureAsset,
|
|
82
|
+
TilesetAsset,
|
|
83
|
+
VideoAsset,
|
|
84
|
+
} from '@forgeax/engine-types';
|
|
85
|
+
export {
|
|
86
|
+
type MaterialArtifactWriteInput,
|
|
87
|
+
type MaterialArtifactWriteResult,
|
|
88
|
+
writeMaterialArtifact,
|
|
89
|
+
} from './material/artifact-writer.js';
|
|
90
|
+
export {
|
|
91
|
+
projectRuntimePack,
|
|
92
|
+
type RuntimeAssetProjectionInput,
|
|
93
|
+
type RuntimePackProjectionInput,
|
|
94
|
+
} from './runtime-projection.js';
|
|
95
|
+
export { validateMeta, validatePack, validatePackV2 } from './schema-compiled.js';
|
|
96
|
+
|
|
97
|
+
export function parsePackV2(value: unknown): Result<PackV2, PackV2Error> {
|
|
98
|
+
if (!validatePackV2(value)) {
|
|
99
|
+
return err({
|
|
100
|
+
code: 'pack-v2-envelope-invalid',
|
|
101
|
+
expected: 'a Pack v2 envelope with unique asset GUIDs and valid descriptors',
|
|
102
|
+
hint: 'validate the pack against packages/pack/schema/pack.schema.json and re-cook it',
|
|
103
|
+
detail: { observed: 'invalid pack', expected: 'schemaVersion 2.0.0' },
|
|
104
|
+
});
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
return ok(value);
|
|
108
|
+
}
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
import { createMaterialArtifactDigest } from '../evidence/material-cook.js';
|
|
2
|
+
|
|
3
|
+
export interface MaterialArtifactWriteInput {
|
|
4
|
+
readonly key: string;
|
|
5
|
+
readonly bytes: Uint8Array;
|
|
6
|
+
readonly mediaType?: string;
|
|
7
|
+
}
|
|
8
|
+
|
|
9
|
+
export interface MaterialArtifactWriteResult {
|
|
10
|
+
readonly key: string;
|
|
11
|
+
readonly bytes: Uint8Array;
|
|
12
|
+
readonly mediaType: string;
|
|
13
|
+
readonly digest: string;
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
/** Pack owns the immutable cooked material artifact tuple. */
|
|
17
|
+
export function writeMaterialArtifact(
|
|
18
|
+
input: MaterialArtifactWriteInput,
|
|
19
|
+
): MaterialArtifactWriteResult {
|
|
20
|
+
return {
|
|
21
|
+
key: input.key,
|
|
22
|
+
bytes: input.bytes.slice(),
|
|
23
|
+
mediaType: input.mediaType ?? 'application/octet-stream',
|
|
24
|
+
digest: createMaterialArtifactDigest(input.bytes),
|
|
25
|
+
};
|
|
26
|
+
}
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
export {
|
|
2
|
+
type CookedMaterialRecord,
|
|
3
|
+
collectMaterialCookRefs,
|
|
4
|
+
createMaterialArtifactDigest,
|
|
5
|
+
createMaterialCookIdentity,
|
|
6
|
+
type MaterialCookArtifact,
|
|
7
|
+
type MaterialCookReceipt,
|
|
8
|
+
type MaterialCookRecordError,
|
|
9
|
+
type MaterialCookRefs,
|
|
10
|
+
type MaterialCookWasmProvenance,
|
|
11
|
+
projectCookedMaterialRecord,
|
|
12
|
+
serializeCookedMaterialRecord,
|
|
13
|
+
serializeMaterialCookReceipt,
|
|
14
|
+
validateCookedMaterialRecord,
|
|
15
|
+
} from './evidence/material-cook.js';
|
|
@@ -0,0 +1,150 @@
|
|
|
1
|
+
/** Canonical mesh binary wire facts shared by build-time and runtime owners. */
|
|
2
|
+
|
|
3
|
+
export const MESH_BIN_VERSION = 4 as const;
|
|
4
|
+
export const MESH_BIN_PROJECTION_VERSION = 1 as const;
|
|
5
|
+
export const MESH_BIN_HEADER_V4_BYTES = 80;
|
|
6
|
+
export const MESH_BIN_DIGEST_BYTES = 32;
|
|
7
|
+
|
|
8
|
+
export interface MeshBinHeaderV4 {
|
|
9
|
+
readonly version: 4;
|
|
10
|
+
readonly projectionVersion: 1;
|
|
11
|
+
readonly mask: number;
|
|
12
|
+
readonly digest: string;
|
|
13
|
+
readonly stride: number;
|
|
14
|
+
readonly vertexCount: number;
|
|
15
|
+
readonly vertexBytes: number;
|
|
16
|
+
readonly indexCount: number;
|
|
17
|
+
readonly indexWidth: 0 | 2 | 4;
|
|
18
|
+
readonly indexBytes: number;
|
|
19
|
+
readonly jsonBytes: number;
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
export interface MeshBinContractError {
|
|
23
|
+
readonly code:
|
|
24
|
+
| 'mesh-bin-header-truncated'
|
|
25
|
+
| 'mesh-bin-version-unsupported'
|
|
26
|
+
| 'mesh-bin-header-invalid';
|
|
27
|
+
readonly subject: 'mesh-bin';
|
|
28
|
+
readonly sourceKey: string;
|
|
29
|
+
readonly expected: string;
|
|
30
|
+
readonly actual: string;
|
|
31
|
+
readonly recovery: string;
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
export type MeshBinHeaderResult =
|
|
35
|
+
| { readonly ok: true; readonly value: MeshBinHeaderV4 }
|
|
36
|
+
| { readonly ok: false; readonly error: MeshBinContractError };
|
|
37
|
+
|
|
38
|
+
function failure(
|
|
39
|
+
code: MeshBinContractError['code'],
|
|
40
|
+
sourceKey: string,
|
|
41
|
+
expected: string,
|
|
42
|
+
actual: string,
|
|
43
|
+
): MeshBinHeaderResult {
|
|
44
|
+
return {
|
|
45
|
+
ok: false,
|
|
46
|
+
error: {
|
|
47
|
+
code,
|
|
48
|
+
subject: 'mesh-bin',
|
|
49
|
+
sourceKey,
|
|
50
|
+
expected,
|
|
51
|
+
actual,
|
|
52
|
+
recovery: 're-cook the source with its Meta sidecar through the build-time importer',
|
|
53
|
+
},
|
|
54
|
+
};
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
function digestBytes(digest: string): Uint8Array {
|
|
58
|
+
const bytes = new Uint8Array(MESH_BIN_DIGEST_BYTES);
|
|
59
|
+
bytes.set(new TextEncoder().encode(digest).subarray(0, MESH_BIN_DIGEST_BYTES));
|
|
60
|
+
return bytes;
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
function readDigest(bytes: Uint8Array): string {
|
|
64
|
+
return new TextDecoder().decode(bytes).replace(/\0+$/u, '');
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
export function writeMeshBinHeader(header: MeshBinHeaderV4, out: Uint8Array): void {
|
|
68
|
+
if (out.byteLength < MESH_BIN_HEADER_V4_BYTES) {
|
|
69
|
+
throw new RangeError('mesh-bin v4 header output is truncated');
|
|
70
|
+
}
|
|
71
|
+
const view = new DataView(out.buffer, out.byteOffset, out.byteLength);
|
|
72
|
+
view.setUint32(0, header.version, true);
|
|
73
|
+
view.setUint32(4, header.projectionVersion, true);
|
|
74
|
+
view.setUint32(8, header.mask, true);
|
|
75
|
+
view.setUint32(12, header.stride, true);
|
|
76
|
+
view.setUint32(16, header.vertexCount, true);
|
|
77
|
+
view.setUint32(20, header.vertexBytes, true);
|
|
78
|
+
view.setUint32(24, header.indexCount, true);
|
|
79
|
+
view.setUint32(28, header.indexWidth, true);
|
|
80
|
+
view.setUint32(32, header.indexBytes, true);
|
|
81
|
+
view.setUint32(36, header.jsonBytes, true);
|
|
82
|
+
out.set(digestBytes(header.digest), 48);
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
export function decodeMeshBinHeader(
|
|
86
|
+
bytes: Uint8Array,
|
|
87
|
+
sourceKey = '<unknown mesh source>',
|
|
88
|
+
): MeshBinHeaderResult {
|
|
89
|
+
if (bytes.byteLength < MESH_BIN_HEADER_V4_BYTES) {
|
|
90
|
+
return failure(
|
|
91
|
+
'mesh-bin-header-truncated',
|
|
92
|
+
sourceKey,
|
|
93
|
+
`mesh-bin v4 header (${MESH_BIN_HEADER_V4_BYTES} bytes)`,
|
|
94
|
+
`${bytes.byteLength} bytes`,
|
|
95
|
+
);
|
|
96
|
+
}
|
|
97
|
+
const view = new DataView(bytes.buffer, bytes.byteOffset, bytes.byteLength);
|
|
98
|
+
const version = view.getUint32(0, true);
|
|
99
|
+
if (version !== MESH_BIN_VERSION) {
|
|
100
|
+
return failure('mesh-bin-version-unsupported', sourceKey, 'mesh-bin v4', `version ${version}`);
|
|
101
|
+
}
|
|
102
|
+
const projectionVersion = view.getUint32(4, true);
|
|
103
|
+
const mask = view.getUint32(8, true);
|
|
104
|
+
const stride = view.getUint32(12, true);
|
|
105
|
+
const vertexCount = view.getUint32(16, true);
|
|
106
|
+
const vertexBytes = view.getUint32(20, true);
|
|
107
|
+
const indexCount = view.getUint32(24, true);
|
|
108
|
+
const indexWidth = view.getUint32(28, true);
|
|
109
|
+
const indexBytes = view.getUint32(32, true);
|
|
110
|
+
const jsonBytes = view.getUint32(36, true);
|
|
111
|
+
const digest = readDigest(bytes.subarray(48, 48 + MESH_BIN_DIGEST_BYTES));
|
|
112
|
+
if (
|
|
113
|
+
projectionVersion !== MESH_BIN_PROJECTION_VERSION ||
|
|
114
|
+
mask === 0 ||
|
|
115
|
+
stride === 0 ||
|
|
116
|
+
!Number.isSafeInteger(vertexCount) ||
|
|
117
|
+
!Number.isSafeInteger(vertexBytes) ||
|
|
118
|
+
!Number.isSafeInteger(indexCount) ||
|
|
119
|
+
!Number.isSafeInteger(indexBytes) ||
|
|
120
|
+
!Number.isSafeInteger(jsonBytes) ||
|
|
121
|
+
(indexCount === 0 && indexWidth !== 0) ||
|
|
122
|
+
(indexCount > 0 && indexWidth !== 2 && indexWidth !== 4) ||
|
|
123
|
+
indexBytes !== indexCount * indexWidth ||
|
|
124
|
+
vertexBytes !== vertexCount * stride ||
|
|
125
|
+
digest.length === 0
|
|
126
|
+
) {
|
|
127
|
+
return failure(
|
|
128
|
+
'mesh-bin-header-invalid',
|
|
129
|
+
sourceKey,
|
|
130
|
+
'safe v4 projection, stride, cardinality, and payload byte lengths',
|
|
131
|
+
`projection=${projectionVersion}; mask=${mask}; stride=${stride}; vertexBytes=${vertexBytes}; indexBytes=${indexBytes}; digest=${digest}`,
|
|
132
|
+
);
|
|
133
|
+
}
|
|
134
|
+
return {
|
|
135
|
+
ok: true,
|
|
136
|
+
value: {
|
|
137
|
+
version: 4,
|
|
138
|
+
projectionVersion: 1,
|
|
139
|
+
mask,
|
|
140
|
+
digest,
|
|
141
|
+
stride,
|
|
142
|
+
vertexCount,
|
|
143
|
+
vertexBytes,
|
|
144
|
+
indexCount,
|
|
145
|
+
indexWidth: indexWidth as 0 | 2 | 4,
|
|
146
|
+
indexBytes,
|
|
147
|
+
jsonBytes,
|
|
148
|
+
},
|
|
149
|
+
};
|
|
150
|
+
}
|