@acpus/core 0.6.0 → 0.7.0-alpha.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 (210) hide show
  1. package/README.md +128 -4
  2. package/dist/.tsbuildinfo +1 -0
  3. package/dist/graph/builder.d.ts +103 -0
  4. package/dist/graph/builder.d.ts.map +1 -0
  5. package/dist/graph/builder.js +209 -0
  6. package/dist/graph/builder.js.map +1 -0
  7. package/dist/graph/lowering.d.ts +8 -0
  8. package/dist/graph/lowering.d.ts.map +1 -0
  9. package/dist/graph/lowering.js +45 -0
  10. package/dist/graph/lowering.js.map +1 -0
  11. package/dist/graph/refs.d.ts +11 -0
  12. package/dist/graph/refs.d.ts.map +1 -0
  13. package/dist/graph/refs.js +12 -0
  14. package/dist/graph/refs.js.map +1 -0
  15. package/dist/graph/scope.d.ts +21 -0
  16. package/dist/graph/scope.d.ts.map +1 -0
  17. package/dist/graph/scope.js +22 -0
  18. package/dist/graph/scope.js.map +1 -0
  19. package/dist/index.d.ts +11 -25
  20. package/dist/index.d.ts.map +1 -1
  21. package/dist/index.js +4 -15
  22. package/dist/index.js.map +1 -1
  23. package/dist/internal/symbols.d.ts +9 -0
  24. package/dist/internal/symbols.d.ts.map +1 -0
  25. package/dist/internal/symbols.js +9 -0
  26. package/dist/internal/symbols.js.map +1 -0
  27. package/dist/internal/type-utils.d.ts +7 -0
  28. package/dist/internal/type-utils.d.ts.map +1 -0
  29. package/dist/internal/type-utils.js +8 -0
  30. package/dist/internal/type-utils.js.map +1 -0
  31. package/dist/ir/types.d.ts +218 -0
  32. package/dist/ir/types.d.ts.map +1 -0
  33. package/dist/ir/types.js.map +1 -0
  34. package/dist/ir/validator.d.ts +3 -0
  35. package/dist/ir/validator.d.ts.map +1 -0
  36. package/dist/ir/validator.js +671 -0
  37. package/dist/ir/validator.js.map +1 -0
  38. package/dist/ir.d.ts +3 -0
  39. package/dist/ir.d.ts.map +1 -0
  40. package/dist/ir.js +2 -0
  41. package/dist/ir.js.map +1 -0
  42. package/dist/nodes/composite/fanout.d.ts +24 -0
  43. package/dist/nodes/composite/fanout.d.ts.map +1 -0
  44. package/dist/nodes/composite/fanout.js +24 -0
  45. package/dist/nodes/composite/fanout.js.map +1 -0
  46. package/dist/nodes/composite/if.d.ts +12 -0
  47. package/dist/nodes/composite/if.d.ts.map +1 -0
  48. package/dist/nodes/composite/if.js +13 -0
  49. package/dist/nodes/composite/if.js.map +1 -0
  50. package/dist/nodes/composite/loop.d.ts +17 -0
  51. package/dist/nodes/composite/loop.d.ts.map +1 -0
  52. package/dist/nodes/composite/loop.js +22 -0
  53. package/dist/nodes/composite/loop.js.map +1 -0
  54. package/dist/nodes/composite/parallel.d.ts +29 -0
  55. package/dist/nodes/composite/parallel.d.ts.map +1 -0
  56. package/dist/nodes/composite/parallel.js +18 -0
  57. package/dist/nodes/composite/parallel.js.map +1 -0
  58. package/dist/nodes/composite/shared.d.ts +38 -0
  59. package/dist/nodes/composite/shared.d.ts.map +1 -0
  60. package/dist/nodes/composite/shared.js +2 -0
  61. package/dist/nodes/composite/shared.js.map +1 -0
  62. package/dist/nodes/composite/switch.d.ts +22 -0
  63. package/dist/nodes/composite/switch.d.ts.map +1 -0
  64. package/dist/nodes/composite/switch.js +12 -0
  65. package/dist/nodes/composite/switch.js.map +1 -0
  66. package/dist/nodes/control/assert.d.ts +10 -0
  67. package/dist/nodes/control/assert.d.ts.map +1 -0
  68. package/dist/nodes/control/assert.js +14 -0
  69. package/dist/nodes/control/assert.js.map +1 -0
  70. package/dist/nodes/leaf/agent.d.ts +63 -0
  71. package/dist/nodes/leaf/agent.d.ts.map +1 -0
  72. package/dist/nodes/leaf/agent.js +56 -0
  73. package/dist/nodes/leaf/agent.js.map +1 -0
  74. package/dist/nodes/leaf/shared.d.ts +11 -0
  75. package/dist/nodes/leaf/shared.d.ts.map +1 -0
  76. package/dist/nodes/leaf/shared.js +2 -0
  77. package/dist/nodes/leaf/shared.js.map +1 -0
  78. package/dist/nodes/leaf/signal.d.ts +27 -0
  79. package/dist/nodes/leaf/signal.d.ts.map +1 -0
  80. package/dist/nodes/leaf/signal.js +18 -0
  81. package/dist/nodes/leaf/signal.js.map +1 -0
  82. package/dist/nodes/leaf/task.d.ts +71 -0
  83. package/dist/nodes/leaf/task.d.ts.map +1 -0
  84. package/dist/nodes/leaf/task.js +88 -0
  85. package/dist/nodes/leaf/task.js.map +1 -0
  86. package/dist/runtime/dollar.d.ts +42 -0
  87. package/dist/runtime/dollar.d.ts.map +1 -0
  88. package/dist/runtime/dollar.js +82 -0
  89. package/dist/runtime/dollar.js.map +1 -0
  90. package/dist/runtime/secret.d.ts +10 -0
  91. package/dist/runtime/secret.d.ts.map +1 -0
  92. package/dist/runtime/secret.js +12 -0
  93. package/dist/runtime/secret.js.map +1 -0
  94. package/dist/runtime/task-context.d.ts +28 -0
  95. package/dist/runtime/task-context.d.ts.map +1 -0
  96. package/dist/runtime/task-context.js +2 -0
  97. package/dist/runtime/task-context.js.map +1 -0
  98. package/dist/runtime.d.ts +5 -0
  99. package/dist/runtime.d.ts.map +1 -0
  100. package/dist/runtime.js +3 -0
  101. package/dist/runtime.js.map +1 -0
  102. package/dist/schema/index.d.ts +6 -2
  103. package/dist/schema/index.d.ts.map +1 -1
  104. package/dist/schema/index.js +3 -2
  105. package/dist/schema/index.js.map +1 -1
  106. package/dist/schema/lower.d.ts +25 -0
  107. package/dist/schema/lower.d.ts.map +1 -0
  108. package/dist/schema/lower.js +198 -0
  109. package/dist/schema/lower.js.map +1 -0
  110. package/dist/schema/validate.d.ts +18 -0
  111. package/dist/schema/validate.d.ts.map +1 -0
  112. package/dist/schema/validate.js +22 -0
  113. package/dist/schema/validate.js.map +1 -0
  114. package/dist/schema/zod.d.ts +17 -0
  115. package/dist/schema/zod.d.ts.map +1 -0
  116. package/dist/schema/zod.js +15 -0
  117. package/dist/schema/zod.js.map +1 -0
  118. package/dist/schema.d.ts +3 -0
  119. package/dist/schema.d.ts.map +1 -0
  120. package/dist/schema.js +2 -0
  121. package/dist/schema.js.map +1 -0
  122. package/dist/template/template.d.ts +5 -0
  123. package/dist/template/template.d.ts.map +1 -0
  124. package/dist/template/template.js +10 -0
  125. package/dist/template/template.js.map +1 -0
  126. package/dist/workflow.d.ts +3 -0
  127. package/dist/workflow.d.ts.map +1 -0
  128. package/dist/workflow.js +2 -0
  129. package/dist/workflow.js.map +1 -0
  130. package/package.json +35 -20
  131. package/dist/agent-overrides.d.ts +0 -33
  132. package/dist/agent-overrides.d.ts.map +0 -1
  133. package/dist/agent-overrides.js +0 -178
  134. package/dist/agent-overrides.js.map +0 -1
  135. package/dist/cel-ast.d.ts +0 -39
  136. package/dist/cel-ast.d.ts.map +0 -1
  137. package/dist/cel-ast.js +0 -157
  138. package/dist/cel-ast.js.map +0 -1
  139. package/dist/cel-environment.d.ts +0 -6
  140. package/dist/cel-environment.d.ts.map +0 -1
  141. package/dist/cel-environment.js +0 -44
  142. package/dist/cel-environment.js.map +0 -1
  143. package/dist/compiler.d.ts +0 -4
  144. package/dist/compiler.d.ts.map +0 -1
  145. package/dist/compiler.js +0 -702
  146. package/dist/compiler.js.map +0 -1
  147. package/dist/composite-contract.d.ts +0 -46
  148. package/dist/composite-contract.d.ts.map +0 -1
  149. package/dist/composite-contract.js +0 -97
  150. package/dist/composite-contract.js.map +0 -1
  151. package/dist/diagnostics.d.ts +0 -9
  152. package/dist/diagnostics.d.ts.map +0 -1
  153. package/dist/diagnostics.js +0 -16
  154. package/dist/diagnostics.js.map +0 -1
  155. package/dist/duration.d.ts +0 -13
  156. package/dist/duration.d.ts.map +0 -1
  157. package/dist/duration.js +0 -32
  158. package/dist/duration.js.map +0 -1
  159. package/dist/expression-scope.d.ts +0 -23
  160. package/dist/expression-scope.d.ts.map +0 -1
  161. package/dist/expression-scope.js +0 -545
  162. package/dist/expression-scope.js.map +0 -1
  163. package/dist/expressions-shared.d.ts +0 -19
  164. package/dist/expressions-shared.d.ts.map +0 -1
  165. package/dist/expressions-shared.js +0 -27
  166. package/dist/expressions-shared.js.map +0 -1
  167. package/dist/expressions.d.ts +0 -9
  168. package/dist/expressions.d.ts.map +0 -1
  169. package/dist/expressions.js +0 -95
  170. package/dist/expressions.js.map +0 -1
  171. package/dist/hash.d.ts +0 -19
  172. package/dist/hash.d.ts.map +0 -1
  173. package/dist/hash.js +0 -120
  174. package/dist/hash.js.map +0 -1
  175. package/dist/hook-validation.d.ts +0 -9
  176. package/dist/hook-validation.d.ts.map +0 -1
  177. package/dist/hook-validation.js +0 -73
  178. package/dist/hook-validation.js.map +0 -1
  179. package/dist/hooks.d.ts +0 -150
  180. package/dist/hooks.d.ts.map +0 -1
  181. package/dist/hooks.js +0 -18
  182. package/dist/hooks.js.map +0 -1
  183. package/dist/schedule.d.ts +0 -3
  184. package/dist/schedule.d.ts.map +0 -1
  185. package/dist/schedule.js +0 -21
  186. package/dist/schedule.js.map +0 -1
  187. package/dist/schema/dsl.d.ts +0 -34
  188. package/dist/schema/dsl.d.ts.map +0 -1
  189. package/dist/schema/dsl.js +0 -231
  190. package/dist/schema/dsl.js.map +0 -1
  191. package/dist/schema/helpers.d.ts +0 -24
  192. package/dist/schema/helpers.d.ts.map +0 -1
  193. package/dist/schema/helpers.js +0 -61
  194. package/dist/schema/helpers.js.map +0 -1
  195. package/dist/schema-validator.d.ts +0 -29
  196. package/dist/schema-validator.d.ts.map +0 -1
  197. package/dist/schema-validator.js +0 -666
  198. package/dist/schema-validator.js.map +0 -1
  199. package/dist/source-resolver.d.ts +0 -22
  200. package/dist/source-resolver.d.ts.map +0 -1
  201. package/dist/source-resolver.js +0 -43
  202. package/dist/source-resolver.js.map +0 -1
  203. package/dist/types.d.ts +0 -126
  204. package/dist/types.d.ts.map +0 -1
  205. package/dist/types.js.map +0 -1
  206. package/dist/workflow-schema.d.ts +0 -10
  207. package/dist/workflow-schema.d.ts.map +0 -1
  208. package/dist/workflow-schema.js +0 -523
  209. package/dist/workflow-schema.js.map +0 -1
  210. /package/dist/{types.js → ir/types.js} +0 -0
