@fluidgrids/workflows 0.0.0-stage → 2026.1027.1

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 +208 -2
  3. package/dist/assemble-CrpUyd_3.d.ts +587 -0
  4. package/dist/assemble-DEf3znGD.d.cts +587 -0
  5. package/dist/chunk-3M34AUIY.cjs +27 -0
  6. package/dist/chunk-5AW745OG.js +25 -0
  7. package/dist/chunk-5LUIDMF4.js +42 -0
  8. package/dist/chunk-6AIAC2FP.cjs +185 -0
  9. package/dist/chunk-6BEW4QD6.js +95 -0
  10. package/dist/chunk-6U446SKY.js +263 -0
  11. package/dist/chunk-7YR7DI7B.cjs +274 -0
  12. package/dist/chunk-CSYBNN3V.cjs +252 -0
  13. package/dist/chunk-E3APHA6O.cjs +199 -0
  14. package/dist/chunk-EEAIM35V.js +748 -0
  15. package/dist/chunk-G4UEPUGO.cjs +46 -0
  16. package/dist/chunk-IHKXLG6I.js +246 -0
  17. package/dist/chunk-MBBKGD7K.js +440 -0
  18. package/dist/chunk-OSPTJCDI.cjs +102 -0
  19. package/dist/chunk-TQF7MYEN.cjs +771 -0
  20. package/dist/chunk-TWOYB4Y7.cjs +76 -0
  21. package/dist/chunk-UB2BHU2M.js +174 -0
  22. package/dist/chunk-UJNHTS6D.js +181 -0
  23. package/dist/chunk-UWQWD7BP.cjs +931 -0
  24. package/dist/chunk-W32BSO3M.js +74 -0
  25. package/dist/chunk-XBIID5DY.js +893 -0
  26. package/dist/chunk-Y7NWR3WH.cjs +450 -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 +1246 -0
  40. package/dist/ir/index.d.cts +838 -0
  41. package/dist/ir/index.d.ts +838 -0
  42. package/dist/ir/index.js +1122 -0
  43. package/dist/lower-CXggJTIo.d.ts +67 -0
  44. package/dist/lower-DAzvsJCo.d.cts +67 -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-CH3XCJND.cjs +17 -0
  48. package/dist/node-evaluator-TULXA3WE.js +8 -0
  49. package/dist/quickjs/index.cjs +46 -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 +44 -0
  53. package/dist/roundtrip/index.cjs +1448 -0
  54. package/dist/roundtrip/index.d.cts +290 -0
  55. package/dist/roundtrip/index.d.ts +290 -0
  56. package/dist/roundtrip/index.js +1397 -0
  57. package/dist/types-Dr2Fn1Ax.d.cts +225 -0
  58. package/dist/types-Dr2Fn1Ax.d.ts +225 -0
  59. package/package.json +143 -4
  60. package/schema/ir-v2.schema.json +655 -0
