@particle-academy/fancy-flow 0.29.0 → 0.30.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/engine.cjs CHANGED
@@ -3,6 +3,21 @@
3
3
  // src/registry/registry.ts
4
4
  var kinds = /* @__PURE__ */ new Map();
5
5
  var aliases = /* @__PURE__ */ new Map();
6
+ var listeners = /* @__PURE__ */ new Set();
7
+ function registerNodeKind(definition) {
8
+ kinds.set(definition.name, definition);
9
+ for (const alias of definition.aliases ?? []) aliases.set(alias, definition.name);
10
+ notify();
11
+ return () => {
12
+ if (kinds.get(definition.name) === definition) {
13
+ kinds.delete(definition.name);
14
+ for (const alias of definition.aliases ?? []) {
15
+ if (aliases.get(alias) === definition.name) aliases.delete(alias);
16
+ }
17
+ notify();
18
+ }
19
+ };
20
+ }
6
21
  function resolveKindId(id) {
7
22
  if (kinds.has(id)) return id;
8
23
  const canonical = aliases.get(id);
@@ -15,6 +30,104 @@ function getNodeKind(name) {
15
30
  function kindIds(kind) {
16
31
  return [kind.name, ...kind.aliases ?? []];
17
32
  }
33
+ function listNodeKinds(category) {
34
+ const all = Array.from(kinds.values());
35
+ return category ? all.filter((k) => k.category === category) : all;
36
+ }
37
+ function notify() {
38
+ for (const l of listeners) l();
39
+ }
40
+ function defaultConfigFor(kind) {
41
+ const fromKind = kind.defaultConfig ? { ...kind.defaultConfig } : {};
42
+ for (const field of kind.configSchema ?? []) {
43
+ if (fromKind[field.key] !== void 0) continue;
44
+ if ("default" in field && field.default !== void 0) {
45
+ fromKind[field.key] = field.default;
46
+ }
47
+ }
48
+ return fromKind;
49
+ }
50
+ function validateConfig(kind, config) {
51
+ const issues = [];
52
+ for (const field of kind.configSchema ?? []) {
53
+ const value = config[field.key];
54
+ if (field.required && (value === void 0 || value === null || value === "")) {
55
+ issues.push({ key: field.key, message: `${field.label} is required` });
56
+ continue;
57
+ }
58
+ if (value === void 0 || value === null) continue;
59
+ const issue = validateField(field, value);
60
+ if (issue) issues.push({ key: field.key, message: issue });
61
+ }
62
+ return issues;
63
+ }
64
+ function validateField(field, value) {
65
+ switch (field.type) {
66
+ case "text":
67
+ case "textarea":
68
+ case "expression":
69
+ case "credential":
70
+ return typeof value === "string" ? null : `${field.label} must be a string`;
71
+ case "number": {
72
+ if (typeof value !== "number" || !Number.isFinite(value)) return `${field.label} must be a number`;
73
+ if (field.min !== void 0 && value < field.min) return `${field.label} must be >= ${field.min}`;
74
+ if (field.max !== void 0 && value > field.max) return `${field.label} must be <= ${field.max}`;
75
+ return null;
76
+ }
77
+ case "switch":
78
+ return typeof value === "boolean" ? null : `${field.label} must be a boolean`;
79
+ case "select": {
80
+ const allowed = field.options.map((o) => o.value);
81
+ return allowed.includes(String(value)) ? null : `${field.label} must be one of ${allowed.join(", ")}`;
82
+ }
83
+ case "json":
84
+ return null;
85
+ // permissive — just JSON-shaped
86
+ case "repeater": {
87
+ if (!Array.isArray(value)) return `${field.label} must be a list`;
88
+ if (field.minItems !== void 0 && value.length < field.minItems) {
89
+ return `${field.label} needs at least ${field.minItems}`;
90
+ }
91
+ if (field.maxItems !== void 0 && value.length > field.maxItems) {
92
+ return `${field.label} allows at most ${field.maxItems}`;
93
+ }
94
+ for (let i = 0; i < value.length; i++) {
95
+ const row = value[i];
96
+ if (!row || typeof row !== "object" || Array.isArray(row)) {
97
+ return `${field.label} item ${i + 1} must be an object`;
98
+ }
99
+ for (const sub of field.fields) {
100
+ const cell = row[sub.key];
101
+ if (sub.required && (cell === void 0 || cell === null || cell === "")) {
102
+ return `${field.label} item ${i + 1}: ${sub.label} is required`;
103
+ }
104
+ if (cell === void 0 || cell === null) continue;
105
+ const issue = validateField(sub, cell);
106
+ if (issue) return `${field.label} item ${i + 1}: ${issue}`;
107
+ }
108
+ }
109
+ return null;
110
+ }
111
+ case "keyvalue": {
112
+ if (typeof value !== "object" || value === null || Array.isArray(value)) {
113
+ return `${field.label} must be a key/value map`;
114
+ }
115
+ const allowed = field.valueOptions?.map((o) => o.value);
116
+ for (const [k, v] of Object.entries(value)) {
117
+ if (typeof v !== "string") return `${field.label}: "${k}" must be a string`;
118
+ if (allowed && !allowed.includes(v)) {
119
+ return `${field.label}: "${k}" must be one of ${allowed.join(", ")}`;
120
+ }
121
+ }
122
+ return null;
123
+ }
124
+ case "document":
125
+ return null;
126
+ // opaque to fancy-flow — the host's editor owns its shape
127
+ default:
128
+ return null;
129
+ }
130
+ }
18
131
 
19
132
  // src/registry/ports.ts
20
133
  function resolvePortSpec(spec, config) {
@@ -542,7 +655,26 @@ function buildGraph(kindId, testCase) {
542
655
  return { graph: { nodes, edges }, ports: effective };
543
656
  }
544
657
  function deepEqual(a, b) {
545
- return JSON.stringify(a) === JSON.stringify(b);
658
+ if (Object.is(a, b)) return true;
659
+ if (a === null || b === null || typeof a !== "object" || typeof b !== "object") return false;
660
+ if (Array.isArray(a) || Array.isArray(b)) {
661
+ if (!Array.isArray(a) || !Array.isArray(b) || a.length !== b.length) return false;
662
+ return a.every((item, index) => deepEqual(item, b[index]));
663
+ }
664
+ const left = definedEntries(a);
665
+ const right = definedEntries(b);
666
+ const keys = Object.keys(left);
667
+ if (keys.length !== Object.keys(right).length) return false;
668
+ return keys.every(
669
+ (key) => Object.prototype.hasOwnProperty.call(right, key) && deepEqual(left[key], right[key])
670
+ );
671
+ }
672
+ function definedEntries(value) {
673
+ const out = {};
674
+ for (const [key, item] of Object.entries(value)) {
675
+ if (item !== void 0) out[key] = item;
676
+ }
677
+ return out;
546
678
  }
547
679
  function installStubs(stubs) {
548
680
  if (!stubs) return () => {
@@ -752,13 +884,20 @@ exports.PAUSE_PREFIX = PAUSE_PREFIX;
752
884
  exports.checkCapabilities = checkCapabilities;
753
885
  exports.checkRuntimeSupport = checkRuntimeSupport;
754
886
  exports.decodePause = decodePause;
887
+ exports.defaultConfigFor = defaultConfigFor;
755
888
  exports.encodePause = encodePause;
889
+ exports.getNodeKind = getNodeKind;
756
890
  exports.isPause = isPause;
891
+ exports.kindIds = kindIds;
892
+ exports.listNodeKinds = listNodeKinds;
757
893
  exports.pauseForHuman = pauseForHuman;
894
+ exports.registerNodeKind = registerNodeKind;
895
+ exports.resolveKindId = resolveKindId;
758
896
  exports.runCohort = runCohort;
759
897
  exports.runFixtures = runFixtures;
760
898
  exports.runFlow = runFlow;
761
899
  exports.satisfiesRange = satisfiesRange;
900
+ exports.validateConfig = validateConfig;
762
901
  exports.validateFixtureFile = validateFixtureFile;
763
902
  exports.validateNodeManifest = validateNodeManifest;
764
903
  //# sourceMappingURL=engine.cjs.map