@@ -0,0 +1,671 @@
1
+ import { validateExprIR } from "@acpus/expression/validator";
2
+ export function validateWorkflowIR(ir) {
3
+ const diagnostics = [];
4
+ if (!isRecord(ir)) {
5
+ addError(diagnostics, "IR002", "WorkflowIR must be an object.", "");
6
+ return diagnostics;
7
+ }
8
+ validateKnownFields(ir, ["irVersion", "name", "description", "inputSchema", "agents", "root", "outputs", "lock", "diagnostics"], diagnostics, "");
9
+ if (ir.irVersion !== 2)
10
+ addError(diagnostics, "IR002", "WorkflowIR irVersion must be 2.", "irVersion");
11
+ if (!ir.name || !/^[A-Za-z_][A-Za-z0-9_-]*$/.test(ir.name)) {
12
+ addWarning(diagnostics, "W002", `Workflow name '${ir.name}' is not identifier-like. This is allowed but discouraged.`);
13
+ }
14
+ if (ir.description !== undefined && typeof ir.description !== "string") {
15
+ addError(diagnostics, "IR002", "WorkflowIR description must be a string.", "description");
16
+ }
17
+ validateSchema(ir.inputSchema, diagnostics, "inputSchema");
18
+ const agents = isRecord(ir.agents) ? ir.agents : undefined;
19
+ if (!agents)
20
+ addError(diagnostics, "IR002", "WorkflowIR agents must be an object.", "agents");
21
+ else
22
+ validateAgents(agents, diagnostics);
23
+ validateLock(ir.lock, diagnostics, "lock");
24
+ validateDiagnostics(ir.diagnostics, diagnostics, "diagnostics");
25
+ validateExprObject(ir.outputs, diagnostics, "outputs");
26
+ const ids = new Set();
27
+ validateScope(ir.root, diagnostics, {
28
+ path: "root",
29
+ ids,
30
+ agents: new Set(agents ? Object.keys(agents) : []),
31
+ });
32
+ return diagnostics;
33
+ }
34
+ function validateLock(lock, diagnostics, path) {
35
+ if (!requireRecord(lock, diagnostics, path, "IR002", "WorkflowIR lock must be an object."))
36
+ return;
37
+ validateKnownFields(lock, ["acpusCoreVersion", "workflowSource", "workflowSourceDigest", "generatedAt", "notes"], diagnostics, path);
38
+ validateRequiredNonEmptyString(lock.acpusCoreVersion, diagnostics, `${path}.acpusCoreVersion`, "IR002", "WorkflowIR lock acpusCoreVersion must be a non-empty string.");
39
+ validateRequiredNonEmptyString(lock.generatedAt, diagnostics, `${path}.generatedAt`, "IR002", "WorkflowIR lock generatedAt must be a non-empty string.");
40
+ const notes = requireArray(lock.notes, diagnostics, `${path}.notes`, "IR002", "WorkflowIR lock notes must be an array.");
41
+ if (!notes)
42
+ return;
43
+ forEachDense(notes, diagnostics, `${path}.notes`, (note, index) => {
44
+ if (typeof note !== "string")
45
+ addError(diagnostics, "IR002", "WorkflowIR lock notes must be a string array.", `${path}.notes.${index}`);
46
+ }, { code: "IR002" });
47
+ }
48
+ function validateDiagnostics(value, diagnostics, path) {
49
+ const items = requireArray(value, diagnostics, path, "IR002", "WorkflowIR diagnostics must be an array.");
50
+ if (!items)
51
+ return;
52
+ forEachDense(items, diagnostics, path, (diagnostic, index) => {
53
+ if (!requireRecord(diagnostic, diagnostics, `${path}.${index}`, "IR002", "WorkflowIR diagnostic entries must be objects.")) {
54
+ return;
55
+ }
56
+ validateKnownFields(diagnostic, ["code", "severity", "message", "path", "source", "hint"], diagnostics, `${path}.${index}`);
57
+ validateRequiredNonEmptyString(diagnostic.code, diagnostics, `${path}.${index}.code`, "IR002", "WorkflowIR diagnostic code must be a non-empty string.");
58
+ validateRequiredEnum(diagnostic.severity, ["error", "warning", "info"], diagnostics, `${path}.${index}.severity`, "IR002", "WorkflowIR diagnostic severity must be error, warning, or info.");
59
+ validateRequiredNonEmptyString(diagnostic.message, diagnostics, `${path}.${index}.message`, "IR002", "WorkflowIR diagnostic message must be a non-empty string.");
60
+ }, { code: "IR002" });
61
+ }
62
+ function validateAgents(agents, diagnostics) {
63
+ for (const [name, agent] of Object.entries(agents)) {
64
+ const path = `agents.${name}`;
65
+ if (!requireRecord(agent, diagnostics, path, "A002", `Agent '${name}' definition must be an object.`))
66
+ continue;
67
+ if (agent.kind === "agent_definition") {
68
+ validateKnownFields(agent, ["kind", "use", "model", "permissionMode", "agentMode", "cwd", "env"], diagnostics, path);
69
+ validateRequiredNonEmptyString(agent.use, diagnostics, `${path}.use`, "A002", `Agent '${name}' use must be a non-empty string.`);
70
+ validatePermissionMode(agent.permissionMode, diagnostics, `${path}.permissionMode`);
71
+ validateAgentMode(agent.agentMode, diagnostics, `${path}.agentMode`);
72
+ if (agent.cwd)
73
+ validateExpr(agent.cwd, diagnostics, `${path}.cwd`);
74
+ validateEnv(agent.env, diagnostics, `${path}.env`);
75
+ continue;
76
+ }
77
+ if (agent.kind === "agent_command") {
78
+ validateKnownFields(agent, ["kind", "command", "model", "permissionMode", "agentMode", "cwd", "env"], diagnostics, path);
79
+ validateRequiredNonEmptyString(agent.command, diagnostics, `${path}.command`, "A002", `Command-backed agent '${name}' command must be a non-empty string.`);
80
+ validatePermissionMode(agent.permissionMode, diagnostics, `${path}.permissionMode`);
81
+ validateAgentMode(agent.agentMode, diagnostics, `${path}.agentMode`);
82
+ if (agent.cwd)
83
+ validateExpr(agent.cwd, diagnostics, `${path}.cwd`);
84
+ validateEnv(agent.env, diagnostics, `${path}.env`);
85
+ continue;
86
+ }
87
+ addError(diagnostics, "A002", `Agent '${name}' kind must be agent_definition or agent_command.`, path);
88
+ }
89
+ }
90
+ function validateScope(scope, diagnostics, ctx) {
91
+ if (!requireRecord(scope, diagnostics, ctx.path, "IR002", "Scope must be an object."))
92
+ return;
93
+ validateKnownFields(scope, ["nodes", "outputs"], diagnostics, ctx.path);
94
+ const nodes = requireArray(scope.nodes, diagnostics, `${ctx.path}.nodes`, "IR002", "Scope nodes must be an array.");
95
+ if (!nodes)
96
+ return;
97
+ forEachDense(nodes, diagnostics, `${ctx.path}.nodes`, node => validateNode(node, diagnostics, ctx), { code: "IR002" });
98
+ if (scope.outputs !== undefined) {
99
+ validateExprObject(scope.outputs, diagnostics, `${ctx.path}.outputs`);
100
+ }
101
+ }
102
+ function validateNode(node, diagnostics, ctx) {
103
+ if (!requireRecord(node, diagnostics, `${ctx.path}.nodes`, "IR002", "Node must be an object."))
104
+ return;
105
+ const id = typeof node.id === "string" ? node.id : "<unknown>";
106
+ const path = `${ctx.path}.nodes.${id}`;
107
+ if (!/^[A-Za-z_][A-Za-z0-9_-]*$/.test(id))
108
+ addError(diagnostics, "ID001", `Invalid node id '${id}'.`, path);
109
+ if (ctx.ids.has(id))
110
+ addError(diagnostics, "ID002", `Duplicate node id '${id}'.`, path);
111
+ ctx.ids.add(id);
112
+ switch (node.kind) {
113
+ case "agent": {
114
+ validateKnownFields(node, ["id", "source", "kind", "outputSchema", "run", "timeout", "retry"], diagnostics, path);
115
+ validateDuration(node.timeout, diagnostics, `${path}.timeout`, `Agent node '${node.id}' timeout`, "IR002");
116
+ validateAgentRun(node.run, diagnostics, `${path}.run`);
117
+ validateRetry(node.retry, diagnostics, `${path}.retry`, node.outputSchema !== undefined);
118
+ const agentKey = isRecord(node.run) && typeof node.run.agent === "string" ? node.run.agent : undefined;
119
+ if (agentKey && !ctx.agents.has(agentKey)) {
120
+ diagnostics.push({
121
+ code: "A001",
122
+ severity: "error",
123
+ message: `Agent node '${node.id}' references undeclared agent '${agentKey}'.`,
124
+ path: `${path}.run.agent`,
125
+ hint: "Declare the agent under defineWorkflow({ agents }) and reference it with agents.<key>.",
126
+ });
127
+ }
128
+ validateSchema(node.outputSchema, diagnostics, `${path}.outputSchema`);
129
+ break;
130
+ }
131
+ case "task": {
132
+ validateKnownFields(node, ["id", "source", "kind", "run", "timeout"], diagnostics, path);
133
+ validateDuration(node.timeout, diagnostics, `${path}.timeout`, `Task node '${node.id}' timeout`, "IR002");
134
+ validateTaskRun(node.run, diagnostics, `${path}.run`);
135
+ break;
136
+ }
137
+ case "signal": {
138
+ validateKnownFields(node, ["id", "source", "kind", "outputSchema", "run", "timeout", "onTimeout"], diagnostics, path);
139
+ validateDuration(node.timeout, diagnostics, `${path}.timeout`, `Signal node '${node.id}' timeout`, "IR002");
140
+ if (node.onTimeout && node.timeout === undefined)
141
+ addError(diagnostics, "S001", `Signal node '${node.id}' onTimeout requires timeout.`, `${path}.onTimeout`);
142
+ validateSignalRun(node.run, diagnostics, `${path}.run`);
143
+ validateSignalTimeout(node.onTimeout, diagnostics, `${path}.onTimeout`, node.id);
144
+ validateSchema(node.outputSchema, diagnostics, `${path}.outputSchema`);
145
+ break;
146
+ }
147
+ case "assert": {
148
+ validateKnownFields(node, ["id", "source", "kind", "condition", "message"], diagnostics, path);
149
+ validateExpr(node.condition, diagnostics, `${path}.condition`);
150
+ if (node.message)
151
+ validateTemplate(node.message, diagnostics, `${path}.message`);
152
+ break;
153
+ }
154
+ case "if": {
155
+ validateKnownFields(node, ["id", "source", "kind", "condition", "then", "else"], diagnostics, path);
156
+ validateExpr(node.condition, diagnostics, `${path}.condition`);
157
+ validateScope(node.then, diagnostics, { ...ctx, path: `${path}.then` });
158
+ if (!node.else) {
159
+ diagnostics.push({
160
+ code: "G002",
161
+ severity: "error",
162
+ message: `If node '${node.id}' must declare else.`,
163
+ path: `${path}.else`,
164
+ hint: "Add an else branch. Use else: () => ({}) for control-only branching.",
165
+ });
166
+ }
167
+ else {
168
+ validateScope(node.else, diagnostics, { ...ctx, path: `${path}.else` });
169
+ }
170
+ break;
171
+ }
172
+ case "switch": {
173
+ validateKnownFields(node, ["id", "source", "kind", "cases", "default"], diagnostics, path);
174
+ if (!Array.isArray(node.cases)) {
175
+ addError(diagnostics, "IR002", "Switch cases must be an array.", `${path}.cases`);
176
+ }
177
+ else {
178
+ forEachDense(node.cases, diagnostics, `${path}.cases`, (c, i) => {
179
+ const casePath = `${path}.cases.${i}`;
180
+ if (!requireRecord(c, diagnostics, casePath, "IR002", "Switch case must be an object."))
181
+ return;
182
+ validateKnownFields(c, ["when", "then"], diagnostics, casePath);
183
+ validateExpr(c.when, diagnostics, `${casePath}.when`);
184
+ validateScope(c.then, diagnostics, { ...ctx, path: `${casePath}.then` });
185
+ }, { code: "IR002" });
186
+ }
187
+ if (!node.default) {
188
+ diagnostics.push({
189
+ code: "G003",
190
+ severity: "error",
191
+ message: `Switch node '${node.id}' must declare default.`,
192
+ path: `${path}.default`,
193
+ hint: "Add a default branch. Use default: () => ({}) for control-only routing.",
194
+ });
195
+ }
196
+ else {
197
+ validateScope(node.default, diagnostics, { ...ctx, path: `${path}.default` });
198
+ }
199
+ break;
200
+ }
201
+ case "parallel": {
202
+ validateKnownFields(node, ["id", "source", "kind", "branches", "strategy", "maxConcurrency"], diagnostics, path);
203
+ if (node.strategy !== "all" && node.strategy !== "race")
204
+ addError(diagnostics, "P001", `Parallel node '${node.id}' strategy must be 'all' or 'race'.`, `${path}.strategy`);
205
+ if (!isRecord(node.branches)) {
206
+ addError(diagnostics, "IR002", "Parallel branches must be an object.", `${path}.branches`);
207
+ break;
208
+ }
209
+ if (node.strategy === "race" && Object.keys(node.branches).length === 0)
210
+ addError(diagnostics, "P002", `Parallel race node '${node.id}' must declare at least one branch.`, `${path}.branches`);
211
+ for (const [name, branch] of Object.entries(node.branches)) {
212
+ const branchPath = `${path}.branches.${name}`;
213
+ if (!requireRecord(branch, diagnostics, branchPath, "IR002", "Parallel branch must be an object."))
214
+ continue;
215
+ validateKnownFields(branch, ["scope"], diagnostics, branchPath);
216
+ validateScope(branch.scope, diagnostics, { ...ctx, path: `${branchPath}.scope` });
217
+ }
218
+ break;
219
+ }
220
+ case "fanout": {
221
+ validateKnownFields(node, ["id", "source", "kind", "over", "key", "do", "maxConcurrency", "strategy", "count"], diagnostics, path);
222
+ const strategy = node.strategy;
223
+ const count = node.count;
224
+ if (strategy !== "all" && strategy !== "quorum")
225
+ addError(diagnostics, "F001", `Fanout node '${node.id}' strategy must be 'all' or 'quorum'.`, `${path}.strategy`);
226
+ if (strategy === "quorum" && (!Number.isInteger(count) || (count ?? 0) <= 0))
227
+ addError(diagnostics, "F002", `Fanout node '${node.id}' quorum count must be a positive integer.`, `${path}.count`);
228
+ if (strategy !== "quorum" && count !== undefined)
229
+ addError(diagnostics, "F003", `Fanout node '${node.id}' count is only valid with quorum strategy.`, `${path}.count`);
230
+ const validOver = validateExpr(node.over, diagnostics, `${path}.over`);
231
+ if (validOver)
232
+ validateFanoutOver(node.over, diagnostics, `${path}.over`);
233
+ if (node.key)
234
+ validateTemplate(node.key, diagnostics, `${path}.key`);
235
+ validateScope(node.do, diagnostics, { ...ctx, path: `${path}.do` });
236
+ break;
237
+ }
238
+ case "loop": {
239
+ validateKnownFields(node, ["id", "source", "kind", "initial", "maxIterations", "do", "stopWhen", "onExhausted"], diagnostics, path);
240
+ validateExpr(node.initial, diagnostics, `${path}.initial`);
241
+ validateExpr(node.maxIterations, diagnostics, `${path}.maxIterations`);
242
+ if (node.onExhausted !== undefined && node.onExhausted !== "fail" && node.onExhausted !== "returnLast")
243
+ addError(diagnostics, "L002", `Loop node '${node.id}' onExhausted must be 'fail' or 'returnLast'.`, `${path}.onExhausted`);
244
+ validateExpr(node.stopWhen, diagnostics, `${path}.stopWhen`);
245
+ validateScope(node.do, diagnostics, { ...ctx, path: `${path}.do` });
246
+ break;
247
+ }
248
+ default:
249
+ addError(diagnostics, "N001", `Unknown node kind '${String(node.kind)}'.`, `${path}.kind`);
250
+ }
251
+ }
252
+ function validateAgentRun(run, diagnostics, path) {
253
+ if (!requireRecord(run, diagnostics, path, "A003", "Agent run must be an object."))
254
+ return;
255
+ validateKnownFields(run, ["kind", "agent", "prompt", "permissionMode", "sessionKey", "cwd", "env"], diagnostics, path);
256
+ if (run.kind !== "agent_run")
257
+ addError(diagnostics, "A003", "Agent run kind must be agent_run.", `${path}.kind`);
258
+ validateRequiredNonEmptyString(run.agent, diagnostics, `${path}.agent`, "A003", "Agent run agent must be a non-empty string.");
259
+ validateTemplate(run.prompt, diagnostics, `${path}.prompt`);
260
+ validatePermissionMode(run.permissionMode, diagnostics, `${path}.permissionMode`);
261
+ if (run.sessionKey)
262
+ validateTemplate(run.sessionKey, diagnostics, `${path}.sessionKey`);
263
+ if (run.cwd)
264
+ validateExpr(run.cwd, diagnostics, `${path}.cwd`);
265
+ validateEnv(run.env, diagnostics, `${path}.env`);
266
+ }
267
+ function validatePermissionMode(value, diagnostics, path) {
268
+ validateOptionalEnum(value, ["approve-reads", "approve-all", "deny-all"], diagnostics, path, "A002", "Agent permissionMode must be approve-reads, approve-all, or deny-all.");
269
+ }
270
+ function validateAgentMode(value, diagnostics, path) {
271
+ if (value === undefined)
272
+ return;
273
+ if (typeof value !== "string" || value.length === 0) {
274
+ addError(diagnostics, "A002", "Agent agentMode must be a non-empty string.", path);
275
+ }
276
+ }
277
+ function validateRetry(value, diagnostics, path, allowed) {
278
+ if (value === undefined)
279
+ return;
280
+ if (!allowed) {
281
+ addError(diagnostics, "IR001", "Agent retry is only valid when outputSchema is declared.", path);
282
+ return;
283
+ }
284
+ if (!requireRecord(value, diagnostics, path, "IR001", "Retry must be an object."))
285
+ return;
286
+ validateKnownFields(value, ["max"], diagnostics, path);
287
+ const max = value.max;
288
+ if (max !== undefined && (typeof max !== "number" || !Number.isInteger(max) || max < 0)) {
289
+ addError(diagnostics, "IR001", "Retry max must be a non-negative integer.", `${path}.max`);
290
+ }
291
+ }
292
+ function validateDuration(value, diagnostics, path, label, code) {
293
+ if (value === undefined)
294
+ return;
295
+ if (typeof value !== "string" || !/^\d+(ms|s|m|h)?$/.test(value)) {
296
+ addError(diagnostics, code, `${label} must be a duration string like 500ms, 30s, 5m, 1h, or 1000.`, path);
297
+ }
298
+ }
299
+ function validateTaskRun(run, diagnostics, path) {
300
+ if (!requireRecord(run, diagnostics, path, "T007", "Task run must be an object."))
301
+ return;
302
+ validateKnownFields(run, ["kind", "input", "target", "cwd", "env", "execution"], diagnostics, path);
303
+ if (run.kind !== "task_run")
304
+ addError(diagnostics, "T007", "Task run kind must be task_run.", `${path}.kind`);
305
+ validateExprObject(run.input, diagnostics, `${path}.input`);
306
+ validateTaskTarget(run.target, diagnostics, `${path}.target`);
307
+ if (run.cwd)
308
+ validateExpr(run.cwd, diagnostics, `${path}.cwd`);
309
+ validateEnv(run.env, diagnostics, `${path}.env`);
310
+ validateTaskExecution(run.execution, diagnostics, `${path}.execution`);
311
+ }
312
+ function validateTaskTarget(target, diagnostics, path) {
313
+ if (!requireRecord(target, diagnostics, path, "T007", "Task run target must be an object."))
314
+ return;
315
+ if (target.kind === "inline") {
316
+ validateKnownFields(target, ["kind", "runtime", "source"], diagnostics, path);
317
+ if (target.runtime !== "node")
318
+ addError(diagnostics, "T007", "Inline task target runtime must be node.", `${path}.runtime`);
319
+ validateRequiredNonEmptyString(target.source, diagnostics, `${path}.source`, "T007", "Inline task target source must be a non-empty string.");
320
+ return;
321
+ }
322
+ if (target.kind === "module") {
323
+ validateKnownFields(target, ["kind", "runtime", "specifier", "exportName", "referrer"], diagnostics, path);
324
+ if (target.runtime !== "node")
325
+ addError(diagnostics, "T007", "Module task target runtime must be node.", `${path}.runtime`);
326
+ validateRequiredNonEmptyString(target.specifier, diagnostics, `${path}.specifier`, "T007", "Module task target specifier must be a non-empty string.");
327
+ validateRequiredNonEmptyString(target.exportName, diagnostics, `${path}.exportName`, "T007", "Module task target exportName must be a non-empty string.");
328
+ validateTaskReferrer(target.referrer, diagnostics, `${path}.referrer`);
329
+ return;
330
+ }
331
+ addError(diagnostics, "T007", "Task run target kind must be inline or module.", `${path}.kind`);
332
+ }
333
+ function validateTaskReferrer(referrer, diagnostics, path) {
334
+ if (!requireRecord(referrer, diagnostics, path, "T007", "Module task target referrer must be an object."))
335
+ return;
336
+ validateKnownFields(referrer, ["kind", "path"], diagnostics, path);
337
+ if (referrer.kind !== "workflow")
338
+ addError(diagnostics, "T007", "Module task target referrer kind must be workflow.", `${path}.kind`);
339
+ if (typeof referrer.path !== "string" || referrer.path.length === 0) {
340
+ addError(diagnostics, "T007", "Module task target referrer path must be a non-empty string.", `${path}.path`);
341
+ }
342
+ else if (referrer.path.startsWith("/") || /^[A-Za-z]:[\\/]/.test(referrer.path)) {
343
+ addError(diagnostics, "T007", "Module task target referrer path must be workspace-relative.", `${path}.path`);
344
+ }
345
+ else if (referrer.path.split(/[\\/]/).includes("..")) {
346
+ addError(diagnostics, "T007", "Module task target referrer path must stay inside the workspace.", `${path}.path`);
347
+ }
348
+ }
349
+ function validateSignalRun(run, diagnostics, path) {
350
+ if (!requireRecord(run, diagnostics, path, "S002", "Signal run must be an object."))
351
+ return;
352
+ validateKnownFields(run, ["kind", "prompt"], diagnostics, path);
353
+ if (run.kind !== "signal_run")
354
+ addError(diagnostics, "S002", "Signal run kind must be signal_run.", `${path}.kind`);
355
+ validateTemplate(run.prompt, diagnostics, `${path}.prompt`);
356
+ }
357
+ function validateSignalTimeout(onTimeout, diagnostics, path, id) {
358
+ if (!onTimeout)
359
+ return;
360
+ if (!requireRecord(onTimeout, diagnostics, path, "S001", `Signal node '${id}' onTimeout must be an object.`))
361
+ return;
362
+ validateKnownFields(onTimeout, ["action", "message"], diagnostics, path);
363
+ if (onTimeout.action !== "fail")
364
+ addError(diagnostics, "S001", `Signal node '${id}' onTimeout action must be 'fail'.`, `${path}.action`);
365
+ if (onTimeout.message !== undefined && typeof onTimeout.message !== "string") {
366
+ addError(diagnostics, "S001", `Signal node '${id}' onTimeout message must be a string.`, `${path}.message`);
367
+ }
368
+ }
369
+ function validateFanoutOver(expr, diagnostics, path) {
370
+ if (expr.kind === "literal" && !Array.isArray(expr.value)) {
371
+ addError(diagnostics, "F004", "Fanout over literal must be an array.", path);
372
+ }
373
+ if (expr.kind === "object") {
374
+ addError(diagnostics, "F004", "Fanout over object expression must be an array value.", path);
375
+ }
376
+ }
377
+ function validateExprObject(values, diagnostics, path) {
378
+ if (!values) {
379
+ addError(diagnostics, "E000", "Expression object is required.", path);
380
+ return;
381
+ }
382
+ if (!requireRecord(values, diagnostics, path, "E004", "Expression object fields must be an object."))
383
+ return;
384
+ for (const [key, expr] of Object.entries(values))
385
+ validateExpr(expr, diagnostics, `${path}.${key}`);
386
+ }
387
+ function validateEnv(values, diagnostics, path) {
388
+ if (values === undefined)
389
+ return;
390
+ if (!requireRecord(values, diagnostics, path, "E004", "Env must be an object."))
391
+ return;
392
+ for (const [key, value] of Object.entries(values)) {
393
+ if (isSecretRef(value)) {
394
+ validateSecretRef(value, diagnostics, `${path}.${key}`);
395
+ continue;
396
+ }
397
+ validateExpr(value, diagnostics, `${path}.${key}`);
398
+ }
399
+ }
400
+ function validateSecretRef(value, diagnostics, path) {
401
+ validateKnownFields(value, ["kind", "name"], diagnostics, path);
402
+ if (typeof value.name !== "string" || value.name.length === 0) {
403
+ addError(diagnostics, "SEC001", "Secret ref name must be a non-empty string.", `${path}.name`);
404
+ }
405
+ }
406
+ function validateTemplate(template, diagnostics, path) {
407
+ if (!template) {
408
+ addError(diagnostics, "TM001", "Template is required.", path);
409
+ return false;
410
+ }
411
+ if (!isRecord(template) || template.kind !== "template" || !Array.isArray(template.parts)) {
412
+ addError(diagnostics, "TM001", "Template must be a template object with parts.", path);
413
+ return false;
414
+ }
415
+ validateKnownFields(template, ["kind", "parts"], diagnostics, path);
416
+ forEachDense(template.parts, diagnostics, `${path}.parts`, (part, index) => {
417
+ const partPath = `${path}.parts.${index}`;
418
+ if (!requireRecord(part, diagnostics, partPath, "TM002", "Template part must be an object."))
419
+ return;
420
+ if (part.kind === "text") {
421
+ validateKnownFields(part, ["kind", "value"], diagnostics, partPath);
422
+ if (typeof part.value !== "string")
423
+ addError(diagnostics, "TM002", "Template text value must be a string.", `${partPath}.value`);
424
+ return;
425
+ }
426
+ if (part.kind === "expr") {
427
+ validateKnownFields(part, ["kind", "expr"], diagnostics, partPath);
428
+ validateExpr(part.expr, diagnostics, `${partPath}.expr`);
429
+ return;
430
+ }
431
+ addError(diagnostics, "TM002", `Unknown template part kind '${String(part.kind)}'.`, `${partPath}.kind`);
432
+ }, { code: "TM002" });
433
+ return true;
434
+ }
435
+ function validateTaskExecution(execution, diagnostics, path) {
436
+ if (!execution)
437
+ return;
438
+ if (!requireRecord(execution, diagnostics, path, "T006", "Task execution must be an object."))
439
+ return;
440
+ validateKnownFields(execution, ["shell", "defaultCommandTimeout", "commandRunner"], diagnostics, path);
441
+ validateOptionalEnum(execution.shell, ["bash", "powershell", "pwsh"], diagnostics, `${path}.shell`, "T006", "Task execution shell must be bash, powershell, or pwsh.");
442
+ validateDuration(execution.defaultCommandTimeout, diagnostics, `${path}.defaultCommandTimeout`, "Task defaultCommandTimeout", "T006");
443
+ validateOptionalEnum(execution.commandRunner, ["acpus-zx-core", "custom"], diagnostics, `${path}.commandRunner`, "T006", "Task commandRunner must be acpus-zx-core or custom.");
444
+ }
445
+ function validateExpr(expr, diagnostics, path) {
446
+ if (expr === undefined || expr === null) {
447
+ addError(diagnostics, "E000", "Expression is required.", path);
448
+ return false;
449
+ }
450
+ if (!isRecord(expr) || typeof expr.kind !== "string") {
451
+ addError(diagnostics, "E004", "Expression must be an object with kind.", path);
452
+ return false;
453
+ }
454
+ if (expr.kind === "literal" && !Object.prototype.hasOwnProperty.call(expr, "value")) {
455
+ addError(diagnostics, "E003", "Expression literal must include value.", `${path}.value`);
456
+ return true;
457
+ }
458
+ const issues = validateExprIR(expr);
459
+ diagnostics.push(...issues.map(issue => expressionDiagnostic(issue, path)));
460
+ return issues.length === 0;
461
+ }
462
+ function expressionDiagnostic(issue, basePath) {
463
+ return {
464
+ code: expressionCode(issue.code),
465
+ severity: issue.severity,
466
+ message: issue.message,
467
+ path: expressionPath(basePath, issue.path),
468
+ };
469
+ }
470
+ function expressionCode(code) {
471
+ if (code === "EX006")
472
+ return "E001";
473
+ if (code === "EX001" || code === "EX003")
474
+ return "E002";
475
+ return "E004";
476
+ }
477
+ function expressionPath(basePath, path) {
478
+ if (!path || path === "$")
479
+ return basePath;
480
+ return `${basePath}${path.slice(1).replaceAll("[", ".").replaceAll("]", "")}`;
481
+ }
482
+ function validateSchema(schema, diagnostics, path) {
483
+ if (!schema)
484
+ return;
485
+ if (!isRecord(schema) || typeof schema.kind !== "string") {
486
+ addError(diagnostics, "SC002", "Schema must be an object with kind.", path);
487
+ return;
488
+ }
489
+ validateSchemaMetadata(schema, diagnostics, path);
490
+ switch (schema.kind) {
491
+ case "unknown":
492
+ case "string":
493
+ case "number":
494
+ case "boolean":
495
+ case "null":
496
+ case "path":
497
+ validateKnownFields(schema, ["kind", "description", "default", "optional", "nullable"], diagnostics, path);
498
+ break;
499
+ case "literal":
500
+ validateKnownFields(schema, ["kind", "value", "description", "default", "optional", "nullable"], diagnostics, path);
501
+ if (!Object.prototype.hasOwnProperty.call(schema, "value") || !isJsonPrimitive(schema.value))
502
+ addError(diagnostics, "SC002", "Literal schema value must be a JSON primitive.", `${path}.value`);
503
+ break;
504
+ case "enum":
505
+ validateKnownFields(schema, ["kind", "values", "description", "default", "optional", "nullable"], diagnostics, path);
506
+ if (!Array.isArray(schema.values)) {
507
+ addError(diagnostics, "SC002", "Enum schema values must be an array.", `${path}.values`);
508
+ }
509
+ else {
510
+ forEachDense(schema.values, diagnostics, `${path}.values`, (value, index) => {
511
+ if (!isJsonPrimitive(value))
512
+ addError(diagnostics, "SC002", "Enum schema values must be JSON primitives.", `${path}.values.${index}`);
513
+ });
514
+ }
515
+ break;
516
+ case "object": {
517
+ validateKnownFields(schema, ["kind", "fields", "required", "additionalProperties", "description", "default", "optional", "nullable"], diagnostics, path);
518
+ if (!isRecord(schema.fields))
519
+ addError(diagnostics, "SC002", "Object schema fields must be an object.", `${path}.fields`);
520
+ const fields = isRecord(schema.fields) ? schema.fields : {};
521
+ const required = Array.isArray(schema.required) ? schema.required : [];
522
+ if (!Array.isArray(schema.required))
523
+ addError(diagnostics, "SC002", "Object schema required must be an array.", `${path}.required`);
524
+ else
525
+ forEachDense(required, diagnostics, `${path}.required`, (req, index) => {
526
+ if (typeof req !== "string" || !(req in fields))
527
+ addError(diagnostics, "SC001", `Required field '${String(req)}' is not present in object fields.`, `${path}.required.${index}`);
528
+ });
529
+ if (typeof schema.additionalProperties !== "boolean")
530
+ addError(diagnostics, "SC002", "Object schema additionalProperties must be a boolean.", `${path}.additionalProperties`);
531
+ for (const [key, field] of Object.entries(fields))
532
+ validateSchema(field, diagnostics, `${path}.fields.${key}`);
533
+ break;
534
+ }
535
+ case "array":
536
+ validateKnownFields(schema, ["kind", "item", "description", "default", "optional", "nullable"], diagnostics, path);
537
+ validateRequiredNestedSchema(schema.item, diagnostics, `${path}.item`);
538
+ break;
539
+ case "record":
540
+ validateKnownFields(schema, ["kind", "value", "description", "default", "optional", "nullable"], diagnostics, path);
541
+ validateRequiredNestedSchema(schema.value, diagnostics, `${path}.value`);
542
+ break;
543
+ case "union": {
544
+ validateKnownFields(schema, ["kind", "variants", "description", "default", "optional", "nullable"], diagnostics, path);
545
+ if (!Array.isArray(schema.variants)) {
546
+ addError(diagnostics, "SC002", "Union schema variants must be an array.", `${path}.variants`);
547
+ break;
548
+ }
549
+ forEachDense(schema.variants, diagnostics, `${path}.variants`, (variant, index) => validateSchema(variant, diagnostics, `${path}.variants.${index}`));
550
+ break;
551
+ }
552
+ default:
553
+ addError(diagnostics, "SC002", `Unknown schema kind '${schema.kind}'.`, `${path}.kind`);
554
+ }
555
+ }
556
+ function validateSchemaMetadata(schema, diagnostics, path) {
557
+ if (schema.description !== undefined && typeof schema.description !== "string")
558
+ addError(diagnostics, "SC002", "Schema description must be a string.", `${path}.description`);
559
+ if (schema.optional !== undefined && typeof schema.optional !== "boolean")
560
+ addError(diagnostics, "SC002", "Schema optional must be a boolean.", `${path}.optional`);
561
+ if (schema.nullable !== undefined && typeof schema.nullable !== "boolean")
562
+ addError(diagnostics, "SC002", "Schema nullable must be a boolean.", `${path}.nullable`);
563
+ if (schema.default !== undefined && !isJsonValue(schema.default))
564
+ addError(diagnostics, "SC002", "Schema default must be JSON-compatible.", `${path}.default`);
565
+ }
566
+ function validateRequiredNestedSchema(schema, diagnostics, path) {
567
+ if (!schema) {
568
+ addError(diagnostics, "SC002", "Nested schema is required.", path);
569
+ return;
570
+ }
571
+ validateSchema(schema, diagnostics, path);
572
+ }
573
+ function validateRequiredSchema(schema, diagnostics, path) {
574
+ if (!schema) {
575
+ addError(diagnostics, "SC000", "Schema is required.", path);
576
+ return;
577
+ }
578
+ validateSchema(schema, diagnostics, path);
579
+ }
580
+ function addError(diagnostics, code, message, path) {
581
+ diagnostics.push({ code, severity: "error", message, ...(path === undefined ? {} : { path }) });
582
+ }
583
+ function addWarning(diagnostics, code, message, path) {
584
+ diagnostics.push({ code, severity: "warning", message, ...(path === undefined ? {} : { path }) });
585
+ }
586
+ function requireRecord(value, diagnostics, path, code, message) {
587
+ if (isRecord(value))
588
+ return true;
589
+ addError(diagnostics, code, message, path);
590
+ return false;
591
+ }
592
+ function requireArray(value, diagnostics, path, code, message) {
593
+ if (Array.isArray(value))
594
+ return value;
595
+ addError(diagnostics, code, message, path);
596
+ return undefined;
597
+ }
598
+ function validateRequiredNonEmptyString(value, diagnostics, path, code, message) {
599
+ if (typeof value !== "string" || value.length === 0)
600
+ addError(diagnostics, code, message, path);
601
+ }
602
+ function validateRequiredEnum(value, allowed, diagnostics, path, code, message) {
603
+ if (!allowed.includes(value))
604
+ addError(diagnostics, code, message, path);
605
+ }
606
+ function validateOptionalEnum(value, allowed, diagnostics, path, code, message) {
607
+ if (value !== undefined)
608
+ validateRequiredEnum(value, allowed, diagnostics, path, code, message);
609
+ }
610
+ function validateKnownFields(value, allowed, diagnostics, path) {
611
+ if (!isRecord(value))
612
+ return;
613
+ const allowedSet = new Set(allowed);
614
+ for (const key of Object.keys(value)) {
615
+ if (!allowedSet.has(key)) {
616
+ diagnostics.push({
617
+ code: "IR001",
618
+ severity: "error",
619
+ message: `Unexpected field '${key}'.`,
620
+ path: path ? `${path}.${key}` : key,
621
+ });
622
+ }
623
+ }
624
+ }
625
+ function forEachDense(items, diagnostics, path, run, options = {}) {
626
+ for (let index = 0; index < items.length; index++) {
627
+ if (!Object.prototype.hasOwnProperty.call(items, index)) {
628
+ diagnostics.push({ code: options.code ?? "SC002", severity: "error", message: options.message ?? "Array values must not contain sparse holes.", path: `${path}.${index}` });
629
+ continue;
630
+ }
631
+ run(items[index], index);
632
+ }
633
+ }
634
+ function isSecretRef(value) {
635
+ return isRecord(value) && value.kind === "secret";
636
+ }
637
+ function isRecord(value) {
638
+ return Boolean(value && typeof value === "object" && !Array.isArray(value));
639
+ }
640
+ function isJsonPrimitive(value) {
641
+ return value === null || typeof value === "string" || typeof value === "number" || typeof value === "boolean";
642
+ }
643
+ function isJsonValue(value, seen = new Set()) {
644
+ if (isJsonPrimitive(value))
645
+ return true;
646
+ if (Array.isArray(value)) {
647
+ if (seen.has(value))
648
+ return false;
649
+ seen.add(value);
650
+ for (let index = 0; index < value.length; index++) {
651
+ if (!Object.prototype.hasOwnProperty.call(value, index) || !isJsonValue(value[index], seen))
652
+ return false;
653
+ }
654
+ seen.delete(value);
655
+ return true;
656
+ }
657
+ if (!isRecord(value))
658
+ return false;
659
+ const prototype = Object.getPrototypeOf(value);
660
+ if (prototype !== Object.prototype && prototype !== null)
661
+ return false;
662
+ if (seen.has(value))
663
+ return false;
664
+ seen.add(value);
665
+ for (const item of Object.values(value))
666
+ if (!isJsonValue(item, seen))
667
+ return false;
668
+ seen.delete(value);
669
+ return true;
670
+ }
671
+ //# sourceMappingURL=validator.js.map