@aura3d/assets 1.3.3 → 1.4.1

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 (73) hide show
  1. package/dist/GLTFLoader.js +1 -0
  2. package/dist/GLTFLoader.js.map +1 -1
  3. package/dist/GLTFRenderResources.d.ts.map +1 -1
  4. package/dist/GLTFRenderResources.js +1 -0
  5. package/dist/GLTFRenderResources.js.map +1 -1
  6. package/package.json +38 -5
  7. package/src/AdvancedAssetCorpus.ts +0 -454
  8. package/src/AssetBundleCacheFixtures.ts +0 -290
  9. package/src/AssetCache.ts +0 -76
  10. package/src/AssetCompatibility.ts +0 -224
  11. package/src/AssetCorpus.ts +0 -329
  12. package/src/AssetDependencyGraph.ts +0 -83
  13. package/src/AssetHandle.ts +0 -74
  14. package/src/AssetImportPreflight.ts +0 -179
  15. package/src/AssetInspection.ts +0 -706
  16. package/src/AssetLoader.ts +0 -36
  17. package/src/AssetManager.ts +0 -211
  18. package/src/AssetRegistry.ts +0 -44
  19. package/src/AssetRenderDefaults.ts +0 -36
  20. package/src/AudioLoader.ts +0 -41
  21. package/src/BlenderExportValidation.ts +0 -227
  22. package/src/CarConceptMaterialStability.ts +0 -1405
  23. package/src/EXRLoader.ts +0 -1
  24. package/src/ExternalParityGLTFCorpus.ts +0 -65
  25. package/src/GLTFAnimationRuntime.ts +0 -1416
  26. package/src/GLTFAutoFit.ts +0 -133
  27. package/src/GLTFCompressionDecoders.ts +0 -190
  28. package/src/GLTFExtensionSupport.ts +0 -138
  29. package/src/GLTFLoader.ts +0 -4059
  30. package/src/GLTFRenderResources.ts +0 -2269
  31. package/src/HDRLoader.ts +0 -1
  32. package/src/ImageLoader.ts +0 -52
  33. package/src/ImportPipeline.ts +0 -148
  34. package/src/KTX2BasisTextureTranscoder.ts +0 -225
  35. package/src/LoadContext.ts +0 -60
  36. package/src/MaterialLoader.ts +0 -166
  37. package/src/MeshOptimization.ts +0 -93
  38. package/src/OBJLoader.ts +0 -551
  39. package/src/SceneAnalysisFixtures.ts +0 -438
  40. package/src/SceneLoader.ts +0 -62
  41. package/src/ShaderLoader.ts +0 -30
  42. package/src/TextureLoader.ts +0 -30
  43. package/src/TexturePipeline.ts +0 -96
  44. package/src/TextureStreaming.ts +0 -20
  45. package/src/WorkerAssetJobs.ts +0 -56
  46. package/src/advanced-gallery/index.ts +0 -18
  47. package/src/asset-corpus/GLTFSceneLoader.ts +0 -9
  48. package/src/asset-corpus/ProductionAssetCorpus.ts +0 -230
  49. package/src/asset-corpus/ProductionAssetPipeline.ts +0 -5
  50. package/src/asset-corpus/ProductionGLTFRenderPipeline.ts +0 -182
  51. package/src/asset-corpus/ProductionHDRTextureLoader.ts +0 -2
  52. package/src/asset-corpus/ProductionKTX2TextureLoader.ts +0 -2
  53. package/src/asset-corpus/ProductionTextureLoader.ts +0 -2
  54. package/src/asset-corpus/index.ts +0 -29
  55. package/src/browser-index.ts +0 -229
  56. package/src/createRenderableScene.ts +0 -128
  57. package/src/index.ts +0 -325
  58. package/src/loadRenderableAsset.ts +0 -67
  59. package/src/loaders/CubeTextureLoader.ts +0 -8
  60. package/src/loaders/EXRLoader.ts +0 -10
  61. package/src/loaders/HDRLoader.ts +0 -7
  62. package/src/loaders/KTX2Loader.ts +0 -7
  63. package/src/loaders/LoaderDiagnostics.ts +0 -36
  64. package/src/loaders/MTLLoader.ts +0 -20
  65. package/src/loaders/OBJLoader.ts +0 -24
  66. package/src/loaders/TextureLoader.ts +0 -14
  67. package/src/loaders/ThreeCompatGLTFLoader.ts +0 -19
  68. package/src/loaders/index.ts +0 -14
  69. package/src/threejs-compatibility/ThreeCompatAssetProvenance.ts +0 -51
  70. package/src/threejs-compatibility/ThreeCompatAssetRegistry.ts +0 -61
  71. package/src/threejs-example-parity/index.ts +0 -262
  72. package/tests/assets.test.ts +0 -393
  73. package/tsconfig.json +0 -7
