@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.
- package/README.md +17 -5
- package/dist/build.mjs +34 -51
- package/dist/build.mjs.map +1 -1
- package/dist/cli-asset.mjs +21 -12
- package/dist/cli-asset.mjs.map +1 -1
- package/dist/index.mjs.map +1 -1
- package/dist/inventory/declaration.d.ts +1 -1
- package/dist/inventory/declaration.d.ts.map +1 -1
- package/dist/inventory/sync.d.ts.map +1 -1
- package/dist/native-cooker-registry.d.ts +1 -0
- package/dist/native-cooker-registry.d.ts.map +1 -1
- package/dist/native-cooker.mjs.map +1 -1
- package/dist/pack-authoring-node.mjs +21 -12
- package/dist/pack-authoring-node.mjs.map +1 -1
- package/dist/runtime.mjs.map +1 -1
- package/dist/scanner.mjs +21 -12
- package/dist/scanner.mjs.map +1 -1
- package/dist/schema-compiled.d.ts.map +1 -1
- package/dist/schema.mjs +40 -62
- package/dist/schema.mjs.map +1 -1
- package/dist/scriptable-pack-node.d.ts.map +1 -1
- package/dist/scriptable-pack-node.mjs +11 -3
- package/dist/scriptable-pack-node.mjs.map +1 -1
- package/dist/scriptable-pack-worker.mjs +5 -1
- package/dist/scriptable-pack-worker.mjs.map +1 -1
- package/package.json +2 -2
- package/src/__tests__/inventory-schema.test.ts +10 -10
- package/src/__tests__/pack-authoring.unit.test.ts +2 -4
- package/src/__tests__/pack.unit.test.ts +86 -124
- package/src/__tests__/scanner-instance-cycle.test.ts +92 -0
- package/src/__tests__/scanner-inventory.contract.test.ts +2 -2
- package/src/__tests__/scriptable-pack-diagnostic.integration.test.ts +45 -0
- package/src/inventory/binding.ts +3 -3
- package/src/inventory/declaration.ts +10 -48
- package/src/inventory/sync.ts +3 -2
- package/src/native-cooker-registry.ts +1 -0
- package/src/scanner.ts +28 -28
- package/src/schema-compiled.ts +40 -79
- package/src/scriptable-pack-node.ts +11 -3
- package/src/scriptable-pack-worker.ts +8 -1
- 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) =>
|
|
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.
|
|
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.
|
|
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
|
-
|
|
71
|
-
|
|
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: [{
|
|
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
|
-
|
|
96
|
-
|
|
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: [{
|
|
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: '
|
|
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
|
|
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
|
-
|
|
655
|
-
components: { Transform: { pos: [
|
|
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
|
-
|
|
675
|
-
|
|
676
|
-
|
|
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/
|
|
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-
|
|
694
|
+
it('rejects ChildOf.parent that is not a non-empty entity key', () => {
|
|
691
695
|
const payload = {
|
|
692
696
|
kind: 'scene',
|
|
693
|
-
entities:
|
|
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:
|
|
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('
|
|
719
|
-
|
|
720
|
-
|
|
721
|
-
|
|
722
|
-
|
|
723
|
-
|
|
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:
|
|
730
|
-
|
|
731
|
-
{
|
|
732
|
-
|
|
733
|
-
|
|
734
|
-
|
|
735
|
-
|
|
736
|
-
|
|
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
|
|
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
|
|
757
|
+
it('accepts an ordered override with multiple components', () => {
|
|
751
758
|
const ok = sceneValidate(
|
|
752
|
-
|
|
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
|
|
759
|
-
const
|
|
760
|
-
|
|
761
|
-
|
|
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
|
|
765
|
-
const
|
|
766
|
-
|
|
767
|
-
|
|
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
|
|
771
|
-
const
|
|
772
|
-
|
|
773
|
-
|
|
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
|
|
777
|
-
const
|
|
778
|
-
|
|
779
|
-
|
|
780
|
-
expect(
|
|
781
|
-
|
|
782
|
-
|
|
783
|
-
|
|
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
|
-
|
|
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
|
-
|
|
834
|
-
|
|
835
|
-
expect(
|
|
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 () => {
|