Advance N-023 through N-026 audited parity

This commit is contained in:
mes123456
2026-08-12 15:23:35 -04:00
parent b3cefaeec5
commit 86136139e2
30 changed files with 5573 additions and 66 deletions

View File

@@ -3,6 +3,9 @@ import { parseGreasePencilData, type GreasePencilDataIR } from "./grease-pencil"
import { parseCompositorGraph, type CompositorGraphIR } from "./compositor";
import { parseSequencerTimeline, type SequencerTimelineIR } from "./sequencer";
import { parseTrackingMaskProject, type TrackingMaskProjectIR } from "./tracking-mask";
import { normalizeProjectAssetPath } from "./asset-path";
import { parseEditorWorkflow, type EditorWorkflowIR } from "./editor-workflow";
import { parseScriptSourceInventory, type ScriptSourceInventoryIR } from "./scripting-platform";
export type SceneNodeType =
| "EMPTY"
@@ -474,6 +477,11 @@ export interface SceneSnapshotIR {
greasePencils?: GreasePencilDataIR[];
trackingMasks?: TrackingMaskProjectIR;
trackingMaskStatus?: "AVAILABLE" | "BLOCKED";
libraryStatus?: "AVAILABLE" | "BLOCKED";
editorWorkflow?: EditorWorkflowIR;
editorWorkflowStatus?: "AVAILABLE" | "BLOCKED";
scriptSources?: ScriptSourceInventoryIR;
scriptSourceStatus?: "AVAILABLE" | "BLOCKED";
libraries?: Array<{
id: string;
name: string;
@@ -482,6 +490,8 @@ export interface SceneSnapshotIR {
packedByteLength?: number;
status: "PACKED" | "EXTERNAL_REQUIRED";
errorCode?: "LINKED_LIBRARY_RESOURCE_REQUIRED";
dependencyIds: string[];
readOnly: true;
}>;
animations: AnimationIR[];
nlaTracks?: NlaTrackIR[];
@@ -1055,5 +1065,72 @@ export function parseSceneSnapshotIR(value: unknown): SceneSnapshotIR {
parseTrackingMaskProject(value.trackingMasks);
if (value.trackingMaskStatus !== "AVAILABLE") throw new Error("SceneIR.trackingMaskStatus must be AVAILABLE when trackingMasks is present");
}
if (value.libraryStatus !== undefined && !["AVAILABLE", "BLOCKED"].includes(value.libraryStatus as string)) {
throw new Error("SceneIR.libraryStatus is invalid");
}
if (value.libraries !== undefined) {
const libraries = value.libraries as unknown[];
if (libraries.length > 0 && value.libraryStatus !== "AVAILABLE") throw new Error("SceneIR.libraryStatus must be AVAILABLE when libraries are present");
if (libraries.length > 1024) throw new Error("SceneIR.libraries exceeds the budget");
const libraryIds = new Set<string>();
for (const [index, library] of libraries.entries()) {
if (!isRecord(library)) throw new Error(`SceneIR.libraries[${index}] must be an object`);
const id = requireString(library.id, `libraries[${index}].id`);
if (!id || id.length > 256 || libraryIds.has(id)) throw new Error(`SceneIR.libraries[${index}].id is invalid`);
libraryIds.add(id);
requireString(library.name, `libraries[${index}].name`);
const sourcePath = requireString(library.sourcePath, `libraries[${index}].sourcePath`);
if (typeof library.packed !== "boolean" || library.readOnly !== true || !Array.isArray(library.dependencyIds) ||
library.dependencyIds.length > 1024 || library.dependencyIds.some((dependency) => typeof dependency !== "string") ||
new Set(library.dependencyIds).size !== library.dependencyIds.length ||
!["PACKED", "EXTERNAL_REQUIRED"].includes(library.status as string)) {
throw new Error(`SceneIR.libraries[${index}] is invalid`);
}
let projectPath = true;
try { normalizeProjectAssetPath(sourcePath); } catch { projectPath = false; }
if (!projectPath) {
throw new Error(`SceneIR.libraries[${index}].sourcePath is outside the project`);
}
if (library.status === "PACKED" && (!library.packed || !Number.isSafeInteger(library.packedByteLength) || (library.packedByteLength as number) <= 0)) {
throw new Error(`SceneIR.libraries[${index}] packed payload is invalid`);
}
if (library.status === "EXTERNAL_REQUIRED" && (library.packed || library.errorCode !== "LINKED_LIBRARY_RESOURCE_REQUIRED")) {
throw new Error(`SceneIR.libraries[${index}] external resource state is invalid`);
}
}
for (const [index, library] of libraries.entries()) {
const dependencies = (library as Record<string, unknown>).dependencyIds as string[];
if (dependencies.some((dependency) => !libraryIds.has(dependency))) throw new Error(`SceneIR.libraries[${index}] references a missing dependency`);
}
const byId = new Map(libraries.map((library) => {
const record = library as Record<string, unknown>;
return [record.id as string, record.dependencyIds as string[]] as const;
}));
const active = new Set<string>();
const complete = new Set<string>();
const visit = (id: string): void => {
if (active.has(id)) throw new Error(`SceneIR.libraries dependency cycle includes ${id}`);
if (complete.has(id)) return;
active.add(id);
for (const dependency of byId.get(id) ?? []) visit(dependency);
active.delete(id);
complete.add(id);
};
byId.forEach((_dependencies, id) => visit(id));
}
if (value.editorWorkflowStatus !== undefined && !["AVAILABLE", "BLOCKED"].includes(value.editorWorkflowStatus as string)) {
throw new Error("SceneIR.editorWorkflowStatus is invalid");
}
if (value.editorWorkflow !== undefined) {
parseEditorWorkflow(value.editorWorkflow);
if (value.editorWorkflowStatus !== "AVAILABLE") throw new Error("SceneIR.editorWorkflowStatus must be AVAILABLE when editorWorkflow is present");
}
if (value.scriptSourceStatus !== undefined && !["AVAILABLE", "BLOCKED"].includes(value.scriptSourceStatus as string)) {
throw new Error("SceneIR.scriptSourceStatus is invalid");
}
if (value.scriptSources !== undefined) {
parseScriptSourceInventory(value.scriptSources);
if (value.scriptSourceStatus !== "AVAILABLE") throw new Error("SceneIR.scriptSourceStatus must be AVAILABLE when scriptSources is present");
}
return value as unknown as SceneSnapshotIR;
}