@cyoda/workflow-core 0.1.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.
package/dist/index.cjs ADDED
@@ -0,0 +1,2004 @@
1
+ "use strict";
2
+ var __defProp = Object.defineProperty;
3
+ var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
4
+ var __getOwnPropNames = Object.getOwnPropertyNames;
5
+ var __hasOwnProp = Object.prototype.hasOwnProperty;
6
+ var __export = (target, all) => {
7
+ for (var name in all)
8
+ __defProp(target, name, { get: all[name], enumerable: true });
9
+ };
10
+ var __copyProps = (to, from, except, desc) => {
11
+ if (from && typeof from === "object" || typeof from === "function") {
12
+ for (let key of __getOwnPropNames(from))
13
+ if (!__hasOwnProp.call(to, key) && key !== except)
14
+ __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
15
+ }
16
+ return to;
17
+ };
18
+ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
19
+
20
+ // src/index.ts
21
+ var index_exports = {};
22
+ __export(index_exports, {
23
+ ArrayCriterionSchema: () => ArrayCriterionSchema,
24
+ CriterionSchema: () => CriterionSchema,
25
+ ExecutionModeSchema: () => ExecutionModeSchema,
26
+ ExportPayloadSchema: () => ExportPayloadSchema,
27
+ ExternalizedProcessorSchema: () => ExternalizedProcessorSchema,
28
+ FunctionConfigSchema: () => FunctionConfigSchema,
29
+ FunctionCriterionSchema: () => FunctionCriterionSchema,
30
+ GroupCriterionSchema: () => GroupCriterionSchema,
31
+ ImportPayloadSchema: () => ImportPayloadSchema,
32
+ LifecycleCriterionSchema: () => LifecycleCriterionSchema,
33
+ NAME_REGEX: () => NAME_REGEX,
34
+ NameSchema: () => NameSchema,
35
+ OPERATOR_TYPES: () => OPERATOR_TYPES,
36
+ OperatorEnum: () => OperatorEnum,
37
+ ParseJsonError: () => ParseJsonError,
38
+ ProcessorSchema: () => ProcessorSchema,
39
+ ScheduledProcessorSchema: () => ScheduledProcessorSchema,
40
+ SchemaError: () => SchemaError,
41
+ SimpleCriterionSchema: () => SimpleCriterionSchema,
42
+ StateSchema: () => StateSchema,
43
+ TransitionSchema: () => TransitionSchema,
44
+ WorkflowApiConflictError: () => WorkflowApiConflictError,
45
+ WorkflowApiTransportError: () => WorkflowApiTransportError,
46
+ WorkflowSchema: () => WorkflowSchema,
47
+ applyPatch: () => applyPatch,
48
+ applyPatches: () => applyPatches,
49
+ assignSyntheticIds: () => assignSyntheticIds,
50
+ findMigrationPath: () => findMigrationPath,
51
+ idFor: () => idFor2,
52
+ invertPatch: () => invertPatch,
53
+ listMigrations: () => listMigrations,
54
+ lookupById: () => lookupById,
55
+ migrateSession: () => migrateSession,
56
+ mintCriterionIds: () => mintCriterionIds,
57
+ normalizeCriterion: () => normalizeCriterion,
58
+ normalizeOperatorAlias: () => normalizeOperatorAlias,
59
+ normalizeProcessor: () => normalizeProcessor,
60
+ normalizeWorkflowInput: () => normalizeWorkflowInput,
61
+ outputCriterion: () => outputCriterion,
62
+ outputFunctionConfig: () => outputFunctionConfig,
63
+ outputProcessor: () => outputProcessor,
64
+ outputTransition: () => outputTransition,
65
+ outputWorkflow: () => outputWorkflow,
66
+ parseEditorDocument: () => parseEditorDocument,
67
+ parseExportPayload: () => parseExportPayload,
68
+ parseImportPayload: () => parseImportPayload,
69
+ prettyStringify: () => prettyStringify,
70
+ registerMigration: () => registerMigration,
71
+ serializeEditorDocument: () => serializeEditorDocument,
72
+ serializeExportPayload: () => serializeExportPayload,
73
+ serializeImportPayload: () => serializeImportPayload,
74
+ validateAfterPatch: () => validateAfterPatch,
75
+ validateAll: () => validateAll,
76
+ validateExportSchema: () => validateExportSchema,
77
+ validateImportSchema: () => validateImportSchema,
78
+ validateSemantics: () => validateSemantics,
79
+ validateSession: () => validateSession,
80
+ zodErrorToIssues: () => zodErrorToIssues
81
+ });
82
+ module.exports = __toCommonJS(index_exports);
83
+
84
+ // src/types/operator.ts
85
+ var OPERATOR_TYPES = /* @__PURE__ */ new Set([
86
+ "EQUALS",
87
+ "NOT_EQUAL",
88
+ "IS_NULL",
89
+ "NOT_NULL",
90
+ "GREATER_THAN",
91
+ "LESS_THAN",
92
+ "GREATER_OR_EQUAL",
93
+ "LESS_OR_EQUAL",
94
+ "BETWEEN",
95
+ "BETWEEN_INCLUSIVE",
96
+ "CONTAINS",
97
+ "NOT_CONTAINS",
98
+ "STARTS_WITH",
99
+ "NOT_STARTS_WITH",
100
+ "ENDS_WITH",
101
+ "NOT_ENDS_WITH",
102
+ "MATCHES_PATTERN",
103
+ "LIKE",
104
+ "IEQUALS",
105
+ "INOT_EQUAL",
106
+ "ICONTAINS",
107
+ "INOT_CONTAINS",
108
+ "ISTARTS_WITH",
109
+ "INOT_STARTS_WITH",
110
+ "IENDS_WITH",
111
+ "INOT_ENDS_WITH",
112
+ "IS_UNCHANGED",
113
+ "IS_CHANGED"
114
+ ]);
115
+
116
+ // src/types/api.ts
117
+ var WorkflowApiConflictError = class extends Error {
118
+ constructor(entity, serverConcurrencyToken, message = "Workflow save conflict: server state has changed.") {
119
+ super(message);
120
+ this.entity = entity;
121
+ this.serverConcurrencyToken = serverConcurrencyToken;
122
+ }
123
+ entity;
124
+ serverConcurrencyToken;
125
+ name = "WorkflowApiConflictError";
126
+ };
127
+ var WorkflowApiTransportError = class extends Error {
128
+ constructor(cause, message = "Workflow API transport error.") {
129
+ super(message);
130
+ this.cause = cause;
131
+ }
132
+ cause;
133
+ name = "WorkflowApiTransportError";
134
+ };
135
+
136
+ // src/schema/name.ts
137
+ var import_zod = require("zod");
138
+ var NAME_REGEX = /^[A-Za-z][A-Za-z0-9_-]*$/;
139
+ var NameSchema = import_zod.z.string().regex(NAME_REGEX, "Invalid name: must start with a letter and contain only letters, digits, _ or -");
140
+
141
+ // src/schema/operator.ts
142
+ var import_zod2 = require("zod");
143
+ var OperatorEnum = import_zod2.z.enum([
144
+ "EQUALS",
145
+ "NOT_EQUAL",
146
+ "IS_NULL",
147
+ "NOT_NULL",
148
+ "GREATER_THAN",
149
+ "LESS_THAN",
150
+ "GREATER_OR_EQUAL",
151
+ "LESS_OR_EQUAL",
152
+ "BETWEEN",
153
+ "BETWEEN_INCLUSIVE",
154
+ "CONTAINS",
155
+ "NOT_CONTAINS",
156
+ "STARTS_WITH",
157
+ "NOT_STARTS_WITH",
158
+ "ENDS_WITH",
159
+ "NOT_ENDS_WITH",
160
+ "MATCHES_PATTERN",
161
+ "LIKE",
162
+ "IEQUALS",
163
+ "INOT_EQUAL",
164
+ "ICONTAINS",
165
+ "INOT_CONTAINS",
166
+ "ISTARTS_WITH",
167
+ "INOT_STARTS_WITH",
168
+ "IENDS_WITH",
169
+ "INOT_ENDS_WITH",
170
+ "IS_UNCHANGED",
171
+ "IS_CHANGED"
172
+ ]);
173
+
174
+ // src/schema/criterion.ts
175
+ var import_zod3 = require("zod");
176
+ var FunctionConfigSchema = import_zod3.z.object({
177
+ attachEntity: import_zod3.z.boolean().optional(),
178
+ calculationNodesTags: import_zod3.z.string().optional(),
179
+ responseTimeoutMs: import_zod3.z.number().int().nonnegative().optional(),
180
+ retryPolicy: import_zod3.z.string().optional(),
181
+ context: import_zod3.z.string().optional()
182
+ });
183
+ var SimpleCriterionSchema = import_zod3.z.object({
184
+ type: import_zod3.z.literal("simple"),
185
+ jsonPath: import_zod3.z.string().min(1),
186
+ operation: OperatorEnum,
187
+ value: import_zod3.z.unknown().optional()
188
+ });
189
+ var LifecycleCriterionSchema = import_zod3.z.object({
190
+ type: import_zod3.z.literal("lifecycle"),
191
+ field: import_zod3.z.enum(["state", "creationDate", "previousTransition"]),
192
+ operation: OperatorEnum,
193
+ value: import_zod3.z.unknown().optional()
194
+ });
195
+ var ArrayCriterionSchema = import_zod3.z.object({
196
+ type: import_zod3.z.literal("array"),
197
+ jsonPath: import_zod3.z.string().min(1),
198
+ operation: OperatorEnum,
199
+ value: import_zod3.z.array(import_zod3.z.string())
200
+ });
201
+ var CriterionSchema = import_zod3.z.lazy(
202
+ () => import_zod3.z.union([
203
+ SimpleCriterionSchema,
204
+ GroupCriterionSchema,
205
+ FunctionCriterionSchema,
206
+ LifecycleCriterionSchema,
207
+ ArrayCriterionSchema
208
+ ])
209
+ );
210
+ var GroupCriterionSchema = import_zod3.z.lazy(
211
+ () => import_zod3.z.object({
212
+ type: import_zod3.z.literal("group"),
213
+ operator: import_zod3.z.enum(["AND", "OR", "NOT"]),
214
+ conditions: import_zod3.z.array(CriterionSchema).min(1)
215
+ })
216
+ );
217
+ var FunctionCriterionSchema = import_zod3.z.lazy(
218
+ () => import_zod3.z.object({
219
+ type: import_zod3.z.literal("function"),
220
+ function: import_zod3.z.object({
221
+ name: NameSchema,
222
+ config: FunctionConfigSchema.optional(),
223
+ criterion: CriterionSchema.optional()
224
+ })
225
+ })
226
+ );
227
+
228
+ // src/schema/processor.ts
229
+ var import_zod4 = require("zod");
230
+ var ExecutionModeSchema = import_zod4.z.enum(["SYNC", "ASYNC_SAME_TX", "ASYNC_NEW_TX"]);
231
+ var ExternalizedProcessorSchema = import_zod4.z.object({
232
+ type: import_zod4.z.literal("externalized"),
233
+ name: NameSchema,
234
+ executionMode: ExecutionModeSchema.optional(),
235
+ config: FunctionConfigSchema.and(
236
+ import_zod4.z.object({
237
+ asyncResult: import_zod4.z.boolean().optional(),
238
+ crossoverToAsyncMs: import_zod4.z.number().int().nonnegative().optional()
239
+ })
240
+ ).optional()
241
+ });
242
+ var ScheduledProcessorSchema = import_zod4.z.object({
243
+ type: import_zod4.z.literal("scheduled"),
244
+ name: NameSchema,
245
+ config: import_zod4.z.object({
246
+ delayMs: import_zod4.z.number().int().nonnegative(),
247
+ transition: import_zod4.z.string().min(1),
248
+ timeoutMs: import_zod4.z.number().int().nonnegative().optional()
249
+ })
250
+ });
251
+ var ProcessorSchema = import_zod4.z.discriminatedUnion("type", [
252
+ ExternalizedProcessorSchema,
253
+ ScheduledProcessorSchema
254
+ ]);
255
+
256
+ // src/schema/workflow.ts
257
+ var import_zod5 = require("zod");
258
+ var TransitionSchema = import_zod5.z.object({
259
+ name: NameSchema,
260
+ next: NameSchema,
261
+ manual: import_zod5.z.boolean(),
262
+ disabled: import_zod5.z.boolean(),
263
+ criterion: CriterionSchema.optional(),
264
+ processors: import_zod5.z.array(ProcessorSchema).optional()
265
+ });
266
+ var StateSchema = import_zod5.z.object({
267
+ transitions: import_zod5.z.array(TransitionSchema)
268
+ });
269
+ var WorkflowSchema = import_zod5.z.object({
270
+ version: import_zod5.z.string().min(1),
271
+ name: NameSchema,
272
+ desc: import_zod5.z.string().optional(),
273
+ initialState: NameSchema,
274
+ active: import_zod5.z.boolean(),
275
+ criterion: CriterionSchema.optional(),
276
+ states: import_zod5.z.record(NameSchema, StateSchema).refine((s) => Object.keys(s).length > 0, "Workflow must have at least one state")
277
+ });
278
+
279
+ // src/schema/payload.ts
280
+ var import_zod6 = require("zod");
281
+ var ImportPayloadSchema = import_zod6.z.object({
282
+ importMode: import_zod6.z.enum(["MERGE", "REPLACE", "ACTIVATE"]),
283
+ workflows: import_zod6.z.array(WorkflowSchema).min(1)
284
+ });
285
+ var ExportPayloadSchema = import_zod6.z.object({
286
+ entityName: NameSchema,
287
+ modelVersion: import_zod6.z.number().int().positive(),
288
+ workflows: import_zod6.z.array(WorkflowSchema).min(1)
289
+ });
290
+
291
+ // src/parse/errors.ts
292
+ var SchemaError = class extends Error {
293
+ constructor(message, path) {
294
+ super(message);
295
+ this.path = path;
296
+ this.name = "SchemaError";
297
+ }
298
+ path;
299
+ };
300
+ var ParseJsonError = class extends Error {
301
+ constructor(message) {
302
+ super(message);
303
+ this.name = "ParseJsonError";
304
+ }
305
+ };
306
+
307
+ // src/parse/operator-alias.ts
308
+ var isObject = (v) => typeof v === "object" && v !== null && !Array.isArray(v);
309
+ function normalizeOperatorAlias(raw) {
310
+ if (Array.isArray(raw)) {
311
+ return raw.map((item) => normalizeOperatorAlias(item));
312
+ }
313
+ if (!isObject(raw)) return raw;
314
+ const result = {};
315
+ for (const [k, v] of Object.entries(raw)) {
316
+ result[k] = normalizeOperatorAlias(v);
317
+ }
318
+ const type = result["type"];
319
+ const needsAlias = type === "simple" || type === "lifecycle" || type === "array" || type === void 0;
320
+ if (needsAlias && "operatorType" in result) {
321
+ const alias = result["operatorType"];
322
+ const existing = result["operation"];
323
+ if (existing !== void 0 && existing !== alias) {
324
+ throw new SchemaError(
325
+ `Conflicting "operation" and "operatorType" values: ${JSON.stringify(existing)} vs ${JSON.stringify(alias)}`
326
+ );
327
+ }
328
+ result["operation"] = existing ?? alias;
329
+ delete result["operatorType"];
330
+ }
331
+ return result;
332
+ }
333
+
334
+ // src/identity/assign.ts
335
+ var import_uuid = require("uuid");
336
+ function emptyIds() {
337
+ return {
338
+ workflows: {},
339
+ states: {},
340
+ transitions: {},
341
+ processors: {},
342
+ criteria: {}
343
+ };
344
+ }
345
+ function indexPriorStates(prior) {
346
+ if (!prior) return {};
347
+ const out = {};
348
+ for (const [uuid, ptr] of Object.entries(prior.ids.states)) {
349
+ out[`${ptr.workflow}:${ptr.state}`] = uuid;
350
+ }
351
+ return out;
352
+ }
353
+ function indexPriorTransitions(prior) {
354
+ if (!prior) return {};
355
+ const out = {};
356
+ for (const [uuid, ptr] of Object.entries(prior.ids.transitions)) {
357
+ const key = `${ptr.workflow}:${ptr.state}`;
358
+ (out[key] ??= []).push(uuid);
359
+ }
360
+ return out;
361
+ }
362
+ function indexPriorProcessors(prior) {
363
+ if (!prior) return {};
364
+ const out = {};
365
+ for (const [uuid, ptr] of Object.entries(prior.ids.processors)) {
366
+ const key = ptr.transitionUuid;
367
+ (out[key] ??= []).push(uuid);
368
+ }
369
+ return out;
370
+ }
371
+ function assignSyntheticIds(session, prior) {
372
+ const ids = emptyIds();
373
+ const priorWorkflowIds = prior?.ids.workflows ?? {};
374
+ const priorStatesByKey = indexPriorStates(prior);
375
+ const priorTransitionsByKey = indexPriorTransitions(prior);
376
+ const priorProcessorsByTransition = indexPriorProcessors(prior);
377
+ const workflowUi = prior?.workflowUi ?? {};
378
+ for (const wf of session.workflows) {
379
+ const wfUuid = priorWorkflowIds[wf.name] ?? (0, import_uuid.v4)();
380
+ ids.workflows[wf.name] = wfUuid;
381
+ assignForWorkflow(
382
+ wf,
383
+ ids,
384
+ priorStatesByKey,
385
+ priorTransitionsByKey,
386
+ priorProcessorsByTransition
387
+ );
388
+ }
389
+ return {
390
+ revision: prior?.revision ?? 0,
391
+ ids,
392
+ workflowUi,
393
+ ...prior?.lastValidJsonHash !== void 0 ? { lastValidJsonHash: prior.lastValidJsonHash } : {}
394
+ };
395
+ }
396
+ function assignForWorkflow(wf, ids, priorStatesByKey, priorTransitionsByKey, priorProcessorsByTransition) {
397
+ for (const stateCode of Object.keys(wf.states)) {
398
+ const key = `${wf.name}:${stateCode}`;
399
+ const uuid = priorStatesByKey[key] ?? (0, import_uuid.v4)();
400
+ const ptr = { workflow: wf.name, state: stateCode };
401
+ ids.states[uuid] = ptr;
402
+ }
403
+ for (const [stateCode, state] of Object.entries(wf.states)) {
404
+ const priorTs = priorTransitionsByKey[`${wf.name}:${stateCode}`] ?? [];
405
+ state.transitions.forEach((t, idx) => {
406
+ const tUuid = priorTs[idx] ?? (0, import_uuid.v4)();
407
+ const tPtr = {
408
+ workflow: wf.name,
409
+ state: stateCode,
410
+ transitionUuid: tUuid
411
+ };
412
+ ids.transitions[tUuid] = tPtr;
413
+ if (t.processors) {
414
+ const priorPs = priorProcessorsByTransition[tUuid] ?? [];
415
+ t.processors.forEach((_p, pIdx) => {
416
+ const pUuid = priorPs[pIdx] ?? (0, import_uuid.v4)();
417
+ const pPtr = {
418
+ workflow: wf.name,
419
+ state: stateCode,
420
+ transitionUuid: tUuid,
421
+ processorUuid: pUuid
422
+ };
423
+ ids.processors[pUuid] = pPtr;
424
+ });
425
+ }
426
+ if (t.criterion) {
427
+ mintCriterionIds(
428
+ t.criterion,
429
+ {
430
+ kind: "transition",
431
+ workflow: wf.name,
432
+ state: stateCode,
433
+ transitionUuid: tUuid
434
+ },
435
+ ["criterion"],
436
+ ids
437
+ );
438
+ }
439
+ });
440
+ }
441
+ if (wf.criterion) {
442
+ mintCriterionIds(
443
+ wf.criterion,
444
+ { kind: "workflow", workflow: wf.name },
445
+ ["criterion"],
446
+ ids
447
+ );
448
+ }
449
+ }
450
+ function mintCriterionIds(c, host, path, ids) {
451
+ const uuid = (0, import_uuid.v4)();
452
+ const ptr = { host, path: [...path] };
453
+ ids.criteria[uuid] = ptr;
454
+ switch (c.type) {
455
+ case "group":
456
+ c.conditions.forEach((child, idx) => {
457
+ mintCriterionIds(child, host, [...path, "conditions", String(idx)], ids);
458
+ });
459
+ return;
460
+ case "function":
461
+ if (c.function.criterion) {
462
+ mintCriterionIds(
463
+ c.function.criterion,
464
+ host,
465
+ [...path, "function", "criterion"],
466
+ ids
467
+ );
468
+ }
469
+ return;
470
+ default:
471
+ return;
472
+ }
473
+ }
474
+
475
+ // src/normalize/input.ts
476
+ function normalizeWorkflowInput(workflow) {
477
+ const out = {
478
+ ...workflow,
479
+ name: workflow.name.trim(),
480
+ version: workflow.version.trim(),
481
+ initialState: workflow.initialState.trim(),
482
+ states: {}
483
+ };
484
+ if (workflow.desc !== void 0) {
485
+ const trimmed = workflow.desc;
486
+ if (trimmed.length === 0) {
487
+ delete out.desc;
488
+ } else {
489
+ out.desc = trimmed;
490
+ }
491
+ }
492
+ if (workflow.criterion !== void 0) {
493
+ out.criterion = normalizeCriterion(workflow.criterion);
494
+ }
495
+ for (const [code, state] of Object.entries(workflow.states)) {
496
+ const trimmedCode = code.trim();
497
+ const normTransitions = state.transitions.map((t) => {
498
+ const nt = {
499
+ ...t,
500
+ name: t.name.trim(),
501
+ next: t.next.trim()
502
+ };
503
+ if (t.criterion !== void 0) nt.criterion = normalizeCriterion(t.criterion);
504
+ if (t.processors !== void 0) {
505
+ if (t.processors.length === 0) {
506
+ delete nt.processors;
507
+ } else {
508
+ nt.processors = t.processors.map(normalizeProcessor);
509
+ }
510
+ }
511
+ return nt;
512
+ });
513
+ out.states[trimmedCode] = { transitions: normTransitions };
514
+ }
515
+ return out;
516
+ }
517
+ function normalizeCriterion(criterion) {
518
+ switch (criterion.type) {
519
+ case "simple":
520
+ return criterion;
521
+ case "group":
522
+ return { ...criterion, conditions: criterion.conditions.map(normalizeCriterion) };
523
+ case "function": {
524
+ const fn = criterion.function;
525
+ const out = {
526
+ type: "function",
527
+ function: {
528
+ name: fn.name.trim(),
529
+ ...fn.config !== void 0 ? { config: fn.config } : {},
530
+ ...fn.criterion !== void 0 ? { criterion: normalizeCriterion(fn.criterion) } : {}
531
+ }
532
+ };
533
+ return out;
534
+ }
535
+ case "lifecycle":
536
+ return criterion;
537
+ case "array":
538
+ return criterion;
539
+ }
540
+ }
541
+ function normalizeProcessor(p) {
542
+ if (p.type === "externalized") {
543
+ return { ...p, name: p.name.trim() };
544
+ }
545
+ return {
546
+ ...p,
547
+ name: p.name.trim(),
548
+ config: { ...p.config, transition: p.config.transition.trim() }
549
+ };
550
+ }
551
+
552
+ // src/validate/helpers.ts
553
+ function isValidName(name) {
554
+ return NAME_REGEX.test(name);
555
+ }
556
+ function* walkCriteria(session) {
557
+ for (const wf of session.workflows) {
558
+ if (wf.criterion) {
559
+ yield* walkInner(wf.criterion, { kind: "workflow", workflow: wf.name });
560
+ }
561
+ for (const [stateCode, state] of Object.entries(wf.states)) {
562
+ for (let i = 0; i < state.transitions.length; i++) {
563
+ const t = state.transitions[i];
564
+ if (t.criterion) {
565
+ yield* walkInner(t.criterion, {
566
+ kind: "transition",
567
+ workflow: wf.name,
568
+ state: stateCode,
569
+ transitionIndex: i,
570
+ transitionName: t.name
571
+ });
572
+ }
573
+ }
574
+ }
575
+ }
576
+ }
577
+ function* walkInner(c, where) {
578
+ yield { criterion: c, where };
579
+ if (c.type === "group") {
580
+ for (const child of c.conditions) yield* walkInner(child, where);
581
+ } else if (c.type === "function" && c.function.criterion) {
582
+ yield* walkInner(c.function.criterion, where);
583
+ }
584
+ }
585
+
586
+ // src/validate/semantic.ts
587
+ var LIFECYCLE_FIELDS = /* @__PURE__ */ new Set(["state", "creationDate", "previousTransition"]);
588
+ function validateSemantics(session, doc) {
589
+ const issues = [];
590
+ issues.push(...duplicateWorkflowNames(session));
591
+ for (const wf of session.workflows) {
592
+ issues.push(...validateWorkflow(wf, doc));
593
+ }
594
+ issues.push(...criterionRules(session));
595
+ if (session.workflows.length === 1) {
596
+ const only = session.workflows[0];
597
+ if (only && only.criterion !== void 0) {
598
+ issues.push({
599
+ severity: "info",
600
+ code: "unused-workflow-criterion",
601
+ message: "Workflow-level criterion is set but the session has only one workflow."
602
+ });
603
+ }
604
+ }
605
+ return issues;
606
+ }
607
+ function duplicateWorkflowNames(session) {
608
+ const seen = /* @__PURE__ */ new Map();
609
+ for (const wf of session.workflows) {
610
+ seen.set(wf.name, (seen.get(wf.name) ?? 0) + 1);
611
+ }
612
+ const out = [];
613
+ for (const [name, count] of seen) {
614
+ if (count > 1) {
615
+ out.push({
616
+ severity: "error",
617
+ code: "duplicate-workflow-name",
618
+ message: `Duplicate workflow name: "${name}" (appears ${count}\xD7)`,
619
+ detail: { name, count }
620
+ });
621
+ }
622
+ }
623
+ return out;
624
+ }
625
+ function validateWorkflow(wf, doc) {
626
+ const issues = [];
627
+ if (!wf.initialState || wf.initialState.length === 0) {
628
+ issues.push({
629
+ severity: "error",
630
+ code: "missing-initial-state",
631
+ message: `Workflow "${wf.name}" has no initialState.`,
632
+ ...idFor(doc, wf.name, "workflow")
633
+ });
634
+ } else if (!(wf.initialState in wf.states)) {
635
+ issues.push({
636
+ severity: "error",
637
+ code: "unknown-initial-state",
638
+ message: `Workflow "${wf.name}" initialState "${wf.initialState}" is not a state.`,
639
+ ...idFor(doc, wf.name, "workflow")
640
+ });
641
+ }
642
+ if (!isValidName(wf.name)) {
643
+ issues.push({
644
+ severity: "error",
645
+ code: "name-regex-violation",
646
+ message: `Workflow name "${wf.name}" is invalid.`
647
+ });
648
+ }
649
+ for (const [stateCode, state] of Object.entries(wf.states)) {
650
+ if (!isValidName(stateCode)) {
651
+ issues.push({
652
+ severity: "error",
653
+ code: "name-regex-violation",
654
+ message: `State code "${stateCode}" is invalid.`
655
+ });
656
+ }
657
+ const transitionSeen = /* @__PURE__ */ new Map();
658
+ for (const t of state.transitions) {
659
+ transitionSeen.set(t.name, (transitionSeen.get(t.name) ?? 0) + 1);
660
+ if (!isValidName(t.name)) {
661
+ issues.push({
662
+ severity: "error",
663
+ code: "name-regex-violation",
664
+ message: `Transition name "${t.name}" is invalid.`
665
+ });
666
+ }
667
+ if (!(t.next in wf.states)) {
668
+ issues.push({
669
+ severity: "error",
670
+ code: "unknown-transition-target",
671
+ message: `Transition "${t.name}" on "${stateCode}" targets unknown state "${t.next}".`
672
+ });
673
+ }
674
+ if (t.processors) {
675
+ const pSeen = /* @__PURE__ */ new Map();
676
+ for (const p of t.processors) {
677
+ pSeen.set(p.name, (pSeen.get(p.name) ?? 0) + 1);
678
+ if (!isValidName(p.name)) {
679
+ issues.push({
680
+ severity: "error",
681
+ code: "name-regex-violation",
682
+ message: `Processor name "${p.name}" is invalid.`
683
+ });
684
+ }
685
+ if (p.type === "scheduled" && p.config.transition.length === 0) {
686
+ issues.push({
687
+ severity: "error",
688
+ code: "scheduled-missing-target",
689
+ message: `Scheduled processor "${p.name}" has empty target transition.`
690
+ });
691
+ }
692
+ if (p.type === "externalized" && p.config) {
693
+ if (p.config.crossoverToAsyncMs !== void 0 && p.config.asyncResult !== true) {
694
+ issues.push({
695
+ severity: "warning",
696
+ code: "crossover-without-async-result",
697
+ message: `Processor "${p.name}" sets crossoverToAsyncMs but asyncResult is not true.`
698
+ });
699
+ }
700
+ }
701
+ }
702
+ for (const [name, count] of pSeen) {
703
+ if (count > 1) {
704
+ issues.push({
705
+ severity: "error",
706
+ code: "duplicate-processor-name",
707
+ message: `Duplicate processor name "${name}" on transition "${t.name}".`
708
+ });
709
+ }
710
+ }
711
+ if (t.processors.length > 5) {
712
+ issues.push({
713
+ severity: "warning",
714
+ code: "processor-overload",
715
+ message: `Transition "${t.name}" has ${t.processors.length} processors (>5).`
716
+ });
717
+ }
718
+ }
719
+ if (t.disabled && wf.active) {
720
+ issues.push({
721
+ severity: "warning",
722
+ code: "disabled-transition-on-active-workflow",
723
+ message: `Transition "${t.name}" is disabled in active workflow "${wf.name}".`
724
+ });
725
+ }
726
+ if (t.processors) {
727
+ for (const p of t.processors) {
728
+ if (p.type === "scheduled") {
729
+ const names = collectTransitionNames(wf);
730
+ if (!names.has(p.config.transition)) {
731
+ issues.push({
732
+ severity: "warning",
733
+ code: "scheduled-target-unresolved",
734
+ message: `Scheduled processor "${p.name}" targets unknown transition "${p.config.transition}" in workflow.`
735
+ });
736
+ }
737
+ }
738
+ }
739
+ }
740
+ }
741
+ for (const [name, count] of transitionSeen) {
742
+ if (count > 1) {
743
+ issues.push({
744
+ severity: "error",
745
+ code: "duplicate-transition-name",
746
+ message: `Duplicate transition name "${name}" on state "${stateCode}".`
747
+ });
748
+ }
749
+ }
750
+ if (state.transitions.length > 8) {
751
+ issues.push({
752
+ severity: "warning",
753
+ code: "excessive-fan-out",
754
+ message: `State "${stateCode}" has ${state.transitions.length} outgoing transitions (>8).`
755
+ });
756
+ }
757
+ if (state.transitions.length > 0 && state.transitions.every((t) => t.manual === true)) {
758
+ issues.push({
759
+ severity: "warning",
760
+ code: "all-transitions-manual",
761
+ message: `State "${stateCode}" has only manual transitions.`
762
+ });
763
+ }
764
+ if (state.transitions.length === 0 && stateCode !== wf.initialState) {
765
+ issues.push({
766
+ severity: "info",
767
+ code: "terminal-state-derived",
768
+ message: `State "${stateCode}" is terminal.`
769
+ });
770
+ }
771
+ }
772
+ const reachable = reachableStates(wf);
773
+ for (const stateCode of Object.keys(wf.states)) {
774
+ if (!reachable.has(stateCode) && stateCode !== wf.initialState) {
775
+ issues.push({
776
+ severity: "warning",
777
+ code: "unreachable-state",
778
+ message: `State "${stateCode}" is unreachable from the initial state.`
779
+ });
780
+ }
781
+ }
782
+ if (!wf.active) {
783
+ issues.push({
784
+ severity: "info",
785
+ code: "workflow-inactive",
786
+ message: `Workflow "${wf.name}" is inactive.`
787
+ });
788
+ }
789
+ const reachableAuto = reachableAutoStates(wf);
790
+ for (const [stateCode, state] of Object.entries(wf.states)) {
791
+ if (!reachableAuto.has(stateCode)) continue;
792
+ for (const t of state.transitions) {
793
+ if (t.manual) continue;
794
+ if (!t.processors) continue;
795
+ for (const p of t.processors) {
796
+ if (p.type === "externalized" && p.executionMode === "SYNC") {
797
+ issues.push({
798
+ severity: "warning",
799
+ code: "sync-on-likely-bottleneck-transition",
800
+ message: `SYNC processor "${p.name}" on auto-reachable transition "${t.name}" may block the main path.`
801
+ });
802
+ }
803
+ }
804
+ }
805
+ }
806
+ return issues;
807
+ }
808
+ function criterionRules(session) {
809
+ const issues = [];
810
+ for (const { criterion, where } of walkCriteria(session)) {
811
+ switch (criterion.type) {
812
+ case "function":
813
+ if (!criterion.function.name || criterion.function.name.length === 0) {
814
+ issues.push({
815
+ severity: "error",
816
+ code: "function-missing-name",
817
+ message: `Function criterion has empty name (at ${describe(where)}).`
818
+ });
819
+ } else if (!isValidName(criterion.function.name)) {
820
+ issues.push({
821
+ severity: "error",
822
+ code: "name-regex-violation",
823
+ message: `Function criterion name "${criterion.function.name}" is invalid.`
824
+ });
825
+ }
826
+ if (!criterion.function.criterion) {
827
+ issues.push({
828
+ severity: "warning",
829
+ code: "function-without-quick-exit",
830
+ message: `Function criterion "${criterion.function.name}" has no local quick-exit criterion.`
831
+ });
832
+ }
833
+ break;
834
+ case "array":
835
+ for (const v of criterion.value) {
836
+ if (typeof v !== "string") {
837
+ issues.push({
838
+ severity: "error",
839
+ code: "array-non-string-value",
840
+ message: `Array criterion value contains a non-string element.`
841
+ });
842
+ break;
843
+ }
844
+ }
845
+ break;
846
+ case "lifecycle":
847
+ if (!LIFECYCLE_FIELDS.has(criterion.field)) {
848
+ issues.push({
849
+ severity: "error",
850
+ code: "lifecycle-invalid-field",
851
+ message: `Lifecycle criterion field "${criterion.field}" is invalid.`
852
+ });
853
+ }
854
+ break;
855
+ case "group":
856
+ if (criterion.operator === "NOT" && criterion.conditions.length > 1) {
857
+ issues.push({
858
+ severity: "warning",
859
+ code: "not-with-multiple-conditions",
860
+ message: `NOT group has ${criterion.conditions.length} conditions; should have exactly one.`
861
+ });
862
+ }
863
+ break;
864
+ case "simple":
865
+ break;
866
+ }
867
+ }
868
+ return issues;
869
+ }
870
+ function reachableStates(wf) {
871
+ const visited = /* @__PURE__ */ new Set();
872
+ if (!(wf.initialState in wf.states)) return visited;
873
+ const queue = [wf.initialState];
874
+ visited.add(wf.initialState);
875
+ while (queue.length) {
876
+ const cur = queue.shift();
877
+ const state = wf.states[cur];
878
+ if (!state) continue;
879
+ for (const t of state.transitions) {
880
+ if (!visited.has(t.next) && t.next in wf.states) {
881
+ visited.add(t.next);
882
+ queue.push(t.next);
883
+ }
884
+ }
885
+ }
886
+ return visited;
887
+ }
888
+ function reachableAutoStates(wf) {
889
+ const visited = /* @__PURE__ */ new Set();
890
+ if (!(wf.initialState in wf.states)) return visited;
891
+ const queue = [wf.initialState];
892
+ visited.add(wf.initialState);
893
+ while (queue.length) {
894
+ const cur = queue.shift();
895
+ const state = wf.states[cur];
896
+ if (!state) continue;
897
+ for (const t of state.transitions) {
898
+ if (t.manual) continue;
899
+ if (!visited.has(t.next) && t.next in wf.states) {
900
+ visited.add(t.next);
901
+ queue.push(t.next);
902
+ }
903
+ }
904
+ }
905
+ return visited;
906
+ }
907
+ function collectTransitionNames(wf) {
908
+ const out = /* @__PURE__ */ new Set();
909
+ for (const state of Object.values(wf.states)) {
910
+ for (const t of state.transitions) out.add(t.name);
911
+ }
912
+ return out;
913
+ }
914
+ function describe(w) {
915
+ if (w.kind === "workflow") return `workflow "${w.workflow}"`;
916
+ return `transition "${w.transitionName}" on "${w.workflow}:${w.state}"`;
917
+ }
918
+ function idFor(doc, workflowName, _kind) {
919
+ if (!doc) return {};
920
+ const id = doc.meta.ids.workflows[workflowName];
921
+ return id ? { targetId: id } : {};
922
+ }
923
+
924
+ // src/validate/schema.ts
925
+ function zodErrorToIssues(err) {
926
+ return err.issues.map((issue) => ({
927
+ severity: "error",
928
+ code: `schema-${issue.code}`,
929
+ message: issue.message,
930
+ detail: {
931
+ path: issue.path
932
+ }
933
+ }));
934
+ }
935
+
936
+ // src/parse/parse-import.ts
937
+ function parseJsonSafe(json) {
938
+ try {
939
+ return { ok: true, value: JSON.parse(json) };
940
+ } catch (e) {
941
+ return { ok: false, err: e.message };
942
+ }
943
+ }
944
+ function parseImportPayload(json, prior) {
945
+ const parsed = parseJsonSafe(json);
946
+ if (!parsed.ok) {
947
+ throw new ParseJsonError(`Invalid JSON: ${parsed.err}`);
948
+ }
949
+ let aliased;
950
+ try {
951
+ aliased = normalizeOperatorAlias(parsed.value);
952
+ } catch (e) {
953
+ return {
954
+ ok: false,
955
+ issues: [
956
+ {
957
+ severity: "error",
958
+ code: "operator-alias-conflict",
959
+ message: e.message
960
+ }
961
+ ]
962
+ };
963
+ }
964
+ const schemaResult = ImportPayloadSchema.safeParse(aliased);
965
+ if (!schemaResult.success) {
966
+ return { ok: false, issues: zodErrorToIssues(schemaResult.error) };
967
+ }
968
+ const normalizedWorkflows = schemaResult.data.workflows.map(normalizeWorkflowInput);
969
+ const session = {
970
+ entity: null,
971
+ importMode: schemaResult.data.importMode,
972
+ workflows: normalizedWorkflows
973
+ };
974
+ const meta = assignSyntheticIds(session, prior);
975
+ const document = { session, meta };
976
+ const issues = validateSemantics(session, document);
977
+ const hasError = issues.some((i) => i.severity === "error");
978
+ return {
979
+ ok: !hasError,
980
+ value: { importMode: session.importMode, workflows: session.workflows },
981
+ document,
982
+ issues
983
+ };
984
+ }
985
+
986
+ // src/parse/parse-export.ts
987
+ function parseExportPayload(json, prior) {
988
+ let parsed;
989
+ try {
990
+ parsed = JSON.parse(json);
991
+ } catch (e) {
992
+ throw new ParseJsonError(`Invalid JSON: ${e.message}`);
993
+ }
994
+ let aliased;
995
+ try {
996
+ aliased = normalizeOperatorAlias(parsed);
997
+ } catch (e) {
998
+ return {
999
+ ok: false,
1000
+ issues: [
1001
+ {
1002
+ severity: "error",
1003
+ code: "operator-alias-conflict",
1004
+ message: e.message
1005
+ }
1006
+ ]
1007
+ };
1008
+ }
1009
+ const schemaResult = ExportPayloadSchema.safeParse(aliased);
1010
+ if (!schemaResult.success) {
1011
+ return { ok: false, issues: zodErrorToIssues(schemaResult.error) };
1012
+ }
1013
+ const normalizedWorkflows = schemaResult.data.workflows.map(normalizeWorkflowInput);
1014
+ const session = {
1015
+ entity: {
1016
+ entityName: schemaResult.data.entityName,
1017
+ modelVersion: schemaResult.data.modelVersion
1018
+ },
1019
+ importMode: "MERGE",
1020
+ workflows: normalizedWorkflows
1021
+ };
1022
+ const meta = assignSyntheticIds(session, prior);
1023
+ const document = { session, meta };
1024
+ const issues = validateSemantics(session, document);
1025
+ const hasError = issues.some((i) => i.severity === "error");
1026
+ return {
1027
+ ok: !hasError,
1028
+ value: {
1029
+ entityName: schemaResult.data.entityName,
1030
+ modelVersion: schemaResult.data.modelVersion,
1031
+ workflows: session.workflows
1032
+ },
1033
+ document,
1034
+ issues
1035
+ };
1036
+ }
1037
+
1038
+ // src/parse/parse-editor-document.ts
1039
+ var import_zod7 = require("zod");
1040
+ var EditorDocumentSchema = import_zod7.z.object({
1041
+ session: import_zod7.z.object({
1042
+ entity: import_zod7.z.object({
1043
+ entityName: import_zod7.z.string(),
1044
+ modelVersion: import_zod7.z.number().int().positive()
1045
+ }).nullable(),
1046
+ importMode: import_zod7.z.enum(["MERGE", "REPLACE", "ACTIVATE"]),
1047
+ workflows: import_zod7.z.array(import_zod7.z.unknown())
1048
+ }),
1049
+ meta: import_zod7.z.object({
1050
+ revision: import_zod7.z.number().int().nonnegative(),
1051
+ ids: import_zod7.z.unknown(),
1052
+ workflowUi: import_zod7.z.record(import_zod7.z.unknown()),
1053
+ lastValidJsonHash: import_zod7.z.string().optional()
1054
+ }).passthrough()
1055
+ });
1056
+ function parseEditorDocument(json) {
1057
+ let parsed;
1058
+ try {
1059
+ parsed = JSON.parse(json);
1060
+ } catch (e) {
1061
+ throw new ParseJsonError(`Invalid JSON: ${e.message}`);
1062
+ }
1063
+ const outerResult = EditorDocumentSchema.safeParse(parsed);
1064
+ if (!outerResult.success) {
1065
+ return { ok: false, issues: zodErrorToIssues(outerResult.error) };
1066
+ }
1067
+ const aliased = normalizeOperatorAlias(outerResult.data.session);
1068
+ const inner = ImportPayloadSchema.omit({ importMode: true }).extend({
1069
+ importMode: import_zod7.z.enum(["MERGE", "REPLACE", "ACTIVATE"])
1070
+ });
1071
+ const sessionResult = inner.safeParse({
1072
+ importMode: outerResult.data.session.importMode,
1073
+ workflows: aliased.workflows
1074
+ });
1075
+ if (!sessionResult.success) {
1076
+ return { ok: false, issues: zodErrorToIssues(sessionResult.error) };
1077
+ }
1078
+ const normalizedWorkflows = sessionResult.data.workflows.map(normalizeWorkflowInput);
1079
+ const session = {
1080
+ entity: outerResult.data.session.entity,
1081
+ importMode: sessionResult.data.importMode,
1082
+ workflows: normalizedWorkflows
1083
+ };
1084
+ const meta = assignSyntheticIds(
1085
+ session,
1086
+ outerResult.data.meta
1087
+ );
1088
+ const document = { session, meta };
1089
+ const issues = validateSemantics(session, document);
1090
+ const hasError = issues.some((i) => i.severity === "error");
1091
+ return { ok: !hasError, document, value: document, issues };
1092
+ }
1093
+
1094
+ // src/normalize/output.ts
1095
+ function outputWorkflow(w) {
1096
+ const out = {
1097
+ version: w.version,
1098
+ name: w.name
1099
+ };
1100
+ if (w.desc !== void 0) out["desc"] = w.desc;
1101
+ out["initialState"] = w.initialState;
1102
+ out["active"] = w.active;
1103
+ if (w.criterion !== void 0) out["criterion"] = outputCriterion(w.criterion);
1104
+ out["states"] = outputStates(w.states);
1105
+ return out;
1106
+ }
1107
+ function outputStates(states) {
1108
+ const out = {};
1109
+ for (const [code, state] of Object.entries(states)) {
1110
+ out[code] = { transitions: state.transitions.map(outputTransition) };
1111
+ }
1112
+ return out;
1113
+ }
1114
+ function outputTransition(t) {
1115
+ const out = {
1116
+ name: t.name,
1117
+ next: t.next,
1118
+ manual: t.manual,
1119
+ disabled: t.disabled
1120
+ };
1121
+ if (t.criterion !== void 0) out["criterion"] = outputCriterion(t.criterion);
1122
+ if (t.processors !== void 0 && t.processors.length > 0) {
1123
+ out["processors"] = t.processors.map(outputProcessor);
1124
+ }
1125
+ return out;
1126
+ }
1127
+ function outputCriterion(c) {
1128
+ switch (c.type) {
1129
+ case "simple": {
1130
+ const out = {
1131
+ type: "simple",
1132
+ jsonPath: c.jsonPath,
1133
+ operation: c.operation
1134
+ };
1135
+ if (c.value !== void 0) out["value"] = c.value;
1136
+ return out;
1137
+ }
1138
+ case "group":
1139
+ return {
1140
+ type: "group",
1141
+ operator: c.operator,
1142
+ conditions: c.conditions.map(outputCriterion)
1143
+ };
1144
+ case "function": {
1145
+ const fn = { name: c.function.name };
1146
+ if (c.function.config !== void 0) {
1147
+ const config = outputFunctionConfig(c.function.config);
1148
+ if (Object.keys(config).length > 0) fn["config"] = config;
1149
+ }
1150
+ if (c.function.criterion !== void 0) {
1151
+ fn["criterion"] = outputCriterion(c.function.criterion);
1152
+ }
1153
+ return { type: "function", function: fn };
1154
+ }
1155
+ case "lifecycle": {
1156
+ const out = {
1157
+ type: "lifecycle",
1158
+ field: c.field,
1159
+ operation: c.operation
1160
+ };
1161
+ if (c.value !== void 0) out["value"] = c.value;
1162
+ return out;
1163
+ }
1164
+ case "array":
1165
+ return {
1166
+ type: "array",
1167
+ jsonPath: c.jsonPath,
1168
+ operation: c.operation,
1169
+ value: c.value
1170
+ };
1171
+ }
1172
+ }
1173
+ function outputProcessor(p) {
1174
+ if (p.type === "externalized") return outputExternalizedProcessor(p);
1175
+ return {
1176
+ type: "scheduled",
1177
+ name: p.name,
1178
+ config: outputScheduledConfig(p.config)
1179
+ };
1180
+ }
1181
+ function outputScheduledConfig(cfg) {
1182
+ const out = {
1183
+ delayMs: cfg.delayMs,
1184
+ transition: cfg.transition
1185
+ };
1186
+ if (cfg.timeoutMs !== void 0) out["timeoutMs"] = cfg.timeoutMs;
1187
+ return out;
1188
+ }
1189
+ function outputExternalizedProcessor(p) {
1190
+ const out = {
1191
+ type: "externalized",
1192
+ name: p.name
1193
+ };
1194
+ if (p.executionMode !== void 0 && p.executionMode !== "ASYNC_NEW_TX") {
1195
+ out["executionMode"] = p.executionMode;
1196
+ }
1197
+ if (p.config !== void 0) {
1198
+ const cfg = outputExternalizedConfig(p.config);
1199
+ if (Object.keys(cfg).length > 0) out["config"] = cfg;
1200
+ }
1201
+ return out;
1202
+ }
1203
+ function outputExternalizedConfig(cfg) {
1204
+ const out = {};
1205
+ if (cfg.attachEntity === true) out["attachEntity"] = true;
1206
+ if (cfg.calculationNodesTags !== void 0 && cfg.calculationNodesTags !== "") {
1207
+ out["calculationNodesTags"] = cfg.calculationNodesTags;
1208
+ }
1209
+ if (cfg.responseTimeoutMs !== void 0) out["responseTimeoutMs"] = cfg.responseTimeoutMs;
1210
+ if (cfg.retryPolicy !== void 0 && cfg.retryPolicy !== "") {
1211
+ out["retryPolicy"] = cfg.retryPolicy;
1212
+ }
1213
+ if (cfg.context !== void 0 && cfg.context !== "") out["context"] = cfg.context;
1214
+ if (cfg.asyncResult === true) out["asyncResult"] = true;
1215
+ if (cfg.asyncResult === true && cfg.crossoverToAsyncMs !== void 0) {
1216
+ out["crossoverToAsyncMs"] = cfg.crossoverToAsyncMs;
1217
+ }
1218
+ return out;
1219
+ }
1220
+ function outputFunctionConfig(cfg) {
1221
+ const c = cfg;
1222
+ const out = {};
1223
+ if (c.attachEntity === true) out["attachEntity"] = true;
1224
+ if (c.calculationNodesTags !== void 0 && c.calculationNodesTags !== "") {
1225
+ out["calculationNodesTags"] = c.calculationNodesTags;
1226
+ }
1227
+ if (c.responseTimeoutMs !== void 0) out["responseTimeoutMs"] = c.responseTimeoutMs;
1228
+ if (c.retryPolicy !== void 0 && c.retryPolicy !== "") out["retryPolicy"] = c.retryPolicy;
1229
+ if (c.context !== void 0 && c.context !== "") out["context"] = c.context;
1230
+ return out;
1231
+ }
1232
+
1233
+ // src/serialize/stringify.ts
1234
+ function prettyStringify(value) {
1235
+ const body = JSON.stringify(value, null, 2);
1236
+ const lfOnly = body.replace(/\r\n/g, "\n");
1237
+ return lfOnly + "\n";
1238
+ }
1239
+
1240
+ // src/serialize/payload.ts
1241
+ function serializeImportPayload(doc) {
1242
+ const payload = {
1243
+ importMode: doc.session.importMode,
1244
+ workflows: doc.session.workflows.map(outputWorkflow)
1245
+ };
1246
+ return prettyStringify(payload);
1247
+ }
1248
+ function serializeExportPayload(doc, entity) {
1249
+ const e = entity ?? doc.session.entity;
1250
+ if (e == null) {
1251
+ throw new Error("serializeExportPayload requires an entity identity");
1252
+ }
1253
+ const payload = {
1254
+ entityName: e.entityName,
1255
+ modelVersion: e.modelVersion,
1256
+ workflows: doc.session.workflows.map(outputWorkflow)
1257
+ };
1258
+ return prettyStringify(payload);
1259
+ }
1260
+ function serializeEditorDocument(doc) {
1261
+ return prettyStringify({
1262
+ session: {
1263
+ entity: doc.session.entity,
1264
+ importMode: doc.session.importMode,
1265
+ workflows: doc.session.workflows.map(outputWorkflow)
1266
+ },
1267
+ meta: doc.meta
1268
+ });
1269
+ }
1270
+
1271
+ // src/identity/lookup.ts
1272
+ function lookupById(doc, uuid) {
1273
+ const { ids } = doc.meta;
1274
+ const session = doc.session;
1275
+ for (const [name, wfUuid] of Object.entries(ids.workflows)) {
1276
+ if (wfUuid === uuid) {
1277
+ const workflow = session.workflows.find((w) => w.name === name);
1278
+ if (workflow) return { kind: "workflow", workflow };
1279
+ }
1280
+ }
1281
+ const statePtr = ids.states[uuid];
1282
+ if (statePtr) {
1283
+ const workflow = session.workflows.find((w) => w.name === statePtr.workflow);
1284
+ if (!workflow) return null;
1285
+ const state = workflow.states[statePtr.state];
1286
+ if (!state) return null;
1287
+ return { kind: "state", workflow, state, stateCode: statePtr.state };
1288
+ }
1289
+ const tPtr = ids.transitions[uuid];
1290
+ if (tPtr) {
1291
+ const workflow = session.workflows.find((w) => w.name === tPtr.workflow);
1292
+ if (!workflow) return null;
1293
+ const state = workflow.states[tPtr.state];
1294
+ if (!state) return null;
1295
+ const transition = findTransitionByUuid(doc, tPtr.workflow, tPtr.state, uuid);
1296
+ if (!transition) return null;
1297
+ return { kind: "transition", workflow, state, transition };
1298
+ }
1299
+ const pPtr = ids.processors[uuid];
1300
+ if (pPtr) {
1301
+ const transition = findTransitionByUuid(
1302
+ doc,
1303
+ pPtr.workflow,
1304
+ pPtr.state,
1305
+ pPtr.transitionUuid
1306
+ );
1307
+ if (!transition) return null;
1308
+ const processor = findProcessorByUuid(doc, uuid);
1309
+ if (!processor) return null;
1310
+ return { kind: "processor", transition, processor };
1311
+ }
1312
+ const cPtr = ids.criteria[uuid];
1313
+ if (cPtr) {
1314
+ const host = resolveHost(doc, cPtr.host);
1315
+ if (host === null) return null;
1316
+ const criterion = walkPath(host, cPtr.path);
1317
+ if (!criterion) return null;
1318
+ return {
1319
+ kind: "criterion",
1320
+ criterion,
1321
+ parent: { host: cPtr.host, path: cPtr.path }
1322
+ };
1323
+ }
1324
+ return null;
1325
+ }
1326
+ function findTransitionByUuid(doc, workflowName, stateCode, transitionUuid) {
1327
+ const workflow = doc.session.workflows.find((w) => w.name === workflowName);
1328
+ if (!workflow) return null;
1329
+ const state = workflow.states[stateCode];
1330
+ if (!state) return null;
1331
+ const orderedUuids = listTransitionUuidsForState(doc, workflowName, stateCode);
1332
+ const idx = orderedUuids.indexOf(transitionUuid);
1333
+ if (idx < 0) return null;
1334
+ return state.transitions[idx] ?? null;
1335
+ }
1336
+ function listTransitionUuidsForState(doc, workflowName, stateCode) {
1337
+ const out = [];
1338
+ for (const [uuid, ptr] of Object.entries(doc.meta.ids.transitions)) {
1339
+ if (ptr.workflow === workflowName && ptr.state === stateCode) {
1340
+ out.push(uuid);
1341
+ }
1342
+ }
1343
+ return out;
1344
+ }
1345
+ function findProcessorByUuid(doc, processorUuid) {
1346
+ const ptr = doc.meta.ids.processors[processorUuid];
1347
+ if (!ptr) return null;
1348
+ const transition = findTransitionByUuid(
1349
+ doc,
1350
+ ptr.workflow,
1351
+ ptr.state,
1352
+ ptr.transitionUuid
1353
+ );
1354
+ if (!transition || !transition.processors) return null;
1355
+ const orderedUuids = [];
1356
+ for (const [uuid, pPtr] of Object.entries(doc.meta.ids.processors)) {
1357
+ if (pPtr.transitionUuid === ptr.transitionUuid) orderedUuids.push(uuid);
1358
+ }
1359
+ const idx = orderedUuids.indexOf(processorUuid);
1360
+ if (idx < 0) return null;
1361
+ return transition.processors[idx] ?? null;
1362
+ }
1363
+ function resolveHost(doc, host) {
1364
+ const workflow = doc.session.workflows.find((w) => w.name === host.workflow);
1365
+ if (!workflow) return null;
1366
+ if (host.kind === "workflow") return workflow;
1367
+ if (host.kind === "transition") {
1368
+ const state = workflow.states[host.state];
1369
+ if (!state) return null;
1370
+ return findTransitionByUuid(doc, host.workflow, host.state, host.transitionUuid);
1371
+ }
1372
+ return null;
1373
+ }
1374
+ function walkPath(root, path) {
1375
+ let node = root;
1376
+ for (const segment of path) {
1377
+ if (node == null || typeof node !== "object") return null;
1378
+ node = node[segment];
1379
+ if (node == null) return null;
1380
+ if (Array.isArray(node)) continue;
1381
+ }
1382
+ if (!node || typeof node !== "object") return null;
1383
+ const maybe = node;
1384
+ if (maybe.type === "simple" || maybe.type === "group" || maybe.type === "function" || maybe.type === "lifecycle" || maybe.type === "array") {
1385
+ return node;
1386
+ }
1387
+ return null;
1388
+ }
1389
+
1390
+ // src/identity/id-for.ts
1391
+ function idFor2(meta, ref) {
1392
+ switch (ref.kind) {
1393
+ case "workflow":
1394
+ return meta.ids.workflows[ref.workflow] ?? null;
1395
+ case "state": {
1396
+ for (const [uuid, ptr] of Object.entries(meta.ids.states)) {
1397
+ if (ptr.workflow === ref.workflow && ptr.state === ref.state) return uuid;
1398
+ }
1399
+ return null;
1400
+ }
1401
+ case "transition": {
1402
+ const matches = [];
1403
+ for (const [uuid, ptr] of Object.entries(meta.ids.transitions)) {
1404
+ if (ptr.workflow === ref.workflow && ptr.state === ref.state) {
1405
+ matches.push(uuid);
1406
+ }
1407
+ }
1408
+ return matches[ref.ordinal] ?? null;
1409
+ }
1410
+ case "processor": {
1411
+ const matches = [];
1412
+ for (const [uuid, ptr] of Object.entries(meta.ids.processors)) {
1413
+ if (ptr.transitionUuid === ref.transitionUuid) matches.push(uuid);
1414
+ }
1415
+ return matches[ref.ordinal] ?? null;
1416
+ }
1417
+ case "criterion": {
1418
+ const target = JSON.stringify(ref.path);
1419
+ for (const [uuid, ptr] of Object.entries(meta.ids.criteria)) {
1420
+ if (JSON.stringify(ptr.host) === JSON.stringify(ref.host) && JSON.stringify(ptr.path) === target) {
1421
+ return uuid;
1422
+ }
1423
+ }
1424
+ return null;
1425
+ }
1426
+ }
1427
+ }
1428
+
1429
+ // src/validate/index.ts
1430
+ function validateImportSchema(raw) {
1431
+ const parsed = ImportPayloadSchema.safeParse(raw);
1432
+ return parsed.success ? [] : zodErrorToIssues(parsed.error);
1433
+ }
1434
+ function validateExportSchema(raw) {
1435
+ const parsed = ExportPayloadSchema.safeParse(raw);
1436
+ return parsed.success ? [] : zodErrorToIssues(parsed.error);
1437
+ }
1438
+ function validateAll(doc) {
1439
+ return validateSemantics(doc.session, doc);
1440
+ }
1441
+ function validateSession(session) {
1442
+ return validateSemantics(session);
1443
+ }
1444
+
1445
+ // src/patch/apply.ts
1446
+ var import_immer = require("immer");
1447
+ function applyPatch(doc, patch) {
1448
+ if (patch.op === "setEdgeAnchors") {
1449
+ return applySetEdgeAnchors(doc, patch);
1450
+ }
1451
+ const nextSession = (0, import_immer.produce)(doc.session, (d) => {
1452
+ const draft = d;
1453
+ switch (patch.op) {
1454
+ case "addWorkflow":
1455
+ draft.workflows.push(patch.workflow);
1456
+ return;
1457
+ case "removeWorkflow":
1458
+ draft.workflows = draft.workflows.filter((w) => w.name !== patch.workflow);
1459
+ return;
1460
+ case "updateWorkflowMeta": {
1461
+ const wf = draft.workflows.find((w) => w.name === patch.workflow);
1462
+ if (!wf) return;
1463
+ Object.assign(wf, patch.updates);
1464
+ return;
1465
+ }
1466
+ case "renameWorkflow": {
1467
+ const wf = draft.workflows.find((w) => w.name === patch.from);
1468
+ if (!wf) return;
1469
+ wf.name = patch.to;
1470
+ return;
1471
+ }
1472
+ case "setInitialState": {
1473
+ const wf = draft.workflows.find((w) => w.name === patch.workflow);
1474
+ if (!wf) return;
1475
+ wf.initialState = patch.stateCode;
1476
+ return;
1477
+ }
1478
+ case "setWorkflowCriterion": {
1479
+ const wf = draft.workflows.find((w) => w.name === patch.workflow);
1480
+ if (!wf) return;
1481
+ if (patch.criterion === void 0) delete wf.criterion;
1482
+ else wf.criterion = patch.criterion;
1483
+ return;
1484
+ }
1485
+ case "addState": {
1486
+ const wf = draft.workflows.find((w) => w.name === patch.workflow);
1487
+ if (!wf) return;
1488
+ if (!(patch.stateCode in wf.states)) {
1489
+ wf.states[patch.stateCode] = { transitions: [] };
1490
+ }
1491
+ return;
1492
+ }
1493
+ case "renameState": {
1494
+ const wf = draft.workflows.find((w) => w.name === patch.workflow);
1495
+ if (!wf) return;
1496
+ renameStateCascading(wf, patch.from, patch.to);
1497
+ return;
1498
+ }
1499
+ case "removeState": {
1500
+ const wf = draft.workflows.find((w) => w.name === patch.workflow);
1501
+ if (!wf) return;
1502
+ removeStateCascading(wf, patch.stateCode);
1503
+ return;
1504
+ }
1505
+ case "addTransition": {
1506
+ const wf = draft.workflows.find((w) => w.name === patch.workflow);
1507
+ if (!wf) return;
1508
+ const state = wf.states[patch.fromState];
1509
+ if (!state) return;
1510
+ state.transitions.push(patch.transition);
1511
+ return;
1512
+ }
1513
+ case "updateTransition": {
1514
+ const loc = locateTransition(doc, patch.transitionUuid);
1515
+ if (!loc) return;
1516
+ const wf = draft.workflows.find((w) => w.name === loc.workflow);
1517
+ const state = wf?.states[loc.state];
1518
+ const transition = state?.transitions[loc.index];
1519
+ if (!transition) return;
1520
+ Object.assign(transition, patch.updates);
1521
+ return;
1522
+ }
1523
+ case "removeTransition": {
1524
+ const loc = locateTransition(doc, patch.transitionUuid);
1525
+ if (!loc) return;
1526
+ const wf = draft.workflows.find((w) => w.name === loc.workflow);
1527
+ const state = wf?.states[loc.state];
1528
+ if (!state) return;
1529
+ state.transitions.splice(loc.index, 1);
1530
+ return;
1531
+ }
1532
+ case "reorderTransition": {
1533
+ const wf = draft.workflows.find((w) => w.name === patch.workflow);
1534
+ const state = wf?.states[patch.fromState];
1535
+ if (!state) return;
1536
+ const loc = locateTransition(doc, patch.transitionUuid);
1537
+ if (!loc) return;
1538
+ const [item] = state.transitions.splice(loc.index, 1);
1539
+ if (!item) return;
1540
+ state.transitions.splice(patch.toIndex, 0, item);
1541
+ return;
1542
+ }
1543
+ case "addProcessor": {
1544
+ const loc = locateTransition(doc, patch.transitionUuid);
1545
+ if (!loc) return;
1546
+ const wf = draft.workflows.find((w) => w.name === loc.workflow);
1547
+ const state = wf?.states[loc.state];
1548
+ const transition = state?.transitions[loc.index];
1549
+ if (!transition) return;
1550
+ if (!transition.processors) transition.processors = [];
1551
+ const idx = patch.index ?? transition.processors.length;
1552
+ transition.processors.splice(idx, 0, patch.processor);
1553
+ return;
1554
+ }
1555
+ case "updateProcessor": {
1556
+ const procLoc = locateProcessor(doc, patch.processorUuid);
1557
+ if (!procLoc) return;
1558
+ const wf = draft.workflows.find((w) => w.name === procLoc.workflow);
1559
+ const state = wf?.states[procLoc.state];
1560
+ const transition = state?.transitions[procLoc.transitionIndex];
1561
+ const processor = transition?.processors?.[procLoc.processorIndex];
1562
+ if (!processor) return;
1563
+ Object.assign(processor, patch.updates);
1564
+ return;
1565
+ }
1566
+ case "removeProcessor": {
1567
+ const procLoc = locateProcessor(doc, patch.processorUuid);
1568
+ if (!procLoc) return;
1569
+ const wf = draft.workflows.find((w) => w.name === procLoc.workflow);
1570
+ const state = wf?.states[procLoc.state];
1571
+ const transition = state?.transitions[procLoc.transitionIndex];
1572
+ if (!transition?.processors) return;
1573
+ transition.processors.splice(procLoc.processorIndex, 1);
1574
+ if (transition.processors.length === 0) delete transition.processors;
1575
+ return;
1576
+ }
1577
+ case "reorderProcessor": {
1578
+ const procLoc = locateProcessor(doc, patch.processorUuid);
1579
+ if (!procLoc) return;
1580
+ const wf = draft.workflows.find((w) => w.name === procLoc.workflow);
1581
+ const state = wf?.states[procLoc.state];
1582
+ const transition = state?.transitions[procLoc.transitionIndex];
1583
+ if (!transition?.processors) return;
1584
+ const [item] = transition.processors.splice(procLoc.processorIndex, 1);
1585
+ if (!item) return;
1586
+ transition.processors.splice(patch.toIndex, 0, item);
1587
+ return;
1588
+ }
1589
+ case "setCriterion": {
1590
+ const host = patch.host;
1591
+ const wf = draft.workflows.find((w) => w.name === host.workflow);
1592
+ if (!wf) return;
1593
+ let container;
1594
+ if (host.kind === "workflow") container = wf;
1595
+ else if (host.kind === "transition") {
1596
+ const state = wf.states[host.state];
1597
+ if (!state) return;
1598
+ const loc = locateTransition(doc, host.transitionUuid);
1599
+ if (!loc) return;
1600
+ container = state.transitions[loc.index];
1601
+ } else {
1602
+ return;
1603
+ }
1604
+ applyCriterionAtPath(
1605
+ container,
1606
+ patch.path,
1607
+ patch.criterion
1608
+ );
1609
+ return;
1610
+ }
1611
+ case "setImportMode":
1612
+ draft.importMode = patch.mode;
1613
+ return;
1614
+ case "setEntity":
1615
+ draft.entity = patch.entity;
1616
+ return;
1617
+ case "replaceSession":
1618
+ draft.workflows = patch.session.workflows;
1619
+ draft.importMode = patch.session.importMode;
1620
+ draft.entity = patch.session.entity;
1621
+ return;
1622
+ }
1623
+ });
1624
+ const nextMeta = assignSyntheticIds(nextSession, doc.meta);
1625
+ return {
1626
+ session: nextSession,
1627
+ meta: { ...nextMeta, revision: doc.meta.revision + 1 }
1628
+ };
1629
+ }
1630
+ function applyPatches(doc, patches) {
1631
+ return patches.reduce((d, p) => applyPatch(d, p), doc);
1632
+ }
1633
+ function validateAfterPatch(doc) {
1634
+ return validateSemantics(doc.session, doc);
1635
+ }
1636
+ function applySetEdgeAnchors(doc, patch) {
1637
+ const ptr = doc.meta.ids.transitions[patch.transitionUuid];
1638
+ if (!ptr) return { ...doc, meta: { ...doc.meta, revision: doc.meta.revision + 1 } };
1639
+ const workflowUi = { ...doc.meta.workflowUi };
1640
+ const current = workflowUi[ptr.workflow] ?? {};
1641
+ const edgeAnchors = { ...current.edgeAnchors ?? {} };
1642
+ if (patch.anchors === null) {
1643
+ delete edgeAnchors[patch.transitionUuid];
1644
+ } else {
1645
+ edgeAnchors[patch.transitionUuid] = { ...patch.anchors };
1646
+ }
1647
+ workflowUi[ptr.workflow] = {
1648
+ ...current,
1649
+ edgeAnchors: Object.keys(edgeAnchors).length > 0 ? edgeAnchors : void 0
1650
+ };
1651
+ return {
1652
+ session: doc.session,
1653
+ meta: {
1654
+ ...doc.meta,
1655
+ workflowUi,
1656
+ revision: doc.meta.revision + 1
1657
+ }
1658
+ };
1659
+ }
1660
+ function renameStateCascading(wf, from, to) {
1661
+ if (!(from in wf.states) || from === to) return;
1662
+ wf.states[to] = wf.states[from];
1663
+ delete wf.states[from];
1664
+ for (const state of Object.values(wf.states)) {
1665
+ for (const t of state.transitions) {
1666
+ if (t.next === from) t.next = to;
1667
+ }
1668
+ }
1669
+ if (wf.initialState === from) wf.initialState = to;
1670
+ }
1671
+ function removeStateCascading(wf, stateCode) {
1672
+ if (!(stateCode in wf.states)) return;
1673
+ delete wf.states[stateCode];
1674
+ for (const state of Object.values(wf.states)) {
1675
+ state.transitions = state.transitions.filter((t) => t.next !== stateCode);
1676
+ }
1677
+ if (wf.initialState === stateCode) wf.initialState = "";
1678
+ }
1679
+ function locateTransition(doc, transitionUuid) {
1680
+ const ptr = doc.meta.ids.transitions[transitionUuid];
1681
+ if (!ptr) return null;
1682
+ const ordered = [];
1683
+ for (const [uuid, p] of Object.entries(doc.meta.ids.transitions)) {
1684
+ if (p.workflow === ptr.workflow && p.state === ptr.state) ordered.push(uuid);
1685
+ }
1686
+ const idx = ordered.indexOf(transitionUuid);
1687
+ if (idx < 0) return null;
1688
+ return { workflow: ptr.workflow, state: ptr.state, index: idx };
1689
+ }
1690
+ function locateProcessor(doc, processorUuid) {
1691
+ const ptr = doc.meta.ids.processors[processorUuid];
1692
+ if (!ptr) return null;
1693
+ const tLoc = locateTransition(doc, ptr.transitionUuid);
1694
+ if (!tLoc) return null;
1695
+ const ordered = [];
1696
+ for (const [uuid, p] of Object.entries(doc.meta.ids.processors)) {
1697
+ if (p.transitionUuid === ptr.transitionUuid) ordered.push(uuid);
1698
+ }
1699
+ const pIdx = ordered.indexOf(processorUuid);
1700
+ if (pIdx < 0) return null;
1701
+ return {
1702
+ workflow: tLoc.workflow,
1703
+ state: tLoc.state,
1704
+ transitionIndex: tLoc.index,
1705
+ processorIndex: pIdx
1706
+ };
1707
+ }
1708
+ function applyCriterionAtPath(container, path, criterion) {
1709
+ if (path.length === 0) return;
1710
+ let node = container;
1711
+ for (let i = 0; i < path.length - 1; i++) {
1712
+ const seg = path[i];
1713
+ const next = node[seg];
1714
+ if (next === void 0 || next === null) return;
1715
+ if (Array.isArray(next)) {
1716
+ const idx = Number(path[i + 1]);
1717
+ const arr = next;
1718
+ const target = arr[idx];
1719
+ if (target === void 0 || typeof target !== "object") return;
1720
+ node = target;
1721
+ i++;
1722
+ } else if (typeof next === "object") {
1723
+ node = next;
1724
+ } else {
1725
+ return;
1726
+ }
1727
+ }
1728
+ const lastSeg = path[path.length - 1];
1729
+ if (criterion === void 0) {
1730
+ delete node[lastSeg];
1731
+ } else {
1732
+ node[lastSeg] = criterion;
1733
+ }
1734
+ }
1735
+
1736
+ // src/patch/invert.ts
1737
+ function invertPatch(doc, patch) {
1738
+ switch (patch.op) {
1739
+ case "addWorkflow":
1740
+ return { op: "removeWorkflow", workflow: patch.workflow.name };
1741
+ case "removeWorkflow":
1742
+ case "renameWorkflow":
1743
+ case "removeState":
1744
+ case "renameState":
1745
+ case "replaceSession":
1746
+ return { op: "replaceSession", session: cloneSession(doc) };
1747
+ case "updateWorkflowMeta": {
1748
+ const wf = findWorkflow(doc, patch.workflow);
1749
+ if (!wf) return noop();
1750
+ const prior = {};
1751
+ for (const key of Object.keys(patch.updates)) {
1752
+ prior[key] = wf[key];
1753
+ }
1754
+ return { op: "updateWorkflowMeta", workflow: patch.workflow, updates: prior };
1755
+ }
1756
+ case "setInitialState": {
1757
+ const wf = findWorkflow(doc, patch.workflow);
1758
+ if (!wf) return noop();
1759
+ return {
1760
+ op: "setInitialState",
1761
+ workflow: patch.workflow,
1762
+ stateCode: wf.initialState
1763
+ };
1764
+ }
1765
+ case "setWorkflowCriterion": {
1766
+ const wf = findWorkflow(doc, patch.workflow);
1767
+ if (!wf) return noop();
1768
+ return wf.criterion ? {
1769
+ op: "setWorkflowCriterion",
1770
+ workflow: patch.workflow,
1771
+ criterion: cloneCriterion(wf.criterion)
1772
+ } : { op: "setWorkflowCriterion", workflow: patch.workflow };
1773
+ }
1774
+ case "addState":
1775
+ return {
1776
+ op: "removeState",
1777
+ workflow: patch.workflow,
1778
+ stateCode: patch.stateCode
1779
+ };
1780
+ case "addTransition": {
1781
+ return { op: "replaceSession", session: cloneSession(doc) };
1782
+ }
1783
+ case "updateTransition": {
1784
+ const t = findTransition(doc, patch.transitionUuid);
1785
+ if (!t) return noop();
1786
+ const prior = {};
1787
+ for (const key of Object.keys(patch.updates)) {
1788
+ prior[key] = t[key];
1789
+ }
1790
+ return {
1791
+ op: "updateTransition",
1792
+ transitionUuid: patch.transitionUuid,
1793
+ updates: prior
1794
+ };
1795
+ }
1796
+ case "removeTransition":
1797
+ case "reorderTransition":
1798
+ return { op: "replaceSession", session: cloneSession(doc) };
1799
+ case "addProcessor":
1800
+ return { op: "replaceSession", session: cloneSession(doc) };
1801
+ case "updateProcessor": {
1802
+ const p = findProcessor(doc, patch.processorUuid);
1803
+ if (!p) return noop();
1804
+ const prior = {};
1805
+ for (const key of Object.keys(patch.updates)) {
1806
+ prior[key] = p[key];
1807
+ }
1808
+ return {
1809
+ op: "updateProcessor",
1810
+ processorUuid: patch.processorUuid,
1811
+ updates: prior
1812
+ };
1813
+ }
1814
+ case "removeProcessor":
1815
+ case "reorderProcessor":
1816
+ return { op: "replaceSession", session: cloneSession(doc) };
1817
+ case "setCriterion": {
1818
+ const prior = readCriterionAt(doc, patch.host, patch.path);
1819
+ return prior === void 0 ? { op: "setCriterion", host: patch.host, path: patch.path } : {
1820
+ op: "setCriterion",
1821
+ host: patch.host,
1822
+ path: patch.path,
1823
+ criterion: cloneCriterion(prior)
1824
+ };
1825
+ }
1826
+ case "setImportMode":
1827
+ return { op: "setImportMode", mode: doc.session.importMode };
1828
+ case "setEntity":
1829
+ return { op: "setEntity", entity: doc.session.entity };
1830
+ case "setEdgeAnchors": {
1831
+ const ptr = doc.meta.ids.transitions[patch.transitionUuid];
1832
+ if (!ptr) return noop();
1833
+ const prior = doc.meta.workflowUi[ptr.workflow]?.edgeAnchors?.[patch.transitionUuid];
1834
+ return {
1835
+ op: "setEdgeAnchors",
1836
+ transitionUuid: patch.transitionUuid,
1837
+ anchors: prior ? { ...prior } : null
1838
+ };
1839
+ }
1840
+ }
1841
+ }
1842
+ function noop() {
1843
+ return { op: "setImportMode", mode: "MERGE" };
1844
+ }
1845
+ function findWorkflow(doc, name) {
1846
+ return doc.session.workflows.find((w) => w.name === name);
1847
+ }
1848
+ function findTransition(doc, transitionUuid) {
1849
+ const ptr = doc.meta.ids.transitions[transitionUuid];
1850
+ if (!ptr) return void 0;
1851
+ const wf = findWorkflow(doc, ptr.workflow);
1852
+ const state = wf?.states[ptr.state];
1853
+ if (!state) return void 0;
1854
+ const ordered = [];
1855
+ for (const [uuid, p] of Object.entries(doc.meta.ids.transitions)) {
1856
+ if (p.workflow === ptr.workflow && p.state === ptr.state) ordered.push(uuid);
1857
+ }
1858
+ const idx = ordered.indexOf(transitionUuid);
1859
+ return state.transitions[idx];
1860
+ }
1861
+ function findProcessor(doc, processorUuid) {
1862
+ const ptr = doc.meta.ids.processors[processorUuid];
1863
+ if (!ptr) return void 0;
1864
+ const t = findTransition(doc, ptr.transitionUuid);
1865
+ if (!t?.processors) return void 0;
1866
+ const ordered = [];
1867
+ for (const [uuid, p] of Object.entries(doc.meta.ids.processors)) {
1868
+ if (p.transitionUuid === ptr.transitionUuid) ordered.push(uuid);
1869
+ }
1870
+ const idx = ordered.indexOf(processorUuid);
1871
+ return t.processors[idx];
1872
+ }
1873
+ function readCriterionAt(doc, host, path) {
1874
+ const wf = findWorkflow(doc, host.workflow);
1875
+ if (!wf) return void 0;
1876
+ let container;
1877
+ if (host.kind === "workflow") {
1878
+ container = wf;
1879
+ } else if (host.kind === "transition" && host.transitionUuid) {
1880
+ const t = findTransition(doc, host.transitionUuid);
1881
+ if (!t) return void 0;
1882
+ container = t;
1883
+ } else {
1884
+ return void 0;
1885
+ }
1886
+ let node = container;
1887
+ for (const seg of path) {
1888
+ if (node === null || node === void 0) return void 0;
1889
+ if (Array.isArray(node)) {
1890
+ node = node[Number(seg)];
1891
+ } else if (typeof node === "object") {
1892
+ node = node[seg];
1893
+ } else {
1894
+ return void 0;
1895
+ }
1896
+ }
1897
+ return node;
1898
+ }
1899
+ function cloneSession(doc) {
1900
+ return structuredClone(doc.session);
1901
+ }
1902
+ function cloneCriterion(c) {
1903
+ return structuredClone(c);
1904
+ }
1905
+
1906
+ // src/migrate/registry.ts
1907
+ var registry = [];
1908
+ function registerMigration(entry) {
1909
+ const dup = registry.find((e) => e.from === entry.from && e.to === entry.to);
1910
+ if (dup) return;
1911
+ registry.push(entry);
1912
+ }
1913
+ function listMigrations() {
1914
+ return registry;
1915
+ }
1916
+ function findMigrationPath(from, to) {
1917
+ if (from === to) return [];
1918
+ const visited = /* @__PURE__ */ new Set();
1919
+ const queue = [
1920
+ { version: from, path: [] }
1921
+ ];
1922
+ while (queue.length > 0) {
1923
+ const head = queue.shift();
1924
+ if (head.version === to) return head.path;
1925
+ if (visited.has(head.version)) continue;
1926
+ visited.add(head.version);
1927
+ for (const entry of registry) {
1928
+ if (entry.from === head.version) {
1929
+ queue.push({ version: entry.to, path: [...head.path, entry] });
1930
+ }
1931
+ }
1932
+ }
1933
+ return null;
1934
+ }
1935
+ function migrateSession(session, from, to) {
1936
+ const path = findMigrationPath(from, to);
1937
+ if (!path) {
1938
+ throw new Error(`No migration path from ${from} to ${to}`);
1939
+ }
1940
+ return path.reduce((s, entry) => entry.migrate(s), session);
1941
+ }
1942
+ registerMigration({ from: "1.0", to: "1.0", migrate: (s) => s });
1943
+ // Annotate the CommonJS export names for ESM import in node:
1944
+ 0 && (module.exports = {
1945
+ ArrayCriterionSchema,
1946
+ CriterionSchema,
1947
+ ExecutionModeSchema,
1948
+ ExportPayloadSchema,
1949
+ ExternalizedProcessorSchema,
1950
+ FunctionConfigSchema,
1951
+ FunctionCriterionSchema,
1952
+ GroupCriterionSchema,
1953
+ ImportPayloadSchema,
1954
+ LifecycleCriterionSchema,
1955
+ NAME_REGEX,
1956
+ NameSchema,
1957
+ OPERATOR_TYPES,
1958
+ OperatorEnum,
1959
+ ParseJsonError,
1960
+ ProcessorSchema,
1961
+ ScheduledProcessorSchema,
1962
+ SchemaError,
1963
+ SimpleCriterionSchema,
1964
+ StateSchema,
1965
+ TransitionSchema,
1966
+ WorkflowApiConflictError,
1967
+ WorkflowApiTransportError,
1968
+ WorkflowSchema,
1969
+ applyPatch,
1970
+ applyPatches,
1971
+ assignSyntheticIds,
1972
+ findMigrationPath,
1973
+ idFor,
1974
+ invertPatch,
1975
+ listMigrations,
1976
+ lookupById,
1977
+ migrateSession,
1978
+ mintCriterionIds,
1979
+ normalizeCriterion,
1980
+ normalizeOperatorAlias,
1981
+ normalizeProcessor,
1982
+ normalizeWorkflowInput,
1983
+ outputCriterion,
1984
+ outputFunctionConfig,
1985
+ outputProcessor,
1986
+ outputTransition,
1987
+ outputWorkflow,
1988
+ parseEditorDocument,
1989
+ parseExportPayload,
1990
+ parseImportPayload,
1991
+ prettyStringify,
1992
+ registerMigration,
1993
+ serializeEditorDocument,
1994
+ serializeExportPayload,
1995
+ serializeImportPayload,
1996
+ validateAfterPatch,
1997
+ validateAll,
1998
+ validateExportSchema,
1999
+ validateImportSchema,
2000
+ validateSemantics,
2001
+ validateSession,
2002
+ zodErrorToIssues
2003
+ });
2004
+ //# sourceMappingURL=index.cjs.map