@rotorsoft/act 0.6.1 → 0.6.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/.tsbuildinfo +1 -1
- package/dist/@types/act.d.ts +2 -8
- package/dist/@types/act.d.ts.map +1 -1
- package/dist/@types/adapters/InMemoryStore.d.ts +18 -6
- package/dist/@types/adapters/InMemoryStore.d.ts.map +1 -1
- package/dist/@types/types/ports.d.ts +3 -3
- package/dist/@types/types/ports.d.ts.map +1 -1
- package/dist/@types/types/reaction.d.ts +28 -13
- package/dist/@types/types/reaction.d.ts.map +1 -1
- package/dist/@types/types/registry.d.ts +9 -0
- package/dist/@types/types/registry.d.ts.map +1 -1
- package/dist/index.cjs +128 -117
- package/dist/index.cjs.map +1 -1
- package/dist/index.js +128 -117
- package/dist/index.js.map +1 -1
- package/package.json +3 -3
package/dist/index.js
CHANGED
|
@@ -197,21 +197,21 @@ async function sleep(ms) {
|
|
|
197
197
|
|
|
198
198
|
// src/adapters/InMemoryStore.ts
|
|
199
199
|
var InMemoryStream = class {
|
|
200
|
-
stream;
|
|
201
|
-
source;
|
|
202
|
-
at = -1;
|
|
203
|
-
retry = -1;
|
|
204
|
-
blocked = false;
|
|
205
|
-
error = "";
|
|
206
|
-
leased_at = void 0;
|
|
207
|
-
leased_by = void 0;
|
|
208
|
-
leased_until = void 0;
|
|
209
200
|
constructor(stream, source) {
|
|
210
201
|
this.stream = stream;
|
|
211
202
|
this.source = source;
|
|
212
203
|
}
|
|
204
|
+
_at = -1;
|
|
205
|
+
_retry = -1;
|
|
206
|
+
_blocked = false;
|
|
207
|
+
_error = "";
|
|
208
|
+
_leased_by = void 0;
|
|
209
|
+
_leased_until = void 0;
|
|
213
210
|
get is_avaliable() {
|
|
214
|
-
return !this.
|
|
211
|
+
return !this._blocked && (!this._leased_until || this._leased_until <= /* @__PURE__ */ new Date());
|
|
212
|
+
}
|
|
213
|
+
get at() {
|
|
214
|
+
return this._at;
|
|
215
215
|
}
|
|
216
216
|
/**
|
|
217
217
|
* Attempt to lease this stream for processing.
|
|
@@ -221,17 +221,18 @@ var InMemoryStream = class {
|
|
|
221
221
|
* @returns The granted lease or undefined if blocked.
|
|
222
222
|
*/
|
|
223
223
|
lease(at, by, millis) {
|
|
224
|
-
if (this.is_avaliable
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
224
|
+
if (this.is_avaliable) {
|
|
225
|
+
if (millis > 0) {
|
|
226
|
+
this._leased_by = by;
|
|
227
|
+
this._leased_until = new Date(Date.now() + millis);
|
|
228
|
+
this._retry = this._retry + 1;
|
|
229
|
+
}
|
|
229
230
|
return {
|
|
230
231
|
stream: this.stream,
|
|
231
232
|
source: this.source,
|
|
232
233
|
at,
|
|
233
234
|
by,
|
|
234
|
-
retry: this.
|
|
235
|
+
retry: this._retry
|
|
235
236
|
};
|
|
236
237
|
}
|
|
237
238
|
}
|
|
@@ -241,27 +242,37 @@ var InMemoryStream = class {
|
|
|
241
242
|
* @param by - Lease holder that processed the watermark.
|
|
242
243
|
*/
|
|
243
244
|
ack(at, by) {
|
|
244
|
-
if (this.
|
|
245
|
-
this.
|
|
246
|
-
this.
|
|
247
|
-
this.
|
|
248
|
-
this.
|
|
249
|
-
|
|
250
|
-
|
|
245
|
+
if (this._leased_by === by) {
|
|
246
|
+
this._leased_by = void 0;
|
|
247
|
+
this._leased_until = void 0;
|
|
248
|
+
this._at = at;
|
|
249
|
+
this._retry = -1;
|
|
250
|
+
return {
|
|
251
|
+
stream: this.stream,
|
|
252
|
+
source: this.source,
|
|
253
|
+
at: this._at,
|
|
254
|
+
by,
|
|
255
|
+
retry: this._retry
|
|
256
|
+
};
|
|
251
257
|
}
|
|
252
|
-
return false;
|
|
253
258
|
}
|
|
254
259
|
/**
|
|
255
260
|
* Block a stream for processing after failing to process and reaching max retries with blocking enabled.
|
|
256
261
|
* @param error Blocked error message.
|
|
257
262
|
*/
|
|
258
263
|
block(by, error) {
|
|
259
|
-
if (this.
|
|
260
|
-
this.
|
|
261
|
-
this.
|
|
262
|
-
return
|
|
264
|
+
if (this._leased_by === by) {
|
|
265
|
+
this._blocked = true;
|
|
266
|
+
this._error = error;
|
|
267
|
+
return {
|
|
268
|
+
stream: this.stream,
|
|
269
|
+
source: this.source,
|
|
270
|
+
at: this._at,
|
|
271
|
+
by: this._leased_by,
|
|
272
|
+
retry: this._retry,
|
|
273
|
+
error: this._error
|
|
274
|
+
};
|
|
263
275
|
}
|
|
264
|
-
return false;
|
|
265
276
|
}
|
|
266
277
|
};
|
|
267
278
|
var InMemoryStore = class {
|
|
@@ -293,6 +304,15 @@ var InMemoryStore = class {
|
|
|
293
304
|
this._events.length = 0;
|
|
294
305
|
this._streams = /* @__PURE__ */ new Map();
|
|
295
306
|
}
|
|
307
|
+
in_query(query, e) {
|
|
308
|
+
if (query.stream && !RegExp(`^${query.stream}$`).test(e.stream))
|
|
309
|
+
return false;
|
|
310
|
+
if (query.names && !query.names.includes(e.name)) return false;
|
|
311
|
+
if (query.correlation && e.meta?.correlation !== query.correlation)
|
|
312
|
+
return false;
|
|
313
|
+
if (e.name === SNAP_EVENT && !query.with_snaps) return false;
|
|
314
|
+
return true;
|
|
315
|
+
}
|
|
296
316
|
/**
|
|
297
317
|
* Query events in the store, optionally filtered by query options.
|
|
298
318
|
* @param callback - Function to call for each event.
|
|
@@ -301,30 +321,32 @@ var InMemoryStore = class {
|
|
|
301
321
|
*/
|
|
302
322
|
async query(callback, query) {
|
|
303
323
|
await sleep();
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
|
|
324
|
+
let count = 0;
|
|
325
|
+
if (query?.backward) {
|
|
326
|
+
let i = (query?.before || this._events.length) - 1;
|
|
327
|
+
while (i >= 0) {
|
|
328
|
+
const e = this._events[i--];
|
|
329
|
+
if (query && !this.in_query(query, e)) continue;
|
|
330
|
+
if (query?.created_before && e.created >= query.created_before)
|
|
331
|
+
continue;
|
|
332
|
+
if (query.after && e.id <= query.after) break;
|
|
333
|
+
if (query.created_after && e.created <= query.created_after) break;
|
|
334
|
+
callback(e);
|
|
335
|
+
count++;
|
|
336
|
+
if (query?.limit && count >= query.limit) break;
|
|
337
|
+
}
|
|
338
|
+
} else {
|
|
339
|
+
let i = (query?.after ?? -1) + 1;
|
|
340
|
+
while (i < this._events.length) {
|
|
341
|
+
const e = this._events[i++];
|
|
342
|
+
if (query && !this.in_query(query, e)) continue;
|
|
343
|
+
if (query?.created_after && e.created <= query.created_after) continue;
|
|
344
|
+
if (query?.before && e.id >= query.before) break;
|
|
345
|
+
if (query?.created_before && e.created >= query.created_before) break;
|
|
346
|
+
callback(e);
|
|
347
|
+
count++;
|
|
348
|
+
if (query?.limit && count >= query.limit) break;
|
|
349
|
+
}
|
|
328
350
|
}
|
|
329
351
|
return count;
|
|
330
352
|
}
|
|
@@ -366,13 +388,15 @@ var InMemoryStore = class {
|
|
|
366
388
|
}
|
|
367
389
|
/**
|
|
368
390
|
* Polls the store for unblocked streams needing processing, ordered by lease watermark ascending.
|
|
369
|
-
* @param
|
|
370
|
-
* @param
|
|
391
|
+
* @param lagging - Max number of streams to poll in ascending order.
|
|
392
|
+
* @param leading - Max number of streams to poll in descending order.
|
|
371
393
|
* @returns The polled streams.
|
|
372
394
|
*/
|
|
373
|
-
async poll(
|
|
395
|
+
async poll(lagging, leading) {
|
|
374
396
|
await sleep();
|
|
375
|
-
|
|
397
|
+
const a = [...this._streams.values()].filter((s) => s.is_avaliable).sort((a2, b2) => a2.at - b2.at).slice(0, lagging).map(({ stream, source, at }) => ({ stream, source, at }));
|
|
398
|
+
const b = [...this._streams.values()].filter((s) => s.is_avaliable).sort((a2, b2) => b2.at - a2.at).slice(0, leading).map(({ stream, source, at }) => ({ stream, source, at }));
|
|
399
|
+
return [...a, ...b];
|
|
376
400
|
}
|
|
377
401
|
/**
|
|
378
402
|
* Lease streams for processing (e.g., for distributed consumers).
|
|
@@ -382,10 +406,11 @@ var InMemoryStore = class {
|
|
|
382
406
|
*/
|
|
383
407
|
async lease(leases, millis) {
|
|
384
408
|
await sleep();
|
|
385
|
-
return leases.map((
|
|
386
|
-
|
|
387
|
-
|
|
388
|
-
|
|
409
|
+
return leases.map((l) => {
|
|
410
|
+
if (!this._streams.has(l.stream)) {
|
|
411
|
+
this._streams.set(l.stream, new InMemoryStream(l.stream, l.source));
|
|
412
|
+
}
|
|
413
|
+
return this._streams.get(l.stream)?.lease(l.at, l.by, millis);
|
|
389
414
|
}).filter((l) => !!l);
|
|
390
415
|
}
|
|
391
416
|
/**
|
|
@@ -394,9 +419,7 @@ var InMemoryStore = class {
|
|
|
394
419
|
*/
|
|
395
420
|
async ack(leases) {
|
|
396
421
|
await sleep();
|
|
397
|
-
return leases.filter(
|
|
398
|
-
(lease) => this._streams.get(lease.stream)?.ack(lease.at, lease.by)
|
|
399
|
-
);
|
|
422
|
+
return leases.map((l) => this._streams.get(l.stream)?.ack(l.at, l.by)).filter((l) => !!l);
|
|
400
423
|
}
|
|
401
424
|
/**
|
|
402
425
|
* Block a stream for processing after failing to process and reaching max retries with blocking enabled.
|
|
@@ -405,9 +428,7 @@ var InMemoryStore = class {
|
|
|
405
428
|
*/
|
|
406
429
|
async block(leases) {
|
|
407
430
|
await sleep();
|
|
408
|
-
return leases.filter(
|
|
409
|
-
(lease) => this._streams.get(lease.stream)?.block(lease.by, lease.error)
|
|
410
|
-
);
|
|
431
|
+
return leases.map((l) => this._streams.get(l.stream)?.block(l.by, l.error)).filter((l) => !!l);
|
|
411
432
|
}
|
|
412
433
|
};
|
|
413
434
|
|
|
@@ -807,13 +828,14 @@ var Act = class {
|
|
|
807
828
|
async drain({
|
|
808
829
|
streamLimit = 10,
|
|
809
830
|
eventLimit = 10,
|
|
810
|
-
leaseMillis = 1e4
|
|
811
|
-
descending = false
|
|
831
|
+
leaseMillis = 1e4
|
|
812
832
|
} = {}) {
|
|
813
833
|
if (!this._drain_locked) {
|
|
814
834
|
try {
|
|
815
835
|
this._drain_locked = true;
|
|
816
|
-
const
|
|
836
|
+
const lagging = Math.ceil(streamLimit * 2 / 3);
|
|
837
|
+
const leading = streamLimit - lagging;
|
|
838
|
+
const polled = await store().poll(lagging, leading);
|
|
817
839
|
const fetched = await Promise.all(
|
|
818
840
|
polled.map(async ({ stream, source, at }) => {
|
|
819
841
|
const events = await this.query_array({
|
|
@@ -821,28 +843,21 @@ var Act = class {
|
|
|
821
843
|
after: at,
|
|
822
844
|
limit: eventLimit
|
|
823
845
|
});
|
|
824
|
-
return { stream, source, events };
|
|
846
|
+
return { stream, source, at, events };
|
|
825
847
|
})
|
|
826
848
|
);
|
|
827
|
-
fetched.length
|
|
828
|
-
|
|
829
|
-
([last_at2, count2], { events }) => [
|
|
830
|
-
Math.max(last_at2, events.at(-1)?.id || 0),
|
|
831
|
-
count2 + events.length
|
|
832
|
-
],
|
|
833
|
-
[0, 0]
|
|
834
|
-
);
|
|
835
|
-
if (count > 0) {
|
|
849
|
+
if (fetched.length) {
|
|
850
|
+
tracer.fetched(fetched);
|
|
836
851
|
const leases = /* @__PURE__ */ new Map();
|
|
852
|
+
const last_window_at = fetched.reduce(
|
|
853
|
+
(max, { at, events }) => Math.max(max, events.at(-1)?.id || at),
|
|
854
|
+
0
|
|
855
|
+
);
|
|
837
856
|
fetched.forEach(({ stream, events }) => {
|
|
838
857
|
const payloads = events.flatMap((event) => {
|
|
839
|
-
const register = this.registry.events[event.name];
|
|
840
|
-
if (!register) return [];
|
|
858
|
+
const register = this.registry.events[event.name] || [];
|
|
841
859
|
return [...register.reactions.values()].filter((reaction) => {
|
|
842
|
-
const resolved = typeof reaction.resolver === "function" ? (
|
|
843
|
-
// @ts-expect-error index by key
|
|
844
|
-
reaction.resolver(event)
|
|
845
|
-
) : reaction.resolver;
|
|
860
|
+
const resolved = typeof reaction.resolver === "function" ? reaction.resolver(event) : reaction.resolver;
|
|
846
861
|
return resolved && resolved.target === stream;
|
|
847
862
|
}).map((reaction) => ({ ...reaction, event }));
|
|
848
863
|
});
|
|
@@ -850,43 +865,39 @@ var Act = class {
|
|
|
850
865
|
lease: {
|
|
851
866
|
stream,
|
|
852
867
|
by: randomUUID2(),
|
|
853
|
-
at: events.at(-1)?.id ||
|
|
854
|
-
//
|
|
868
|
+
at: events.at(-1)?.id || last_window_at,
|
|
869
|
+
// ff when no matching events
|
|
855
870
|
retry: 0
|
|
856
871
|
},
|
|
857
872
|
// @ts-expect-error indexed by key
|
|
858
873
|
payloads
|
|
859
874
|
});
|
|
860
875
|
});
|
|
861
|
-
|
|
862
|
-
|
|
863
|
-
|
|
864
|
-
|
|
865
|
-
|
|
866
|
-
|
|
867
|
-
|
|
868
|
-
|
|
869
|
-
|
|
870
|
-
|
|
871
|
-
|
|
872
|
-
|
|
873
|
-
|
|
874
|
-
|
|
875
|
-
|
|
876
|
-
|
|
877
|
-
|
|
878
|
-
|
|
879
|
-
|
|
880
|
-
|
|
881
|
-
|
|
882
|
-
|
|
883
|
-
|
|
884
|
-
tracer.blocked(blocked);
|
|
885
|
-
this.emit("blocked", blocked);
|
|
886
|
-
}
|
|
887
|
-
return { leased, acked, blocked };
|
|
888
|
-
}
|
|
876
|
+
const leased = await store().lease(
|
|
877
|
+
[...leases.values()].map((l) => l.lease),
|
|
878
|
+
leaseMillis
|
|
879
|
+
);
|
|
880
|
+
tracer.leased(leased);
|
|
881
|
+
const handled = await Promise.all(
|
|
882
|
+
leased.map(
|
|
883
|
+
(lease) => this.handle(lease, leases.get(lease.stream).payloads)
|
|
884
|
+
)
|
|
885
|
+
);
|
|
886
|
+
const acked = await store().ack(
|
|
887
|
+
handled.filter(({ error }) => !error).map(({ at, lease }) => ({ ...lease, at }))
|
|
888
|
+
);
|
|
889
|
+
if (acked.length) {
|
|
890
|
+
tracer.acked(acked);
|
|
891
|
+
this.emit("acked", acked);
|
|
892
|
+
}
|
|
893
|
+
const blocked = await store().block(
|
|
894
|
+
handled.filter(({ block }) => block).map(({ lease, error }) => ({ ...lease, error }))
|
|
895
|
+
);
|
|
896
|
+
if (blocked.length) {
|
|
897
|
+
tracer.blocked(blocked);
|
|
898
|
+
this.emit("blocked", blocked);
|
|
889
899
|
}
|
|
900
|
+
return { fetched, leased, acked, blocked };
|
|
890
901
|
}
|
|
891
902
|
} catch (error) {
|
|
892
903
|
logger.error(error);
|
|
@@ -894,7 +905,7 @@ var Act = class {
|
|
|
894
905
|
this._drain_locked = false;
|
|
895
906
|
}
|
|
896
907
|
}
|
|
897
|
-
return { leased: [], acked: [], blocked: [] };
|
|
908
|
+
return { fetched: [], leased: [], acked: [], blocked: [] };
|
|
898
909
|
}
|
|
899
910
|
/**
|
|
900
911
|
* Correlates streams using reaction resolvers.
|