@xxrandr/conecta-diagram 0.0.0-stage → 0.1.4

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (41) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +319 -2
  3. package/dist/chunk-3QGSCPWL.cjs +663 -0
  4. package/dist/chunk-43VYPB4U.js +2136 -0
  5. package/dist/chunk-6ADTOXBT.cjs +626 -0
  6. package/dist/chunk-B2RLLFGC.js +0 -0
  7. package/dist/chunk-GXOBW2BK.js +290 -0
  8. package/dist/chunk-HZER5BPR.js +1122 -0
  9. package/dist/chunk-IT5H6WO2.js +626 -0
  10. package/dist/chunk-LKOWNTPR.cjs +1 -0
  11. package/dist/chunk-Q62JPMJH.js +663 -0
  12. package/dist/chunk-QI4NNFRQ.cjs +290 -0
  13. package/dist/chunk-VD32JNDO.cjs +2136 -0
  14. package/dist/chunk-ZMSQTBVW.cjs +1122 -0
  15. package/dist/core/index.cjs +56 -0
  16. package/dist/core/index.d.cts +65 -0
  17. package/dist/core/index.d.ts +65 -0
  18. package/dist/core/index.js +56 -0
  19. package/dist/d3/index.cjs +8 -0
  20. package/dist/d3/index.d.cts +46 -0
  21. package/dist/d3/index.d.ts +46 -0
  22. package/dist/d3/index.js +8 -0
  23. package/dist/index.cjs +71 -0
  24. package/dist/index.d.cts +5 -0
  25. package/dist/index.d.ts +5 -0
  26. package/dist/index.js +71 -0
  27. package/dist/photon.css +339 -0
  28. package/dist/runner-display-BkBJpBY-.d.ts +151 -0
  29. package/dist/runner-display-DHHtavSX.d.cts +151 -0
  30. package/dist/store-CtOInpPu.d.cts +242 -0
  31. package/dist/store-CtOInpPu.d.ts +242 -0
  32. package/dist/svg/index.cjs +15 -0
  33. package/dist/svg/index.d.cts +172 -0
  34. package/dist/svg/index.d.ts +172 -0
  35. package/dist/svg/index.js +15 -0
  36. package/docs/devtools-refactor.md +93 -0
  37. package/docs/java-runtime-integration.md +268 -0
  38. package/docs/photon-template.html +547 -0
  39. package/docs/presentation-architecture.md +30 -0
  40. package/docs/runner-display.md +109 -0
  41. package/package.json +130 -4