@@ -1,290 +0,0 @@
1
- export type AssetBundleCacheEvictionPolicy = "lru" | "lfu" | "fifo" | "ttl" | "size";
2
-
3
- export interface AssetBundleCacheInput {
4
- readonly assetId: string;
5
- readonly url: string;
6
- readonly meshCount: number;
7
- readonly materialCount: number;
8
- readonly textureCount: number;
9
- readonly animationCount: number;
10
- readonly skinCount: number;
11
- readonly morphTargetCount: number;
12
- readonly decodedTextureBytes?: number;
13
- }
14
-
15
- export interface AssetBundleManifestEvidenceEntry {
16
- readonly id: string;
17
- readonly path: string;
18
- readonly type: "document" | "buffer" | "texture" | "material" | "animation" | "metadata";
19
- readonly sizeBytes: number;
20
- readonly dependencies: readonly string[];
21
- }
22
-
23
- export interface AssetBundleCacheEvidence {
24
- readonly source: "origin-master-asset-bundle-cache-adapted";
25
- readonly manifest: {
26
- readonly id: string;
27
- readonly version: "external-parity-generated";
28
- readonly assetCount: number;
29
- readonly totalBytes: number;
30
- readonly entries: readonly AssetBundleManifestEvidenceEntry[];
31
- };
32
- readonly dependencyGraph: {
33
- readonly rootAssetId: string;
34
- readonly loadOrder: readonly string[];
35
- readonly releaseOrder: readonly string[];
36
- readonly directDependencies: readonly string[];
37
- readonly transitiveDependencies: readonly string[];
38
- readonly cycleDetected: false;
39
- };
40
- readonly cache: {
41
- readonly policy: AssetBundleCacheEvictionPolicy;
42
- readonly maxEntries: number;
43
- readonly maxBytes: number;
44
- readonly cachedEntries: number;
45
- readonly memoryBytes: number;
46
- readonly hits: number;
47
- readonly misses: number;
48
- readonly evictions: number;
49
- readonly hitRate: number;
50
- readonly retainedAssetIds: readonly string[];
51
- readonly evictedAssetIds: readonly string[];
52
- };
53
- readonly productionReadiness: {
54
- readonly bundleManifest: true;
55
- readonly dependencySorting: true;
56
- readonly memoryBudgetEviction: true;
57
- readonly cacheTelemetry: true;
58
- readonly inFlightDeduplicationBoundary: true;
59
- };
60
- readonly blockedClaims: readonly string[];
61
- readonly claimBoundary: string;
62
- readonly hash: string;
63
- }
64
-
65
- export function createAssetBundleCacheEvidence(input: AssetBundleCacheInput): AssetBundleCacheEvidence {
66
- assertInput(input);
67
- const rootId = normalizeAssetId(input.assetId);
68
- const entries = createManifestEntries(rootId, input);
69
- const loadOrder = topologicalSort(entries);
70
- const releaseOrder = [...loadOrder].reverse();
71
- const cache = simulateCache(loadOrder, entries);
72
- const manifest = {
73
- id: `${rootId}-bundle`,
74
- version: "external-parity-generated" as const,
75
- assetCount: entries.length,
76
- totalBytes: sumBytes(entries),
77
- entries
78
- };
79
- const directDependencies = entries.find((entry) => entry.id === rootId)?.dependencies ?? [];
80
- const evidenceCore = {
81
- manifest,
82
- dependencyGraph: {
83
- rootAssetId: rootId,
84
- loadOrder,
85
- releaseOrder,
86
- directDependencies,
87
- transitiveDependencies: loadOrder.filter((id) => id !== rootId),
88
- cycleDetected: false as const
89
- },
90
- cache
91
- };
92
- return {
93
- source: "origin-master-asset-bundle-cache-adapted",
94
- ...evidenceCore,
95
- productionReadiness: {
96
- bundleManifest: true,
97
- dependencySorting: true,
98
- memoryBudgetEviction: true,
99
- cacheTelemetry: true,
100
- inFlightDeduplicationBoundary: true
101
- },
102
- blockedClaims: [
103
- "Unity Addressables catalog parity",
104
- "Unreal Asset Manager primary asset parity",
105
- "IndexedDB persistent cache certification",
106
- "CDN cache invalidation and patch streaming parity",
107
- "native platform asset bundle packaging parity"
108
- ],
109
- claimBoundary: "This evidence ports the old branch asset bundle/cache concepts into deterministic ExternalParity manifest, dependency-order, and memory-budget telemetry for loaded glTF assets. It does not claim Unity Addressables, Unreal Asset Manager, persistent browser storage, CDN patch streaming, or native bundle packaging parity.",
110
- hash: stableHash(JSON.stringify(evidenceCore))
111
- };
112
- }
113
-
114
- function createManifestEntries(rootId: string, input: AssetBundleCacheInput): readonly AssetBundleManifestEvidenceEntry[] {
115
- const textureCount = Math.max(0, Math.trunc(input.textureCount));
116
- const materialCount = Math.max(0, Math.trunc(input.materialCount));
117
- const meshCount = Math.max(0, Math.trunc(input.meshCount));
118
- const animationCount = Math.max(0, Math.trunc(input.animationCount));
119
- const textureIds = Array.from({ length: Math.max(1, textureCount) }, (_, index) => `${rootId}:texture-${index + 1}`);
120
- const entries: AssetBundleManifestEvidenceEntry[] = [
121
- {
122
- id: rootId,
123
- path: input.url,
124
- type: "document",
125
- sizeBytes: 2048 + materialCount * 256 + meshCount * 512,
126
- dependencies: [
127
- `${rootId}:geometry-buffer`,
128
- `${rootId}:material-manifest`,
129
- `${rootId}:metadata`,
130
- ...(animationCount > 0 ? [`${rootId}:animation`] : [])
131
- ]
132
- },
133
- {
134
- id: `${rootId}:geometry-buffer`,
135
- path: `${input.url}#geometry-buffer`,
136
- type: "buffer",
137
- sizeBytes: 8192 + meshCount * 32768 + input.skinCount * 4096 + input.morphTargetCount * 8192,
138
- dependencies: []
139
- },
140
- {
141
- id: `${rootId}:material-manifest`,
142
- path: `${input.url}#materials`,
143
- type: "material",
144
- sizeBytes: 1024 + materialCount * 768,
145
- dependencies: textureIds
146
- },
147
- {
148
- id: `${rootId}:metadata`,
149
- path: `${input.url}#loader-diagnostics`,
150
- type: "metadata",
151
- sizeBytes: 1024,
152
- dependencies: []
153
- }
154
- ];
155
-
156
- for (let index = 0; index < textureIds.length; index += 1) {
157
- entries.push({
158
- id: textureIds[index] ?? `${rootId}:texture-${index + 1}`,
159
- path: `${input.url}#texture-${index + 1}`,
160
- type: "texture",
161
- sizeBytes: Math.max(4096, Math.trunc((input.decodedTextureBytes ?? textureCount * 65536) / Math.max(1, textureCount))),
162
- dependencies: []
163
- });
164
- }
165
-
166
- if (animationCount > 0) {
167
- entries.push({
168
- id: `${rootId}:animation`,
169
- path: `${input.url}#animation`,
170
- type: "animation",
171
- sizeBytes: 2048 + animationCount * 4096,
172
- dependencies: [`${rootId}:geometry-buffer`]
173
- });
174
- }
175
-
176
- return entries;
177
- }
178
-
179
- function topologicalSort(entries: readonly AssetBundleManifestEvidenceEntry[]): readonly string[] {
180
- const byId = new Map(entries.map((entry) => [entry.id, entry]));
181
- const sorted: string[] = [];
182
- const visited = new Set<string>();
183
- const visiting = new Set<string>();
184
- const visit = (id: string): void => {
185
- if (visited.has(id)) return;
186
- if (visiting.has(id)) throw new Error(`Asset bundle dependency cycle detected: ${id}`);
187
- visiting.add(id);
188
- for (const dependency of byId.get(id)?.dependencies ?? []) {
189
- visit(dependency);
190
- }
191
- visiting.delete(id);
192
- visited.add(id);
193
- sorted.push(id);
194
- };
195
- for (const entry of entries) visit(entry.id);
196
- return sorted;
197
- }
198
-
199
- function simulateCache(loadOrder: readonly string[], entries: readonly AssetBundleManifestEvidenceEntry[]): AssetBundleCacheEvidence["cache"] {
200
- const byId = new Map(entries.map((entry) => [entry.id, entry]));
201
- const maxEntries = Math.max(3, Math.min(6, loadOrder.length - 1));
202
- const maxBytes = Math.max(32768, Math.trunc(sumBytes(entries) * 0.72));
203
- const retained = new Map<string, { readonly sizeBytes: number; accessCount: number; lastAccess: number }>();
204
- const evicted: string[] = [];
205
- let clock = 0;
206
- let memoryBytes = 0;
207
- let hits = 0;
208
- let misses = 0;
209
- let evictions = 0;
210
-
211
- for (const id of loadOrder) {
212
- clock += 1;
213
- const hit = retained.get(id);
214
- if (hit) {
215
- hit.accessCount += 1;
216
- hit.lastAccess = clock;
217
- hits += 1;
218
- continue;
219
- }
220
- misses += 1;
221
- const sizeBytes = byId.get(id)?.sizeBytes ?? 0;
222
- while ((retained.size >= maxEntries || memoryBytes + sizeBytes > maxBytes) && retained.size > 0) {
223
- const victim = [...retained.entries()].sort((left, right) => left[1].lastAccess - right[1].lastAccess)[0];
224
- if (!victim) break;
225
- retained.delete(victim[0]);
226
- memoryBytes -= victim[1].sizeBytes;
227
- evicted.push(victim[0]);
228
- evictions += 1;
229
- }
230
- if (sizeBytes <= maxBytes) {
231
- retained.set(id, { sizeBytes, accessCount: 1, lastAccess: clock });
232
- memoryBytes += sizeBytes;
233
- }
234
- }
235
-
236
- for (const id of loadOrder.slice(-2)) {
237
- if (retained.has(id)) {
238
- retained.get(id)!.accessCount += 1;
239
- hits += 1;
240
- } else {
241
- misses += 1;
242
- }
243
- }
244
-
245
- return {
246
- policy: "lru",
247
- maxEntries,
248
- maxBytes,
249
- cachedEntries: retained.size,
250
- memoryBytes,
251
- hits,
252
- misses,
253
- evictions,
254
- hitRate: Number((hits / Math.max(1, hits + misses)).toFixed(4)),
255
- retainedAssetIds: [...retained.keys()],
256
- evictedAssetIds: evicted
257
- };
258
- }
259
-
260
- function assertInput(input: AssetBundleCacheInput): void {
261
- if (input.assetId.trim().length === 0) throw new Error("assetId is required.");
262
- if (input.url.trim().length === 0) throw new Error("url is required.");
263
- for (const [name, value] of Object.entries({
264
- meshCount: input.meshCount,
265
- materialCount: input.materialCount,
266
- textureCount: input.textureCount,
267
- animationCount: input.animationCount,
268
- skinCount: input.skinCount,
269
- morphTargetCount: input.morphTargetCount
270
- })) {
271
- if (!Number.isFinite(value) || value < 0) throw new Error(`${name} must be a non-negative finite number.`);
272
- }
273
- }
274
-
275
- function normalizeAssetId(id: string): string {
276
- return id.trim().toLowerCase().replace(/[^a-z0-9]+/g, "-").replace(/^-+|-+$/g, "") || "asset";
277
- }
278
-
279
- function sumBytes(entries: readonly AssetBundleManifestEvidenceEntry[]): number {
280
- return entries.reduce((sum, entry) => sum + entry.sizeBytes, 0);
281
- }
282
-
283
- function stableHash(value: string): string {
284
- let hash = 2166136261;
285
- for (let index = 0; index < value.length; index += 1) {
286
- hash ^= value.charCodeAt(index);
287
- hash = Math.imul(hash, 16777619);
288
- }
289
- return (hash >>> 0).toString(16).padStart(8, "0");
290
- }
package/src/AssetCache.ts DELETED
@@ -1,76 +0,0 @@
1
- import { AssetHandle } from "./AssetHandle";
2
-
3
- interface CacheEntry<T> {
4
- readonly handle: AssetHandle<T>;
5
- readonly promise?: Promise<AssetHandle<T>>;
6
- }
7
-
8
- export interface AssetCacheSnapshot {
9
- readonly cachedEntries: number;
10
- readonly inFlightEntries: number;
11
- readonly totalEntries: number;
12
- readonly keys: readonly string[];
13
- }
14
-
15
- export class AssetCache {
16
- private readonly entries = new Map<string, CacheEntry<unknown>>();
17
- private readonly inFlight = new Map<string, Promise<AssetHandle<unknown>>>();
18
-
19
- has(key: string): boolean {
20
- return this.entries.has(key) || this.inFlight.has(key);
21
- }
22
-
23
- get<T>(key: string): AssetHandle<T> | undefined {
24
- const entry = this.entries.get(key);
25
- if (!entry) {
26
- return undefined;
27
- }
28
-
29
- return entry.handle as AssetHandle<T>;
30
- }
31
-
32
- getInFlight<T>(key: string): Promise<AssetHandle<T>> | undefined {
33
- return this.inFlight.get(key) as Promise<AssetHandle<T>> | undefined;
34
- }
35
-
36
- set<T>(key: string, handle: AssetHandle<T>): void {
37
- this.entries.set(key, { handle: handle as unknown as AssetHandle<unknown> });
38
- }
39
-
40
- setInFlight<T>(key: string, promise: Promise<AssetHandle<T>>): void {
41
- this.inFlight.set(key, promise as Promise<AssetHandle<unknown>>);
42
- promise
43
- .then((handle) => {
44
- this.entries.set(key, { handle: handle as unknown as AssetHandle<unknown> });
45
- })
46
- .catch(() => {
47
- // Failed loads are intentionally not cached; callers receive the original rejection.
48
- })
49
- .finally(() => {
50
- this.inFlight.delete(key);
51
- });
52
- }
53
-
54
- delete(key: string): boolean {
55
- return this.entries.delete(key);
56
- }
57
-
58
- clear(): void {
59
- this.entries.clear();
60
- this.inFlight.clear();
61
- }
62
-
63
- keys(): readonly string[] {
64
- return [...new Set([...this.entries.keys(), ...this.inFlight.keys()])];
65
- }
66
-
67
- snapshot(): AssetCacheSnapshot {
68
- const keys = this.keys();
69
- return {
70
- cachedEntries: this.entries.size,
71
- inFlightEntries: this.inFlight.size,
72
- totalEntries: keys.length,
73
- keys
74
- };
75
- }
76
- }
@@ -1,224 +0,0 @@
1
- import {
2
- assertValidGLTFCorpusManifest,
3
- createGLTFCorpusReport,
4
- type AssetDiagnostic,
5
- type AssetImportSettings,
6
- type GLTFCorpusAssetReport,
7
- type GLTFCorpusManifest,
8
- type GLTFCorpusReport
9
- } from "./AssetCorpus";
10
- import type { BlenderExportValidationReport } from "./BlenderExportValidation";
11
-
12
- export type AssetCompatibilityLoaderName = "aura3d" | "threejs" | "babylonjs" | "blender-export";
13
- export type AssetCompatibilityStatus = "pass" | "warn" | "expected-fail" | "not-run";
14
-
15
- export interface AssetLoaderCompatibilityResult {
16
- readonly loader: AssetCompatibilityLoaderName;
17
- readonly status: AssetCompatibilityStatus;
18
- readonly diagnostics: readonly AssetDiagnostic[];
19
- }
20
-
21
- export interface AssetCompatibilityReportAsset {
22
- readonly id: string;
23
- readonly name: string;
24
- readonly format: string;
25
- readonly tags: readonly string[];
26
- readonly sourceUri: string;
27
- readonly sourceRevision: string;
28
- readonly sourceSha256: string;
29
- readonly importSettings: AssetImportSettings;
30
- readonly loaders: readonly AssetLoaderCompatibilityResult[];
31
- }
32
-
33
- export interface AssetCompatibilityReport {
34
- readonly schemaVersion: "asset-compatibility-report";
35
- readonly generatedAt: string;
36
- readonly sourceManifest: GLTFCorpusReport["sourceManifest"];
37
- readonly fixtureStatus: {
38
- readonly blenderExportFixtures: "present" | "missing";
39
- };
40
- readonly blenderExportValidation?: BlenderExportValidationReport;
41
- readonly summary: {
42
- readonly assetCount: number;
43
- readonly aura3d: Record<AssetCompatibilityStatus, number>;
44
- readonly threejs: Record<AssetCompatibilityStatus, number>;
45
- readonly babylonjs: Record<AssetCompatibilityStatus, number>;
46
- readonly blenderExport: Record<AssetCompatibilityStatus, number>;
47
- };
48
- readonly assets: readonly AssetCompatibilityReportAsset[];
49
- }
50
-
51
- export interface AssetCompatibilityReportOptions {
52
- readonly generatedAt?: string;
53
- readonly blenderExportFixturesPresent?: boolean;
54
- readonly blenderExportValidation?: BlenderExportValidationReport;
55
- readonly externalLoaderResults?: readonly ExternalAssetLoaderCompatibilityResult[];
56
- readonly blenderExportResults?: readonly BlenderExportCompatibilityResult[];
57
- }
58
-
59
- export interface ExternalAssetLoaderCompatibilityResult extends AssetLoaderCompatibilityResult {
60
- readonly assetId: string;
61
- readonly loader: "threejs" | "babylonjs";
62
- }
63
-
64
- export interface BlenderExportCompatibilityResult extends AssetLoaderCompatibilityResult {
65
- readonly assetId: string;
66
- readonly loader: "blender-export";
67
- }
68
-
69
- export function createAssetCompatibilityReport(
70
- manifest: GLTFCorpusManifest,
71
- options: AssetCompatibilityReportOptions = {}
72
- ): AssetCompatibilityReport {
73
- const validated = assertValidGLTFCorpusManifest(manifest);
74
- const corpusReport = createGLTFCorpusReport(validated, options.generatedAt ?? new Date().toISOString());
75
- const blenderExportFixturesPresent = options.blenderExportFixturesPresent ?? options.blenderExportValidation !== undefined;
76
- const externalResults = indexExternalLoaderResults(options.externalLoaderResults ?? []);
77
- const blenderExportResults = indexBlenderExportResults(options.blenderExportResults ?? []);
78
- const assets = corpusReport.assets.map((asset) => createCompatibilityAsset(asset, blenderExportFixturesPresent, externalResults, blenderExportResults));
79
-
80
- return {
81
- schemaVersion: "asset-compatibility-report",
82
- generatedAt: corpusReport.generatedAt,
83
- sourceManifest: corpusReport.sourceManifest,
84
- fixtureStatus: {
85
- blenderExportFixtures: blenderExportFixturesPresent ? "present" : "missing"
86
- },
87
- ...(options.blenderExportValidation ? { blenderExportValidation: options.blenderExportValidation } : {}),
88
- summary: {
89
- assetCount: assets.length,
90
- aura3d: summarize(assets, "aura3d"),
91
- threejs: summarize(assets, "threejs"),
92
- babylonjs: summarize(assets, "babylonjs"),
93
- blenderExport: summarize(assets, "blender-export")
94
- },
95
- assets
96
- };
97
- }
98
-
99
- function createCompatibilityAsset(
100
- asset: GLTFCorpusAssetReport,
101
- blenderExportFixturesPresent: boolean,
102
- externalResults: ReadonlyMap<string, AssetLoaderCompatibilityResult>,
103
- blenderExportResults: ReadonlyMap<string, AssetLoaderCompatibilityResult>
104
- ): AssetCompatibilityReportAsset {
105
- return {
106
- id: asset.id,
107
- name: asset.name,
108
- format: asset.format,
109
- tags: asset.tags,
110
- sourceUri: asset.sourceUri,
111
- sourceRevision: asset.sourceRevision,
112
- sourceSha256: asset.sourceSha256,
113
- importSettings: asset.importSettings,
114
- loaders: [
115
- {
116
- loader: "aura3d",
117
- status: asset.expectedStatus === "expected-fail" ? "expected-fail" : asset.expectedStatus,
118
- diagnostics: asset.diagnostics
119
- },
120
- externalResults.get(externalResultKey(asset.id, "threejs")) ?? externalLoaderScaffold("threejs", asset),
121
- externalResults.get(externalResultKey(asset.id, "babylonjs")) ?? externalLoaderScaffold("babylonjs", asset),
122
- blenderExportResults.get(asset.id) ?? blenderExportScaffold(asset, blenderExportFixturesPresent)
123
- ]
124
- };
125
- }
126
-
127
- function indexExternalLoaderResults(
128
- results: readonly ExternalAssetLoaderCompatibilityResult[]
129
- ): ReadonlyMap<string, AssetLoaderCompatibilityResult> {
130
- const indexed = new Map<string, AssetLoaderCompatibilityResult>();
131
- for (const result of results) {
132
- indexed.set(externalResultKey(result.assetId, result.loader), {
133
- loader: result.loader,
134
- status: result.status,
135
- diagnostics: result.diagnostics
136
- });
137
- }
138
- return indexed;
139
- }
140
-
141
- function indexBlenderExportResults(
142
- results: readonly BlenderExportCompatibilityResult[]
143
- ): ReadonlyMap<string, AssetLoaderCompatibilityResult> {
144
- const indexed = new Map<string, AssetLoaderCompatibilityResult>();
145
- for (const result of results) {
146
- indexed.set(result.assetId, {
147
- loader: result.loader,
148
- status: result.status,
149
- diagnostics: result.diagnostics
150
- });
151
- }
152
- return indexed;
153
- }
154
-
155
- function externalResultKey(assetId: string, loader: "threejs" | "babylonjs"): string {
156
- return `${assetId}:${loader}`;
157
- }
158
-
159
- function externalLoaderScaffold(loader: "threejs" | "babylonjs", asset: GLTFCorpusAssetReport): AssetLoaderCompatibilityResult {
160
- return {
161
- loader,
162
- status: "not-run",
163
- diagnostics: [
164
- diagnostic(
165
- `ASSET_${loader.toUpperCase()}_LOADER_NOT_RUN`,
166
- "External loader compatibility has not been executed in this bounded slice.",
167
- `Run ${asset.id} through a pinned ${loader} GLTFLoader version and record pass, warn, or expected-fail evidence before claiming ${loader} parity.`
168
- )
169
- ]
170
- };
171
- }
172
-
173
- function blenderExportScaffold(asset: GLTFCorpusAssetReport, fixturesPresent: boolean): AssetLoaderCompatibilityResult {
174
- if (fixturesPresent) {
175
- return {
176
- loader: "blender-export",
177
- status: "not-run",
178
- diagnostics: [
179
- diagnostic(
180
- "ASSET_BLENDER_EXPORT_VALIDATION_PENDING",
181
- "Blender export fixtures were detected, but no export validation runner is wired in this slice.",
182
- `Run ${asset.id} or the matching fixture through the Blender exporter validation command and record the exported glTF diagnostics.`
183
- )
184
- ]
185
- };
186
- }
187
- return {
188
- loader: "blender-export",
189
- status: "not-run",
190
- diagnostics: [
191
- diagnostic(
192
- "ASSET_BLENDER_EXPORT_FIXTURES_MISSING",
193
- "No Blender-export fixtures are present in the asset test corpus.",
194
- "Add checked-in exported glTF/GLB fixtures before marking Blender-export validation complete."
195
- )
196
- ]
197
- };
198
- }
199
-
200
- function summarize(
201
- assets: readonly AssetCompatibilityReportAsset[],
202
- loader: AssetCompatibilityLoaderName
203
- ): Record<AssetCompatibilityStatus, number> {
204
- const counts: Record<AssetCompatibilityStatus, number> = {
205
- pass: 0,
206
- warn: 0,
207
- "expected-fail": 0,
208
- "not-run": 0
209
- };
210
- for (const asset of assets) {
211
- const result = asset.loaders.find((entry) => entry.loader === loader);
212
- counts[result?.status ?? "not-run"] += 1;
213
- }
214
- return counts;
215
- }
216
-
217
- function diagnostic(code: string, message: string, nextAction: string): AssetDiagnostic {
218
- return {
219
- code,
220
- severity: "info",
221
- message,
222
- nextAction
223
- };
224
- }