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/README.md +8 -1
- package/SPEC-RU.md +104 -36
- package/SPEC.md +104 -36
- package/dist/index.cjs +314 -63
- package/dist/index.d.cts +38 -8
- package/dist/index.d.ts +38 -8
- package/dist/index.js +309 -61
- package/package.json +12 -2
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/
|
|
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
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
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
|
|
89
|
-
const
|
|
90
|
-
handlers.add(
|
|
91
|
-
|
|
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 =
|
|
105
|
+
const handlers = registry.get(event);
|
|
97
106
|
if (handlers) {
|
|
98
107
|
handlers.delete(handler);
|
|
99
108
|
if (handlers.size === 0) {
|
|
100
|
-
|
|
109
|
+
registry.delete(event);
|
|
101
110
|
}
|
|
102
111
|
}
|
|
103
112
|
} else {
|
|
104
|
-
|
|
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/
|
|
422
|
-
var
|
|
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/
|
|
531
|
-
var
|
|
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.
|
|
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.
|
|
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.
|
|
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/
|
|
2104
|
+
// src/createRunner.ts
|
|
1984
2105
|
var createRunner = (options = {}) => {
|
|
1985
|
-
const actionsRegistry =
|
|
1986
|
-
const conditionsRegistry =
|
|
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/
|
|
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/
|
|
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 &&
|
|
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/
|
|
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
|
|
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",
|