@forgeax/engine-pack 0.1.28 → 0.1.29

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 (41) hide show
  1. package/README.md +17 -5
  2. package/dist/build.mjs +34 -51
  3. package/dist/build.mjs.map +1 -1
  4. package/dist/cli-asset.mjs +21 -12
  5. package/dist/cli-asset.mjs.map +1 -1
  6. package/dist/index.mjs.map +1 -1
  7. package/dist/inventory/declaration.d.ts +1 -1
  8. package/dist/inventory/declaration.d.ts.map +1 -1
  9. package/dist/inventory/sync.d.ts.map +1 -1
  10. package/dist/native-cooker-registry.d.ts +1 -0
  11. package/dist/native-cooker-registry.d.ts.map +1 -1
  12. package/dist/native-cooker.mjs.map +1 -1
  13. package/dist/pack-authoring-node.mjs +21 -12
  14. package/dist/pack-authoring-node.mjs.map +1 -1
  15. package/dist/runtime.mjs.map +1 -1
  16. package/dist/scanner.mjs +21 -12
  17. package/dist/scanner.mjs.map +1 -1
  18. package/dist/schema-compiled.d.ts.map +1 -1
  19. package/dist/schema.mjs +40 -62
  20. package/dist/schema.mjs.map +1 -1
  21. package/dist/scriptable-pack-node.d.ts.map +1 -1
  22. package/dist/scriptable-pack-node.mjs +11 -3
  23. package/dist/scriptable-pack-node.mjs.map +1 -1
  24. package/dist/scriptable-pack-worker.mjs +5 -1
  25. package/dist/scriptable-pack-worker.mjs.map +1 -1
  26. package/package.json +2 -2
  27. package/src/__tests__/inventory-schema.test.ts +10 -10
  28. package/src/__tests__/pack-authoring.unit.test.ts +2 -4
  29. package/src/__tests__/pack.unit.test.ts +86 -124
  30. package/src/__tests__/scanner-instance-cycle.test.ts +92 -0
  31. package/src/__tests__/scanner-inventory.contract.test.ts +2 -2
  32. package/src/__tests__/scriptable-pack-diagnostic.integration.test.ts +45 -0
  33. package/src/inventory/binding.ts +3 -3
  34. package/src/inventory/declaration.ts +10 -48
  35. package/src/inventory/sync.ts +3 -2
  36. package/src/native-cooker-registry.ts +1 -0
  37. package/src/scanner.ts +28 -28
  38. package/src/schema-compiled.ts +40 -79
  39. package/src/scriptable-pack-node.ts +11 -3
  40. package/src/scriptable-pack-worker.ts +8 -1
  41. package/src/__tests__/scanner-mount-cycle.test.ts +0 -232
@@ -149,7 +149,11 @@ function compileModuleClosure(entryPath, outputRoot, loadId) {
149
149
  ) ?? [];
150
150
  if (errors.length > 0) {
151
151
  throw new Error(
152
- `ScriptablePack TypeScript transpile failed for ${path}: ${errors.map((diagnostic) => ts.flattenDiagnosticMessageText(diagnostic.messageText, "\n")).join("; ")}`
152
+ `ScriptablePack TypeScript transpile failed for ${path}: ${errors.map((diagnostic) => {
153
+ const position = diagnostic.file?.getLineAndCharacterOfPosition(diagnostic.start ?? 0);
154
+ const location = position === void 0 ? path : `${path}:${position.line + 1}:${position.character + 1}`;
155
+ return `${location}: ${ts.flattenDiagnosticMessageText(diagnostic.messageText, "\n")}`;
156
+ }).join("; ")}`
153
157
  );
154
158
  }
