@fluidgrids/workflows 0.0.0-stage → 2026.1027.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (60) hide show
  1. package/LICENSE +9 -0
  2. package/README.md +187 -2
  3. package/dist/assemble-BJo9TECS.d.cts +569 -0
  4. package/dist/assemble-DMOuaxRI.d.ts +569 -0
  5. package/dist/chunk-265J2FCH.cjs +33 -0
  6. package/dist/chunk-27HASETB.cjs +250 -0
  7. package/dist/chunk-4DPEGDPW.js +74 -0
  8. package/dist/chunk-5LUIDMF4.js +42 -0
  9. package/dist/chunk-AU3YW7FZ.cjs +279 -0
  10. package/dist/chunk-BWSLWF2E.js +722 -0
  11. package/dist/chunk-BYJ7QTLY.cjs +76 -0
  12. package/dist/chunk-G4UEPUGO.cjs +46 -0
  13. package/dist/chunk-GO34UHSY.js +95 -0
  14. package/dist/chunk-IZQNUNSU.cjs +931 -0
  15. package/dist/chunk-KUTMY2IO.cjs +745 -0
  16. package/dist/chunk-LMX35GNQ.js +893 -0
  17. package/dist/chunk-LRM6JXJA.cjs +181 -0
  18. package/dist/chunk-LSKSBVL4.cjs +102 -0
  19. package/dist/chunk-LUXCXFOE.cjs +269 -0
  20. package/dist/chunk-PL73GJR4.js +174 -0
  21. package/dist/chunk-REV7ZOA4.js +170 -0
  22. package/dist/chunk-RRQ5HOBF.cjs +185 -0
  23. package/dist/chunk-S34NJE3A.js +262 -0
  24. package/dist/chunk-SRQRLWOS.js +31 -0
  25. package/dist/chunk-SS4LT2IP.js +268 -0
  26. package/dist/chunk-X4HKU3MX.js +244 -0
  27. package/dist/codegen/index.cjs +18 -0
  28. package/dist/codegen/index.d.cts +15 -0
  29. package/dist/codegen/index.d.ts +15 -0
  30. package/dist/codegen/index.js +5 -0
  31. package/dist/expressions-DhHaqyar.d.cts +62 -0
  32. package/dist/expressions-DhHaqyar.d.ts +62 -0
  33. package/dist/hash-CZkTXPCX.d.cts +15 -0
  34. package/dist/hash-CZkTXPCX.d.ts +15 -0
  35. package/dist/index.cjs +117 -0
  36. package/dist/index.d.cts +27 -0
  37. package/dist/index.d.ts +27 -0
  38. package/dist/index.js +4 -0
  39. package/dist/ir/index.cjs +1244 -0
  40. package/dist/ir/index.d.cts +828 -0
  41. package/dist/ir/index.d.ts +828 -0
  42. package/dist/ir/index.js +1120 -0
  43. package/dist/lower-DcOY8ux0.d.cts +49 -0
  44. package/dist/lower-Dw5grNrI.d.ts +49 -0
  45. package/dist/manifest-Bp9ZJlav.d.ts +60 -0
  46. package/dist/manifest-Duf-gHdO.d.cts +60 -0
  47. package/dist/node-evaluator-2GTUZVPK.cjs +17 -0
  48. package/dist/node-evaluator-RMYEWCXZ.js +8 -0
  49. package/dist/quickjs/index.cjs +92 -0
  50. package/dist/quickjs/index.d.cts +18 -0
  51. package/dist/quickjs/index.d.ts +18 -0
  52. package/dist/quickjs/index.js +90 -0
  53. package/dist/roundtrip/index.cjs +1375 -0
  54. package/dist/roundtrip/index.d.cts +287 -0
  55. package/dist/roundtrip/index.d.ts +287 -0
  56. package/dist/roundtrip/index.js +1328 -0
  57. package/dist/types-Dr2Fn1Ax.d.cts +225 -0
  58. package/dist/types-Dr2Fn1Ax.d.ts +225 -0
  59. package/package.json +117 -4
  60. package/schema/ir-v2.schema.json +655 -0
