slapflow 1.1.0 → 1.2.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.
package/dist/index.js CHANGED
@@ -1,9 +1,6 @@
1
1
  // src/helpers/config/defineConfig.ts
2
2
  var defineConfig = (config) => config;
3
3
 
4
- // src/helpers/trace/cloneData.ts
5
- var cloneData = (data) => ({ ...data });
6
-
7
4
  // src/helpers/trace/createMemoryTraceSink.ts
8
5
  var createMemoryTraceSink = () => {
9
6
  const items = [];
@@ -15,22 +12,9 @@ var createMemoryTraceSink = () => {
15
12
  };
16
13
  };
17
14
 
18
- // src/helpers/errors/slapError.ts
19
- var slapError = (code, message, extras = {}) => ({ code, message, ...extras });
20
-
21
15
  // src/helpers/errors/defineErrorReporter.ts
22
16
  var defineErrorReporter = (handlers) => typeof handlers === "function" ? handlers : handlers.report;
23
17
 
24
- // src/errors.ts
25
- var SyncAsyncError = class extends Error {
26
- slapError;
27
- constructor(error) {
28
- super(error.message);
29
- this.name = "SyncAsyncError";
30
- this.slapError = error;
31
- }
32
- };
33
-
34
18
  // src/helpers/ids/createId.ts
35
19
  var alphabet = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789";
36
20
  var idLength = 12;
@@ -70,7 +54,7 @@ var serializeError = (error) => ({
70
54
  error: error instanceof Error ? error.message : String(error)
71
55
  });
72
56
 
