@semacode/cli 2.2.0 → 2.3.0

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 (127) hide show
  1. package/README.md +91 -4
  2. package/dist/agentContext.d.ts +1 -1
  3. package/dist/agentContextPack.js +6 -1
  4. package/dist/agentContextTipos.d.ts +4 -2
  5. package/dist/agentContextTipos.js +7 -0
  6. package/dist/agentEntryPoints.js +70 -19
  7. package/dist/comandos.js +4 -0
  8. package/dist/discovery/catalog.d.ts +11 -0
  9. package/dist/discovery/catalog.js +521 -0
  10. package/dist/discovery/command.d.ts +11 -0
  11. package/dist/discovery/command.js +226 -0
  12. package/dist/discovery/index.d.ts +5 -0
  13. package/dist/discovery/index.js +8 -0
  14. package/dist/discovery/ranker.d.ts +3 -0
  15. package/dist/discovery/ranker.js +310 -0
  16. package/dist/discovery/redaction.d.ts +2 -0
  17. package/dist/discovery/redaction.js +21 -0
  18. package/dist/discovery/render.d.ts +2 -0
  19. package/dist/discovery/render.js +100 -0
  20. package/dist/discovery/types.d.ts +119 -0
  21. package/dist/discovery/types.js +13 -0
  22. package/dist/exemplosOficiais.js +29 -8
  23. package/dist/iaPrompts.d.ts +1 -1
  24. package/dist/iaPrompts.js +3 -0
  25. package/dist/index.d.ts +2 -0
  26. package/dist/index.js +23 -5
  27. package/dist/index.part01.d.ts +1 -1
  28. package/dist/index.part01.js +39 -4
  29. package/dist/index.part06.js +3 -0
  30. package/dist/initTemplatesBase.js +3 -2
  31. package/dist/profileArtefatoValidadores.d.ts +3 -2
  32. package/dist/profileArtefatoValidadores.js +81 -0
  33. package/dist/profileAuthorTipos.d.ts +4 -3
  34. package/dist/profileCatalogo.d.ts +5 -5
  35. package/dist/profileCatalogo.js +30 -0
  36. package/dist/profileCommand.d.ts +8 -2
  37. package/dist/profileCommand.js +15 -5
  38. package/dist/profileRegras.d.ts +5 -5
  39. package/dist/profileRegras.js +13 -1
  40. package/dist/profileRulePacks.js +68 -0
  41. package/dist/sistemasInterativos/adapterProtocol.d.ts +3 -0
  42. package/dist/sistemasInterativos/adapterProtocol.js +240 -0
  43. package/dist/sistemasInterativos/advancedCatalog.d.ts +5 -0
  44. package/dist/sistemasInterativos/advancedCatalog.js +442 -0
  45. package/dist/sistemasInterativos/autonomy.d.ts +230 -0
  46. package/dist/sistemasInterativos/autonomy.js +753 -0
  47. package/dist/sistemasInterativos/canonical.d.ts +3 -0
  48. package/dist/sistemasInterativos/canonical.js +43 -0
  49. package/dist/sistemasInterativos/catalog.d.ts +116 -0
  50. package/dist/sistemasInterativos/catalog.js +543 -0
  51. package/dist/sistemasInterativos/command.d.ts +7 -0
  52. package/dist/sistemasInterativos/command.js +311 -0
  53. package/dist/sistemasInterativos/controlRun.d.ts +56 -0
  54. package/dist/sistemasInterativos/controlRun.js +391 -0
  55. package/dist/sistemasInterativos/evidence.d.ts +5 -0
  56. package/dist/sistemasInterativos/evidence.js +306 -0
  57. package/dist/sistemasInterativos/experienceIr.d.ts +12 -0
  58. package/dist/sistemasInterativos/experienceIr.js +895 -0
  59. package/dist/sistemasInterativos/experienceIrSemantics.d.ts +30 -0
  60. package/dist/sistemasInterativos/experienceIrSemantics.js +368 -0
  61. package/dist/sistemasInterativos/experienceIrTypes.d.ts +370 -0
  62. package/dist/sistemasInterativos/experienceIrTypes.js +5 -0
  63. package/dist/sistemasInterativos/extensionCommand.d.ts +391 -0
  64. package/dist/sistemasInterativos/extensionCommand.js +383 -0
  65. package/dist/sistemasInterativos/index.d.ts +19 -0
  66. package/dist/sistemasInterativos/index.js +22 -0
  67. package/dist/sistemasInterativos/operations.d.ts +40 -0
  68. package/dist/sistemasInterativos/operations.js +1167 -0
  69. package/dist/sistemasInterativos/operationsTypes.d.ts +297 -0
  70. package/dist/sistemasInterativos/operationsTypes.js +11 -0
  71. package/dist/sistemasInterativos/planner.d.ts +2 -0
  72. package/dist/sistemasInterativos/planner.js +159 -0
  73. package/dist/sistemasInterativos/portability.d.ts +164 -0
  74. package/dist/sistemasInterativos/portability.js +539 -0
  75. package/dist/sistemasInterativos/render.d.ts +3 -0
  76. package/dist/sistemasInterativos/render.js +179 -0
  77. package/dist/sistemasInterativos/temporal.d.ts +183 -0
  78. package/dist/sistemasInterativos/temporal.js +543 -0
  79. package/dist/sistemasInterativos/types.d.ts +215 -0
  80. package/dist/sistemasInterativos/types.js +4 -0
  81. package/dist/sistemasInterativos/validator.d.ts +2 -0
  82. package/dist/sistemasInterativos/validator.js +305 -0
  83. package/docs/README.md +6 -0
  84. package/docs/ai-integration.md +7 -1
  85. package/docs/ai-workflow.md +5 -4
  86. package/docs/commands.md +43 -1
  87. package/docs/descoberta-capacidades.md +106 -0
  88. package/docs/documentation.md +11 -2
  89. package/docs/profiles.md +11 -2
  90. package/docs/sistemas-interativos.md +381 -0
  91. package/docs/testing.md +11 -2
  92. package/exemplos/profile_simulation.sema +211 -0
  93. package/exemplos/sistemas-interativos/README.md +100 -0
  94. package/exemplos/sistemas-interativos/advanced/acceptance-context-evaluate-valid.json +5 -0
  95. package/exemplos/sistemas-interativos/advanced/acceptance-lock-valid.json +16 -0
  96. package/exemplos/sistemas-interativos/advanced/asset-provenance-valid.json +32 -0
  97. package/exemplos/sistemas-interativos/advanced/autonomy-repair-valid.json +93 -0
  98. package/exemplos/sistemas-interativos/advanced/control-run-definition-valid.json +78 -0
  99. package/exemplos/sistemas-interativos/advanced/control-run-valid.json +27 -0
  100. package/exemplos/sistemas-interativos/advanced/distributed-workers-valid.json +79 -0
  101. package/exemplos/sistemas-interativos/advanced/editor-state-valid.json +42 -0
  102. package/exemplos/sistemas-interativos/advanced/engine-snapshot-after-valid.json +32 -0
  103. package/exemplos/sistemas-interativos/advanced/engine-snapshot-before-valid.json +23 -0
  104. package/exemplos/sistemas-interativos/advanced/job-orchestration-valid.json +39 -0
  105. package/exemplos/sistemas-interativos/advanced/multimodal-evidence-valid.json +46 -0
  106. package/exemplos/sistemas-interativos/advanced/multiplayer-authority-valid.json +39 -0
  107. package/exemplos/sistemas-interativos/advanced/playtest-fuzz-valid.json +37 -0
  108. package/exemplos/sistemas-interativos/advanced/portability-valid.json +104 -0
  109. package/exemplos/sistemas-interativos/advanced/temporal-evidence-valid.json +27 -0
  110. package/exemplos/sistemas-interativos/advanced/temporal-valid.json +118 -0
  111. package/exemplos/sistemas-interativos/experience-ir-valid.json +459 -0
  112. package/exemplos/sistemas-interativos/game-2d.json +83 -0
  113. package/exemplos/sistemas-interativos/game-3d-human.json +81 -0
  114. package/exemplos/sistemas-interativos/game-pixel-16-bit.json +97 -0
  115. package/exemplos/sistemas-interativos/game-pixel-8-bit.json +94 -0
  116. package/exemplos/sistemas-interativos/game-xr-human.json +80 -0
  117. package/exemplos/sistemas-interativos/hybrid-2-5d.json +108 -0
  118. package/exemplos/sistemas-interativos/protocol-mutating-rollback-valid.json +75 -0
  119. package/exemplos/sistemas-interativos/protocol-read-only-valid.json +58 -0
  120. package/exemplos/sistemas-interativos/simulation-2d-controlled.json +97 -0
  121. package/exemplos/sistemas-interativos/simulation-3d-calibrated-autonomous.json +141 -0
  122. package/exemplos/sistemas-interativos/simulation-headless-autonomous-batch.json +112 -0
  123. package/exemplos/sistemas-interativos/simulation-pixel-16-bit.json +103 -0
  124. package/exemplos/sistemas-interativos/simulation-text-controlled.json +95 -0
  125. package/node_modules/@sema/gerador-lua/dist/index.js +31 -2
  126. package/node_modules/@sema/gerador-lua/dist/index.js.map +1 -1
  127. package/package.json +1 -1
