slapflow 1.0.2 → 1.2.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.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;
@@ -52,14 +36,35 @@ var isBusEvent = (event) => {
52
36
  return typeof candidate.id === "string" && typeof candidate.topic === "string" && typeof candidate.occurredAt === "number" && typeof candidate.serialized === "string" && "parsed" in candidate && (candidate.origin === void 0 || typeof candidate.origin === "string");
53
37
  };
54
38
 
39
+ // src/helpers/pubSub/matchesTopic.ts
40
+ var matchesTopic = (topic, pattern) => {
41
+ if (!pattern.includes("*")) {
42
+ return topic === pattern;
43
+ }
44
+ const topicParts = topic.split(".");
45
+ const patternParts = pattern.split(".");
46
+ if (topicParts.length !== patternParts.length) {
47
+ return false;
48
+ }
49
+ return patternParts.every((part, index) => part === "*" || part === topicParts[index]);
50
+ };
51
+
55
52
  // src/helpers/pubSub/serializeError.ts
56
53
  var serializeError = (error) => ({
57
54
  error: error instanceof Error ? error.message : String(error)
58
55
  });
59
56
 
60
- // src/pubSub.ts
57
+ // src/createPubSub.ts
61
58
  var createPubSub = (options = {}) => {
62
59
  const subscribers = /* @__PURE__ */ new Map();
60
+ const wildcardSubscribers = /* @__PURE__ */ new Map();
61
+ const runHandler = (event, handler) => {
62
+ try {
63
+ handler(event);
64
+ } catch (error) {
65
+ options.onError?.({ type: "subscriber", event, error });
66
+ }
67
+ };
63
68
  const dispatch = (event) => {
64
69
  let dispatchedEvent;
65
70
  if (!isBusEvent(event)) {
@@ -73,10 +78,13 @@ var createPubSub = (options = {}) => {
73
78
  const handlers = subscribers.get(event.topic);
74
79
  if (handlers) {
75
80
  for (const handler of [...handlers]) {
76
- try {
77
- handler(event);
78
- } catch (error) {
79
- options.onError?.({ type: "subscriber", event, error });
81
+ runHandler(event, handler);
82
+ }
83
+ }
84
+ for (const [pattern, wildcardHandlers] of wildcardSubscribers) {
85
+ if (matchesTopic(event.topic, pattern)) {
86
+ for (const handler of [...wildcardHandlers]) {
87
+ runHandler(event, handler);
80
88
  }
81
89
  }
82
90
  }
@@ -85,23 +93,24 @@ var createPubSub = (options = {}) => {
85
93
  return dispatchedEvent;
86
94
  };
87
95
  const on = (event, handler) => {
88
- const handlers = subscribers.get(event) ?? /* @__PURE__ */ new Set();
89
- const listener = handler;
90
- handlers.add(listener);
91
- subscribers.set(event, handlers);
96
+ const registry = event.includes("*") ? wildcardSubscribers : subscribers;
97
+ const handlers = registry.get(event) ?? /* @__PURE__ */ new Set();
98
+ handlers.add(handler);
99
+ registry.set(event, handlers);
92
100
  return () => off(event, handler);
93
101
  };
94
102
  const off = (event, handler) => {
103
+ const registry = event.includes("*") ? wildcardSubscribers : subscribers;
95
104
  if (handler) {
96
- const handlers = subscribers.get(event);
105
+ const handlers = registry.get(event);
97
106
  if (handlers) {
98
107
  handlers.delete(handler);
99
108
  if (handlers.size === 0) {
100
- subscribers.delete(event);
109
+ registry.delete(event);
101
110
  }
102
111
  }
103
112
  } else {
104
- subscribers.delete(event);
113
+ registry.delete(event);
105
114
  }
106
115
  };
107
116
  const emit = (topic, payload, emitOptions = {}) => {
@@ -143,6 +152,16 @@ var createPubSub = (options = {}) => {
143
152
  };
144
153
  var PubSub = createPubSub();
145
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
+
146
165
  // src/helpers/actions/coreDelay.ts
147
166
  var coreDelay = async ({
148
167
  props,
@@ -179,10 +198,7 @@ var coreEmit = ({
179
198
  };
180
199
 
181
200
  // src/helpers/actions/coreFail.ts
182
- var coreFail = ({
183
- props,
184
- runtime
185
- }) => runtime.fail(String(props.reason ?? "failed"), props.data);
201
+ var coreFail = ({ props, runtime }) => runtime.fail(String(props.reason ?? "failed"), props.data);
186
202
 
187
203
  // src/helpers/retry/getRetryDelay.ts
188
204
  var getRetryDelay = (attempt, options) => {
@@ -389,10 +405,7 @@ var corePatch = ({
389
405
  };
390
406
 
391
407
  // src/helpers/actions/coreSet.ts
392
- var coreSet = ({
393
- props,
394
- runtime
395
- }) => {
408
+ var coreSet = ({ props, runtime }) => {
396
409
  const path = props.path;
397
410
  if (typeof path === "string") {
398
411
  runtime.set(path, props.value);
@@ -413,13 +426,10 @@ var coreSetData = ({
413
426
  };
414
427
 
415
428
  // src/helpers/actions/coreStop.ts
416
- var coreStop = ({
417
- props,
418
- runtime
419
- }) => runtime.stop(String(props.reason ?? "stopped"));
429
+ var coreStop = ({ props, runtime }) => runtime.stop(String(props.reason ?? "stopped"));
420
430
 
421
- // src/registry/actions.ts
422
- var createActionsRegistry = () => /* @__PURE__ */ new Map([
431
+ // src/helpers/actions/index.ts
432
+ var BUILTIN_ACTIONS = [
423
433
  ["core.noop", coreNoop],
424
434
  ["core.stop", coreStop],
425
435
  ["core.fail", coreFail],
@@ -433,7 +443,8 @@ var createActionsRegistry = () => /* @__PURE__ */ new Map([
433
443
  ["core.emit", coreEmit],
434
444
  ["core.patch", corePatch],
435
445
  ["core.delay", coreDelay]
436
- ]);
446
+ ];
447
+ var BUILTIN_ACTION_NAMES = new Set(BUILTIN_ACTIONS.map(([name]) => name));
437
448
 
438
449
  // src/helpers/conditions/changedCondition.ts
439
450
  var changedCondition = (_args, current, previous) => !Object.is(current, previous);
@@ -527,8 +538,8 @@ var typeIsCondition = (_args, value, expected) => {
527
538
  return expected === "string" || expected === "number" || expected === "boolean" ? typeof value === expected : false;
528
539
  };
529
540
 
530
- // src/registry/conditions.ts
531
- var createConditionsRegistry = () => /* @__PURE__ */ new Map([
541
+ // src/helpers/conditions/index.ts
542
+ var BUILTIN_CONDITIONS = [
532
543
  ["eq", eqCondition],
533
544
  ["neq", neqCondition],
534
545
  ["gt", gtCondition],
@@ -545,7 +556,8 @@ var createConditionsRegistry = () => /* @__PURE__ */ new Map([
545
556
  ["typeIs", typeIsCondition],
546
557
  ["changed", changedCondition],
547
558
  ["cooldownReady", cooldownReadyCondition]
548
- ]);
559
+ ];
560
+ var BUILTIN_CONDITION_NAMES = new Set(BUILTIN_CONDITIONS.map(([name]) => name));
549
561
 
550
562
  // src/helpers/runner/applyResult.ts
551
563
  var applyResult = (result, state, mergeData) => {
@@ -559,6 +571,9 @@ var applyResult = (result, state, mergeData) => {
559
571
  state.events.push(...result.events);
560
572
  };
561
573
 
574
+ // src/helpers/errors/slapError.ts
575
+ var slapError = (code, message, extras = {}) => ({ code, message, ...extras });
576
+
562
577
  // src/helpers/path/resolveValue.ts
563
578
  import { pick as pick3 } from "objwalk";
564
579
 
@@ -997,7 +1012,7 @@ var executeNext = (item, depth, state, environment) => {
997
1012
  const id = typeof item === "string" ? item : item.strategy;
998
1013
  if (typeof item !== "string" && item.when) {
999
1014
  const runtime = createRuntime(state);
1000
- const condition = evaluateCondition(item.when, environment.conditionsRegistry, { ...state, runtime, strategy: id });
1015
+ const condition = evaluateCondition(item.when, environment.registry.conditions, { ...state, runtime, strategy: id });
1001
1016
  if (!condition.ok) {
1002
1017
  return { status: "failed", error: condition.error, patches: [], events: [] };
1003
1018
  }
@@ -1249,6 +1264,9 @@ var normalizeActionResult = (raw) => {
1249
1264
  };
1250
1265
  };
1251
1266
 
1267
+ // src/helpers/trace/cloneData.ts
1268
+ var cloneData = (data) => ({ ...data });
1269
+
1252
1270
  // src/helpers/runner/pushTrace.ts
1253
1271
  var pushTrace = (state, step, depth, strategyId, strategy, status, props, dataBefore, startedAt, reason) => {
1254
1272
  state.traceSink?.push({
@@ -1431,7 +1449,7 @@ var executeStrategy = (id, extraProps, depth, state, environment) => {
1431
1449
  events: []
1432
1450
  };
1433
1451
  }
1434
- const action = environment.actionsRegistry.get(strategy.fn);
1452
+ const action = environment.registry.actions.get(strategy.fn);
1435
1453
  if (!action) {
1436
1454
  return {
1437
1455
  status: "failed",
@@ -1486,7 +1504,7 @@ var executeStrategy = (id, extraProps, depth, state, environment) => {
1486
1504
  const dataBefore = cloneData(state.data);
1487
1505
  const traceStep = state.stepCounter.current + 1;
1488
1506
  const startedAt = Date.now();
1489
- const condition = evaluateCondition(strategy.when, environment.conditionsRegistry, {
1507
+ const condition = evaluateCondition(strategy.when, environment.registry.conditions, {
1490
1508
  ...state,
1491
1509
  runtime,
1492
1510
  strategy: id
@@ -1798,6 +1816,17 @@ var validateCondition = (expression, strategy, path, conditionsRegistry, errors)
1798
1816
  return;
1799
1817
  }
1800
1818
  const [operator, ...args] = expression;
1819
+ if (operator === "guard") {
1820
+ if (args.length !== 1 || typeof args[0] !== "string") {
1821
+ errors.push({
1822
+ code: "CONDITION_INVALID",
1823
+ message: "Guard reference must be a single string name",
1824
+ strategy,
1825
+ path
1826
+ });
1827
+ }
1828
+ return;
1829
+ }
1801
1830
  if (operator === "and" || operator === "or") {
1802
1831
  args.forEach((arg, index) => validateCondition(arg, strategy, `${path}.${index + 1}`, conditionsRegistry, errors));
1803
1832
  return;
@@ -1843,6 +1872,82 @@ var validateNextList = (config, list, path, strategy, conditionsRegistry, errors
1843
1872
  });
1844
1873
  };
1845
1874
 
1875
+ // src/helpers/validation/resolveGuards.ts
1876
+ var isGuardRef = (expression) => Array.isArray(expression) && expression.length === 2 && expression[0] === "guard" && typeof expression[1] === "string";
1877
+ var resolveRef = (config, name, visiting, path) => {
1878
+ if (visiting.includes(name)) {
1879
+ return {
1880
+ issue: { code: "GUARD_CYCLE", message: `Guard "${name}" is part of a reference cycle`, guard: name, path }
1881
+ };
1882
+ }
1883
+ const guard = config.guards?.[name];
1884
+ if (guard === void 0) {
1885
+ return { issue: { code: "GUARD_NOT_FOUND", message: `Guard "${name}" is not defined`, guard: name, path } };
1886
+ }
1887
+ return resolveExpression(config, guard, [...visiting, name], path);
1888
+ };
1889
+ var resolveExpression = (config, expression, visiting, path) => {
1890
+ if (isGuardRef(expression)) {
1891
+ return resolveRef(config, expression[1], visiting, path);
1892
+ }
1893
+ if (!Array.isArray(expression) || typeof expression[0] !== "string") {
1894
+ return { expression };
1895
+ }
1896
+ const [operator, ...args] = expression;
1897
+ if (operator === "and" || operator === "or") {
1898
+ const resolved = [];
1899
+ for (let index = 0; index < args.length; index += 1) {
1900
+ const result = resolveExpression(config, args[index], visiting, `${path}.${index + 1}`);
1901
+ if (result.issue) {
1902
+ return result;
1903
+ }
1904
+ resolved.push(result.expression);
1905
+ }
1906
+ return { expression: [operator, ...resolved] };
1907
+ }
1908
+ if (operator === "not") {
1909
+ const result = resolveExpression(config, args[0], visiting, `${path}.1`);
1910
+ if (result.issue) {
1911
+ return result;
1912
+ }
1913
+ return { expression: ["not", result.expression] };
1914
+ }
1915
+ return { expression };
1916
+ };
1917
+ var resolveGuards = (config) => {
1918
+ const issues = [];
1919
+ const resolveWhen = (when, path) => {
1920
+ if (when === void 0) {
1921
+ return void 0;
1922
+ }
1923
+ const result = resolveExpression(config, when, [], path);
1924
+ if (result.issue) {
1925
+ issues.push(result.issue);
1926
+ return when;
1927
+ }
1928
+ return result.expression;
1929
+ };
1930
+ const resolveNext = (next, prefix) => next.map((item, index) => {
1931
+ if (typeof item === "string" || !item || typeof item !== "object" || item.when === void 0) {
1932
+ return item;
1933
+ }
1934
+ const target = item;
1935
+ const when = resolveWhen(target.when, `${prefix}.${index}.when`);
1936
+ return when === void 0 ? item : { ...target, when };
1937
+ });
1938
+ const strategies = {};
1939
+ for (const [id, strategy] of Object.entries(config.strategies)) {
1940
+ const when = strategy.when === void 0 ? void 0 : resolveWhen(strategy.when, `${id}.when`);
1941
+ strategies[id] = {
1942
+ ...strategy,
1943
+ ...when !== void 0 ? { when } : {},
1944
+ ...strategy.then ? { then: resolveNext(strategy.then, `${id}.then`) } : {},
1945
+ ...strategy.catch ? { catch: resolveNext(strategy.catch, `${id}.catch`) } : {}
1946
+ };
1947
+ }
1948
+ return { config: { ...config, strategies }, issues };
1949
+ };
1950
+
1846
1951
  // src/helpers/validation/validateConfig.ts
1847
1952
  var validateConfig = (config, actionsRegistry, conditionsRegistry) => {
1848
1953
  const errors = [];
@@ -1861,6 +1966,22 @@ var validateConfig = (config, actionsRegistry, conditionsRegistry) => {
1861
1966
  warnings
1862
1967
  };
1863
1968
  }
1969
+ if (config.guards !== void 0) {
1970
+ if (typeof config.guards !== "object" || Array.isArray(config.guards)) {
1971
+ errors.push({ code: "GUARD_INVALID", message: "Config guards must be an object", path: "guards" });
1972
+ } else {
1973
+ for (const [name, expression] of Object.entries(config.guards)) {
1974
+ validateCondition(expression, name, `guards.${name}`, conditionsRegistry, errors);
1975
+ }
1976
+ }
1977
+ }
1978
+ for (const issue of resolveGuards(config).issues) {
1979
+ errors.push({
1980
+ code: issue.code,
1981
+ message: issue.message,
1982
+ ...issue.path ? { path: issue.path } : {}
1983
+ });
1984
+ }
1864
1985
  for (const [id, strategy] of Object.entries(config.strategies ?? {})) {
1865
1986
  if (!strategy || typeof strategy !== "object") {
1866
1987
  errors.push({ code: "STRATEGY_INVALID", message: "Strategy must be an object", strategy: id });
@@ -1980,10 +2101,10 @@ var createRunCancellation = (source) => {
1980
2101
  };
1981
2102
  };
1982
2103
 
1983
- // src/helpers/runner/createRunner.ts
2104
+ // src/createRunner.ts
1984
2105
  var createRunner = (options = {}) => {
1985
- const actionsRegistry = createActionsRegistry();
1986
- const conditionsRegistry = createConditionsRegistry();
2106
+ const actionsRegistry = new Map(BUILTIN_ACTIONS);
2107
+ const conditionsRegistry = new Map(BUILTIN_CONDITIONS);
1987
2108
  const configRef = {};
1988
2109
  const timeout = options.timeout ?? options.timeoutMs;
1989
2110
  const runnerOptions = timeout === void 0 ? options : { ...options, timeout };
@@ -1993,19 +2114,24 @@ var createRunner = (options = {}) => {
1993
2114
  console.warn("timeoutMs is deprecated; use timeout. It will be removed in a future major release.");
1994
2115
  }
1995
2116
  const environment = {
1996
- actionsRegistry,
1997
- conditionsRegistry,
2117
+ registry: { actions: actionsRegistry, conditions: conditionsRegistry },
1998
2118
  configRef,
1999
2119
  options: runnerOptions,
2000
2120
  mergeData
2001
2121
  };
2002
2122
  const registerAction = (name, action) => {
2123
+ if (BUILTIN_ACTION_NAMES.has(name)) {
2124
+ throw new Error(`Cannot override built-in action "${name}"`);
2125
+ }
2003
2126
  actionsRegistry.set(name, action);
2004
2127
  };
2005
2128
  const registerActions = (items) => {
2006
2129
  Object.entries(items).forEach(([name, action]) => registerAction(name, action));
2007
2130
  };
2008
2131
  const registerCondition = (name, condition) => {
2132
+ if (BUILTIN_CONDITION_NAMES.has(name)) {
2133
+ throw new Error(`Cannot override built-in condition "${name}"`);
2134
+ }
2009
2135
  conditionsRegistry.set(name, condition);
2010
2136
  };
2011
2137
  const registerConditions = (items) => {
@@ -2016,7 +2142,7 @@ var createRunner = (options = {}) => {
2016
2142
  return { ...result, warnings: [...result.warnings, ...runnerLimitWarnings(runnerOptions)] };
2017
2143
  };
2018
2144
  const loadConfig = (nextConfig) => {
2019
- configRef.current = nextConfig;
2145
+ configRef.current = resolveGuards(nextConfig).config;
2020
2146
  return validateConfig2(nextConfig);
2021
2147
  };
2022
2148
  const runInternal = (entrypoint, context, input, sync, runOptions) => {
@@ -2102,7 +2228,7 @@ var parseDomBinding = (binding, prefix) => {
2102
2228
  return separator <= 0 || separator === source.length - 1 ? void 0 : { selector: source.slice(0, separator), eventType: source.slice(separator + 1) };
2103
2229
  };
2104
2230
 
2105
- // src/flow.ts
2231
+ // src/createFlow.ts
2106
2232
  var busBindingPrefix = "[bus] ";
2107
2233
  var domBindingPrefix = "[dom] ";
2108
2234
  var defaultMaxQueueSize = 50;
@@ -2343,10 +2469,11 @@ var createFlow = (definition, options) => {
2343
2469
  return { runner, start, stop };
2344
2470
  };
2345
2471
 
2346
- // src/ws.ts
2472
+ // src/createWS.ts
2347
2473
  var openState = 1;
2348
2474
  var maxSeenEvents = 1e3;
2349
2475
  var createWS = (options) => {
2476
+ console.warn("[slapflow] createWS is deprecated and will be removed soon. Use createWebSocket instead.");
2350
2477
  const inboundTopics = new Set(options.inboundTopics ?? []);
2351
2478
  const outboundTopics = new Set(options.outboundTopics ?? []);
2352
2479
  const seenEventIds = /* @__PURE__ */ new Set();
@@ -2358,6 +2485,14 @@ var createWS = (options) => {
2358
2485
  let started = false;
2359
2486
  let currentStatus = "idle";
2360
2487
  const diagnosticsBus = options.bus;
2488
+ const isInboundTopic = (topic) => {
2489
+ for (const pattern of inboundTopics) {
2490
+ if (matchesTopic(topic, pattern)) {
2491
+ return true;
2492
+ }
2493
+ }
2494
+ return false;
2495
+ };
2361
2496
  const emitDiagnostic = (topic, payload) => {
2362
2497
  diagnosticsBus.emit(topic, payload, {
2363
2498
  ...options.origin ? { origin: options.origin } : {}
@@ -2435,7 +2570,7 @@ var createWS = (options) => {
2435
2570
  if (typeof data === "string") {
2436
2571
  try {
2437
2572
  const busEvent = JSON.parse(data);
2438
- if (busEvent.topic && inboundTopics.has(busEvent.topic)) {
2573
+ if (busEvent.topic && isInboundTopic(busEvent.topic)) {
2439
2574
  if (busEvent.id) {
2440
2575
  rememberEvent(busEvent.id);
2441
2576
  }
@@ -2502,7 +2637,117 @@ var createWS = (options) => {
2502
2637
  return { start, stop, reconnect, status: () => currentStatus };
2503
2638
  };
2504
2639
 
2505
- // src/catchError.ts
2640
+ // src/createWebSocket.ts
2641
+ var createWebSocket = (options) => {
2642
+ let socket;
2643
+ let retryTimer;
2644
+ let retryAttempt = 0;
2645
+ let started = false;
2646
+ let currentStatus = "idle";
2647
+ const emitSocketEvent = (topic, payload) => {
2648
+ options.bus.dispatch({
2649
+ id: createId(),
2650
+ topic,
2651
+ occurredAt: Date.now(),
2652
+ ...options.origin ? { origin: options.origin } : {},
2653
+ parsed: payload,
2654
+ serialized: JSON.stringify(payload)
2655
+ });
2656
+ };
2657
+ const scheduleRetry = () => {
2658
+ const delay = getRetryDelay(retryAttempt, options.reconnect ?? {});
2659
+ const attempt = retryAttempt + 1;
2660
+ currentStatus = "reconnecting";
2661
+ retryAttempt = attempt;
2662
+ retryTimer = setTimeout(() => {
2663
+ retryTimer = void 0;
2664
+ connect();
2665
+ }, delay);
2666
+ };
2667
+ const connect = () => {
2668
+ if (!started || socket) {
2669
+ return;
2670
+ }
2671
+ currentStatus = "connecting";
2672
+ try {
2673
+ const current = new WebSocket(options.url, options.protocols ?? []);
2674
+ socket = current;
2675
+ current.addEventListener("open", () => {
2676
+ if (socket === current) {
2677
+ retryAttempt = 0;
2678
+ currentStatus = "connected";
2679
+ emitSocketEvent("open", { url: options.url });
2680
+ }
2681
+ });
2682
+ current.addEventListener("message", (event) => {
2683
+ if (socket !== current) {
2684
+ return;
2685
+ }
2686
+ let parsed = event.data;
2687
+ if (typeof parsed === "string") {
2688
+ try {
2689
+ parsed = JSON.parse(parsed);
2690
+ } catch {
2691
+ parsed = event.data;
2692
+ }
2693
+ }
2694
+ emitSocketEvent("message", parsed);
2695
+ });
2696
+ current.addEventListener("close", (event) => {
2697
+ if (socket !== current) {
2698
+ return;
2699
+ }
2700
+ socket = void 0;
2701
+ emitSocketEvent("close", { code: event.code, reason: event.reason });
2702
+ if (started) {
2703
+ scheduleRetry();
2704
+ }
2705
+ });
2706
+ current.addEventListener("error", (event) => {
2707
+ emitSocketEvent("error", { error: event });
2708
+ });
2709
+ } catch (error) {
2710
+ socket = void 0;
2711
+ emitSocketEvent("error", { error });
2712
+ if (started) {
2713
+ scheduleRetry();
2714
+ }
2715
+ }
2716
+ };
2717
+ const start = () => {
2718
+ if (!started) {
2719
+ started = true;
2720
+ connect();
2721
+ }
2722
+ };
2723
+ const stop = () => {
2724
+ started = false;
2725
+ if (retryTimer) {
2726
+ clearTimeout(retryTimer);
2727
+ }
2728
+ retryTimer = void 0;
2729
+ const current = socket;
2730
+ socket = void 0;
2731
+ current?.close();
2732
+ currentStatus = "stopped";
2733
+ };
2734
+ const reconnect = () => {
2735
+ if (!started) {
2736
+ return;
2737
+ }
2738
+ if (retryTimer) {
2739
+ clearTimeout(retryTimer);
2740
+ retryTimer = void 0;
2741
+ }
2742
+ const current = socket;
2743
+ socket = void 0;
2744
+ current?.close();
2745
+ connect();
2746
+ };
2747
+ return { start, stop, reconnect, status: () => currentStatus };
2748
+ };
2749
+
2750
+ // src/helpers/catchError.ts
2506
2751
  var catchError = (callback) => new Promise((resolve, reject) => {
2507
2752
  try {
2508
2753
  resolve(callback());
@@ -2511,14 +2756,17 @@ var catchError = (callback) => new Promise((resolve, reject) => {
2511
2756
  }
2512
2757
  });
2513
2758
  export {
2759
+ BUILTIN_ACTIONS,
2760
+ BUILTIN_ACTION_NAMES,
2761
+ BUILTIN_CONDITIONS,
2762
+ BUILTIN_CONDITION_NAMES,
2514
2763
  PubSub,
2515
2764
  catchError,
2516
- createActionsRegistry,
2517
- createConditionsRegistry,
2518
2765
  createFlow,
2519
2766
  createMemoryTraceSink,
2520
2767
  createPubSub,
2521
2768
  createWS,
2769
+ createWebSocket,
2522
2770
  defineConfig,
2523
2771
  defineErrorReporter
2524
2772
  };
package/package.json CHANGED
@@ -1,12 +1,20 @@
1
1
  {
2
2
  "name": "slapflow",
3
- "version": "1.0.2",
3
+ "version": "1.2.0",
4
4
  "description": "Chain actions behavior runtime",
5
5
  "license": "ISC",
6
6
  "author": "Sergey Khalilov",
7
7
  "keywords": [
8
8
  "behavior",
9
9
  "workflow",
10
+ "orchestration",
11
+ "state-machine",
12
+ "event-driven",
13
+ "concurrency",
14
+ "cancellation",
15
+ "pubsub",
16
+ "fetch",
17
+ "websocket",
10
18
  "action-runner",
11
19
  "rules-engine",
12
20
  "typescript"
@@ -49,7 +57,9 @@
49
57
  "test": "npm run typecheck && vitest run",
50
58
  "build": "tsup src/index.ts --format esm,cjs --dts --clean",
51
59
  "pack:check": "npm pack --dry-run",
52
- "prepublishOnly": "npm test && npm run build"
60
+ "prepublishOnly": "npm test && npm run build",
61
+ "format": "prettier --write .",
62
+ "check": "prettier --check ."
53
63
  },
54
64
  "devDependencies": {
55
65
  "@types/node": "^26.1.0",