155
159
  writeFileSync(join(outputRoot, outputName(path)), transpiled.outputText);
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/scriptable-pack-worker.ts"],"names":["resolve"],"mappings":"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","file":"scriptable-pack-worker.mjs","sourcesContent":["import { createHash } from 'node:crypto';\nimport { existsSync, readFileSync, writeFileSync } from 'node:fs';\nimport { dirname, extname, join, resolve } from 'node:path';\nimport { fileURLToPath, pathToFileURL } from 'node:url';\nimport { parentPort, workerData } from 'node:worker_threads';\nimport ts from 'typescript';\n\nif (parentPort === null) throw new Error('ScriptablePack worker requires a parent port');\n\nconst port = parentPort;\nconst workerMode = workerData as {\n readonly mode?: 'single' | 'reusable';\n readonly sourcePath?: string;\n readonly compileRoot?: string;\n};\nconst reusable = workerMode.mode === 'reusable';\nlet sourcePath = workerMode.sourcePath;\nlet compileRoot = workerMode.compileRoot;\ntype LoadedDefinition = {\n readonly schemaVersion?: unknown;\n readonly packageId?: unknown;\n readonly name?: unknown;\n readonly parameters?: unknown;\n readonly sceneComponents?: unknown;\n readonly build?: unknown;\n};\nlet definition: LoadedDefinition | undefined;\nlet nextReadId = 0;\nconst reads = new Map<number, (value: unknown) => void>();\n\nfunction failure(error: unknown): unknown {\n if (error === null || typeof error !== 'object') return error;\n const value = error as Record<string, unknown>;\n return {\n name: value.name,\n code: value.code,\n expected: value.expected,\n actual: value.actual,\n hint: value.hint,\n detail: value.detail,\n message: value.message,\n };\n}\n\nfunction serializableSceneComponents(value: unknown): unknown {\n if (!Array.isArray(value)) return undefined;\n return value.map((component) => {\n const candidate = component as { readonly name?: unknown; readonly fields?: unknown };\n const fields =\n candidate.fields !== null &&\n typeof candidate.fields === 'object' &&\n !Array.isArray(candidate.fields)\n ? Object.fromEntries(\n Object.entries(candidate.fields as Record<string, unknown>).map(([name, field]) => [\n name,\n typeof field === 'string' ? field : (field as { readonly type?: unknown })?.type,\n ]),\n )\n : undefined;\n return { name: candidate.name, fields };\n });\n}\n\nconst SOURCE_EXTENSIONS = ['.ts', '.tsx', '.mts', '.cts', '.js', '.mjs', '.cjs'] as const;\n\nfunction resolveRelativeImport(importer: string, specifier: string): string | undefined {\n if (!specifier.startsWith('.')) return undefined;\n const raw = resolve(dirname(importer), specifier);\n const candidates =\n extname(raw).length > 0\n ? [raw]\n : [\n raw,\n ...SOURCE_EXTENSIONS.map((extension) => `${raw}${extension}`),\n resolve(raw, 'index.ts'),\n ];\n return candidates.find((candidate) => existsSync(candidate));\n}\n\nfunction outputName(path: string): string {\n const digest = createHash('sha256').update(path).digest('hex').slice(0, 20);\n return `${digest}.mjs`;\n}\n\nfunction resolveBareImport(importer: string, specifier: string): string | undefined {\n const resolver = (\n import.meta as ImportMeta & {\n readonly resolve?: (specifier: string, parent?: string) => string;\n }\n ).resolve;\n if (resolver === undefined) return undefined;\n // Resolve consumer dependencies from the source file first, then resolve\n // Engine self-references from this worker's package. A compiled source\n // closure lives in /tmp, so using only its path makes\n // @forgeax/engine-pack/source disappear from Node's lookup ancestry.\n for (const parent of [pathToFileURL(importer).href, import.meta.url]) {\n try {\n const resolved = resolver(specifier, parent);\n if (resolved.startsWith('file:')) return fileURLToPath(resolved);\n } catch {\n // Try the loader package before treating the import as unavailable.\n }\n }\n return undefined;\n}\n\nfunction resolvedModuleSpecifier(\n target: string,\n sources: ReadonlyMap<string, string>,\n loadId?: number,\n): string {\n if (sources.has(target)) {\n return `./${outputName(target)}${loadId === undefined ? '' : `?load=${loadId}`}`;\n }\n return pathToFileURL(target).href;\n}\n\n/**\n * Node 22 does not execute `.ts` modules. Compile the trusted local module\n * closure into one disposable ESM directory before importing it. Bare package\n * imports are resolved by Node from the original source module and rewritten\n * to their canonical file URL; relative imports are rewritten to the compiled\n * closure by identity. This keeps unrelated nested node_modules directories\n * (for example Vite's cache) from changing package resolution.\n */\nfunction compileModuleClosure(entryPath: string, outputRoot: string, loadId?: number): string {\n const pending = [resolve(entryPath)];\n const sources = new Map<string, string>();\n const imports = new Map<string, ReadonlyMap<string, string>>();\n while (pending.length > 0) {\n const path = pending.pop();\n if (path === undefined || sources.has(path)) continue;\n const source = readFileSync(path, 'utf8');\n sources.set(path, source);\n const resolvedImports = new Map<string, string>();\n for (const imported of ts.preProcessFile(source, true, true).importedFiles) {\n const target = imported.fileName.startsWith('.')\n ? resolveRelativeImport(path, imported.fileName)\n : resolveBareImport(path, imported.fileName);\n if (target === undefined) continue;\n resolvedImports.set(imported.fileName, target);\n if (imported.fileName.startsWith('.') || /\\.(?:ts|tsx|mts|cts)$/.test(target)) {\n pending.push(target);\n }\n }\n imports.set(path, resolvedImports);\n }\n\n for (const [path, source] of sources) {\n const replacements = imports.get(path) ?? new Map<string, string>();\n const rewriteRelativeImports: ts.TransformerFactory<ts.SourceFile> = (context) => {\n const visit: ts.Visitor = (node) => {\n if (\n (ts.isImportDeclaration(node) || ts.isExportDeclaration(node)) &&\n node.moduleSpecifier !== undefined &&\n ts.isStringLiteral(node.moduleSpecifier)\n ) {\n const target = replacements.get(node.moduleSpecifier.text);\n if (target !== undefined) {\n const moduleSpecifier = ts.factory.createStringLiteral(\n resolvedModuleSpecifier(target, sources, loadId),\n );\n return ts.isImportDeclaration(node)\n ? ts.factory.updateImportDeclaration(\n node,\n node.modifiers,\n node.importClause,\n moduleSpecifier,\n node.attributes,\n )\n : ts.factory.updateExportDeclaration(\n node,\n node.modifiers,\n node.isTypeOnly,\n node.exportClause,\n moduleSpecifier,\n node.attributes,\n );\n }\n }\n if (\n ts.isCallExpression(node) &&\n node.expression.kind === ts.SyntaxKind.ImportKeyword &&\n node.arguments.length === 1 &&\n node.arguments[0] !== undefined &&\n ts.isStringLiteral(node.arguments[0])\n ) {\n const target = replacements.get(node.arguments[0].text);\n if (target !== undefined) {\n return ts.factory.updateCallExpression(node, node.expression, node.typeArguments, [\n ts.factory.createStringLiteral(resolvedModuleSpecifier(target, sources, loadId)),\n ]);\n }\n }\n return ts.visitEachChild(node, visit, context);\n };\n return (sourceFile) => ts.visitNode(sourceFile, visit) as ts.SourceFile;\n };\n const transpiled = ts.transpileModule(source, {\n fileName: path,\n compilerOptions: {\n target: ts.ScriptTarget.ES2022,\n module: ts.ModuleKind.ESNext,\n moduleResolution: ts.ModuleResolutionKind.Bundler,\n isolatedModules: true,\n sourceMap: false,\n },\n transformers: { before: [rewriteRelativeImports] },\n reportDiagnostics: true,\n });\n const errors =\n transpiled.diagnostics?.filter(\n (diagnostic) => diagnostic.category === ts.DiagnosticCategory.Error,\n ) ?? [];\n if (errors.length > 0) {\n throw new Error(\n `ScriptablePack TypeScript transpile failed for ${path}: ${errors\n .map((diagnostic) => ts.flattenDiagnosticMessageText(diagnostic.messageText, '\\n'))\n .join('; ')}`,\n );\n }\n writeFileSync(join(outputRoot, outputName(path)), transpiled.outputText);\n }\n return join(outputRoot, outputName(resolve(entryPath)));\n}\n\nasync function loadDefinition(task: {\n readonly sourcePath: string;\n readonly compileRoot: string;\n readonly loadId?: number;\n}): Promise<void> {\n sourcePath = task.sourcePath;\n compileRoot = task.compileRoot;\n definition = undefined;\n reads.clear();\n const executablePath = SOURCE_EXTENSIONS.includes(\n extname(task.sourcePath) as (typeof SOURCE_EXTENSIONS)[number],\n )\n ? compileModuleClosure(task.sourcePath, task.compileRoot, task.loadId)\n : task.sourcePath;\n const loaded = (await import(\n `${pathToFileURL(executablePath).href}${reusable ? `?load=${task.loadId ?? 0}` : ''}`\n )) as {\n readonly default?: unknown;\n };\n definition = loaded.default as LoadedDefinition | undefined;\n port.postMessage({\n kind: 'loaded',\n ...(task.loadId === undefined ? {} : { loadId: task.loadId }),\n definition:\n definition === undefined\n ? undefined\n : {\n schemaVersion: definition.schemaVersion,\n packageId: definition.packageId,\n ...(definition.name === undefined ? {} : { name: definition.name }),\n ...(definition.parameters === undefined ? {} : { parameters: definition.parameters }),\n ...(definition.sceneComponents === undefined\n ? {}\n : { sceneComponents: serializableSceneComponents(definition.sceneComponents) }),\n },\n });\n}\n\nasync function handleMessage(message: unknown): Promise<void> {\n if (message === null || typeof message !== 'object') return;\n const value = message as Record<string, unknown>;\n if (value.kind === 'asset-result' && typeof value.readId === 'number') {\n reads.get(value.readId)?.(value.result);\n reads.delete(value.readId);\n return;\n }\n if (\n reusable &&\n value.kind === 'load' &&\n typeof value.sourcePath === 'string' &&\n typeof value.compileRoot === 'string' &&\n typeof value.loadId === 'number'\n ) {\n try {\n await loadDefinition({\n sourcePath: value.sourcePath,\n compileRoot: value.compileRoot,\n loadId: value.loadId,\n });\n } catch (error) {\n port.postMessage({ kind: 'load-threw', loadId: value.loadId, error: failure(error) });\n }\n return;\n }\n if (value.kind !== 'build' || typeof value.buildId !== 'number') return;\n if (typeof definition?.build !== 'function') {\n port.postMessage({\n kind: 'build-result',\n buildId: value.buildId,\n result: { ok: false, error: { message: 'default export has no build function' } },\n });\n return;\n }\n try {\n const context =\n value.context !== null && typeof value.context === 'object'\n ? (value.context as { readonly packageId?: unknown; readonly values?: unknown })\n : undefined;\n const buildContext = {\n ...(Array.isArray(context?.packageId)\n ? { packageId: new Uint8Array(context.packageId as number[]) }\n : {}),\n ...(context?.values !== undefined ? { values: context.values } : {}),\n readByGuid(guid: Uint8Array): Promise<unknown> {\n const readId = nextReadId++;\n port.postMessage({ kind: 'asset-read', buildId: value.buildId, readId, guid });\n return new Promise((resolve) => reads.set(readId, resolve));\n },\n };\n const result = await definition.build(buildContext);\n port.postMessage({\n kind: 'build-result',\n buildId: value.buildId,\n result:\n result !== null && typeof result === 'object' && 'ok' in result && !result.ok\n ? { ...result, error: failure(result.error) }\n : result,\n });\n } catch (error) {\n port.postMessage({ kind: 'build-threw', buildId: value.buildId, error: failure(error) });\n }\n}\n\nport.on('message', (message: unknown) => {\n void handleMessage(message);\n});\n\nif (!reusable && sourcePath !== undefined && compileRoot !== undefined) {\n try {\n await loadDefinition({ sourcePath, compileRoot });\n } catch (error) {\n port.postMessage({ kind: 'load-threw', error: failure(error) });\n }\n}\n"]}
1
+ {"version":3,"sources":["../src/scriptable-pack-worker.ts"],"names":["resolve"],"mappings":"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","file":"scriptable-pack-worker.mjs","sourcesContent":["import { createHash } from 'node:crypto';\nimport { existsSync, readFileSync, writeFileSync } from 'node:fs';\nimport { dirname, extname, join, resolve } from 'node:path';\nimport { fileURLToPath, pathToFileURL } from 'node:url';\nimport { parentPort, workerData } from 'node:worker_threads';\nimport ts from 'typescript';\n\nif (parentPort === null) throw new Error('ScriptablePack worker requires a parent port');\n\nconst port = parentPort;\nconst workerMode = workerData as {\n readonly mode?: 'single' | 'reusable';\n readonly sourcePath?: string;\n readonly compileRoot?: string;\n};\nconst reusable = workerMode.mode === 'reusable';\nlet sourcePath = workerMode.sourcePath;\nlet compileRoot = workerMode.compileRoot;\ntype LoadedDefinition = {\n readonly schemaVersion?: unknown;\n readonly packageId?: unknown;\n readonly name?: unknown;\n readonly parameters?: unknown;\n readonly sceneComponents?: unknown;\n readonly build?: unknown;\n};\nlet definition: LoadedDefinition | undefined;\nlet nextReadId = 0;\nconst reads = new Map<number, (value: unknown) => void>();\n\nfunction failure(error: unknown): unknown {\n if (error === null || typeof error !== 'object') return error;\n const value = error as Record<string, unknown>;\n return {\n name: value.name,\n code: value.code,\n expected: value.expected,\n actual: value.actual,\n hint: value.hint,\n detail: value.detail,\n message: value.message,\n };\n}\n\nfunction serializableSceneComponents(value: unknown): unknown {\n if (!Array.isArray(value)) return undefined;\n return value.map((component) => {\n const candidate = component as { readonly name?: unknown; readonly fields?: unknown };\n const fields =\n candidate.fields !== null &&\n typeof candidate.fields === 'object' &&\n !Array.isArray(candidate.fields)\n ? Object.fromEntries(\n Object.entries(candidate.fields as Record<string, unknown>).map(([name, field]) => [\n name,\n typeof field === 'string' ? field : (field as { readonly type?: unknown })?.type,\n ]),\n )\n : undefined;\n return { name: candidate.name, fields };\n });\n}\n\nconst SOURCE_EXTENSIONS = ['.ts', '.tsx', '.mts', '.cts', '.js', '.mjs', '.cjs'] as const;\n\nfunction resolveRelativeImport(importer: string, specifier: string): string | undefined {\n if (!specifier.startsWith('.')) return undefined;\n const raw = resolve(dirname(importer), specifier);\n const candidates =\n extname(raw).length > 0\n ? [raw]\n : [\n raw,\n ...SOURCE_EXTENSIONS.map((extension) => `${raw}${extension}`),\n resolve(raw, 'index.ts'),\n ];\n return candidates.find((candidate) => existsSync(candidate));\n}\n\nfunction outputName(path: string): string {\n const digest = createHash('sha256').update(path).digest('hex').slice(0, 20);\n return `${digest}.mjs`;\n}\n\nfunction resolveBareImport(importer: string, specifier: string): string | undefined {\n const resolver = (\n import.meta as ImportMeta & {\n readonly resolve?: (specifier: string, parent?: string) => string;\n }\n ).resolve;\n if (resolver === undefined) return undefined;\n // Resolve consumer dependencies from the source file first, then resolve\n // Engine self-references from this worker's package. A compiled source\n // closure lives in /tmp, so using only its path makes\n // @forgeax/engine-pack/source disappear from Node's lookup ancestry.\n for (const parent of [pathToFileURL(importer).href, import.meta.url]) {\n try {\n const resolved = resolver(specifier, parent);\n if (resolved.startsWith('file:')) return fileURLToPath(resolved);\n } catch {\n // Try the loader package before treating the import as unavailable.\n }\n }\n return undefined;\n}\n\nfunction resolvedModuleSpecifier(\n target: string,\n sources: ReadonlyMap<string, string>,\n loadId?: number,\n): string {\n if (sources.has(target)) {\n return `./${outputName(target)}${loadId === undefined ? '' : `?load=${loadId}`}`;\n }\n return pathToFileURL(target).href;\n}\n\n/**\n * Node 22 does not execute `.ts` modules. Compile the trusted local module\n * closure into one disposable ESM directory before importing it. Bare package\n * imports are resolved by Node from the original source module and rewritten\n * to their canonical file URL; relative imports are rewritten to the compiled\n * closure by identity. This keeps unrelated nested node_modules directories\n * (for example Vite's cache) from changing package resolution.\n */\nfunction compileModuleClosure(entryPath: string, outputRoot: string, loadId?: number): string {\n const pending = [resolve(entryPath)];\n const sources = new Map<string, string>();\n const imports = new Map<string, ReadonlyMap<string, string>>();\n while (pending.length > 0) {\n const path = pending.pop();\n if (path === undefined || sources.has(path)) continue;\n const source = readFileSync(path, 'utf8');\n sources.set(path, source);\n const resolvedImports = new Map<string, string>();\n for (const imported of ts.preProcessFile(source, true, true).importedFiles) {\n const target = imported.fileName.startsWith('.')\n ? resolveRelativeImport(path, imported.fileName)\n : resolveBareImport(path, imported.fileName);\n if (target === undefined) continue;\n resolvedImports.set(imported.fileName, target);\n if (imported.fileName.startsWith('.') || /\\.(?:ts|tsx|mts|cts)$/.test(target)) {\n pending.push(target);\n }\n }\n imports.set(path, resolvedImports);\n }\n\n for (const [path, source] of sources) {\n const replacements = imports.get(path) ?? new Map<string, string>();\n const rewriteRelativeImports: ts.TransformerFactory<ts.SourceFile> = (context) => {\n const visit: ts.Visitor = (node) => {\n if (\n (ts.isImportDeclaration(node) || ts.isExportDeclaration(node)) &&\n node.moduleSpecifier !== undefined &&\n ts.isStringLiteral(node.moduleSpecifier)\n ) {\n const target = replacements.get(node.moduleSpecifier.text);\n if (target !== undefined) {\n const moduleSpecifier = ts.factory.createStringLiteral(\n resolvedModuleSpecifier(target, sources, loadId),\n );\n return ts.isImportDeclaration(node)\n ? ts.factory.updateImportDeclaration(\n node,\n node.modifiers,\n node.importClause,\n moduleSpecifier,\n node.attributes,\n )\n : ts.factory.updateExportDeclaration(\n node,\n node.modifiers,\n node.isTypeOnly,\n node.exportClause,\n moduleSpecifier,\n node.attributes,\n );\n }\n }\n if (\n ts.isCallExpression(node) &&\n node.expression.kind === ts.SyntaxKind.ImportKeyword &&\n node.arguments.length === 1 &&\n node.arguments[0] !== undefined &&\n ts.isStringLiteral(node.arguments[0])\n ) {\n const target = replacements.get(node.arguments[0].text);\n if (target !== undefined) {\n return ts.factory.updateCallExpression(node, node.expression, node.typeArguments, [\n ts.factory.createStringLiteral(resolvedModuleSpecifier(target, sources, loadId)),\n ]);\n }\n }\n return ts.visitEachChild(node, visit, context);\n };\n return (sourceFile) => ts.visitNode(sourceFile, visit) as ts.SourceFile;\n };\n const transpiled = ts.transpileModule(source, {\n fileName: path,\n compilerOptions: {\n target: ts.ScriptTarget.ES2022,\n module: ts.ModuleKind.ESNext,\n moduleResolution: ts.ModuleResolutionKind.Bundler,\n isolatedModules: true,\n sourceMap: false,\n },\n transformers: { before: [rewriteRelativeImports] },\n reportDiagnostics: true,\n });\n const errors =\n transpiled.diagnostics?.filter(\n (diagnostic) => diagnostic.category === ts.DiagnosticCategory.Error,\n ) ?? [];\n if (errors.length > 0) {\n throw new Error(\n `ScriptablePack TypeScript transpile failed for ${path}: ${errors\n .map((diagnostic) => {\n const position = diagnostic.file?.getLineAndCharacterOfPosition(diagnostic.start ?? 0);\n const location =\n position === undefined\n ? path\n : `${path}:${position.line + 1}:${position.character + 1}`;\n return `${location}: ${ts.flattenDiagnosticMessageText(diagnostic.messageText, '\\n')}`;\n })\n .join('; ')}`,\n );\n }\n writeFileSync(join(outputRoot, outputName(path)), transpiled.outputText);\n }\n return join(outputRoot, outputName(resolve(entryPath)));\n}\n\nasync function loadDefinition(task: {\n readonly sourcePath: string;\n readonly compileRoot: string;\n readonly loadId?: number;\n}): Promise<void> {\n sourcePath = task.sourcePath;\n compileRoot = task.compileRoot;\n definition = undefined;\n reads.clear();\n const executablePath = SOURCE_EXTENSIONS.includes(\n extname(task.sourcePath) as (typeof SOURCE_EXTENSIONS)[number],\n )\n ? compileModuleClosure(task.sourcePath, task.compileRoot, task.loadId)\n : task.sourcePath;\n const loaded = (await import(\n `${pathToFileURL(executablePath).href}${reusable ? `?load=${task.loadId ?? 0}` : ''}`\n )) as {\n readonly default?: unknown;\n };\n definition = loaded.default as LoadedDefinition | undefined;\n port.postMessage({\n kind: 'loaded',\n ...(task.loadId === undefined ? {} : { loadId: task.loadId }),\n definition:\n definition === undefined\n ? undefined\n : {\n schemaVersion: definition.schemaVersion,\n packageId: definition.packageId,\n ...(definition.name === undefined ? {} : { name: definition.name }),\n ...(definition.parameters === undefined ? {} : { parameters: definition.parameters }),\n ...(definition.sceneComponents === undefined\n ? {}\n : { sceneComponents: serializableSceneComponents(definition.sceneComponents) }),\n },\n });\n}\n\nasync function handleMessage(message: unknown): Promise<void> {\n if (message === null || typeof message !== 'object') return;\n const value = message as Record<string, unknown>;\n if (value.kind === 'asset-result' && typeof value.readId === 'number') {\n reads.get(value.readId)?.(value.result);\n reads.delete(value.readId);\n return;\n }\n if (\n reusable &&\n value.kind === 'load' &&\n typeof value.sourcePath === 'string' &&\n typeof value.compileRoot === 'string' &&\n typeof value.loadId === 'number'\n ) {\n try {\n await loadDefinition({\n sourcePath: value.sourcePath,\n compileRoot: value.compileRoot,\n loadId: value.loadId,\n });\n } catch (error) {\n port.postMessage({ kind: 'load-threw', loadId: value.loadId, error: failure(error) });\n }\n return;\n }\n if (value.kind !== 'build' || typeof value.buildId !== 'number') return;\n if (typeof definition?.build !== 'function') {\n port.postMessage({\n kind: 'build-result',\n buildId: value.buildId,\n result: { ok: false, error: { message: 'default export has no build function' } },\n });\n return;\n }\n try {\n const context =\n value.context !== null && typeof value.context === 'object'\n ? (value.context as { readonly packageId?: unknown; readonly values?: unknown })\n : undefined;\n const buildContext = {\n ...(Array.isArray(context?.packageId)\n ? { packageId: new Uint8Array(context.packageId as number[]) }\n : {}),\n ...(context?.values !== undefined ? { values: context.values } : {}),\n readByGuid(guid: Uint8Array): Promise<unknown> {\n const readId = nextReadId++;\n port.postMessage({ kind: 'asset-read', buildId: value.buildId, readId, guid });\n return new Promise((resolve) => reads.set(readId, resolve));\n },\n };\n const result = await definition.build(buildContext);\n port.postMessage({\n kind: 'build-result',\n buildId: value.buildId,\n result:\n result !== null && typeof result === 'object' && 'ok' in result && !result.ok\n ? { ...result, error: failure(result.error) }\n : result,\n });\n } catch (error) {\n port.postMessage({ kind: 'build-threw', buildId: value.buildId, error: failure(error) });\n }\n}\n\nport.on('message', (message: unknown) => {\n void handleMessage(message);\n});\n\nif (!reusable && sourcePath !== undefined && compileRoot !== undefined) {\n try {\n await loadDefinition({ sourcePath, compileRoot });\n } catch (error) {\n port.postMessage({ kind: 'load-threw', error: failure(error) });\n }\n}\n"]}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@forgeax/engine-pack",
3
- "version": "0.1.28",
3
+ "version": "0.1.29",
4
4
  "private": false,
5
5
  "type": "module",
6
6
  "license": "Apache-2.0",
@@ -128,7 +128,7 @@
128
128
  }