@@ -0,0 +1,895 @@
1
+ // SEMA-GOVERNED: sema.produto.sistemas_interativos.ir
2
+ // Contratos: contratos/sema/sistemas_interativos_ir.sema e sistemas_interativos_ir_semantica.sema
3
+ // Descrição: Experience IR v1 content-addressed; valida e projeta sem executar engine ou escrever no workspace.
4
+ import { createHash } from "node:crypto";
5
+ import { EXPERIENCE_IR_SEMANTIC_ROLE_SCHEMA, obterPapeisSemanticosExperienceIr, obterReferenciasConceituaisExperienceIr, validarSemanticaExperienceIr, valorSensivelExperienceIr, } from "./experienceIrSemantics.js";
6
+ export * from "./experienceIrTypes.js";
7
+ export { EXPERIENCE_IR_SEMANTIC_ROLE_SCHEMA, EXPERIENCE_IR_SEMANTIC_ROLES_SCHEMA_VERSION, validarSemanticaExperienceIr, } from "./experienceIrSemantics.js";
8
+ export const EXPERIENCE_IR_SCHEMA_VERSION = "sema.experience-ir/v1";
9
+ export const EXPERIENCE_IR_INDEX_SCHEMA_VERSION = "sema.experience-ir.index/v1";
10
+ export const EXPERIENCE_IR_CHUNK_SCHEMA_VERSION = "sema.experience-ir.chunk/v1";
11
+ export const EXPERIENCE_IR_SERIALIZATION_SCHEMA_VERSION = "sema.experience-ir.serialization/v1";
12
+ const MAX_NODES = 20_000;
13
+ const MAX_REFS = 2_000;
14
+ const MAX_DEPTH = 64;
15
+ const MAX_VALUES = 250_000;
16
+ const MAX_BYTES = 16 * 1024 * 1024;
17
+ const MAX_ISSUES = 256;
18
+ const SHA256 = /^sha256:[a-f0-9]{64}$/u;
19
+ const SEMANTIC_ID = /^[a-z][a-z0-9]*(?:[._-][a-z0-9]+)+$/u;
20
+ const UNSAFE_KEYS = new Set(["__proto__", "constructor", "prototype"]);
21
+ const EXTENSION_NAME = /^[a-z][a-z0-9]*(?:[._/-][a-z0-9]+)+$/u;
22
+ const EXTENSION_MEDIA_TYPE = /^[a-z0-9][a-z0-9.+-]{0,63}\/[a-z0-9][a-z0-9.+-]{0,63}$/u;
23
+ const SENSITIVE_KEY = /(?:password|passwd|senha|secret|token|api.?key|private.?key|credential|authorization|cookie)/iu;
24
+ const KIND_PREFIX = {
25
+ PROJECT: "project", WORLD: "world", SCENE: "scene", ENTITY: "entity",
26
+ COMPONENT: "component", TRANSFORM: "transform", CAMERA: "camera", LIGHT: "light",
27
+ MATERIAL: "material", TEXTURE: "texture", AUDIO: "audio", PHYSICS: "physics",
28
+ CONSTRAINT: "constraint", ANIMATION: "animation", VFX: "vfx", TIMELINE: "timeline",
29
+ INPUT: "input", SAVE: "save", NETWORK: "network", BUILD: "build",
30
+ };
31
+ const COLLECTIONS = [
32
+ ["WORLD", "worlds"], ["SCENE", "scenes"], ["ENTITY", "entities"],
33
+ ["COMPONENT", "components"], ["TRANSFORM", "transforms"], ["CAMERA", "cameras"],
34
+ ["LIGHT", "lights"], ["MATERIAL", "materials"], ["TEXTURE", "textures"],
35
+ ["AUDIO", "audio"], ["PHYSICS", "physics"], ["CONSTRAINT", "constraints"],
36
+ ["ANIMATION", "animations"], ["VFX", "vfx"], ["TIMELINE", "timelines"],
37
+ ["INPUT", "inputs"], ["SAVE", "saves"], ["NETWORK", "networks"], ["BUILD", "builds"],
38
+ ];
39
+ const ALL_KINDS = Object.freeze(Object.keys(KIND_PREFIX));
40
+ const TARGET_KINDS = ALL_KINDS.filter((kind) => kind !== "PROJECT");
41
+ const ASSET_KINDS = ["TEXTURE", "AUDIO", "ANIMATION", "VFX"];
42
+ const BASE_KEYS = ["semanticId", "kind", "references", "extensions"];
43
+ const NODE_KEYS = {
44
+ PROJECT: [...BASE_KEYS, "name", "worldIds", "defaultSceneId"],
45
+ WORLD: [...BASE_KEYS, "name", "sceneIds"],
46
+ SCENE: [...BASE_KEYS, "name", "semanticRole", "entityIds", "cameraId", "lightIds"],
47
+ ENTITY: [...BASE_KEYS, "name", "componentIds", "transformId", "parentEntityId"],
48
+ COMPONENT: [...BASE_KEYS, "ownerEntityId", "componentType", "properties"],
49
+ TRANSFORM: [...BASE_KEYS, "semanticRole", "parentTransformId", "translation", "rotation", "scale"],
50
+ CAMERA: [...BASE_KEYS, "transformId", "projection", "near", "far", "fieldOfViewRadians"],
51
+ LIGHT: [...BASE_KEYS, "transformId", "lightType", "color", "intensity"],
52
+ MATERIAL: [...BASE_KEYS, "shadingModel", "textureIds", "parameters"],
53
+ TEXTURE: [...BASE_KEYS, "usage", "colorSpace", "provenance"],
54
+ AUDIO: [...BASE_KEYS, "ownerEntityId", "spatial", "loop", "provenance"],
55
+ PHYSICS: [...BASE_KEYS, "ownerEntityId", "bodyType", "massKilograms", "collider", "colliders"],
56
+ CONSTRAINT: [...BASE_KEYS, "constraintType", "sourceId", "targetId", "parameters"],
57
+ ANIMATION: [...BASE_KEYS, "targetIds", "durationSeconds", "provenance"],
58
+ VFX: [...BASE_KEYS, "ownerEntityId", "materialIds", "provenance", "emitters"],
59
+ TIMELINE: [...BASE_KEYS, "durationSeconds", "tracks"],
60
+ INPUT: [...BASE_KEYS, "contextEntityId", "actions"],
61
+ SAVE: [...BASE_KEYS, "semanticRole", "strategy", "schemaRevision", "stateSemanticIds"],
62
+ NETWORK: [...BASE_KEYS, "mode", "authority", "replicatedSemanticIds", "tickRateHz"],
63
+ BUILD: [...BASE_KEYS, "target", "entrySceneId", "assetIds", "options"],
64
+ };
65
+ const SERIALIZATION = {
66
+ json: {
67
+ version: "1",
68
+ mediaType: "application/vnd.sema.experience-ir+json",
69
+ canonicalization: "SEMA_CANONICAL_JSON_V1",
70
+ },
71
+ cbor: {
72
+ mode: "EXTERNAL_CODEC_REQUIRED",
73
+ mediaType: "application/cbor",
74
+ encoded: false,
75
+ deterministicEncodingRequired: true,
76
+ codec: null,
77
+ },
78
+ };
79
+ const BOUNDARY = {
80
+ executed: false,
81
+ workspaceMutated: false,
82
+ authoritative: false,
83
+ };
84
+ class Issues {
85
+ list = [];
86
+ truncated = false;
87
+ add(code, path, message) {
88
+ if (this.list.length >= MAX_ISSUES) {
89
+ this.truncated = true;
90
+ return;
91
+ }
92
+ this.list.push({ code, path, message });
93
+ }
94
+ finish() {
95
+ const output = [...this.list];
96
+ if (this.truncated)
97
+ output.push({ code: "IR_ISSUE_LIMIT", path: "$", message: "Issue limit reached." });
98
+ return output.sort((a, b) => (a.path.localeCompare(b.path, "en") || a.code.localeCompare(b.code, "en") ||
99
+ a.message.localeCompare(b.message, "en")));
100
+ }
101
+ }
102
+ function isRecord(value) {
103
+ if (value === null || typeof value !== "object" || Array.isArray(value))
104
+ return false;
105
+ const prototype = Object.getPrototypeOf(value);
106
+ return prototype === Object.prototype || prototype === null;
107
+ }
108
+ function validateJson(value, issues) {
109
+ const seen = new WeakSet();
110
+ let count = 0;
111
+ let valid = true;
112
+ const visit = (current, path, depth) => {
113
+ count += 1;
114
+ if (count > MAX_VALUES) {
115
+ valid = false;
116
+ issues.add("IR_JSON_VALUE_LIMIT", "$", "JSON value limit exceeded.");
117
+ return;
118
+ }
119
+ if (depth > MAX_DEPTH) {
120
+ valid = false;
121
+ issues.add("IR_JSON_DEPTH", path, "JSON depth limit exceeded.");
122
+ return;
123
+ }
124
+ if (typeof current === "string") {
125
+ if (valorSensivelExperienceIr(current)) {
126
+ valid = false;
127
+ issues.add("IR_SENSITIVE_VALUE", path, "Sensitive string values are not accepted in Experience IR.");
128
+ }
129
+ return;
130
+ }
131
+ if (current === null || typeof current === "boolean")
132
+ return;
133
+ if (typeof current === "number") {
134
+ if (!Number.isFinite(current)) {
135
+ valid = false;
136
+ issues.add("IR_JSON_NUMBER", path, "JSON numbers must be finite.");
137
+ }
138
+ return;
139
+ }
140
+ if (typeof current !== "object") {
141
+ valid = false;
142
+ issues.add("IR_JSON_TYPE", path, "Only JSON values are accepted.");
143
+ return;
144
+ }
145
+ if (seen.has(current)) {
146
+ valid = false;
147
+ issues.add("IR_JSON_GRAPH", path, "Cycles and shared aliases are not accepted.");
148
+ return;
149
+ }
150
+ seen.add(current);
151
+ if (Array.isArray(current)) {
152
+ current.forEach((item, index) => visit(item, `${path}[${index}]`, depth + 1));
153
+ return;
154
+ }
155
+ if (!isRecord(current)) {
156
+ valid = false;
157
+ issues.add("IR_JSON_OBJECT", path, "Objects must use a plain JSON prototype.");
158
+ return;
159
+ }
160
+ const descriptors = Object.getOwnPropertyDescriptors(current);
161
+ Object.keys(descriptors).sort().forEach((key, index) => {
162
+ const descriptor = descriptors[key];
163
+ const childPath = `${path}.*[${index}]`;
164
+ if (UNSAFE_KEYS.has(key)) {
165
+ valid = false;
166
+ issues.add("IR_JSON_UNSAFE_KEY", childPath, "Unsafe JSON key rejected.");
167
+ }
168
+ else if (SENSITIVE_KEY.test(key)) {
169
+ valid = false;
170
+ issues.add("IR_SENSITIVE_KEY", childPath, "Sensitive data keys are not accepted in Experience IR.");
171
+ }
172
+ else if (descriptor.get || descriptor.set || !("value" in descriptor)) {
173
+ valid = false;
174
+ issues.add("IR_JSON_ACCESSOR", childPath, "JSON accessors are not accepted.");
175
+ }
176
+ else
177
+ visit(descriptor.value, childPath, depth + 1);
178
+ });
179
+ };
180
+ visit(value, "$", 0);
181
+ return valid;
182
+ }
183
+ function allowedKeys(value, allowed, path, issues) {
184
+ const whitelist = new Set(allowed);
185
+ Object.keys(value).sort().forEach((key, index) => {
186
+ if (!whitelist.has(key))
187
+ issues.add("IR_UNKNOWN_FIELD", `${path}.*[${index}]`, "Unknown Experience IR v1 field.");
188
+ });
189
+ }
190
+ function validateExtensions(value, path, issues) {
191
+ if (value === undefined)
192
+ return;
193
+ if (!isRecord(value)) {
194
+ issues.add("IR_EXTENSIONS", path, "Extensions must be a JSON object.");
195
+ return;
196
+ }
197
+ Object.entries(value).sort(([a], [b]) => a.localeCompare(b, "en")).forEach(([name, descriptor], index) => {
198
+ const entryPath = `${path}.*[${index}]`;
199
+ if (!EXTENSION_NAME.test(name) || SENSITIVE_KEY.test(name)) {
200
+ issues.add("IR_EXTENSION_NAME", entryPath, "Extension name must be a non-sensitive public namespace.");
201
+ }
202
+ if (!isRecord(descriptor)) {
203
+ issues.add("IR_EXTENSION_DESCRIPTOR", entryPath, "Extension must be a content-addressed descriptor.");
204
+ return;
205
+ }
206
+ allowedKeys(descriptor, ["schemaVersion", "payloadDigest", "mediaType"], entryPath, issues);
207
+ const schemaVersion = text(descriptor, "schemaVersion", entryPath, issues);
208
+ if (schemaVersion && !EXTENSION_NAME.test(schemaVersion)) {
209
+ issues.add("IR_EXTENSION_SCHEMA", `${entryPath}.schemaVersion`, "Extension schema version is invalid.");
210
+ }
211
+ const payloadDigest = text(descriptor, "payloadDigest", entryPath, issues);
212
+ if (payloadDigest && !SHA256.test(payloadDigest)) {
213
+ issues.add("IR_EXTENSION_DIGEST", `${entryPath}.payloadDigest`, "Extension payload must be referenced by SHA-256 digest.");
214
+ }
215
+ const mediaType = text(descriptor, "mediaType", entryPath, issues, true);
216
+ if (mediaType && !EXTENSION_MEDIA_TYPE.test(mediaType)) {
217
+ issues.add("IR_EXTENSION_MEDIA_TYPE", `${entryPath}.mediaType`, "Extension media type is invalid.");
218
+ }
219
+ });
220
+ }
221
+ function text(value, key, path, issues, optional = false) {
222
+ if (optional && value[key] === undefined)
223
+ return null;
224
+ const candidate = value[key];
225
+ if (typeof candidate !== "string" || candidate.trim() === "") {
226
+ issues.add("IR_REQUIRED_STRING", `${path}.${key}`, "Required non-empty string is missing.");
227
+ return null;
228
+ }
229
+ return candidate;
230
+ }
231
+ function numberValue(value, key, path, issues, positive = false, optional = false) {
232
+ if (optional && value[key] === undefined)
233
+ return null;
234
+ const candidate = value[key];
235
+ if (typeof candidate !== "number" || !Number.isFinite(candidate) || (positive && candidate <= 0)) {
236
+ issues.add("IR_REQUIRED_NUMBER", `${path}.${key}`, "Required finite number is invalid.");
237
+ return null;
238
+ }
239
+ return candidate;
240
+ }
241
+ function booleanValue(value, key, path, issues) {
242
+ if (typeof value[key] !== "boolean") {
243
+ issues.add("IR_REQUIRED_BOOLEAN", `${path}.${key}`, "Required boolean is missing.");
244
+ return null;
245
+ }
246
+ return value[key];
247
+ }
248
+ function objectValue(value, key, path, issues) {
249
+ if (!isRecord(value[key])) {
250
+ issues.add("IR_REQUIRED_OBJECT", `${path}.${key}`, "Required object is missing.");
251
+ return null;
252
+ }
253
+ return value[key];
254
+ }
255
+ function enumValue(value, key, options, path, issues) {
256
+ const candidate = text(value, key, path, issues);
257
+ if (candidate !== null && !options.includes(candidate)) {
258
+ issues.add("IR_ENUM", `${path}.${key}`, "Value is outside the supported enum.");
259
+ return null;
260
+ }
261
+ return candidate;
262
+ }
263
+ function stringList(value, key, path, issues, optional = false) {
264
+ if (optional && value[key] === undefined)
265
+ return [];
266
+ const candidate = value[key];
267
+ if (!Array.isArray(candidate)) {
268
+ issues.add("IR_REQUIRED_ARRAY", `${path}.${key}`, "Required string array is missing.");
269
+ return [];
270
+ }
271
+ if (candidate.length > MAX_REFS)
272
+ issues.add("IR_REFERENCE_LIMIT", `${path}.${key}`, "Reference limit exceeded.");
273
+ const result = [];
274
+ const seen = new Set();
275
+ candidate.forEach((item, index) => {
276
+ if (typeof item !== "string" || item.trim() === "") {
277
+ issues.add("IR_STRING_ARRAY_ITEM", `${path}.${key}[${index}]`, "String array item is invalid.");
278
+ }
279
+ else if (seen.has(item)) {
280
+ issues.add("IR_DUPLICATE_ARRAY_ITEM", `${path}.${key}[${index}]`, "String array items must be unique.");
281
+ }
282
+ else {
283
+ seen.add(item);
284
+ result.push(item);
285
+ }
286
+ });
287
+ return result;
288
+ }
289
+ function tuple(value, length, path, issues, positive = false) {
290
+ if (!Array.isArray(value) || value.length !== length || value.some((item) => (typeof item !== "number" || !Number.isFinite(item) || (positive && item <= 0))))
291
+ issues.add("IR_NUMERIC_TUPLE", path, "Numeric tuple is invalid.");
292
+ }
293
+ function validateCoordinateSystem(document, issues) {
294
+ const coordinate = objectValue(document, "coordinateSystem", "$", issues);
295
+ if (!coordinate)
296
+ return;
297
+ allowedKeys(coordinate, ["units", "axes", "worldScaleMetersPerUnit"], "$.coordinateSystem", issues);
298
+ const units = objectValue(coordinate, "units", "$.coordinateSystem", issues);
299
+ if (units) {
300
+ allowedKeys(units, ["length", "mass", "time", "angle"], "$.coordinateSystem.units", issues);
301
+ enumValue(units, "length", ["METER", "CENTIMETER", "MILLIMETER", "KILOMETER"], "$.coordinateSystem.units", issues);
302
+ enumValue(units, "mass", ["KILOGRAM", "GRAM"], "$.coordinateSystem.units", issues);
303
+ enumValue(units, "time", ["SECOND", "MILLISECOND"], "$.coordinateSystem.units", issues);
304
+ enumValue(units, "angle", ["RADIAN", "DEGREE"], "$.coordinateSystem.units", issues);
305
+ }
306
+ const axes = objectValue(coordinate, "axes", "$.coordinateSystem", issues);
307
+ if (axes) {
308
+ const axisOptions = ["X", "Y", "Z", "NEGATIVE_X", "NEGATIVE_Y", "NEGATIVE_Z"];
309
+ allowedKeys(axes, ["handedness", "up", "forward"], "$.coordinateSystem.axes", issues);
310
+ enumValue(axes, "handedness", ["RIGHT_HANDED", "LEFT_HANDED"], "$.coordinateSystem.axes", issues);
311
+ const up = enumValue(axes, "up", axisOptions, "$.coordinateSystem.axes", issues);
312
+ const forward = enumValue(axes, "forward", axisOptions, "$.coordinateSystem.axes", issues);
313
+ if (up && forward && up.replace("NEGATIVE_", "") === forward.replace("NEGATIVE_", "")) {
314
+ issues.add("IR_AXES_COLLINEAR", "$.coordinateSystem.axes", "Up and forward axes must not be collinear.");
315
+ }
316
+ }
317
+ numberValue(coordinate, "worldScaleMetersPerUnit", "$.coordinateSystem", issues, true);
318
+ }
319
+ function validateSerialization(document, issues) {
320
+ const serialization = objectValue(document, "serialization", "$", issues);
321
+ if (!serialization)
322
+ return;
323
+ allowedKeys(serialization, ["json", "cbor"], "$.serialization", issues);
324
+ const json = objectValue(serialization, "json", "$.serialization", issues);
325
+ if (json) {
326
+ allowedKeys(json, ["version", "mediaType", "canonicalization"], "$.serialization.json", issues);
327
+ if (json.version !== SERIALIZATION.json.version || json.mediaType !== SERIALIZATION.json.mediaType ||
328
+ json.canonicalization !== SERIALIZATION.json.canonicalization) {
329
+ issues.add("IR_JSON_DESCRIPTOR", "$.serialization.json", "JSON v1 descriptor is invalid.");
330
+ }
331
+ }
332
+ const cbor = objectValue(serialization, "cbor", "$.serialization", issues);
333
+ if (cbor) {
334
+ allowedKeys(cbor, ["mode", "mediaType", "encoded", "deterministicEncodingRequired", "codec"], "$.serialization.cbor", issues);
335
+ if (cbor.mode !== SERIALIZATION.cbor.mode || cbor.mediaType !== SERIALIZATION.cbor.mediaType ||
336
+ cbor.encoded !== false || cbor.deterministicEncodingRequired !== true || cbor.codec !== null) {
337
+ issues.add("IR_CBOR_DESCRIPTOR", "$.serialization.cbor", "CBOR must remain an external-codec descriptor.");
338
+ }
339
+ }
340
+ }
341
+ function validateProvenance(value, path, issues) {
342
+ if (!isRecord(value)) {
343
+ issues.add("IR_PROVENANCE", path, "Asset provenance is required.");
344
+ return;
345
+ }
346
+ allowedKeys(value, ["source", "license", "hash", "derivations"], path, issues);
347
+ const source = objectValue(value, "source", path, issues);
348
+ const sourceKind = source
349
+ ? enumValue(source, "kind", ["ORIGINAL", "GENERATED", "DERIVED", "VENDOR"], `${path}.source`, issues)
350
+ : null;
351
+ if (source) {
352
+ allowedKeys(source, ["kind", "uri"], `${path}.source`, issues);
353
+ text(source, "uri", `${path}.source`, issues);
354
+ }
355
+ const license = objectValue(value, "license", path, issues);
356
+ if (license) {
357
+ allowedKeys(license, ["name", "spdxId", "uri", "attributionRequired"], `${path}.license`, issues);
358
+ text(license, "name", `${path}.license`, issues);
359
+ text(license, "spdxId", `${path}.license`, issues, true);
360
+ text(license, "uri", `${path}.license`, issues, true);
361
+ booleanValue(license, "attributionRequired", `${path}.license`, issues);
362
+ }
363
+ const hash = text(value, "hash", path, issues);
364
+ if (hash && !SHA256.test(hash))
365
+ issues.add("IR_ASSET_HASH", `${path}.hash`, "Asset hash must be lowercase SHA-256.");
366
+ if (!Array.isArray(value.derivations)) {
367
+ issues.add("IR_DERIVATIONS", `${path}.derivations`, "Asset derivations must be an array.");
368
+ return;
369
+ }
370
+ if (sourceKind === "DERIVED" && value.derivations.length === 0) {
371
+ issues.add("IR_DERIVATION_REQUIRED", `${path}.derivations`, "Derived assets require derivation evidence.");
372
+ }
373
+ let finalHash = null;
374
+ value.derivations.forEach((item, index) => {
375
+ const itemPath = `${path}.derivations[${index}]`;
376
+ if (!isRecord(item)) {
377
+ issues.add("IR_DERIVATION", itemPath, "Derivation must be an object.");
378
+ return;
379
+ }
380
+ allowedKeys(item, ["operation", "tool", "toolVersion", "inputHashes", "parametersDigest", "outputHash"], itemPath, issues);
381
+ text(item, "operation", itemPath, issues);
382
+ text(item, "tool", itemPath, issues);
383
+ text(item, "toolVersion", itemPath, issues, true);
384
+ stringList(item, "inputHashes", itemPath, issues).forEach((digest, digestIndex) => {
385
+ if (!SHA256.test(digest))
386
+ issues.add("IR_DERIVATION_HASH", `${itemPath}.inputHashes[${digestIndex}]`, "Derivation hash must be lowercase SHA-256.");
387
+ });
388
+ const parameters = text(item, "parametersDigest", itemPath, issues);
389
+ finalHash = text(item, "outputHash", itemPath, issues);
390
+ if (parameters && !SHA256.test(parameters))
391
+ issues.add("IR_DERIVATION_HASH", `${itemPath}.parametersDigest`, "Derivation hash must be lowercase SHA-256.");
392
+ if (finalHash && !SHA256.test(finalHash))
393
+ issues.add("IR_DERIVATION_HASH", `${itemPath}.outputHash`, "Derivation hash must be lowercase SHA-256.");
394
+ });
395
+ if (finalHash !== null && hash !== null && finalHash !== hash) {
396
+ issues.add("IR_DERIVATION_FINAL_HASH", `${path}.derivations`, "Final derivation output must match the asset hash.");
397
+ }
398
+ }
399
+ const REF_RULES = {
400
+ PROJECT: [
401
+ { field: "worldIds", kinds: ["WORLD"], many: true },
402
+ { field: "defaultSceneId", kinds: ["SCENE"] },
403
+ ],
404
+ WORLD: [{ field: "sceneIds", kinds: ["SCENE"], many: true }],
405
+ SCENE: [
406
+ { field: "entityIds", kinds: ["ENTITY"], many: true },
407
+ { field: "cameraId", kinds: ["CAMERA"] },
408
+ { field: "lightIds", kinds: ["LIGHT"], many: true },
409
+ ],
410
+ ENTITY: [
411
+ { field: "componentIds", kinds: ["COMPONENT"], many: true },
412
+ { field: "transformId", kinds: ["TRANSFORM"] },
413
+ { field: "parentEntityId", kinds: ["ENTITY"], optional: true },
414
+ ],
415
+ COMPONENT: [{ field: "ownerEntityId", kinds: ["ENTITY"] }],
416
+ TRANSFORM: [{ field: "parentTransformId", kinds: ["TRANSFORM"], optional: true }],
417
+ CAMERA: [{ field: "transformId", kinds: ["TRANSFORM"] }],
418
+ LIGHT: [{ field: "transformId", kinds: ["TRANSFORM"] }],
419
+ MATERIAL: [{ field: "textureIds", kinds: ["TEXTURE"], many: true }],
420
+ TEXTURE: [],
421
+ AUDIO: [{ field: "ownerEntityId", kinds: ["ENTITY"], optional: true }],
422
+ PHYSICS: [{ field: "ownerEntityId", kinds: ["ENTITY"] }],
423
+ CONSTRAINT: [
424
+ { field: "sourceId", kinds: TARGET_KINDS },
425
+ { field: "targetId", kinds: TARGET_KINDS },
426
+ ],
427
+ ANIMATION: [{ field: "targetIds", kinds: TARGET_KINDS, many: true }],
428
+ VFX: [
429
+ { field: "ownerEntityId", kinds: ["ENTITY"], optional: true },
430
+ { field: "materialIds", kinds: ["MATERIAL"], many: true },
431
+ ],
432
+ TIMELINE: [],
433
+ INPUT: [{ field: "contextEntityId", kinds: ["ENTITY"], optional: true }],
434
+ SAVE: [{ field: "stateSemanticIds", kinds: TARGET_KINDS, many: true }],
435
+ NETWORK: [{ field: "replicatedSemanticIds", kinds: TARGET_KINDS, many: true }],
436
+ BUILD: [
437
+ { field: "entrySceneId", kinds: ["SCENE"] },
438
+ { field: "assetIds", kinds: ASSET_KINDS, many: true },
439
+ ],
440
+ };
441
+ function addReference(references, id, expectedKinds, path) {
442
+ if (id !== null)
443
+ references.push({ id, expectedKinds, path });
444
+ }
445
+ function validateReferenceFields(node, issues) {
446
+ const references = [];
447
+ for (const rule of REF_RULES[node.kind]) {
448
+ if (rule.many) {
449
+ stringList(node.value, rule.field, node.path, issues, rule.optional).forEach((id, index) => {
450
+ addReference(references, id, rule.kinds, `${node.path}.${rule.field}[${index}]`);
451
+ });
452
+ }
453
+ else {
454
+ addReference(references, text(node.value, rule.field, node.path, issues, rule.optional), rule.kinds, `${node.path}.${rule.field}`);
455
+ }
456
+ }
457
+ return references;
458
+ }
459
+ function validateTimeline(node, issues, references) {
460
+ const duration = numberValue(node.value, "durationSeconds", node.path, issues, true);
461
+ if (!Array.isArray(node.value.tracks)) {
462
+ issues.add("IR_REQUIRED_ARRAY", `${node.path}.tracks`, "Timeline tracks must be an array.");
463
+ return;
464
+ }
465
+ const trackIds = new Set();
466
+ node.value.tracks.forEach((track, trackIndex) => {
467
+ const trackPath = `${node.path}.tracks[${trackIndex}]`;
468
+ if (!isRecord(track)) {
469
+ issues.add("IR_TIMELINE_TRACK", trackPath, "Timeline track must be an object.");
470
+ return;
471
+ }
472
+ allowedKeys(track, ["semanticRole", "trackId", "trackType", "targetId", "keyframes", "clips", "events"], trackPath, issues);
473
+ const trackId = text(track, "trackId", trackPath, issues);
474
+ if (trackId && trackIds.has(trackId))
475
+ issues.add("IR_DUPLICATE_TRACK", `${trackPath}.trackId`, "Track IDs must be unique.");
476
+ if (trackId)
477
+ trackIds.add(trackId);
478
+ addReference(references, text(track, "targetId", trackPath, issues), TARGET_KINDS, `${trackPath}.targetId`);
479
+ if (!Array.isArray(track.keyframes)) {
480
+ issues.add("IR_REQUIRED_ARRAY", `${trackPath}.keyframes`, "Timeline keyframes must be an array.");
481
+ return;
482
+ }
483
+ let previous = -Infinity;
484
+ track.keyframes.forEach((keyframe, keyframeIndex) => {
485
+ const keyframePath = `${trackPath}.keyframes[${keyframeIndex}]`;
486
+ if (!isRecord(keyframe)) {
487
+ issues.add("IR_TIMELINE_KEYFRAME", keyframePath, "Timeline keyframe must be an object.");
488
+ return;
489
+ }
490
+ allowedKeys(keyframe, ["atSeconds", "value"], keyframePath, issues);
491
+ const at = numberValue(keyframe, "atSeconds", keyframePath, issues);
492
+ if (at !== null && (at < 0 || (duration !== null && at > duration) || at < previous)) {
493
+ issues.add("IR_TIMELINE_TIME", `${keyframePath}.atSeconds`, "Keyframe time must be ordered and inside the timeline.");
494
+ }
495
+ if (at !== null)
496
+ previous = at;
497
+ if (!("value" in keyframe))
498
+ issues.add("IR_TIMELINE_VALUE", `${keyframePath}.value`, "Keyframe value is required.");
499
+ });
500
+ });
501
+ }
502
+ function validateInput(node, issues) {
503
+ if (!Array.isArray(node.value.actions)) {
504
+ issues.add("IR_REQUIRED_ARRAY", `${node.path}.actions`, "Input actions must be an array.");
505
+ return;
506
+ }
507
+ const actionIds = new Set();
508
+ node.value.actions.forEach((action, index) => {
509
+ const actionPath = `${node.path}.actions[${index}]`;
510
+ if (!isRecord(action)) {
511
+ issues.add("IR_INPUT_ACTION", actionPath, "Input action must be an object.");
512
+ return;
513
+ }
514
+ allowedKeys(action, ["actionId", "bindings"], actionPath, issues);
515
+ const actionId = text(action, "actionId", actionPath, issues);
516
+ if (actionId && actionIds.has(actionId))
517
+ issues.add("IR_DUPLICATE_ACTION", `${actionPath}.actionId`, "Action IDs must be unique.");
518
+ if (actionId)
519
+ actionIds.add(actionId);
520
+ stringList(action, "bindings", actionPath, issues);
521
+ });
522
+ }
523
+ function validateSpecificFields(node, issues, references) {
524
+ const value = node.value;
525
+ switch (node.kind) {
526
+ case "PROJECT":
527
+ case "WORLD":
528
+ case "SCENE":
529
+ case "ENTITY":
530
+ text(value, "name", node.path, issues);
531
+ break;
532
+ case "COMPONENT":
533
+ text(value, "componentType", node.path, issues);
534
+ objectValue(value, "properties", node.path, issues);
535
+ break;
536
+ case "TRANSFORM":
537
+ tuple(value.translation, 3, `${node.path}.translation`, issues);
538
+ tuple(value.rotation, 4, `${node.path}.rotation`, issues);
539
+ tuple(value.scale, 3, `${node.path}.scale`, issues, true);
540
+ break;
541
+ case "CAMERA": {
542
+ enumValue(value, "projection", ["PERSPECTIVE", "ORTHOGRAPHIC"], node.path, issues);
543
+ const near = numberValue(value, "near", node.path, issues, true);
544
+ const far = numberValue(value, "far", node.path, issues, true);
545
+ numberValue(value, "fieldOfViewRadians", node.path, issues, true, true);
546
+ if (near !== null && far !== null && near >= far)
547
+ issues.add("IR_CAMERA_RANGE", node.path, "Camera near must be smaller than far.");
548
+ break;
549
+ }
550
+ case "LIGHT": {
551
+ enumValue(value, "lightType", ["DIRECTIONAL", "POINT", "SPOT", "AREA"], node.path, issues);
552
+ const color = text(value, "color", node.path, issues);
553
+ if (color && !/^#[0-9a-f]{6}$/iu.test(color))
554
+ issues.add("IR_LIGHT_COLOR", `${node.path}.color`, "Light color must use hexadecimal RGB.");
555
+ const intensity = objectValue(value, "intensity", node.path, issues);
556
+ if (intensity) {
557
+ allowedKeys(intensity, ["value", "unit"], `${node.path}.intensity`, issues);
558
+ numberValue(intensity, "value", `${node.path}.intensity`, issues, true);
559
+ enumValue(intensity, "unit", ["LUMEN", "LUX", "CANDELA"], `${node.path}.intensity`, issues);
560
+ }
561
+ break;
562
+ }
563
+ case "MATERIAL":
564
+ text(value, "shadingModel", node.path, issues);
565
+ objectValue(value, "parameters", node.path, issues);
566
+ break;
567
+ case "TEXTURE":
568
+ enumValue(value, "usage", ["ALBEDO", "NORMAL", "EMISSIVE", "ROUGHNESS", "METALLIC", "UI", "OTHER"], node.path, issues);
569
+ enumValue(value, "colorSpace", ["SRGB", "LINEAR"], node.path, issues);
570
+ validateProvenance(value.provenance, `${node.path}.provenance`, issues);
571
+ break;
572
+ case "AUDIO":
573
+ booleanValue(value, "spatial", node.path, issues);
574
+ booleanValue(value, "loop", node.path, issues);
575
+ validateProvenance(value.provenance, `${node.path}.provenance`, issues);
576
+ break;
577
+ case "PHYSICS": {
578
+ enumValue(value, "bodyType", ["STATIC", "KINEMATIC", "DYNAMIC"], node.path, issues);
579
+ const mass = numberValue(value, "massKilograms", node.path, issues);
580
+ if (mass !== null && mass < 0)
581
+ issues.add("IR_PHYSICS_MASS", `${node.path}.massKilograms`, "Physics mass cannot be negative.");
582
+ objectValue(value, "collider", node.path, issues);
583
+ break;
584
+ }
585
+ case "CONSTRAINT":
586
+ text(value, "constraintType", node.path, issues);
587
+ objectValue(value, "parameters", node.path, issues);
588
+ break;
589
+ case "ANIMATION":
590
+ numberValue(value, "durationSeconds", node.path, issues, true);
591
+ validateProvenance(value.provenance, `${node.path}.provenance`, issues);
592
+ break;
593
+ case "VFX":
594
+ validateProvenance(value.provenance, `${node.path}.provenance`, issues);
595
+ break;
596
+ case "TIMELINE":
597
+ validateTimeline(node, issues, references);
598
+ break;
599
+ case "INPUT":
600
+ validateInput(node, issues);
601
+ break;
602
+ case "SAVE":
603
+ enumValue(value, "strategy", ["SNAPSHOT", "EVENT_LOG", "HYBRID"], node.path, issues);
604
+ text(value, "schemaRevision", node.path, issues);
605
+ break;
606
+ case "NETWORK":
607
+ enumValue(value, "mode", ["OFFLINE", "CLIENT_SERVER", "PEER_TO_PEER", "LOCKSTEP"], node.path, issues);
608
+ enumValue(value, "authority", ["NONE", "SERVER", "OWNER", "DISTRIBUTED"], node.path, issues);
609
+ numberValue(value, "tickRateHz", node.path, issues);
610
+ break;
611
+ case "BUILD":
612
+ text(value, "target", node.path, issues);
613
+ objectValue(value, "options", node.path, issues);
614
+ break;
615
+ }
616
+ }
617
+ function validateNode(node, issues, conceptReferences = []) {
618
+ allowedKeys(node.value, NODE_KEYS[node.kind], node.path, issues);
619
+ const semanticId = text(node.value, "semanticId", node.path, issues);
620
+ if (semanticId && (!SEMANTIC_ID.test(semanticId) || !semanticId.startsWith(`${KIND_PREFIX[node.kind]}.`))) {
621
+ issues.add("IR_SEMANTIC_ID", `${node.path}.semanticId`, "Semantic ID format or kind prefix is invalid.");
622
+ }
623
+ if (node.value.kind !== node.kind)
624
+ issues.add("IR_NODE_KIND", `${node.path}.kind`, "Node kind does not match its collection.");
625
+ validateExtensions(node.value.extensions, `${node.path}.extensions`, issues);
626
+ const typed = [...validateReferenceFields(node, issues), ...conceptReferences];
627
+ validateSpecificFields(node, issues, typed);
628
+ const declared = stringList(node.value, "references", node.path, issues);
629
+ declared.forEach((id, index) => {
630
+ if (!SEMANTIC_ID.test(id))
631
+ issues.add("IR_REFERENCE_ID", `${node.path}.references[${index}]`, "Reference semantic ID is invalid.");
632
+ });
633
+ const expected = [...new Set(typed.map((reference) => reference.id))].sort();
634
+ if (JSON.stringify([...declared].sort()) !== JSON.stringify(expected)) {
635
+ issues.add("IR_REFERENCE_DECLARATION", `${node.path}.references`, "Declared references must exactly match typed relationships.");
636
+ }
637
+ return typed;
638
+ }
639
+ function canonicalJson(value) {
640
+ if (value === null || typeof value === "boolean" || typeof value === "string")
641
+ return JSON.stringify(value);
642
+ if (typeof value === "number")
643
+ return Object.is(value, -0) ? "0" : JSON.stringify(value);
644
+ if (Array.isArray(value))
645
+ return `[${value.map(canonicalJson).join(",")}]`;
646
+ const record = value;
647
+ return `{${Object.keys(record).sort().map((key) => `${JSON.stringify(key)}:${canonicalJson(record[key])}`).join(",")}}`;
648
+ }
649
+ function digest(value) {
650
+ return `sha256:${createHash("sha256").update(canonicalJson(value), "utf8").digest("hex")}`;
651
+ }
652
+ function cloneCanonical(value) {
653
+ return JSON.parse(canonicalJson(value));
654
+ }
655
+ function toJsonPointer(path) {
656
+ return path.replace(/^\$\.?/u, "/").replace(/\[(\d+)\]/gu, "/$1").replace(/\./gu, "/");
657
+ }
658
+ function collectNodes(document, issues) {
659
+ const nodes = [];
660
+ if (!isRecord(document.project)) {
661
+ issues.add("IR_PROJECT", "$.project", "Project node is required.");
662
+ }
663
+ else
664
+ nodes.push({ value: document.project, kind: "PROJECT", path: "$.project" });
665
+ for (const [kind, collection] of COLLECTIONS) {
666
+ const value = document[collection];
667
+ if (!Array.isArray(value)) {
668
+ issues.add("IR_COLLECTION", `$.${collection}`, "Required node collection is missing.");
669
+ continue;
670
+ }
671
+ if (value.length === 0)
672
+ issues.add("IR_COLLECTION_EMPTY", `$.${collection}`, "Required node collection cannot be empty.");
673
+ value.forEach((item, index) => {
674
+ const path = `$.${collection}[${index}]`;
675
+ if (!isRecord(item))
676
+ issues.add("IR_NODE", path, "Collection item must be an object.");
677
+ else
678
+ nodes.push({ value: item, kind, path });
679
+ });
680
+ }
681
+ if (nodes.length > MAX_NODES)
682
+ issues.add("IR_NODE_LIMIT", "$", "Node limit exceeded.");
683
+ return nodes;
684
+ }
685
+ function validateInternal(input) {
686
+ const issues = new Issues();
687
+ if (!validateJson(input, issues) || !isRecord(input)) {
688
+ if (!isRecord(input))
689
+ issues.add("IR_DOCUMENT", "$", "Experience IR document must be a plain object.");
690
+ return { document: null, documentDigest: null, nodes: [], issues: issues.finish() };
691
+ }
692
+ const serialized = JSON.stringify(input);
693
+ if (Buffer.byteLength(serialized, "utf8") > MAX_BYTES)
694
+ issues.add("IR_BYTE_LIMIT", "$", "Document byte limit exceeded.");
695
+ allowedKeys(input, [
696
+ "schemaVersion", "revision", "coordinateSystem", "serialization", "project", "worlds", "scenes",
697
+ "entities", "components", "transforms", "cameras", "lights", "materials", "textures", "audio",
698
+ "physics", "constraints", "animations", "vfx", "timelines", "inputs", "saves", "networks", "builds",
699
+ "extensions",
700
+ ], "$", issues);
701
+ if (input.schemaVersion !== EXPERIENCE_IR_SCHEMA_VERSION)
702
+ issues.add("IR_SCHEMA_VERSION", "$.schemaVersion", "Unsupported Experience IR schema version.");
703
+ text(input, "revision", "$", issues);
704
+ validateExtensions(input.extensions, "$.extensions", issues);
705
+ validateCoordinateSystem(input, issues);
706
+ validateSerialization(input, issues);
707
+ const nodes = collectNodes(input, issues);
708
+ const semantics = validarSemanticaExperienceIr(nodes);
709
+ semantics.issues.forEach((issue) => issues.add(issue.code, issue.path, issue.message));
710
+ const byId = new Map();
711
+ const typedByNode = new Map();
712
+ for (const node of nodes) {
713
+ const conceptReferences = obterReferenciasConceituaisExperienceIr(node).map((reference) => ({
714
+ id: reference.id,
715
+ expectedKinds: reference.expectedKinds,
716
+ path: reference.path,
717
+ }));
718
+ const typed = validateNode(node, issues, conceptReferences);
719
+ typedByNode.set(node, typed);
720
+ const id = typeof node.value.semanticId === "string" ? node.value.semanticId : null;
721
+ if (id !== null) {
722
+ if (byId.has(id))
723
+ issues.add("IR_DUPLICATE_SEMANTIC_ID", `${node.path}.semanticId`, "Semantic IDs must be globally unique.");
724
+ else
725
+ byId.set(id, node);
726
+ }
727
+ }
728
+ for (const node of nodes) {
729
+ const declared = Array.isArray(node.value.references) ? node.value.references : [];
730
+ declared.forEach((id, index) => {
731
+ if (typeof id === "string" && !byId.has(id))
732
+ issues.add("IR_DANGLING_REFERENCE", `${node.path}.references[${index}]`, "Declared reference target does not exist.");
733
+ });
734
+ for (const reference of typedByNode.get(node) ?? []) {
735
+ const target = byId.get(reference.id);
736
+ if (!target)
737
+ issues.add("IR_DANGLING_REFERENCE", reference.path, "Typed reference target does not exist.");
738
+ else if (!reference.expectedKinds.includes(target.kind))
739
+ issues.add("IR_REFERENCE_KIND", reference.path, "Typed reference target kind is invalid.");
740
+ }
741
+ }
742
+ const finished = issues.finish();
743
+ if (finished.length > 0)
744
+ return { document: null, documentDigest: null, nodes, issues: finished };
745
+ const document = input;
746
+ return { document, documentDigest: digest(document), nodes, issues: finished };
747
+ }
748
+ function buildIndex(validation) {
749
+ const entries = validation.nodes.map((node) => ({
750
+ semanticId: node.value.semanticId,
751
+ kind: node.kind,
752
+ semanticRoles: obterPapeisSemanticosExperienceIr(node.value, node.kind),
753
+ path: toJsonPointer(node.path),
754
+ contentDigest: digest(node.value),
755
+ references: [...node.value.references].sort(),
756
+ })).sort((a, b) => a.semanticId.localeCompare(b.semanticId, "en"));
757
+ const grouped = new Map();
758
+ entries.forEach((entry) => {
759
+ const prefix = entry.semanticId.split(/[._-]/u, 1)[0];
760
+ grouped.set(prefix, [...(grouped.get(prefix) ?? []), entry]);
761
+ });
762
+ const partitions = [...grouped.entries()].sort(([a], [b]) => a.localeCompare(b, "en"))
763
+ .map(([prefix, values]) => ({ prefix, entryCount: values.length, contentDigest: digest(values) }));
764
+ return {
765
+ schemaVersion: EXPERIENCE_IR_INDEX_SCHEMA_VERSION,
766
+ documentDigest: validation.documentDigest,
767
+ entries,
768
+ partitions,
769
+ authoritative: false,
770
+ };
771
+ }
772
+ export function validarExperienceIr(input) {
773
+ const validation = validateInternal(input);
774
+ return {
775
+ sucesso: true,
776
+ valido: validation.document !== null,
777
+ documentDigest: validation.documentDigest,
778
+ issues: validation.issues,
779
+ ...BOUNDARY,
780
+ };
781
+ }
782
+ export function indexarExperienceIr(input) {
783
+ const validation = validateInternal(input);
784
+ if (!validation.document)
785
+ return { sucesso: false, indice: null, issues: validation.issues, ...BOUNDARY };
786
+ return { sucesso: true, indice: buildIndex(validation), issues: [], ...BOUNDARY };
787
+ }
788
+ export function consultarIndiceExperienceIr(index, semanticId) {
789
+ if (typeof semanticId !== "string" || !SEMANTIC_ID.test(semanticId) || !Array.isArray(index?.entries)) {
790
+ return { sucesso: true, encontrado: false, entry: null, ...BOUNDARY };
791
+ }
792
+ let low = 0;
793
+ let high = index.entries.length - 1;
794
+ while (low <= high) {
795
+ const middle = Math.floor((low + high) / 2);
796
+ const entry = index.entries[middle];
797
+ const comparison = entry.semanticId.localeCompare(semanticId, "en");
798
+ if (comparison === 0)
799
+ return { sucesso: true, encontrado: true, entry, ...BOUNDARY };
800
+ if (comparison < 0)
801
+ low = middle + 1;
802
+ else
803
+ high = middle - 1;
804
+ }
805
+ return { sucesso: true, encontrado: false, entry: null, ...BOUNDARY };
806
+ }
807
+ export function criarChunkExperienceIr(input, semanticIds, incluirDependencias = true) {
808
+ const validation = validateInternal(input);
809
+ if (!validation.document)
810
+ return { sucesso: false, chunk: null, issues: validation.issues, ...BOUNDARY };
811
+ const issues = new Issues();
812
+ if (!Array.isArray(semanticIds) || semanticIds.length === 0 || semanticIds.length > MAX_REFS) {
813
+ issues.add("IR_CHUNK_IDS", "$.semanticIds", "A bounded non-empty semantic ID array is required.");
814
+ }
815
+ if (typeof incluirDependencias !== "boolean")
816
+ issues.add("IR_CHUNK_DEPENDENCIES", "$.incluirDependencias", "Dependency mode must be boolean.");
817
+ const requested = [];
818
+ const requestedSet = new Set();
819
+ const byId = new Map(validation.nodes.map((node) => [node.value.semanticId, node]));
820
+ if (Array.isArray(semanticIds))
821
+ semanticIds.forEach((candidate, index) => {
822
+ const path = `$.semanticIds[${index}]`;
823
+ if (typeof candidate !== "string" || !SEMANTIC_ID.test(candidate) || !byId.has(candidate)) {
824
+ issues.add("IR_CHUNK_ID", path, "Requested semantic ID is invalid or unknown.");
825
+ }
826
+ else if (requestedSet.has(candidate)) {
827
+ issues.add("IR_CHUNK_DUPLICATE_ID", path, "Requested semantic IDs must be unique.");
828
+ }
829
+ else {
830
+ requestedSet.add(candidate);
831
+ requested.push(candidate);
832
+ }
833
+ });
834
+ const finished = issues.finish();
835
+ if (finished.length > 0)
836
+ return { sucesso: false, chunk: null, issues: finished, ...BOUNDARY };
837
+ const selected = new Set(requested);
838
+ if (incluirDependencias) {
839
+ const queue = [...requested];
840
+ while (queue.length > 0) {
841
+ const current = byId.get(queue.shift());
842
+ current.value.references.forEach((reference) => {
843
+ if (!selected.has(reference)) {
844
+ selected.add(reference);
845
+ queue.push(reference);
846
+ }
847
+ });
848
+ }
849
+ }
850
+ const index = buildIndex(validation);
851
+ const indexById = new Map(index.entries.map((entry) => [entry.semanticId, entry]));
852
+ const entries = [...selected].sort().map((id) => {
853
+ const node = byId.get(id);
854
+ const entry = indexById.get(id);
855
+ return {
856
+ semanticId: id,
857
+ kind: node.kind,
858
+ semanticRoles: entry.semanticRoles,
859
+ contentDigest: entry.contentDigest,
860
+ value: cloneCanonical(node.value),
861
+ };
862
+ });
863
+ const externalReferences = incluirDependencias ? [] : [...new Set(entries.flatMap((entry) => byId.get(entry.semanticId)?.value.references).filter((reference) => !selected.has(reference)))].sort();
864
+ const payload = {
865
+ schemaVersion: EXPERIENCE_IR_CHUNK_SCHEMA_VERSION,
866
+ documentDigest: validation.documentDigest,
867
+ requestedSemanticIdDigests: requested.map((id) => digest(id)).sort(),
868
+ entries,
869
+ externalReferences,
870
+ authoritative: false,
871
+ };
872
+ return { sucesso: true, chunk: { ...payload, chunkDigest: digest(payload) }, issues: [], ...BOUNDARY };
873
+ }
874
+ export function descreverSerializacaoExperienceIr(schemaVersion = EXPERIENCE_IR_SCHEMA_VERSION) {
875
+ if (schemaVersion !== EXPERIENCE_IR_SCHEMA_VERSION) {
876
+ return {
877
+ sucesso: false,
878
+ descriptor: null,
879
+ issues: [{ code: "IR_SCHEMA_VERSION", path: "$.schemaVersion", message: "Unsupported Experience IR schema version." }],
880
+ ...BOUNDARY,
881
+ };
882
+ }
883
+ return {
884
+ sucesso: true,
885
+ descriptor: {
886
+ schemaVersion: EXPERIENCE_IR_SERIALIZATION_SCHEMA_VERSION,
887
+ json: { ...SERIALIZATION.json, native: true },
888
+ cbor: { ...SERIALIZATION.cbor, support: "EXTERNAL", installed: false },
889
+ semanticRoles: EXPERIENCE_IR_SEMANTIC_ROLE_SCHEMA,
890
+ },
891
+ issues: [],
892
+ ...BOUNDARY,
893
+ };
894
+ }
895
+ //# sourceMappingURL=experienceIr.js.map