@@ -0,0 +1,771 @@
1
+ 'use strict';
2
+
3
+ var chunkG4UEPUGO_cjs = require('./chunk-G4UEPUGO.cjs');
4
+ var chunk6AIAC2FP_cjs = require('./chunk-6AIAC2FP.cjs');
5
+ var chunkE3APHA6O_cjs = require('./chunk-E3APHA6O.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 chunkE3APHA6O_cjs.isRecord(value) && value["__fluidgrids"] === WORKFLOW_MARKER && chunkE3APHA6O_cjs.isRecord(value["recording"]);
86
+ }
87
+ function deepMerge(base, extra, path) {
88
+ for (const [key, value] of Object.entries(extra)) {
89
+ const existing = chunkE3APHA6O_cjs.ownValue(base, key);
90
+ if (chunkE3APHA6O_cjs.isRecord(existing) && chunkE3APHA6O_cjs.isRecord(value)) {
91
+ deepMerge(existing, value, `${path}.${key}`);
92
+ } else if (existing !== void 0) {
93
+ throw new chunk6AIAC2FP_cjs.BuilderError(
94
+ `extra sets ${path}.${key}, which the builder already wrote; use the typed option`
95
+ );
96
+ } else {
97
+ chunkE3APHA6O_cjs.setOwn(base, key, lowerValue(value, `${path}.${key}`));
98
+ }
99
+ }
100
+ }
101
+ function lowerValue(value, path) {
102
+ const model = chunk6AIAC2FP_cjs.refModelOf(value);
103
+ if (model) return chunkE3APHA6O_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 chunk6AIAC2FP_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 (chunk6AIAC2FP_cjs.isPredicate(value)) {
112
+ throw new chunk6AIAC2FP_cjs.BuilderError(`${path} is a condition; conditions belong in wf.if({ condition })`);
113
+ }
114
+ if (chunkE3APHA6O_cjs.isRecord(value)) {
115
+ const proto = Object.getPrototypeOf(value);
116
+ if (proto !== Object.prototype && proto !== null) {
117
+ throw new chunk6AIAC2FP_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
+ chunkE3APHA6O_cjs.setOwn(out, key, lowerValue(child, `${path}.${key}`));
123
+ }
124
+ return out;
125
+ }
126
+ throw new chunk6AIAC2FP_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 (!chunkE3APHA6O_cjs.isRecord(lowered)) throw new chunk6AIAC2FP_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: chunk6AIAC2FP_cjs.makeRef({ kind: "node", nodeId: id }),
146
+ handle: (name) => handleRef(id, name)
147
+ };
148
+ }
149
+ function isNodeRef(value) {
150
+ return chunkE3APHA6O_cjs.isRecord(value) && value["kind"] === "node" && typeof value["id"] === "string";
151
+ }
152
+ function isHandleRef(value) {
153
+ return chunkE3APHA6O_cjs.isRecord(value) && value["kind"] === "handle" && typeof value["nodeId"] === "string";
154
+ }
155
+ function isEdgeSpec(value) {
156
+ return chunkE3APHA6O_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 chunk6AIAC2FP_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 chunk6AIAC2FP_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 = chunk6AIAC2FP_cjs.makeRef({ kind: "json" });
191
+ /** `$vars`: workflow variables. */
192
+ vars = chunk6AIAC2FP_cjs.makeRef({ kind: "vars" });
193
+ /** `$now`. */
194
+ now = chunk6AIAC2FP_cjs.makeRef({ kind: "now" });
195
+ /** `$execution`: run metadata. */
196
+ execution = chunk6AIAC2FP_cjs.makeRef({ kind: "execution" });
197
+ nodes = [];
198
+ edges = [];
199
+ notes = [];
200
+ ids = /* @__PURE__ */ new Set();
201
+ /** Ids given explicitly; a second edge with the same explicit id is an error. */
202
+ edgeIds = /* @__PURE__ */ new Set();
203
+ /** Edges whose id is the default, as indices into `edges`, with the unsuffixed default id. */
204
+ defaultIds = [];
205
+ edgeOrder;
206
+ /** A reference to a node by id, for nodes declared later in the source. */
207
+ ref(id) {
208
+ return nodeRef(id);
209
+ }
210
+ /** A trigger node. */
211
+ trigger(operation, options = {}) {
212
+ return this.add(operation, options, {});
213
+ }
214
+ /** A store or internal node. */
215
+ step(operation, options = {}) {
216
+ return this.add(operation, options, {});
217
+ }
218
+ /** `internal:if`. */
219
+ if(options) {
220
+ const { condition, ...rest } = options;
221
+ const sugar = {};
222
+ if (condition !== void 0) {
223
+ if (typeof condition === "string") sugar["condition"] = condition;
224
+ else if (chunk6AIAC2FP_cjs.isPredicate(condition)) sugar["conditionTree"] = chunk6AIAC2FP_cjs.conditionTreeOf(condition);
225
+ else
226
+ throw new chunk6AIAC2FP_cjs.BuilderError(
227
+ "condition takes a comparison such as x.output.total.gte(500), and(), or() or a string"
228
+ );
229
+ }
230
+ const ref = this.add(internalNodes.if, rest, sugar);
231
+ return { ...ref, true: ref.handle("true"), false: ref.handle("false") };
232
+ }
233
+ /** `internal:switch`. */
234
+ switch(options) {
235
+ const { value, cases, ...rest } = options;
236
+ const sugar = {};
237
+ if (value !== void 0) sugar["switchValue"] = value;
238
+ if (cases !== void 0) sugar["cases"] = cases;
239
+ const ref = this.add(internalNodes.switch, rest, sugar);
240
+ return { ...ref, case: (handle) => ref.handle(handle), default: ref.handle("default") };
241
+ }
242
+ /**
243
+ * `internal:loop`. With a body callback, the nodes the callback declares
244
+ * after `batch` form the loop body and the builder adds the return edge from
245
+ * the node the callback returns.
246
+ */
247
+ loop(options, body) {
248
+ const { items, batchSize, ...rest } = options;
249
+ const sugar = {};
250
+ if (items !== void 0) sugar["sourceArray"] = items;
251
+ if (batchSize !== void 0) sugar["batchSize"] = batchSize;
252
+ const ref = this.add(internalNodes.loop, rest, sugar);
253
+ const loopRef = { ...ref, body: ref.handle("loop"), done: ref.handle("done") };
254
+ if (body) {
255
+ const last = body(loopRef.body);
256
+ if (last !== void 0) this.connect(last, ref.id, {});
257
+ }
258
+ return loopRef;
259
+ }
260
+ /** `internal:merge`. */
261
+ merge(options) {
262
+ const { inputs, mode, ...rest } = options;
263
+ const sugar = {};
264
+ if (mode !== void 0) sugar["mode"] = mode;
265
+ if (inputs !== void 0 && rest.after !== void 0) {
266
+ throw new chunk6AIAC2FP_cjs.BuilderError("merge takes inputs or after, not both");
267
+ }
268
+ const after = inputs?.map(
269
+ (input, index) => edge(input, { targetHandle: `input-${index + 1}` })
270
+ );
271
+ return this.add(internalNodes.merge, after ? { ...rest, after } : rest, sugar);
272
+ }
273
+ /** `internal:wait`. */
274
+ wait(options) {
275
+ return this.add(internalNodes.wait, options, {});
276
+ }
277
+ /** `internal:wait-for-signal` (IR v2). */
278
+ waitForSignal(options) {
279
+ const { name, limit, ...rest } = options;
280
+ const sugar = {};
281
+ if (name !== void 0) sugar["signalName"] = name;
282
+ if (limit !== void 0) sugar["limitWait"] = limitWait(limit);
283
+ const ref = this.add(internalNodes.waitForSignal, rest, sugar);
284
+ return { ...ref, received: ref.handle("received"), timedOut: ref.handle("timedOut") };
285
+ }
286
+ /** `internal:send-and-wait`. */
287
+ sendAndWait(options) {
288
+ const ref = this.add(internalNodes.sendAndWait, options, {});
289
+ return {
290
+ ...ref,
291
+ approved: ref.handle("approved"),
292
+ declined: ref.handle("declined"),
293
+ timedOut: ref.handle("timedOut")
294
+ };
295
+ }
296
+ /** `internal:subworkflow`. */
297
+ subworkflow(options) {
298
+ const { key, targetVersion, input, ...rest } = options;
299
+ const sugar = {};
300
+ if (key !== void 0) sugar["workflowKey"] = key;
301
+ if (targetVersion !== void 0) sugar["version"] = targetVersion;
302
+ if (input !== void 0) sugar["inputMapping"] = input;
303
+ return this.add(internalNodes.subworkflow, rest, sugar);
304
+ }
305
+ /** `internal:validate`. */
306
+ validate(options) {
307
+ const ref = this.add(internalNodes.validate, options, {});
308
+ return { ...ref, valid: ref.handle("valid"), invalid: ref.handle("invalid") };
309
+ }
310
+ /** `internal:compare-datasets`. */
311
+ compareDatasets(options) {
312
+ const ref = this.add(internalNodes.compareDatasets, options, {});
313
+ return {
314
+ ...ref,
315
+ inAOnly: ref.handle("inAOnly"),
316
+ inBOnly: ref.handle("inBOnly"),
317
+ same: ref.handle("same"),
318
+ different: ref.handle("different")
319
+ };
320
+ }
321
+ /** `internal:set`. */
322
+ set(options) {
323
+ return this.add(internalNodes.set, options, {});
324
+ }
325
+ /** `internal:filter`. */
326
+ filter(options) {
327
+ return this.add(internalNodes.filter, options, {});
328
+ }
329
+ /** `internal:aggregate`. */
330
+ aggregate(options) {
331
+ return this.add(internalNodes.aggregate, options, {});
332
+ }
333
+ /** `internal:split-out`. */
334
+ splitOut(options) {
335
+ return this.add(internalNodes.splitOut, options, {});
336
+ }
337
+ /** `internal:limit`. */
338
+ limit(options) {
339
+ return this.add(internalNodes.limit, options, {});
340
+ }
341
+ /** `internal:dedupe`. */
342
+ dedupe(options) {
343
+ return this.add(internalNodes.dedupe, options, {});
344
+ }
345
+ /** `internal:sort`. */
346
+ sort(options) {
347
+ return this.add(internalNodes.sort, options, {});
348
+ }
349
+ /** `internal:datatable`. */
350
+ dataTable(options) {
351
+ return this.add(internalNodes.dataTable, options, {});
352
+ }
353
+ /** `internal:http`. */
354
+ http(options) {
355
+ return this.add(internalNodes.http, options, {});
356
+ }
357
+ /** `internal:code`. The code is a string (`args.code`), so it round-trips byte for byte. */
358
+ code(options) {
359
+ return this.add(internalNodes.code, options, {});
360
+ }
361
+ /** `internal:transform`. */
362
+ transform(options) {
363
+ return this.add(internalNodes.transform, options, {});
364
+ }
365
+ /** `internal:log`. */
366
+ log(options) {
367
+ return this.add(internalNodes.log, options, {});
368
+ }
369
+ /** `internal:respond-to-webhook`. */
370
+ respondToWebhook(options) {
371
+ return this.add(internalNodes.respondToWebhook, options, {});
372
+ }
373
+ /** `internal:error` (stop and error). */
374
+ stopAndError(options) {
375
+ return this.add(internalNodes.error, options, {});
376
+ }
377
+ /** `internal:noop`. */
378
+ noop(options) {
379
+ return this.add(internalNodes.noop, options, {});
380
+ }
381
+ /** A sticky note on the canvas. */
382
+ note(content, options) {
383
+ if (typeof content !== "string") throw new chunk6AIAC2FP_cjs.BuilderError("A note takes Markdown text");
384
+ this.claimId(options.id, "sticky note");
385
+ const near = options.near === void 0 ? void 0 : typeof options.near === "string" ? options.near : options.near.id;
386
+ this.notes.push({
387
+ note: {
388
+ id: options.id,
389
+ type: "sticky-note",
390
+ width: 260,
391
+ height: 170,
392
+ zIndex: -1,
393
+ data: { content, color: options.color ?? "yellow" }
394
+ },
395
+ ...near !== void 0 ? { near } : {}
396
+ });
397
+ }
398
+ /** An edge added outside `after`, for example a loop's return edge. */
399
+ connect(from, to, options = {}) {
400
+ const target = typeof to === "string" ? to : to.id;
401
+ this.addEdge(edge(from, options), target);
402
+ }
403
+ /**
404
+ * Put `data.edges` in this exact order. Each entry names one edge, by id or,
405
+ * for an edge without an id, by its position in declaration order; every edge
406
+ * appears once. The lifter emits this when a stored definition's edge order
407
+ * differs from the order the code declares its edges in.
408
+ */
409
+ edgeOrderIs(order) {
410
+ if (this.edgeOrder) throw new chunk6AIAC2FP_cjs.BuilderError("edgeOrderIs() may be called once");
411
+ this.edgeOrder = [...order];
412
+ }
413
+ /** The recording, once the callback has returned. */
414
+ finish(key, document) {
415
+ this.assignDefaultEdgeIds();
416
+ return {
417
+ format: RECORDING_FORMAT,
418
+ formatVersion: RECORDING_FORMAT_VERSION,
419
+ key,
420
+ document,
421
+ nodes: this.nodes,
422
+ edges: this.edges,
423
+ ...this.edgeOrder ? { edgeOrder: this.edgeOrder } : {},
424
+ notes: this.notes
425
+ };
426
+ }
427
+ claimId(id, what) {
428
+ if (typeof id !== "string" || id === "") throw new chunk6AIAC2FP_cjs.BuilderError(`A ${what} needs an id`);
429
+ if (this.ids.has(id)) throw new chunk6AIAC2FP_cjs.BuilderError(`Duplicate id "${id}"`);
430
+ this.ids.add(id);
431
+ }
432
+ resolveId(options, operation) {
433
+ if (options.id !== void 0) return options.id;
434
+ if (!this.autoIds) {
435
+ throw new chunk6AIAC2FP_cjs.BuilderError(`Every node needs an id (a ${operation.nodeType} node has none)`);
436
+ }
437
+ const base = slugify(options.label ?? operation.nodeType.split(/[:/]/).pop() ?? "node");
438
+ let candidate = base;
439
+ for (let suffix = 2; this.ids.has(candidate); suffix++) candidate = `${base}-${suffix}`;
440
+ return candidate;
441
+ }
442
+ /**
443
+ * Give every edge declared without an id its default id. Explicit ids are
444
+ * claimed first, so a default never takes an id the code names; a default that
445
+ * is taken gets `-2`, `-3`, … in declaration order. The result depends only on
446
+ * the builder calls.
447
+ */
448
+ assignDefaultEdgeIds() {
449
+ const taken = new Set(this.edgeIds);
450
+ for (const { index, base } of this.defaultIds) {
451
+ let id = base;
452
+ for (let suffix = 2; taken.has(id); suffix++) id = `${base}-${suffix}`;
453
+ taken.add(id);
454
+ const built = this.edges[index];
455
+ if (built) built.id = id;
456
+ }
457
+ this.defaultIds.length = 0;
458
+ }
459
+ addEdge(spec, target) {
460
+ const { source, sourceHandle } = sourceOf(spec.from);
461
+ const explicit = typeof spec.id === "string" ? spec.id : void 0;
462
+ if (spec.id !== void 0 && spec.id !== false && explicit === void 0)
463
+ throw new chunk6AIAC2FP_cjs.BuilderError("An edge id must be a string or false");
464
+ if (explicit !== void 0) {
465
+ if (this.edgeIds.has(explicit)) throw new chunk6AIAC2FP_cjs.BuilderError(`Duplicate edge id "${explicit}"`);
466
+ this.edgeIds.add(explicit);
467
+ }
468
+ const base = spec.id === void 0 ? defaultEdgeId(source, target, sourceHandle, spec.targetHandle) : void 0;
469
+ const id = explicit ?? base;
470
+ if (base !== void 0) this.defaultIds.push({ index: this.edges.length, base });
471
+ const built = id !== void 0 ? { id, source, target } : { source, target };
472
+ if (sourceHandle !== void 0) built["sourceHandle"] = sourceHandle;
473
+ if (spec.targetHandle !== void 0) built["targetHandle"] = spec.targetHandle;
474
+ if (spec.type !== false) built["type"] = spec.type ?? "deletable";
475
+ if (spec.label !== void 0) built["label"] = spec.label;
476
+ const where = `edge ${id ?? `${source}->${target}`}`;
477
+ if (spec.data !== void 0) built["data"] = lowerObject(spec.data, `${where} data`);
478
+ if (spec.mapping !== void 0)
479
+ built["mapping"] = lowerObject(spec.mapping, `${where} mapping`);
480
+ if (spec.extra !== void 0)
481
+ deepMerge(built, lowerObject(spec.extra, `${where} extra`), where);
482
+ this.edges.push(built);
483
+ }
484
+ add(operation, options, sugar) {
485
+ if (!isOperation(operation)) {
486
+ throw new chunk6AIAC2FP_cjs.BuilderError(
487
+ "The first argument must be a node operation (nodes.*, triggers.* or a generated client)"
488
+ );
489
+ }
490
+ if (typeof options !== "object" || options === null || Array.isArray(options)) {
491
+ throw new chunk6AIAC2FP_cjs.BuilderError("Node options must be an object");
492
+ }
493
+ const id = this.resolveId(options, operation);
494
+ this.claimId(id, "node");
495
+ const where = `node ${id}`;
496
+ const node = { id, type: operation.nodeType };
497
+ if (options.label !== void 0) node["label"] = options.label;
498
+ const operationName = options.operation === false ? void 0 : options.operation ?? operation.operation;
499
+ if (operationName !== void 0) node["operation_name"] = operationName;
500
+ const version = options.version === false ? void 0 : options.version ?? operation.version;
501
+ if (version !== void 0) node["version"] = version;
502
+ const args = {};
503
+ const explicitArgs = options.args === void 0 ? void 0 : lowerObject(options.args, `${where} args`);
504
+ const sugarArgs = lowerObject(sugar, `${where} args`);
505
+ if (explicitArgs || Object.keys(sugarArgs).length > 0) {
506
+ for (const key of Object.keys(sugarArgs)) {
507
+ if (explicitArgs && Object.hasOwn(explicitArgs, key)) {
508
+ throw new chunk6AIAC2FP_cjs.BuilderError(`${where}: "${key}" is set twice (by an option and in args)`);
509
+ }
510
+ }
511
+ args["state"] = { ...explicitArgs, ...sugarArgs };
512
+ }
513
+ if (options.connection !== void 0 && options.connections !== void 0) {
514
+ throw new chunk6AIAC2FP_cjs.BuilderError(`${where}: use connection (names) or connections (ids), not both`);
515
+ }
516
+ const connectionNames = options.connection === void 0 ? void 0 : typeof options.connection === "string" ? [options.connection] : [...options.connection];
517
+ if (connectionNames?.some((name) => typeof name !== "string" || name === "")) {
518
+ throw new chunk6AIAC2FP_cjs.BuilderError(`${where}: connection names must be non-empty strings`);
519
+ }
520
+ if (options.connections !== void 0)
521
+ args["connections"] = lowerValue(options.connections, `${where} connections`);
522
+ if (options.credentials !== void 0)
523
+ args["credentials"] = lowerValue(options.credentials, `${where} credentials`);
524
+ if (options.context !== void 0)
525
+ args["context"] = lowerObject(options.context, `${where} context`);
526
+ if (Object.keys(args).length > 0 || connectionNames) node["args"] = args;
527
+ const hasSugar = options.retry !== void 0 || options.timeout !== void 0 || options.onError !== void 0;
528
+ if (options.errorConfig !== void 0 && hasSugar) {
529
+ throw new chunk6AIAC2FP_cjs.BuilderError(`${where}: use errorConfig or retry / timeout / onError, not both`);
530
+ }
531
+ if (options.errorConfig !== void 0) {
532
+ node["errorConfig"] = lowerObject(options.errorConfig, `${where} errorConfig`);
533
+ } else if (hasSugar) {
534
+ const errorConfig = {};
535
+ if (typeof options.onError === "string") errorConfig["onError"] = options.onError;
536
+ else if (options.onError !== void 0) {
537
+ errorConfig["onError"] = "branch";
538
+ errorConfig["errorBranchId"] = options.onError.branch;
539
+ }
540
+ if (options.retry !== void 0)
541
+ errorConfig["retryConfig"] = lowerObject(options.retry, `${where} retry`);
542
+ if (options.timeout !== void 0)
543
+ errorConfig["timeout"] = lowerObject(options.timeout, `${where} timeout`);
544
+ node["errorConfig"] = errorConfig;
545
+ }
546
+ if (options.executionSettings !== void 0) {
547
+ const settings = lowerObject(options.executionSettings, `${where} executionSettings`);
548
+ node["executionSettings"] = settings;
549
+ if (settings["runOn"] !== void 0)
550
+ node["data"] = { executionSettings: { runOn: settings["runOn"] } };
551
+ }
552
+ if (options.disabled !== void 0) node["disabled"] = options.disabled;
553
+ if (options.extra !== void 0)
554
+ deepMerge(node, lowerObject(options.extra, `${where} extra`), where);
555
+ this.nodes.push({
556
+ // Built field by field above from lowered JSON.
557
+ node,
558
+ ...connectionNames ? { connectionNames } : {}
559
+ });
560
+ const after = options.after === void 0 ? [] : Array.isArray(options.after) ? options.after : [options.after];
561
+ for (const entry of after) {
562
+ this.addEdge(isEdgeSpec(entry) ? entry : edge(entry), id);
563
+ }
564
+ return nodeRef(id);
565
+ }
566
+ };
567
+ function defineWorkflow(options, build) {
568
+ if (typeof options !== "object" || options === null || typeof options.key !== "string" || options.key === "") {
569
+ throw new chunk6AIAC2FP_cjs.BuilderError("defineWorkflow needs a key");
570
+ }
571
+ const builder = new WorkflowBuilder(options.autoIds === true);
572
+ build(builder);
573
+ const document = { key: options.key };
574
+ if (options.name !== void 0) document["name"] = options.name;
575
+ if (options.description !== void 0) document["description"] = options.description;
576
+ if (options.interface !== void 0)
577
+ document["interface"] = lowerObject(options.interface, "interface");
578
+ if (options.code !== void 0) document["code"] = lowerObject(options.code, "code");
579
+ if (options.config !== void 0) document["config"] = lowerObject(options.config, "config");
580
+ if (options.meta !== void 0) document["meta"] = lowerObject(options.meta, "meta");
581
+ if (options.tags !== void 0) document["tags"] = lowerValue(options.tags, "tags");
582
+ if (options.extra !== void 0)
583
+ deepMerge(document, lowerObject(options.extra, "extra"), "workflow");
584
+ const recording = builder.finish(options.key, document);
585
+ return { __fluidgrids: WORKFLOW_MARKER, key: options.key, recording };
586
+ }
587
+
588
+ // src/generated/version.ts
589
+ var SDK_VERSION = "2026.1027.1";
590
+
591
+ // src/builder/assemble.ts
592
+ var LAYOUT_FORMAT = "fluidgrids.layout";
593
+ var LAYOUT_FORMAT_VERSION = 1;
594
+ var UnresolvedConnectionsError = class extends Error {
595
+ constructor(names) {
596
+ super(`Create these connections in the workspace first: ${names.join(", ")}`);
597
+ this.names = names;
598
+ this.name = "UnresolvedConnectionsError";
599
+ }
600
+ names;
601
+ };
602
+ function resolveConnection(resolver, name) {
603
+ if (!resolver) return void 0;
604
+ if (typeof resolver === "function") return resolver(name);
605
+ return Object.hasOwn(resolver, name) ? resolver[name] : void 0;
606
+ }
607
+ function parseLayoutSidecar(value) {
608
+ if (!chunkE3APHA6O_cjs.isRecord(value) || value["format"] !== LAYOUT_FORMAT || value["formatVersion"] !== LAYOUT_FORMAT_VERSION) {
609
+ throw new chunk6AIAC2FP_cjs.BuilderError(
610
+ `A layout file must have format "${LAYOUT_FORMAT}" and formatVersion ${LAYOUT_FORMAT_VERSION}`
611
+ );
612
+ }
613
+ const positions = value["positions"];
614
+ if (!chunkE3APHA6O_cjs.isRecord(positions)) throw new chunk6AIAC2FP_cjs.BuilderError("A layout file needs a positions object");
615
+ const parsed = {};
616
+ for (const [id, position] of Object.entries(positions)) {
617
+ if (!chunkE3APHA6O_cjs.isRecord(position) || typeof position["x"] !== "number" || typeof position["y"] !== "number") {
618
+ throw new chunk6AIAC2FP_cjs.BuilderError(`The layout position of "${id}" must be { x, y }`);
619
+ }
620
+ chunkE3APHA6O_cjs.setOwn(parsed, id, { x: position["x"], y: position["y"] });
621
+ }
622
+ const sidecar = {
623
+ format: LAYOUT_FORMAT,
624
+ formatVersion: LAYOUT_FORMAT_VERSION,
625
+ positions: parsed
626
+ };
627
+ if (value["stickyNotes"] !== void 0) {
628
+ if (!Array.isArray(value["stickyNotes"]) || !value["stickyNotes"].every(chunkE3APHA6O_cjs.isRecord)) {
629
+ throw new chunk6AIAC2FP_cjs.BuilderError("stickyNotes in a layout file must be an array of objects");
630
+ }
631
+ sidecar.stickyNotes = chunkE3APHA6O_cjs.cloneJson(value["stickyNotes"]);
632
+ }
633
+ if (value["metaLayout"] !== void 0) {
634
+ if (!chunkE3APHA6O_cjs.isRecord(value["metaLayout"]))
635
+ throw new chunk6AIAC2FP_cjs.BuilderError("metaLayout in a layout file must be an object");
636
+ sidecar.metaLayout = chunkE3APHA6O_cjs.cloneJson(value["metaLayout"]);
637
+ }
638
+ return sidecar;
639
+ }
640
+ function orderEdges(edges, order) {
641
+ if (!order) return edges;
642
+ const indexById = /* @__PURE__ */ new Map();
643
+ edges.forEach((item, index) => {
644
+ if (typeof item.id === "string") indexById.set(item.id, index);
645
+ });
646
+ const indices = order.map(
647
+ (entry) => typeof entry === "number" ? entry : indexById.get(entry) ?? -1
648
+ );
649
+ const valid = indices.length === edges.length && new Set(indices).size === indices.length && indices.every((index) => Number.isInteger(index) && index >= 0 && index < edges.length);
650
+ if (!valid)
651
+ throw new chunk6AIAC2FP_cjs.BuilderError(
652
+ "edgeOrderIs() must name every edge exactly once, by id or declaration position"
653
+ );
654
+ return indices.map((index) => edges[index]);
655
+ }
656
+ function assembleRecording(recording, options = {}) {
657
+ const missing = /* @__PURE__ */ new Set();
658
+ const nodes2 = recording.nodes.map((recorded) => {
659
+ const node = chunkE3APHA6O_cjs.cloneJson(recorded.node);
660
+ if (recorded.connectionNames) {
661
+ const refs = [];
662
+ for (const name of recorded.connectionNames) {
663
+ const ref = resolveConnection(options.connections, name);
664
+ if (ref) refs.push({ connectionId: ref.connectionId, adapter: ref.adapter });
665
+ else missing.add(name);
666
+ }
667
+ const args = chunkE3APHA6O_cjs.isRecord(node["args"]) ? node["args"] : {};
668
+ args["connections"] = refs;
669
+ node["args"] = args;
670
+ }
671
+ return node;
672
+ });
673
+ if (missing.size > 0) throw new UnresolvedConnectionsError([...missing].sort());
674
+ const edges = orderEdges(chunkE3APHA6O_cjs.cloneJson(recording.edges), recording.edgeOrder);
675
+ const ids = nodes2.map((node) => String(node["id"]));
676
+ const positions = options.layout?.positions ?? {};
677
+ const needsAuto = ids.some((id) => !Object.hasOwn(positions, id));
678
+ const auto = needsAuto ? chunkG4UEPUGO_cjs.autoLayout(ids, edges) : /* @__PURE__ */ new Map();
679
+ for (const node of nodes2) {
680
+ const id = String(node["id"]);
681
+ const position = Object.hasOwn(positions, id) ? positions[id] : auto.get(id);
682
+ if (position) node["position"] = { x: position.x, y: position.y };
683
+ }
684
+ const stickyNotes = options.layout?.stickyNotes !== void 0 || recording.notes.length > 0 ? [...options.layout?.stickyNotes ? chunkE3APHA6O_cjs.cloneJson(options.layout.stickyNotes) : []] : void 0;
685
+ for (const recorded of recording.notes) {
686
+ const anchor = recorded.near !== void 0 ? nodes2.find((node) => node["id"] === recorded.near) : void 0;
687
+ const anchorPosition = chunkE3APHA6O_cjs.isRecord(anchor?.["position"]) ? anchor["position"] : { x: 0, y: 0 };
688
+ const x = typeof anchorPosition["x"] === "number" ? anchorPosition["x"] : 0;
689
+ const y = typeof anchorPosition["y"] === "number" ? anchorPosition["y"] : 0;
690
+ stickyNotes?.push({ ...chunkE3APHA6O_cjs.cloneJson(recorded.note), position: { x, y: y - 220 } });
691
+ }
692
+ const document = chunkE3APHA6O_cjs.cloneJson(recording.document);
693
+ const extraData = chunkE3APHA6O_cjs.isRecord(document["data"]) ? document["data"] : {};
694
+ for (const key of ["nodes", "edges", "stickyNotes"]) {
695
+ if (Object.hasOwn(extraData, key))
696
+ throw new chunk6AIAC2FP_cjs.BuilderError(`extra.data.${key} is written by the builder`);
697
+ }
698
+ delete document["data"];
699
+ const authoring = options.authoring ?? {
700
+ mode: "canvas",
701
+ generator: "sdk-ts",
702
+ generatorVersion: SDK_VERSION
703
+ };
704
+ const out = { schemaVersion: 2 };
705
+ for (const key of ["key", "name", "description", "interface", "code"]) {
706
+ if (document[key] !== void 0) out[key] = document[key];
707
+ }
708
+ out["authoring"] = authoring;
709
+ out["data"] = {
710
+ ...extraData,
711
+ nodes: nodes2,
712
+ edges,
713
+ ...stickyNotes !== void 0 ? { stickyNotes } : {}
714
+ };
715
+ for (const key of ["config"]) if (document[key] !== void 0) out[key] = document[key];
716
+ if (options.pinnedData !== void 0) out["pinnedData"] = chunkE3APHA6O_cjs.cloneJson(options.pinnedData);
717
+ if (options.layout?.metaLayout !== void 0) {
718
+ const meta = chunkE3APHA6O_cjs.isRecord(document["meta"]) ? document["meta"] : {};
719
+ if (meta["layout"] !== void 0)
720
+ throw new chunk6AIAC2FP_cjs.BuilderError("meta.layout comes from the layout file; do not set it in code");
721
+ meta["layout"] = chunkE3APHA6O_cjs.cloneJson(options.layout.metaLayout);
722
+ document["meta"] = meta;
723
+ }
724
+ for (const [key, value] of Object.entries(document)) {
725
+ if (!Object.hasOwn(out, key) && value !== void 0) chunkE3APHA6O_cjs.setOwn(out, key, value);
726
+ }
727
+ return out;
728
+ }
729
+ function toIR(workflow, options = {}) {
730
+ if (!isTier1Workflow(workflow))
731
+ throw new chunk6AIAC2FP_cjs.BuilderError("toIR takes the value defineWorkflow returned");
732
+ return assembleRecording(workflow.recording, options);
733
+ }
734
+
735
+ // src/builder/collect.ts
736
+ var COLLECTION_FORMAT = "fluidgrids.tier1-collection";
737
+ function collectWorkflowExports(modules) {
738
+ const workflows = [];
739
+ for (const [module, namespace] of modules) {
740
+ for (const exportName of Object.keys(namespace).sort()) {
741
+ const value = namespace[exportName];
742
+ if (isTier1Workflow(value))
743
+ workflows.push({ module, exportName, recording: value.recording });
744
+ }
745
+ }
746
+ const collection = { format: COLLECTION_FORMAT, workflows };
747
+ return JSON.stringify(collection);
748
+ }
749
+
750
+ exports.COLLECTION_FORMAT = COLLECTION_FORMAT;
751
+ exports.LAYOUT_FORMAT = LAYOUT_FORMAT;
752
+ exports.LAYOUT_FORMAT_VERSION = LAYOUT_FORMAT_VERSION;
753
+ exports.RECORDING_FORMAT = RECORDING_FORMAT;
754
+ exports.RECORDING_FORMAT_VERSION = RECORDING_FORMAT_VERSION;
755
+ exports.SDK_VERSION = SDK_VERSION;
756
+ exports.UnresolvedConnectionsError = UnresolvedConnectionsError;
757
+ exports.WorkflowBuilder = WorkflowBuilder;
758
+ exports.assembleRecording = assembleRecording;
759
+ exports.collectWorkflowExports = collectWorkflowExports;
760
+ exports.defaultEdgeId = defaultEdgeId;
761
+ exports.defineWorkflow = defineWorkflow;
762
+ exports.edge = edge;
763
+ exports.internalNodes = internalNodes;
764
+ exports.isOperation = isOperation;
765
+ exports.isTier1Workflow = isTier1Workflow;
766
+ exports.nodes = nodes;
767
+ exports.op = op;
768
+ exports.parseLayoutSidecar = parseLayoutSidecar;
769
+ exports.toIR = toIR;
770
+ exports.trigger = trigger;
771
+ exports.triggers = triggers;