129
129
  },
130
130
  "dependencies": {
131
- "@forgeax/engine-types": "0.1.28",
131
+ "@forgeax/engine-types": "0.1.29",
132
132
  "@noble/hashes": "^2.2.0",
133
133
  "fast-glob": "^3.3.3",
134
134
  "typescript": "^5.8.3",
@@ -66,10 +66,10 @@ describe('author inventory declaration contract', () => {
66
66
  kind: 'scene',
67
67
  payload: {
68
68
  kind: 'scene',
69
- entities: [
70
- { localId: 0, bindingKey: 'player', components: { Name: { value: 'Renamed' } } },
71
- { localId: 1, bindingKey: 'camera', components: {} },
72
- ],
69
+ entities: {
70
+ player: { components: { Name: { value: 'Renamed' } } },
71
+ camera: { components: {} },
72
+ },
73
73
  },
74
74
  refs: [],
75
75
  },
@@ -78,7 +78,7 @@ describe('author inventory declaration contract', () => {
78
78
 
79
79
  expect(result).toMatchObject({
80
80
  ok: true,
81
- value: { declarations: [{ sceneBindings: ['player', 'camera'] }] },
81
+ value: { declarations: [{ sceneEntityKeys: ['player', 'camera'] }] },
82
82
  });
83
83
  });
84
84
 
@@ -91,10 +91,10 @@ describe('author inventory declaration contract', () => {
91
91
  kind: 'scene',
92
92
  payload: {
93
93
  kind: 'scene',
94
- entities: [
95
- { localId: 0, components: { Transform: { position: [0, 0, 0] } } },
96
- { localId: 1, bindingKey: 'player', components: {} },
97
- ],
94
+ entities: {
95
+ decorative: { components: { Transform: { position: [0, 0, 0] } } },
96
+ player: { components: {} },
97
+ },
98
98
  },
99
99
  refs: [],
100
100
  },
@@ -103,7 +103,7 @@ describe('author inventory declaration contract', () => {
103
103
 
104
104
  expect(result).toMatchObject({
105
105
  ok: true,
106
- value: { declarations: [{ sceneBindings: ['player'] }] },
106
+ value: { declarations: [{ sceneEntityKeys: ['decorative', 'player'] }] },
107
107
  });
108
108
  });
109
109
  });
@@ -38,8 +38,7 @@ describe('ScriptablePack and Pack authoring', () => {
38
38
  ok({
39
39
  'scene/main': {
40
40
  kind: 'scene',
41
- entities: [],
42
- mounts: [],
41
+ entities: {},
43
42
  source: AssetGuid.format(AssetGuid.derive(subjectId, 'mesh/main')),
44
43
  },
45
44
  }),
@@ -62,8 +61,7 @@ describe('ScriptablePack and Pack authoring', () => {
62
61
  ok({
63
62
  'scene/main': {
64
63
  kind: 'scene',
65
- entities: values.segments >= 0 ? [] : [],
66
- mounts: [],
64
+ entities: values.segments >= 0 ? {} : {},
67
65
  },
68
66
  }),
69
67
  });
@@ -628,7 +628,7 @@ const V1_WHITELIST = new Set([
628
628
  type: 'object',
629
629
  additionalProperties: false,
630
630
  required: ['parent'],
631
- properties: { parent: { type: 'integer', minimum: 0 } },
631
+ properties: { parent: { type: 'string', minLength: 1 } },
632
632
  } as const;
633
633
  const componentSchemas: Record<string, object> = {
634
634
  Transform: TRANSFORM_SCHEMA,
@@ -639,58 +639,62 @@ const V1_WHITELIST = new Set([
639
639
 
640
640
  describe('scene-schema.test.ts', () => {
641
641
  describe('SceneAsset payload schema - positive cases (w4 / D-P4)', () => {
642
- it('accepts a valid 3-node payload (Transform + MeshFilter + ChildOf)', () => {
642
+ it('accepts a valid keyed payload (Transform + MeshFilter + ChildOf)', () => {
643
643
  const payload = {
644
644
  kind: 'scene',
645
- entities: [
646
- {
647
- localId: 0,
645
+ entities: {
646
+ root: {
648
647
  components: {
649
648
  Transform: { pos: [1, 2, 3] },
650
649
  MeshFilter: { mesh: 'cube' },
651
650
  },
652
651
  },
653
- {
654
- localId: 1,
655
- components: { Transform: { pos: [0, 0, 0] }, ChildOf: { parent: 0 } },
652
+ child: { components: { Transform: { pos: [0, 0, 0] }, ChildOf: { parent: 'root' } } },
653
+ grandchild: {
654
+ components: { Transform: { pos: [5, 5, 5] }, ChildOf: { parent: 'child' } },
656
655
  },
657
- {
658
- localId: 2,
659
- components: { Transform: { pos: [5, 5, 5] }, ChildOf: { parent: 1 } },
660
- },
661
- ],
656
+ },
662
657
  };
663
658
  const ok = sceneValidate(payload);
664
659
  expect(ok).toBe(true);
665
660
  expect(sceneValidate.errors ?? []).toEqual([]);
666
661
  });
662
+
663
+ it('accepts explicit skin dependency refs in the keyed payload', () => {
664
+ const payload = {
665
+ kind: 'scene',
666
+ skinGuids: ['skin-guid', 0],
667
+ entities: { root: { components: {} } },
668
+ };
669
+ expect(sceneValidate(payload)).toBe(true);
670
+ });
667
671
  });
668
672
 
669
673
  describe('SceneAsset payload schema - typo field name (AC-08(b))', () => {
670
674
  it('rejects Transform with typo field `pozX`, ajv error mentions additional properties', () => {
671
675
  const payload = {
672
676
  kind: 'scene',
673
- entities: [
674
- { localId: 0, components: { Transform: { pos: [1, 2, 3] } } },
675
- { localId: 1, components: { Transform: { pos: [0, 0, 0] } } },
676
- { localId: 2, components: { Transform: { pozX: 7, pos: [0, 0, 0] } } },
677
- ],
677
+ entities: {
678
+ root: { components: { Transform: { pos: [1, 2, 3] } } },
679
+ child: { components: { Transform: { pos: [0, 0, 0] } } },
680
+ broken: { components: { Transform: { pozX: 7, pos: [0, 0, 0] } } },
681
+ },
678
682
  };
679
683
  const ok = sceneValidate(payload);
680
684
  expect(ok).toBe(false);
681
685
  const errors = sceneValidate.errors ?? [];
682
686
  expect(errors.length).toBeGreaterThan(0);
683
687
  const e = errors[0];
684
- expect(e?.instancePath ?? '').toContain('/entities/2/components/Transform');
688
+ expect(e?.instancePath ?? '').toContain('/entities/broken/components/Transform');
685
689
  expect(e?.message ?? '').toContain('additional properties');
686
690
  });
687
691
  });
688
692
 
689
693
  describe('SceneAsset payload schema - ChildOf.parent type guard', () => {
690
- it('rejects ChildOf.parent that is not a non-negative integer', () => {
694
+ it('rejects ChildOf.parent that is not a non-empty entity key', () => {
691
695
  const payload = {
692
696
  kind: 'scene',
693
- entities: [{ localId: 0, components: { ChildOf: { parent: -1 } } }],
697
+ entities: { root: { components: { ChildOf: { parent: 3 } } } },
694
698
  };
695
699
  const ok = sceneValidate(payload);
696
700
  expect(ok).toBe(false);
@@ -703,7 +707,7 @@ const V1_WHITELIST = new Set([
703
707
  it('rejects a SceneEntity.components key not in the registered schema map', () => {
704
708
  const payload = {
705
709
  kind: 'scene',
706
- entities: [{ localId: 0, components: { MysteryComponent: { foo: 1 } } }],
710
+ entities: { root: { components: { MysteryComponent: { foo: 1 } } } },
707
711
  };
708
712
  const ok = sceneValidate(payload);
709
713
  expect(ok).toBe(false);
@@ -713,135 +717,93 @@ const V1_WHITELIST = new Set([
713
717
  expect(e?.message ?? '').toContain('additional properties');
714
718
  });
715
719
  });
720
+
721
+ it('rejects an empty authored entity key', () => {
722
+ const payload = { kind: 'scene', entities: { '': { components: {} } } };
723
+ expect(sceneValidate(payload)).toBe(false);
724
+ expect(
725
+ (sceneValidate.errors ?? []).some((e) => (e?.message ?? '').includes('property name')),
726
+ ).toBe(true);
727
+ });
716
728
  });
717
729
 
718
- describe('mount-override-schema.test.ts', () => {
719
- // feat-20260713-mount-override-component-add-and-shared-ref-round M1 / w1
720
- // (AC-03). `mountOverrideSchema.field` is now optional so the override shape
721
- // carries an implicit component-granular discriminant: an override WITH
722
- // `field` patches one field (legacy 4-key), an override WITHOUT `field`
723
- // adds/upserts the whole component (new 3-key). The schema accepts both;
724
- // `localId` / `comp` / `value` stay required; `additionalProperties: false`
725
- // rejects any op-tag discriminant (OOS: no `switch (op)` fan-out downstream).
726
- function makeMountPayload(override: Record<string, unknown>): Record<string, unknown> {
730
+ describe('scene-instance-schema.test.ts', () => {
731
+ type MutableInstanceEntity = {
732
+ instance: { overrides: Array<Record<string, unknown>> };
733
+ };
734
+
735
+ function makeInstancePayload(override: Record<string, unknown>): Record<string, unknown> {
727
736
  return {
728
737
  kind: 'scene',
729
- entities: [{ localId: 0, components: { Transform: { pos: [0, 0, 0] } } }],
730
- mounts: [
731
- {
732
- localId: 1,
733
- source: 0,
734
- memberFirst: 2,
735
- memberCount: 1,
736
- overrides: [override],
738
+ entities: {
739
+ root: { components: {} },
740
+ child: {
741
+ components: {},
742
+ instance: {
743
+ source: 'child-guid',
744
+ overrides: [{ target: ['leaf'], components: override }],
745
+ },
737
746
  },
738
- ],
747
+ },
739
748
  };
740
749
  }
741
750
 
742
- it('accepts the 4-key patch shape (with field)', () => {
743
- const ok = sceneValidate(
744
- makeMountPayload({ localId: 2, comp: 'Transform', field: 'pos', value: [1, 2, 3] }),
745
- );
751
+ it('accepts a keyed nested instance with component fields', () => {
752
+ const ok = sceneValidate(makeInstancePayload({ Transform: { pos: [1, 2, 3] } }));
746
753
  expect(ok).toBe(true);
747
754
  expect(sceneValidate.errors ?? []).toEqual([]);
748
755
  });
749
756
 
750
- it('accepts the 3-key add shape (without field)', () => {
757
+ it('accepts an ordered override with multiple components', () => {
751
758
  const ok = sceneValidate(
752
- makeMountPayload({ localId: 2, comp: 'Transform', value: { pos: [1, 2, 3] } }),
759
+ makeInstancePayload({
760
+ Transform: { pos: [1, 2, 3] },
761
+ MeshFilter: { mesh: 'cube' },
762
+ }),
753
763
  );
754
764
  expect(ok).toBe(true);
755
765
  expect(sceneValidate.errors ?? []).toEqual([]);
756
766
  });
757
767
 
758
- it('rejects an override missing localId', () => {
759
- const ok = sceneValidate(makeMountPayload({ comp: 'Transform', value: [1, 2, 3] }));
760
- expect(ok).toBe(false);
761
- expect((sceneValidate.errors ?? []).length).toBeGreaterThan(0);
768
+ it('rejects an override missing target', () => {
769
+ const payload = makeInstancePayload({ Transform: { pos: [1, 2, 3] } });
770
+ const instance = (payload.entities as Record<string, MutableInstanceEntity>).child.instance;
771
+ instance.overrides = [{ components: { Transform: { pos: [1, 2, 3] } } }];
772
+ expect(sceneValidate(payload)).toBe(false);
762
773
  });
763
774
 
764
- it('rejects an override missing comp', () => {
765
- const ok = sceneValidate(makeMountPayload({ localId: 2, value: [1, 2, 3] }));
766
- expect(ok).toBe(false);
767
- expect((sceneValidate.errors ?? []).length).toBeGreaterThan(0);
775
+ it('rejects an override missing components', () => {
776
+ const payload = makeInstancePayload({ Transform: { pos: [1, 2, 3] } });
777
+ const instance = (payload.entities as Record<string, MutableInstanceEntity>).child.instance;
778
+ instance.overrides = [{ target: ['leaf'] }];
779
+ expect(sceneValidate(payload)).toBe(false);
768
780
  });
769
781
 
770
- it('rejects an override missing value', () => {
771
- const ok = sceneValidate(makeMountPayload({ localId: 2, comp: 'Transform', field: 'pos' }));
772
- expect(ok).toBe(false);
773
- expect((sceneValidate.errors ?? []).length).toBeGreaterThan(0);
782
+ it('rejects an instance missing source', () => {
783
+ const payload = makeInstancePayload({ Transform: { pos: [1, 2, 3] } });
784
+ const child = (payload.entities as Record<string, MutableInstanceEntity>).child;
785
+ child.instance = { overrides: [{ target: ['leaf'], components: {} }] };
786
+ expect(sceneValidate(payload)).toBe(false);
774
787
  });
775
788
 
776
- it('rejects an override carrying an op discriminant field (additionalProperties:false)', () => {
777
- const ok = sceneValidate(
778
- makeMountPayload({ localId: 2, comp: 'Transform', value: [1, 2, 3], op: 'add' }),
779
- );
780
- expect(ok).toBe(false);
781
- const errors = sceneValidate.errors ?? [];
782
- expect(errors.length).toBeGreaterThan(0);
783
- expect(errors.some((e) => (e?.message ?? '').includes('additional properties'))).toBe(true);
789
+ it('rejects an override carrying an op discriminant field', () => {
790
+ const payload = makeInstancePayload({ Transform: { pos: [1, 2, 3] } });
791
+ const instance = (payload.entities as Record<string, MutableInstanceEntity>).child.instance;
792
+ instance.overrides = [{ target: ['leaf'], components: {}, op: 'add' }];
793
+ expect(sceneValidate(payload)).toBe(false);
794
+ expect(
795
+ (sceneValidate.errors ?? []).some((e) =>
796
+ (e?.message ?? '').includes('additional properties'),
797
+ ),
798
+ ).toBe(true);
784
799
  });
785
800
 
786
- // ═════════════════════════════════════════════════════════════════════════
787
- // w22 AC-03 / D-9: serialize→validate round-trip on fold-produced overrides
788
- //
789
- // The M5 collect fold emits component-add overrides (no `field`) whose value
790
- // is a per-field map that may carry shared<T> GUID strings (e.g. the added
791
- // AnimationPlayer.clips = [GUID, 0, 0, 0]). ajv's mountOverrideSchema must
792
- // accept that fold-produced shape, and a serialize→validate→deserialize
793
- // round-trip must not drop the override data. The override value is a free-
794
- // form object (`value: {}`) at the schema layer — runtime type checks live
795
- // in the ecs apply path (setSceneOverride / _validateMountOverrides).
796
- // ═════════════════════════════════════════════════════════════════════════
797
-
798
- const W22_CLIP = 'f1e2d3c4-b5a6-4b7c-8d9e-0f1a2b3c4d5e';
799
-
800
- it('(w22-a) accepts a fold-produced add-override with a shared-field GUID array value', () => {
801
- const payload = makeMountPayload({
802
- localId: 2,
803
- comp: 'Transform',
804
- // component-add form (no field); value carries a positional array with a
805
- // GUID string at slot 0 and NULL-sentinel placeholders — the exact shape
806
- // the fold + serialize wiring emits for AnimationPlayer.clips.
807
- value: { clips: [W22_CLIP, 0, 0, 0] },
808
- });
809
- const ok = sceneValidate(payload);
810
- expect(ok).toBe(true);
811
- expect(sceneValidate.errors ?? []).toEqual([]);
812
- });
813
-
814
- it('(w22-b) accepts an add-override whose value has no shared field at all', () => {
815
- // component-add with a plain scalar-only value map (the minimal add shape).
816
- const ok = sceneValidate(
817
- makeMountPayload({ localId: 2, comp: 'Transform', value: { pos: [9, 9, 9] } }),
818
- );
819
- expect(ok).toBe(true);
820
- expect(sceneValidate.errors ?? []).toEqual([]);
821
- });
822
-
823
- it('(w22-c) round-trip: JSON serialize → validate → parse preserves override data', () => {
824
- const override = {
825
- localId: 2,
826
- comp: 'Transform',
827
- value: { clips: [W22_CLIP, 0, 0, 0], pos: [1, 2, 3] },
828
- };
829
- const payload = makeMountPayload(override);
830
-
831
- // Simulate the on-disk pack path: JSON.stringify → fetch → JSON.parse.
801
+ it('round-trips keyed instance override data through JSON', () => {
802
+ const payload = makeInstancePayload({ Transform: { pos: [1, 2, 3] } });
832
803
  const roundTripped = JSON.parse(JSON.stringify(payload)) as Record<string, unknown>;
833
- const ok = sceneValidate(roundTripped);
834
- expect(ok).toBe(true);
835
- expect(sceneValidate.errors ?? []).toEqual([]);
836
-
837
- // No data lost: the override survives the round-trip byte-for-byte.
838
- const mounts = roundTripped.mounts as Array<Record<string, unknown>>;
839
- const overrides = mounts[0]?.overrides as Array<Record<string, unknown>>;
840
- expect(overrides).toHaveLength(1);
841
- expect(overrides[0]).toEqual(override);
842
- const value = overrides[0]?.value as Record<string, unknown>;
843
- expect(value.clips).toEqual([W22_CLIP, 0, 0, 0]);
844
- expect(value.pos).toEqual([1, 2, 3]);
804
+ expect(sceneValidate(roundTripped)).toBe(true);
805
+ const child = (roundTripped.entities as Record<string, MutableInstanceEntity>).child;
806
+ expect(child.instance.overrides[0]?.target).toEqual(['leaf']);
845
807
  });
846
808
  });
847
809
  }
@@ -0,0 +1,92 @@
1
+ // Scanner dependency-cycle coverage for keyed SceneAsset instances.
2
+
3
+ import { mkdtemp, rm, writeFile } from 'node:fs/promises';
4
+ import { tmpdir } from 'node:os';
5
+ import { join } from 'node:path';
6
+ import { afterEach, beforeEach, describe, expect, it } from 'vitest';
7
+ import { scan } from '../scanner.js';
8
+
9
+ const GUID_A = 'aa000000-0000-4000-8000-000000000aaa';
10
+ const GUID_B = 'bb000000-0000-4000-8000-000000000bbb';
11
+ const GUID_C = 'cc000000-0000-4000-8000-000000000ccc';
12
+
13
+ function sceneAssetPack(guid: string, source?: string): unknown {
14
+ return {
15
+ schemaVersion: '1.0.0',
16
+ kind: 'internal-text-package',
17
+ assets: [
18
+ {
19
+ guid,
20
+ kind: 'scene',
21
+ refs: source === undefined ? [] : [source],
22
+ payload: {
23
+ kind: 'scene',
24
+ entities:
25
+ source === undefined
26
+ ? { root: { components: {} } }
27
+ : { child: { components: {}, instance: { source: 0 } } },
28
+ },
29
+ },
30
+ ],
31
+ };
32
+ }
33
+
34
+ describe('scanner keyed SceneAsset instance cycle detection', () => {
35
+ let dir: string;
36
+
37
+ beforeEach(async () => {
38
+ dir = await mkdtemp(join(tmpdir(), 'pack-instance-cycle-'));
39
+ });
40
+
41
+ afterEach(async () => {
42
+ await rm(dir, { recursive: true, force: true });
43
+ });
44
+
45
+ it('accepts an acyclic instance chain A -> B -> C', async () => {
46
+ await writeFile(
47
+ join(dir, 'a.pack.json'),
48
+ JSON.stringify(sceneAssetPack(GUID_A, GUID_B)),
49
+ 'utf8',
50
+ );
51
+ await writeFile(
52
+ join(dir, 'b.pack.json'),
53
+ JSON.stringify(sceneAssetPack(GUID_B, GUID_C)),
54
+ 'utf8',
55
+ );
56
+ await writeFile(join(dir, 'c.pack.json'), JSON.stringify(sceneAssetPack(GUID_C)), 'utf8');
57
+ expect((await scan([dir])).ok).toBe(true);
58
+ });
59
+
60
+ it.each([
61
+ ['two', [GUID_A, GUID_B]],
62
+ ['three', [GUID_A, GUID_B, GUID_C]],
63
+ ])('rejects a %s-asset instance cycle', async (_name, cycle) => {
64
+ const [a, b, c] = cycle;
65
+ await writeFile(join(dir, 'a.pack.json'), JSON.stringify(sceneAssetPack(a, b)), 'utf8');
66
+ await writeFile(join(dir, 'b.pack.json'), JSON.stringify(sceneAssetPack(b, c ?? a)), 'utf8');
67
+ if (c !== undefined)
68
+ await writeFile(join(dir, 'c.pack.json'), JSON.stringify(sceneAssetPack(c, a)), 'utf8');
69
+ const result = await scan([dir]);
70
+ expect(result.ok).toBe(false);
71
+ if (result.ok) return;
72
+ expect(result.error.code).toBe('pack-cyclic-reference');
73
+ const detail = result.error.detail as { kind?: string; cycle?: readonly string[] };
74
+ expect(detail.kind).toBe('mount-asset');
75
+ const found = detail.cycle ?? [];
76
+ expect(found[0]).toBe(found[found.length - 1]);
77
+ for (const guid of cycle) expect(found).toContain(guid);
78
+ });
79
+
80
+ it('rejects a self-instance', async () => {
81
+ await writeFile(
82
+ join(dir, 'a.pack.json'),
83
+ JSON.stringify(sceneAssetPack(GUID_A, GUID_A)),
84
+ 'utf8',
85
+ );
86
+ const result = await scan([dir]);
87
+ expect(result.ok).toBe(false);
88
+ if (result.ok) return;
89
+ expect(result.error.code).toBe('pack-cyclic-reference');
90
+ expect((result.error.detail as { kind?: string }).kind).toBe('mount-asset');
91
+ });
92
+ });
@@ -95,7 +95,7 @@ describe('engine-pack scanner inventory contract', () => {
95
95
  default: {
96
96
  schemaVersion: '2.0.0',
97
97
  packageId: scriptablePackageId,
98
- build: () => ({ ok: true, value: { effect: { kind: 'scene', entities: [] } } }),
98
+ build: () => ({ ok: true, value: { effect: { kind: 'scene', entities: {} } } }),
99
99
  },
100
100
  }),
101
101
  },
@@ -143,7 +143,7 @@ describe('engine-pack scanner inventory contract', () => {
143
143
  default: {
144
144
  schemaVersion: '2.0.0',
145
145
  packageId,
146
- build: () => ({ ok: true, value: { effect: { kind: 'scene', entities: [] } } }),
146
+ build: () => ({ ok: true, value: { effect: { kind: 'scene', entities: {} } } }),
147
147
  },
148
148
  }),
149
149
  dispose: async () => {