@state-flow/core 1.0.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/LICENSE +21 -0
- package/README.md +159 -0
- package/dist/flow.d.ts +49 -0
- package/dist/flow.d.ts.map +1 -0
- package/dist/index.d.ts +13 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +1507 -0
- package/dist/index.js.map +1 -0
- package/dist/index.mjs +1458 -0
- package/dist/index.mjs.map +1 -0
- package/dist/logger.d.ts +45 -0
- package/dist/logger.d.ts.map +1 -0
- package/dist/package-json.d.ts +8 -0
- package/dist/package-json.d.ts.map +1 -0
- package/dist/result.d.ts +60 -0
- package/dist/result.d.ts.map +1 -0
- package/dist/signal.d.ts +19 -0
- package/dist/signal.d.ts.map +1 -0
- package/dist/state.d.ts +56 -0
- package/dist/state.d.ts.map +1 -0
- package/dist/symbols.d.ts +9 -0
- package/dist/symbols.d.ts.map +1 -0
- package/dist/utils.d.ts +15 -0
- package/dist/utils.d.ts.map +1 -0
- package/package.json +76 -0
package/dist/index.mjs
ADDED
|
@@ -0,0 +1,1458 @@
|
|
|
1
|
+
var __typeError = (msg) => {
|
|
2
|
+
throw TypeError(msg);
|
|
3
|
+
};
|
|
4
|
+
var __accessCheck = (obj, member, msg) => member.has(obj) || __typeError("Cannot " + msg);
|
|
5
|
+
var __privateGet = (obj, member, getter) => (__accessCheck(obj, member, "read from private field"), getter ? getter.call(obj) : member.get(obj));
|
|
6
|
+
var __privateAdd = (obj, member, value) => member.has(obj) ? __typeError("Cannot add the same private member more than once") : member instanceof WeakSet ? member.add(obj) : member.set(obj, value);
|
|
7
|
+
var __privateSet = (obj, member, value, setter) => (__accessCheck(obj, member, "write to private field"), setter ? setter.call(obj, value) : member.set(obj, value), value);
|
|
8
|
+
var __privateMethod = (obj, member, method) => (__accessCheck(obj, member, "access private method"), method);
|
|
9
|
+
|
|
10
|
+
// src/package-json.ts
|
|
11
|
+
var package_json_default = { name: "@state-flow/core", version: "1.0.0", commit: "6b8863c9d96e7ff65eaf983b247f122628b986d5", branch: "HEAD" };
|
|
12
|
+
|
|
13
|
+
// src/flow.ts
|
|
14
|
+
import { EventEmitter } from "events";
|
|
15
|
+
|
|
16
|
+
// src/logger.ts
|
|
17
|
+
var STACKTRACE_VIEWER_URL = "https://stacktrace.pirogov.dev/";
|
|
18
|
+
var dispatchContextProvider = null;
|
|
19
|
+
function setGlobalDispatchContextProvider(provider) {
|
|
20
|
+
dispatchContextProvider = provider;
|
|
21
|
+
return () => {
|
|
22
|
+
if (dispatchContextProvider === provider) {
|
|
23
|
+
dispatchContextProvider = null;
|
|
24
|
+
}
|
|
25
|
+
};
|
|
26
|
+
}
|
|
27
|
+
function getGlobalDispatchContext() {
|
|
28
|
+
if (dispatchContextProvider == null) {
|
|
29
|
+
return null;
|
|
30
|
+
}
|
|
31
|
+
try {
|
|
32
|
+
return dispatchContextProvider();
|
|
33
|
+
} catch {
|
|
34
|
+
return null;
|
|
35
|
+
}
|
|
36
|
+
}
|
|
37
|
+
var globalLogHandlers = /* @__PURE__ */ new Set();
|
|
38
|
+
function addGlobalLogHandler(handler) {
|
|
39
|
+
globalLogHandlers.add(handler);
|
|
40
|
+
return () => {
|
|
41
|
+
globalLogHandlers.delete(handler);
|
|
42
|
+
};
|
|
43
|
+
}
|
|
44
|
+
function withGlobalLogHandlers(handlers) {
|
|
45
|
+
if (globalLogHandlers.size === 0) {
|
|
46
|
+
return handlers;
|
|
47
|
+
}
|
|
48
|
+
return [...handlers, ...globalLogHandlers];
|
|
49
|
+
}
|
|
50
|
+
var consoleSilencedOverride = null;
|
|
51
|
+
function isConsoleSilenced() {
|
|
52
|
+
if (consoleSilencedOverride !== null) {
|
|
53
|
+
return consoleSilencedOverride;
|
|
54
|
+
}
|
|
55
|
+
return typeof process !== "undefined" && process.env?.VITEST != null;
|
|
56
|
+
}
|
|
57
|
+
function setConsoleLogSilenced(silenced) {
|
|
58
|
+
consoleSilencedOverride = silenced;
|
|
59
|
+
}
|
|
60
|
+
var consoleLogHandler = (entry) => {
|
|
61
|
+
if (isConsoleSilenced()) {
|
|
62
|
+
return;
|
|
63
|
+
}
|
|
64
|
+
const logs = {};
|
|
65
|
+
entry.stateChanges.reduce((l, r) => {
|
|
66
|
+
var _a;
|
|
67
|
+
l[_a = r.stateName] ?? (l[_a] = []);
|
|
68
|
+
l[r.stateName].push(`State: ${r.oldState} => ${r.newState}`);
|
|
69
|
+
return l;
|
|
70
|
+
}, logs);
|
|
71
|
+
entry.handlerResults.reduce((l, r) => {
|
|
72
|
+
var _a;
|
|
73
|
+
l[_a = r.stateName] ?? (l[_a] = []);
|
|
74
|
+
l[r.stateName].push(` ${r.type} ${r.handlerName}() => ${r.result}`);
|
|
75
|
+
return l;
|
|
76
|
+
}, logs);
|
|
77
|
+
entry.observers.reduce((l, r) => {
|
|
78
|
+
var _a;
|
|
79
|
+
l[_a = r.stateName] ?? (l[_a] = []);
|
|
80
|
+
l[r.stateName].push(` observed by ${r.observerName}() => ${r.needObserve}`);
|
|
81
|
+
return l;
|
|
82
|
+
}, logs);
|
|
83
|
+
console.groupCollapsed(entry.message);
|
|
84
|
+
for (const [, rows] of Object.entries(logs)) {
|
|
85
|
+
for (const row of rows) {
|
|
86
|
+
console.log(row);
|
|
87
|
+
}
|
|
88
|
+
}
|
|
89
|
+
if (entry.enqueuedSignals.length > 0) {
|
|
90
|
+
console.log("\nEnqueued Signals:");
|
|
91
|
+
for (const enq of entry.enqueuedSignals) {
|
|
92
|
+
console.log(` ${enq.signal} (from ${enq.fromHandler})`);
|
|
93
|
+
}
|
|
94
|
+
}
|
|
95
|
+
console.log("\nFinal States:");
|
|
96
|
+
Object.entries(entry.finalStates).forEach(([name, state]) => {
|
|
97
|
+
console.debug(` ${name}: ${state}`);
|
|
98
|
+
});
|
|
99
|
+
if (entry.stacktrace != null) {
|
|
100
|
+
console.log("\nStacktrace:");
|
|
101
|
+
console.log(`${STACKTRACE_VIEWER_URL}#${btoa(JSON.stringify(entry.stacktrace))}`);
|
|
102
|
+
}
|
|
103
|
+
console.groupEnd();
|
|
104
|
+
};
|
|
105
|
+
function emitGrouped(handlers, label, entries, context = null) {
|
|
106
|
+
if (entries.length === 0) {
|
|
107
|
+
return;
|
|
108
|
+
}
|
|
109
|
+
if (isConsoleSilenced()) {
|
|
110
|
+
for (const entry of entries) {
|
|
111
|
+
entry.groupLabel = label;
|
|
112
|
+
if (context != null) {
|
|
113
|
+
entry.dispatchContext = context;
|
|
114
|
+
}
|
|
115
|
+
for (const handler of handlers) {
|
|
116
|
+
handler(entry);
|
|
117
|
+
}
|
|
118
|
+
}
|
|
119
|
+
return;
|
|
120
|
+
}
|
|
121
|
+
console.group(context != null ? `${label} \u27F5 ${context}` : label);
|
|
122
|
+
try {
|
|
123
|
+
for (const entry of entries) {
|
|
124
|
+
entry.groupLabel = label;
|
|
125
|
+
if (context != null) {
|
|
126
|
+
entry.dispatchContext = context;
|
|
127
|
+
}
|
|
128
|
+
for (const handler of handlers) {
|
|
129
|
+
handler(entry);
|
|
130
|
+
}
|
|
131
|
+
}
|
|
132
|
+
} finally {
|
|
133
|
+
console.groupEnd();
|
|
134
|
+
}
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
// src/symbols.ts
|
|
138
|
+
var SIGNAL = /* @__PURE__ */ Symbol.for("SIGNAL");
|
|
139
|
+
var DEF = /* @__PURE__ */ Symbol.for("DEF");
|
|
140
|
+
var IS_INITIAL = /* @__PURE__ */ Symbol.for("IS_INITIAL");
|
|
141
|
+
var VARIANT = /* @__PURE__ */ Symbol.for("VARIANT");
|
|
142
|
+
var SIGNALS = /* @__PURE__ */ Symbol.for("SIGNALS");
|
|
143
|
+
var HANDLERS = /* @__PURE__ */ Symbol.for("HANDLERS");
|
|
144
|
+
var STRING_REPR = /* @__PURE__ */ Symbol.for("STRING_REPR");
|
|
145
|
+
var PARSER = /* @__PURE__ */ Symbol.for("PARSER");
|
|
146
|
+
if (typeof Symbol.dispose === "undefined") {
|
|
147
|
+
Object.defineProperty(Symbol, "dispose", { value: /* @__PURE__ */ Symbol.for("dispose") });
|
|
148
|
+
}
|
|
149
|
+
if (typeof Symbol.asyncDispose === "undefined") {
|
|
150
|
+
Object.defineProperty(Symbol, "asyncDispose", { value: /* @__PURE__ */ Symbol.for("asyncDispose") });
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
// src/utils.ts
|
|
154
|
+
function buildName(ctx, func) {
|
|
155
|
+
let fnName = null;
|
|
156
|
+
let ctxName = null;
|
|
157
|
+
if (func != null) {
|
|
158
|
+
fnName = Symbol.toStringTag in func && func[Symbol.toStringTag] !== "AsyncFunction" ? func[Symbol.toStringTag] : func.name;
|
|
159
|
+
}
|
|
160
|
+
if (ctx != null && typeof ctx === "object") {
|
|
161
|
+
ctxName = Symbol.toStringTag in ctx ? ctx[Symbol.toStringTag] : ctx.constructor.name;
|
|
162
|
+
}
|
|
163
|
+
if (ctxName == null) {
|
|
164
|
+
return String(fnName);
|
|
165
|
+
} else if (fnName == null) {
|
|
166
|
+
return String(ctxName);
|
|
167
|
+
}
|
|
168
|
+
if (String(fnName).startsWith(String(ctxName))) {
|
|
169
|
+
return String(fnName);
|
|
170
|
+
}
|
|
171
|
+
return `${ctxName}.${fnName}`;
|
|
172
|
+
}
|
|
173
|
+
var StateFlowError = class extends Error {
|
|
174
|
+
constructor(message, inner = null) {
|
|
175
|
+
super(message);
|
|
176
|
+
this.name = "StateFlowError";
|
|
177
|
+
if (inner != null) {
|
|
178
|
+
this.stack = inner.stack;
|
|
179
|
+
}
|
|
180
|
+
}
|
|
181
|
+
};
|
|
182
|
+
var urlRegex = /(https?:\/\/[^:]+(?::\d+)?(?:\/[^:]*)*?):(\d+):(\d+)/g;
|
|
183
|
+
function parseCompactStacktrace(stacktrace) {
|
|
184
|
+
if (stacktrace == null) {
|
|
185
|
+
return null;
|
|
186
|
+
}
|
|
187
|
+
const urlMap = /* @__PURE__ */ new Map();
|
|
188
|
+
const urls = [];
|
|
189
|
+
const compactFrames = [];
|
|
190
|
+
const matches = stacktrace.matchAll(urlRegex);
|
|
191
|
+
for (const match of matches) {
|
|
192
|
+
const [, url, lineStr, columnStr] = match;
|
|
193
|
+
if (!urlMap.has(url)) {
|
|
194
|
+
urlMap.set(url, urls.length);
|
|
195
|
+
urls.push(url);
|
|
196
|
+
}
|
|
197
|
+
const urlIndex = urlMap.get(url);
|
|
198
|
+
const line = parseInt(lineStr, 10);
|
|
199
|
+
const column = parseInt(columnStr, 10);
|
|
200
|
+
compactFrames.push([urlIndex, line, column]);
|
|
201
|
+
}
|
|
202
|
+
if (compactFrames.length === 0) {
|
|
203
|
+
return null;
|
|
204
|
+
}
|
|
205
|
+
return [urls, compactFrames];
|
|
206
|
+
}
|
|
207
|
+
var maxArrayItems = 10;
|
|
208
|
+
var maxStringLen = 25;
|
|
209
|
+
var abbreviate = true;
|
|
210
|
+
var inlineSingleArrays = true;
|
|
211
|
+
var maxDepth = 3;
|
|
212
|
+
var SPACES_REGEX = /\s/;
|
|
213
|
+
var BRACKETS_REGEX = /[{}[\]=]/;
|
|
214
|
+
function isObject(val) {
|
|
215
|
+
return val !== null && typeof val === "object" && !Array.isArray(val);
|
|
216
|
+
}
|
|
217
|
+
function isPlainObject(val) {
|
|
218
|
+
if (!isObject(val)) {
|
|
219
|
+
return false;
|
|
220
|
+
}
|
|
221
|
+
const proto = Object.getPrototypeOf(val);
|
|
222
|
+
return proto === null || proto === Object.prototype;
|
|
223
|
+
}
|
|
224
|
+
function getClassName(val) {
|
|
225
|
+
const proto = Object.getPrototypeOf(val);
|
|
226
|
+
if (proto?.constructor?.name != null) {
|
|
227
|
+
return proto.constructor.name;
|
|
228
|
+
}
|
|
229
|
+
return "Object";
|
|
230
|
+
}
|
|
231
|
+
function flattenPath(val, path = [], depthBudget = maxDepth) {
|
|
232
|
+
if (isPlainObject(val) && depthBudget > 0) {
|
|
233
|
+
const keys = Reflect.ownKeys(val).filter((k) => typeof k === "string");
|
|
234
|
+
if (keys.length === 1) {
|
|
235
|
+
const key = keys[0];
|
|
236
|
+
if (key !== void 0) {
|
|
237
|
+
const nextVal = Reflect.get(val, key);
|
|
238
|
+
return flattenPath(nextVal, [...path, key], depthBudget - 1);
|
|
239
|
+
}
|
|
240
|
+
}
|
|
241
|
+
}
|
|
242
|
+
return { path, value: val };
|
|
243
|
+
}
|
|
244
|
+
function serializeValue(val, depth = 0) {
|
|
245
|
+
if (val === null) {
|
|
246
|
+
return abbreviate ? "N" : "null";
|
|
247
|
+
}
|
|
248
|
+
if (val === void 0) {
|
|
249
|
+
return abbreviate ? "U" : "undefined";
|
|
250
|
+
}
|
|
251
|
+
if (typeof val === "boolean") {
|
|
252
|
+
return abbreviate ? val ? "T" : "F" : String(val);
|
|
253
|
+
}
|
|
254
|
+
if (typeof val === "number") {
|
|
255
|
+
return String(val);
|
|
256
|
+
}
|
|
257
|
+
if (typeof val === "string") {
|
|
258
|
+
let str = val;
|
|
259
|
+
if (maxStringLen && str.length > maxStringLen) {
|
|
260
|
+
str = `${str.slice(0, maxStringLen)}\u2026`;
|
|
261
|
+
}
|
|
262
|
+
const hasSpaces = SPACES_REGEX.test(str);
|
|
263
|
+
const hasSpecialChars = BRACKETS_REGEX.test(str);
|
|
264
|
+
if (hasSpaces || hasSpecialChars) {
|
|
265
|
+
return `'${str.replace(/'/g, "\\'")}'`;
|
|
266
|
+
}
|
|
267
|
+
return str;
|
|
268
|
+
}
|
|
269
|
+
if (Array.isArray(val)) {
|
|
270
|
+
if (inlineSingleArrays && val.length === 1) {
|
|
271
|
+
const first = val[0];
|
|
272
|
+
if (first === null || first === void 0 || typeof first === "number" || typeof first === "boolean" || typeof first === "string") {
|
|
273
|
+
return serializeValue(first, depth + 1);
|
|
274
|
+
}
|
|
275
|
+
}
|
|
276
|
+
let items = val;
|
|
277
|
+
let truncated = false;
|
|
278
|
+
if (maxArrayItems && val.length > maxArrayItems) {
|
|
279
|
+
items = val.slice(0, maxArrayItems);
|
|
280
|
+
truncated = true;
|
|
281
|
+
}
|
|
282
|
+
const serialized = items.map((item) => {
|
|
283
|
+
if (isPlainObject(item)) {
|
|
284
|
+
if (depth >= maxDepth) {
|
|
285
|
+
return "{...}";
|
|
286
|
+
}
|
|
287
|
+
const depthBudget = maxDepth - depth - 1;
|
|
288
|
+
const entries = Reflect.ownKeys(item).filter((k) => typeof k === "string").map((k) => {
|
|
289
|
+
const v = Reflect.get(item, k);
|
|
290
|
+
const { path, value } = flattenPath(v, [], depthBudget);
|
|
291
|
+
const fullPath = path.length > 0 ? `${k}.${path.join(".")}` : k;
|
|
292
|
+
return `${fullPath}=${serializeValue(value, depth + 1 + path.length)}`;
|
|
293
|
+
});
|
|
294
|
+
return entries.length === 1 ? entries[0] : `{${entries.join(" ")}}`;
|
|
295
|
+
}
|
|
296
|
+
return serializeValue(item, depth + 1);
|
|
297
|
+
});
|
|
298
|
+
const result = serialized.join(" ");
|
|
299
|
+
return `[${result}${truncated ? ` \u2026+${val.length - maxArrayItems}` : ""}]`;
|
|
300
|
+
}
|
|
301
|
+
if (isObject(val)) {
|
|
302
|
+
if (val instanceof Date) {
|
|
303
|
+
return val.toISOString();
|
|
304
|
+
}
|
|
305
|
+
if (Reflect.has(val, Symbol.toPrimitive) && !Reflect.has(val, VARIANT)) {
|
|
306
|
+
return String(val);
|
|
307
|
+
}
|
|
308
|
+
if (val instanceof Map) {
|
|
309
|
+
if (depth >= maxDepth) {
|
|
310
|
+
return `Map(${val.size}){...}`;
|
|
311
|
+
}
|
|
312
|
+
let entries2 = [...val.entries()];
|
|
313
|
+
const dropped = maxArrayItems ? entries2.length - maxArrayItems : 0;
|
|
314
|
+
if (dropped > 0) {
|
|
315
|
+
entries2 = entries2.slice(0, maxArrayItems);
|
|
316
|
+
}
|
|
317
|
+
const body = entries2.map(([k, v]) => `${serializeValue(k, depth + 1)}=${serializeValue(v, depth + 1)}`);
|
|
318
|
+
return `Map(${val.size}){${body.join(" ")}${dropped > 0 ? ` \u2026+${dropped}` : ""}}`;
|
|
319
|
+
}
|
|
320
|
+
if (val instanceof Set) {
|
|
321
|
+
if (depth >= maxDepth) {
|
|
322
|
+
return `Set(${val.size}){...}`;
|
|
323
|
+
}
|
|
324
|
+
let items = [...val.values()];
|
|
325
|
+
const dropped = maxArrayItems ? items.length - maxArrayItems : 0;
|
|
326
|
+
if (dropped > 0) {
|
|
327
|
+
items = items.slice(0, maxArrayItems);
|
|
328
|
+
}
|
|
329
|
+
const body = items.map((item) => serializeValue(item, depth + 1));
|
|
330
|
+
return `Set(${val.size}){${body.join(" ")}${dropped > 0 ? ` \u2026+${dropped}` : ""}}`;
|
|
331
|
+
}
|
|
332
|
+
if (val instanceof Error) {
|
|
333
|
+
let message = val.message;
|
|
334
|
+
if (maxStringLen && message.length > maxStringLen) {
|
|
335
|
+
message = `${message.slice(0, maxStringLen)}\u2026`;
|
|
336
|
+
}
|
|
337
|
+
return `${val.name}(${message})`;
|
|
338
|
+
}
|
|
339
|
+
if (val instanceof RegExp) {
|
|
340
|
+
return String(val);
|
|
341
|
+
}
|
|
342
|
+
if (val instanceof ArrayBuffer) {
|
|
343
|
+
return `ArrayBuffer(${val.byteLength})`;
|
|
344
|
+
}
|
|
345
|
+
if (ArrayBuffer.isView(val)) {
|
|
346
|
+
const length = val.length ?? val.byteLength;
|
|
347
|
+
return `${getClassName(val)}(${length})`;
|
|
348
|
+
}
|
|
349
|
+
if (typeof val.type === "string" && getClassName(val).endsWith("Event")) {
|
|
350
|
+
return `${getClassName(val)}(${val.type})`;
|
|
351
|
+
}
|
|
352
|
+
if (getClassName(val) === "URL" && typeof val.href === "string") {
|
|
353
|
+
return serializeValue(val.href, depth);
|
|
354
|
+
}
|
|
355
|
+
if (!isPlainObject(val)) {
|
|
356
|
+
const className = getClassName(val);
|
|
357
|
+
return `{${className}}`;
|
|
358
|
+
}
|
|
359
|
+
if (depth >= maxDepth) {
|
|
360
|
+
return "{...}";
|
|
361
|
+
}
|
|
362
|
+
const depthBudget = maxDepth - depth - 1;
|
|
363
|
+
const entries = Reflect.ownKeys(val).filter((k) => typeof k === "string").map((k) => {
|
|
364
|
+
const v = Reflect.get(val, k);
|
|
365
|
+
const { path, value } = flattenPath(v, [], depthBudget);
|
|
366
|
+
const fullPath = path.length > 0 ? `${k}.${path.join(".")}` : k;
|
|
367
|
+
return `${fullPath}=${serializeValue(value, depth + 1 + path.length)}`;
|
|
368
|
+
});
|
|
369
|
+
return depth === 0 ? entries.join(" ") : `{${entries.join(" ")}}`;
|
|
370
|
+
}
|
|
371
|
+
return String(val);
|
|
372
|
+
}
|
|
373
|
+
function serializeDebug(obj) {
|
|
374
|
+
if (obj == null) {
|
|
375
|
+
return "";
|
|
376
|
+
}
|
|
377
|
+
return serializeValue(obj);
|
|
378
|
+
}
|
|
379
|
+
|
|
380
|
+
// src/result.ts
|
|
381
|
+
var ResultKind = /* @__PURE__ */ ((ResultKind2) => {
|
|
382
|
+
ResultKind2[ResultKind2["OK"] = 0] = "OK";
|
|
383
|
+
ResultKind2[ResultKind2["Ignored"] = 1] = "Ignored";
|
|
384
|
+
ResultKind2[ResultKind2["InTransition"] = 2] = "InTransition";
|
|
385
|
+
ResultKind2[ResultKind2["Rejected"] = 3] = "Rejected";
|
|
386
|
+
ResultKind2[ResultKind2["Error"] = 4] = "Error";
|
|
387
|
+
return ResultKind2;
|
|
388
|
+
})(ResultKind || {});
|
|
389
|
+
var _executors, _enqueued, _promise, _expectedKinds, _timestamps, _signal, _handler, _state, _stacktrace, _meta, _Result_instances, assertExpected_fn;
|
|
390
|
+
var _Result = class _Result {
|
|
391
|
+
/**
|
|
392
|
+
* Internal constructor of Result
|
|
393
|
+
*
|
|
394
|
+
* @param kind
|
|
395
|
+
* @param data
|
|
396
|
+
* @param msg
|
|
397
|
+
* @param ts
|
|
398
|
+
* @private
|
|
399
|
+
* @internal
|
|
400
|
+
*/
|
|
401
|
+
constructor(kind, data, msg, ts = Date.now()) {
|
|
402
|
+
this.kind = kind;
|
|
403
|
+
this.data = data;
|
|
404
|
+
this.msg = msg;
|
|
405
|
+
__privateAdd(this, _Result_instances);
|
|
406
|
+
// a list of transitioning result promises
|
|
407
|
+
__privateAdd(this, _executors, []);
|
|
408
|
+
// signals to dispatch after this result completes successfully
|
|
409
|
+
__privateAdd(this, _enqueued, []);
|
|
410
|
+
// cached promise for current result
|
|
411
|
+
__privateAdd(this, _promise, null);
|
|
412
|
+
// kinds the caller asserted via expect() on an InTransition result.
|
|
413
|
+
// The assertion is deferred to the FINAL resolved result and enforced in done().
|
|
414
|
+
__privateAdd(this, _expectedKinds, null);
|
|
415
|
+
// contexts for logging purposes
|
|
416
|
+
__privateAdd(this, _timestamps, [Date.now(), Date.now()]);
|
|
417
|
+
__privateAdd(this, _signal, null);
|
|
418
|
+
__privateAdd(this, _handler, null);
|
|
419
|
+
__privateAdd(this, _state, null);
|
|
420
|
+
__privateAdd(this, _stacktrace, null);
|
|
421
|
+
__privateAdd(this, _meta, null);
|
|
422
|
+
__privateSet(this, _timestamps, [ts, ts]);
|
|
423
|
+
if (kind === 2 /* InTransition */) {
|
|
424
|
+
__privateGet(this, _executors).push(data);
|
|
425
|
+
}
|
|
426
|
+
Object.freeze(this);
|
|
427
|
+
}
|
|
428
|
+
get startedAt() {
|
|
429
|
+
return __privateGet(this, _timestamps)[0];
|
|
430
|
+
}
|
|
431
|
+
get finishedAt() {
|
|
432
|
+
return __privateGet(this, _timestamps)[1];
|
|
433
|
+
}
|
|
434
|
+
get stacktrace() {
|
|
435
|
+
return __privateGet(this, _stacktrace);
|
|
436
|
+
}
|
|
437
|
+
in(...args) {
|
|
438
|
+
return args.includes(this.kind);
|
|
439
|
+
}
|
|
440
|
+
/**
|
|
441
|
+
* Returns a promise with result
|
|
442
|
+
* Useful for `InTransition` kind results
|
|
443
|
+
* For all other kinds it wraps "this" object
|
|
444
|
+
*/
|
|
445
|
+
async done() {
|
|
446
|
+
if (this.kind === 2 /* InTransition */ && __privateGet(this, _meta)?.transitioning != null && typeof __privateGet(this, _meta).transitioning !== "boolean") {
|
|
447
|
+
return __privateMethod(this, _Result_instances, assertExpected_fn).call(this, await __privateGet(this, _meta).transitioning);
|
|
448
|
+
}
|
|
449
|
+
const result = await this.waitAll();
|
|
450
|
+
if (__privateGet(this, _meta)?.transitioning != null) {
|
|
451
|
+
await __privateGet(this, _meta).transitioning;
|
|
452
|
+
}
|
|
453
|
+
return __privateMethod(this, _Result_instances, assertExpected_fn).call(this, result);
|
|
454
|
+
}
|
|
455
|
+
/**
|
|
456
|
+
* Error object for `Error` results
|
|
457
|
+
* Or null for all other kinds of results
|
|
458
|
+
*/
|
|
459
|
+
get error() {
|
|
460
|
+
if (this.msg instanceof Error) {
|
|
461
|
+
return this.msg;
|
|
462
|
+
}
|
|
463
|
+
return null;
|
|
464
|
+
}
|
|
465
|
+
/**
|
|
466
|
+
* Message string for `Ignored` or `Rejected` kinds
|
|
467
|
+
* Or null for all other kinds of results
|
|
468
|
+
*/
|
|
469
|
+
get message() {
|
|
470
|
+
if (typeof this.msg === "string" && this.msg !== "") {
|
|
471
|
+
return this.msg;
|
|
472
|
+
}
|
|
473
|
+
return null;
|
|
474
|
+
}
|
|
475
|
+
/**
|
|
476
|
+
* Signals enqueued for dispatch after this result completes successfully
|
|
477
|
+
*/
|
|
478
|
+
get enqueuedSignals() {
|
|
479
|
+
return __privateGet(this, _enqueued);
|
|
480
|
+
}
|
|
481
|
+
/**
|
|
482
|
+
* @internal
|
|
483
|
+
* @private
|
|
484
|
+
*/
|
|
485
|
+
waitAll() {
|
|
486
|
+
if (__privateGet(this, _promise) == null) {
|
|
487
|
+
if (this.kind === 2 /* InTransition */) {
|
|
488
|
+
__privateSet(this, _promise, Promise.all(__privateGet(this, _executors)).then(mergeResults).then((res) => res.kind === 2 /* InTransition */ ? res.waitAll() : res).then((res) => res.withTimestamps(...__privateGet(this, _timestamps), Date.now())));
|
|
489
|
+
} else {
|
|
490
|
+
__privateSet(this, _promise, Promise.resolve(this));
|
|
491
|
+
}
|
|
492
|
+
}
|
|
493
|
+
return __privateGet(this, _promise);
|
|
494
|
+
}
|
|
495
|
+
/**
|
|
496
|
+
* Creates `Ignored` result with explanation message
|
|
497
|
+
*
|
|
498
|
+
* @param message explanation message
|
|
499
|
+
*/
|
|
500
|
+
static ignore(message) {
|
|
501
|
+
return new _Result(1 /* Ignored */, null, message);
|
|
502
|
+
}
|
|
503
|
+
/**
|
|
504
|
+
* Creates `Ok` result without any messages
|
|
505
|
+
*/
|
|
506
|
+
static ok(data = null) {
|
|
507
|
+
return new _Result(0 /* OK */, data, "");
|
|
508
|
+
}
|
|
509
|
+
/**
|
|
510
|
+
* Creates `Ok` result with a new state
|
|
511
|
+
* Used in a flow handlers
|
|
512
|
+
*
|
|
513
|
+
* @param data a new state
|
|
514
|
+
*/
|
|
515
|
+
static state(data) {
|
|
516
|
+
return new _Result(0 /* OK */, data, "");
|
|
517
|
+
}
|
|
518
|
+
/**
|
|
519
|
+
* Creates `Ok` result that enqueues a follow-up signal for dispatch
|
|
520
|
+
* after the current dispatch completes successfully.
|
|
521
|
+
* If the enqueued signal fails, the entire chain is rolled back.
|
|
522
|
+
*
|
|
523
|
+
* @param signal - Signal to dispatch after current dispatch succeeds
|
|
524
|
+
*/
|
|
525
|
+
static enqueue(signal) {
|
|
526
|
+
const result = new _Result(0 /* OK */, null, "");
|
|
527
|
+
__privateSet(result, _enqueued, [signal]);
|
|
528
|
+
return result;
|
|
529
|
+
}
|
|
530
|
+
/**
|
|
531
|
+
* Creates a new `InTransition` result with using async callback
|
|
532
|
+
* and timeout for executing it.
|
|
533
|
+
*
|
|
534
|
+
* @param asyncFn
|
|
535
|
+
* @param timeout
|
|
536
|
+
*/
|
|
537
|
+
static transition(asyncFn, timeout = 500) {
|
|
538
|
+
const ts = Date.now();
|
|
539
|
+
return new _Result(
|
|
540
|
+
2 /* InTransition */,
|
|
541
|
+
withTimeout(
|
|
542
|
+
asyncFn().catch((err) => _Result.error(err)),
|
|
543
|
+
timeout
|
|
544
|
+
),
|
|
545
|
+
"",
|
|
546
|
+
ts
|
|
547
|
+
);
|
|
548
|
+
}
|
|
549
|
+
/**
|
|
550
|
+
* Creates a new `Rejected` result with explanation message
|
|
551
|
+
*
|
|
552
|
+
* @param message
|
|
553
|
+
*/
|
|
554
|
+
static reject(message) {
|
|
555
|
+
return new _Result(3 /* Rejected */, null, message);
|
|
556
|
+
}
|
|
557
|
+
static error(error) {
|
|
558
|
+
if (error instanceof Error) {
|
|
559
|
+
return new _Result(4 /* Error */, null, error);
|
|
560
|
+
}
|
|
561
|
+
return new _Result(4 /* Error */, null, new Error(String(error)));
|
|
562
|
+
}
|
|
563
|
+
expect(...kinds) {
|
|
564
|
+
if (this.kind === 2 /* InTransition */) {
|
|
565
|
+
__privateSet(this, _expectedKinds, kinds);
|
|
566
|
+
return this;
|
|
567
|
+
}
|
|
568
|
+
if (!kinds.includes(this.kind)) {
|
|
569
|
+
throw new StateFlowError(
|
|
570
|
+
buildErrorMessages(this.kind, this.error, this.message, __privateGet(this, _signal), __privateGet(this, _handler), __privateGet(this, _state)),
|
|
571
|
+
this.error
|
|
572
|
+
);
|
|
573
|
+
}
|
|
574
|
+
return this;
|
|
575
|
+
}
|
|
576
|
+
/**
|
|
577
|
+
* @internal
|
|
578
|
+
*/
|
|
579
|
+
withTimestamps(...ts) {
|
|
580
|
+
__privateSet(this, _timestamps, [Math.min(...ts), Math.max(...ts)]);
|
|
581
|
+
return this;
|
|
582
|
+
}
|
|
583
|
+
/**
|
|
584
|
+
* @internal
|
|
585
|
+
* @param signal
|
|
586
|
+
*/
|
|
587
|
+
withSignal(signal) {
|
|
588
|
+
__privateSet(this, _signal, signal);
|
|
589
|
+
return this;
|
|
590
|
+
}
|
|
591
|
+
/**
|
|
592
|
+
* @internal
|
|
593
|
+
* @param name
|
|
594
|
+
*/
|
|
595
|
+
withHandlerName(name) {
|
|
596
|
+
__privateSet(this, _handler, name);
|
|
597
|
+
return this;
|
|
598
|
+
}
|
|
599
|
+
withStacktrace(stack) {
|
|
600
|
+
__privateSet(this, _stacktrace, Array.isArray(stack) ? stack : parseCompactStacktrace(stack));
|
|
601
|
+
return this;
|
|
602
|
+
}
|
|
603
|
+
/**
|
|
604
|
+
* @internal
|
|
605
|
+
* @param state
|
|
606
|
+
*/
|
|
607
|
+
withStateUpdating(state) {
|
|
608
|
+
__privateSet(this, _state, state);
|
|
609
|
+
return this;
|
|
610
|
+
}
|
|
611
|
+
withMeta(meta) {
|
|
612
|
+
__privateSet(this, _meta, meta);
|
|
613
|
+
return this;
|
|
614
|
+
}
|
|
615
|
+
/**
|
|
616
|
+
* @internal
|
|
617
|
+
*/
|
|
618
|
+
withEnqueued(signals) {
|
|
619
|
+
__privateSet(this, _enqueued, signals);
|
|
620
|
+
return this;
|
|
621
|
+
}
|
|
622
|
+
/**
|
|
623
|
+
* @internal
|
|
624
|
+
* @param other
|
|
625
|
+
*/
|
|
626
|
+
merge(other) {
|
|
627
|
+
const combinedEnqueued = [...__privateGet(this, _enqueued), ...__privateGet(other, _enqueued)];
|
|
628
|
+
if (this.kind === other.kind) {
|
|
629
|
+
switch (this.kind) {
|
|
630
|
+
case 1 /* Ignored */:
|
|
631
|
+
return _Result.ignore(mergeStrings(this.msg, other.msg)).withTimestamps(...__privateGet(this, _timestamps), ...__privateGet(other, _timestamps)).withSignal(__privateGet(this, _signal)).withHandlerName(__privateGet(this, _handler)).withStateUpdating(__privateGet(this, _state)).withStacktrace(__privateGet(this, _stacktrace)).withEnqueued(combinedEnqueued);
|
|
632
|
+
case 0 /* OK */:
|
|
633
|
+
return this.withTimestamps(...__privateGet(this, _timestamps), ...__privateGet(other, _timestamps)).withEnqueued(combinedEnqueued);
|
|
634
|
+
case 2 /* InTransition */:
|
|
635
|
+
__privateGet(this, _executors).push(...__privateGet(other, _executors));
|
|
636
|
+
__privateSet(this, _enqueued, combinedEnqueued);
|
|
637
|
+
return this;
|
|
638
|
+
case 3 /* Rejected */:
|
|
639
|
+
return this.withTimestamps(...__privateGet(this, _timestamps), ...__privateGet(other, _timestamps)).withEnqueued(combinedEnqueued);
|
|
640
|
+
case 4 /* Error */: {
|
|
641
|
+
const msg = [this.data, other.data].filter((m) => m !== "").map((e) => String(e)).filter((m) => m !== "").join("; ");
|
|
642
|
+
return _Result.error(new StateFlowError(msg)).withTimestamps(...__privateGet(this, _timestamps), ...__privateGet(other, _timestamps)).withSignal(__privateGet(this, _signal)).withHandlerName(__privateGet(this, _handler)).withStateUpdating(__privateGet(this, _state)).withStacktrace(__privateGet(this, _stacktrace)).withEnqueued(combinedEnqueued);
|
|
643
|
+
}
|
|
644
|
+
default:
|
|
645
|
+
throw new Error(`unknown state result: ${this.kind}`);
|
|
646
|
+
}
|
|
647
|
+
}
|
|
648
|
+
const choose = (a, b) => {
|
|
649
|
+
switch (a.kind) {
|
|
650
|
+
case 1 /* Ignored */:
|
|
651
|
+
return b;
|
|
652
|
+
case 2 /* InTransition */:
|
|
653
|
+
__privateGet(a, _executors).push(b.waitAll());
|
|
654
|
+
return a;
|
|
655
|
+
case 3 /* Rejected */:
|
|
656
|
+
case 4 /* Error */:
|
|
657
|
+
return a;
|
|
658
|
+
default:
|
|
659
|
+
return null;
|
|
660
|
+
}
|
|
661
|
+
};
|
|
662
|
+
return (choose(this, other) ?? choose(other, this) ?? this).withTimestamps(...__privateGet(this, _timestamps), ...__privateGet(other, _timestamps)).withSignal(__privateGet(this, _signal)).withHandlerName(__privateGet(this, _handler)).withStateUpdating(__privateGet(this, _state)).withStacktrace(__privateGet(this, _stacktrace)).withEnqueued(combinedEnqueued);
|
|
663
|
+
}
|
|
664
|
+
[Symbol.toStringTag]() {
|
|
665
|
+
if (__privateGet(this, _enqueued).length > 0) {
|
|
666
|
+
return `Result.Enqueue(${__privateGet(this, _enqueued).map(String).join(", ")})`;
|
|
667
|
+
}
|
|
668
|
+
return `Result.${ResultKind[this.kind]}()`;
|
|
669
|
+
}
|
|
670
|
+
[Symbol.toPrimitive]() {
|
|
671
|
+
if (__privateGet(this, _enqueued).length > 0) {
|
|
672
|
+
return `Enqueue(${__privateGet(this, _enqueued).map(String).join(", ")})`;
|
|
673
|
+
}
|
|
674
|
+
if (this.kind === 4 /* Error */) {
|
|
675
|
+
return this.error != null ? String(this.error) : ResultKind[this.kind];
|
|
676
|
+
}
|
|
677
|
+
return `${ResultKind[this.kind]}${this.message != null ? `: ${this.message}` : ""}`;
|
|
678
|
+
}
|
|
679
|
+
};
|
|
680
|
+
_executors = new WeakMap();
|
|
681
|
+
_enqueued = new WeakMap();
|
|
682
|
+
_promise = new WeakMap();
|
|
683
|
+
_expectedKinds = new WeakMap();
|
|
684
|
+
_timestamps = new WeakMap();
|
|
685
|
+
_signal = new WeakMap();
|
|
686
|
+
_handler = new WeakMap();
|
|
687
|
+
_state = new WeakMap();
|
|
688
|
+
_stacktrace = new WeakMap();
|
|
689
|
+
_meta = new WeakMap();
|
|
690
|
+
_Result_instances = new WeakSet();
|
|
691
|
+
/**
|
|
692
|
+
* Applies a previously-stored expect() assertion (from an InTransition
|
|
693
|
+
* result) to the FINAL resolved result. Throws if the final kind is not
|
|
694
|
+
* allowed. No-op when no expectation was registered.
|
|
695
|
+
* @internal
|
|
696
|
+
*/
|
|
697
|
+
assertExpected_fn = function(final) {
|
|
698
|
+
if (__privateGet(this, _expectedKinds) == null) {
|
|
699
|
+
return final;
|
|
700
|
+
}
|
|
701
|
+
if (!__privateGet(this, _expectedKinds).includes(final.kind)) {
|
|
702
|
+
throw new StateFlowError(
|
|
703
|
+
buildErrorMessages(
|
|
704
|
+
final.kind,
|
|
705
|
+
final.error,
|
|
706
|
+
final.message,
|
|
707
|
+
__privateGet(final, _signal) ?? __privateGet(this, _signal),
|
|
708
|
+
__privateGet(final, _handler) ?? __privateGet(this, _handler),
|
|
709
|
+
__privateGet(final, _state) ?? __privateGet(this, _state)
|
|
710
|
+
),
|
|
711
|
+
final.error
|
|
712
|
+
);
|
|
713
|
+
}
|
|
714
|
+
return final;
|
|
715
|
+
};
|
|
716
|
+
var Result = _Result;
|
|
717
|
+
function mergeStrings(a, b) {
|
|
718
|
+
if (a == null && b == null) {
|
|
719
|
+
return "";
|
|
720
|
+
}
|
|
721
|
+
if (a == null || a === "") {
|
|
722
|
+
return String(b);
|
|
723
|
+
}
|
|
724
|
+
if (b == null || b === "") {
|
|
725
|
+
return String(a);
|
|
726
|
+
}
|
|
727
|
+
return `${a}; ${b}`;
|
|
728
|
+
}
|
|
729
|
+
function withTimeout(promise, timeout) {
|
|
730
|
+
return new Promise((resolve, reject) => {
|
|
731
|
+
const timer = setTimeout(() => {
|
|
732
|
+
resolve(Result.error(new Error("timeout")));
|
|
733
|
+
}, timeout);
|
|
734
|
+
promise.then(
|
|
735
|
+
(res) => {
|
|
736
|
+
clearTimeout(timer);
|
|
737
|
+
resolve(res);
|
|
738
|
+
},
|
|
739
|
+
(err) => {
|
|
740
|
+
clearTimeout(timer);
|
|
741
|
+
reject(err);
|
|
742
|
+
}
|
|
743
|
+
);
|
|
744
|
+
});
|
|
745
|
+
}
|
|
746
|
+
function buildErrorMessages(got, error, message, signal = null, handler = null, stateUpd = null) {
|
|
747
|
+
let msg;
|
|
748
|
+
if (signal != null) {
|
|
749
|
+
msg = `[SF] Signal '${String(signal)}' `;
|
|
750
|
+
} else {
|
|
751
|
+
msg = "[SF] Result ";
|
|
752
|
+
}
|
|
753
|
+
if (handler != null) {
|
|
754
|
+
msg += `in handler '${handler}()' `;
|
|
755
|
+
}
|
|
756
|
+
switch (got) {
|
|
757
|
+
case 3 /* Rejected */:
|
|
758
|
+
msg += "was rejected";
|
|
759
|
+
break;
|
|
760
|
+
case 4 /* Error */:
|
|
761
|
+
msg += "thrown an error";
|
|
762
|
+
break;
|
|
763
|
+
case 2 /* InTransition */:
|
|
764
|
+
msg += "was in transition";
|
|
765
|
+
break;
|
|
766
|
+
case 0 /* OK */:
|
|
767
|
+
msg += "was ok";
|
|
768
|
+
break;
|
|
769
|
+
default:
|
|
770
|
+
msg += "was ignored";
|
|
771
|
+
}
|
|
772
|
+
if (stateUpd != null) {
|
|
773
|
+
msg += ` while tried to ${stateUpd}`;
|
|
774
|
+
}
|
|
775
|
+
if (error != null) {
|
|
776
|
+
msg += `: ${error}`;
|
|
777
|
+
}
|
|
778
|
+
if (message != null) {
|
|
779
|
+
msg += `: Message: ${message}`;
|
|
780
|
+
}
|
|
781
|
+
return msg;
|
|
782
|
+
}
|
|
783
|
+
var dispatchCounter = 0;
|
|
784
|
+
var ResultCollector = class {
|
|
785
|
+
constructor(flowName, signal, duringTransition = false) {
|
|
786
|
+
this.results = [];
|
|
787
|
+
this.entry = {
|
|
788
|
+
message: "",
|
|
789
|
+
flowName,
|
|
790
|
+
signal: String(signal),
|
|
791
|
+
isAsync: false,
|
|
792
|
+
startTime: Date.now(),
|
|
793
|
+
dispatchOrder: ++dispatchCounter,
|
|
794
|
+
finalStates: {},
|
|
795
|
+
stateChanges: [],
|
|
796
|
+
handlerResults: [],
|
|
797
|
+
observers: [],
|
|
798
|
+
enqueuedSignals: [],
|
|
799
|
+
stacktrace: null,
|
|
800
|
+
finalResult: ""
|
|
801
|
+
};
|
|
802
|
+
if (duringTransition) {
|
|
803
|
+
this.entry.duringTransition = true;
|
|
804
|
+
}
|
|
805
|
+
}
|
|
806
|
+
merge() {
|
|
807
|
+
if (this.results.length === 1 && this.results[0] != null) {
|
|
808
|
+
return this.results[0];
|
|
809
|
+
}
|
|
810
|
+
const final = mergeResults(this.results);
|
|
811
|
+
this.results = [final];
|
|
812
|
+
return final;
|
|
813
|
+
}
|
|
814
|
+
push(val) {
|
|
815
|
+
const res = val instanceof Result ? val : Result.error(val);
|
|
816
|
+
this.results.push(res);
|
|
817
|
+
}
|
|
818
|
+
logStateChange(stateName, oldState, newState) {
|
|
819
|
+
this.entry.stateChanges.push({
|
|
820
|
+
stateName,
|
|
821
|
+
oldState: String(oldState),
|
|
822
|
+
newState: String(newState)
|
|
823
|
+
});
|
|
824
|
+
}
|
|
825
|
+
logHandler(stateName, type, handlerName, result) {
|
|
826
|
+
this.entry.handlerResults.push({
|
|
827
|
+
type,
|
|
828
|
+
handlerName,
|
|
829
|
+
stateName,
|
|
830
|
+
result: String(result)
|
|
831
|
+
});
|
|
832
|
+
}
|
|
833
|
+
logEnqueue(signal, handlerName) {
|
|
834
|
+
this.entry.enqueuedSignals.push({ signal, fromHandler: handlerName });
|
|
835
|
+
}
|
|
836
|
+
logObserver(stateName, observerName, needObserve) {
|
|
837
|
+
this.entry.observers.push({
|
|
838
|
+
stateName,
|
|
839
|
+
observerName,
|
|
840
|
+
needObserve
|
|
841
|
+
});
|
|
842
|
+
}
|
|
843
|
+
async finish(snapshot, result) {
|
|
844
|
+
this.entry.stacktrace = result.stacktrace;
|
|
845
|
+
let finalResult = result;
|
|
846
|
+
if (result.kind === 2 /* InTransition */) {
|
|
847
|
+
const startTime = this.entry.startTime;
|
|
848
|
+
finalResult = await result.waitAll();
|
|
849
|
+
this.entry.isAsync = true;
|
|
850
|
+
this.entry.duration = Date.now() - startTime;
|
|
851
|
+
}
|
|
852
|
+
this.entry.finalStates = Object.fromEntries(Object.entries(snapshot).map(([k, v]) => [k, String(v)]));
|
|
853
|
+
this.entry.finalResult = String(finalResult);
|
|
854
|
+
const timing = this.entry.duration != null ? `[${this.entry.duration}ms] ` : "";
|
|
855
|
+
const stale = this.entry.duringTransition ? "(during transition) " : "";
|
|
856
|
+
this.entry.message = `[SF/${this.entry.flowName}] ${this.entry.signal} - ${this.entry.isAsync ? "async " : ""}${timing}${stale}${this.entry.finalResult}`;
|
|
857
|
+
return this.entry;
|
|
858
|
+
}
|
|
859
|
+
};
|
|
860
|
+
function mergeResults(results, ignoreMessage = "") {
|
|
861
|
+
return results.reduce((l, r) => r.merge(l), Result.ignore(ignoreMessage));
|
|
862
|
+
}
|
|
863
|
+
|
|
864
|
+
// src/state.ts
|
|
865
|
+
function isState(state) {
|
|
866
|
+
return typeof state === "object" && state != null && VARIANT in state;
|
|
867
|
+
}
|
|
868
|
+
function isStateDef(obj) {
|
|
869
|
+
return obj != null && typeof obj === "function" && SIGNALS in obj;
|
|
870
|
+
}
|
|
871
|
+
function isStateInstance(obj, name) {
|
|
872
|
+
if (obj == null || typeof obj !== "object" || !(VARIANT in obj) || !(Symbol.toStringTag in obj)) {
|
|
873
|
+
return false;
|
|
874
|
+
}
|
|
875
|
+
return String(obj[Symbol.toStringTag]).startsWith(name);
|
|
876
|
+
}
|
|
877
|
+
function stateAccepts(state, signalName) {
|
|
878
|
+
return signalName in state[VARIANT][HANDLERS];
|
|
879
|
+
}
|
|
880
|
+
function stateVar(obj) {
|
|
881
|
+
if (obj == null || typeof obj !== "object" || !(VARIANT in obj)) {
|
|
882
|
+
throw new StateFlowError("Not a state");
|
|
883
|
+
}
|
|
884
|
+
return obj[VARIANT];
|
|
885
|
+
}
|
|
886
|
+
function getInitialState(def) {
|
|
887
|
+
const svar = Object.values(def).find((x) => x[IS_INITIAL]);
|
|
888
|
+
if (svar == null) {
|
|
889
|
+
throw new StateFlowError("Initial state variant not found");
|
|
890
|
+
}
|
|
891
|
+
return svar;
|
|
892
|
+
}
|
|
893
|
+
function getName(def) {
|
|
894
|
+
return def[Symbol.toStringTag];
|
|
895
|
+
}
|
|
896
|
+
function factory(value, props) {
|
|
897
|
+
const name = String(value);
|
|
898
|
+
const inst = value[DEF][PARSER](props);
|
|
899
|
+
Object.defineProperties(inst, {
|
|
900
|
+
[Symbol.toStringTag]: { value: name, writable: false, configurable: false, enumerable: false },
|
|
901
|
+
[Symbol.toPrimitive]: {
|
|
902
|
+
value: () => `${name}(${value[DEF][STRING_REPR](inst)})`,
|
|
903
|
+
writable: false,
|
|
904
|
+
configurable: false,
|
|
905
|
+
enumerable: false
|
|
906
|
+
},
|
|
907
|
+
[VARIANT]: { value, writable: false, configurable: false, enumerable: false }
|
|
908
|
+
});
|
|
909
|
+
return Object.freeze(inst);
|
|
910
|
+
}
|
|
911
|
+
function extract(name, ctx) {
|
|
912
|
+
if (ctx == null || typeof ctx !== "object" || !(name in ctx)) {
|
|
913
|
+
throw new StateFlowError("State Flow object not found");
|
|
914
|
+
}
|
|
915
|
+
const state = Reflect.get(ctx, name);
|
|
916
|
+
if (!isStateInstance(state, name)) {
|
|
917
|
+
throw new StateFlowError(`State not found: ${name}`);
|
|
918
|
+
}
|
|
919
|
+
return state;
|
|
920
|
+
}
|
|
921
|
+
var _signals, _variants, _parser, _stringRepr, _name;
|
|
922
|
+
var StateBuilder = class {
|
|
923
|
+
constructor() {
|
|
924
|
+
__privateAdd(this, _signals);
|
|
925
|
+
__privateAdd(this, _variants, []);
|
|
926
|
+
__privateAdd(this, _parser, (v) => ({ ...v }));
|
|
927
|
+
__privateAdd(this, _stringRepr, (v) => serializeDebug(v));
|
|
928
|
+
__privateAdd(this, _name, "");
|
|
929
|
+
}
|
|
930
|
+
name(name) {
|
|
931
|
+
const self = this;
|
|
932
|
+
__privateSet(self, _name, name);
|
|
933
|
+
return self;
|
|
934
|
+
}
|
|
935
|
+
signals(signals) {
|
|
936
|
+
const self = this;
|
|
937
|
+
__privateSet(self, _signals, signals);
|
|
938
|
+
return self;
|
|
939
|
+
}
|
|
940
|
+
variant(variant, isInitial = false) {
|
|
941
|
+
const self = this;
|
|
942
|
+
if (isInitial && __privateGet(self, _variants).some(([_, i]) => i)) {
|
|
943
|
+
throw new StateFlowError("Only one initial state variant is allowed");
|
|
944
|
+
}
|
|
945
|
+
__privateGet(self, _variants).push([variant, isInitial]);
|
|
946
|
+
return self;
|
|
947
|
+
}
|
|
948
|
+
parser(func) {
|
|
949
|
+
__privateSet(this, _parser, func);
|
|
950
|
+
return this;
|
|
951
|
+
}
|
|
952
|
+
stringRepr(func) {
|
|
953
|
+
__privateSet(this, _stringRepr, func);
|
|
954
|
+
return this;
|
|
955
|
+
}
|
|
956
|
+
build() {
|
|
957
|
+
const signals = __privateGet(this, _signals);
|
|
958
|
+
const name = __privateGet(this, _name);
|
|
959
|
+
if (signals == null) {
|
|
960
|
+
throw new StateFlowError("Signals were not provided");
|
|
961
|
+
}
|
|
962
|
+
if (name === "") {
|
|
963
|
+
throw new StateFlowError("Name was not provided");
|
|
964
|
+
}
|
|
965
|
+
if (__privateGet(this, _variants).length === 0) {
|
|
966
|
+
throw new StateFlowError("No state variants are defined");
|
|
967
|
+
}
|
|
968
|
+
const def = extract.bind(null, name);
|
|
969
|
+
Object.assign(def, Object.fromEntries(__privateGet(this, _variants).map(([v, i]) => this.buildFactories(def, v, i))));
|
|
970
|
+
Object.defineProperties(def, {
|
|
971
|
+
[Symbol.toStringTag]: { value: __privateGet(this, _name), enumerable: false, configurable: false, writable: false },
|
|
972
|
+
[Symbol.toPrimitive]: { value: () => __privateGet(this, _name), enumerable: false, configurable: false, writable: false },
|
|
973
|
+
[SIGNALS]: { value: signals, enumerable: false, configurable: false, writable: false },
|
|
974
|
+
[PARSER]: { value: __privateGet(this, _parser), enumerable: false, configurable: false, writable: false },
|
|
975
|
+
[STRING_REPR]: { value: __privateGet(this, _stringRepr), enumerable: false, configurable: false, writable: false }
|
|
976
|
+
});
|
|
977
|
+
return Object.freeze(def);
|
|
978
|
+
}
|
|
979
|
+
buildFactories(def, variant, isInitial) {
|
|
980
|
+
const fn = Object.defineProperties(
|
|
981
|
+
function stateFactory(props) {
|
|
982
|
+
return factory(fn, props);
|
|
983
|
+
},
|
|
984
|
+
{
|
|
985
|
+
[Symbol.toStringTag]: { value: variant, enumerable: false, writable: false, configurable: false },
|
|
986
|
+
[Symbol.toPrimitive]: {
|
|
987
|
+
value: () => `${String(def)}.${String(variant)}`,
|
|
988
|
+
enumerable: false,
|
|
989
|
+
writable: false,
|
|
990
|
+
configurable: false
|
|
991
|
+
},
|
|
992
|
+
[DEF]: { value: def, enumerable: false, writable: false, configurable: false },
|
|
993
|
+
[HANDLERS]: { value: {}, enumerable: false, writable: false, configurable: false },
|
|
994
|
+
[IS_INITIAL]: { value: isInitial, enumerable: false, writable: false, configurable: false }
|
|
995
|
+
}
|
|
996
|
+
);
|
|
997
|
+
return [variant, fn];
|
|
998
|
+
}
|
|
999
|
+
};
|
|
1000
|
+
_signals = new WeakMap();
|
|
1001
|
+
_variants = new WeakMap();
|
|
1002
|
+
_parser = new WeakMap();
|
|
1003
|
+
_stringRepr = new WeakMap();
|
|
1004
|
+
_name = new WeakMap();
|
|
1005
|
+
function defineState() {
|
|
1006
|
+
return new StateBuilder();
|
|
1007
|
+
}
|
|
1008
|
+
function defineFlow(state, handlers) {
|
|
1009
|
+
if (Object.isFrozen(state[HANDLERS])) {
|
|
1010
|
+
throw new StateFlowError(`Flow is already defined: ${String(state)}`);
|
|
1011
|
+
}
|
|
1012
|
+
Object.assign(state[HANDLERS], handlers);
|
|
1013
|
+
Object.freeze(state[HANDLERS]);
|
|
1014
|
+
}
|
|
1015
|
+
function handleSignal(state, signal, ctx) {
|
|
1016
|
+
const signalName = signal[Symbol.toStringTag];
|
|
1017
|
+
if (!stateAccepts(state, signalName)) {
|
|
1018
|
+
return Result.ignore("");
|
|
1019
|
+
}
|
|
1020
|
+
const handler = state[VARIANT][HANDLERS][signalName];
|
|
1021
|
+
try {
|
|
1022
|
+
const result = handler(state, signal, ctx);
|
|
1023
|
+
if (result instanceof Result) {
|
|
1024
|
+
return result;
|
|
1025
|
+
}
|
|
1026
|
+
if (isState(result)) {
|
|
1027
|
+
return Result.state(result);
|
|
1028
|
+
}
|
|
1029
|
+
return Result.state(state[VARIANT](result));
|
|
1030
|
+
} catch (err) {
|
|
1031
|
+
return Result.error(err);
|
|
1032
|
+
}
|
|
1033
|
+
}
|
|
1034
|
+
|
|
1035
|
+
// src/flow.ts
|
|
1036
|
+
var managementStateMap = /* @__PURE__ */ new WeakMap();
|
|
1037
|
+
var observersMap = /* @__PURE__ */ new WeakMap();
|
|
1038
|
+
var currentRC = {
|
|
1039
|
+
ref: null,
|
|
1040
|
+
set(rc) {
|
|
1041
|
+
this.ref = rc;
|
|
1042
|
+
},
|
|
1043
|
+
clear() {
|
|
1044
|
+
this.ref = null;
|
|
1045
|
+
}
|
|
1046
|
+
};
|
|
1047
|
+
function eventKey(kind, state) {
|
|
1048
|
+
return `${kind}:${state}`;
|
|
1049
|
+
}
|
|
1050
|
+
function stateMeta(target) {
|
|
1051
|
+
const meta = managementStateMap.get(target);
|
|
1052
|
+
if (meta == null) {
|
|
1053
|
+
throw new StateFlowError(`${buildName(target)} doesn't contains state flows`);
|
|
1054
|
+
}
|
|
1055
|
+
return meta;
|
|
1056
|
+
}
|
|
1057
|
+
function addStateHandler(emitter, kind, stateVar2, cb, ctx) {
|
|
1058
|
+
const wrapper = (state, snapshot, stateUpd, collector) => {
|
|
1059
|
+
const handlerName = buildName(ctx, cb);
|
|
1060
|
+
try {
|
|
1061
|
+
const res = cb.apply(ctx, [state, snapshot]);
|
|
1062
|
+
if (!(res instanceof Result)) {
|
|
1063
|
+
throw new StateFlowError(`Handler '${handlerName}' returned not result`);
|
|
1064
|
+
}
|
|
1065
|
+
collector?.logHandler(String(stateVar2[DEF]), kind, handlerName, res);
|
|
1066
|
+
collector?.push(res.withHandlerName(handlerName).withStateUpdating(stateUpd));
|
|
1067
|
+
} catch (err) {
|
|
1068
|
+
collector?.logHandler(String(stateVar2[DEF]), kind, handlerName, err);
|
|
1069
|
+
collector?.push(Result.error(err).withHandlerName(handlerName).withStateUpdating(stateUpd));
|
|
1070
|
+
}
|
|
1071
|
+
};
|
|
1072
|
+
emitter.on(eventKey(kind, String(stateVar2)), wrapper);
|
|
1073
|
+
}
|
|
1074
|
+
function applyFlow(target, states, initializer, config = {}) {
|
|
1075
|
+
for (const def of states) {
|
|
1076
|
+
if (!isStateDef(def)) {
|
|
1077
|
+
throw new StateFlowError("Incorrect state definition");
|
|
1078
|
+
}
|
|
1079
|
+
const name = getName(def);
|
|
1080
|
+
const st = target[name];
|
|
1081
|
+
if (st == null) {
|
|
1082
|
+
throw new StateFlowError(`State object ${name} not found`);
|
|
1083
|
+
}
|
|
1084
|
+
Object.defineProperty(target, name, { value: getInitialState(def)(st) });
|
|
1085
|
+
}
|
|
1086
|
+
const logHandlers = config.logHandlers?.length ? config.logHandlers : [consoleLogHandler];
|
|
1087
|
+
const emitter = new EventEmitter();
|
|
1088
|
+
managementStateMap.set(target, {
|
|
1089
|
+
emitter,
|
|
1090
|
+
logHandlers,
|
|
1091
|
+
states,
|
|
1092
|
+
transitioning: null,
|
|
1093
|
+
lockHolder: null,
|
|
1094
|
+
lockQueue: [],
|
|
1095
|
+
logGroup: null,
|
|
1096
|
+
name: String(Symbol.toStringTag in target ? target[Symbol.toStringTag] : target.constructor.name)
|
|
1097
|
+
});
|
|
1098
|
+
initializer({
|
|
1099
|
+
addEnterHandler: addStateHandler.bind(null, emitter, "enter"),
|
|
1100
|
+
addUpdateHandler: addStateHandler.bind(null, emitter, "update"),
|
|
1101
|
+
addExitHandler: addStateHandler.bind(null, emitter, "exit"),
|
|
1102
|
+
addRollbackHandler: addStateHandler.bind(null, emitter, "rollback")
|
|
1103
|
+
});
|
|
1104
|
+
}
|
|
1105
|
+
function prepareSnapshot(target) {
|
|
1106
|
+
const meta = managementStateMap.get(target);
|
|
1107
|
+
if (meta == null) {
|
|
1108
|
+
return {};
|
|
1109
|
+
}
|
|
1110
|
+
return Object.fromEntries(
|
|
1111
|
+
meta.states.map((s) => [s[Symbol.toStringTag], Reflect.get(target, s[Symbol.toStringTag])])
|
|
1112
|
+
);
|
|
1113
|
+
}
|
|
1114
|
+
function dispatch(target, signal, mute = false) {
|
|
1115
|
+
const meta = stateMeta(target);
|
|
1116
|
+
if (meta.lockHolder != null) {
|
|
1117
|
+
throw new StateFlowError("Lock is held. Use `await using send = lock(target)` for queued access");
|
|
1118
|
+
}
|
|
1119
|
+
if (meta.transitioning != null) {
|
|
1120
|
+
throw new StateFlowError("States are in transitioning. Use `await sync(obj)` or await a previous result");
|
|
1121
|
+
}
|
|
1122
|
+
const chainSnapshot = prepareSnapshot(target);
|
|
1123
|
+
const result = dispatchCore(target, signal, meta, mute);
|
|
1124
|
+
return processEnqueueChain(target, result, meta, chainSnapshot, mute);
|
|
1125
|
+
}
|
|
1126
|
+
async function lock(target, label) {
|
|
1127
|
+
const meta = stateMeta(target);
|
|
1128
|
+
const id = /* @__PURE__ */ Symbol("lock");
|
|
1129
|
+
const dispatchContext = label != null ? getGlobalDispatchContext() : null;
|
|
1130
|
+
const lockRequestedAt = dispatchContext != null ? Date.now() : 0;
|
|
1131
|
+
if (meta.lockHolder != null || meta.lockQueue.length > 0) {
|
|
1132
|
+
await new Promise((resolve) => meta.lockQueue.push({ id, resolve }));
|
|
1133
|
+
} else {
|
|
1134
|
+
meta.lockHolder = id;
|
|
1135
|
+
}
|
|
1136
|
+
await sync(target);
|
|
1137
|
+
if (label != null) {
|
|
1138
|
+
const lockWaitMs = dispatchContext != null ? Date.now() - lockRequestedAt : 0;
|
|
1139
|
+
const context = dispatchContext != null && lockWaitMs >= 5 ? `${dispatchContext} [lock ${lockWaitMs}ms]` : dispatchContext;
|
|
1140
|
+
meta.logGroup = { label, pending: [], context };
|
|
1141
|
+
}
|
|
1142
|
+
let chainSnapshot = null;
|
|
1143
|
+
const dispose = async () => {
|
|
1144
|
+
if (meta.lockHolder !== id) {
|
|
1145
|
+
return;
|
|
1146
|
+
}
|
|
1147
|
+
if (meta.transitioning instanceof Promise) {
|
|
1148
|
+
await meta.transitioning;
|
|
1149
|
+
}
|
|
1150
|
+
chainSnapshot = null;
|
|
1151
|
+
const group = meta.logGroup;
|
|
1152
|
+
if (group != null) {
|
|
1153
|
+
meta.logGroup = null;
|
|
1154
|
+
const settled = await Promise.allSettled(group.pending);
|
|
1155
|
+
const entries = [];
|
|
1156
|
+
for (const outcome of settled) {
|
|
1157
|
+
if (outcome.status === "fulfilled") {
|
|
1158
|
+
entries.push(outcome.value);
|
|
1159
|
+
}
|
|
1160
|
+
}
|
|
1161
|
+
emitGrouped(withGlobalLogHandlers(meta.logHandlers), group.label, entries, group.context);
|
|
1162
|
+
}
|
|
1163
|
+
const next = meta.lockQueue.shift();
|
|
1164
|
+
if (next) {
|
|
1165
|
+
meta.lockHolder = next.id;
|
|
1166
|
+
next.resolve();
|
|
1167
|
+
} else {
|
|
1168
|
+
meta.lockHolder = null;
|
|
1169
|
+
}
|
|
1170
|
+
};
|
|
1171
|
+
const send = Object.assign(
|
|
1172
|
+
(signal, mute = false) => {
|
|
1173
|
+
if (meta.lockHolder !== id) {
|
|
1174
|
+
throw new StateFlowError("Lock has been released");
|
|
1175
|
+
}
|
|
1176
|
+
if (chainSnapshot == null) {
|
|
1177
|
+
chainSnapshot = prepareSnapshot(target);
|
|
1178
|
+
}
|
|
1179
|
+
const result = dispatchCore(target, signal, meta, mute);
|
|
1180
|
+
return processEnqueueChain(target, result, meta, chainSnapshot, mute);
|
|
1181
|
+
},
|
|
1182
|
+
{ [Symbol.asyncDispose]: dispose }
|
|
1183
|
+
);
|
|
1184
|
+
return send;
|
|
1185
|
+
}
|
|
1186
|
+
function dispatchCore(target, signal, meta, mute) {
|
|
1187
|
+
const st = new Error().stack;
|
|
1188
|
+
const collector = new ResultCollector(meta.name, signal, meta.transitioning != null);
|
|
1189
|
+
let result = Result.ignore("");
|
|
1190
|
+
let snapshot = prepareSnapshot(target);
|
|
1191
|
+
try {
|
|
1192
|
+
result = prepareStatesAfterSignal(target, signal, meta.states, collector);
|
|
1193
|
+
if (!result.in(0 /* OK */) || result.data == null) {
|
|
1194
|
+
return result.withSignal(signal).withStacktrace(st ?? null);
|
|
1195
|
+
}
|
|
1196
|
+
const processResult = result.data;
|
|
1197
|
+
snapshot = processResult.snapshot;
|
|
1198
|
+
for (const kind of ["exit", "update", "enter"]) {
|
|
1199
|
+
processHandlers(kind, meta.emitter, processResult[kind], snapshot, collector);
|
|
1200
|
+
result = collector.merge();
|
|
1201
|
+
if (result.in(3 /* Rejected */, 4 /* Error */)) {
|
|
1202
|
+
processHandlers("rollback", meta.emitter, processResult.rollback, prepareSnapshot(target), collector);
|
|
1203
|
+
return result.withSignal(signal).withStacktrace(st ?? null);
|
|
1204
|
+
}
|
|
1205
|
+
}
|
|
1206
|
+
result = result.merge(Result.ok());
|
|
1207
|
+
for (const enqueued of result.enqueuedSignals) {
|
|
1208
|
+
collector.logEnqueue(String(enqueued), result.message ?? "handler");
|
|
1209
|
+
}
|
|
1210
|
+
if (result.enqueuedSignals.length > 1) {
|
|
1211
|
+
console.warn(
|
|
1212
|
+
`[SF/${meta.name}] ${String(signal)}: ${result.enqueuedSignals.length} signals were enqueued in a single dispatch cycle by different handlers (${result.enqueuedSignals.map(String).join(", ")}). Only ONE enqueue per cycle (the self-terminating record chain) is supported; co-enqueuing from multiple handlers is unsupported and may not behave as expected.`
|
|
1213
|
+
);
|
|
1214
|
+
}
|
|
1215
|
+
if (result.kind !== 2 /* InTransition */) {
|
|
1216
|
+
meta.transitioning = true;
|
|
1217
|
+
applyState(target, snapshot, collector);
|
|
1218
|
+
} else {
|
|
1219
|
+
meta.transitioning = result.withMeta(meta).waitAll().then((res) => {
|
|
1220
|
+
meta.transitioning = null;
|
|
1221
|
+
if (res.kind === 0 /* OK */) {
|
|
1222
|
+
const preTransitionSnap = res.enqueuedSignals.length > 0 ? prepareSnapshot(target) : null;
|
|
1223
|
+
applyState(target, snapshot, collector);
|
|
1224
|
+
if (preTransitionSnap && res.enqueuedSignals.length > 0) {
|
|
1225
|
+
const enqResult = processEnqueueChain(target, res, meta, preTransitionSnap, mute);
|
|
1226
|
+
if (enqResult.in(3 /* Rejected */, 4 /* Error */)) {
|
|
1227
|
+
return enqResult;
|
|
1228
|
+
}
|
|
1229
|
+
}
|
|
1230
|
+
} else if (res.in(3 /* Rejected */, 4 /* Error */)) {
|
|
1231
|
+
processHandlers("rollback", meta.emitter, processResult.rollback, prepareSnapshot(target), collector);
|
|
1232
|
+
}
|
|
1233
|
+
return res;
|
|
1234
|
+
});
|
|
1235
|
+
}
|
|
1236
|
+
return result.withMeta(meta).withSignal(signal).withStacktrace(st ?? null);
|
|
1237
|
+
} finally {
|
|
1238
|
+
if (meta.transitioning === true) {
|
|
1239
|
+
meta.transitioning = null;
|
|
1240
|
+
}
|
|
1241
|
+
if (!mute) {
|
|
1242
|
+
const finished = collector.finish(snapshot, result);
|
|
1243
|
+
const group = meta.logGroup;
|
|
1244
|
+
if (group != null) {
|
|
1245
|
+
group.pending.push(finished);
|
|
1246
|
+
} else {
|
|
1247
|
+
finished.then((entry) => {
|
|
1248
|
+
withGlobalLogHandlers(meta.logHandlers).forEach((handler) => {
|
|
1249
|
+
handler(entry);
|
|
1250
|
+
});
|
|
1251
|
+
}).catch(() => {
|
|
1252
|
+
});
|
|
1253
|
+
}
|
|
1254
|
+
}
|
|
1255
|
+
}
|
|
1256
|
+
}
|
|
1257
|
+
function processEnqueueChain(target, result, meta, chainSnapshot, mute) {
|
|
1258
|
+
let current = result;
|
|
1259
|
+
while (current.enqueuedSignals.length > 0) {
|
|
1260
|
+
const signals = [...current.enqueuedSignals];
|
|
1261
|
+
for (const signal of signals) {
|
|
1262
|
+
const enqResult = dispatchCore(target, signal, meta, mute);
|
|
1263
|
+
if (enqResult.in(3 /* Rejected */, 4 /* Error */)) {
|
|
1264
|
+
rollbackToSnapshot(target, chainSnapshot);
|
|
1265
|
+
return enqResult;
|
|
1266
|
+
}
|
|
1267
|
+
current = enqResult;
|
|
1268
|
+
}
|
|
1269
|
+
}
|
|
1270
|
+
return current;
|
|
1271
|
+
}
|
|
1272
|
+
function rollbackToSnapshot(target, snapshot) {
|
|
1273
|
+
for (const [key, value] of Object.entries(snapshot)) {
|
|
1274
|
+
Reflect.set(target, key, value);
|
|
1275
|
+
}
|
|
1276
|
+
}
|
|
1277
|
+
function prepareStatesAfterSignal(target, signal, states, collector) {
|
|
1278
|
+
const processResult = {
|
|
1279
|
+
snapshot: Object.fromEntries(states.map((s) => [s[Symbol.toStringTag], s(target)])),
|
|
1280
|
+
enter: [],
|
|
1281
|
+
update: [],
|
|
1282
|
+
exit: [],
|
|
1283
|
+
rollback: []
|
|
1284
|
+
};
|
|
1285
|
+
const results = [];
|
|
1286
|
+
const unhandled = [];
|
|
1287
|
+
for (const def of states) {
|
|
1288
|
+
const name = getName(def);
|
|
1289
|
+
const oldState = def(target);
|
|
1290
|
+
if (!stateAccepts(oldState, signal[Symbol.toStringTag])) {
|
|
1291
|
+
unhandled.push(String(oldState[Symbol.toStringTag]));
|
|
1292
|
+
}
|
|
1293
|
+
const result = handleSignal(oldState, signal, processResult.snapshot);
|
|
1294
|
+
switch (result.kind) {
|
|
1295
|
+
// state was successfully changed
|
|
1296
|
+
case 0 /* OK */: {
|
|
1297
|
+
const newState = result.data;
|
|
1298
|
+
const desc = `'${oldState}->${newState}'`;
|
|
1299
|
+
processResult.snapshot[name] = newState;
|
|
1300
|
+
collector.logStateChange(def[Symbol.toStringTag], oldState, newState);
|
|
1301
|
+
if (newState[VARIANT] === oldState[VARIANT]) {
|
|
1302
|
+
processResult.update.push([newState, `update ${desc}`]);
|
|
1303
|
+
} else {
|
|
1304
|
+
processResult.enter.push([newState, `enter to ${desc}`]);
|
|
1305
|
+
processResult.exit.push([oldState, `exit from ${desc}`]);
|
|
1306
|
+
}
|
|
1307
|
+
processResult.rollback.push([oldState, `rollback ${desc}`]);
|
|
1308
|
+
break;
|
|
1309
|
+
}
|
|
1310
|
+
// the signal was ignored completely
|
|
1311
|
+
case 1 /* Ignored */:
|
|
1312
|
+
processResult.snapshot[name] = oldState;
|
|
1313
|
+
break;
|
|
1314
|
+
// got incorrect result
|
|
1315
|
+
case 2 /* InTransition */:
|
|
1316
|
+
return Result.error(new StateFlowError("Transition is not allowed in flow handlers")).withSignal(
|
|
1317
|
+
signal
|
|
1318
|
+
);
|
|
1319
|
+
// Error or Rejected are left
|
|
1320
|
+
default:
|
|
1321
|
+
return result;
|
|
1322
|
+
}
|
|
1323
|
+
results.push(result);
|
|
1324
|
+
}
|
|
1325
|
+
const final = mergeResults(results);
|
|
1326
|
+
if (final.kind !== 0 /* OK */) {
|
|
1327
|
+
if (final.kind === 1 /* Ignored */ && final.message == null && unhandled.length === states.length) {
|
|
1328
|
+
return Result.ignore(`no handler in ${unhandled.join(", ")}`);
|
|
1329
|
+
}
|
|
1330
|
+
return final;
|
|
1331
|
+
}
|
|
1332
|
+
return Result.ok(processResult);
|
|
1333
|
+
}
|
|
1334
|
+
function applyState(target, snapshot, rc) {
|
|
1335
|
+
const meta = stateMeta(target);
|
|
1336
|
+
currentRC.set(rc);
|
|
1337
|
+
for (const [key, value] of Object.entries(snapshot)) {
|
|
1338
|
+
if (value !== Reflect.get(target, key)) {
|
|
1339
|
+
meta.emitter.emit(eventKey("observe", String(value[VARIANT])), Reflect.get(target, key), value);
|
|
1340
|
+
Reflect.set(target, key, value);
|
|
1341
|
+
}
|
|
1342
|
+
}
|
|
1343
|
+
currentRC.clear();
|
|
1344
|
+
}
|
|
1345
|
+
function processHandlers(kind, emitter, states, snapshot, collector) {
|
|
1346
|
+
for (const [state, desc] of states) {
|
|
1347
|
+
const key = eventKey(kind, String(state[VARIANT]));
|
|
1348
|
+
emitter.emit(key, snapshot[state[VARIANT][DEF][Symbol.toStringTag]], snapshot, desc, collector);
|
|
1349
|
+
}
|
|
1350
|
+
}
|
|
1351
|
+
async function sync(target) {
|
|
1352
|
+
const meta = stateMeta(target);
|
|
1353
|
+
const transitioning = meta.transitioning;
|
|
1354
|
+
if (transitioning instanceof Promise) {
|
|
1355
|
+
return new Promise((r) => {
|
|
1356
|
+
const wait = () => {
|
|
1357
|
+
if (meta.transitioning == null) {
|
|
1358
|
+
r();
|
|
1359
|
+
return;
|
|
1360
|
+
}
|
|
1361
|
+
transitioning.finally(() => setTimeout(wait, 0)).catch(() => {
|
|
1362
|
+
});
|
|
1363
|
+
};
|
|
1364
|
+
wait();
|
|
1365
|
+
});
|
|
1366
|
+
}
|
|
1367
|
+
}
|
|
1368
|
+
function observe(target, stateVariants, handlerFn, compareFn = (a, b) => a !== b, ctx) {
|
|
1369
|
+
const meta = stateMeta(target);
|
|
1370
|
+
for (const stateVar2 of stateVariants) {
|
|
1371
|
+
subscribe(meta.emitter, stateVar2, compareFn, handlerFn, ctx);
|
|
1372
|
+
}
|
|
1373
|
+
return {
|
|
1374
|
+
[Symbol.dispose]: () => {
|
|
1375
|
+
for (const stateVar2 of stateVariants) {
|
|
1376
|
+
unsubscribe(meta.emitter, stateVar2, handlerFn);
|
|
1377
|
+
}
|
|
1378
|
+
}
|
|
1379
|
+
};
|
|
1380
|
+
}
|
|
1381
|
+
function subscribe(emitter, stateVar2, cmpFn, hdlFn, ctx) {
|
|
1382
|
+
const key = eventKey("observe", String(stateVar2));
|
|
1383
|
+
const cb = (a, b) => {
|
|
1384
|
+
const needObserve = cmpFn(a, b);
|
|
1385
|
+
currentRC.ref?.logObserver(String(stateVar2[DEF]), buildName(ctx, hdlFn), needObserve);
|
|
1386
|
+
if (needObserve) {
|
|
1387
|
+
setTimeout(() => hdlFn(b), 0);
|
|
1388
|
+
}
|
|
1389
|
+
};
|
|
1390
|
+
emitter.on(key, cb);
|
|
1391
|
+
observersMap.set(hdlFn, [...observersMap.get(hdlFn) ?? [], cb]);
|
|
1392
|
+
}
|
|
1393
|
+
function unsubscribe(emitter, stateVar2, hdlFn) {
|
|
1394
|
+
const key = eventKey("observe", String(stateVar2));
|
|
1395
|
+
const cbs = observersMap.get(hdlFn);
|
|
1396
|
+
cbs?.forEach((cb) => {
|
|
1397
|
+
emitter.off(key, cb);
|
|
1398
|
+
});
|
|
1399
|
+
}
|
|
1400
|
+
function disposeFlow(target) {
|
|
1401
|
+
const meta = stateMeta(target);
|
|
1402
|
+
meta.emitter.removeAllListeners();
|
|
1403
|
+
Reflect.deleteProperty(meta, "emitter");
|
|
1404
|
+
for (const state of meta.states) {
|
|
1405
|
+
Reflect.deleteProperty(target, state[Symbol.toStringTag]);
|
|
1406
|
+
}
|
|
1407
|
+
}
|
|
1408
|
+
|
|
1409
|
+
// src/signal.ts
|
|
1410
|
+
function defineSignal(name, stringRepr = (args) => serializeDebug(args)) {
|
|
1411
|
+
const fn = (p) => {
|
|
1412
|
+
return Object.freeze({
|
|
1413
|
+
...p,
|
|
1414
|
+
[SIGNAL]: true,
|
|
1415
|
+
[Symbol.toStringTag]: name,
|
|
1416
|
+
[Symbol.toPrimitive]: () => `${name}{${stringRepr(p)}}`
|
|
1417
|
+
});
|
|
1418
|
+
};
|
|
1419
|
+
Reflect.defineProperty(fn, Symbol.toStringTag, {
|
|
1420
|
+
value: name,
|
|
1421
|
+
writable: false,
|
|
1422
|
+
configurable: false,
|
|
1423
|
+
enumerable: false
|
|
1424
|
+
});
|
|
1425
|
+
return fn;
|
|
1426
|
+
}
|
|
1427
|
+
|
|
1428
|
+
// src/index.ts
|
|
1429
|
+
globalThis.__STATE_FLOW__ ?? (globalThis.__STATE_FLOW__ = {
|
|
1430
|
+
version: `${package_json_default.version} (${package_json_default.branch} <${package_json_default.commit}>)`
|
|
1431
|
+
});
|
|
1432
|
+
export {
|
|
1433
|
+
PARSER,
|
|
1434
|
+
Result,
|
|
1435
|
+
ResultCollector,
|
|
1436
|
+
ResultKind,
|
|
1437
|
+
SIGNALS,
|
|
1438
|
+
STRING_REPR,
|
|
1439
|
+
StateFlowError,
|
|
1440
|
+
VARIANT,
|
|
1441
|
+
addGlobalLogHandler,
|
|
1442
|
+
applyFlow,
|
|
1443
|
+
consoleLogHandler,
|
|
1444
|
+
defineFlow,
|
|
1445
|
+
defineSignal,
|
|
1446
|
+
defineState,
|
|
1447
|
+
dispatch,
|
|
1448
|
+
disposeFlow,
|
|
1449
|
+
isState,
|
|
1450
|
+
lock,
|
|
1451
|
+
observe,
|
|
1452
|
+
serializeDebug,
|
|
1453
|
+
setConsoleLogSilenced,
|
|
1454
|
+
setGlobalDispatchContextProvider,
|
|
1455
|
+
stateVar,
|
|
1456
|
+
sync
|
|
1457
|
+
};
|
|
1458
|
+
//# sourceMappingURL=index.mjs.map
|