@forgeax/engine-import 0.1.20 → 0.1.23
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/README.md +91 -3
- package/dist/__tests__/ies-contract.unit.test.d.ts +2 -0
- package/dist/__tests__/ies-contract.unit.test.d.ts.map +1 -0
- package/dist/__tests__/ies-producer.unit.test.d.ts +2 -0
- package/dist/__tests__/ies-producer.unit.test.d.ts.map +1 -0
- package/dist/__tests__/mesh-lod-contract.unit.test.d.ts +2 -0
- package/dist/__tests__/mesh-lod-contract.unit.test.d.ts.map +1 -0
- package/dist/__tests__/mesh-lod-reimport.integration.test.d.ts +2 -0
- package/dist/__tests__/mesh-lod-reimport.integration.test.d.ts.map +1 -0
- package/dist/__tests__/parameterized-scriptable-pack.unit.test.d.ts +2 -0
- package/dist/__tests__/parameterized-scriptable-pack.unit.test.d.ts.map +1 -0
- package/dist/__tests__/scriptable-pack-file-snapshot.unit.test.d.ts +2 -0
- package/dist/__tests__/scriptable-pack-file-snapshot.unit.test.d.ts.map +1 -0
- package/dist/__tests__/source-package-publication-lod.integration.test.d.ts +2 -0
- package/dist/__tests__/source-package-publication-lod.integration.test.d.ts.map +1 -0
- package/dist/browser.d.ts +1 -0
- package/dist/browser.d.ts.map +1 -1
- package/dist/browser.mjs +230 -3
- package/dist/browser.mjs.map +1 -1
- package/dist/build-production.d.ts +1 -8
- package/dist/build-production.d.ts.map +1 -1
- package/dist/ies/ies-importer.d.ts +4 -0
- package/dist/ies/ies-importer.d.ts.map +1 -0
- package/dist/ies/parse-lm63.d.ts +13 -0
- package/dist/ies/parse-lm63.d.ts.map +1 -0
- package/dist/ies/resample-type-c.d.ts +4 -0
- package/dist/ies/resample-type-c.d.ts.map +1 -0
- package/dist/import-runner.d.ts.map +1 -1
- package/dist/index.d.ts +11 -6
- package/dist/index.d.ts.map +1 -1
- package/dist/index.mjs +2550 -1029
- package/dist/index.mjs.map +1 -1
- package/dist/mesh-bin.d.ts.map +1 -1
- package/dist/mesh-bin.mjs +29 -1
- package/dist/mesh-bin.mjs.map +1 -1
- package/dist/mesh-lod.d.ts +60 -0
- package/dist/mesh-lod.d.ts.map +1 -0
- package/dist/parameterized-scriptable-pack.d.ts +68 -0
- package/dist/parameterized-scriptable-pack.d.ts.map +1 -0
- package/dist/scriptable-pack-file-snapshot.d.ts +71 -0
- package/dist/scriptable-pack-file-snapshot.d.ts.map +1 -0
- package/dist/scriptable-pack-host.d.ts +70 -39
- package/dist/scriptable-pack-host.d.ts.map +1 -1
- package/dist/scriptable-pack-output-producers.d.ts +1 -0
- package/dist/scriptable-pack-output-producers.d.ts.map +1 -1
- package/dist/scriptable-pack-staged-snapshot.d.ts +2 -2
- package/dist/scriptable-pack-staged-snapshot.d.ts.map +1 -1
- package/dist/scriptable-pack.d.ts +13 -26
- package/dist/scriptable-pack.d.ts.map +1 -1
- package/dist/source-package-errors.d.ts.map +1 -1
- package/dist/source-package-publication.d.ts.map +1 -1
- package/package.json +8 -7
- package/src/__tests__/ies-contract.unit.test.ts +31 -0
- package/src/__tests__/ies-producer.unit.test.ts +104 -0
- package/src/__tests__/mesh-bin.test.ts +69 -0
- package/src/__tests__/mesh-lod-contract.unit.test.ts +92 -0
- package/src/__tests__/mesh-lod-reimport.integration.test.ts +15 -0
- package/src/__tests__/parameterized-scriptable-pack.unit.test.ts +270 -0
- package/src/__tests__/scriptable-pack-file-snapshot.unit.test.ts +177 -0
- package/src/__tests__/scriptable-pack-host.unit.test.ts +196 -4
- package/src/__tests__/scriptable-pack-production-producers.unit.test.ts +7 -1
- package/src/__tests__/scriptable-pack-staged-snapshot.unit.test.ts +43 -12
- package/src/__tests__/source-package-publication-lod.integration.test.ts +29 -0
- package/src/__tests__/source-package-publication.integration.test.ts +26 -1
- package/src/browser.ts +5 -0
- package/src/build-production.ts +498 -141
- package/src/ies/ies-importer.ts +126 -0
- package/src/ies/parse-lm63.ts +98 -0
- package/src/ies/resample-type-c.ts +108 -0
- package/src/import-runner.ts +58 -0
- package/src/index.ts +49 -19
- package/src/mesh-bin.ts +38 -0
- package/src/mesh-lod.ts +265 -0
- package/src/parameterized-scriptable-pack.ts +725 -0
- package/src/scriptable-pack-file-snapshot.ts +346 -0
- package/src/scriptable-pack-host.ts +790 -343
- package/src/scriptable-pack-output-producers.ts +58 -1
- package/src/scriptable-pack-staged-snapshot.ts +12 -11
- package/src/scriptable-pack.ts +16 -485
- package/src/source-package-errors.ts +6 -0
- package/src/source-package-publication.ts +32 -2
- package/dist/.tsbuildinfo +0 -1
- package/dist/__tests__/scriptable-pack-product.contract.test.d.ts +0 -2
- package/dist/__tests__/scriptable-pack-product.contract.test.d.ts.map +0 -1
- package/dist/__tests__/scriptable-pack.integration.test.d.ts +0 -2
- package/dist/__tests__/scriptable-pack.integration.test.d.ts.map +0 -1
- package/dist/scriptable-source-package.d.ts +0 -14
- package/dist/scriptable-source-package.d.ts.map +0 -1
- package/src/__tests__/scriptable-pack-product.contract.test.ts +0 -46
- package/src/__tests__/scriptable-pack.integration.test.ts +0 -435
- package/src/scriptable-source-package.ts +0 -88
|
@@ -0,0 +1,346 @@
|
|
|
1
|
+
import { createHash } from 'node:crypto';
|
|
2
|
+
import { readdir, readFile, stat } from 'node:fs/promises';
|
|
3
|
+
import { resolve } from 'node:path';
|
|
4
|
+
import { isScriptablePackAssetKind, validatePack } from '@forgeax/engine-pack';
|
|
5
|
+
import { AssetGuid } from '@forgeax/engine-pack/guid';
|
|
6
|
+
import type { Asset, AssetGuid as AssetGuidType, Result } from '@forgeax/engine-types';
|
|
7
|
+
import { err, ok } from '@forgeax/engine-types';
|
|
8
|
+
import type {
|
|
9
|
+
ScriptablePackAssetSnapshot,
|
|
10
|
+
ScriptablePackAssetSnapshotSource,
|
|
11
|
+
} from './scriptable-pack.js';
|
|
12
|
+
|
|
13
|
+
export interface ScriptablePackFileAssetSnapshotSourceOptions {
|
|
14
|
+
readonly assetRoots: readonly string[];
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
export type ScriptablePackFileAssetSnapshotError =
|
|
18
|
+
| {
|
|
19
|
+
readonly code: 'scriptable-pack-file-root-unreadable';
|
|
20
|
+
readonly expected: string;
|
|
21
|
+
readonly hint: string;
|
|
22
|
+
readonly detail: { readonly root: string; readonly reason: string };
|
|
23
|
+
}
|
|
24
|
+
| {
|
|
25
|
+
readonly code: 'scriptable-pack-file-unreadable';
|
|
26
|
+
readonly expected: string;
|
|
27
|
+
readonly hint: string;
|
|
28
|
+
readonly detail: { readonly path: string; readonly reason: string };
|
|
29
|
+
}
|
|
30
|
+
| {
|
|
31
|
+
readonly code: 'scriptable-pack-file-json-invalid';
|
|
32
|
+
readonly expected: string;
|
|
33
|
+
readonly hint: string;
|
|
34
|
+
readonly detail: { readonly path: string; readonly reason: string };
|
|
35
|
+
}
|
|
36
|
+
| {
|
|
37
|
+
readonly code: 'scriptable-pack-file-invalid';
|
|
38
|
+
readonly expected: string;
|
|
39
|
+
readonly hint: string;
|
|
40
|
+
readonly detail: {
|
|
41
|
+
readonly path: string;
|
|
42
|
+
readonly ajvErrors: readonly { readonly instancePath: string; readonly message: string }[];
|
|
43
|
+
};
|
|
44
|
+
}
|
|
45
|
+
| {
|
|
46
|
+
readonly code: 'scriptable-pack-file-guid-collision';
|
|
47
|
+
readonly expected: string;
|
|
48
|
+
readonly hint: string;
|
|
49
|
+
readonly detail: { readonly guid: string; readonly paths: readonly [string, string] };
|
|
50
|
+
}
|
|
51
|
+
| {
|
|
52
|
+
readonly code: 'scriptable-pack-file-asset-invalid';
|
|
53
|
+
readonly expected: string;
|
|
54
|
+
readonly hint: string;
|
|
55
|
+
readonly detail: { readonly path: string; readonly guid: string; readonly reason: string };
|
|
56
|
+
}
|
|
57
|
+
| {
|
|
58
|
+
readonly code: 'asset-not-found';
|
|
59
|
+
readonly expected: string;
|
|
60
|
+
readonly hint: string;
|
|
61
|
+
readonly detail: { readonly guid: string };
|
|
62
|
+
};
|
|
63
|
+
|
|
64
|
+
interface PackAssetRow {
|
|
65
|
+
readonly guid: string;
|
|
66
|
+
readonly kind: string;
|
|
67
|
+
readonly payload: unknown;
|
|
68
|
+
readonly refs: readonly string[];
|
|
69
|
+
readonly [key: string]: unknown;
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
interface PackDocument {
|
|
73
|
+
readonly assets: readonly PackAssetRow[];
|
|
74
|
+
readonly [key: string]: unknown;
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
interface IndexedAsset {
|
|
78
|
+
readonly asset: Asset;
|
|
79
|
+
readonly digest: string;
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
interface SnapshotIndex {
|
|
83
|
+
readonly generation: number;
|
|
84
|
+
readonly assets: ReadonlyMap<string, IndexedAsset>;
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
type SnapshotResult<T> = Result<T, ScriptablePackFileAssetSnapshotError>;
|
|
88
|
+
|
|
89
|
+
function isRecord(value: unknown): value is Record<string, unknown> {
|
|
90
|
+
return value !== null && typeof value === 'object' && !Array.isArray(value);
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
function stable(value: unknown): string {
|
|
94
|
+
if (value instanceof ArrayBuffer) {
|
|
95
|
+
return `ArrayBuffer:${JSON.stringify(Array.from(new Uint8Array(value)))}`;
|
|
96
|
+
}
|
|
97
|
+
if (ArrayBuffer.isView(value)) {
|
|
98
|
+
return `${value.constructor.name}:${JSON.stringify(
|
|
99
|
+
Array.from(new Uint8Array(value.buffer, value.byteOffset, value.byteLength)),
|
|
100
|
+
)}`;
|
|
101
|
+
}
|
|
102
|
+
if (Array.isArray(value)) return `[${value.map(stable).join(',')}]`;
|
|
103
|
+
if (value !== null && typeof value === 'object') {
|
|
104
|
+
const record = value as Record<string, unknown>;
|
|
105
|
+
return `{${Object.keys(record)
|
|
106
|
+
.sort()
|
|
107
|
+
.map((key) => `${JSON.stringify(key)}:${stable(record[key])}`)
|
|
108
|
+
.join(',')}}`;
|
|
109
|
+
}
|
|
110
|
+
return JSON.stringify(value) ?? 'null';
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
function digest(value: unknown): string {
|
|
114
|
+
return `sha256:${createHash('sha256').update(stable(value)).digest('hex')}`;
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
function generationFromDigest(value: string): number {
|
|
118
|
+
const parsed = Number.parseInt(value.slice('sha256:'.length, 'sha256:'.length + 8), 16);
|
|
119
|
+
return Number.isSafeInteger(parsed) && parsed > 0 ? parsed : 1;
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
function reasonOf(error: unknown): string {
|
|
123
|
+
return error instanceof Error ? error.message : String(error);
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
async function collectPackFiles(
|
|
127
|
+
assetRoots: readonly string[],
|
|
128
|
+
): Promise<SnapshotResult<readonly string[]>> {
|
|
129
|
+
const files = new Set<string>();
|
|
130
|
+
|
|
131
|
+
async function visit(directory: string): Promise<SnapshotResult<void>> {
|
|
132
|
+
let entries: import('node:fs').Dirent[];
|
|
133
|
+
try {
|
|
134
|
+
entries = (await readdir(directory, { withFileTypes: true })) as import('node:fs').Dirent[];
|
|
135
|
+
} catch (error) {
|
|
136
|
+
return err({
|
|
137
|
+
code: 'scriptable-pack-file-root-unreadable',
|
|
138
|
+
expected: 'every configured ScriptablePack asset root to be readable',
|
|
139
|
+
hint: 'restore the asset root or configure a readable directory, then retry the ScriptablePack build',
|
|
140
|
+
detail: { root: directory, reason: reasonOf(error) },
|
|
141
|
+
});
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
for (const entry of entries.sort((left, right) => left.name.localeCompare(right.name))) {
|
|
145
|
+
const child = resolve(directory, entry.name);
|
|
146
|
+
if (entry.isDirectory()) {
|
|
147
|
+
const nested = await visit(child);
|
|
148
|
+
if (!nested.ok) return nested;
|
|
149
|
+
} else if (entry.isFile() && entry.name.endsWith('.pack.json')) {
|
|
150
|
+
files.add(child);
|
|
151
|
+
}
|
|
152
|
+
}
|
|
153
|
+
return ok(undefined);
|
|
154
|
+
}
|
|
155
|
+
|
|
156
|
+
for (const rawRoot of [...new Set(assetRoots.map((root) => resolve(root)))].sort((a, b) =>
|
|
157
|
+
a.localeCompare(b),
|
|
158
|
+
)) {
|
|
159
|
+
let info: import('node:fs').Stats;
|
|
160
|
+
try {
|
|
161
|
+
info = await stat(rawRoot);
|
|
162
|
+
} catch (error) {
|
|
163
|
+
return err({
|
|
164
|
+
code: 'scriptable-pack-file-root-unreadable',
|
|
165
|
+
expected: 'every configured ScriptablePack asset root to exist and be readable',
|
|
166
|
+
hint: 'restore the asset root or configure a readable directory, then retry the ScriptablePack build',
|
|
167
|
+
detail: { root: rawRoot, reason: reasonOf(error) },
|
|
168
|
+
});
|
|
169
|
+
}
|
|
170
|
+
if (info.isFile()) {
|
|
171
|
+
if (rawRoot.endsWith('.pack.json')) files.add(rawRoot);
|
|
172
|
+
continue;
|
|
173
|
+
}
|
|
174
|
+
if (!info.isDirectory()) {
|
|
175
|
+
return err({
|
|
176
|
+
code: 'scriptable-pack-file-root-unreadable',
|
|
177
|
+
expected: 'every configured ScriptablePack asset root to be a file or directory',
|
|
178
|
+
hint: 'configure an asset root containing ordinary .pack.json files, then retry the ScriptablePack build',
|
|
179
|
+
detail: { root: rawRoot, reason: 'root is neither a file nor a directory' },
|
|
180
|
+
});
|
|
181
|
+
}
|
|
182
|
+
const visited = await visit(rawRoot);
|
|
183
|
+
if (!visited.ok) return visited;
|
|
184
|
+
}
|
|
185
|
+
|
|
186
|
+
return ok([...files].sort((left, right) => left.localeCompare(right)));
|
|
187
|
+
}
|
|
188
|
+
|
|
189
|
+
function invalidPack(
|
|
190
|
+
path: string,
|
|
191
|
+
errors: readonly { readonly instancePath: string; readonly message: string }[],
|
|
192
|
+
): ScriptablePackFileAssetSnapshotError {
|
|
193
|
+
return {
|
|
194
|
+
code: 'scriptable-pack-file-invalid',
|
|
195
|
+
expected: 'a schema-valid ordinary .pack.json package',
|
|
196
|
+
hint: 'repair the Pack JSON against the package schema, then retry the ScriptablePack build',
|
|
197
|
+
detail: { path, ajvErrors: errors },
|
|
198
|
+
};
|
|
199
|
+
}
|
|
200
|
+
|
|
201
|
+
function assetFromRow(
|
|
202
|
+
path: string,
|
|
203
|
+
row: PackAssetRow,
|
|
204
|
+
): SnapshotResult<{ readonly asset: Asset; readonly digest: string }> {
|
|
205
|
+
if (!isScriptablePackAssetKind(row.kind)) {
|
|
206
|
+
return err({
|
|
207
|
+
code: 'scriptable-pack-file-asset-invalid',
|
|
208
|
+
expected: 'an ordinary ScriptablePack Asset kind from SCRIPTABLE_PACK_ASSET_KINDS',
|
|
209
|
+
hint: 'use an ordinary engine Asset kind or keep the custom payload outside this Asset snapshot source',
|
|
210
|
+
detail: { path, guid: row.guid, reason: `unsupported kind ${JSON.stringify(row.kind)}` },
|
|
211
|
+
});
|
|
212
|
+
}
|
|
213
|
+
if (!isRecord(row.payload)) {
|
|
214
|
+
return err({
|
|
215
|
+
code: 'scriptable-pack-file-asset-invalid',
|
|
216
|
+
expected: 'the Pack row payload to be a JSON object',
|
|
217
|
+
hint: 'write a complete ordinary Asset payload in the .pack.json row, then retry the ScriptablePack build',
|
|
218
|
+
detail: { path, guid: row.guid, reason: 'payload is not an object' },
|
|
219
|
+
});
|
|
220
|
+
}
|
|
221
|
+
const payloadKind = row.payload.kind;
|
|
222
|
+
if (payloadKind !== undefined && payloadKind !== row.kind) {
|
|
223
|
+
return err({
|
|
224
|
+
code: 'scriptable-pack-file-asset-invalid',
|
|
225
|
+
expected: 'the row kind and payload.kind to agree',
|
|
226
|
+
hint: 'repair the duplicated kind discriminant in the .pack.json row, then retry the ScriptablePack build',
|
|
227
|
+
detail: {
|
|
228
|
+
path,
|
|
229
|
+
guid: row.guid,
|
|
230
|
+
reason: `row kind ${JSON.stringify(row.kind)} does not match payload.kind ${JSON.stringify(payloadKind)}`,
|
|
231
|
+
},
|
|
232
|
+
});
|
|
233
|
+
}
|
|
234
|
+
const asset = { ...row.payload, kind: row.kind } as Asset;
|
|
235
|
+
return ok({ asset, digest: digest(asset) });
|
|
236
|
+
}
|
|
237
|
+
|
|
238
|
+
async function indexPackFiles(
|
|
239
|
+
assetRoots: readonly string[],
|
|
240
|
+
): Promise<SnapshotResult<SnapshotIndex>> {
|
|
241
|
+
const files = await collectPackFiles(assetRoots);
|
|
242
|
+
if (!files.ok) return files;
|
|
243
|
+
|
|
244
|
+
const byGuid = new Map<string, IndexedAsset>();
|
|
245
|
+
const pathsByGuid = new Map<string, string>();
|
|
246
|
+
const documents: PackDocument[] = [];
|
|
247
|
+
|
|
248
|
+
for (const path of files.value) {
|
|
249
|
+
let body: string;
|
|
250
|
+
try {
|
|
251
|
+
body = await readFile(path, 'utf8');
|
|
252
|
+
} catch (error) {
|
|
253
|
+
return err({
|
|
254
|
+
code: 'scriptable-pack-file-unreadable',
|
|
255
|
+
expected: 'a readable .pack.json file',
|
|
256
|
+
hint: 'restore the .pack.json file, then retry the ScriptablePack build',
|
|
257
|
+
detail: { path, reason: reasonOf(error) },
|
|
258
|
+
});
|
|
259
|
+
}
|
|
260
|
+
|
|
261
|
+
let parsed: unknown;
|
|
262
|
+
try {
|
|
263
|
+
parsed = JSON.parse(body) as unknown;
|
|
264
|
+
} catch (error) {
|
|
265
|
+
return err({
|
|
266
|
+
code: 'scriptable-pack-file-json-invalid',
|
|
267
|
+
expected: 'a parseable JSON .pack.json file',
|
|
268
|
+
hint: 'repair or restore the .pack.json file, then retry the ScriptablePack build',
|
|
269
|
+
detail: { path, reason: reasonOf(error) },
|
|
270
|
+
});
|
|
271
|
+
}
|
|
272
|
+
|
|
273
|
+
if (!validatePack(parsed)) {
|
|
274
|
+
return err(
|
|
275
|
+
invalidPack(
|
|
276
|
+
path,
|
|
277
|
+
(validatePack.errors ?? []).map((error) => ({
|
|
278
|
+
instancePath: error.instancePath,
|
|
279
|
+
message: error.message ?? 'unknown schema validation error',
|
|
280
|
+
})),
|
|
281
|
+
),
|
|
282
|
+
);
|
|
283
|
+
}
|
|
284
|
+
|
|
285
|
+
const document = parsed as PackDocument;
|
|
286
|
+
documents.push(document);
|
|
287
|
+
for (const row of document.assets) {
|
|
288
|
+
const existingPath = pathsByGuid.get(row.guid.toLowerCase());
|
|
289
|
+
if (existingPath !== undefined) {
|
|
290
|
+
return err({
|
|
291
|
+
code: 'scriptable-pack-file-guid-collision',
|
|
292
|
+
expected: 'each ordinary Pack asset GUID to be declared by exactly one .pack.json file',
|
|
293
|
+
hint: 'remove the duplicate GUID declaration or keep one authoritative .pack.json package, then retry',
|
|
294
|
+
detail: { guid: row.guid.toLowerCase(), paths: [existingPath, path] },
|
|
295
|
+
});
|
|
296
|
+
}
|
|
297
|
+
const asset = assetFromRow(path, row);
|
|
298
|
+
if (!asset.ok) return asset;
|
|
299
|
+
const key = row.guid.toLowerCase();
|
|
300
|
+
pathsByGuid.set(key, path);
|
|
301
|
+
byGuid.set(key, asset.value);
|
|
302
|
+
}
|
|
303
|
+
}
|
|
304
|
+
|
|
305
|
+
const sourceDigest = digest(
|
|
306
|
+
[...documents].sort((left, right) => stable(left).localeCompare(stable(right))),
|
|
307
|
+
);
|
|
308
|
+
return ok({
|
|
309
|
+
generation: generationFromDigest(sourceDigest),
|
|
310
|
+
assets: byGuid,
|
|
311
|
+
});
|
|
312
|
+
}
|
|
313
|
+
|
|
314
|
+
/**
|
|
315
|
+
* Create a build-time ScriptablePack reader backed by ordinary `.pack.json`
|
|
316
|
+
* files below the configured asset roots. The first read builds one immutable
|
|
317
|
+
* GUID index; subsequent reads return private Asset snapshots from that index.
|
|
318
|
+
*/
|
|
319
|
+
export function createScriptablePackFileAssetSnapshotSource(
|
|
320
|
+
options: ScriptablePackFileAssetSnapshotSourceOptions,
|
|
321
|
+
): ScriptablePackAssetSnapshotSource {
|
|
322
|
+
const index = indexPackFiles(options.assetRoots);
|
|
323
|
+
return {
|
|
324
|
+
async readByGuid(
|
|
325
|
+
guid: AssetGuidType,
|
|
326
|
+
): Promise<Result<ScriptablePackAssetSnapshot, ScriptablePackFileAssetSnapshotError>> {
|
|
327
|
+
const key = AssetGuid.format(guid).toLowerCase();
|
|
328
|
+
const indexed = await index;
|
|
329
|
+
if (!indexed.ok) return indexed;
|
|
330
|
+
const asset = indexed.value.assets.get(key);
|
|
331
|
+
if (asset === undefined) {
|
|
332
|
+
return err({
|
|
333
|
+
code: 'asset-not-found',
|
|
334
|
+
expected: `an ordinary .pack.json asset with GUID ${key} under the configured asset roots`,
|
|
335
|
+
hint: 'add the dependency to an asset root or declare it as a ScriptablePack external reference, then retry',
|
|
336
|
+
detail: { guid: key },
|
|
337
|
+
});
|
|
338
|
+
}
|
|
339
|
+
return ok({
|
|
340
|
+
asset: structuredClone(asset.asset),
|
|
341
|
+
generation: indexed.value.generation,
|
|
342
|
+
digest: asset.digest,
|
|
343
|
+
});
|
|
344
|
+
},
|
|
345
|
+
};
|
|
346
|
+
}
|