@rotorsoft/act 1.32.30 → 1.33.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 +0 -1
- package/dist/.tsbuildinfo +1 -1
- package/dist/@types/act.d.ts +41 -233
- package/dist/@types/act.d.ts.map +1 -1
- package/dist/@types/adapters/in-memory-store.d.ts.map +1 -1
- package/dist/@types/builders/act-builder.d.ts +2 -115
- package/dist/@types/builders/act-builder.d.ts.map +1 -1
- package/dist/@types/builders/builder-utils.d.ts +1 -1
- package/dist/@types/builders/builder-utils.d.ts.map +1 -1
- package/dist/@types/builders/event-builder.d.ts +5 -5
- package/dist/@types/builders/event-builder.d.ts.map +1 -1
- package/dist/@types/builders/merge.d.ts +1 -1
- package/dist/@types/builders/merge.d.ts.map +1 -1
- package/dist/@types/builders/projection-builder.d.ts +2 -144
- package/dist/@types/builders/projection-builder.d.ts.map +1 -1
- package/dist/@types/builders/slice-builder.d.ts +2 -71
- package/dist/@types/builders/slice-builder.d.ts.map +1 -1
- package/dist/@types/builders/state-builder.d.ts +2 -376
- package/dist/@types/builders/state-builder.d.ts.map +1 -1
- package/dist/@types/config.d.ts +6 -43
- package/dist/@types/config.d.ts.map +1 -1
- package/dist/@types/csv.d.ts +1 -15
- package/dist/@types/csv.d.ts.map +1 -1
- package/dist/@types/index.d.ts +0 -1
- package/dist/@types/index.d.ts.map +1 -1
- package/dist/@types/internal/autoclose-policy.d.ts +1 -59
- package/dist/@types/internal/autoclose-policy.d.ts.map +1 -1
- package/dist/@types/internal/autoclose-reaction.d.ts +1 -2
- package/dist/@types/internal/autoclose-reaction.d.ts.map +1 -1
- package/dist/@types/internal/autoclose-window.d.ts +1 -1
- package/dist/@types/internal/autoclose-window.d.ts.map +1 -1
- package/dist/@types/internal/circuit-breaker.d.ts +1 -2
- package/dist/@types/internal/circuit-breaker.d.ts.map +1 -1
- package/dist/@types/{builders/build-classify.d.ts → internal/classify.d.ts} +2 -15
- package/dist/@types/internal/classify.d.ts.map +1 -0
- package/dist/@types/internal/close-cycle.d.ts.map +1 -1
- package/dist/@types/internal/config.d.ts +2 -42
- package/dist/@types/internal/config.d.ts.map +1 -1
- package/dist/@types/internal/correlate-cycle.d.ts +1 -1
- package/dist/@types/internal/correlate-cycle.d.ts.map +1 -1
- package/dist/@types/internal/{correlator.d.ts → correlation-id.d.ts} +1 -1
- package/dist/@types/internal/correlation-id.d.ts.map +1 -0
- package/dist/@types/internal/defer-config.d.ts.map +1 -1
- package/dist/@types/internal/drain-cycle.d.ts +1 -1
- package/dist/@types/internal/drain-cycle.d.ts.map +1 -1
- package/dist/@types/internal/{drain.d.ts → drain-ops.d.ts} +1 -1
- package/dist/@types/internal/drain-ops.d.ts.map +1 -0
- package/dist/@types/internal/event-sourcing.d.ts +1 -8
- package/dist/@types/internal/event-sourcing.d.ts.map +1 -1
- package/dist/@types/internal/{sensitive.d.ts → pii.d.ts} +4 -71
- package/dist/@types/internal/pii.d.ts.map +1 -0
- package/dist/@types/internal/projection-fold.d.ts +1 -2
- package/dist/@types/internal/projection-fold.d.ts.map +1 -1
- package/dist/@types/internal/{close-signal.d.ts → reaction-dispatch.d.ts} +47 -2
- package/dist/@types/internal/reaction-dispatch.d.ts.map +1 -0
- package/dist/@types/internal/report-once.d.ts +1 -1
- package/dist/@types/internal/settle.d.ts +21 -12
- package/dist/@types/internal/settle.d.ts.map +1 -1
- package/dist/@types/internal/tracing.d.ts +3 -3
- package/dist/@types/internal/tracing.d.ts.map +1 -1
- package/dist/@types/ports.d.ts +3 -43
- package/dist/@types/ports.d.ts.map +1 -1
- package/dist/@types/scoped.d.ts +1 -6
- package/dist/@types/scoped.d.ts.map +1 -1
- package/dist/@types/sensitive.d.ts +56 -0
- package/dist/@types/sensitive.d.ts.map +1 -0
- package/dist/@types/test/index.d.ts +1 -0
- package/dist/@types/test/index.d.ts.map +1 -1
- package/dist/@types/test/sandbox.d.ts +2 -26
- package/dist/@types/test/sandbox.d.ts.map +1 -1
- package/dist/@types/types/act.d.ts +216 -0
- package/dist/@types/types/act.d.ts.map +1 -0
- package/dist/@types/types/action.d.ts +67 -0
- package/dist/@types/types/action.d.ts.map +1 -1
- package/dist/@types/types/builders.d.ts +722 -0
- package/dist/@types/types/builders.d.ts.map +1 -0
- package/dist/@types/types/errors.d.ts +31 -1
- package/dist/@types/types/errors.d.ts.map +1 -1
- package/dist/@types/types/index.d.ts +38 -0
- package/dist/@types/types/index.d.ts.map +1 -1
- package/dist/@types/types/ports.d.ts +15 -9
- package/dist/@types/types/ports.d.ts.map +1 -1
- package/dist/@types/types/reaction.d.ts +11 -1
- package/dist/@types/types/reaction.d.ts.map +1 -1
- package/dist/@types/types/sandbox.d.ts +31 -0
- package/dist/@types/types/sandbox.d.ts.map +1 -0
- package/dist/@types/types/schemas.d.ts +12 -17
- package/dist/@types/types/schemas.d.ts.map +1 -1
- package/dist/{chunk-HHKLVRR4.js → chunk-2PMUONGF.js} +38 -57
- package/dist/chunk-2PMUONGF.js.map +1 -0
- package/dist/chunk-HF433MAF.cjs +264 -0
- package/dist/chunk-HF433MAF.cjs.map +1 -0
- package/dist/{chunk-X3MMQIIP.js → chunk-JABYJGT7.js} +174 -22
- package/dist/chunk-JABYJGT7.js.map +1 -0
- package/dist/chunk-VE3MTY4A.cjs +1233 -0
- package/dist/chunk-VE3MTY4A.cjs.map +1 -0
- package/dist/index.cjs +1645 -3041
- package/dist/index.cjs.map +1 -1
- package/dist/index.js +751 -813
- package/dist/index.js.map +1 -1
- package/dist/test/index.cjs +16 -1113
- package/dist/test/index.cjs.map +1 -1
- package/dist/test/index.js +2 -2
- package/dist/test/index.js.map +1 -1
- package/dist/types/index.cjs +58 -276
- package/dist/types/index.cjs.map +1 -1
- package/dist/types/index.js +15 -1
- package/package.json +1 -1
- package/dist/@types/builders/build-classify.d.ts.map +0 -1
- package/dist/@types/builders/builder-base.d.ts +0 -70
- package/dist/@types/builders/builder-base.d.ts.map +0 -1
- package/dist/@types/builders/reaction-builder.d.ts +0 -55
- package/dist/@types/builders/reaction-builder.d.ts.map +0 -1
- package/dist/@types/internal/close-signal.d.ts.map +0 -1
- package/dist/@types/internal/correlator.d.ts.map +0 -1
- package/dist/@types/internal/defer-signal.d.ts +0 -32
- package/dist/@types/internal/defer-signal.d.ts.map +0 -1
- package/dist/@types/internal/drain.d.ts.map +0 -1
- package/dist/@types/internal/index.d.ts +0 -38
- package/dist/@types/internal/index.d.ts.map +0 -1
- package/dist/@types/internal/sensitive.d.ts.map +0 -1
- package/dist/chunk-HHKLVRR4.js.map +0 -1
- package/dist/chunk-X3MMQIIP.js.map +0 -1
package/dist/test/index.cjs
CHANGED
|
@@ -1,1130 +1,34 @@
|
|
|
1
|
-
"use strict";
|
|
2
|
-
var __create = Object.create;
|
|
3
|
-
var __defProp = Object.defineProperty;
|
|
4
|
-
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
|
|
5
|
-
var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
6
|
-
var __getProtoOf = Object.getPrototypeOf;
|
|
7
|
-
var __hasOwnProp = Object.prototype.hasOwnProperty;
|
|
8
|
-
var __export = (target, all) => {
|
|
9
|
-
for (var name in all)
|
|
10
|
-
__defProp(target, name, { get: all[name], enumerable: true });
|
|
11
|
-
};
|
|
12
|
-
var __copyProps = (to, from, except, desc) => {
|
|
13
|
-
if (from && typeof from === "object" || typeof from === "function") {
|
|
14
|
-
for (let key of __getOwnPropNames(from))
|
|
15
|
-
if (!__hasOwnProp.call(to, key) && key !== except)
|
|
16
|
-
__defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
|
|
17
|
-
}
|
|
18
|
-
return to;
|
|
19
|
-
};
|
|
20
|
-
var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(
|
|
21
|
-
// If the importer is in node compatibility mode or this is not an ESM
|
|
22
|
-
// file that has been converted to a CommonJS file using a Babel-
|
|
23
|
-
// compatible transform (i.e. "__esModule" has not been set), then set
|
|
24
|
-
// "default" to the CommonJS "module.exports" for node compatibility.
|
|
25
|
-
isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target,
|
|
26
|
-
mod
|
|
27
|
-
));
|
|
28
|
-
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
|
|
1
|
+
"use strict";Object.defineProperty(exports, "__esModule", {value: true});
|
|
29
2
|
|
|
30
|
-
// src/test/index.ts
|
|
31
|
-
var test_exports = {};
|
|
32
|
-
__export(test_exports, {
|
|
33
|
-
fixture: () => fixture,
|
|
34
|
-
sandbox: () => sandbox
|
|
35
|
-
});
|
|
36
|
-
module.exports = __toCommonJS(test_exports);
|
|
37
3
|
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
// src/internal/lru-map.ts
|
|
42
|
-
var LruMap = class {
|
|
43
|
-
_entries = /* @__PURE__ */ new Map();
|
|
44
|
-
_max_size;
|
|
45
|
-
constructor(maxSize) {
|
|
46
|
-
this._max_size = maxSize;
|
|
47
|
-
}
|
|
48
|
-
get(key) {
|
|
49
|
-
const v = this._entries.get(key);
|
|
50
|
-
if (v === void 0) return void 0;
|
|
51
|
-
this._entries.delete(key);
|
|
52
|
-
this._entries.set(key, v);
|
|
53
|
-
return v;
|
|
54
|
-
}
|
|
55
|
-
has(key) {
|
|
56
|
-
return this._entries.has(key);
|
|
57
|
-
}
|
|
58
|
-
set(key, value) {
|
|
59
|
-
this._entries.delete(key);
|
|
60
|
-
if (this._entries.size >= this._max_size) {
|
|
61
|
-
const oldest = this._entries.keys().next().value;
|
|
62
|
-
this._entries.delete(oldest);
|
|
63
|
-
}
|
|
64
|
-
this._entries.set(key, value);
|
|
65
|
-
}
|
|
66
|
-
delete(key) {
|
|
67
|
-
return this._entries.delete(key);
|
|
68
|
-
}
|
|
69
|
-
clear() {
|
|
70
|
-
this._entries.clear();
|
|
71
|
-
}
|
|
72
|
-
get size() {
|
|
73
|
-
return this._entries.size;
|
|
74
|
-
}
|
|
75
|
-
};
|
|
76
|
-
|
|
77
|
-
// src/adapters/in-memory-cache.ts
|
|
78
|
-
var InMemoryCache = class {
|
|
79
|
-
// CacheEntry<any> lets `get<TState>` and `set<TState>` flow without casts:
|
|
80
|
-
// any is bidirectionally compatible with the per-call TState binding, while
|
|
81
|
-
// the public Cache interface still presents a typed surface to callers.
|
|
82
|
-
_entries;
|
|
83
|
-
constructor(options) {
|
|
84
|
-
this._entries = new LruMap(options?.maxSize ?? 1e3);
|
|
85
|
-
}
|
|
86
|
-
/** @inheritDoc */
|
|
87
|
-
async get(stream) {
|
|
88
|
-
return this._entries.get(stream);
|
|
89
|
-
}
|
|
90
|
-
/** @inheritDoc */
|
|
91
|
-
async set(stream, entry) {
|
|
92
|
-
this._entries.set(stream, entry);
|
|
93
|
-
}
|
|
94
|
-
/** @inheritDoc */
|
|
95
|
-
async invalidate(stream) {
|
|
96
|
-
this._entries.delete(stream);
|
|
97
|
-
}
|
|
98
|
-
/** @inheritDoc */
|
|
99
|
-
async clear() {
|
|
100
|
-
this._entries.clear();
|
|
101
|
-
}
|
|
102
|
-
/** @inheritDoc */
|
|
103
|
-
async dispose() {
|
|
104
|
-
this._entries.clear();
|
|
105
|
-
}
|
|
106
|
-
/** Current number of entries held by the LRU. */
|
|
107
|
-
get size() {
|
|
108
|
-
return this._entries.size;
|
|
109
|
-
}
|
|
110
|
-
};
|
|
111
|
-
|
|
112
|
-
// src/adapters/console-logger.ts
|
|
113
|
-
var LEVEL_VALUES = {
|
|
114
|
-
fatal: 60,
|
|
115
|
-
error: 50,
|
|
116
|
-
warn: 40,
|
|
117
|
-
info: 30,
|
|
118
|
-
debug: 20,
|
|
119
|
-
trace: 10
|
|
120
|
-
};
|
|
121
|
-
var LEVEL_COLORS = {
|
|
122
|
-
fatal: "\x1B[41m\x1B[37m",
|
|
123
|
-
// white on red bg
|
|
124
|
-
error: "\x1B[31m",
|
|
125
|
-
// red
|
|
126
|
-
warn: "\x1B[33m",
|
|
127
|
-
// yellow
|
|
128
|
-
info: "\x1B[32m",
|
|
129
|
-
// green
|
|
130
|
-
debug: "\x1B[36m",
|
|
131
|
-
// cyan
|
|
132
|
-
trace: "\x1B[90m"
|
|
133
|
-
// gray
|
|
134
|
-
};
|
|
135
|
-
var RESET = "\x1B[0m";
|
|
136
|
-
var noop = () => {
|
|
137
|
-
};
|
|
138
|
-
var ConsoleLogger = class _ConsoleLogger {
|
|
139
|
-
level;
|
|
140
|
-
_pretty;
|
|
141
|
-
fatal;
|
|
142
|
-
error;
|
|
143
|
-
warn;
|
|
144
|
-
info;
|
|
145
|
-
debug;
|
|
146
|
-
trace;
|
|
147
|
-
constructor(options = {}) {
|
|
148
|
-
const {
|
|
149
|
-
level = "info",
|
|
150
|
-
pretty = process.env.NODE_ENV !== "production",
|
|
151
|
-
bindings
|
|
152
|
-
} = options;
|
|
153
|
-
this._pretty = pretty;
|
|
154
|
-
this.level = level;
|
|
155
|
-
const threshold = LEVEL_VALUES[level] ?? 30;
|
|
156
|
-
const write = pretty ? this._pretty_write.bind(this, bindings) : this._json_write.bind(this, bindings);
|
|
157
|
-
this.fatal = write.bind(this, "fatal", 60);
|
|
158
|
-
this.error = threshold <= 50 ? write.bind(this, "error", 50) : noop;
|
|
159
|
-
this.warn = threshold <= 40 ? write.bind(this, "warn", 40) : noop;
|
|
160
|
-
this.info = threshold <= 30 ? write.bind(this, "info", 30) : noop;
|
|
161
|
-
this.debug = threshold <= 20 ? write.bind(this, "debug", 20) : noop;
|
|
162
|
-
this.trace = threshold <= 10 ? write.bind(this, "trace", 10) : noop;
|
|
163
|
-
}
|
|
164
|
-
/** No-op — `console.log` has no resources to release. */
|
|
165
|
-
async dispose() {
|
|
166
|
-
}
|
|
167
|
-
/** @inheritDoc */
|
|
168
|
-
child(bindings) {
|
|
169
|
-
return new _ConsoleLogger({
|
|
170
|
-
level: this.level,
|
|
171
|
-
pretty: this._pretty,
|
|
172
|
-
bindings
|
|
173
|
-
});
|
|
174
|
-
}
|
|
175
|
-
_json_write(bindings, level, _num, obj_or_msg, msg) {
|
|
176
|
-
let obj;
|
|
177
|
-
let message;
|
|
178
|
-
if (typeof obj_or_msg === "string") {
|
|
179
|
-
message = obj_or_msg;
|
|
180
|
-
obj = {};
|
|
181
|
-
} else if (obj_or_msg instanceof Error) {
|
|
182
|
-
message = msg ?? obj_or_msg.message;
|
|
183
|
-
obj = {
|
|
184
|
-
error: { message: obj_or_msg.message, name: obj_or_msg.name },
|
|
185
|
-
stack: obj_or_msg.stack
|
|
186
|
-
};
|
|
187
|
-
} else if (obj_or_msg !== null && typeof obj_or_msg === "object") {
|
|
188
|
-
message = msg;
|
|
189
|
-
obj = { ...obj_or_msg };
|
|
190
|
-
} else {
|
|
191
|
-
message = msg;
|
|
192
|
-
obj = { value: obj_or_msg };
|
|
193
|
-
}
|
|
194
|
-
const entry = Object.assign({ level, time: Date.now() }, bindings, obj);
|
|
195
|
-
if (message) entry.msg = message;
|
|
196
|
-
let line;
|
|
197
|
-
try {
|
|
198
|
-
line = JSON.stringify(entry);
|
|
199
|
-
} catch {
|
|
200
|
-
line = JSON.stringify({
|
|
201
|
-
level,
|
|
202
|
-
time: entry.time,
|
|
203
|
-
msg: message ?? "[unserializable]",
|
|
204
|
-
unserializable: true
|
|
205
|
-
});
|
|
206
|
-
}
|
|
207
|
-
process.stdout.write(line + "\n");
|
|
208
|
-
}
|
|
209
|
-
_pretty_write(bindings, level, _num, obj_or_msg, msg) {
|
|
210
|
-
const color = LEVEL_COLORS[level];
|
|
211
|
-
const tag = `${color}${level.toUpperCase().padEnd(5)}${RESET}`;
|
|
212
|
-
const ts = (/* @__PURE__ */ new Date()).toISOString().slice(11, 23);
|
|
213
|
-
let message;
|
|
214
|
-
let data;
|
|
215
|
-
if (typeof obj_or_msg === "string") {
|
|
216
|
-
message = obj_or_msg;
|
|
217
|
-
} else if (obj_or_msg instanceof Error) {
|
|
218
|
-
message = msg ?? obj_or_msg.message;
|
|
219
|
-
data = obj_or_msg.stack;
|
|
220
|
-
} else {
|
|
221
|
-
message = msg ?? "";
|
|
222
|
-
if (obj_or_msg !== void 0 && obj_or_msg !== null) {
|
|
223
|
-
try {
|
|
224
|
-
data = JSON.stringify(obj_or_msg);
|
|
225
|
-
} catch {
|
|
226
|
-
data = "[unserializable]";
|
|
227
|
-
}
|
|
228
|
-
}
|
|
229
|
-
}
|
|
230
|
-
const bind_str = bindings && Object.keys(bindings).length ? ` ${JSON.stringify(bindings)}` : "";
|
|
231
|
-
const parts = [ts, tag, message, data, bind_str].filter(Boolean);
|
|
232
|
-
process.stdout.write(parts.join(" ") + "\n");
|
|
233
|
-
}
|
|
234
|
-
};
|
|
235
|
-
|
|
236
|
-
// src/config.ts
|
|
237
|
-
var fs = __toESM(require("fs"), 1);
|
|
238
|
-
var import_zod4 = require("zod");
|
|
239
|
-
|
|
240
|
-
// src/types/errors.ts
|
|
241
|
-
var Errors = {
|
|
242
|
-
ValidationError: "ERR_VALIDATION",
|
|
243
|
-
InvariantError: "ERR_INVARIANT",
|
|
244
|
-
ConcurrencyError: "ERR_CONCURRENCY",
|
|
245
|
-
StreamClosedError: "ERR_STREAM_CLOSED",
|
|
246
|
-
NonRetryableError: "ERR_NON_RETRYABLE",
|
|
247
|
-
StoreError: "ERR_STORE"
|
|
248
|
-
};
|
|
249
|
-
var ValidationError = class extends Error {
|
|
250
|
-
/** The type of target being validated (e.g., "action", "event") */
|
|
251
|
-
target;
|
|
252
|
-
/** The invalid payload that failed validation */
|
|
253
|
-
payload;
|
|
254
|
-
/** Zod validation error details */
|
|
255
|
-
details;
|
|
256
|
-
constructor(target, payload, details) {
|
|
257
|
-
super(`Invalid ${target} payload`);
|
|
258
|
-
this.name = Errors.ValidationError;
|
|
259
|
-
this.target = target;
|
|
260
|
-
this.payload = payload;
|
|
261
|
-
this.details = details;
|
|
262
|
-
}
|
|
263
|
-
};
|
|
264
|
-
var ConcurrencyError = class extends Error {
|
|
265
|
-
/** The stream that had the concurrent modification */
|
|
266
|
-
stream;
|
|
267
|
-
/** The actual current version in the store */
|
|
268
|
-
lastVersion;
|
|
269
|
-
/** The events that were being committed */
|
|
270
|
-
events;
|
|
271
|
-
/** The version number that was expected */
|
|
272
|
-
expectedVersion;
|
|
273
|
-
constructor(stream, lastVersion, events, expectedVersion) {
|
|
274
|
-
super(
|
|
275
|
-
`Concurrency error committing "${events.map((e) => `${stream}.${e.name}`).join(
|
|
276
|
-
", "
|
|
277
|
-
)}". Expected version ${expectedVersion} but found version ${lastVersion}.`
|
|
278
|
-
);
|
|
279
|
-
this.name = Errors.ConcurrencyError;
|
|
280
|
-
this.stream = stream;
|
|
281
|
-
this.lastVersion = lastVersion;
|
|
282
|
-
this.events = events;
|
|
283
|
-
this.expectedVersion = expectedVersion;
|
|
284
|
-
}
|
|
285
|
-
};
|
|
286
|
-
|
|
287
|
-
// src/types/schemas.ts
|
|
288
|
-
var import_zod2 = require("zod");
|
|
289
|
-
|
|
290
|
-
// src/internal/sensitive.ts
|
|
291
|
-
var import_zod = require("zod");
|
|
292
|
-
var _registry = import_zod.z.registry();
|
|
293
|
-
|
|
294
|
-
// src/types/schemas.ts
|
|
295
|
-
var ZodEmpty = import_zod2.z.record(import_zod2.z.string(), import_zod2.z.never());
|
|
296
|
-
var ActorSchema = import_zod2.z.object({
|
|
297
|
-
id: import_zod2.z.string(),
|
|
298
|
-
name: import_zod2.z.string()
|
|
299
|
-
}).loose().readonly();
|
|
300
|
-
var TargetSchema = import_zod2.z.object({
|
|
301
|
-
stream: import_zod2.z.string(),
|
|
302
|
-
actor: ActorSchema,
|
|
303
|
-
expectedVersion: import_zod2.z.number().optional()
|
|
304
|
-
}).loose().readonly();
|
|
305
|
-
var CausationEventSchema = import_zod2.z.object({
|
|
306
|
-
id: import_zod2.z.number(),
|
|
307
|
-
name: import_zod2.z.string(),
|
|
308
|
-
stream: import_zod2.z.string()
|
|
309
|
-
});
|
|
310
|
-
var EventMetaSchema = import_zod2.z.object({
|
|
311
|
-
correlation: import_zod2.z.string(),
|
|
312
|
-
causation: import_zod2.z.object({
|
|
313
|
-
action: TargetSchema.and(import_zod2.z.object({ name: import_zod2.z.string() })).optional(),
|
|
314
|
-
event: CausationEventSchema.optional()
|
|
315
|
-
})
|
|
316
|
-
}).readonly();
|
|
317
|
-
var CommittedMetaSchema = import_zod2.z.object({
|
|
318
|
-
id: import_zod2.z.number(),
|
|
319
|
-
stream: import_zod2.z.string(),
|
|
320
|
-
version: import_zod2.z.number(),
|
|
321
|
-
created: import_zod2.z.date(),
|
|
322
|
-
meta: EventMetaSchema
|
|
323
|
-
}).readonly();
|
|
324
|
-
var QuerySchema = import_zod2.z.object({
|
|
325
|
-
stream: import_zod2.z.string().optional(),
|
|
326
|
-
names: import_zod2.z.string().array().optional(),
|
|
327
|
-
before: import_zod2.z.number().optional(),
|
|
328
|
-
after: import_zod2.z.number().optional(),
|
|
329
|
-
limit: import_zod2.z.number().optional(),
|
|
330
|
-
created_before: import_zod2.z.date().optional(),
|
|
331
|
-
created_after: import_zod2.z.date().optional(),
|
|
332
|
-
backward: import_zod2.z.boolean().optional(),
|
|
333
|
-
correlation: import_zod2.z.string().optional(),
|
|
334
|
-
with_snaps: import_zod2.z.boolean().optional(),
|
|
335
|
-
with_pii: import_zod2.z.boolean().optional(),
|
|
336
|
-
stream_exact: import_zod2.z.boolean().optional()
|
|
337
|
-
}).readonly();
|
|
338
|
-
|
|
339
|
-
// src/types/index.ts
|
|
340
|
-
var Environments = [
|
|
341
|
-
"development",
|
|
342
|
-
"test",
|
|
343
|
-
"staging",
|
|
344
|
-
"production"
|
|
345
|
-
];
|
|
346
|
-
var LogLevels = [
|
|
347
|
-
"fatal",
|
|
348
|
-
"error",
|
|
349
|
-
"warn",
|
|
350
|
-
"info",
|
|
351
|
-
"debug",
|
|
352
|
-
"trace"
|
|
353
|
-
];
|
|
354
|
-
|
|
355
|
-
// src/utils.ts
|
|
356
|
-
var import_zod3 = require("zod");
|
|
357
|
-
var validate = (target, payload, schema) => {
|
|
358
|
-
try {
|
|
359
|
-
return schema ? schema.parse(payload) : payload;
|
|
360
|
-
} catch (error) {
|
|
361
|
-
if (error instanceof import_zod3.ZodError) {
|
|
362
|
-
throw new ValidationError(target, payload, (0, import_zod3.prettifyError)(error));
|
|
363
|
-
}
|
|
364
|
-
throw new ValidationError(target, payload, error);
|
|
365
|
-
}
|
|
366
|
-
};
|
|
367
|
-
var extend = (source, schema, target) => {
|
|
368
|
-
const value = validate("config", source, schema);
|
|
369
|
-
return { ...target, ...value };
|
|
370
|
-
};
|
|
371
|
-
async function sleep(ms) {
|
|
372
|
-
return new Promise((resolve) => setTimeout(resolve, ms ?? config().sleepMs));
|
|
373
|
-
}
|
|
374
|
-
|
|
375
|
-
// src/config.ts
|
|
376
|
-
var PackageSchema = import_zod4.z.object({
|
|
377
|
-
name: import_zod4.z.string().min(1),
|
|
378
|
-
version: import_zod4.z.string().min(1),
|
|
379
|
-
description: import_zod4.z.string().min(1).optional(),
|
|
380
|
-
author: import_zod4.z.object({ name: import_zod4.z.string().min(1), email: import_zod4.z.string().optional() }).optional().or(import_zod4.z.string().min(1)).optional(),
|
|
381
|
-
license: import_zod4.z.string().min(1).optional(),
|
|
382
|
-
dependencies: import_zod4.z.record(import_zod4.z.string(), import_zod4.z.string()).optional()
|
|
383
|
-
});
|
|
384
|
-
var FALLBACK_PACKAGE = {
|
|
385
|
-
name: "act-fallback",
|
|
386
|
-
version: "0.0.0-fallback",
|
|
387
|
-
description: "Synthetic fallback \u2014 package.json could not be loaded"
|
|
388
|
-
};
|
|
389
|
-
var get_package = () => {
|
|
390
|
-
try {
|
|
391
|
-
const raw = JSON.parse(fs.readFileSync("package.json").toString());
|
|
392
|
-
const pkg2 = {
|
|
393
|
-
name: FALLBACK_PACKAGE.name,
|
|
394
|
-
version: FALLBACK_PACKAGE.version
|
|
395
|
-
};
|
|
396
|
-
for (const [key, schema] of Object.entries(PackageSchema.shape)) {
|
|
397
|
-
const field = schema.safeParse(raw?.[key]);
|
|
398
|
-
if (field.success && field.data !== void 0) pkg2[key] = field.data;
|
|
399
|
-
}
|
|
400
|
-
return pkg2;
|
|
401
|
-
} catch (err) {
|
|
402
|
-
pkg_load_error = err;
|
|
403
|
-
return FALLBACK_PACKAGE;
|
|
404
|
-
}
|
|
405
|
-
};
|
|
406
|
-
var pkg_load_error;
|
|
407
|
-
var BaseSchema = PackageSchema.extend({
|
|
408
|
-
env: import_zod4.z.enum(Environments),
|
|
409
|
-
logLevel: import_zod4.z.enum(LogLevels),
|
|
410
|
-
logSingleLine: import_zod4.z.boolean(),
|
|
411
|
-
sleepMs: import_zod4.z.number().int().min(0).max(5e3)
|
|
412
|
-
});
|
|
413
|
-
var { NODE_ENV, LOG_LEVEL, LOG_SINGLE_LINE, SLEEP_MS } = process.env;
|
|
414
|
-
var env = NODE_ENV || "development";
|
|
415
|
-
var logLevel = LOG_LEVEL || (NODE_ENV === "test" ? "fatal" : NODE_ENV === "production" ? "info" : "trace");
|
|
416
|
-
var logSingleLine = (LOG_SINGLE_LINE || "true") === "true";
|
|
417
|
-
var sleepMs = parseInt(NODE_ENV === "test" ? "0" : SLEEP_MS ?? "100", 10);
|
|
418
|
-
var pkg = get_package();
|
|
419
|
-
var _validated;
|
|
420
|
-
var config = () => {
|
|
421
|
-
if (!_validated) {
|
|
422
|
-
_validated = extend(
|
|
423
|
-
{ ...pkg, env, logLevel, logSingleLine, sleepMs },
|
|
424
|
-
BaseSchema
|
|
425
|
-
);
|
|
426
|
-
if (pkg_load_error) {
|
|
427
|
-
const msg = pkg_load_error instanceof Error ? pkg_load_error.message : typeof pkg_load_error === "string" ? pkg_load_error : "unknown error";
|
|
428
|
-
log().warn(
|
|
429
|
-
`[act] Could not read package.json (${msg}); using synthetic name="${FALLBACK_PACKAGE.name}" version="${FALLBACK_PACKAGE.version}".`
|
|
430
|
-
);
|
|
431
|
-
pkg_load_error = void 0;
|
|
432
|
-
}
|
|
433
|
-
}
|
|
434
|
-
return _validated;
|
|
435
|
-
};
|
|
436
|
-
|
|
437
|
-
// src/scoped.ts
|
|
438
|
-
var import_node_async_hooks = require("async_hooks");
|
|
439
|
-
var scoped = new import_node_async_hooks.AsyncLocalStorage();
|
|
440
|
-
var reacting = new import_node_async_hooks.AsyncLocalStorage();
|
|
441
|
-
|
|
442
|
-
// src/ports.ts
|
|
443
|
-
var adapters = /* @__PURE__ */ new Map();
|
|
444
|
-
function port(injector) {
|
|
445
|
-
return (adapter) => {
|
|
446
|
-
if (!adapters.has(injector.name)) {
|
|
447
|
-
const injected = injector(adapter);
|
|
448
|
-
adapters.set(injector.name, injected);
|
|
449
|
-
log().info(`[act] + ${injector.name}:${injected.constructor.name}`);
|
|
450
|
-
}
|
|
451
|
-
return adapters.get(injector.name);
|
|
452
|
-
};
|
|
453
|
-
}
|
|
454
|
-
var log = port(function log2(adapter) {
|
|
455
|
-
const cfg = config();
|
|
456
|
-
return adapter || new ConsoleLogger({
|
|
457
|
-
level: cfg.logLevel,
|
|
458
|
-
pretty: cfg.env !== "production"
|
|
459
|
-
});
|
|
460
|
-
});
|
|
461
|
-
var _store = port(function store(adapter) {
|
|
462
|
-
return adapter ?? new InMemoryStore();
|
|
463
|
-
});
|
|
464
|
-
var _cache = port(function cache(adapter) {
|
|
465
|
-
return adapter ?? new InMemoryCache();
|
|
466
|
-
});
|
|
467
|
-
var SNAP_EVENT = "__snapshot__";
|
|
468
|
-
var TOMBSTONE_EVENT = "__tombstone__";
|
|
469
|
-
var DEFAULT_LANE = "default";
|
|
470
|
-
|
|
471
|
-
// src/adapters/in-memory-store.ts
|
|
472
|
-
var InMemoryStream = class {
|
|
473
|
-
stream;
|
|
474
|
-
source;
|
|
475
|
-
_at = -1;
|
|
476
|
-
_retry = -1;
|
|
477
|
-
_blocked = false;
|
|
478
|
-
_error = "";
|
|
479
|
-
_leased_by = void 0;
|
|
480
|
-
_leased_until = void 0;
|
|
481
|
-
_priority = 0;
|
|
482
|
-
_lane = DEFAULT_LANE;
|
|
483
|
-
// While in the future, the stream is held out of `claim`.
|
|
484
|
-
_deferred_at = void 0;
|
|
485
|
-
// Work mark; `undefined` = never correlated, so `claim` skips it.
|
|
486
|
-
_correlated_at = void 0;
|
|
487
|
-
constructor(stream, source, priority = 0, lane = DEFAULT_LANE) {
|
|
488
|
-
this.stream = stream;
|
|
489
|
-
this.source = source;
|
|
490
|
-
this._priority = priority;
|
|
491
|
-
this._lane = lane;
|
|
492
|
-
}
|
|
493
|
-
get priority() {
|
|
494
|
-
return this._priority;
|
|
495
|
-
}
|
|
496
|
-
get lane() {
|
|
497
|
-
return this._lane;
|
|
498
|
-
}
|
|
499
|
-
/** Replaced on every subscribe, so the current config wins on restart. */
|
|
500
|
-
set lane(value) {
|
|
501
|
-
this._lane = value;
|
|
502
|
-
}
|
|
503
|
-
bump_priority(priority) {
|
|
504
|
-
if (priority > this._priority) this._priority = priority;
|
|
505
|
-
}
|
|
506
|
-
get correlated_at() {
|
|
507
|
-
return this._correlated_at;
|
|
508
|
-
}
|
|
509
|
-
/** A mark never regresses, including at zero and negatives. */
|
|
510
|
-
mark(correlated_at) {
|
|
511
|
-
if (this._correlated_at === void 0 || correlated_at > this._correlated_at)
|
|
512
|
-
this._correlated_at = correlated_at;
|
|
513
|
-
}
|
|
514
|
-
/** Operator override; ignores the subscribe-time `max()`. */
|
|
515
|
-
set_priority(priority) {
|
|
516
|
-
this._priority = priority;
|
|
517
|
-
}
|
|
518
|
-
get is_available() {
|
|
519
|
-
return !this._blocked && (!this._leased_until || this._leased_until <= /* @__PURE__ */ new Date()) && (!this._deferred_at || this._deferred_at <= Date.now());
|
|
520
|
-
}
|
|
521
|
-
defer(deferred_at) {
|
|
522
|
-
this._deferred_at = deferred_at;
|
|
523
|
-
this._retry = -1;
|
|
524
|
-
}
|
|
525
|
-
get at() {
|
|
526
|
-
return this._at;
|
|
527
|
-
}
|
|
528
|
-
get retry() {
|
|
529
|
-
return this._retry;
|
|
530
|
-
}
|
|
531
|
-
get blocked() {
|
|
532
|
-
return this._blocked;
|
|
533
|
-
}
|
|
534
|
-
get error() {
|
|
535
|
-
return this._error;
|
|
536
|
-
}
|
|
537
|
-
get leased_by() {
|
|
538
|
-
return this._leased_by;
|
|
539
|
-
}
|
|
540
|
-
get leased_until() {
|
|
541
|
-
return this._leased_until;
|
|
542
|
-
}
|
|
543
|
-
get deferred_at() {
|
|
544
|
-
return this._deferred_at;
|
|
545
|
-
}
|
|
546
|
-
lease(lease, millis) {
|
|
547
|
-
this._leased_by = lease.by;
|
|
548
|
-
this._leased_until = new Date(Date.now() + millis);
|
|
549
|
-
this._retry = this._retry + 1;
|
|
550
|
-
return {
|
|
551
|
-
stream: this.stream,
|
|
552
|
-
source: this.source,
|
|
553
|
-
at: lease.at,
|
|
554
|
-
by: lease.by,
|
|
555
|
-
retry: this._retry,
|
|
556
|
-
lagging: lease.lagging,
|
|
557
|
-
lane: this._lane
|
|
558
|
-
};
|
|
559
|
-
}
|
|
560
|
-
/** Ack, or with `due`, also persist the schedule. See {@link Store.ack}. */
|
|
561
|
-
ack(lease) {
|
|
562
|
-
if (this._leased_by === lease.by) {
|
|
563
|
-
this._leased_by = void 0;
|
|
564
|
-
this._leased_until = void 0;
|
|
565
|
-
if (lease.due !== void 0) {
|
|
566
|
-
this._at = lease.at;
|
|
567
|
-
this._retry = lease.retry;
|
|
568
|
-
this._deferred_at = lease.due;
|
|
569
|
-
return void 0;
|
|
570
|
-
}
|
|
571
|
-
this._retry = -1;
|
|
572
|
-
this._at = lease.at;
|
|
573
|
-
this._deferred_at = void 0;
|
|
574
|
-
return {
|
|
575
|
-
stream: this.stream,
|
|
576
|
-
source: this.source,
|
|
577
|
-
at: this._at,
|
|
578
|
-
by: lease.by,
|
|
579
|
-
retry: this._retry,
|
|
580
|
-
lagging: lease.lagging,
|
|
581
|
-
lane: this._lane
|
|
582
|
-
};
|
|
583
|
-
}
|
|
584
|
-
}
|
|
585
|
-
block(lease, error) {
|
|
586
|
-
if (this._leased_by === lease.by && !this._blocked) {
|
|
587
|
-
this._blocked = true;
|
|
588
|
-
this._error = error;
|
|
589
|
-
this._deferred_at = void 0;
|
|
590
|
-
return {
|
|
591
|
-
stream: this.stream,
|
|
592
|
-
source: this.source,
|
|
593
|
-
at: this._at,
|
|
594
|
-
by: this._leased_by,
|
|
595
|
-
retry: this._retry,
|
|
596
|
-
error: this._error,
|
|
597
|
-
lagging: lease.lagging,
|
|
598
|
-
lane: this._lane
|
|
599
|
-
};
|
|
600
|
-
}
|
|
601
|
-
}
|
|
602
|
-
/** Retry resets to -1 (released); the next claim makes it 0. */
|
|
603
|
-
reset() {
|
|
604
|
-
this._at = -1;
|
|
605
|
-
this._retry = -1;
|
|
606
|
-
this._blocked = false;
|
|
607
|
-
this._error = "";
|
|
608
|
-
this._leased_by = void 0;
|
|
609
|
-
this._leased_until = void 0;
|
|
610
|
-
this._deferred_at = void 0;
|
|
611
|
-
}
|
|
612
|
-
/** True only when the stream was blocked. */
|
|
613
|
-
unblock() {
|
|
614
|
-
if (!this._blocked) return false;
|
|
615
|
-
this._blocked = false;
|
|
616
|
-
this._retry = -1;
|
|
617
|
-
this._error = "";
|
|
618
|
-
this._leased_by = void 0;
|
|
619
|
-
this._leased_until = void 0;
|
|
620
|
-
this._deferred_at = void 0;
|
|
621
|
-
return true;
|
|
622
|
-
}
|
|
623
|
-
};
|
|
624
|
-
var InMemoryStore = class {
|
|
625
|
-
_events = [];
|
|
626
|
-
// Never reused. Deletions punch holes, so ids are not array indexes.
|
|
627
|
-
_next_id = 0;
|
|
628
|
-
_streams = /* @__PURE__ */ new Map();
|
|
629
|
-
/** Correlate checkpoint: how far the log has been READ. */
|
|
630
|
-
_correlated_at = -1;
|
|
631
|
-
/** Per-correlator checkpoint and lease; keyless callers use `_correlated_at`. */
|
|
632
|
-
_correlators = /* @__PURE__ */ new Map();
|
|
633
|
-
_stream_versions = /* @__PURE__ */ new Map();
|
|
634
|
-
// stream → (event_id → cloned pii). Two levels so `forget_pii` drops one
|
|
635
|
-
// inner Map. Absence means no PII.
|
|
636
|
-
_pii = /* @__PURE__ */ new Map();
|
|
637
|
-
_reset_indexes() {
|
|
638
|
-
this._events.length = 0;
|
|
639
|
-
this._next_id = 0;
|
|
640
|
-
this._correlated_at = -1;
|
|
641
|
-
this._stream_versions.clear();
|
|
642
|
-
this._pii.clear();
|
|
643
|
-
}
|
|
644
|
-
// Stores a cloned copy, so caller-side mutation can't reach it.
|
|
645
|
-
_put_pii(stream, id, pii) {
|
|
646
|
-
let per_stream = this._pii.get(stream);
|
|
647
|
-
if (!per_stream) {
|
|
648
|
-
per_stream = /* @__PURE__ */ new Map();
|
|
649
|
-
this._pii.set(stream, per_stream);
|
|
650
|
-
}
|
|
651
|
-
per_stream.set(id, structuredClone(pii));
|
|
652
|
-
}
|
|
653
|
-
// First index whose id is greater than `after` (ids are sorted, not dense).
|
|
654
|
-
_first_index_after(after) {
|
|
655
|
-
let lo = 0;
|
|
656
|
-
let hi = this._events.length;
|
|
657
|
-
while (lo < hi) {
|
|
658
|
-
const mid = lo + hi >>> 1;
|
|
659
|
-
if (this._events[mid].id > after) hi = mid;
|
|
660
|
-
else lo = mid + 1;
|
|
661
|
-
}
|
|
662
|
-
return lo;
|
|
663
|
-
}
|
|
664
|
-
// Allocation-free for events without PII, the common case.
|
|
665
|
-
_with_pii(e, wanted = true) {
|
|
666
|
-
if (!wanted) return e;
|
|
667
|
-
const pii = this._pii.get(e.stream)?.get(e.id);
|
|
668
|
-
return pii ? { ...e, pii } : e;
|
|
669
|
-
}
|
|
670
|
-
async dispose() {
|
|
671
|
-
await sleep();
|
|
672
|
-
this._reset_indexes();
|
|
673
|
-
}
|
|
674
|
-
async seed() {
|
|
675
|
-
await sleep();
|
|
676
|
-
}
|
|
677
|
-
async drop() {
|
|
678
|
-
await sleep();
|
|
679
|
-
this._reset_indexes();
|
|
680
|
-
this._streams = /* @__PURE__ */ new Map();
|
|
681
|
-
}
|
|
682
|
-
in_query(query, e) {
|
|
683
|
-
if (query.stream && !this._stream_matches(query.stream, query, e.stream))
|
|
684
|
-
return false;
|
|
685
|
-
if (query.names && !query.names.includes(e.name)) return false;
|
|
686
|
-
if (query.correlation && e.meta?.correlation !== query.correlation)
|
|
687
|
-
return false;
|
|
688
|
-
if (e.name === SNAP_EVENT && !query.with_snaps) return false;
|
|
689
|
-
if (query.created_after && e.created <= query.created_after) return false;
|
|
690
|
-
if (query.created_before && e.created >= query.created_before) return false;
|
|
691
|
-
return true;
|
|
692
|
-
}
|
|
693
|
-
_stream_matches(pattern, query, stream) {
|
|
694
|
-
return query.stream_exact ? stream === pattern : RegExp(pattern).test(stream);
|
|
695
|
-
}
|
|
696
|
-
/** Index of the stream's latest snapshot, or -1; an explicit `after` wins. */
|
|
697
|
-
_snap_floor(query) {
|
|
698
|
-
if (!query?.with_snaps || !query.stream_exact || query.stream === void 0 || query.after !== void 0)
|
|
699
|
-
return -1;
|
|
700
|
-
for (let j = this._events.length - 1; j >= 0; j--) {
|
|
701
|
-
const e = this._events[j];
|
|
702
|
-
if (e.stream === query.stream && e.name === SNAP_EVENT) return j;
|
|
703
|
-
}
|
|
704
|
-
return -1;
|
|
705
|
-
}
|
|
706
|
-
async query(callback, query) {
|
|
707
|
-
await sleep();
|
|
708
|
-
const floor = this._snap_floor(query);
|
|
709
|
-
const lo = floor >= 0 ? floor : this._first_index_after(query?.after ?? -1);
|
|
710
|
-
const hi = query?.before !== void 0 ? this._first_index_after(query.before - 1) : this._events.length;
|
|
711
|
-
const with_pii = query?.with_pii !== false;
|
|
712
|
-
let count = 0;
|
|
713
|
-
for (let k = 0; k < hi - lo; k++) {
|
|
714
|
-
const e = this._events[query?.backward ? hi - 1 - k : lo + k];
|
|
715
|
-
if (query && !this.in_query(query, e)) continue;
|
|
716
|
-
await Promise.resolve(
|
|
717
|
-
callback(this._with_pii(e, with_pii))
|
|
718
|
-
);
|
|
719
|
-
count++;
|
|
720
|
-
if (query?.limit && count >= query.limit) break;
|
|
721
|
-
}
|
|
722
|
-
return count;
|
|
723
|
-
}
|
|
724
|
-
async commit(stream, msgs, meta, expectedVersion) {
|
|
725
|
-
await sleep();
|
|
726
|
-
const current_version = this._stream_versions.get(stream) ?? -1;
|
|
727
|
-
if (typeof expectedVersion === "number" && current_version !== expectedVersion) {
|
|
728
|
-
throw new ConcurrencyError(
|
|
729
|
-
stream,
|
|
730
|
-
current_version,
|
|
731
|
-
msgs,
|
|
732
|
-
expectedVersion
|
|
733
|
-
);
|
|
734
|
-
}
|
|
735
|
-
let version = current_version + 1;
|
|
736
|
-
const committed = msgs.map(({ name, data, pii }) => {
|
|
737
|
-
const c = {
|
|
738
|
-
id: this._next_id++,
|
|
739
|
-
stream,
|
|
740
|
-
version,
|
|
741
|
-
created: /* @__PURE__ */ new Date(),
|
|
742
|
-
name,
|
|
743
|
-
data,
|
|
744
|
-
meta
|
|
745
|
-
};
|
|
746
|
-
this._events.push(c);
|
|
747
|
-
if (pii != null) this._put_pii(stream, c.id, pii);
|
|
748
|
-
version++;
|
|
749
|
-
return this._with_pii(c);
|
|
750
|
-
});
|
|
751
|
-
this._stream_versions.set(stream, version - 1);
|
|
752
|
-
return committed;
|
|
753
|
-
}
|
|
754
|
-
async claim(lagging, leading, by, millis, lane) {
|
|
755
|
-
await sleep();
|
|
756
|
-
const has_work = (s) => s.correlated_at !== void 0 && s.at < s.correlated_at;
|
|
757
|
-
const available = [...this._streams.values()].filter(
|
|
758
|
-
(s) => s.is_available && has_work(s) && (lane === void 0 || s.lane === lane)
|
|
759
|
-
);
|
|
760
|
-
const fair = lagging >= 2 ? Math.max(1, Math.floor(lagging / 4)) : 0;
|
|
761
|
-
const by_priority = [...available].sort(
|
|
762
|
-
(a, b) => b.priority - a.priority || a.at - b.at
|
|
763
|
-
);
|
|
764
|
-
const priority_slice = by_priority.slice(0, lagging - fair);
|
|
765
|
-
const picked = new Set(priority_slice.map((s) => s.stream));
|
|
766
|
-
const fair_slice = [...available].sort((a, b) => a.at - b.at).filter((s) => !picked.has(s.stream)).slice(0, fair);
|
|
767
|
-
const lag = [...priority_slice, ...fair_slice].map((s) => ({
|
|
768
|
-
stream: s.stream,
|
|
769
|
-
source: s.source,
|
|
770
|
-
at: s.at,
|
|
771
|
-
lagging: true
|
|
772
|
-
}));
|
|
773
|
-
const lead = available.sort((a, b) => b.at - a.at).slice(0, leading).map((s) => ({
|
|
774
|
-
stream: s.stream,
|
|
775
|
-
source: s.source,
|
|
776
|
-
at: s.at,
|
|
777
|
-
lagging: false
|
|
778
|
-
}));
|
|
779
|
-
const seen = /* @__PURE__ */ new Set();
|
|
780
|
-
const combined = [...lag, ...lead].filter((p) => {
|
|
781
|
-
if (seen.has(p.stream)) return false;
|
|
782
|
-
seen.add(p.stream);
|
|
783
|
-
return true;
|
|
784
|
-
});
|
|
785
|
-
return combined.map(
|
|
786
|
-
(p) => this._streams.get(p.stream)?.lease({ ...p, by, retry: 0 }, millis)
|
|
787
|
-
).filter((l) => !!l);
|
|
788
|
-
}
|
|
789
|
-
async subscribe(streams, correlated_at, correlator) {
|
|
790
|
-
await sleep();
|
|
791
|
-
let correlating;
|
|
792
|
-
let keyed_at;
|
|
793
|
-
if (correlator) {
|
|
794
|
-
const now = Date.now();
|
|
795
|
-
const held = this._correlators.get(correlator.key);
|
|
796
|
-
const at = Math.max(held?.at ?? this._correlated_at, correlated_at ?? -1);
|
|
797
|
-
keyed_at = at;
|
|
798
|
-
if (!held || held.until < now || held.by === correlator.by) {
|
|
799
|
-
correlating = true;
|
|
800
|
-
this._correlators.set(correlator.key, {
|
|
801
|
-
at,
|
|
802
|
-
by: correlator.by,
|
|
803
|
-
// Release outright, or a successor in the same ms is still refused.
|
|
804
|
-
until: correlator.millis > 0 ? now + correlator.millis : 0
|
|
805
|
-
});
|
|
806
|
-
} else {
|
|
807
|
-
correlating = false;
|
|
808
|
-
held.at = at;
|
|
809
|
-
}
|
|
810
|
-
if (correlated_at !== void 0 && correlated_at > this._correlated_at)
|
|
811
|
-
this._correlated_at = correlated_at;
|
|
812
|
-
} else if (correlated_at !== void 0 && correlated_at > this._correlated_at)
|
|
813
|
-
this._correlated_at = correlated_at;
|
|
814
|
-
let subscribed = 0;
|
|
815
|
-
for (const {
|
|
816
|
-
stream,
|
|
817
|
-
source,
|
|
818
|
-
priority = 0,
|
|
819
|
-
lane = DEFAULT_LANE,
|
|
820
|
-
correlated_at: correlated_at2
|
|
821
|
-
} of streams) {
|
|
822
|
-
const existing = this._streams.get(stream);
|
|
823
|
-
if (existing) {
|
|
824
|
-
if (priority >= existing.priority) existing.lane = lane;
|
|
825
|
-
existing.bump_priority(priority);
|
|
826
|
-
if (correlated_at2 !== void 0) existing.mark(correlated_at2);
|
|
827
|
-
} else {
|
|
828
|
-
const created = new InMemoryStream(stream, source, priority, lane);
|
|
829
|
-
if (correlated_at2 !== void 0) created.mark(correlated_at2);
|
|
830
|
-
this._streams.set(stream, created);
|
|
831
|
-
subscribed++;
|
|
832
|
-
}
|
|
833
|
-
}
|
|
834
|
-
let watermark = -1;
|
|
835
|
-
for (const s of this._streams.values()) {
|
|
836
|
-
if (s.at > watermark) watermark = s.at;
|
|
837
|
-
}
|
|
838
|
-
return {
|
|
839
|
-
subscribed,
|
|
840
|
-
watermark,
|
|
841
|
-
correlated_at: keyed_at ?? this._correlated_at,
|
|
842
|
-
...correlating === void 0 ? {} : { correlating }
|
|
843
|
-
};
|
|
844
|
-
}
|
|
845
|
-
async ack(leases) {
|
|
846
|
-
await sleep();
|
|
847
|
-
return leases.map((l) => this._streams.get(l.stream)?.ack(l)).filter((l) => !!l);
|
|
848
|
-
}
|
|
849
|
-
async block(leases) {
|
|
850
|
-
await sleep();
|
|
851
|
-
return leases.map((l) => this._streams.get(l.stream)?.block(l, l.error)).filter((l) => !!l);
|
|
852
|
-
}
|
|
853
|
-
async defer(input, deferred_at) {
|
|
854
|
-
await sleep();
|
|
855
|
-
const selected = this._select(input);
|
|
856
|
-
for (const s of selected) s.defer(deferred_at);
|
|
857
|
-
return selected.length;
|
|
858
|
-
}
|
|
859
|
-
/** De-duplicated like the SQL `stream = ANY(...)`; unknown names skipped. */
|
|
860
|
-
_select(input) {
|
|
861
|
-
if (Array.isArray(input))
|
|
862
|
-
return [...new Set(input)].map((name) => this._streams.get(name)).filter((s) => s !== void 0);
|
|
863
|
-
return [...this._streams.values()].filter(this._filter_predicate(input));
|
|
864
|
-
}
|
|
865
|
-
_filter_predicate(filter) {
|
|
866
|
-
const stream_re = filter.stream !== void 0 && !filter.stream_exact ? new RegExp(filter.stream) : void 0;
|
|
867
|
-
const source_re = filter.source !== void 0 && !filter.source_exact ? new RegExp(filter.source) : void 0;
|
|
868
|
-
return (s) => {
|
|
869
|
-
if (filter.stream !== void 0) {
|
|
870
|
-
if (filter.stream_exact ? s.stream !== filter.stream : !stream_re.test(s.stream))
|
|
871
|
-
return false;
|
|
872
|
-
}
|
|
873
|
-
if (filter.source !== void 0) {
|
|
874
|
-
if (s.source === void 0) return false;
|
|
875
|
-
if (filter.source_exact ? s.source !== filter.source : !source_re.test(s.source))
|
|
876
|
-
return false;
|
|
877
|
-
}
|
|
878
|
-
if (filter.blocked !== void 0 && s.blocked !== filter.blocked)
|
|
879
|
-
return false;
|
|
880
|
-
if (filter.lane !== void 0 && s.lane !== filter.lane) return false;
|
|
881
|
-
return true;
|
|
882
|
-
};
|
|
883
|
-
}
|
|
884
|
-
async reset(input) {
|
|
885
|
-
await sleep();
|
|
886
|
-
const selected = this._select(input);
|
|
887
|
-
for (const s of selected) s.reset();
|
|
888
|
-
return selected.length;
|
|
889
|
-
}
|
|
890
|
-
async forget_pii(stream) {
|
|
891
|
-
await sleep();
|
|
892
|
-
const count = this._pii.get(stream)?.size ?? 0;
|
|
893
|
-
this._pii.delete(stream);
|
|
894
|
-
return count;
|
|
895
|
-
}
|
|
896
|
-
async unblock(input) {
|
|
897
|
-
await sleep();
|
|
898
|
-
const selected = this._select(
|
|
899
|
-
Array.isArray(input) ? input : { ...input, blocked: true }
|
|
900
|
-
);
|
|
901
|
-
return selected.filter((s) => s.unblock()).length;
|
|
902
|
-
}
|
|
903
|
-
async prioritize(filter, priority) {
|
|
904
|
-
await sleep();
|
|
905
|
-
const changed = this._select(filter).filter((s) => s.priority !== priority);
|
|
906
|
-
for (const s of changed) s.set_priority(priority);
|
|
907
|
-
return changed.length;
|
|
908
|
-
}
|
|
909
|
-
async query_streams(callback, query) {
|
|
910
|
-
await sleep();
|
|
911
|
-
const limit = query?.limit ?? 100;
|
|
912
|
-
const after = query?.after;
|
|
913
|
-
const source_matches = query?.source_matches;
|
|
914
|
-
const matches = this._filter_predicate(query ?? {});
|
|
915
|
-
const reverse_cache = /* @__PURE__ */ new Map();
|
|
916
|
-
const reverse_match = (source) => {
|
|
917
|
-
let re = reverse_cache.get(source);
|
|
918
|
-
if (!re) {
|
|
919
|
-
re = new RegExp(source);
|
|
920
|
-
reverse_cache.set(source, re);
|
|
921
|
-
}
|
|
922
|
-
return source_matches.some((name) => re.test(name));
|
|
923
|
-
};
|
|
924
|
-
const sorted = [...this._streams.keys()].sort().map((stream) => this._streams.get(stream));
|
|
925
|
-
let count = 0;
|
|
926
|
-
for (const s of sorted) {
|
|
927
|
-
if (after !== void 0 && s.stream <= after) continue;
|
|
928
|
-
if (!matches(s)) continue;
|
|
929
|
-
if (source_matches !== void 0) {
|
|
930
|
-
if (s.source && !reverse_match(s.source)) continue;
|
|
931
|
-
}
|
|
932
|
-
if (count >= limit) break;
|
|
933
|
-
callback({
|
|
934
|
-
stream: s.stream,
|
|
935
|
-
source: s.source,
|
|
936
|
-
at: s.at,
|
|
937
|
-
retry: s.retry,
|
|
938
|
-
blocked: s.blocked,
|
|
939
|
-
error: s.error,
|
|
940
|
-
priority: s.priority,
|
|
941
|
-
leased_by: s.leased_by,
|
|
942
|
-
leased_until: s.leased_until,
|
|
943
|
-
lane: s.lane,
|
|
944
|
-
deferred_at: s.deferred_at,
|
|
945
|
-
correlated_at: s.correlated_at
|
|
946
|
-
});
|
|
947
|
-
count++;
|
|
948
|
-
}
|
|
949
|
-
return { maxEventId: this._events.at(-1)?.id ?? -1, count };
|
|
950
|
-
}
|
|
951
|
-
async query_stats(input, options) {
|
|
952
|
-
await sleep();
|
|
953
|
-
const exclude = new Set(options?.exclude ?? []);
|
|
954
|
-
const want_tail = options?.tail ?? false;
|
|
955
|
-
const want_count = options?.count ?? false;
|
|
956
|
-
const want_names = options?.names ?? false;
|
|
957
|
-
const before = options?.before;
|
|
958
|
-
const after = options?.after;
|
|
959
|
-
const limit = options?.limit;
|
|
960
|
-
const array_targets = Array.isArray(input) ? new Set(input) : null;
|
|
961
|
-
const filter = Array.isArray(input) ? null : input;
|
|
962
|
-
const stream_re = filter?.stream !== void 0 && !filter.stream_exact ? new RegExp(filter.stream) : void 0;
|
|
963
|
-
const scope_cache = /* @__PURE__ */ new Map();
|
|
964
|
-
const in_scope = (stream) => {
|
|
965
|
-
const cached = scope_cache.get(stream);
|
|
966
|
-
if (cached !== void 0) return cached;
|
|
967
|
-
let ok = true;
|
|
968
|
-
if (array_targets) {
|
|
969
|
-
ok = array_targets.has(stream);
|
|
970
|
-
} else if (filter?.stream !== void 0) {
|
|
971
|
-
ok = filter.stream_exact ? stream === filter.stream : (
|
|
972
|
-
// stream_re set when stream is regex
|
|
973
|
-
stream_re.test(stream)
|
|
974
|
-
);
|
|
975
|
-
}
|
|
976
|
-
scope_cache.set(stream, ok);
|
|
977
|
-
return ok;
|
|
978
|
-
};
|
|
979
|
-
const acc = /* @__PURE__ */ new Map();
|
|
980
|
-
for (const e of this._events) {
|
|
981
|
-
if (before !== void 0 && e.id >= before) continue;
|
|
982
|
-
if (!in_scope(e.stream)) continue;
|
|
983
|
-
if (exclude.has(e.name)) continue;
|
|
984
|
-
let a = acc.get(e.stream);
|
|
985
|
-
if (!a) {
|
|
986
|
-
a = { head: e, count: 0 };
|
|
987
|
-
if (want_tail) a.tail = e;
|
|
988
|
-
if (want_names) a.names = {};
|
|
989
|
-
acc.set(e.stream, a);
|
|
990
|
-
}
|
|
991
|
-
a.head = e;
|
|
992
|
-
a.count++;
|
|
993
|
-
if (want_names) {
|
|
994
|
-
const n = String(e.name);
|
|
995
|
-
a.names[n] = (a.names[n] ?? 0) + 1;
|
|
996
|
-
}
|
|
997
|
-
}
|
|
998
|
-
const ordered = [...acc.keys()].sort();
|
|
999
|
-
const out = /* @__PURE__ */ new Map();
|
|
1000
|
-
for (const stream of ordered) {
|
|
1001
|
-
if (limit !== void 0 && out.size >= limit) break;
|
|
1002
|
-
if (after !== void 0 && stream <= after) continue;
|
|
1003
|
-
const a = acc.get(stream);
|
|
1004
|
-
const stats = { head: a.head };
|
|
1005
|
-
if (want_tail) stats.tail = a.tail;
|
|
1006
|
-
if (want_count) stats.count = a.count;
|
|
1007
|
-
if (want_names) stats.names = a.names;
|
|
1008
|
-
out.set(stream, stats);
|
|
1009
|
-
}
|
|
1010
|
-
return out;
|
|
1011
|
-
}
|
|
1012
|
-
async truncate(targets) {
|
|
1013
|
-
await sleep();
|
|
1014
|
-
const result = /* @__PURE__ */ new Map();
|
|
1015
|
-
const windowed = targets.filter((t) => t.before !== void 0);
|
|
1016
|
-
if (windowed.length) {
|
|
1017
|
-
const drop = /* @__PURE__ */ new Set();
|
|
1018
|
-
for (const { stream, before, max_id } of windowed) {
|
|
1019
|
-
let boundary;
|
|
1020
|
-
for (const e of this._events) {
|
|
1021
|
-
if (e.stream === stream && e.name === SNAP_EVENT && e.created < before && (max_id === void 0 || e.id <= max_id) && (!boundary || e.id > boundary.id))
|
|
1022
|
-
boundary = e;
|
|
1023
|
-
}
|
|
1024
|
-
if (!boundary) continue;
|
|
1025
|
-
let deleted = 0;
|
|
1026
|
-
for (const e of this._events) {
|
|
1027
|
-
if (e.stream === stream && e.id < boundary.id) {
|
|
1028
|
-
drop.add(e.id);
|
|
1029
|
-
this._pii.get(stream)?.delete(e.id);
|
|
1030
|
-
deleted++;
|
|
1031
|
-
}
|
|
1032
|
-
}
|
|
1033
|
-
result.set(stream, { deleted, committed: boundary, before });
|
|
1034
|
-
}
|
|
1035
|
-
if (drop.size) this._events = this._events.filter((e) => !drop.has(e.id));
|
|
1036
|
-
}
|
|
1037
|
-
const full = targets.filter((t) => t.before === void 0);
|
|
1038
|
-
for (const { stream, snapshot, meta } of full) {
|
|
1039
|
-
let deleted = 0;
|
|
1040
|
-
this._events = this._events.filter((e) => {
|
|
1041
|
-
if (e.stream !== stream) return true;
|
|
1042
|
-
deleted++;
|
|
1043
|
-
return false;
|
|
1044
|
-
});
|
|
1045
|
-
this._stream_versions.delete(stream);
|
|
1046
|
-
this._pii.delete(stream);
|
|
1047
|
-
const event = {
|
|
1048
|
-
id: this._next_id++,
|
|
1049
|
-
stream,
|
|
1050
|
-
version: 0,
|
|
1051
|
-
created: /* @__PURE__ */ new Date(),
|
|
1052
|
-
name: snapshot !== void 0 ? SNAP_EVENT : TOMBSTONE_EVENT,
|
|
1053
|
-
data: snapshot ?? {},
|
|
1054
|
-
meta: meta ?? { correlation: "", causation: {} }
|
|
1055
|
-
};
|
|
1056
|
-
this._events.push(event);
|
|
1057
|
-
this._stream_versions.set(stream, 0);
|
|
1058
|
-
result.set(stream, { deleted, committed: event });
|
|
1059
|
-
}
|
|
1060
|
-
return result;
|
|
1061
|
-
}
|
|
1062
|
-
/** A throw inside the driver restores the captured state. Ids restart at 0. */
|
|
1063
|
-
async restore(driver) {
|
|
1064
|
-
await sleep();
|
|
1065
|
-
const prev_events = this._events;
|
|
1066
|
-
const prev_next_id = this._next_id;
|
|
1067
|
-
const prev_streams = this._streams;
|
|
1068
|
-
const prev_stream_versions = this._stream_versions;
|
|
1069
|
-
const prev_pii = this._pii;
|
|
1070
|
-
const prev_correlated_at = this._correlated_at;
|
|
1071
|
-
const prev_correlators = new Map(
|
|
1072
|
-
[...this._correlators].map(([k, v]) => [k, { ...v }])
|
|
1073
|
-
);
|
|
1074
|
-
this._correlated_at = -1;
|
|
1075
|
-
for (const c of this._correlators.values()) c.at = -1;
|
|
1076
|
-
this._events = [];
|
|
1077
|
-
this._next_id = 0;
|
|
1078
|
-
this._streams = /* @__PURE__ */ new Map();
|
|
1079
|
-
this._stream_versions = /* @__PURE__ */ new Map();
|
|
1080
|
-
this._pii = /* @__PURE__ */ new Map();
|
|
1081
|
-
try {
|
|
1082
|
-
await driver(async (event) => {
|
|
1083
|
-
const id = this._next_id++;
|
|
1084
|
-
const { pii, ...rest } = event;
|
|
1085
|
-
const committed = { ...rest, id };
|
|
1086
|
-
this._events.push(committed);
|
|
1087
|
-
if (pii != null) this._put_pii(event.stream, id, pii);
|
|
1088
|
-
this._stream_versions.set(event.stream, event.version);
|
|
1089
|
-
return id;
|
|
1090
|
-
});
|
|
1091
|
-
} catch (err) {
|
|
1092
|
-
this._events = prev_events;
|
|
1093
|
-
this._next_id = prev_next_id;
|
|
1094
|
-
this._streams = prev_streams;
|
|
1095
|
-
this._stream_versions = prev_stream_versions;
|
|
1096
|
-
this._pii = prev_pii;
|
|
1097
|
-
this._correlated_at = prev_correlated_at;
|
|
1098
|
-
this._correlators = prev_correlators;
|
|
1099
|
-
throw err;
|
|
1100
|
-
}
|
|
1101
|
-
}
|
|
1102
|
-
};
|
|
4
|
+
var _chunkVE3MTY4Acjs = require('../chunk-VE3MTY4A.cjs');
|
|
5
|
+
require('../chunk-HF433MAF.cjs');
|
|
1103
6
|
|
|
1104
7
|
// src/test/sandbox.ts
|
|
8
|
+
var _vitest = require('vitest');
|
|
1105
9
|
async function sandbox(builder, options = {}) {
|
|
1106
|
-
const
|
|
1107
|
-
const
|
|
1108
|
-
await
|
|
10
|
+
const store = options.store ? await options.store() : new (0, _chunkVE3MTY4Acjs.InMemoryStore)();
|
|
11
|
+
const cache = options.cache ? await options.cache() : new (0, _chunkVE3MTY4Acjs.InMemoryCache)();
|
|
12
|
+
await store.seed();
|
|
1109
13
|
const app = builder.build({
|
|
1110
14
|
...options.actOptions,
|
|
1111
|
-
scoped: { store
|
|
15
|
+
scoped: { store, cache }
|
|
1112
16
|
});
|
|
1113
17
|
let _disposed;
|
|
1114
18
|
const dispose = () => {
|
|
1115
19
|
if (!_disposed) {
|
|
1116
20
|
_disposed = (async () => {
|
|
1117
21
|
await app.shutdown();
|
|
1118
|
-
await
|
|
1119
|
-
await
|
|
22
|
+
await store.dispose();
|
|
23
|
+
await cache.dispose();
|
|
1120
24
|
})();
|
|
1121
25
|
}
|
|
1122
26
|
return _disposed;
|
|
1123
27
|
};
|
|
1124
|
-
return { app, store
|
|
28
|
+
return { app, store, cache, dispose };
|
|
1125
29
|
}
|
|
1126
30
|
function fixture(builder, defaults = {}) {
|
|
1127
|
-
return
|
|
31
|
+
return _vitest.test.extend({
|
|
1128
32
|
app: async ({}, use) => {
|
|
1129
33
|
const ctx = await sandbox(builder, defaults);
|
|
1130
34
|
try {
|
|
@@ -1135,9 +39,8 @@ function fixture(builder, defaults = {}) {
|
|
|
1135
39
|
}
|
|
1136
40
|
});
|
|
1137
41
|
}
|
|
1138
|
-
|
|
1139
|
-
|
|
1140
|
-
|
|
1141
|
-
|
|
1142
|
-
});
|
|
42
|
+
|
|
43
|
+
|
|
44
|
+
|
|
45
|
+
exports.fixture = fixture; exports.sandbox = sandbox;
|
|
1143
46
|
//# sourceMappingURL=index.cjs.map
|