actly 1.1.5 → 1.3.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/CHANGELOG.md +234 -0
- package/MIGRATION.md +141 -0
- package/README.md +334 -428
- package/SECURITY.md +56 -0
- package/dist/core/act.cjs +630 -126
- package/dist/core/act.d.ts +6 -84
- package/dist/core/act.d.ts.map +1 -1
- package/dist/core/act.js +630 -129
- package/dist/core/executor.cjs +1 -26
- package/dist/core/executor.d.ts +2 -38
- package/dist/core/executor.d.ts.map +1 -1
- package/dist/core/executor.js +1 -27
- package/dist/core/health.cjs +163 -0
- package/dist/core/health.d.ts +33 -0
- package/dist/core/health.d.ts.map +1 -0
- package/dist/core/health.js +150 -0
- package/dist/core/shutdown.cjs +71 -0
- package/dist/core/shutdown.d.ts +5 -0
- package/dist/core/shutdown.d.ts.map +1 -0
- package/dist/core/shutdown.js +65 -0
- package/dist/core/tenant.cjs +113 -0
- package/dist/core/tenant.d.ts +24 -0
- package/dist/core/tenant.d.ts.map +1 -0
- package/dist/core/tenant.js +109 -0
- package/dist/errors.cjs +149 -0
- package/dist/errors.d.ts +94 -0
- package/dist/errors.d.ts.map +1 -0
- package/dist/errors.js +134 -0
- package/dist/index.cjs +51 -8
- package/dist/index.d.ts +22 -3
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +14 -6
- package/dist/observability.cjs +6 -0
- package/dist/observability.d.ts +86 -0
- package/dist/observability.d.ts.map +1 -0
- package/dist/observability.js +3 -0
- package/dist/policies/bulkhead.cjs +137 -0
- package/dist/policies/bulkhead.d.ts +3 -0
- package/dist/policies/bulkhead.d.ts.map +1 -0
- package/dist/policies/bulkhead.js +134 -0
- package/dist/policies/cache.cjs +84 -67
- package/dist/policies/cache.d.ts +0 -33
- package/dist/policies/cache.d.ts.map +1 -1
- package/dist/policies/cache.js +84 -68
- package/dist/policies/circuitBreaker.cjs +179 -0
- package/dist/policies/circuitBreaker.d.ts +3 -0
- package/dist/policies/circuitBreaker.d.ts.map +1 -0
- package/dist/policies/circuitBreaker.js +176 -0
- package/dist/policies/dedupe.cjs +46 -64
- package/dist/policies/dedupe.d.ts +0 -41
- package/dist/policies/dedupe.d.ts.map +1 -1
- package/dist/policies/dedupe.js +46 -65
- package/dist/policies/noop.cjs +6 -0
- package/dist/policies/noop.d.ts +3 -0
- package/dist/policies/noop.d.ts.map +1 -0
- package/dist/policies/noop.js +3 -0
- package/dist/policies/rateLimit.cjs +45 -0
- package/dist/policies/rateLimit.d.ts +3 -0
- package/dist/policies/rateLimit.d.ts.map +1 -0
- package/dist/policies/rateLimit.js +42 -0
- package/dist/policies/retry.cjs +129 -57
- package/dist/policies/retry.d.ts +0 -27
- package/dist/policies/retry.d.ts.map +1 -1
- package/dist/policies/retry.js +129 -58
- package/dist/policies/timeout.cjs +47 -103
- package/dist/policies/timeout.d.ts +2 -46
- package/dist/policies/timeout.d.ts.map +1 -1
- package/dist/policies/timeout.js +46 -102
- package/dist/stores/base.cjs +0 -10
- package/dist/stores/base.d.ts +4 -67
- package/dist/stores/base.d.ts.map +1 -1
- package/dist/stores/base.js +0 -11
- package/dist/stores/memory.cjs +147 -87
- package/dist/stores/memory.d.ts +11 -59
- package/dist/stores/memory.d.ts.map +1 -1
- package/dist/stores/memory.js +146 -88
- package/dist/testing/index.cjs +56 -0
- package/dist/testing/index.d.ts +6 -0
- package/dist/testing/index.d.ts.map +1 -0
- package/dist/testing/index.js +52 -0
- package/dist/types/index.cjs +0 -1
- package/dist/types/index.d.ts +62 -179
- package/dist/types/index.d.ts.map +1 -1
- package/dist/types/index.js +0 -2
- package/dist/utils/abort.cjs +46 -73
- package/dist/utils/abort.d.ts +4 -51
- package/dist/utils/abort.d.ts.map +1 -1
- package/dist/utils/abort.js +46 -74
- package/dist/utils/abortPool.cjs +25 -0
- package/dist/utils/abortPool.d.ts +4 -0
- package/dist/utils/abortPool.d.ts.map +1 -0
- package/dist/utils/abortPool.js +20 -0
- package/dist/utils/backoff.cjs +8 -15
- package/dist/utils/backoff.d.ts +0 -11
- package/dist/utils/backoff.d.ts.map +1 -1
- package/dist/utils/backoff.js +8 -16
- package/dist/utils/decorator.cjs +44 -0
- package/dist/utils/decorator.d.ts +3 -0
- package/dist/utils/decorator.d.ts.map +1 -0
- package/dist/utils/decorator.js +41 -0
- package/dist/utils/key.cjs +45 -0
- package/dist/utils/key.d.ts +2 -0
- package/dist/utils/key.d.ts.map +1 -0
- package/dist/utils/key.js +42 -0
- package/dist/utils/limits.cjs +22 -0
- package/dist/utils/limits.d.ts +21 -0
- package/dist/utils/limits.d.ts.map +1 -0
- package/dist/utils/limits.js +19 -0
- package/dist/utils/safeCall.cjs +28 -0
- package/dist/utils/safeCall.d.ts +2 -0
- package/dist/utils/safeCall.d.ts.map +1 -0
- package/dist/utils/safeCall.js +25 -0
- package/dist/utils/sanitize.cjs +50 -0
- package/dist/utils/sanitize.d.ts +3 -0
- package/dist/utils/sanitize.d.ts.map +1 -0
- package/dist/utils/sanitize.js +46 -0
- package/dist/utils/validate.cjs +163 -26
- package/dist/utils/validate.d.ts +6 -8
- package/dist/utils/validate.d.ts.map +1 -1
- package/dist/utils/validate.js +158 -27
- package/package.json +42 -7
- package/dist/core/act.js.map +0 -1
- package/dist/core/executor.js.map +0 -1
- package/dist/index.js.map +0 -1
- package/dist/policies/cache.js.map +0 -1
- package/dist/policies/dedupe.js.map +0 -1
- package/dist/policies/retry.js.map +0 -1
- package/dist/policies/timeout.js.map +0 -1
- package/dist/state/store.cjs +0 -14
- package/dist/state/store.d.ts +0 -11
- package/dist/state/store.d.ts.map +0 -1
- package/dist/state/store.js +0 -11
- package/dist/state/store.js.map +0 -1
- package/dist/stores/base.js.map +0 -1
- package/dist/stores/memory.js.map +0 -1
- package/dist/types/index.js.map +0 -1
- package/dist/utils/abort.js.map +0 -1
- package/dist/utils/backoff.js.map +0 -1
- package/dist/utils/validate.js.map +0 -1
package/dist/stores/memory.d.ts
CHANGED
|
@@ -1,79 +1,31 @@
|
|
|
1
1
|
import type { SyncStateStore } from './base.js';
|
|
2
2
|
export interface InMemoryStoreOptions {
|
|
3
|
-
/**
|
|
4
|
-
* Periodically sweep and remove expired entries in the background.
|
|
5
|
-
*
|
|
6
|
-
* Disabled by default. The store evicts lazily on `get()` / `has()` access,
|
|
7
|
-
* which is sufficient for most use cases. Enable `autoCleanup` when the
|
|
8
|
-
* store is long-lived and accumulates many TTL'd entries that are never
|
|
9
|
-
* re-read — for example, a server-side cache that receives write-heavy
|
|
10
|
-
* traffic with low subsequent read rates.
|
|
11
|
-
*/
|
|
12
3
|
autoCleanup?: boolean;
|
|
13
|
-
/**
|
|
14
|
-
* Interval between background sweeps in milliseconds.
|
|
15
|
-
* Defaults to 30 000 (30 seconds). Ignored when `autoCleanup` is false.
|
|
16
|
-
*/
|
|
17
4
|
cleanupIntervalMs?: number;
|
|
18
|
-
/**
|
|
19
|
-
* Maximum number of live entries the store will hold.
|
|
20
|
-
*
|
|
21
|
-
* When `set()` would exceed this limit, the least-recently-used entry is
|
|
22
|
-
* evicted before the new one is inserted (LRU semantics). Updates to an
|
|
23
|
-
* existing key do not trigger eviction.
|
|
24
|
-
*
|
|
25
|
-
* Defaults to `Infinity` (unbounded). Set a finite value for long-running
|
|
26
|
-
* caches with high-cardinality keys to bound memory usage.
|
|
27
|
-
*
|
|
28
|
-
* The LRU order is updated on `get()` and `set()` — both move the accessed
|
|
29
|
-
* key to the most-recent position.
|
|
30
|
-
*/
|
|
31
5
|
maxSize?: number;
|
|
6
|
+
memoryPressureCleanup?: boolean;
|
|
32
7
|
}
|
|
33
|
-
/**
|
|
34
|
-
* Reference `SyncStateStore` implementation backed by a `Map`.
|
|
35
|
-
*
|
|
36
|
-
* # LRU semantics
|
|
37
|
-
*
|
|
38
|
-
* `Map` iteration order is insertion order, so we implement LRU by
|
|
39
|
-
* `delete` + `set` on every access — the most-recently-touched key ends up
|
|
40
|
-
* at the end of the iteration, and the oldest is `entries.keys().next().value`.
|
|
41
|
-
*
|
|
42
|
-
* # Expiry
|
|
43
|
-
*
|
|
44
|
-
* Lazy on `get()` / `has()`: expired entries are deleted when touched.
|
|
45
|
-
* Background sweep (optional) reclaims entries that are never re-read.
|
|
46
|
-
*/
|
|
47
8
|
export declare class InMemoryStore implements SyncStateStore {
|
|
48
9
|
readonly _sync: true;
|
|
49
|
-
private
|
|
10
|
+
private static finalizer;
|
|
11
|
+
private readonly map;
|
|
50
12
|
private readonly maxSize;
|
|
13
|
+
private head?;
|
|
14
|
+
private tail?;
|
|
51
15
|
private cleanupTimer;
|
|
16
|
+
private memoryListener;
|
|
52
17
|
constructor(options?: InMemoryStoreOptions);
|
|
53
18
|
get<T>(key: string): T | undefined;
|
|
54
19
|
set<T>(key: string, value: T, ttlMs?: number): void;
|
|
55
20
|
delete(key: string): void;
|
|
56
21
|
has(key: string): boolean;
|
|
57
22
|
clear(): void;
|
|
58
|
-
/**
|
|
59
|
-
* Return the count of live (non-expired) entries.
|
|
60
|
-
*
|
|
61
|
-
* Pure query — does NOT touch LRU order. Expired entries discovered during
|
|
62
|
-
* the scan are evicted opportunistically (they were already invisible to
|
|
63
|
-
* `get()`, so eviction has no observable effect beyond memory reclamation).
|
|
64
|
-
*
|
|
65
|
-
* Two-pass to avoid mutating the Map during iteration (spec-safe).
|
|
66
|
-
*/
|
|
67
23
|
size(): number;
|
|
68
|
-
/**
|
|
69
|
-
* Stop the background cleanup timer and release internal state.
|
|
70
|
-
* Safe to call multiple times — subsequent calls are no-ops.
|
|
71
|
-
*/
|
|
72
24
|
destroy(): void;
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
private _sweep;
|
|
25
|
+
private appendTail;
|
|
26
|
+
private removeNode;
|
|
27
|
+
private moveToTail;
|
|
28
|
+
private sweep;
|
|
78
29
|
}
|
|
30
|
+
export declare function createDefaultStore(): InMemoryStore;
|
|
79
31
|
//# sourceMappingURL=memory.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"memory.d.ts","sourceRoot":"","sources":["../../src/stores/memory.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,WAAW,CAAA;
|
|
1
|
+
{"version":3,"file":"memory.d.ts","sourceRoot":"","sources":["../../src/stores/memory.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,WAAW,CAAA;AAgC/C,MAAM,WAAW,oBAAoB;IAKnC,WAAW,CAAC,EAAE,OAAO,CAAA;IAGrB,iBAAiB,CAAC,EAAE,MAAM,CAAA;IAU1B,OAAO,CAAC,EAAE,MAAM,CAAA;IAQhB,qBAAqB,CAAC,EAAE,OAAO,CAAA;CAChC;AAcD,qBAAa,aAAc,YAAW,cAAc;IAClD,QAAQ,CAAC,KAAK,EAAG,IAAI,CAAS;IAS9B,OAAO,CAAC,MAAM,CAAC,SAAS,CAAkE;IAS1F,OAAO,CAAC,QAAQ,CAAC,GAAG,CAA6B;IACjD,OAAO,CAAC,QAAQ,CAAC,OAAO,CAAQ;IAChC,OAAO,CAAC,IAAI,CAAC,CAAS;IACtB,OAAO,CAAC,IAAI,CAAC,CAAS;IACtB,OAAO,CAAC,YAAY,CAA4C;IAChE,OAAO,CAAC,cAAc,CAA4C;gBAEtD,OAAO,GAAE,oBAAyB;IAkD9C,GAAG,CAAC,CAAC,EAAE,GAAG,EAAE,MAAM,GAAG,CAAC,GAAG,SAAS;IAclC,GAAG,CAAC,CAAC,EAAE,GAAG,EAAE,MAAM,EAAE,KAAK,EAAE,CAAC,EAAE,KAAK,CAAC,EAAE,MAAM,GAAG,IAAI;IA+BnD,MAAM,CAAC,GAAG,EAAE,MAAM,GAAG,IAAI;IAOzB,GAAG,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO;IAYzB,KAAK,IAAI,IAAI;IAYb,IAAI,IAAI,MAAM;IAUd,OAAO,IAAI,IAAI;IAyBf,OAAO,CAAC,UAAU;IAWlB,OAAO,CAAC,UAAU;IAelB,OAAO,CAAC,UAAU;IAWlB,OAAO,CAAC,KAAK;CAoBd;AAOD,wBAAgB,kBAAkB,IAAI,aAAa,CAMlD"}
|
package/dist/stores/memory.js
CHANGED
|
@@ -1,137 +1,195 @@
|
|
|
1
|
+
import { LIMITS } from '../utils/limits.js';
|
|
1
2
|
function isUnrefable(t) {
|
|
2
3
|
return typeof t.unref === 'function';
|
|
3
4
|
}
|
|
4
|
-
// ─── Implementation ───────────────────────────────────────────────────────────
|
|
5
|
-
/**
|
|
6
|
-
* Reference `SyncStateStore` implementation backed by a `Map`.
|
|
7
|
-
*
|
|
8
|
-
* # LRU semantics
|
|
9
|
-
*
|
|
10
|
-
* `Map` iteration order is insertion order, so we implement LRU by
|
|
11
|
-
* `delete` + `set` on every access — the most-recently-touched key ends up
|
|
12
|
-
* at the end of the iteration, and the oldest is `entries.keys().next().value`.
|
|
13
|
-
*
|
|
14
|
-
* # Expiry
|
|
15
|
-
*
|
|
16
|
-
* Lazy on `get()` / `has()`: expired entries are deleted when touched.
|
|
17
|
-
* Background sweep (optional) reclaims entries that are never re-read.
|
|
18
|
-
*/
|
|
19
5
|
export class InMemoryStore {
|
|
20
6
|
_sync = true;
|
|
21
|
-
|
|
7
|
+
static finalizer;
|
|
8
|
+
static {
|
|
9
|
+
if (typeof FinalizationRegistry === 'function') {
|
|
10
|
+
InMemoryStore.finalizer = new FinalizationRegistry((timer) => {
|
|
11
|
+
try {
|
|
12
|
+
clearInterval(timer);
|
|
13
|
+
}
|
|
14
|
+
catch { }
|
|
15
|
+
});
|
|
16
|
+
}
|
|
17
|
+
}
|
|
18
|
+
map = new Map();
|
|
22
19
|
maxSize;
|
|
20
|
+
head;
|
|
21
|
+
tail;
|
|
23
22
|
cleanupTimer;
|
|
23
|
+
memoryListener;
|
|
24
24
|
constructor(options = {}) {
|
|
25
|
-
const { autoCleanup = false, cleanupIntervalMs = 30_000, maxSize =
|
|
25
|
+
const { autoCleanup = false, cleanupIntervalMs = 30_000, maxSize = LIMITS.DEFAULT_STORE_MAX_SIZE, memoryPressureCleanup = false, } = options;
|
|
26
26
|
if (!Number.isFinite(maxSize) || maxSize <= 0) {
|
|
27
|
-
// Infinity is allowed (unbounded); any other non-positive finite value
|
|
28
|
-
// is a programmer error.
|
|
29
27
|
if (maxSize !== Number.POSITIVE_INFINITY) {
|
|
30
28
|
throw new RangeError(`Actly: InMemoryStore maxSize must be a positive finite number or Infinity, got ${maxSize}`);
|
|
31
29
|
}
|
|
32
30
|
}
|
|
31
|
+
if (maxSize !== Number.POSITIVE_INFINITY && !Number.isInteger(maxSize)) {
|
|
32
|
+
throw new RangeError(`Actly: InMemoryStore maxSize must be a positive integer or Infinity, got ${maxSize}`);
|
|
33
|
+
}
|
|
33
34
|
this.maxSize = maxSize;
|
|
34
35
|
if (autoCleanup) {
|
|
35
|
-
const timer = setInterval(() => this.
|
|
36
|
+
const timer = setInterval(() => this.sweep(), cleanupIntervalMs);
|
|
36
37
|
if (isUnrefable(timer))
|
|
37
38
|
timer.unref();
|
|
38
39
|
this.cleanupTimer = timer;
|
|
40
|
+
InMemoryStore.finalizer?.register(this, timer, this);
|
|
41
|
+
}
|
|
42
|
+
if (memoryPressureCleanup) {
|
|
43
|
+
const processOn = process.on;
|
|
44
|
+
if (typeof processOn === 'function') {
|
|
45
|
+
const memoryListener = () => {
|
|
46
|
+
try {
|
|
47
|
+
this.sweep();
|
|
48
|
+
}
|
|
49
|
+
catch { }
|
|
50
|
+
};
|
|
51
|
+
processOn.call(process, 'memory', memoryListener);
|
|
52
|
+
this.memoryListener = memoryListener;
|
|
53
|
+
}
|
|
39
54
|
}
|
|
40
55
|
}
|
|
41
56
|
get(key) {
|
|
42
|
-
const
|
|
43
|
-
if (!
|
|
57
|
+
const node = this.map.get(key);
|
|
58
|
+
if (!node)
|
|
44
59
|
return undefined;
|
|
45
|
-
if (
|
|
46
|
-
this.
|
|
60
|
+
if (node.expiresAt !== null && Date.now() > node.expiresAt) {
|
|
61
|
+
this.removeNode(node);
|
|
62
|
+
this.map.delete(key);
|
|
47
63
|
return undefined;
|
|
48
64
|
}
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
this.entries.delete(key);
|
|
52
|
-
this.entries.set(key, entry);
|
|
53
|
-
return entry.value;
|
|
65
|
+
this.moveToTail(node);
|
|
66
|
+
return node.value;
|
|
54
67
|
}
|
|
55
68
|
set(key, value, ttlMs) {
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
this.
|
|
66
|
-
|
|
69
|
+
const existing = this.map.get(key);
|
|
70
|
+
const now = Date.now();
|
|
71
|
+
if (existing) {
|
|
72
|
+
existing.value = value;
|
|
73
|
+
existing.expiresAt = ttlMs != null && Number.isFinite(ttlMs) && ttlMs > 0 ? now + ttlMs : null;
|
|
74
|
+
existing.insertedAt = now;
|
|
75
|
+
this.moveToTail(existing);
|
|
76
|
+
return;
|
|
77
|
+
}
|
|
78
|
+
while (this.map.size >= this.maxSize && this.head) {
|
|
79
|
+
const evict = this.head;
|
|
80
|
+
this.removeNode(evict);
|
|
81
|
+
this.map.delete(evict.key);
|
|
82
|
+
}
|
|
83
|
+
const node = {
|
|
84
|
+
key,
|
|
85
|
+
value,
|
|
86
|
+
expiresAt: ttlMs != null && Number.isFinite(ttlMs) && ttlMs > 0 ? now + ttlMs : null,
|
|
87
|
+
insertedAt: now,
|
|
88
|
+
};
|
|
89
|
+
this.map.set(key, node);
|
|
90
|
+
this.appendTail(node);
|
|
67
91
|
}
|
|
68
92
|
delete(key) {
|
|
69
|
-
this.
|
|
93
|
+
const node = this.map.get(key);
|
|
94
|
+
if (!node)
|
|
95
|
+
return;
|
|
96
|
+
this.removeNode(node);
|
|
97
|
+
this.map.delete(key);
|
|
70
98
|
}
|
|
71
99
|
has(key) {
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
const entry = this.entries.get(key);
|
|
75
|
-
if (!entry)
|
|
100
|
+
const node = this.map.get(key);
|
|
101
|
+
if (!node)
|
|
76
102
|
return false;
|
|
77
|
-
if (
|
|
78
|
-
this.
|
|
103
|
+
if (node.expiresAt !== null && Date.now() > node.expiresAt) {
|
|
104
|
+
this.removeNode(node);
|
|
105
|
+
this.map.delete(key);
|
|
79
106
|
return false;
|
|
80
107
|
}
|
|
81
108
|
return true;
|
|
82
109
|
}
|
|
83
110
|
clear() {
|
|
84
|
-
this.
|
|
111
|
+
this.map.clear();
|
|
112
|
+
this.head = undefined;
|
|
113
|
+
this.tail = undefined;
|
|
85
114
|
}
|
|
86
|
-
/**
|
|
87
|
-
* Return the count of live (non-expired) entries.
|
|
88
|
-
*
|
|
89
|
-
* Pure query — does NOT touch LRU order. Expired entries discovered during
|
|
90
|
-
* the scan are evicted opportunistically (they were already invisible to
|
|
91
|
-
* `get()`, so eviction has no observable effect beyond memory reclamation).
|
|
92
|
-
*
|
|
93
|
-
* Two-pass to avoid mutating the Map during iteration (spec-safe).
|
|
94
|
-
*/
|
|
95
115
|
size() {
|
|
96
|
-
|
|
97
|
-
const expired = [];
|
|
98
|
-
let count = 0;
|
|
99
|
-
for (const [key, entry] of this.entries) {
|
|
100
|
-
if (entry.expiresAt !== null && entry.expiresAt <= now) {
|
|
101
|
-
expired.push(key);
|
|
102
|
-
}
|
|
103
|
-
else {
|
|
104
|
-
count++;
|
|
105
|
-
}
|
|
106
|
-
}
|
|
107
|
-
for (const key of expired)
|
|
108
|
-
this.entries.delete(key);
|
|
109
|
-
return count;
|
|
116
|
+
return this.map.size;
|
|
110
117
|
}
|
|
111
|
-
/**
|
|
112
|
-
* Stop the background cleanup timer and release internal state.
|
|
113
|
-
* Safe to call multiple times — subsequent calls are no-ops.
|
|
114
|
-
*/
|
|
115
118
|
destroy() {
|
|
116
119
|
if (this.cleanupTimer !== undefined) {
|
|
117
120
|
clearInterval(this.cleanupTimer);
|
|
118
121
|
this.cleanupTimer = undefined;
|
|
122
|
+
InMemoryStore.finalizer?.unregister(this);
|
|
123
|
+
}
|
|
124
|
+
if (this.memoryListener) {
|
|
125
|
+
const processOff = process.off;
|
|
126
|
+
if (typeof processOff === 'function') {
|
|
127
|
+
processOff.call(process, 'memory', this.memoryListener);
|
|
128
|
+
}
|
|
129
|
+
this.memoryListener = undefined;
|
|
119
130
|
}
|
|
131
|
+
this.map.clear();
|
|
132
|
+
this.head = undefined;
|
|
133
|
+
this.tail = undefined;
|
|
120
134
|
}
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
135
|
+
appendTail(node) {
|
|
136
|
+
if (this.tail) {
|
|
137
|
+
this.tail.next = node;
|
|
138
|
+
node.prev = this.tail;
|
|
139
|
+
node.next = undefined;
|
|
140
|
+
}
|
|
141
|
+
else {
|
|
142
|
+
this.head = node;
|
|
143
|
+
}
|
|
144
|
+
this.tail = node;
|
|
145
|
+
}
|
|
146
|
+
removeNode(node) {
|
|
147
|
+
if (node.prev) {
|
|
148
|
+
node.prev.next = node.next;
|
|
149
|
+
}
|
|
150
|
+
else {
|
|
151
|
+
this.head = node.next;
|
|
152
|
+
}
|
|
153
|
+
if (node.next) {
|
|
154
|
+
node.next.prev = node.prev;
|
|
155
|
+
}
|
|
156
|
+
else {
|
|
157
|
+
this.tail = node.prev;
|
|
158
|
+
}
|
|
159
|
+
node.prev = undefined;
|
|
160
|
+
node.next = undefined;
|
|
161
|
+
}
|
|
162
|
+
moveToTail(node) {
|
|
163
|
+
if (this.tail === node)
|
|
164
|
+
return;
|
|
165
|
+
this.removeNode(node);
|
|
166
|
+
this.appendTail(node);
|
|
167
|
+
}
|
|
168
|
+
sweep() {
|
|
169
|
+
try {
|
|
170
|
+
const now = Date.now();
|
|
171
|
+
const expired = [];
|
|
172
|
+
for (const [key, node] of this.map) {
|
|
173
|
+
if (node.expiresAt !== null && now > node.expiresAt) {
|
|
174
|
+
expired.push(key);
|
|
175
|
+
}
|
|
131
176
|
}
|
|
177
|
+
for (const key of expired) {
|
|
178
|
+
const node = this.map.get(key);
|
|
179
|
+
if (node) {
|
|
180
|
+
this.removeNode(node);
|
|
181
|
+
this.map.delete(key);
|
|
182
|
+
}
|
|
183
|
+
}
|
|
184
|
+
}
|
|
185
|
+
catch {
|
|
132
186
|
}
|
|
133
|
-
for (const key of expired)
|
|
134
|
-
this.entries.delete(key);
|
|
135
187
|
}
|
|
136
188
|
}
|
|
137
|
-
|
|
189
|
+
export function createDefaultStore() {
|
|
190
|
+
return new InMemoryStore({
|
|
191
|
+
maxSize: LIMITS.DEFAULT_STORE_MAX_SIZE,
|
|
192
|
+
autoCleanup: true,
|
|
193
|
+
cleanupIntervalMs: LIMITS.DEFAULT_STORE_CLEANUP_INTERVAL_MS,
|
|
194
|
+
});
|
|
195
|
+
}
|
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.waitForObsHook = waitForObsHook;
|
|
4
|
+
exports.isActlyEventType = isActlyEventType;
|
|
5
|
+
function waitForObsHook(hooks, hookName, timeoutMs = 5000) {
|
|
6
|
+
let resolveFn;
|
|
7
|
+
let rejectFn;
|
|
8
|
+
const promise = new Promise((resolve, reject) => {
|
|
9
|
+
resolveFn = resolve;
|
|
10
|
+
rejectFn = reject;
|
|
11
|
+
});
|
|
12
|
+
let settled = false;
|
|
13
|
+
const timer = setTimeout(() => {
|
|
14
|
+
if (settled)
|
|
15
|
+
return;
|
|
16
|
+
settled = true;
|
|
17
|
+
hooks[hookName] = previousHook;
|
|
18
|
+
rejectFn(new Error(`Actly: waitForObsHook timed out after ${timeoutMs}ms waiting for "${String(hookName)}".`));
|
|
19
|
+
}, timeoutMs);
|
|
20
|
+
const unrefable = timer;
|
|
21
|
+
if (typeof unrefable.unref === 'function')
|
|
22
|
+
unrefable.unref();
|
|
23
|
+
const previousHook = hooks[hookName];
|
|
24
|
+
const cleanup = () => {
|
|
25
|
+
if (settled)
|
|
26
|
+
return;
|
|
27
|
+
settled = true;
|
|
28
|
+
clearTimeout(timer);
|
|
29
|
+
hooks[hookName] = previousHook;
|
|
30
|
+
};
|
|
31
|
+
hooks[hookName] = (event) => {
|
|
32
|
+
if (typeof previousHook === 'function') {
|
|
33
|
+
try {
|
|
34
|
+
previousHook(event);
|
|
35
|
+
}
|
|
36
|
+
catch { }
|
|
37
|
+
}
|
|
38
|
+
if (settled)
|
|
39
|
+
return;
|
|
40
|
+
cleanup();
|
|
41
|
+
resolveFn(event);
|
|
42
|
+
};
|
|
43
|
+
return Object.assign(promise, { cancel: cleanup });
|
|
44
|
+
}
|
|
45
|
+
function isActlyEventType(value) {
|
|
46
|
+
return (value === 'attempt' ||
|
|
47
|
+
value === 'retry' ||
|
|
48
|
+
value === 'cache-hit' ||
|
|
49
|
+
value === 'cache-miss' ||
|
|
50
|
+
value === 'dedupe-join' ||
|
|
51
|
+
value === 'timeout' ||
|
|
52
|
+
value === 'final-success' ||
|
|
53
|
+
value === 'final-failure' ||
|
|
54
|
+
value === 'backpressure' ||
|
|
55
|
+
value === 'watchdog');
|
|
56
|
+
}
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
import type { ObservabilityHooks, ActlyEventType } from '../observability.js';
|
|
2
|
+
export declare function waitForObsHook<K extends keyof ObservabilityHooks>(hooks: ObservabilityHooks, hookName: K, timeoutMs?: number): Promise<Parameters<NonNullable<ObservabilityHooks[K]>>[0]> & {
|
|
3
|
+
cancel: () => void;
|
|
4
|
+
};
|
|
5
|
+
export declare function isActlyEventType(value: unknown): value is ActlyEventType;
|
|
6
|
+
//# sourceMappingURL=index.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/testing/index.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,kBAAkB,EAAE,cAAc,EAAE,MAAM,qBAAqB,CAAA;AAyB7E,wBAAgB,cAAc,CAAC,CAAC,SAAS,MAAM,kBAAkB,EAC/D,KAAK,EAAE,kBAAkB,EACzB,QAAQ,EAAE,CAAC,EACX,SAAS,SAAO,GACf,OAAO,CAAC,UAAU,CAAC,WAAW,CAAC,kBAAkB,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,GAAG;IAAE,MAAM,EAAE,MAAM,IAAI,CAAA;CAAE,CA+CrF;AAMD,wBAAgB,gBAAgB,CAAC,KAAK,EAAE,OAAO,GAAG,KAAK,IAAI,cAAc,CAaxE"}
|
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
export function waitForObsHook(hooks, hookName, timeoutMs = 5000) {
|
|
2
|
+
let resolveFn;
|
|
3
|
+
let rejectFn;
|
|
4
|
+
const promise = new Promise((resolve, reject) => {
|
|
5
|
+
resolveFn = resolve;
|
|
6
|
+
rejectFn = reject;
|
|
7
|
+
});
|
|
8
|
+
let settled = false;
|
|
9
|
+
const timer = setTimeout(() => {
|
|
10
|
+
if (settled)
|
|
11
|
+
return;
|
|
12
|
+
settled = true;
|
|
13
|
+
hooks[hookName] = previousHook;
|
|
14
|
+
rejectFn(new Error(`Actly: waitForObsHook timed out after ${timeoutMs}ms waiting for "${String(hookName)}".`));
|
|
15
|
+
}, timeoutMs);
|
|
16
|
+
const unrefable = timer;
|
|
17
|
+
if (typeof unrefable.unref === 'function')
|
|
18
|
+
unrefable.unref();
|
|
19
|
+
const previousHook = hooks[hookName];
|
|
20
|
+
const cleanup = () => {
|
|
21
|
+
if (settled)
|
|
22
|
+
return;
|
|
23
|
+
settled = true;
|
|
24
|
+
clearTimeout(timer);
|
|
25
|
+
hooks[hookName] = previousHook;
|
|
26
|
+
};
|
|
27
|
+
hooks[hookName] = (event) => {
|
|
28
|
+
if (typeof previousHook === 'function') {
|
|
29
|
+
try {
|
|
30
|
+
previousHook(event);
|
|
31
|
+
}
|
|
32
|
+
catch { }
|
|
33
|
+
}
|
|
34
|
+
if (settled)
|
|
35
|
+
return;
|
|
36
|
+
cleanup();
|
|
37
|
+
resolveFn(event);
|
|
38
|
+
};
|
|
39
|
+
return Object.assign(promise, { cancel: cleanup });
|
|
40
|
+
}
|
|
41
|
+
export function isActlyEventType(value) {
|
|
42
|
+
return (value === 'attempt' ||
|
|
43
|
+
value === 'retry' ||
|
|
44
|
+
value === 'cache-hit' ||
|
|
45
|
+
value === 'cache-miss' ||
|
|
46
|
+
value === 'dedupe-join' ||
|
|
47
|
+
value === 'timeout' ||
|
|
48
|
+
value === 'final-success' ||
|
|
49
|
+
value === 'final-failure' ||
|
|
50
|
+
value === 'backpressure' ||
|
|
51
|
+
value === 'watchdog');
|
|
52
|
+
}
|
package/dist/types/index.cjs
CHANGED