73
- // src/pubSub.ts
57
+ // src/createPubSub.ts
74
58
  var createPubSub = (options = {}) => {
75
59
  const subscribers = /* @__PURE__ */ new Map();
76
60
  const wildcardSubscribers = /* @__PURE__ */ new Map();
@@ -168,6 +152,16 @@ var createPubSub = (options = {}) => {
168
152
  };
169
153
  var PubSub = createPubSub();
170
154
 
155
+ // src/helpers/errors/syncAsyncError.ts
156
+ var SyncAsyncError = class extends Error {
157
+ slapError;
158
+ constructor(error) {
159
+ super(error.message);
160
+ this.name = "SyncAsyncError";
161
+ this.slapError = error;
162
+ }
163
+ };
164
+
171
165
  // src/helpers/actions/coreDelay.ts
172
166
  var coreDelay = async ({
173
167
  props,
@@ -204,10 +198,7 @@ var coreEmit = ({
204
198
  };
205
199
 
206
200
  // src/helpers/actions/coreFail.ts
207
- var coreFail = ({
208
- props,
209
- runtime
210
- }) => runtime.fail(String(props.reason ?? "failed"), props.data);
201
+ var coreFail = ({ props, runtime }) => runtime.fail(String(props.reason ?? "failed"), props.data);
211
202
 
212
203
  // src/helpers/retry/getRetryDelay.ts
213
204
  var getRetryDelay = (attempt, options) => {
@@ -414,10 +405,7 @@ var corePatch = ({
414
405
  };
415
406
 
416
407
  // src/helpers/actions/coreSet.ts
417
- var coreSet = ({
418
- props,
419
- runtime
420
- }) => {
408
+ var coreSet = ({ props, runtime }) => {
421
409
  const path = props.path;
422
410
  if (typeof path === "string") {
423
411
  runtime.set(path, props.value);
@@ -438,13 +426,10 @@ var coreSetData = ({
438
426
  };
439
427
 
440
428
  // src/helpers/actions/coreStop.ts
441
- var coreStop = ({
442
- props,
443
- runtime
444
- }) => runtime.stop(String(props.reason ?? "stopped"));
429
+ var coreStop = ({ props, runtime }) => runtime.stop(String(props.reason ?? "stopped"));
445
430
 
446
- // src/registry/actions.ts
447
- var createActionsRegistry = () => /* @__PURE__ */ new Map([
431
+ // src/helpers/actions/index.ts
432
+ var BUILTIN_ACTIONS = [
448
433
  ["core.noop", coreNoop],
449
434
  ["core.stop", coreStop],
450
435
  ["core.fail", coreFail],
@@ -458,7 +443,8 @@ var createActionsRegistry = () => /* @__PURE__ */ new Map([
458
443
  ["core.emit", coreEmit],
459
444
  ["core.patch", corePatch],
460
445
  ["core.delay", coreDelay]
461
- ]);
446
+ ];
447
+ var BUILTIN_ACTION_NAMES = new Set(BUILTIN_ACTIONS.map(([name]) => name));
462
448
 
463
449
  // src/helpers/conditions/changedCondition.ts
464
450
  var changedCondition = (_args, current, previous) => !Object.is(current, previous);
@@ -552,8 +538,8 @@ var typeIsCondition = (_args, value, expected) => {
552
538
  return expected === "string" || expected === "number" || expected === "boolean" ? typeof value === expected : false;
553
539
  };
554
540
 
555
- // src/registry/conditions.ts
556
- var createConditionsRegistry = () => /* @__PURE__ */ new Map([
541
+ // src/helpers/conditions/index.ts
542
+ var BUILTIN_CONDITIONS = [
557
543
  ["eq", eqCondition],
558
544
  ["neq", neqCondition],
559
545
  ["gt", gtCondition],
@@ -570,7 +556,8 @@ var createConditionsRegistry = () => /* @__PURE__ */ new Map([
570
556
  ["typeIs", typeIsCondition],
571
557
  ["changed", changedCondition],
572
558
  ["cooldownReady", cooldownReadyCondition]
573
- ]);
559
+ ];
560
+ var BUILTIN_CONDITION_NAMES = new Set(BUILTIN_CONDITIONS.map(([name]) => name));
574
561
 
575
562
  // src/helpers/runner/applyResult.ts
576
563
  var applyResult = (result, state, mergeData) => {
@@ -584,6 +571,9 @@ var applyResult = (result, state, mergeData) => {
584
571
  state.events.push(...result.events);
585
572
  };
586
573
 
574
+ // src/helpers/errors/slapError.ts
575
+ var slapError = (code, message, extras = {}) => ({ code, message, ...extras });
576
+
587
577
  // src/helpers/path/resolveValue.ts
588
578
  import { pick as pick3 } from "objwalk";
589
579
 
@@ -1022,7 +1012,7 @@ var executeNext = (item, depth, state, environment) => {
1022
1012
  const id = typeof item === "string" ? item : item.strategy;
1023
1013
  if (typeof item !== "string" && item.when) {
1024
1014
  const runtime = createRuntime(state);
1025
- const condition = evaluateCondition(item.when, environment.conditionsRegistry, { ...state, runtime, strategy: id });
1015
+ const condition = evaluateCondition(item.when, environment.registry.conditions, { ...state, runtime, strategy: id });
1026
1016
  if (!condition.ok) {
1027
1017
  return { status: "failed", error: condition.error, patches: [], events: [] };
1028
1018
  }
@@ -1274,6 +1264,9 @@ var normalizeActionResult = (raw) => {
1274
1264
  };
1275
1265
  };
1276
1266
 
1267
+ // src/helpers/trace/cloneData.ts
1268
+ var cloneData = (data) => ({ ...data });
1269
+
1277
1270
  // src/helpers/runner/pushTrace.ts
1278
1271
  var pushTrace = (state, step, depth, strategyId, strategy, status, props, dataBefore, startedAt, reason) => {
1279
1272
  state.traceSink?.push({
@@ -1456,7 +1449,7 @@ var executeStrategy = (id, extraProps, depth, state, environment) => {
1456
1449
  events: []
1457
1450
  };
1458
1451
  }
1459
- const action = environment.actionsRegistry.get(strategy.fn);
1452
+ const action = environment.registry.actions.get(strategy.fn);
1460
1453
  if (!action) {
1461
1454
  return {
1462
1455
  status: "failed",
@@ -1511,7 +1504,7 @@ var executeStrategy = (id, extraProps, depth, state, environment) => {
1511
1504
  const dataBefore = cloneData(state.data);
1512
1505
  const traceStep = state.stepCounter.current + 1;
1513
1506
  const startedAt = Date.now();
1514
- const condition = evaluateCondition(strategy.when, environment.conditionsRegistry, {
1507
+ const condition = evaluateCondition(strategy.when, environment.registry.conditions, {
1515
1508
  ...state,
1516
1509
  runtime,
1517
1510
  strategy: id
@@ -1825,7 +1818,12 @@ var validateCondition = (expression, strategy, path, conditionsRegistry, errors)
1825
1818
  const [operator, ...args] = expression;
1826
1819
  if (operator === "guard") {
1827
1820
  if (args.length !== 1 || typeof args[0] !== "string") {
1828
- errors.push({ code: "CONDITION_INVALID", message: "Guard reference must be a single string name", strategy, path });
1821
+ errors.push({
1822
+ code: "CONDITION_INVALID",
1823
+ message: "Guard reference must be a single string name",
1824
+ strategy,
1825
+ path
1826
+ });
1829
1827
  }
1830
1828
  return;
1831
1829
  }
@@ -1878,7 +1876,9 @@ var validateNextList = (config, list, path, strategy, conditionsRegistry, errors
1878
1876
  var isGuardRef = (expression) => Array.isArray(expression) && expression.length === 2 && expression[0] === "guard" && typeof expression[1] === "string";
1879
1877
  var resolveRef = (config, name, visiting, path) => {
1880
1878
  if (visiting.includes(name)) {
1881
- return { issue: { code: "GUARD_CYCLE", message: `Guard "${name}" is part of a reference cycle`, guard: name, path } };
1879
+ return {
1880
+ issue: { code: "GUARD_CYCLE", message: `Guard "${name}" is part of a reference cycle`, guard: name, path }
1881
+ };
1882
1882
  }
1883
1883
  const guard = config.guards?.[name];
1884
1884
  if (guard === void 0) {
@@ -2101,10 +2101,10 @@ var createRunCancellation = (source) => {
2101
2101
  };
2102
2102
  };
2103
2103
 
2104
- // src/helpers/runner/createRunner.ts
2104
+ // src/createRunner.ts
2105
2105
  var createRunner = (options = {}) => {
2106
- const actionsRegistry = createActionsRegistry();
2107
- const conditionsRegistry = createConditionsRegistry();
2106
+ const actionsRegistry = new Map(BUILTIN_ACTIONS);
2107
+ const conditionsRegistry = new Map(BUILTIN_CONDITIONS);
2108
2108
  const configRef = {};
2109
2109
  const timeout = options.timeout ?? options.timeoutMs;
2110
2110
  const runnerOptions = timeout === void 0 ? options : { ...options, timeout };
@@ -2114,19 +2114,24 @@ var createRunner = (options = {}) => {
2114
2114
  console.warn("timeoutMs is deprecated; use timeout. It will be removed in a future major release.");
2115
2115
  }
2116
2116
  const environment = {
2117
- actionsRegistry,
2118
- conditionsRegistry,
2117
+ registry: { actions: actionsRegistry, conditions: conditionsRegistry },
2119
2118
  configRef,
2120
2119
  options: runnerOptions,
2121
2120
  mergeData
2122
2121
  };
2123
2122
  const registerAction = (name, action) => {
2123
+ if (BUILTIN_ACTION_NAMES.has(name)) {
2124
+ throw new Error(`Cannot override built-in action "${name}"`);
2125
+ }
2124
2126
  actionsRegistry.set(name, action);
2125
2127
  };
2126
2128
  const registerActions = (items) => {
2127
2129
  Object.entries(items).forEach(([name, action]) => registerAction(name, action));
2128
2130
  };
2129
2131
  const registerCondition = (name, condition) => {
2132
+ if (BUILTIN_CONDITION_NAMES.has(name)) {
2133
+ throw new Error(`Cannot override built-in condition "${name}"`);
2134
+ }
2130
2135
  conditionsRegistry.set(name, condition);
2131
2136
  };
2132
2137
  const registerConditions = (items) => {
@@ -2223,7 +2228,7 @@ var parseDomBinding = (binding, prefix) => {
2223
2228
  return separator <= 0 || separator === source.length - 1 ? void 0 : { selector: source.slice(0, separator), eventType: source.slice(separator + 1) };
2224
2229
  };
2225
2230
 
2226
- // src/flow.ts
2231
+ // src/createFlow.ts
2227
2232
  var busBindingPrefix = "[bus] ";
2228
2233
  var domBindingPrefix = "[dom] ";
2229
2234
  var defaultMaxQueueSize = 50;
@@ -2464,10 +2469,11 @@ var createFlow = (definition, options) => {
2464
2469
  return { runner, start, stop };
2465
2470
  };
2466
2471
 
2467
- // src/ws.ts
2472
+ // src/createWS.ts
2468
2473
  var openState = 1;
2469
2474
  var maxSeenEvents = 1e3;
2470
2475
  var createWS = (options) => {
2476
+ console.warn("[slapflow] createWS is deprecated and will be removed soon. Use createWebSocket instead.");
2471
2477
  const inboundTopics = new Set(options.inboundTopics ?? []);
2472
2478
  const outboundTopics = new Set(options.outboundTopics ?? []);
2473
2479
  const seenEventIds = /* @__PURE__ */ new Set();
@@ -2631,7 +2637,150 @@ var createWS = (options) => {
2631
2637
  return { start, stop, reconnect, status: () => currentStatus };
2632
2638
  };
2633
2639
 
2634
- // src/catchError.ts
2640
+ // src/helpers/parsePayload.ts
2641
+ var parsePayload = (data) => {
2642
+ if (typeof data === "string") {
2643
+ try {
2644
+ return JSON.parse(data);
2645
+ } catch {
2646
+ return data;
2647
+ }
2648
+ }
2649
+ return data;
2650
+ };
2651
+
2652
+ // src/createWebSocket.ts
2653
+ var WebSocketConnection = class {
2654
+ constructor(options) {
2655
+ this.options = options;
2656
+ }
2657
+ options;
2658
+ _socket;
2659
+ _retryTimer;
2660
+ _retryAttempt = 0;
2661
+ _queue = [];
2662
+ _started = false;
2663
+ _status = "idle";
2664
+ start() {
2665
+ if (!this._started) {
2666
+ this._started = true;
2667
+ this.connect();
2668
+ }
2669
+ }
2670
+ stop() {
2671
+ this._started = false;
2672
+ this.clearTimer();
2673
+ this.clearQueue();
2674
+ const current = this._socket;
2675
+ this._socket = void 0;
2676
+ current?.close();
2677
+ this.setStatus("stopped");
2678
+ }
2679
+ reconnect() {
2680
+ if (this._started) {
2681
+ this.clearTimer();
2682
+ const current = this._socket;
2683
+ this._socket = void 0;
2684
+ current?.close();
2685
+ this.connect();
2686
+ }
2687
+ }
2688
+ status() {
2689
+ return this._status;
2690
+ }
2691
+ send(data) {
2692
+ if (this._socket?.readyState === WebSocket.OPEN) {
2693
+ this._socket.send(data);
2694
+ } else {
2695
+ this._queue.push(data);
2696
+ }
2697
+ }
2698
+ connect() {
2699
+ if (!this._started || this._socket) {
2700
+ return;
2701
+ }
2702
+ const { url, protocols } = this.options;
2703
+ const current = new WebSocket(url, protocols ?? []);
2704
+ this._socket = current;
2705
+ this.setStatus("connecting");
2706
+ current.addEventListener("open", () => this.onOpen(current));
2707
+ current.addEventListener("message", (event) => this.onMessage(current, event));
2708
+ current.addEventListener("close", (event) => this.onClose(current, event));
2709
+ current.addEventListener("error", (event) => this.onError(current, event));
2710
+ }
2711
+ onOpen(current) {
2712
+ if (this._socket === current) {
2713
+ this._retryAttempt = 0;
2714
+ this.setStatus("connected");
2715
+ this.flush();
2716
+ this.dispatch("open", { url: this.options.url });
2717
+ }
2718
+ }
2719
+ onMessage(current, event) {
2720
+ if (this._socket === current) {
2721
+ this.dispatch("message", parsePayload(event.data));
2722
+ }
2723
+ }
2724
+ onClose(current, event) {
2725
+ if (this._socket === current) {
2726
+ this._socket = void 0;
2727
+ this.dispatch("close", event);
2728
+ if (this._started) {
2729
+ this.retry();
2730
+ }
2731
+ }
2732
+ }
2733
+ onError(current, event) {
2734
+ if (this._socket === current) {
2735
+ this.dispatch("error", serializeError(event.error));
2736
+ }
2737
+ }
2738
+ dispatch(topic, parsed) {
2739
+ const { bus, origin } = this.options;
2740
+ bus.dispatch({
2741
+ id: createId(),
2742
+ topic,
2743
+ occurredAt: Date.now(),
2744
+ ...origin ? { origin } : {},
2745
+ parsed,
2746
+ serialized: JSON.stringify(parsed)
2747
+ });
2748
+ }
2749
+ flush() {
2750
+ if (this._socket && this._socket.readyState === WebSocket.OPEN) {
2751
+ for (const message of this._queue) {
2752
+ this._socket.send(message);
2753
+ }
2754
+ this.clearQueue();
2755
+ }
2756
+ }
2757
+ retry() {
2758
+ this.setStatus("reconnecting");
2759
+ this._retryTimer = setTimeout(
2760
+ () => {
2761
+ this._retryTimer = void 0;
2762
+ this.connect();
2763
+ },
2764
+ getRetryDelay(this._retryAttempt, this.options.reconnect ?? {})
2765
+ );
2766
+ this._retryAttempt = this._retryAttempt + 1;
2767
+ }
2768
+ setStatus(status) {
2769
+ this._status = status || "idle";
2770
+ }
2771
+ clearQueue() {
2772
+ this._queue = [];
2773
+ }
2774
+ clearTimer() {
2775
+ if (this._retryTimer) {
2776
+ clearTimeout(this._retryTimer);
2777
+ }
2778
+ this._retryTimer = void 0;
2779
+ }
2780
+ };
2781
+ var createWebSocket = (options) => new WebSocketConnection(options);
2782
+
2783
+ // src/helpers/catchError.ts
2635
2784
  var catchError = (callback) => new Promise((resolve, reject) => {
2636
2785
  try {
2637
2786
  resolve(callback());
@@ -2640,14 +2789,17 @@ var catchError = (callback) => new Promise((resolve, reject) => {
2640
2789
  }
2641
2790
  });
2642
2791
  export {
2792
+ BUILTIN_ACTIONS,
2793
+ BUILTIN_ACTION_NAMES,
2794
+ BUILTIN_CONDITIONS,
2795
+ BUILTIN_CONDITION_NAMES,
2643
2796
  PubSub,
2644
2797
  catchError,
2645
- createActionsRegistry,
2646
- createConditionsRegistry,
2647
2798
  createFlow,
2648
2799
  createMemoryTraceSink,
2649
2800
  createPubSub,
2650
2801
  createWS,
2802
+ createWebSocket,
2651
2803
  defineConfig,
2652
2804
  defineErrorReporter
2653
2805
  };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "slapflow",
3
- "version": "1.1.0",
3
+ "version": "1.2.1",
4
4
  "description": "Chain actions behavior runtime",
5
5
  "license": "ISC",
6
6
  "author": "Sergey Khalilov",
@@ -57,7 +57,9 @@
57
57
  "test": "npm run typecheck && vitest run",
58
58
  "build": "tsup src/index.ts --format esm,cjs --dts --clean",
59
59
  "pack:check": "npm pack --dry-run",
60
- "prepublishOnly": "npm test && npm run build"
60
+ "prepublishOnly": "npm test && npm run build",
61
+ "format": "prettier --write .",
62
+ "check": "prettier --check ."
61
63
  },
62
64
  "devDependencies": {
63
65
  "@types/node": "^26.1.0",