@@ -0,0 +1,663 @@
1
+ "use strict";Object.defineProperty(exports, "__esModule", {value: true}); function _nullishCoalesce(lhs, rhsFn) { if (lhs != null) { return lhs; } else { return rhsFn(); } } function _optionalChain(ops) { let lastAccessLHS = undefined; let value = ops[0]; let i = 1; while (i < ops.length) { const op = ops[i]; const fn = ops[i + 1]; i += 2; if ((op === 'optionalAccess' || op === 'optionalCall') && value == null) { return undefined; } if (op === 'access' || op === 'optionalAccess') { lastAccessLHS = value; value = fn(value); } else if (op === 'call' || op === 'optionalCall') { value = fn((...args) => value.call(lastAccessLHS, ...args)); lastAccessLHS = undefined; } } return value; }// src/core/types.ts
2
+ var FlowError = class extends Error {
3
+ constructor(code, message, diagnostics = []) {
4
+ super(message);
5
+ this.code = code;
6
+ this.diagnostics = diagnostics;
7
+ this.name = "FlowError";
8
+ }
9
+
10
+
11
+ };
12
+ function createFlow(id) {
13
+ return { schemaVersion: 1, id, nodes: [], edges: [] };
14
+ }
15
+
16
+ // src/core/actions.ts
17
+ var object = (value) => !!value && typeof value === "object" && !Array.isArray(value);
18
+ var strings = (value) => Array.isArray(value) && value.every((item) => typeof item === "string");
19
+ var isCodePreview = (value) => object(value) && typeof value.language === "string" && typeof value.source === "string";
20
+ function isTaskAction(value) {
21
+ if (!object(value) || typeof value.type !== "string" || !value.type.trim() || !object(value.body))
22
+ return false;
23
+ if (value.version !== void 0 && (!Number.isSafeInteger(value.version) || value.version < 0))
24
+ return false;
25
+ if ([value.streaming, value.expectsResult].some(
26
+ (item) => item !== void 0 && typeof item !== "boolean"
27
+ ))
28
+ return false;
29
+ const body = value.body;
30
+ const fields = {
31
+ api: ["method", "url"],
32
+ database: ["databaseType", "datasource", "operation", "statement"],
33
+ file: ["operation", "path"],
34
+ process: ["profile"],
35
+ ssh: ["connectionId", "command"],
36
+ sftp: ["connectionId", "operation", "remotePath"],
37
+ mail: ["connectionId", "operation"],
38
+ messaging: ["connectionId", "operation", "topic"],
39
+ event: ["operation", "topic"],
40
+ cancel: ["targetCommandId"],
41
+ generic: ["name"]
42
+ };
43
+ if (typeof body.kind !== "string" || !Object.hasOwn(fields, body.kind) || !fields[body.kind].every((key) => typeof body[key] === "string"))
44
+ return false;
45
+ const family = value.type.split(".")[0];
46
+ const expected = family === "command" ? value.type === "command.cancel" ? "cancel" : "generic" : [
47
+ "api",
48
+ "database",
49
+ "file",
50
+ "process",
51
+ "ssh",
52
+ "sftp",
53
+ "mail",
54
+ "messaging",
55
+ "event"
56
+ ].includes(family) ? family : "generic";
57
+ if (body.kind !== expected) return false;
58
+ if (body.code !== void 0 && !isCodePreview(body.code)) return false;
59
+ if (body.arguments !== void 0 && !strings(body.arguments)) return false;
60
+ if (body.to !== void 0 && !strings(body.to)) return false;
61
+ if (body.resumable !== void 0 && typeof body.resumable !== "boolean")
62
+ return false;
63
+ return [
64
+ "connectionId",
65
+ "bodyPreview",
66
+ "destination",
67
+ "transferId",
68
+ "localPath",
69
+ "subject",
70
+ "path",
71
+ "reason",
72
+ "preview"
73
+ ].every((key) => body[key] === void 0 || typeof body[key] === "string");
74
+ }
75
+ function capabilityState(type, advertised) {
76
+ if (advertised)
77
+ return advertised.includes(type) ? "available" : "unsupported";
78
+ return ["database.transaction", "database.metadata"].includes(type) ? "unsupported" : "unverified";
79
+ }
80
+ function displayUrl(value) {
81
+ try {
82
+ const url = new URL(value);
83
+ url.username = "";
84
+ url.password = "";
85
+ url.hash = "";
86
+ for (const key of [...url.searchParams.keys()])
87
+ url.searchParams.set(key, "[redacted]");
88
+ return url.toString();
89
+ } catch (e) {
90
+ return value.replace(/([?&][^=&#]+)=([^&#]*)/g, "$1=[redacted]").split("#")[0];
91
+ }
92
+ }
93
+ function actionPresentation(action) {
94
+ const body = action.body;
95
+ switch (body.kind) {
96
+ case "api":
97
+ return {
98
+ title: `HTTP \xB7 ${body.method}`,
99
+ lines: [
100
+ displayUrl(body.url),
101
+ body.connectionId ? `Connection: ${body.connectionId}` : "Logical API target",
102
+ _nullishCoalesce(body.bodyPreview, () => ( ""))
103
+ ]
104
+ };
105
+ case "database":
106
+ return {
107
+ title: `${body.databaseType} \xB7 ${body.operation}`,
108
+ lines: [
109
+ `Datasource: ${body.datasource}`,
110
+ ...body.statement.split(/\r?\n/)
111
+ ],
112
+ code: { language: "sql", source: body.statement }
113
+ };
114
+ case "file":
115
+ return {
116
+ title: `File \xB7 ${body.operation}`,
117
+ lines: [
118
+ body.path,
119
+ body.destination ? `\u2192 ${body.destination}` : body.transferId ? `Transfer: ${body.transferId}` : "",
120
+ body.resumable ? "Resumable transfer" : ""
121
+ ]
122
+ };
123
+ case "process":
124
+ return {
125
+ title: body.code ? `${body.code.language} \xB7 process` : "Process",
126
+ lines: [
127
+ `Profile: ${body.profile}`,
128
+ ..._nullishCoalesce(_optionalChain([body, 'access', _ => _.code, 'optionalAccess', _2 => _2.source, 'access', _3 => _3.split, 'call', _4 => _4(/\r?\n/)]), () => ( [
129
+ _nullishCoalesce(_optionalChain([body, 'access', _5 => _5.arguments, 'optionalAccess', _6 => _6.join, 'call', _7 => _7(" ")]), () => ( ""))
130
+ ]))
131
+ ],
132
+ ...body.code ? { code: body.code } : {}
133
+ };
134
+ case "ssh":
135
+ return {
136
+ title: "SSH \xB7 exec",
137
+ lines: [`Connection: ${body.connectionId}`, body.command],
138
+ code: { language: "shell", source: body.command }
139
+ };
140
+ case "sftp":
141
+ return {
142
+ title: `SFTP \xB7 ${body.operation}`,
143
+ lines: [
144
+ `Connection: ${body.connectionId}`,
145
+ body.remotePath,
146
+ _nullishCoalesce(_nullishCoalesce(body.localPath, () => ( body.destination)), () => ( ""))
147
+ ]
148
+ };
149
+ case "mail":
150
+ return {
151
+ title: `Mail \xB7 ${body.operation}`,
152
+ lines: [
153
+ _nullishCoalesce(body.subject, () => ( "")),
154
+ _nullishCoalesce(_optionalChain([body, 'access', _8 => _8.to, 'optionalAccess', _9 => _9.join, 'call', _10 => _10(", ")]), () => ( "")),
155
+ `Connection: ${body.connectionId}`
156
+ ]
157
+ };
158
+ case "messaging":
159
+ return {
160
+ title: `Messaging \xB7 ${body.operation}`,
161
+ lines: [body.topic, `Connection: ${body.connectionId}`]
162
+ };
163
+ case "event":
164
+ return {
165
+ title: `Event \xB7 ${body.operation}`,
166
+ lines: [body.topic, _nullishCoalesce(body.path, () => ( ""))]
167
+ };
168
+ case "cancel":
169
+ return {
170
+ title: "Command \xB7 cancel",
171
+ lines: [`Target: ${body.targetCommandId}`, _nullishCoalesce(body.reason, () => ( ""))]
172
+ };
173
+ case "generic":
174
+ return { title: "Extension", lines: [body.name, _nullishCoalesce(body.preview, () => ( ""))] };
175
+ }
176
+ }
177
+ function formatCondition(condition2) {
178
+ if (condition2.op === "all" || condition2.op === "any")
179
+ return `(${condition2.conditions.map(formatCondition).join(condition2.op === "all" ? " AND " : " OR ")})`;
180
+ if (condition2.op === "not")
181
+ return `NOT ${formatCondition(condition2.condition)}`;
182
+ if (!("path" in condition2)) return "";
183
+ const path = condition2.path.join(".") || "$context";
184
+ if (condition2.op === "exists") return `exists(${path})`;
185
+ const operators = {
186
+ eq: "==",
187
+ neq: "!=",
188
+ gt: ">",
189
+ gte: ">=",
190
+ lt: "<",
191
+ lte: "<=",
192
+ in: "IN"
193
+ };
194
+ return `${path} ${operators[condition2.op]} ${JSON.stringify(condition2.value)}`;
195
+ }
196
+
197
+ // src/core/json.ts
198
+ function assertJson(value, ancestors = /* @__PURE__ */ new Set(), depth = 0) {
199
+ if (depth > 128)
200
+ throw new FlowError("INVALID_JSON", "JSON nesting exceeds 128 levels.");
201
+ if (value === null || typeof value === "string" || typeof value === "boolean")
202
+ return;
203
+ if (typeof value === "number" && Number.isFinite(value)) return;
204
+ if (typeof value !== "object")
205
+ throw new FlowError(
206
+ "INVALID_JSON",
207
+ "Expected finite, serializable JSON data."
208
+ );
209
+ if (ancestors.has(value))
210
+ throw new FlowError("INVALID_JSON", "Circular data is not supported.");
211
+ if (!Array.isArray(value) && Object.getPrototypeOf(value) !== Object.prototype && Object.getPrototypeOf(value) !== null) {
212
+ throw new FlowError(
213
+ "INVALID_JSON",
214
+ "Only plain JSON objects are supported."
215
+ );
216
+ }
217
+ if (Object.getOwnPropertySymbols(value).length)
218
+ throw new FlowError("INVALID_JSON", "Symbol properties are not supported.");
219
+ ancestors.add(value);
220
+ for (const [key, descriptor] of Object.entries(
221
+ Object.getOwnPropertyDescriptors(value)
222
+ )) {
223
+ if (!descriptor.enumerable && !(Array.isArray(value) && key === "length"))
224
+ throw new FlowError(
225
+ "INVALID_JSON",
226
+ "Non-enumerable properties are not supported."
227
+ );
228
+ if (!("value" in descriptor))
229
+ throw new FlowError("INVALID_JSON", "Accessors are not supported.");
230
+ assertJson(descriptor.value, ancestors, depth + 1);
231
+ }
232
+ if (Array.isArray(value) && Object.keys(value).length !== value.length) {
233
+ throw new FlowError(
234
+ "INVALID_JSON",
235
+ "Sparse or extended arrays are not supported."
236
+ );
237
+ }
238
+ ancestors.delete(value);
239
+ }
240
+ function clone(value) {
241
+ return JSON.parse(JSON.stringify(value));
242
+ }
243
+ function freeze(value) {
244
+ if (value && typeof value === "object" && !Object.isFrozen(value)) {
245
+ Object.freeze(value);
246
+ for (const child of Object.values(value)) freeze(child);
247
+ }
248
+ return value;
249
+ }
250
+ function equal(a, b) {
251
+ if (a === b) return true;
252
+ if (!a || !b || typeof a !== "object" || typeof b !== "object") return false;
253
+ if (Array.isArray(a) !== Array.isArray(b)) return false;
254
+ const keys = Object.keys(a);
255
+ return keys.length === Object.keys(b).length && keys.every(
256
+ (key) => Object.hasOwn(b, key) && equal(
257
+ a[key],
258
+ b[key]
259
+ )
260
+ );
261
+ }
262
+
263
+ // src/core/validation.ts
264
+ var record = (value) => value !== null && typeof value === "object" && !Array.isArray(value);
265
+ var text = (value) => typeof value === "string" && value.trim().length > 0;
266
+ var finite = (value) => typeof value === "number" && Number.isFinite(value);
267
+ function condition(value) {
268
+ if (!record(value)) return false;
269
+ if (value.op === "all" || value.op === "any")
270
+ return Array.isArray(value.conditions) && value.conditions.every(condition);
271
+ if (value.op === "not") return condition(value.condition);
272
+ if (!Array.isArray(value.path) || !value.path.every((key) => typeof key === "string"))
273
+ return false;
274
+ if (value.op === "exists") return true;
275
+ if (!["eq", "neq", "gt", "gte", "lt", "lte", "in"].includes(String(value.op)) || !Object.hasOwn(value, "value"))
276
+ return false;
277
+ if (value.op === "in") return Array.isArray(value.value);
278
+ if (["gt", "gte", "lt", "lte"].includes(String(value.op)))
279
+ return finite(value.value);
280
+ return true;
281
+ }
282
+ function validateFlow(value, options = {}) {
283
+ const issues = [];
284
+ const add = (code, message, path, severity = "error") => issues.push({ code, message, path, severity });
285
+ try {
286
+ assertJson(value);
287
+ } catch (error) {
288
+ add(
289
+ "INVALID_JSON",
290
+ error instanceof Error ? error.message : "Invalid JSON.",
291
+ ""
292
+ );
293
+ return issues;
294
+ }
295
+ if (!record(value) || value.schemaVersion !== 1 || !text(value.id) || !Array.isArray(value.nodes) || !Array.isArray(value.edges)) {
296
+ add(
297
+ "INVALID_DOCUMENT",
298
+ "Expected schemaVersion 1, a nonempty id, nodes and edges arrays.",
299
+ ""
300
+ );
301
+ return issues;
302
+ }
303
+ if (value.data !== void 0 && !record(value.data))
304
+ add("INVALID_DATA", "data must be an object.", "data");
305
+ if (value.globals !== void 0) {
306
+ if (!Array.isArray(value.globals))
307
+ add("INVALID_GLOBAL", "globals must be an array.", "globals");
308
+ else {
309
+ const ids = /* @__PURE__ */ new Set(), names = /* @__PURE__ */ new Set();
310
+ for (const [index, variable] of value.globals.entries()) {
311
+ const path = `globals[${index}]`;
312
+ if (!record(variable) || !text(variable.id) || !text(variable.name) || !["array", "boolean", "integer", "json"].includes(
313
+ String(variable.type)
314
+ ) || !Object.hasOwn(variable, "value")) {
315
+ add(
316
+ "INVALID_GLOBAL",
317
+ "Expected a global id, name, explicit type and JSON value.",
318
+ path
319
+ );
320
+ continue;
321
+ }
322
+ if (variable.type === "array" && !Array.isArray(variable.value) || variable.type === "boolean" && typeof variable.value !== "boolean" || variable.type === "integer" && !Number.isSafeInteger(variable.value))
323
+ add(
324
+ "GLOBAL_TYPE_MISMATCH",
325
+ "Global value does not match its type badge.",
326
+ path
327
+ );
328
+ if (ids.has(variable.id) || names.has(variable.name))
329
+ add("DUPLICATE_GLOBAL", "Global ids and names must be unique.", path);
330
+ ids.add(variable.id);
331
+ names.add(variable.name);
332
+ }
333
+ }
334
+ }
335
+ for (const [index, node] of value.nodes.entries()) {
336
+ const path = `nodes[${index}]`;
337
+ if (!record(node) || !text(node.id) || typeof node.label !== "string" || !["start", "end", "task", "wait", "decision", "fork", "join"].includes(
338
+ String(node.type)
339
+ ) || !record(node.position) || !finite(node.position.x) || !finite(node.position.y)) {
340
+ add(
341
+ "INVALID_NODE",
342
+ "Expected node id, type, label and finite position.",
343
+ path
344
+ );
345
+ continue;
346
+ }
347
+ if (node.type === "task" && (node.mode !== void 0 && !["sync", "async"].includes(String(node.mode)) || node.handler !== void 0 && !text(node.handler)))
348
+ add("INVALID_TASK", "Invalid task mode or handler.", path);
349
+ if (node.type === "wait" && !text(node.signal))
350
+ add("INVALID_WAIT", "Wait nodes require a signal name.", path);
351
+ if (node.action !== void 0 && (!["task", "decision"].includes(String(node.type)) || !isTaskAction(node.action)))
352
+ add(
353
+ "INVALID_ACTION",
354
+ "Expected a display-only action with a matching command family and valid preview fields.",
355
+ `${path}.action`
356
+ );
357
+ if (node.decision !== void 0 && (node.type !== "decision" || !record(node.decision) || node.decision.description !== void 0 && typeof node.decision.description !== "string" || node.decision.expression !== void 0 && !isCodePreview(node.decision.expression)))
358
+ add(
359
+ "INVALID_DECISION_DISPLAY",
360
+ "Invalid decision display description or expression preview.",
361
+ `${path}.decision`
362
+ );
363
+ if (node.signals !== void 0 && (!record(node.signals) || ![node.signals.inputs, node.signals.outputs].every(
364
+ (items) => Array.isArray(items) && items.every(text)
365
+ )))
366
+ add(
367
+ "INVALID_SIGNALS",
368
+ "Signal inputs/outputs must be arrays of nonempty names.",
369
+ `${path}.signals`
370
+ );
371
+ if (node.type === "fork" && !text(node.join))
372
+ add("INVALID_FORK", "Fork nodes require a paired join id.", path);
373
+ if (node.type === "join" && !text(node.fork))
374
+ add("INVALID_JOIN", "Join nodes require a paired fork id.", path);
375
+ if (node.data !== void 0 && !record(node.data))
376
+ add("INVALID_DATA", "data must be an object.", `${path}.data`);
377
+ if (node.visibleWhen !== void 0 && !condition(node.visibleWhen))
378
+ add(
379
+ "INVALID_CONDITION",
380
+ "Invalid visibility condition.",
381
+ `${path}.visibleWhen`
382
+ );
383
+ }
384
+ for (const [index, edge] of value.edges.entries()) {
385
+ const path = `edges[${index}]`;
386
+ if (!record(edge) || !text(edge.id) || !text(edge.source) || !text(edge.target)) {
387
+ add("INVALID_EDGE", "Expected edge id, source and target.", path);
388
+ continue;
389
+ }
390
+ if (edge.label !== void 0 && typeof edge.label !== "string")
391
+ add("INVALID_EDGE", "Edge label must be text.", path);
392
+ if (edge.condition !== void 0 && !condition(edge.condition))
393
+ add(
394
+ "INVALID_CONDITION",
395
+ "Invalid routing condition.",
396
+ `${path}.condition`
397
+ );
398
+ if (edge.visibleWhen !== void 0 && !condition(edge.visibleWhen))
399
+ add(
400
+ "INVALID_CONDITION",
401
+ "Invalid visibility condition.",
402
+ `${path}.visibleWhen`
403
+ );
404
+ if (edge.default !== void 0 && typeof edge.default !== "boolean")
405
+ add("INVALID_EDGE", "default must be boolean.", path);
406
+ if (edge.priority !== void 0 && !Number.isSafeInteger(edge.priority))
407
+ add("INVALID_EDGE", "priority must be a safe integer.", path);
408
+ if (edge.data !== void 0 && !record(edge.data))
409
+ add("INVALID_DATA", "data must be an object.", `${path}.data`);
410
+ }
411
+ if (issues.length) return issues;
412
+ const document = value;
413
+ const nodes = new Map(document.nodes.map((node) => [node.id, node]));
414
+ const edgeIds = /* @__PURE__ */ new Set();
415
+ if (nodes.size !== document.nodes.length)
416
+ add("DUPLICATE_NODE", "Node ids must be unique.", "nodes");
417
+ for (const edge of document.edges) {
418
+ if (edgeIds.has(edge.id))
419
+ add("DUPLICATE_EDGE", `Duplicate edge ${edge.id}.`, "edges");
420
+ edgeIds.add(edge.id);
421
+ if (!nodes.has(edge.source) || !nodes.has(edge.target))
422
+ add(
423
+ "DANGLING_EDGE",
424
+ `Edge ${edge.id} references a missing node.`,
425
+ "edges"
426
+ );
427
+ }
428
+ if (document.creationOrder !== void 0) {
429
+ const seen = /* @__PURE__ */ new Set();
430
+ if (!Array.isArray(document.creationOrder))
431
+ add(
432
+ "INVALID_CREATION_ORDER",
433
+ "creationOrder must be an array.",
434
+ "creationOrder"
435
+ );
436
+ else {
437
+ for (const reference of document.creationOrder) {
438
+ if (!record(reference) || !["node", "edge"].includes(String(reference.type)) || !text(reference.id) || !(reference.type === "node" ? nodes.has(reference.id) : edgeIds.has(reference.id))) {
439
+ add(
440
+ "INVALID_CREATION_ORDER",
441
+ "Creation order references an unknown item.",
442
+ "creationOrder"
443
+ );
444
+ continue;
445
+ }
446
+ const key = JSON.stringify([reference.type, reference.id]);
447
+ if (seen.has(key))
448
+ add(
449
+ "INVALID_CREATION_ORDER",
450
+ "Creation order contains duplicates.",
451
+ "creationOrder"
452
+ );
453
+ seen.add(key);
454
+ }
455
+ if (seen.size !== document.nodes.length + document.edges.length)
456
+ add(
457
+ "INVALID_CREATION_ORDER",
458
+ "Creation order must include every node and edge exactly once.",
459
+ "creationOrder"
460
+ );
461
+ }
462
+ }
463
+ if (issues.length || options.mode === "draft") return issues;
464
+ const incoming = new Map(
465
+ document.nodes.map((node) => [
466
+ node.id,
467
+ document.edges.filter((edge) => edge.target === node.id)
468
+ ])
469
+ );
470
+ const outgoing = new Map(
471
+ document.nodes.map((node) => [
472
+ node.id,
473
+ document.edges.filter((edge) => edge.source === node.id)
474
+ ])
475
+ );
476
+ const starts = document.nodes.filter((node) => node.type === "start");
477
+ if (starts.length !== 1)
478
+ add(
479
+ "START_COUNT",
480
+ "An executable flow requires exactly one start node.",
481
+ "nodes"
482
+ );
483
+ if (!document.nodes.some((node) => node.type === "end"))
484
+ add("END_REQUIRED", "An executable flow requires an end node.", "nodes");
485
+ for (const [index, node] of document.nodes.entries()) {
486
+ const path = `nodes[${index}]`;
487
+ const inputs = incoming.get(node.id);
488
+ const outputs = outgoing.get(node.id);
489
+ if (node.type === "start" && inputs.length)
490
+ add("START_INPUT", "Start nodes cannot have incoming edges.", path);
491
+ if (node.type === "end" && outputs.length)
492
+ add("END_OUTPUT", "End nodes cannot have outgoing edges.", path);
493
+ if (!["end", "fork", "decision"].includes(node.type) && outputs.length !== 1) {
494
+ add(
495
+ "OUTPUT_COUNT",
496
+ "This node requires exactly one outgoing edge; use a fork for parallel work.",
497
+ path
498
+ );
499
+ }
500
+ if (node.type === "decision") {
501
+ if (outputs.length < 2)
502
+ add(
503
+ "DECISION_OUTPUTS",
504
+ "Decisions require at least two branches.",
505
+ path
506
+ );
507
+ if (outputs.filter((edge) => edge.default).length !== 1)
508
+ add(
509
+ "DECISION_DEFAULT",
510
+ "Decisions require exactly one fallback branch.",
511
+ path
512
+ );
513
+ if (outputs.some(
514
+ (edge) => edge.default ? edge.condition !== void 0 : !edge.condition
515
+ )) {
516
+ add(
517
+ "DECISION_CONDITION",
518
+ "Each non-default branch needs a condition; the fallback cannot have one.",
519
+ path
520
+ );
521
+ }
522
+ } else if (outputs.some(
523
+ (edge) => edge.condition !== void 0 || edge.default !== void 0 || edge.priority !== void 0
524
+ )) {
525
+ add(
526
+ "UNEXPECTED_ROUTING",
527
+ "Conditions, defaults and priorities are only allowed on decision outputs.",
528
+ path
529
+ );
530
+ }
531
+ if (node.type === "fork") {
532
+ const join = nodes.get(node.join);
533
+ if (outputs.length < 2)
534
+ add("FORK_OUTPUTS", "Forks require at least two branches.", path);
535
+ if (_optionalChain([join, 'optionalAccess', _11 => _11.type]) !== "join" || join.fork !== node.id)
536
+ add("FORK_PAIR", "Fork/join references must be reciprocal.", path);
537
+ }
538
+ if (node.type === "join") {
539
+ const fork = nodes.get(node.fork);
540
+ if (_optionalChain([fork, 'optionalAccess', _12 => _12.type]) !== "fork" || fork.join !== node.id)
541
+ add("JOIN_PAIR", "Fork/join references must be reciprocal.", path);
542
+ }
543
+ }
544
+ if (starts.length === 1) {
545
+ const visited = /* @__PURE__ */ new Set();
546
+ const reached = /* @__PURE__ */ new Set();
547
+ const queue = [
548
+ { id: starts[0].id, stack: [] }
549
+ ];
550
+ while (queue.length) {
551
+ const item = queue.pop();
552
+ const key = JSON.stringify([item.id, item.stack]);
553
+ if (visited.has(key)) continue;
554
+ visited.add(key);
555
+ if (visited.size > 1e5) {
556
+ add(
557
+ "VALIDATION_LIMIT",
558
+ "Parallel control-flow complexity exceeds the validation limit.",
559
+ "nodes"
560
+ );
561
+ break;
562
+ }
563
+ reached.add(item.id);
564
+ const node = nodes.get(item.id);
565
+ let stack = item.stack;
566
+ if (node.type === "fork") {
567
+ if (stack.includes(node.id)) {
568
+ add(
569
+ "PARALLEL_REENTRY",
570
+ `Fork ${node.id} re-enters before joining.`,
571
+ "nodes"
572
+ );
573
+ continue;
574
+ }
575
+ stack = [...stack, node.id];
576
+ } else if (node.type === "join") {
577
+ if (stack.at(-1) !== node.fork) {
578
+ add(
579
+ "PARALLEL_STRUCTURE",
580
+ `Join ${node.id} is entered outside its matching fork.`,
581
+ "nodes"
582
+ );
583
+ continue;
584
+ }
585
+ stack = stack.slice(0, -1);
586
+ } else if (node.type === "end" && stack.length) {
587
+ add(
588
+ "PARALLEL_ESCAPE",
589
+ `End ${node.id} bypasses a required join.`,
590
+ "nodes"
591
+ );
592
+ }
593
+ for (const edge of outgoing.get(node.id))
594
+ queue.push({ id: edge.target, stack });
595
+ }
596
+ for (const node of document.nodes)
597
+ if (!reached.has(node.id))
598
+ add(
599
+ "UNREACHABLE",
600
+ `Node ${node.id} is unreachable.`,
601
+ "nodes",
602
+ "warning"
603
+ );
604
+ const canEnd = new Set(
605
+ document.nodes.filter((node) => node.type === "end").map((node) => node.id)
606
+ );
607
+ const pending = [...canEnd];
608
+ while (pending.length)
609
+ for (const edge of incoming.get(pending.pop())) {
610
+ if (!canEnd.has(edge.source)) {
611
+ canEnd.add(edge.source);
612
+ pending.push(edge.source);
613
+ }
614
+ }
615
+ for (const node of document.nodes)
616
+ if (!canEnd.has(node.id))
617
+ add("NO_END_PATH", `Node ${node.id} has no path to an end.`, "nodes");
618
+ }
619
+ return issues;
620
+ }
621
+ function assertFlow(value, mode = "draft") {
622
+ const diagnostics = validateFlow(value, { mode });
623
+ if (diagnostics.some((item) => item.severity === "error"))
624
+ throw new FlowError("INVALID_FLOW", "Flow validation failed.", diagnostics);
625
+ }
626
+ function parseFlow(json) {
627
+ let value;
628
+ try {
629
+ value = JSON.parse(json);
630
+ } catch (e2) {
631
+ throw new FlowError("INVALID_JSON", "Invalid JSON document.");
632
+ }
633
+ assertFlow(value);
634
+ return value;
635
+ }
636
+ function serializeFlow(document) {
637
+ assertFlow(document);
638
+ return JSON.stringify(document, null, 2);
639
+ }
640
+ function copyFlow(document) {
641
+ assertFlow(document);
642
+ return clone(document);
643
+ }
644
+
645
+
646
+
647
+
648
+
649
+
650
+
651
+
652
+
653
+
654
+
655
+
656
+
657
+
658
+
659
+
660
+
661
+
662
+
663
+ exports.FlowError = FlowError; exports.createFlow = createFlow; exports.assertJson = assertJson; exports.clone = clone; exports.freeze = freeze; exports.equal = equal; exports.isCodePreview = isCodePreview; exports.isTaskAction = isTaskAction; exports.capabilityState = capabilityState; exports.displayUrl = displayUrl; exports.actionPresentation = actionPresentation; exports.formatCondition = formatCondition; exports.validateFlow = validateFlow; exports.assertFlow = assertFlow; exports.parseFlow = parseFlow; exports.serializeFlow = serializeFlow; exports.copyFlow = copyFlow;