@@ -0,0 +1,745 @@
1
+ 'use strict';
2
+
3
+ var chunkG4UEPUGO_cjs = require('./chunk-G4UEPUGO.cjs');
4
+ var chunkRRQ5HOBF_cjs = require('./chunk-RRQ5HOBF.cjs');
5
+ var chunkLRM6JXJA_cjs = require('./chunk-LRM6JXJA.cjs');
6
+
7
+ // src/builder/descriptors.ts
8
+ function op(nodeType, version, operation) {
9
+ return {
10
+ kind: "operation",
11
+ nodeType,
12
+ ...version !== void 0 ? { version } : {},
13
+ ...operation !== void 0 ? { operation } : {}
14
+ };
15
+ }
16
+ function trigger(nodeType, version, operation) {
17
+ return { ...op(nodeType, version, operation), role: "trigger" };
18
+ }
19
+ function isOperation(value) {
20
+ return typeof value === "object" && value !== null && value.kind === "operation" && typeof value.nodeType === "string";
21
+ }
22
+ var triggers = {
23
+ api: trigger("internal:api"),
24
+ manual: trigger("internal:api"),
25
+ schedule: trigger("internal:schedule"),
26
+ webhook: trigger("internal:webhook"),
27
+ form: trigger("internal:form"),
28
+ errorTrigger: trigger("internal:error-trigger"),
29
+ workflowCall: trigger("internal:workflow-call"),
30
+ rssFeed: trigger("internal:rss-feed"),
31
+ httpPoll: trigger("internal:http-poll"),
32
+ databasePoll: trigger("internal:database-poll"),
33
+ emailImap: trigger("internal:email-imap"),
34
+ fileUpload: trigger("internal:file-upload"),
35
+ recordChange: trigger("internal:record-change"),
36
+ stateChange: trigger("internal:state-change")
37
+ };
38
+ var internalNodes = {
39
+ merge: op("internal:merge"),
40
+ if: op("internal:if"),
41
+ switch: op("internal:switch"),
42
+ loop: op("internal:loop"),
43
+ set: op("internal:set"),
44
+ filter: op("internal:filter"),
45
+ aggregate: op("internal:aggregate"),
46
+ wait: op("internal:wait"),
47
+ noop: op("internal:noop"),
48
+ code: op("internal:code"),
49
+ http: op("internal:http"),
50
+ transform: op("internal:transform"),
51
+ validate: op("internal:validate"),
52
+ log: op("internal:log"),
53
+ error: op("internal:error"),
54
+ subworkflow: op("internal:subworkflow"),
55
+ compareDatasets: op("internal:compare-datasets"),
56
+ splitOut: op("internal:split-out"),
57
+ limit: op("internal:limit"),
58
+ dedupe: op("internal:dedupe"),
59
+ sort: op("internal:sort"),
60
+ dataTable: op("internal:datatable"),
61
+ respondToWebhook: op("internal:respond-to-webhook"),
62
+ sendAndWait: op("internal:send-and-wait"),
63
+ codeWorkflow: op("internal:code-workflow"),
64
+ waitForSignal: op("internal:wait-for-signal")
65
+ };
66
+ var nodes = {
67
+ internal: internalNodes,
68
+ /**
69
+ * Any node type, including ones the registry snapshot does not know. Typed
70
+ * as JSON in and out.
71
+ */
72
+ custom(nodeType, operation, version) {
73
+ return op(nodeType, version, operation);
74
+ }
75
+ };
76
+
77
+ // src/builder/workflow.ts
78
+ var RECORDING_FORMAT = "fluidgrids.tier1-recording";
79
+ var RECORDING_FORMAT_VERSION = 1;
80
+ var WORKFLOW_MARKER = "fluidgrids.tier1-workflow";
81
+ function edge(from, options = {}) {
82
+ return { kind: "edge", from, ...options };
83
+ }
84
+ function isTier1Workflow(value) {
85
+ return chunkLRM6JXJA_cjs.isRecord(value) && value["__fluidgrids"] === WORKFLOW_MARKER && chunkLRM6JXJA_cjs.isRecord(value["recording"]);
86
+ }
87
+ function deepMerge(base, extra, path) {
88
+ for (const [key, value] of Object.entries(extra)) {
89
+ const existing = base[key];
90
+ if (chunkLRM6JXJA_cjs.isRecord(existing) && chunkLRM6JXJA_cjs.isRecord(value)) {
91
+ deepMerge(existing, value, `${path}.${key}`);
92
+ } else if (existing !== void 0) {
93
+ throw new chunkRRQ5HOBF_cjs.BuilderError(
94
+ `extra sets ${path}.${key}, which the builder already wrote; use the typed option`
95
+ );
96
+ } else {
97
+ base[key] = lowerValue(value, `${path}.${key}`);
98
+ }
99
+ }
100
+ }
101
+ function lowerValue(value, path) {
102
+ const model = chunkRRQ5HOBF_cjs.refModelOf(value);
103
+ if (model) return chunkLRM6JXJA_cjs.formatRefTemplate(model);
104
+ if (value === null || typeof value === "string" || typeof value === "boolean") return value;
105
+ if (typeof value === "number") {
106
+ if (!Number.isFinite(value)) throw new chunkRRQ5HOBF_cjs.BuilderError(`${path} is not a finite number`);
107
+ return value;
108
+ }
109
+ if (Array.isArray(value))
110
+ return value.map((item, index) => lowerValue(item, `${path}[${index}]`));
111
+ if (chunkRRQ5HOBF_cjs.isPredicate(value)) {
112
+ throw new chunkRRQ5HOBF_cjs.BuilderError(`${path} is a condition; conditions belong in wf.if({ condition })`);
113
+ }
114
+ if (chunkLRM6JXJA_cjs.isRecord(value)) {
115
+ const proto = Object.getPrototypeOf(value);
116
+ if (proto !== Object.prototype && proto !== null) {
117
+ throw new chunkRRQ5HOBF_cjs.BuilderError(`${path} is not a plain object`);
118
+ }
119
+ const out = {};
120
+ for (const [key, child] of Object.entries(value)) {
121
+ if (child === void 0) continue;
122
+ out[key] = lowerValue(child, `${path}.${key}`);
123
+ }
124
+ return out;
125
+ }
126
+ throw new chunkRRQ5HOBF_cjs.BuilderError(`${path} cannot be stored in a workflow definition (${typeof value})`);
127
+ }
128
+ function lowerObject(value, path) {
129
+ const lowered = lowerValue(value, path);
130
+ if (!chunkLRM6JXJA_cjs.isRecord(lowered)) throw new chunkRRQ5HOBF_cjs.BuilderError(`${path} must be an object`);
131
+ return lowered;
132
+ }
133
+ function slugify(text) {
134
+ const slug = text.toLowerCase().replace(/[^a-z0-9]+/g, "-").replace(/^-+|-+$/g, "").slice(0, 50);
135
+ return /^[a-z]/.test(slug) ? slug : `node-${slug || "x"}`;
136
+ }
137
+ function handleRef(nodeId, handle) {
138
+ return { kind: "handle", nodeId, handle };
139
+ }
140
+ function nodeRef(id) {
141
+ return {
142
+ kind: "node",
143
+ id,
144
+ // An untyped reference serves every output type; the static type comes from the operation.
145
+ output: chunkRRQ5HOBF_cjs.makeRef({ kind: "node", nodeId: id }),
146
+ handle: (name) => handleRef(id, name)
147
+ };
148
+ }
149
+ function isNodeRef(value) {
150
+ return chunkLRM6JXJA_cjs.isRecord(value) && value["kind"] === "node" && typeof value["id"] === "string";
151
+ }
152
+ function isHandleRef(value) {
153
+ return chunkLRM6JXJA_cjs.isRecord(value) && value["kind"] === "handle" && typeof value["nodeId"] === "string";
154
+ }
155
+ function isEdgeSpec(value) {
156
+ return chunkLRM6JXJA_cjs.isRecord(value) && value["kind"] === "edge" && "from" in value;
157
+ }
158
+ function sourceOf(from) {
159
+ if (typeof from === "string") return { source: from };
160
+ if (isHandleRef(from)) return { source: from.nodeId, sourceHandle: from.handle };
161
+ if (isNodeRef(from)) return { source: from.id };
162
+ throw new chunkRRQ5HOBF_cjs.BuilderError("after takes node references, handles, node ids or edge(\u2026) entries");
163
+ }
164
+ function defaultEdgeId(source, target, sourceHandle, targetHandle) {
165
+ return ["e", source, target, sourceHandle, targetHandle].filter((part) => Boolean(part)).join("-");
166
+ }
167
+ var LIMIT_UNITS = {
168
+ s: "seconds",
169
+ m: "minutes",
170
+ h: "hours",
171
+ d: "days",
172
+ w: "weeks"
173
+ };
174
+ function limitWait(limit) {
175
+ if (typeof limit === "string") {
176
+ const match = /^([1-9][0-9]*)([smhdw])$/.exec(limit);
177
+ const unit = match?.[2] !== void 0 ? LIMIT_UNITS[match[2]] : void 0;
178
+ if (!match?.[1] || !unit)
179
+ throw new chunkRRQ5HOBF_cjs.BuilderError(`limit "${limit}" must look like 30s, 15m, 2h, 3d or 1w`);
180
+ return { amount: Number(match[1]), unit };
181
+ }
182
+ return { amount: limit.amount, unit: limit.unit };
183
+ }
184
+ var WorkflowBuilder = class {
185
+ constructor(autoIds) {
186
+ this.autoIds = autoIds;
187
+ }
188
+ autoIds;
189
+ /** `$json`: the current item's JSON. */
190
+ json = chunkRRQ5HOBF_cjs.makeRef({ kind: "json" });
191
+ /** `$vars`: workflow variables. */
192
+ vars = chunkRRQ5HOBF_cjs.makeRef({ kind: "vars" });
193
+ /** `$now`. */
194
+ now = chunkRRQ5HOBF_cjs.makeRef({ kind: "now" });
195
+ /** `$execution`: run metadata. */
196
+ execution = chunkRRQ5HOBF_cjs.makeRef({ kind: "execution" });
197
+ nodes = [];
198
+ edges = [];
199
+ notes = [];
200
+ ids = /* @__PURE__ */ new Set();
201
+ edgeIds = /* @__PURE__ */ new Set();
202
+ edgeOrder;
203
+ /** A reference to a node by id, for nodes declared later in the source. */
204
+ ref(id) {
205
+ return nodeRef(id);
206
+ }
207
+ /** A trigger node. */
208
+ trigger(operation, options = {}) {
209
+ return this.add(operation, options, {});
210
+ }
211
+ /** A store or internal node. */
212
+ step(operation, options = {}) {
213
+ return this.add(operation, options, {});
214
+ }
215
+ /** `internal:if`. */
216
+ if(options) {
217
+ const { condition, ...rest } = options;
218
+ const sugar = {};
219
+ if (condition !== void 0) {
220
+ if (typeof condition === "string") sugar["condition"] = condition;
221
+ else if (chunkRRQ5HOBF_cjs.isPredicate(condition)) sugar["conditionTree"] = chunkRRQ5HOBF_cjs.conditionTreeOf(condition);
222
+ else
223
+ throw new chunkRRQ5HOBF_cjs.BuilderError(
224
+ "condition takes a comparison such as x.output.total.gte(500), and(), or() or a string"
225
+ );
226
+ }
227
+ const ref = this.add(internalNodes.if, rest, sugar);
228
+ return { ...ref, true: ref.handle("true"), false: ref.handle("false") };
229
+ }
230
+ /** `internal:switch`. */
231
+ switch(options) {
232
+ const { value, cases, ...rest } = options;
233
+ const sugar = {};
234
+ if (value !== void 0) sugar["switchValue"] = value;
235
+ if (cases !== void 0) sugar["cases"] = cases;
236
+ const ref = this.add(internalNodes.switch, rest, sugar);
237
+ return { ...ref, case: (handle) => ref.handle(handle), default: ref.handle("default") };
238
+ }
239
+ /**
240
+ * `internal:loop`. With a body callback, the nodes the callback declares
241
+ * after `batch` form the loop body and the builder adds the return edge from
242
+ * the node the callback returns.
243
+ */
244
+ loop(options, body) {
245
+ const { items, batchSize, ...rest } = options;
246
+ const sugar = {};
247
+ if (items !== void 0) sugar["sourceArray"] = items;
248
+ if (batchSize !== void 0) sugar["batchSize"] = batchSize;
249
+ const ref = this.add(internalNodes.loop, rest, sugar);
250
+ const loopRef = { ...ref, body: ref.handle("loop"), done: ref.handle("done") };
251
+ if (body) {
252
+ const last = body(loopRef.body);
253
+ if (last !== void 0) this.connect(last, ref.id, {});
254
+ }
255
+ return loopRef;
256
+ }
257
+ /** `internal:merge`. */
258
+ merge(options) {
259
+ const { inputs, mode, ...rest } = options;
260
+ const sugar = {};
261
+ if (mode !== void 0) sugar["mode"] = mode;
262
+ if (inputs !== void 0 && rest.after !== void 0) {
263
+ throw new chunkRRQ5HOBF_cjs.BuilderError("merge takes inputs or after, not both");
264
+ }
265
+ const after = inputs?.map(
266
+ (input, index) => edge(input, { targetHandle: `input-${index + 1}` })
267
+ );
268
+ return this.add(internalNodes.merge, after ? { ...rest, after } : rest, sugar);
269
+ }
270
+ /** `internal:wait`. */
271
+ wait(options) {
272
+ return this.add(internalNodes.wait, options, {});
273
+ }
274
+ /** `internal:wait-for-signal` (IR v2). */
275
+ waitForSignal(options) {
276
+ const { name, limit, ...rest } = options;
277
+ const sugar = {};
278
+ if (name !== void 0) sugar["signalName"] = name;
279
+ if (limit !== void 0) sugar["limitWait"] = limitWait(limit);
280
+ const ref = this.add(internalNodes.waitForSignal, rest, sugar);
281
+ return { ...ref, received: ref.handle("received"), timedOut: ref.handle("timedOut") };
282
+ }
283
+ /** `internal:send-and-wait`. */
284
+ sendAndWait(options) {
285
+ const ref = this.add(internalNodes.sendAndWait, options, {});
286
+ return {
287
+ ...ref,
288
+ approved: ref.handle("approved"),
289
+ declined: ref.handle("declined"),
290
+ timedOut: ref.handle("timedOut")
291
+ };
292
+ }
293
+ /** `internal:subworkflow`. */
294
+ subworkflow(options) {
295
+ const { key, targetVersion, input, ...rest } = options;
296
+ const sugar = {};
297
+ if (key !== void 0) sugar["workflowKey"] = key;
298
+ if (targetVersion !== void 0) sugar["version"] = targetVersion;
299
+ if (input !== void 0) sugar["inputMapping"] = input;
300
+ return this.add(internalNodes.subworkflow, rest, sugar);
301
+ }
302
+ /** `internal:validate`. */
303
+ validate(options) {
304
+ const ref = this.add(internalNodes.validate, options, {});
305
+ return { ...ref, valid: ref.handle("valid"), invalid: ref.handle("invalid") };
306
+ }
307
+ /** `internal:compare-datasets`. */
308
+ compareDatasets(options) {
309
+ const ref = this.add(internalNodes.compareDatasets, options, {});
310
+ return {
311
+ ...ref,
312
+ inAOnly: ref.handle("inAOnly"),
313
+ inBOnly: ref.handle("inBOnly"),
314
+ same: ref.handle("same"),
315
+ different: ref.handle("different")
316
+ };
317
+ }
318
+ /** `internal:set`. */
319
+ set(options) {
320
+ return this.add(internalNodes.set, options, {});
321
+ }
322
+ /** `internal:filter`. */
323
+ filter(options) {
324
+ return this.add(internalNodes.filter, options, {});
325
+ }
326
+ /** `internal:aggregate`. */
327
+ aggregate(options) {
328
+ return this.add(internalNodes.aggregate, options, {});
329
+ }
330
+ /** `internal:split-out`. */
331
+ splitOut(options) {
332
+ return this.add(internalNodes.splitOut, options, {});
333
+ }
334
+ /** `internal:limit`. */
335
+ limit(options) {
336
+ return this.add(internalNodes.limit, options, {});
337
+ }
338
+ /** `internal:dedupe`. */
339
+ dedupe(options) {
340
+ return this.add(internalNodes.dedupe, options, {});
341
+ }
342
+ /** `internal:sort`. */
343
+ sort(options) {
344
+ return this.add(internalNodes.sort, options, {});
345
+ }
346
+ /** `internal:datatable`. */
347
+ dataTable(options) {
348
+ return this.add(internalNodes.dataTable, options, {});
349
+ }
350
+ /** `internal:http`. */
351
+ http(options) {
352
+ return this.add(internalNodes.http, options, {});
353
+ }
354
+ /** `internal:code`. The code is a string (`args.code`), so it round-trips byte for byte. */
355
+ code(options) {
356
+ return this.add(internalNodes.code, options, {});
357
+ }
358
+ /** `internal:transform`. */
359
+ transform(options) {
360
+ return this.add(internalNodes.transform, options, {});
361
+ }
362
+ /** `internal:log`. */
363
+ log(options) {
364
+ return this.add(internalNodes.log, options, {});
365
+ }
366
+ /** `internal:respond-to-webhook`. */
367
+ respondToWebhook(options) {
368
+ return this.add(internalNodes.respondToWebhook, options, {});
369
+ }
370
+ /** `internal:error` (stop and error). */
371
+ stopAndError(options) {
372
+ return this.add(internalNodes.error, options, {});
373
+ }
374
+ /** `internal:noop`. */
375
+ noop(options) {
376
+ return this.add(internalNodes.noop, options, {});
377
+ }
378
+ /** A sticky note on the canvas. */
379
+ note(content, options) {
380
+ if (typeof content !== "string") throw new chunkRRQ5HOBF_cjs.BuilderError("A note takes Markdown text");
381
+ this.claimId(options.id, "sticky note");
382
+ const near = options.near === void 0 ? void 0 : typeof options.near === "string" ? options.near : options.near.id;
383
+ this.notes.push({
384
+ note: {
385
+ id: options.id,
386
+ type: "sticky-note",
387
+ width: 260,
388
+ height: 170,
389
+ zIndex: -1,
390
+ data: { content, color: options.color ?? "yellow" }
391
+ },
392
+ ...near !== void 0 ? { near } : {}
393
+ });
394
+ }
395
+ /** An edge added outside `after`, for example a loop's return edge. */
396
+ connect(from, to, options = {}) {
397
+ const target = typeof to === "string" ? to : to.id;
398
+ this.addEdge(edge(from, options), target);
399
+ }
400
+ /**
401
+ * Put `data.edges` in this exact order. Each entry names one edge, by id or,
402
+ * for an edge without an id, by its position in declaration order; every edge
403
+ * appears once. The lifter emits this when a stored definition's edge order
404
+ * differs from the order the code declares its edges in.
405
+ */
406
+ edgeOrderIs(order) {
407
+ if (this.edgeOrder) throw new chunkRRQ5HOBF_cjs.BuilderError("edgeOrderIs() may be called once");
408
+ this.edgeOrder = [...order];
409
+ }
410
+ /** The recording, once the callback has returned. */
411
+ finish(key, document) {
412
+ return {
413
+ format: RECORDING_FORMAT,
414
+ formatVersion: RECORDING_FORMAT_VERSION,
415
+ key,
416
+ document,
417
+ nodes: this.nodes,
418
+ edges: this.edges,
419
+ ...this.edgeOrder ? { edgeOrder: this.edgeOrder } : {},
420
+ notes: this.notes
421
+ };
422
+ }
423
+ claimId(id, what) {
424
+ if (typeof id !== "string" || id === "") throw new chunkRRQ5HOBF_cjs.BuilderError(`A ${what} needs an id`);
425
+ if (this.ids.has(id)) throw new chunkRRQ5HOBF_cjs.BuilderError(`Duplicate id "${id}"`);
426
+ this.ids.add(id);
427
+ }
428
+ resolveId(options, operation) {
429
+ if (options.id !== void 0) return options.id;
430
+ if (!this.autoIds) {
431
+ throw new chunkRRQ5HOBF_cjs.BuilderError(`Every node needs an id (a ${operation.nodeType} node has none)`);
432
+ }
433
+ const base = slugify(options.label ?? operation.nodeType.split(/[:/]/).pop() ?? "node");
434
+ let candidate = base;
435
+ for (let suffix = 2; this.ids.has(candidate); suffix++) candidate = `${base}-${suffix}`;
436
+ return candidate;
437
+ }
438
+ addEdge(spec, target) {
439
+ const { source, sourceHandle } = sourceOf(spec.from);
440
+ const id = spec.id === false ? void 0 : spec.id ?? defaultEdgeId(source, target, sourceHandle, spec.targetHandle);
441
+ if (id !== void 0) {
442
+ if (this.edgeIds.has(id)) throw new chunkRRQ5HOBF_cjs.BuilderError(`Duplicate edge id "${id}"`);
443
+ this.edgeIds.add(id);
444
+ }
445
+ const built = id !== void 0 ? { id, source, target } : { source, target };
446
+ if (sourceHandle !== void 0) built["sourceHandle"] = sourceHandle;
447
+ if (spec.targetHandle !== void 0) built["targetHandle"] = spec.targetHandle;
448
+ if (spec.type !== false) built["type"] = spec.type ?? "deletable";
449
+ if (spec.label !== void 0) built["label"] = spec.label;
450
+ const where = `edge ${id ?? `${source}->${target}`}`;
451
+ if (spec.data !== void 0) built["data"] = lowerObject(spec.data, `${where} data`);
452
+ if (spec.mapping !== void 0)
453
+ built["mapping"] = lowerObject(spec.mapping, `${where} mapping`);
454
+ if (spec.extra !== void 0)
455
+ deepMerge(built, lowerObject(spec.extra, `${where} extra`), where);
456
+ this.edges.push(built);
457
+ }
458
+ add(operation, options, sugar) {
459
+ if (!isOperation(operation)) {
460
+ throw new chunkRRQ5HOBF_cjs.BuilderError(
461
+ "The first argument must be a node operation (nodes.*, triggers.* or a generated client)"
462
+ );
463
+ }
464
+ if (typeof options !== "object" || options === null || Array.isArray(options)) {
465
+ throw new chunkRRQ5HOBF_cjs.BuilderError("Node options must be an object");
466
+ }
467
+ const id = this.resolveId(options, operation);
468
+ this.claimId(id, "node");
469
+ const where = `node ${id}`;
470
+ const node = { id, type: operation.nodeType };
471
+ if (options.label !== void 0) node["label"] = options.label;
472
+ const operationName = options.operation === false ? void 0 : options.operation ?? operation.operation;
473
+ if (operationName !== void 0) node["operation_name"] = operationName;
474
+ const version = options.version === false ? void 0 : options.version ?? operation.version;
475
+ if (version !== void 0) node["version"] = version;
476
+ const args = {};
477
+ const explicitArgs = options.args === void 0 ? void 0 : lowerObject(options.args, `${where} args`);
478
+ const sugarArgs = lowerObject(sugar, `${where} args`);
479
+ if (explicitArgs || Object.keys(sugarArgs).length > 0) {
480
+ for (const key of Object.keys(sugarArgs)) {
481
+ if (explicitArgs && Object.hasOwn(explicitArgs, key)) {
482
+ throw new chunkRRQ5HOBF_cjs.BuilderError(`${where}: "${key}" is set twice (by an option and in args)`);
483
+ }
484
+ }
485
+ args["state"] = { ...explicitArgs, ...sugarArgs };
486
+ }
487
+ if (options.connection !== void 0 && options.connections !== void 0) {
488
+ throw new chunkRRQ5HOBF_cjs.BuilderError(`${where}: use connection (names) or connections (ids), not both`);
489
+ }
490
+ const connectionNames = options.connection === void 0 ? void 0 : typeof options.connection === "string" ? [options.connection] : [...options.connection];
491
+ if (connectionNames?.some((name) => typeof name !== "string" || name === "")) {
492
+ throw new chunkRRQ5HOBF_cjs.BuilderError(`${where}: connection names must be non-empty strings`);
493
+ }
494
+ if (options.connections !== void 0)
495
+ args["connections"] = lowerValue(options.connections, `${where} connections`);
496
+ if (options.credentials !== void 0)
497
+ args["credentials"] = lowerValue(options.credentials, `${where} credentials`);
498
+ if (options.context !== void 0)
499
+ args["context"] = lowerObject(options.context, `${where} context`);
500
+ if (Object.keys(args).length > 0 || connectionNames) node["args"] = args;
501
+ const hasSugar = options.retry !== void 0 || options.timeout !== void 0 || options.onError !== void 0;
502
+ if (options.errorConfig !== void 0 && hasSugar) {
503
+ throw new chunkRRQ5HOBF_cjs.BuilderError(`${where}: use errorConfig or retry / timeout / onError, not both`);
504
+ }
505
+ if (options.errorConfig !== void 0) {
506
+ node["errorConfig"] = lowerObject(options.errorConfig, `${where} errorConfig`);
507
+ } else if (hasSugar) {
508
+ const errorConfig = {};
509
+ if (typeof options.onError === "string") errorConfig["onError"] = options.onError;
510
+ else if (options.onError !== void 0) {
511
+ errorConfig["onError"] = "branch";
512
+ errorConfig["errorBranchId"] = options.onError.branch;
513
+ }
514
+ if (options.retry !== void 0)
515
+ errorConfig["retryConfig"] = lowerObject(options.retry, `${where} retry`);
516
+ if (options.timeout !== void 0)
517
+ errorConfig["timeout"] = lowerObject(options.timeout, `${where} timeout`);
518
+ node["errorConfig"] = errorConfig;
519
+ }
520
+ if (options.executionSettings !== void 0) {
521
+ const settings = lowerObject(options.executionSettings, `${where} executionSettings`);
522
+ node["executionSettings"] = settings;
523
+ if (settings["runOn"] !== void 0)
524
+ node["data"] = { executionSettings: { runOn: settings["runOn"] } };
525
+ }
526
+ if (options.disabled !== void 0) node["disabled"] = options.disabled;
527
+ if (options.extra !== void 0)
528
+ deepMerge(node, lowerObject(options.extra, `${where} extra`), where);
529
+ this.nodes.push({
530
+ // Built field by field above from lowered JSON.
531
+ node,
532
+ ...connectionNames ? { connectionNames } : {}
533
+ });
534
+ const after = options.after === void 0 ? [] : Array.isArray(options.after) ? options.after : [options.after];
535
+ for (const entry of after) {
536
+ this.addEdge(isEdgeSpec(entry) ? entry : edge(entry), id);
537
+ }
538
+ return nodeRef(id);
539
+ }
540
+ };
541
+ function defineWorkflow(options, build) {
542
+ if (typeof options !== "object" || options === null || typeof options.key !== "string" || options.key === "") {
543
+ throw new chunkRRQ5HOBF_cjs.BuilderError("defineWorkflow needs a key");
544
+ }
545
+ const builder = new WorkflowBuilder(options.autoIds === true);
546
+ build(builder);
547
+ const document = { key: options.key };
548
+ if (options.name !== void 0) document["name"] = options.name;
549
+ if (options.description !== void 0) document["description"] = options.description;
550
+ if (options.interface !== void 0)
551
+ document["interface"] = lowerObject(options.interface, "interface");
552
+ if (options.code !== void 0) document["code"] = lowerObject(options.code, "code");
553
+ if (options.config !== void 0) document["config"] = lowerObject(options.config, "config");
554
+ if (options.meta !== void 0) document["meta"] = lowerObject(options.meta, "meta");
555
+ if (options.tags !== void 0) document["tags"] = lowerValue(options.tags, "tags");
556
+ if (options.extra !== void 0)
557
+ deepMerge(document, lowerObject(options.extra, "extra"), "workflow");
558
+ const recording = builder.finish(options.key, document);
559
+ return { __fluidgrids: WORKFLOW_MARKER, key: options.key, recording };
560
+ }
561
+
562
+ // src/generated/version.ts
563
+ var SDK_VERSION = "2026.1027.0";
564
+
565
+ // src/builder/assemble.ts
566
+ var LAYOUT_FORMAT = "fluidgrids.layout";
567
+ var LAYOUT_FORMAT_VERSION = 1;
568
+ var UnresolvedConnectionsError = class extends Error {
569
+ constructor(names) {
570
+ super(`Create these connections in the workspace first: ${names.join(", ")}`);
571
+ this.names = names;
572
+ this.name = "UnresolvedConnectionsError";
573
+ }
574
+ names;
575
+ };
576
+ function resolveConnection(resolver, name) {
577
+ if (!resolver) return void 0;
578
+ if (typeof resolver === "function") return resolver(name);
579
+ return Object.hasOwn(resolver, name) ? resolver[name] : void 0;
580
+ }
581
+ function parseLayoutSidecar(value) {
582
+ if (!chunkLRM6JXJA_cjs.isRecord(value) || value["format"] !== LAYOUT_FORMAT || value["formatVersion"] !== LAYOUT_FORMAT_VERSION) {
583
+ throw new chunkRRQ5HOBF_cjs.BuilderError(
584
+ `A layout file must have format "${LAYOUT_FORMAT}" and formatVersion ${LAYOUT_FORMAT_VERSION}`
585
+ );
586
+ }
587
+ const positions = value["positions"];
588
+ if (!chunkLRM6JXJA_cjs.isRecord(positions)) throw new chunkRRQ5HOBF_cjs.BuilderError("A layout file needs a positions object");
589
+ const parsed = {};
590
+ for (const [id, position] of Object.entries(positions)) {
591
+ if (!chunkLRM6JXJA_cjs.isRecord(position) || typeof position["x"] !== "number" || typeof position["y"] !== "number") {
592
+ throw new chunkRRQ5HOBF_cjs.BuilderError(`The layout position of "${id}" must be { x, y }`);
593
+ }
594
+ parsed[id] = { x: position["x"], y: position["y"] };
595
+ }
596
+ const sidecar = {
597
+ format: LAYOUT_FORMAT,
598
+ formatVersion: LAYOUT_FORMAT_VERSION,
599
+ positions: parsed
600
+ };
601
+ if (value["stickyNotes"] !== void 0) {
602
+ if (!Array.isArray(value["stickyNotes"]) || !value["stickyNotes"].every(chunkLRM6JXJA_cjs.isRecord)) {
603
+ throw new chunkRRQ5HOBF_cjs.BuilderError("stickyNotes in a layout file must be an array of objects");
604
+ }
605
+ sidecar.stickyNotes = chunkLRM6JXJA_cjs.cloneJson(value["stickyNotes"]);
606
+ }
607
+ if (value["metaLayout"] !== void 0) {
608
+ if (!chunkLRM6JXJA_cjs.isRecord(value["metaLayout"]))
609
+ throw new chunkRRQ5HOBF_cjs.BuilderError("metaLayout in a layout file must be an object");
610
+ sidecar.metaLayout = chunkLRM6JXJA_cjs.cloneJson(value["metaLayout"]);
611
+ }
612
+ return sidecar;
613
+ }
614
+ function orderEdges(edges, order) {
615
+ if (!order) return edges;
616
+ const indexById = /* @__PURE__ */ new Map();
617
+ edges.forEach((item, index) => {
618
+ if (typeof item.id === "string") indexById.set(item.id, index);
619
+ });
620
+ const indices = order.map(
621
+ (entry) => typeof entry === "number" ? entry : indexById.get(entry) ?? -1
622
+ );
623
+ const valid = indices.length === edges.length && new Set(indices).size === indices.length && indices.every((index) => Number.isInteger(index) && index >= 0 && index < edges.length);
624
+ if (!valid)
625
+ throw new chunkRRQ5HOBF_cjs.BuilderError(
626
+ "edgeOrderIs() must name every edge exactly once, by id or declaration position"
627
+ );
628
+ return indices.map((index) => edges[index]);
629
+ }
630
+ function assembleRecording(recording, options = {}) {
631
+ const missing = /* @__PURE__ */ new Set();
632
+ const nodes2 = recording.nodes.map((recorded) => {
633
+ const node = chunkLRM6JXJA_cjs.cloneJson(recorded.node);
634
+ if (recorded.connectionNames) {
635
+ const refs = [];
636
+ for (const name of recorded.connectionNames) {
637
+ const ref = resolveConnection(options.connections, name);
638
+ if (ref) refs.push({ connectionId: ref.connectionId, adapter: ref.adapter });
639
+ else missing.add(name);
640
+ }
641
+ const args = chunkLRM6JXJA_cjs.isRecord(node["args"]) ? node["args"] : {};
642
+ args["connections"] = refs;
643
+ node["args"] = args;
644
+ }
645
+ return node;
646
+ });
647
+ if (missing.size > 0) throw new UnresolvedConnectionsError([...missing].sort());
648
+ const edges = orderEdges(chunkLRM6JXJA_cjs.cloneJson(recording.edges), recording.edgeOrder);
649
+ const ids = nodes2.map((node) => String(node["id"]));
650
+ const positions = options.layout?.positions ?? {};
651
+ const needsAuto = ids.some((id) => !Object.hasOwn(positions, id));
652
+ const auto = needsAuto ? chunkG4UEPUGO_cjs.autoLayout(ids, edges) : /* @__PURE__ */ new Map();
653
+ for (const node of nodes2) {
654
+ const id = String(node["id"]);
655
+ const position = Object.hasOwn(positions, id) ? positions[id] : auto.get(id);
656
+ if (position) node["position"] = { x: position.x, y: position.y };
657
+ }
658
+ const stickyNotes = options.layout?.stickyNotes !== void 0 || recording.notes.length > 0 ? [...options.layout?.stickyNotes ? chunkLRM6JXJA_cjs.cloneJson(options.layout.stickyNotes) : []] : void 0;
659
+ for (const recorded of recording.notes) {
660
+ const anchor = recorded.near !== void 0 ? nodes2.find((node) => node["id"] === recorded.near) : void 0;
661
+ const anchorPosition = chunkLRM6JXJA_cjs.isRecord(anchor?.["position"]) ? anchor["position"] : { x: 0, y: 0 };
662
+ const x = typeof anchorPosition["x"] === "number" ? anchorPosition["x"] : 0;
663
+ const y = typeof anchorPosition["y"] === "number" ? anchorPosition["y"] : 0;
664
+ stickyNotes?.push({ ...chunkLRM6JXJA_cjs.cloneJson(recorded.note), position: { x, y: y - 220 } });
665
+ }
666
+ const document = chunkLRM6JXJA_cjs.cloneJson(recording.document);
667
+ const extraData = chunkLRM6JXJA_cjs.isRecord(document["data"]) ? document["data"] : {};
668
+ for (const key of ["nodes", "edges", "stickyNotes"]) {
669
+ if (Object.hasOwn(extraData, key))
670
+ throw new chunkRRQ5HOBF_cjs.BuilderError(`extra.data.${key} is written by the builder`);
671
+ }
672
+ delete document["data"];
673
+ const authoring = options.authoring ?? {
674
+ mode: "canvas",
675
+ generator: "sdk-ts",
676
+ generatorVersion: SDK_VERSION
677
+ };
678
+ const out = { schemaVersion: 2 };
679
+ for (const key of ["key", "name", "description", "interface", "code"]) {
680
+ if (document[key] !== void 0) out[key] = document[key];
681
+ }
682
+ out["authoring"] = authoring;
683
+ out["data"] = {
684
+ ...extraData,
685
+ nodes: nodes2,
686
+ edges,
687
+ ...stickyNotes !== void 0 ? { stickyNotes } : {}
688
+ };
689
+ for (const key of ["config"]) if (document[key] !== void 0) out[key] = document[key];
690
+ if (options.pinnedData !== void 0) out["pinnedData"] = chunkLRM6JXJA_cjs.cloneJson(options.pinnedData);
691
+ if (options.layout?.metaLayout !== void 0) {
692
+ const meta = chunkLRM6JXJA_cjs.isRecord(document["meta"]) ? document["meta"] : {};
693
+ if (meta["layout"] !== void 0)
694
+ throw new chunkRRQ5HOBF_cjs.BuilderError("meta.layout comes from the layout file; do not set it in code");
695
+ meta["layout"] = chunkLRM6JXJA_cjs.cloneJson(options.layout.metaLayout);
696
+ document["meta"] = meta;
697
+ }
698
+ for (const [key, value] of Object.entries(document)) {
699
+ if (!Object.hasOwn(out, key) && value !== void 0) out[key] = value;
700
+ }
701
+ return out;
702
+ }
703
+ function toIR(workflow, options = {}) {
704
+ if (!isTier1Workflow(workflow))
705
+ throw new chunkRRQ5HOBF_cjs.BuilderError("toIR takes the value defineWorkflow returned");
706
+ return assembleRecording(workflow.recording, options);
707
+ }
708
+
709
+ // src/builder/collect.ts
710
+ var COLLECTION_FORMAT = "fluidgrids.tier1-collection";
711
+ function collectWorkflowExports(modules) {
712
+ const workflows = [];
713
+ for (const [module, namespace] of modules) {
714
+ for (const exportName of Object.keys(namespace).sort()) {
715
+ const value = namespace[exportName];
716
+ if (isTier1Workflow(value))
717
+ workflows.push({ module, exportName, recording: value.recording });
718
+ }
719
+ }
720
+ const collection = { format: COLLECTION_FORMAT, workflows };
721
+ return JSON.stringify(collection);
722
+ }
723
+
724
+ exports.COLLECTION_FORMAT = COLLECTION_FORMAT;
725
+ exports.LAYOUT_FORMAT = LAYOUT_FORMAT;
726
+ exports.LAYOUT_FORMAT_VERSION = LAYOUT_FORMAT_VERSION;
727
+ exports.RECORDING_FORMAT = RECORDING_FORMAT;
728
+ exports.RECORDING_FORMAT_VERSION = RECORDING_FORMAT_VERSION;
729
+ exports.SDK_VERSION = SDK_VERSION;
730
+ exports.UnresolvedConnectionsError = UnresolvedConnectionsError;
731
+ exports.WorkflowBuilder = WorkflowBuilder;
732
+ exports.assembleRecording = assembleRecording;
733
+ exports.collectWorkflowExports = collectWorkflowExports;
734
+ exports.defaultEdgeId = defaultEdgeId;
735
+ exports.defineWorkflow = defineWorkflow;
736
+ exports.edge = edge;
737
+ exports.internalNodes = internalNodes;
738
+ exports.isOperation = isOperation;
739
+ exports.isTier1Workflow = isTier1Workflow;
740
+ exports.nodes = nodes;
741
+ exports.op = op;
742
+ exports.parseLayoutSidecar = parseLayoutSidecar;
743
+ exports.toIR = toIR;
744
+ exports.trigger = trigger;
745
+ exports.triggers = triggers;