@periodic/vanadium 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/CHANGELOG.md +37 -0
- package/LICENSE +21 -0
- package/README.md +846 -0
- package/dist/cjs/adapters/memory/index.js +134 -0
- package/dist/cjs/adapters/memory/index.js.map +1 -0
- package/dist/cjs/adapters/mongodb/index.js +189 -0
- package/dist/cjs/adapters/mongodb/index.js.map +1 -0
- package/dist/cjs/adapters/mongoose/index.js +199 -0
- package/dist/cjs/adapters/mongoose/index.js.map +1 -0
- package/dist/cjs/adapters/postgres/index.js +202 -0
- package/dist/cjs/adapters/postgres/index.js.map +1 -0
- package/dist/cjs/adapters/prisma/index.js +176 -0
- package/dist/cjs/adapters/prisma/index.js.map +1 -0
- package/dist/cjs/adapters/redis/index.js +178 -0
- package/dist/cjs/adapters/redis/index.js.map +1 -0
- package/dist/cjs/cleanup/engine.js +100 -0
- package/dist/cjs/cleanup/engine.js.map +1 -0
- package/dist/cjs/core/concurrencyGuard.js +50 -0
- package/dist/cjs/core/concurrencyGuard.js.map +1 -0
- package/dist/cjs/core/metrics.js +39 -0
- package/dist/cjs/core/metrics.js.map +1 -0
- package/dist/cjs/core/stateMachine.js +46 -0
- package/dist/cjs/core/stateMachine.js.map +1 -0
- package/dist/cjs/errors/index.js +127 -0
- package/dist/cjs/errors/index.js.map +1 -0
- package/dist/cjs/http/express.js +84 -0
- package/dist/cjs/http/express.js.map +1 -0
- package/dist/cjs/http/fastify.js +70 -0
- package/dist/cjs/http/fastify.js.map +1 -0
- package/dist/cjs/idempotency/engine.js +266 -0
- package/dist/cjs/idempotency/engine.js.map +1 -0
- package/dist/cjs/index.js +19 -0
- package/dist/cjs/index.js.map +1 -0
- package/dist/cjs/lock/engine.js +187 -0
- package/dist/cjs/lock/engine.js.map +1 -0
- package/dist/cjs/observability/metrics.js +92 -0
- package/dist/cjs/observability/metrics.js.map +1 -0
- package/dist/cjs/resilience/circuitBreaker.js +129 -0
- package/dist/cjs/resilience/circuitBreaker.js.map +1 -0
- package/dist/cjs/types/index.js +13 -0
- package/dist/cjs/types/index.js.map +1 -0
- package/dist/cjs/utils/crypto.js +64 -0
- package/dist/cjs/utils/crypto.js.map +1 -0
- package/dist/cjs/utils/keys.js +40 -0
- package/dist/cjs/utils/keys.js.map +1 -0
- package/dist/cjs/utils/sleep.js +25 -0
- package/dist/cjs/utils/sleep.js.map +1 -0
- package/dist/esm/adapters/memory/index.js +129 -0
- package/dist/esm/adapters/memory/index.js.map +1 -0
- package/dist/esm/adapters/mongodb/index.js +184 -0
- package/dist/esm/adapters/mongodb/index.js.map +1 -0
- package/dist/esm/adapters/mongoose/index.js +193 -0
- package/dist/esm/adapters/mongoose/index.js.map +1 -0
- package/dist/esm/adapters/postgres/index.js +197 -0
- package/dist/esm/adapters/postgres/index.js.map +1 -0
- package/dist/esm/adapters/prisma/index.js +171 -0
- package/dist/esm/adapters/prisma/index.js.map +1 -0
- package/dist/esm/adapters/redis/index.js +173 -0
- package/dist/esm/adapters/redis/index.js.map +1 -0
- package/dist/esm/cleanup/engine.js +95 -0
- package/dist/esm/cleanup/engine.js.map +1 -0
- package/dist/esm/core/concurrencyGuard.js +46 -0
- package/dist/esm/core/concurrencyGuard.js.map +1 -0
- package/dist/esm/core/metrics.js +35 -0
- package/dist/esm/core/metrics.js.map +1 -0
- package/dist/esm/core/stateMachine.js +40 -0
- package/dist/esm/core/stateMachine.js.map +1 -0
- package/dist/esm/errors/index.js +114 -0
- package/dist/esm/errors/index.js.map +1 -0
- package/dist/esm/http/express.js +81 -0
- package/dist/esm/http/express.js.map +1 -0
- package/dist/esm/http/fastify.js +67 -0
- package/dist/esm/http/fastify.js.map +1 -0
- package/dist/esm/idempotency/engine.js +261 -0
- package/dist/esm/idempotency/engine.js.map +1 -0
- package/dist/esm/index.js +9 -0
- package/dist/esm/index.js.map +1 -0
- package/dist/esm/lock/engine.js +182 -0
- package/dist/esm/lock/engine.js.map +1 -0
- package/dist/esm/observability/metrics.js +89 -0
- package/dist/esm/observability/metrics.js.map +1 -0
- package/dist/esm/resilience/circuitBreaker.js +124 -0
- package/dist/esm/resilience/circuitBreaker.js.map +1 -0
- package/dist/esm/types/index.js +10 -0
- package/dist/esm/types/index.js.map +1 -0
- package/dist/esm/utils/crypto.js +58 -0
- package/dist/esm/utils/crypto.js.map +1 -0
- package/dist/esm/utils/keys.js +35 -0
- package/dist/esm/utils/keys.js.map +1 -0
- package/dist/esm/utils/sleep.js +20 -0
- package/dist/esm/utils/sleep.js.map +1 -0
- package/dist/types/adapters/memory/index.d.ts +49 -0
- package/dist/types/adapters/memory/index.d.ts.map +1 -0
- package/dist/types/adapters/mongodb/index.d.ts +97 -0
- package/dist/types/adapters/mongodb/index.d.ts.map +1 -0
- package/dist/types/adapters/mongoose/index.d.ts +107 -0
- package/dist/types/adapters/mongoose/index.d.ts.map +1 -0
- package/dist/types/adapters/postgres/index.d.ts +85 -0
- package/dist/types/adapters/postgres/index.d.ts.map +1 -0
- package/dist/types/adapters/prisma/index.d.ts +73 -0
- package/dist/types/adapters/prisma/index.d.ts.map +1 -0
- package/dist/types/adapters/redis/index.d.ts +77 -0
- package/dist/types/adapters/redis/index.d.ts.map +1 -0
- package/dist/types/cleanup/engine.d.ts +41 -0
- package/dist/types/cleanup/engine.d.ts.map +1 -0
- package/dist/types/core/concurrencyGuard.d.ts +28 -0
- package/dist/types/core/concurrencyGuard.d.ts.map +1 -0
- package/dist/types/core/metrics.d.ts +13 -0
- package/dist/types/core/metrics.d.ts.map +1 -0
- package/dist/types/core/stateMachine.d.ts +20 -0
- package/dist/types/core/stateMachine.d.ts.map +1 -0
- package/dist/types/errors/index.d.ts +32 -0
- package/dist/types/errors/index.d.ts.map +1 -0
- package/dist/types/http/express.d.ts +50 -0
- package/dist/types/http/express.d.ts.map +1 -0
- package/dist/types/http/fastify.d.ts +48 -0
- package/dist/types/http/fastify.d.ts.map +1 -0
- package/dist/types/idempotency/engine.d.ts +24 -0
- package/dist/types/idempotency/engine.d.ts.map +1 -0
- package/dist/types/index.d.ts +8 -0
- package/dist/types/index.d.ts.map +1 -0
- package/dist/types/lock/engine.d.ts +28 -0
- package/dist/types/lock/engine.d.ts.map +1 -0
- package/dist/types/observability/metrics.d.ts +45 -0
- package/dist/types/observability/metrics.d.ts.map +1 -0
- package/dist/types/resilience/circuitBreaker.d.ts +48 -0
- package/dist/types/resilience/circuitBreaker.d.ts.map +1 -0
- package/dist/types/types/index.d.ts +170 -0
- package/dist/types/types/index.d.ts.map +1 -0
- package/dist/types/utils/crypto.d.ts +20 -0
- package/dist/types/utils/crypto.d.ts.map +1 -0
- package/dist/types/utils/keys.d.ts +15 -0
- package/dist/types/utils/keys.d.ts.map +1 -0
- package/dist/types/utils/sleep.d.ts +13 -0
- package/dist/types/utils/sleep.d.ts.map +1 -0
- package/package.json +140 -0
|
@@ -0,0 +1,124 @@
|
|
|
1
|
+
import { createStorageError } from '../errors/index.js';
|
|
2
|
+
import { sleep } from '../utils/sleep.js';
|
|
3
|
+
const DEFAULT_FAILURE_THRESHOLD = 5;
|
|
4
|
+
const DEFAULT_RESET_TIMEOUT_MS = 30000;
|
|
5
|
+
const DEFAULT_HALF_OPEN_MAX_CALLS = 1;
|
|
6
|
+
// ─── Circuit Breaker ──────────────────────────────────────────────────────────
|
|
7
|
+
/**
|
|
8
|
+
* Wraps a StorageAdapter with a circuit breaker pattern.
|
|
9
|
+
* Protects the application from repeated storage failures.
|
|
10
|
+
*
|
|
11
|
+
* States:
|
|
12
|
+
* - CLOSED: Normal operation. Failures counted.
|
|
13
|
+
* - OPEN: Storage blocked. All calls throw immediately.
|
|
14
|
+
* - HALF_OPEN: Probe mode. Limited calls allowed to test recovery.
|
|
15
|
+
*/
|
|
16
|
+
export class CircuitBreakerAdapter {
|
|
17
|
+
constructor(adapter, options = {}, clock = Date.now) {
|
|
18
|
+
this.state = 'CLOSED';
|
|
19
|
+
this.failureCount = 0;
|
|
20
|
+
this.successCount = 0;
|
|
21
|
+
this.halfOpenCalls = 0;
|
|
22
|
+
this.adapter = adapter;
|
|
23
|
+
this.name = `circuit-breaker(${adapter.name})`;
|
|
24
|
+
this.failureThreshold = options.failureThreshold ?? DEFAULT_FAILURE_THRESHOLD;
|
|
25
|
+
this.resetTimeoutMs = options.resetTimeoutMs ?? DEFAULT_RESET_TIMEOUT_MS;
|
|
26
|
+
this.halfOpenMaxCalls = options.halfOpenMaxCalls ?? DEFAULT_HALF_OPEN_MAX_CALLS;
|
|
27
|
+
this.clock = clock;
|
|
28
|
+
this.lastFailureAt = clock();
|
|
29
|
+
}
|
|
30
|
+
// ── StorageAdapter implementation ─────────────────────────────────────────
|
|
31
|
+
async get(key) {
|
|
32
|
+
return this._execute(() => this.adapter.get(key), key);
|
|
33
|
+
}
|
|
34
|
+
async set(key, value, ttlMs) {
|
|
35
|
+
return this._execute(() => this.adapter.set(key, value, ttlMs), key);
|
|
36
|
+
}
|
|
37
|
+
async delete(key) {
|
|
38
|
+
return this._execute(() => this.adapter.delete(key), key);
|
|
39
|
+
}
|
|
40
|
+
async compareAndSet(key, expectedOwnerToken, newValue, ttlMs) {
|
|
41
|
+
if (!this.adapter.compareAndSet) {
|
|
42
|
+
throw createStorageError(key, this.name, new Error('Adapter does not support compareAndSet'), this.clock);
|
|
43
|
+
}
|
|
44
|
+
return this._execute(() => this.adapter.compareAndSet(key, expectedOwnerToken, newValue, ttlMs), key);
|
|
45
|
+
}
|
|
46
|
+
// ── State Inspection ──────────────────────────────────────────────────────
|
|
47
|
+
getState() {
|
|
48
|
+
this._checkReset();
|
|
49
|
+
return this.state;
|
|
50
|
+
}
|
|
51
|
+
reset() {
|
|
52
|
+
this.state = 'CLOSED';
|
|
53
|
+
this.failureCount = 0;
|
|
54
|
+
this.successCount = 0;
|
|
55
|
+
this.halfOpenCalls = 0;
|
|
56
|
+
}
|
|
57
|
+
// ── Private ───────────────────────────────────────────────────────────────
|
|
58
|
+
async _execute(fn, key) {
|
|
59
|
+
this._checkReset();
|
|
60
|
+
if (this.state === 'OPEN') {
|
|
61
|
+
throw createStorageError(key, this.name, new Error(`Circuit breaker is OPEN for adapter "${this.adapter.name}". Storage calls blocked.`), this.clock);
|
|
62
|
+
}
|
|
63
|
+
if (this.state === 'HALF_OPEN' && this.halfOpenCalls >= this.halfOpenMaxCalls) {
|
|
64
|
+
await sleep(100); // brief backoff
|
|
65
|
+
throw createStorageError(key, this.name, new Error('Circuit breaker is HALF_OPEN. Probe call limit reached.'), this.clock);
|
|
66
|
+
}
|
|
67
|
+
if (this.state === 'HALF_OPEN') {
|
|
68
|
+
this.halfOpenCalls++;
|
|
69
|
+
}
|
|
70
|
+
try {
|
|
71
|
+
const result = await fn();
|
|
72
|
+
this._onSuccess();
|
|
73
|
+
return result;
|
|
74
|
+
}
|
|
75
|
+
catch (err) {
|
|
76
|
+
this._onFailure();
|
|
77
|
+
throw err;
|
|
78
|
+
}
|
|
79
|
+
}
|
|
80
|
+
_onSuccess() {
|
|
81
|
+
this.failureCount = 0;
|
|
82
|
+
this.successCount++;
|
|
83
|
+
if (this.state === 'HALF_OPEN') {
|
|
84
|
+
this.state = 'CLOSED';
|
|
85
|
+
this.halfOpenCalls = 0;
|
|
86
|
+
}
|
|
87
|
+
}
|
|
88
|
+
_onFailure() {
|
|
89
|
+
this.failureCount++;
|
|
90
|
+
this.lastFailureAt = this.clock();
|
|
91
|
+
if (this.state === 'HALF_OPEN') {
|
|
92
|
+
this.state = 'OPEN';
|
|
93
|
+
this.halfOpenCalls = 0;
|
|
94
|
+
}
|
|
95
|
+
else if (this.failureCount >= this.failureThreshold) {
|
|
96
|
+
this.state = 'OPEN';
|
|
97
|
+
}
|
|
98
|
+
}
|
|
99
|
+
_checkReset() {
|
|
100
|
+
if (this.state === 'OPEN') {
|
|
101
|
+
const elapsed = this.clock() - this.lastFailureAt;
|
|
102
|
+
if (elapsed >= this.resetTimeoutMs) {
|
|
103
|
+
this.state = 'HALF_OPEN';
|
|
104
|
+
this.halfOpenCalls = 0;
|
|
105
|
+
}
|
|
106
|
+
}
|
|
107
|
+
}
|
|
108
|
+
}
|
|
109
|
+
// ─── Factory ──────────────────────────────────────────────────────────────────
|
|
110
|
+
/**
|
|
111
|
+
* Wrap any StorageAdapter with a circuit breaker.
|
|
112
|
+
*
|
|
113
|
+
* @example
|
|
114
|
+
* ```ts
|
|
115
|
+
* const protectedAdapter = createCircuitBreaker(redisAdapter, {
|
|
116
|
+
* failureThreshold: 3,
|
|
117
|
+
* resetTimeoutMs: 15_000,
|
|
118
|
+
* });
|
|
119
|
+
* ```
|
|
120
|
+
*/
|
|
121
|
+
export function createCircuitBreaker(adapter, options, clock) {
|
|
122
|
+
return new CircuitBreakerAdapter(adapter, options, clock);
|
|
123
|
+
}
|
|
124
|
+
//# sourceMappingURL=circuitBreaker.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"circuitBreaker.js","sourceRoot":"","sources":["../../../src/resilience/circuitBreaker.ts"],"names":[],"mappings":"AAEA,OAAO,EAAE,kBAAkB,EAAE,MAAM,oBAAoB,CAAC;AACxD,OAAO,EAAE,KAAK,EAAE,MAAM,mBAAmB,CAAC;AAE1C,MAAM,yBAAyB,GAAG,CAAC,CAAC;AACpC,MAAM,wBAAwB,GAAG,KAAM,CAAC;AACxC,MAAM,2BAA2B,GAAG,CAAC,CAAC;AAEtC,iFAAiF;AAEjF;;;;;;;;GAQG;AACH,MAAM,OAAO,qBAAqB;IAehC,YACE,OAAuB,EACvB,UAAiC,EAAE,EACnC,QAAsB,IAAI,CAAC,GAAG;QAfxB,UAAK,GAAwB,QAAQ,CAAC;QACtC,iBAAY,GAAG,CAAC,CAAC;QACjB,iBAAY,GAAG,CAAC,CAAC;QAEjB,kBAAa,GAAG,CAAC,CAAC;QAaxB,IAAI,CAAC,OAAO,GAAG,OAAO,CAAC;QACvB,IAAI,CAAC,IAAI,GAAG,mBAAmB,OAAO,CAAC,IAAI,GAAG,CAAC;QAC/C,IAAI,CAAC,gBAAgB,GAAG,OAAO,CAAC,gBAAgB,IAAI,yBAAyB,CAAC;QAC9E,IAAI,CAAC,cAAc,GAAG,OAAO,CAAC,cAAc,IAAI,wBAAwB,CAAC;QACzE,IAAI,CAAC,gBAAgB,GAAG,OAAO,CAAC,gBAAgB,IAAI,2BAA2B,CAAC;QAChF,IAAI,CAAC,KAAK,GAAG,KAAK,CAAC;QACnB,IAAI,CAAC,aAAa,GAAG,KAAK,EAAE,CAAC;IAC/B,CAAC;IAED,6EAA6E;IAE7E,KAAK,CAAC,GAAG,CAAc,GAAW;QAChC,OAAO,IAAI,CAAC,QAAQ,CAAC,GAAG,EAAE,CAAC,IAAI,CAAC,OAAO,CAAC,GAAG,CAAI,GAAG,CAAC,EAAE,GAAG,CAAC,CAAC;IAC5D,CAAC;IAED,KAAK,CAAC,GAAG,CAAc,GAAW,EAAE,KAAsB,EAAE,KAAc;QACxE,OAAO,IAAI,CAAC,QAAQ,CAAC,GAAG,EAAE,CAAC,IAAI,CAAC,OAAO,CAAC,GAAG,CAAI,GAAG,EAAE,KAAK,EAAE,KAAK,CAAC,EAAE,GAAG,CAAC,CAAC;IAC1E,CAAC;IAED,KAAK,CAAC,MAAM,CAAC,GAAW;QACtB,OAAO,IAAI,CAAC,QAAQ,CAAC,GAAG,EAAE,CAAC,IAAI,CAAC,OAAO,CAAC,MAAM,CAAC,GAAG,CAAC,EAAE,GAAG,CAAC,CAAC;IAC5D,CAAC;IAED,KAAK,CAAC,aAAa,CACjB,GAAW,EACX,kBAA0B,EAC1B,QAAyB,EACzB,KAAc;QAEd,IAAI,CAAC,IAAI,CAAC,OAAO,CAAC,aAAa,EAAE,CAAC;YAChC,MAAM,kBAAkB,CACtB,GAAG,EACH,IAAI,CAAC,IAAI,EACT,IAAI,KAAK,CAAC,wCAAwC,CAAC,EACnD,IAAI,CAAC,KAAK,CACX,CAAC;QACJ,CAAC;QACD,OAAO,IAAI,CAAC,QAAQ,CAClB,GAAG,EAAE,CAAC,IAAI,CAAC,OAAO,CAAC,aAAc,CAAC,GAAG,EAAE,kBAAkB,EAAE,QAAQ,EAAE,KAAK,CAAC,EAC3E,GAAG,CACJ,CAAC;IACJ,CAAC;IAED,6EAA6E;IAE7E,QAAQ;QACN,IAAI,CAAC,WAAW,EAAE,CAAC;QACnB,OAAO,IAAI,CAAC,KAAK,CAAC;IACpB,CAAC;IAED,KAAK;QACH,IAAI,CAAC,KAAK,GAAG,QAAQ,CAAC;QACtB,IAAI,CAAC,YAAY,GAAG,CAAC,CAAC;QACtB,IAAI,CAAC,YAAY,GAAG,CAAC,CAAC;QACtB,IAAI,CAAC,aAAa,GAAG,CAAC,CAAC;IACzB,CAAC;IAED,6EAA6E;IAErE,KAAK,CAAC,QAAQ,CAAI,EAAoB,EAAE,GAAW;QACzD,IAAI,CAAC,WAAW,EAAE,CAAC;QAEnB,IAAI,IAAI,CAAC,KAAK,KAAK,MAAM,EAAE,CAAC;YAC1B,MAAM,kBAAkB,CACtB,GAAG,EACH,IAAI,CAAC,IAAI,EACT,IAAI,KAAK,CACP,wCAAwC,IAAI,CAAC,OAAO,CAAC,IAAI,2BAA2B,CACrF,EACD,IAAI,CAAC,KAAK,CACX,CAAC;QACJ,CAAC;QAED,IAAI,IAAI,CAAC,KAAK,KAAK,WAAW,IAAI,IAAI,CAAC,aAAa,IAAI,IAAI,CAAC,gBAAgB,EAAE,CAAC;YAC9E,MAAM,KAAK,CAAC,GAAG,CAAC,CAAC,CAAC,gBAAgB;YAClC,MAAM,kBAAkB,CACtB,GAAG,EACH,IAAI,CAAC,IAAI,EACT,IAAI,KAAK,CAAC,yDAAyD,CAAC,EACpE,IAAI,CAAC,KAAK,CACX,CAAC;QACJ,CAAC;QAED,IAAI,IAAI,CAAC,KAAK,KAAK,WAAW,EAAE,CAAC;YAC/B,IAAI,CAAC,aAAa,EAAE,CAAC;QACvB,CAAC;QAED,IAAI,CAAC;YACH,MAAM,MAAM,GAAG,MAAM,EAAE,EAAE,CAAC;YAC1B,IAAI,CAAC,UAAU,EAAE,CAAC;YAClB,OAAO,MAAM,CAAC;QAChB,CAAC;QAAC,OAAO,GAAG,EAAE,CAAC;YACb,IAAI,CAAC,UAAU,EAAE,CAAC;YAClB,MAAM,GAAG,CAAC;QACZ,CAAC;IACH,CAAC;IAEO,UAAU;QAChB,IAAI,CAAC,YAAY,GAAG,CAAC,CAAC;QACtB,IAAI,CAAC,YAAY,EAAE,CAAC;QACpB,IAAI,IAAI,CAAC,KAAK,KAAK,WAAW,EAAE,CAAC;YAC/B,IAAI,CAAC,KAAK,GAAG,QAAQ,CAAC;YACtB,IAAI,CAAC,aAAa,GAAG,CAAC,CAAC;QACzB,CAAC;IACH,CAAC;IAEO,UAAU;QAChB,IAAI,CAAC,YAAY,EAAE,CAAC;QACpB,IAAI,CAAC,aAAa,GAAG,IAAI,CAAC,KAAK,EAAE,CAAC;QAClC,IAAI,IAAI,CAAC,KAAK,KAAK,WAAW,EAAE,CAAC;YAC/B,IAAI,CAAC,KAAK,GAAG,MAAM,CAAC;YACpB,IAAI,CAAC,aAAa,GAAG,CAAC,CAAC;QACzB,CAAC;aAAM,IAAI,IAAI,CAAC,YAAY,IAAI,IAAI,CAAC,gBAAgB,EAAE,CAAC;YACtD,IAAI,CAAC,KAAK,GAAG,MAAM,CAAC;QACtB,CAAC;IACH,CAAC;IAEO,WAAW;QACjB,IAAI,IAAI,CAAC,KAAK,KAAK,MAAM,EAAE,CAAC;YAC1B,MAAM,OAAO,GAAG,IAAI,CAAC,KAAK,EAAE,GAAG,IAAI,CAAC,aAAa,CAAC;YAClD,IAAI,OAAO,IAAI,IAAI,CAAC,cAAc,EAAE,CAAC;gBACnC,IAAI,CAAC,KAAK,GAAG,WAAW,CAAC;gBACzB,IAAI,CAAC,aAAa,GAAG,CAAC,CAAC;YACzB,CAAC;QACH,CAAC;IACH,CAAC;CACF;AAED,iFAAiF;AAEjF;;;;;;;;;;GAUG;AACH,MAAM,UAAU,oBAAoB,CAClC,OAAuB,EACvB,OAA+B,EAC/B,KAAoB;IAEpB,OAAO,IAAI,qBAAqB,CAAC,OAAO,EAAE,OAAO,EAAE,KAAK,CAAC,CAAC;AAC5D,CAAC"}
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @periodic/vanadium — Core Type Definitions
|
|
3
|
+
* Deterministic idempotency and distributed lock engine
|
|
4
|
+
*/
|
|
5
|
+
export const VALID_TRANSITIONS = [
|
|
6
|
+
'IN_PROGRESS -> COMPLETED',
|
|
7
|
+
'IN_PROGRESS -> FAILED',
|
|
8
|
+
'IN_PROGRESS -> IN_PROGRESS', // expired takeover
|
|
9
|
+
];
|
|
10
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../../src/types/index.ts"],"names":[],"mappings":"AAAA;;;GAGG;AA2OH,MAAM,CAAC,MAAM,iBAAiB,GAA+B;IAC3D,0BAA0B;IAC1B,uBAAuB;IACvB,4BAA4B,EAAE,mBAAmB;CACzC,CAAC"}
|
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
import { createHash, randomUUID } from 'node:crypto';
|
|
2
|
+
/**
|
|
3
|
+
* Deterministically hash an arbitrary payload using SHA-256.
|
|
4
|
+
* JSON.stringify order is normalized using sorted keys for determinism.
|
|
5
|
+
*/
|
|
6
|
+
export function hashPayload(payload) {
|
|
7
|
+
const normalized = JSON.stringify(sortKeys(payload));
|
|
8
|
+
return createHash('sha256').update(normalized).digest('hex');
|
|
9
|
+
}
|
|
10
|
+
/**
|
|
11
|
+
* Recursively sort object keys for deterministic JSON serialization.
|
|
12
|
+
*/
|
|
13
|
+
function sortKeys(value) {
|
|
14
|
+
if (value === null || typeof value !== 'object') {
|
|
15
|
+
return value;
|
|
16
|
+
}
|
|
17
|
+
if (Array.isArray(value)) {
|
|
18
|
+
return value.map(sortKeys);
|
|
19
|
+
}
|
|
20
|
+
const sorted = {};
|
|
21
|
+
for (const key of Object.keys(value).sort()) {
|
|
22
|
+
sorted[key] = sortKeys(value[key]);
|
|
23
|
+
}
|
|
24
|
+
return sorted;
|
|
25
|
+
}
|
|
26
|
+
/**
|
|
27
|
+
* Generate a cryptographically secure owner token (UUID v4).
|
|
28
|
+
*/
|
|
29
|
+
export function generateOwnerToken() {
|
|
30
|
+
return randomUUID();
|
|
31
|
+
}
|
|
32
|
+
/**
|
|
33
|
+
* Safely serialize a value to JSON string.
|
|
34
|
+
* Throws STORAGE_ERROR compatible message on failure.
|
|
35
|
+
*/
|
|
36
|
+
export function safeJsonStringify(value) {
|
|
37
|
+
try {
|
|
38
|
+
return JSON.stringify(value);
|
|
39
|
+
}
|
|
40
|
+
catch (err) {
|
|
41
|
+
const msg = err instanceof Error ? err.message : String(err);
|
|
42
|
+
throw new Error(`JSON serialization failed: ${msg}`);
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
/**
|
|
46
|
+
* Safely parse a JSON string.
|
|
47
|
+
* Throws STORAGE_ERROR compatible message on failure.
|
|
48
|
+
*/
|
|
49
|
+
export function safeJsonParse(raw, context = 'record') {
|
|
50
|
+
try {
|
|
51
|
+
return JSON.parse(raw);
|
|
52
|
+
}
|
|
53
|
+
catch (err) {
|
|
54
|
+
const msg = err instanceof Error ? err.message : String(err);
|
|
55
|
+
throw new Error(`JSON parse failure in ${context}: ${msg}`);
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
//# sourceMappingURL=crypto.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"crypto.js","sourceRoot":"","sources":["../../../src/utils/crypto.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,UAAU,EAAE,UAAU,EAAE,MAAM,aAAa,CAAC;AAErD;;;GAGG;AACH,MAAM,UAAU,WAAW,CAAC,OAAgB;IAC1C,MAAM,UAAU,GAAG,IAAI,CAAC,SAAS,CAAC,QAAQ,CAAC,OAAO,CAAC,CAAC,CAAC;IACrD,OAAO,UAAU,CAAC,QAAQ,CAAC,CAAC,MAAM,CAAC,UAAU,CAAC,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC;AAC/D,CAAC;AAED;;GAEG;AACH,SAAS,QAAQ,CAAC,KAAc;IAC9B,IAAI,KAAK,KAAK,IAAI,IAAI,OAAO,KAAK,KAAK,QAAQ,EAAE,CAAC;QAChD,OAAO,KAAK,CAAC;IACf,CAAC;IACD,IAAI,KAAK,CAAC,OAAO,CAAC,KAAK,CAAC,EAAE,CAAC;QACzB,OAAO,KAAK,CAAC,GAAG,CAAC,QAAQ,CAAC,CAAC;IAC7B,CAAC;IACD,MAAM,MAAM,GAA4B,EAAE,CAAC;IAC3C,KAAK,MAAM,GAAG,IAAI,MAAM,CAAC,IAAI,CAAC,KAAgC,CAAC,CAAC,IAAI,EAAE,EAAE,CAAC;QACvE,MAAM,CAAC,GAAG,CAAC,GAAG,QAAQ,CAAE,KAAiC,CAAC,GAAG,CAAC,CAAC,CAAC;IAClE,CAAC;IACD,OAAO,MAAM,CAAC;AAChB,CAAC;AAED;;GAEG;AACH,MAAM,UAAU,kBAAkB;IAChC,OAAO,UAAU,EAAE,CAAC;AACtB,CAAC;AAED;;;GAGG;AACH,MAAM,UAAU,iBAAiB,CAAC,KAAc;IAC9C,IAAI,CAAC;QACH,OAAO,IAAI,CAAC,SAAS,CAAC,KAAK,CAAC,CAAC;IAC/B,CAAC;IAAC,OAAO,GAAG,EAAE,CAAC;QACb,MAAM,GAAG,GAAG,GAAG,YAAY,KAAK,CAAC,CAAC,CAAC,GAAG,CAAC,OAAO,CAAC,CAAC,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC;QAC7D,MAAM,IAAI,KAAK,CAAC,8BAA8B,GAAG,EAAE,CAAC,CAAC;IACvD,CAAC;AACH,CAAC;AAED;;;GAGG;AACH,MAAM,UAAU,aAAa,CAAc,GAAW,EAAE,OAAO,GAAG,QAAQ;IACxE,IAAI,CAAC;QACH,OAAO,IAAI,CAAC,KAAK,CAAC,GAAG,CAAM,CAAC;IAC9B,CAAC;IAAC,OAAO,GAAG,EAAE,CAAC;QACb,MAAM,GAAG,GAAG,GAAG,YAAY,KAAK,CAAC,CAAC,CAAC,GAAG,CAAC,OAAO,CAAC,CAAC,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC;QAC7D,MAAM,IAAI,KAAK,CAAC,yBAAyB,OAAO,KAAK,GAAG,EAAE,CAAC,CAAC;IAC9D,CAAC;AACH,CAAC"}
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
import { createConfigurationError } from '../errors/index.js';
|
|
2
|
+
const MAX_KEY_LENGTH = 512;
|
|
3
|
+
const VALID_KEY_PATTERN = /^[\w\-:.@/]+$/;
|
|
4
|
+
/**
|
|
5
|
+
* Validate an idempotency key for safety and format correctness.
|
|
6
|
+
* Throws a VanadiumError (CONFIGURATION_ERROR) on invalid input.
|
|
7
|
+
*/
|
|
8
|
+
export function validateKey(key, adapterName) {
|
|
9
|
+
if (!key || key.trim().length === 0) {
|
|
10
|
+
throw createConfigurationError(key, adapterName, 'Idempotency key must be a non-empty string.');
|
|
11
|
+
}
|
|
12
|
+
if (key.length > MAX_KEY_LENGTH) {
|
|
13
|
+
throw createConfigurationError(key, adapterName, `Idempotency key exceeds maximum length of ${MAX_KEY_LENGTH} characters. Got ${key.length}.`);
|
|
14
|
+
}
|
|
15
|
+
if (!VALID_KEY_PATTERN.test(key)) {
|
|
16
|
+
throw createConfigurationError(key, adapterName, `Idempotency key "${key}" contains invalid characters. ` +
|
|
17
|
+
`Allowed: alphanumeric, hyphens, underscores, colons, dots, @, and forward slashes.`);
|
|
18
|
+
}
|
|
19
|
+
}
|
|
20
|
+
/**
|
|
21
|
+
* Build a namespaced key: prefix + namespace + key.
|
|
22
|
+
* Example: "vanadium:payments:order_123"
|
|
23
|
+
*/
|
|
24
|
+
export function buildNamespacedKey(key, prefix = '', namespace = '') {
|
|
25
|
+
const parts = [prefix, namespace, key].filter(Boolean);
|
|
26
|
+
return parts.join(':');
|
|
27
|
+
}
|
|
28
|
+
/**
|
|
29
|
+
* Assert the key is valid and return it unchanged (for chaining).
|
|
30
|
+
*/
|
|
31
|
+
export function assertValidKey(key, adapterName) {
|
|
32
|
+
validateKey(key, adapterName);
|
|
33
|
+
return key;
|
|
34
|
+
}
|
|
35
|
+
//# sourceMappingURL=keys.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"keys.js","sourceRoot":"","sources":["../../../src/utils/keys.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,wBAAwB,EAAE,MAAM,oBAAoB,CAAC;AAE9D,MAAM,cAAc,GAAG,GAAG,CAAC;AAC3B,MAAM,iBAAiB,GAAG,eAAe,CAAC;AAE1C;;;GAGG;AACH,MAAM,UAAU,WAAW,CAAC,GAAW,EAAE,WAAmB;IAC1D,IAAI,CAAC,GAAG,IAAI,GAAG,CAAC,IAAI,EAAE,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;QACpC,MAAM,wBAAwB,CAAC,GAAG,EAAE,WAAW,EAAE,6CAA6C,CAAC,CAAC;IAClG,CAAC;IACD,IAAI,GAAG,CAAC,MAAM,GAAG,cAAc,EAAE,CAAC;QAChC,MAAM,wBAAwB,CAC5B,GAAG,EACH,WAAW,EACX,6CAA6C,cAAc,oBAAoB,GAAG,CAAC,MAAM,GAAG,CAC7F,CAAC;IACJ,CAAC;IACD,IAAI,CAAC,iBAAiB,CAAC,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC;QACjC,MAAM,wBAAwB,CAC5B,GAAG,EACH,WAAW,EACX,oBAAoB,GAAG,iCAAiC;YACtD,oFAAoF,CACvF,CAAC;IACJ,CAAC;AACH,CAAC;AAED;;;GAGG;AACH,MAAM,UAAU,kBAAkB,CAAC,GAAW,EAAE,MAAM,GAAG,EAAE,EAAE,SAAS,GAAG,EAAE;IACzE,MAAM,KAAK,GAAG,CAAC,MAAM,EAAE,SAAS,EAAE,GAAG,CAAC,CAAC,MAAM,CAAC,OAAO,CAAC,CAAC;IACvD,OAAO,KAAK,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC;AACzB,CAAC;AAED;;GAEG;AACH,MAAM,UAAU,cAAc,CAAC,GAAW,EAAE,WAAmB;IAC7D,WAAW,CAAC,GAAG,EAAE,WAAW,CAAC,CAAC;IAC9B,OAAO,GAAG,CAAC;AACb,CAAC"}
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Non-blocking async sleep.
|
|
3
|
+
*/
|
|
4
|
+
export function sleep(ms) {
|
|
5
|
+
return new Promise((resolve) => setTimeout(resolve, ms));
|
|
6
|
+
}
|
|
7
|
+
/**
|
|
8
|
+
* Add ±jitter to a base delay to prevent thundering herd.
|
|
9
|
+
*/
|
|
10
|
+
export function jitter(baseMs, factor = 0.2) {
|
|
11
|
+
const delta = baseMs * factor;
|
|
12
|
+
return baseMs + (Math.random() * 2 - 1) * delta;
|
|
13
|
+
}
|
|
14
|
+
/**
|
|
15
|
+
* Sleep with jitter applied.
|
|
16
|
+
*/
|
|
17
|
+
export function sleepWithJitter(baseMs, factor = 0.2) {
|
|
18
|
+
return sleep(jitter(baseMs, factor));
|
|
19
|
+
}
|
|
20
|
+
//# sourceMappingURL=sleep.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"sleep.js","sourceRoot":"","sources":["../../../src/utils/sleep.ts"],"names":[],"mappings":"AAAA;;GAEG;AACH,MAAM,UAAU,KAAK,CAAC,EAAU;IAC9B,OAAO,IAAI,OAAO,CAAC,CAAC,OAAO,EAAE,EAAE,CAAC,UAAU,CAAC,OAAO,EAAE,EAAE,CAAC,CAAC,CAAC;AAC3D,CAAC;AAED;;GAEG;AACH,MAAM,UAAU,MAAM,CAAC,MAAc,EAAE,MAAM,GAAG,GAAG;IACjD,MAAM,KAAK,GAAG,MAAM,GAAG,MAAM,CAAC;IAC9B,OAAO,MAAM,GAAG,CAAC,IAAI,CAAC,MAAM,EAAE,GAAG,CAAC,GAAG,CAAC,CAAC,GAAG,KAAK,CAAC;AAClD,CAAC;AAED;;GAEG;AACH,MAAM,UAAU,eAAe,CAAC,MAAc,EAAE,MAAM,GAAG,GAAG;IAC1D,OAAO,KAAK,CAAC,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC,CAAC;AACvC,CAAC"}
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
import type { StorageAdapter, StoredRecord } from '../../types/index.js';
|
|
2
|
+
export interface MemoryAdapterOptions {
|
|
3
|
+
/** Maximum number of keys before LRU eviction (default: unlimited) */
|
|
4
|
+
maxKeys?: number;
|
|
5
|
+
/** Clock function — injectable for deterministic testing */
|
|
6
|
+
clock?: () => number;
|
|
7
|
+
}
|
|
8
|
+
/**
|
|
9
|
+
* Built-in in-memory storage adapter.
|
|
10
|
+
*
|
|
11
|
+
* Features:
|
|
12
|
+
* - O(1) key lookup via Map
|
|
13
|
+
* - Automatic TTL eviction on read
|
|
14
|
+
* - Optional maxKeys with LRU eviction to prevent unbounded growth
|
|
15
|
+
* - No global state — each instance is isolated
|
|
16
|
+
*
|
|
17
|
+
* @example
|
|
18
|
+
* ```ts
|
|
19
|
+
* import { createMemoryAdapter } from '@periodic/vanadium';
|
|
20
|
+
* const adapter = createMemoryAdapter({ maxKeys: 10_000 });
|
|
21
|
+
* ```
|
|
22
|
+
*/
|
|
23
|
+
export declare class MemoryAdapter implements StorageAdapter {
|
|
24
|
+
readonly name = "memory";
|
|
25
|
+
private readonly store;
|
|
26
|
+
private readonly maxKeys;
|
|
27
|
+
private readonly clock;
|
|
28
|
+
constructor(options?: MemoryAdapterOptions);
|
|
29
|
+
get<T = unknown>(key: string): Promise<StoredRecord<T> | null>;
|
|
30
|
+
set<T = unknown>(key: string, value: StoredRecord<T>, ttlMs?: number): Promise<void>;
|
|
31
|
+
delete(key: string): Promise<void>;
|
|
32
|
+
compareAndSet<T = unknown>(key: string, expectedOwnerToken: string, newValue: StoredRecord<T>, ttlMs?: number): Promise<boolean>;
|
|
33
|
+
setIfAbsent<T = unknown>(key: string, value: StoredRecord<T>, ttlMs?: number): Promise<boolean>;
|
|
34
|
+
/** Evict the single least-recently-accessed entry. */
|
|
35
|
+
private _evictLRU;
|
|
36
|
+
/** Return current number of stored keys. */
|
|
37
|
+
size(): number;
|
|
38
|
+
/** Clear all stored entries. */
|
|
39
|
+
clear(): void;
|
|
40
|
+
/** Expose adapter capabilities. */
|
|
41
|
+
capabilities(): {
|
|
42
|
+
transactions: boolean;
|
|
43
|
+
cas: boolean;
|
|
44
|
+
ttl: boolean;
|
|
45
|
+
advisoryLocks: boolean;
|
|
46
|
+
};
|
|
47
|
+
}
|
|
48
|
+
export declare function createMemoryAdapter(options?: MemoryAdapterOptions): MemoryAdapter;
|
|
49
|
+
//# sourceMappingURL=index.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../../src/adapters/memory/index.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,cAAc,EAAE,YAAY,EAAE,MAAM,sBAAsB,CAAC;AAKzE,MAAM,WAAW,oBAAoB;IACnC,sEAAsE;IACtE,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,4DAA4D;IAC5D,KAAK,CAAC,EAAE,MAAM,MAAM,CAAC;CACtB;AAYD;;;;;;;;;;;;;;GAcG;AACH,qBAAa,aAAc,YAAW,cAAc;IAClD,SAAgB,IAAI,YAAY;IAEhC,OAAO,CAAC,QAAQ,CAAC,KAAK,CAAkC;IACxD,OAAO,CAAC,QAAQ,CAAC,OAAO,CAAS;IACjC,OAAO,CAAC,QAAQ,CAAC,KAAK,CAAe;gBAEzB,OAAO,GAAE,oBAAyB;IAKxC,GAAG,CAAC,CAAC,GAAG,OAAO,EAAE,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC,YAAY,CAAC,CAAC,CAAC,GAAG,IAAI,CAAC;IAiB9D,GAAG,CAAC,CAAC,GAAG,OAAO,EAAE,GAAG,EAAE,MAAM,EAAE,KAAK,EAAE,YAAY,CAAC,CAAC,CAAC,EAAE,KAAK,CAAC,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;IAgBpF,MAAM,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;IAIlC,aAAa,CAAC,CAAC,GAAG,OAAO,EAC7B,GAAG,EAAE,MAAM,EACX,kBAAkB,EAAE,MAAM,EAC1B,QAAQ,EAAE,YAAY,CAAC,CAAC,CAAC,EACzB,KAAK,CAAC,EAAE,MAAM,GACb,OAAO,CAAC,OAAO,CAAC;IAiBb,WAAW,CAAC,CAAC,GAAG,OAAO,EAC3B,GAAG,EAAE,MAAM,EACX,KAAK,EAAE,YAAY,CAAC,CAAC,CAAC,EACtB,KAAK,CAAC,EAAE,MAAM,GACb,OAAO,CAAC,OAAO,CAAC;IAkBnB,sDAAsD;IACtD,OAAO,CAAC,SAAS;IAgBjB,4CAA4C;IAC5C,IAAI,IAAI,MAAM;IAId,gCAAgC;IAChC,KAAK,IAAI,IAAI;IAIb,mCAAmC;IACnC,YAAY;;;;;;CAQb;AAID,wBAAgB,mBAAmB,CAAC,OAAO,CAAC,EAAE,oBAAoB,GAAG,aAAa,CAEjF"}
|
|
@@ -0,0 +1,97 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* MongoDB Storage Adapter for @periodic/vanadium
|
|
3
|
+
*
|
|
4
|
+
* Peer dependency: "mongodb" >= 5.0.0
|
|
5
|
+
* Import: import { createMongoAdapter } from '@periodic/vanadium/adapters/mongodb'
|
|
6
|
+
*
|
|
7
|
+
* Uses the official MongoDB driver (NOT Mongoose).
|
|
8
|
+
* The caller is responsible for providing a connected MongoClient.
|
|
9
|
+
*
|
|
10
|
+
* Required indexes (run once):
|
|
11
|
+
* ```js
|
|
12
|
+
* db.collection('vanadium_records').createIndex({ expiresAt: 1 });
|
|
13
|
+
* db.collection('vanadium_records').createIndex({ status: 1, expiresAt: 1 });
|
|
14
|
+
* ```
|
|
15
|
+
*/
|
|
16
|
+
import type { StorageAdapter, StoredRecord } from '../../types/index.js';
|
|
17
|
+
interface MongoCollection {
|
|
18
|
+
findOne(filter: any, options?: any): Promise<any>;
|
|
19
|
+
insertOne(doc: any, options?: any): Promise<any>;
|
|
20
|
+
findOneAndUpdate(filter: any, update: any, options?: any): Promise<any>;
|
|
21
|
+
deleteOne(filter: any, options?: any): Promise<any>;
|
|
22
|
+
createIndex(spec: any, options?: any): Promise<string>;
|
|
23
|
+
}
|
|
24
|
+
interface MongoDb {
|
|
25
|
+
collection(name: string): MongoCollection;
|
|
26
|
+
}
|
|
27
|
+
interface MongoClientLike {
|
|
28
|
+
db(name: string): MongoDb;
|
|
29
|
+
startSession(): any;
|
|
30
|
+
}
|
|
31
|
+
export interface MongoAdapterOptions {
|
|
32
|
+
/** Connected MongoClient */
|
|
33
|
+
client: MongoClientLike;
|
|
34
|
+
/** Database name */
|
|
35
|
+
dbName: string;
|
|
36
|
+
/** Collection name (default: "vanadium_records") */
|
|
37
|
+
collectionName?: string;
|
|
38
|
+
/** Use multi-document transactions (requires replica set) (default: true) */
|
|
39
|
+
useTransactions?: boolean;
|
|
40
|
+
/** Write concern (default: { w: "majority" }) */
|
|
41
|
+
writeConcern?: Record<string, any>;
|
|
42
|
+
/** Clock function for deterministic testing */
|
|
43
|
+
clock?: () => number;
|
|
44
|
+
}
|
|
45
|
+
interface VanadiumDocument {
|
|
46
|
+
_id: string;
|
|
47
|
+
status: string;
|
|
48
|
+
result?: unknown;
|
|
49
|
+
payloadHash?: string;
|
|
50
|
+
ownerToken?: string;
|
|
51
|
+
attempts: number;
|
|
52
|
+
createdAt: number;
|
|
53
|
+
updatedAt: number;
|
|
54
|
+
expiresAt?: number;
|
|
55
|
+
}
|
|
56
|
+
export declare class MongoAdapter implements StorageAdapter {
|
|
57
|
+
readonly name = "mongodb";
|
|
58
|
+
private readonly db;
|
|
59
|
+
private readonly collectionName;
|
|
60
|
+
private readonly useTransactions;
|
|
61
|
+
private readonly clock;
|
|
62
|
+
constructor(options: MongoAdapterOptions);
|
|
63
|
+
private get collection();
|
|
64
|
+
get<T = unknown>(key: string): Promise<StoredRecord<T> | null>;
|
|
65
|
+
set<T = unknown>(key: string, value: StoredRecord<T>, ttlMs?: number): Promise<void>;
|
|
66
|
+
delete(key: string): Promise<void>;
|
|
67
|
+
compareAndSet<T = unknown>(key: string, expectedOwnerToken: string, newValue: StoredRecord<T>, ttlMs?: number): Promise<boolean>;
|
|
68
|
+
/**
|
|
69
|
+
* Atomic expired takeover using conditional findOneAndUpdate.
|
|
70
|
+
* Guarantees only one concurrent caller succeeds.
|
|
71
|
+
*/
|
|
72
|
+
takeoverExpired(key: string, newOwnerToken: string, ttlMs: number): Promise<VanadiumDocument | null>;
|
|
73
|
+
capabilities(): {
|
|
74
|
+
transactions: boolean;
|
|
75
|
+
cas: boolean;
|
|
76
|
+
ttl: boolean;
|
|
77
|
+
advisoryLocks: boolean;
|
|
78
|
+
};
|
|
79
|
+
private _docToRecord;
|
|
80
|
+
}
|
|
81
|
+
/**
|
|
82
|
+
* Create a MongoDB storage adapter.
|
|
83
|
+
*
|
|
84
|
+
* @example
|
|
85
|
+
* ```ts
|
|
86
|
+
* import { MongoClient } from 'mongodb';
|
|
87
|
+
* import { createMongoAdapter } from '@periodic/vanadium/adapters/mongodb';
|
|
88
|
+
*
|
|
89
|
+
* const client = new MongoClient(process.env.MONGO_URL!);
|
|
90
|
+
* await client.connect();
|
|
91
|
+
*
|
|
92
|
+
* const adapter = createMongoAdapter({ client, dbName: 'myapp' });
|
|
93
|
+
* ```
|
|
94
|
+
*/
|
|
95
|
+
export declare function createMongoAdapter(options: MongoAdapterOptions): MongoAdapter;
|
|
96
|
+
export {};
|
|
97
|
+
//# sourceMappingURL=index.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../../src/adapters/mongodb/index.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,107 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Mongoose Storage Adapter Wrapper for @periodic/vanadium
|
|
3
|
+
*
|
|
4
|
+
* Peer dependency: "mongoose" >= 7.0.0
|
|
5
|
+
* Import: import { createMongooseAdapter } from '@periodic/vanadium/adapters/mongoose'
|
|
6
|
+
*
|
|
7
|
+
* This is a WRAPPER that preserves identical semantics to the MongoDB native adapter.
|
|
8
|
+
* Mongoose is purely an ORM layer — it does NOT change idempotency behavior.
|
|
9
|
+
*
|
|
10
|
+
* Required Prisma schema:
|
|
11
|
+
* ```prisma
|
|
12
|
+
* model vanadiumRecord {
|
|
13
|
+
* id String @id @map("_id")
|
|
14
|
+
* status String
|
|
15
|
+
* result Json?
|
|
16
|
+
* payloadHash String?
|
|
17
|
+
* ownerToken String?
|
|
18
|
+
* attempts Int @default(0)
|
|
19
|
+
* createdAt BigInt
|
|
20
|
+
* updatedAt BigInt
|
|
21
|
+
* expiresAt BigInt?
|
|
22
|
+
* }
|
|
23
|
+
* ```
|
|
24
|
+
*
|
|
25
|
+
* Required Mongoose schema (use createVanadiumSchema() helper):
|
|
26
|
+
* ```ts
|
|
27
|
+
* import { createVanadiumSchema } from '@periodic/vanadium/adapters/mongoose';
|
|
28
|
+
* const VanadiumRecord = mongoose.model('VanadiumRecord', createVanadiumSchema());
|
|
29
|
+
* ```
|
|
30
|
+
*/
|
|
31
|
+
import type { StorageAdapter, StoredRecord } from '../../types/index.js';
|
|
32
|
+
interface MongooseDocument {
|
|
33
|
+
_id: string;
|
|
34
|
+
status: string;
|
|
35
|
+
result?: unknown;
|
|
36
|
+
payloadHash?: string;
|
|
37
|
+
ownerToken?: string;
|
|
38
|
+
attempts: number;
|
|
39
|
+
createdAt: number;
|
|
40
|
+
updatedAt: number;
|
|
41
|
+
expiresAt?: number;
|
|
42
|
+
toObject(): Record<string, unknown>;
|
|
43
|
+
}
|
|
44
|
+
interface MongooseModel {
|
|
45
|
+
create(doc: any): Promise<MongooseDocument>;
|
|
46
|
+
findOneAndUpdate(filter: any, update: any, options?: any): Promise<MongooseDocument | null>;
|
|
47
|
+
findOne(filter: any): Promise<MongooseDocument | null>;
|
|
48
|
+
deleteOne(filter: any): Promise<any>;
|
|
49
|
+
db: {
|
|
50
|
+
startSession(): Promise<any>;
|
|
51
|
+
};
|
|
52
|
+
}
|
|
53
|
+
type SchemaDefinition = Record<string, unknown>;
|
|
54
|
+
interface MongooseSchemaConstructor {
|
|
55
|
+
new (definition: SchemaDefinition, options?: Record<string, unknown>): unknown;
|
|
56
|
+
Types: {
|
|
57
|
+
Mixed: unknown;
|
|
58
|
+
};
|
|
59
|
+
}
|
|
60
|
+
export interface MongooseAdapterOptions {
|
|
61
|
+
/** Mongoose Model with Vanadium schema */
|
|
62
|
+
model: MongooseModel;
|
|
63
|
+
/** Use transactions (requires replica set) (default: true) */
|
|
64
|
+
useTransactions?: boolean;
|
|
65
|
+
/** Custom session factory */
|
|
66
|
+
sessionFactory?: () => Promise<unknown>;
|
|
67
|
+
/** Clock function for deterministic testing */
|
|
68
|
+
clock?: () => number;
|
|
69
|
+
}
|
|
70
|
+
export declare class MongooseAdapter implements StorageAdapter {
|
|
71
|
+
readonly name = "mongoose";
|
|
72
|
+
private readonly model;
|
|
73
|
+
private readonly useTransactions;
|
|
74
|
+
private readonly clock;
|
|
75
|
+
constructor(options: MongooseAdapterOptions);
|
|
76
|
+
get<T = unknown>(key: string): Promise<StoredRecord<T> | null>;
|
|
77
|
+
set<T = unknown>(key: string, value: StoredRecord<T>, ttlMs?: number): Promise<void>;
|
|
78
|
+
delete(key: string): Promise<void>;
|
|
79
|
+
compareAndSet<T = unknown>(key: string, expectedOwnerToken: string, newValue: StoredRecord<T>, ttlMs?: number): Promise<boolean>;
|
|
80
|
+
capabilities(): {
|
|
81
|
+
transactions: boolean;
|
|
82
|
+
cas: boolean;
|
|
83
|
+
ttl: boolean;
|
|
84
|
+
advisoryLocks: boolean;
|
|
85
|
+
};
|
|
86
|
+
private _docToRecord;
|
|
87
|
+
}
|
|
88
|
+
/**
|
|
89
|
+
* Build the required Mongoose schema for Vanadium records.
|
|
90
|
+
* Pass the Mongoose Schema constructor from your mongoose import.
|
|
91
|
+
*/
|
|
92
|
+
export declare function createVanadiumSchema(Schema: MongooseSchemaConstructor): unknown;
|
|
93
|
+
/**
|
|
94
|
+
* Create a Mongoose storage adapter.
|
|
95
|
+
*
|
|
96
|
+
* @example
|
|
97
|
+
* ```ts
|
|
98
|
+
* import mongoose from 'mongoose';
|
|
99
|
+
* import { createMongooseAdapter, createVanadiumSchema } from '@periodic/vanadium/adapters/mongoose';
|
|
100
|
+
*
|
|
101
|
+
* const VanadiumRecord = mongoose.model('VanadiumRecord', createVanadiumSchema(mongoose.Schema));
|
|
102
|
+
* const adapter = createMongooseAdapter({ model: VanadiumRecord });
|
|
103
|
+
* ```
|
|
104
|
+
*/
|
|
105
|
+
export declare function createMongooseAdapter(options: MongooseAdapterOptions): MongooseAdapter;
|
|
106
|
+
export {};
|
|
107
|
+
//# sourceMappingURL=index.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../../src/adapters/mongoose/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA6BG;AAEH,OAAO,KAAK,EAAE,cAAc,EAAE,YAAY,EAAE,MAAM,sBAAsB,CAAC;AAMzE,UAAU,gBAAgB;IACxB,GAAG,EAAE,MAAM,CAAC;IACZ,MAAM,EAAE,MAAM,CAAC;IACf,MAAM,CAAC,EAAE,OAAO,CAAC;IACjB,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,QAAQ,EAAE,MAAM,CAAC;IACjB,SAAS,EAAE,MAAM,CAAC;IAClB,SAAS,EAAE,MAAM,CAAC;IAClB,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,QAAQ,IAAI,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;CACrC;AAED,UAAU,aAAa;IACrB,MAAM,CAAC,GAAG,EAAE,GAAG,GAAG,OAAO,CAAC,gBAAgB,CAAC,CAAC;IAC5C,gBAAgB,CAAC,MAAM,EAAE,GAAG,EAAE,MAAM,EAAE,GAAG,EAAE,OAAO,CAAC,EAAE,GAAG,GAAG,OAAO,CAAC,gBAAgB,GAAG,IAAI,CAAC,CAAC;IAC5F,OAAO,CAAC,MAAM,EAAE,GAAG,GAAG,OAAO,CAAC,gBAAgB,GAAG,IAAI,CAAC,CAAC;IACvD,SAAS,CAAC,MAAM,EAAE,GAAG,GAAG,OAAO,CAAC,GAAG,CAAC,CAAC;IACrC,EAAE,EAAE;QACF,YAAY,IAAI,OAAO,CAAC,GAAG,CAAC,CAAC;KAC9B,CAAC;CACH;AAED,KAAK,gBAAgB,GAAG,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;AAEhD,UAAU,yBAAyB;IACjC,KAAK,UAAU,EAAE,gBAAgB,EAAE,OAAO,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,OAAO,CAAC;IAC/E,KAAK,EAAE;QAAE,KAAK,EAAE,OAAO,CAAA;KAAE,CAAC;CAC3B;AAOD,MAAM,WAAW,sBAAsB;IACrC,0CAA0C;IAC1C,KAAK,EAAE,aAAa,CAAC;IACrB,8DAA8D;IAC9D,eAAe,CAAC,EAAE,OAAO,CAAC;IAC1B,6BAA6B;IAC7B,cAAc,CAAC,EAAE,MAAM,OAAO,CAAC,OAAO,CAAC,CAAC;IACxC,+CAA+C;IAC/C,KAAK,CAAC,EAAE,MAAM,MAAM,CAAC;CACtB;AAID,qBAAa,eAAgB,YAAW,cAAc;IACpD,SAAgB,IAAI,cAAc;IAElC,OAAO,CAAC,QAAQ,CAAC,KAAK,CAAgB;IACtC,OAAO,CAAC,QAAQ,CAAC,eAAe,CAAU;IAC1C,OAAO,CAAC,QAAQ,CAAC,KAAK,CAAe;gBAGzB,OAAO,EAAE,sBAAsB;IAWrC,GAAG,CAAC,CAAC,GAAG,OAAO,EAAE,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC,YAAY,CAAC,CAAC,CAAC,GAAG,IAAI,CAAC;IAU9D,GAAG,CAAC,CAAC,GAAG,OAAO,EAAE,GAAG,EAAE,MAAM,EAAE,KAAK,EAAE,YAAY,CAAC,CAAC,CAAC,EAAE,KAAK,CAAC,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;IA+CpF,MAAM,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;IAQlC,aAAa,CAAC,CAAC,GAAG,OAAO,EAC7B,GAAG,EAAE,MAAM,EACX,kBAAkB,EAAE,MAAM,EAC1B,QAAQ,EAAE,YAAY,CAAC,CAAC,CAAC,EACzB,KAAK,CAAC,EAAE,MAAM,GACb,OAAO,CAAC,OAAO,CAAC;IA4BnB,YAAY;;;;;;IAWZ,OAAO,CAAC,YAAY;CAarB;AAID;;;GAGG;AACH,wBAAgB,oBAAoB,CAAC,MAAM,EAAE,yBAAyB,GAAG,OAAO,CAsB/E;AAID;;;;;;;;;;;GAWG;AACH,wBAAgB,qBAAqB,CAAC,OAAO,EAAE,sBAAsB,GAAG,eAAe,CAEtF"}
|
|
@@ -0,0 +1,85 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* PostgreSQL Storage Adapter for @periodic/vanadium
|
|
3
|
+
*
|
|
4
|
+
* Peer dependency: "pg" >= 8.0.0
|
|
5
|
+
* Import: import { createPostgresAdapter } from '@periodic/vanadium/adapters/postgres'
|
|
6
|
+
*
|
|
7
|
+
* The caller MUST provide an already-created Pool or Client instance.
|
|
8
|
+
* This adapter never creates or closes connections.
|
|
9
|
+
*
|
|
10
|
+
* Required schema:
|
|
11
|
+
* ```sql
|
|
12
|
+
* CREATE TABLE public.vanadium_records (
|
|
13
|
+
* key TEXT PRIMARY KEY,
|
|
14
|
+
* status TEXT NOT NULL CHECK (status IN ('IN_PROGRESS','COMPLETED','FAILED')),
|
|
15
|
+
* result JSONB,
|
|
16
|
+
* payload_hash TEXT,
|
|
17
|
+
* owner_token TEXT,
|
|
18
|
+
* attempts INTEGER NOT NULL DEFAULT 0,
|
|
19
|
+
* created_at BIGINT NOT NULL,
|
|
20
|
+
* updated_at BIGINT NOT NULL,
|
|
21
|
+
* expires_at BIGINT
|
|
22
|
+
* );
|
|
23
|
+
* CREATE INDEX idx_vanadium_expires_at ON public.vanadium_records (expires_at);
|
|
24
|
+
* CREATE INDEX idx_vanadium_status_expires ON public.vanadium_records (status, expires_at);
|
|
25
|
+
* ```
|
|
26
|
+
*/
|
|
27
|
+
import type { StorageAdapter, StoredRecord } from '../../types/index.js';
|
|
28
|
+
interface PgQueryable {
|
|
29
|
+
query(text: string, values?: any[]): Promise<{
|
|
30
|
+
rows: any[];
|
|
31
|
+
rowCount: number | null;
|
|
32
|
+
}>;
|
|
33
|
+
}
|
|
34
|
+
export interface PostgresAdapterOptions {
|
|
35
|
+
/** Pre-created pg Pool or Client */
|
|
36
|
+
client: PgQueryable & {
|
|
37
|
+
connect?: () => Promise<PgQueryable & {
|
|
38
|
+
release: () => void;
|
|
39
|
+
}>;
|
|
40
|
+
};
|
|
41
|
+
/** Table name (default: "vanadium_records") */
|
|
42
|
+
tableName?: string;
|
|
43
|
+
/** Schema name (default: "public") */
|
|
44
|
+
schemaName?: string;
|
|
45
|
+
/** Use pg advisory locks for distributed lock mode (default: false) */
|
|
46
|
+
useAdvisoryLocks?: boolean;
|
|
47
|
+
/** Clock function for deterministic testing */
|
|
48
|
+
clock?: () => number;
|
|
49
|
+
}
|
|
50
|
+
export declare class PostgresAdapter implements StorageAdapter {
|
|
51
|
+
readonly name = "postgres";
|
|
52
|
+
private readonly client;
|
|
53
|
+
private readonly table;
|
|
54
|
+
private readonly useAdvisoryLocks;
|
|
55
|
+
private readonly clock;
|
|
56
|
+
constructor(options: PostgresAdapterOptions);
|
|
57
|
+
get<T = unknown>(key: string): Promise<StoredRecord<T> | null>;
|
|
58
|
+
set<T = unknown>(key: string, value: StoredRecord<T>, ttlMs?: number): Promise<void>;
|
|
59
|
+
delete(key: string): Promise<void>;
|
|
60
|
+
compareAndSet<T = unknown>(key: string, expectedOwnerToken: string, newValue: StoredRecord<T>, ttlMs?: number): Promise<boolean>;
|
|
61
|
+
tryAdvisoryLock(key: string): Promise<boolean>;
|
|
62
|
+
releaseAdvisoryLock(key: string): Promise<void>;
|
|
63
|
+
capabilities(): {
|
|
64
|
+
transactions: boolean;
|
|
65
|
+
cas: boolean;
|
|
66
|
+
ttl: boolean;
|
|
67
|
+
advisoryLocks: boolean;
|
|
68
|
+
};
|
|
69
|
+
private _rowToRecord;
|
|
70
|
+
}
|
|
71
|
+
/**
|
|
72
|
+
* Create a PostgreSQL storage adapter.
|
|
73
|
+
*
|
|
74
|
+
* @example
|
|
75
|
+
* ```ts
|
|
76
|
+
* import { Pool } from 'pg';
|
|
77
|
+
* import { createPostgresAdapter } from '@periodic/vanadium/adapters/postgres';
|
|
78
|
+
*
|
|
79
|
+
* const pool = new Pool({ connectionString: process.env.DATABASE_URL });
|
|
80
|
+
* const adapter = createPostgresAdapter({ client: pool });
|
|
81
|
+
* ```
|
|
82
|
+
*/
|
|
83
|
+
export declare function createPostgresAdapter(options: PostgresAdapterOptions): PostgresAdapter;
|
|
84
|
+
export {};
|
|
85
|
+
//# sourceMappingURL=index.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../../src/adapters/postgres/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;GAyBG;AAEH,OAAO,KAAK,EAAE,cAAc,EAAE,YAAY,EAAE,MAAM,sBAAsB,CAAC;AAMzE,UAAU,WAAW;IACnB,KAAK,CAAC,IAAI,EAAE,MAAM,EAAE,MAAM,CAAC,EAAE,GAAG,EAAE,GAAG,OAAO,CAAC;QAAE,IAAI,EAAE,GAAG,EAAE,CAAC;QAAC,QAAQ,EAAE,MAAM,GAAG,IAAI,CAAA;KAAE,CAAC,CAAC;CACxF;AAKD,MAAM,WAAW,sBAAsB;IACrC,oCAAoC;IACpC,MAAM,EAAE,WAAW,GAAG;QAAE,OAAO,CAAC,EAAE,MAAM,OAAO,CAAC,WAAW,GAAG;YAAE,OAAO,EAAE,MAAM,IAAI,CAAA;SAAE,CAAC,CAAA;KAAE,CAAC;IACzF,+CAA+C;IAC/C,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,sCAAsC;IACtC,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,uEAAuE;IACvE,gBAAgB,CAAC,EAAE,OAAO,CAAC;IAC3B,+CAA+C;IAC/C,KAAK,CAAC,EAAE,MAAM,MAAM,CAAC;CACtB;AAkBD,qBAAa,eAAgB,YAAW,cAAc;IACpD,SAAgB,IAAI,cAAc;IAElC,OAAO,CAAC,QAAQ,CAAC,MAAM,CAAmC;IAC1D,OAAO,CAAC,QAAQ,CAAC,KAAK,CAAS;IAC/B,OAAO,CAAC,QAAQ,CAAC,gBAAgB,CAAU;IAC3C,OAAO,CAAC,QAAQ,CAAC,KAAK,CAAe;gBAEzB,OAAO,EAAE,sBAAsB;IAkBrC,GAAG,CAAC,CAAC,GAAG,OAAO,EAAE,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC,YAAY,CAAC,CAAC,CAAC,GAAG,IAAI,CAAC;IAW9D,GAAG,CAAC,CAAC,GAAG,OAAO,EAAE,GAAG,EAAE,MAAM,EAAE,KAAK,EAAE,YAAY,CAAC,CAAC,CAAC,EAAE,KAAK,CAAC,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;IAqCpF,MAAM,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;IASlC,aAAa,CAAC,CAAC,GAAG,OAAO,EAC7B,GAAG,EAAE,MAAM,EACX,kBAAkB,EAAE,MAAM,EAC1B,QAAQ,EAAE,YAAY,CAAC,CAAC,CAAC,EACzB,KAAK,CAAC,EAAE,MAAM,GACb,OAAO,CAAC,OAAO,CAAC;IAsCb,eAAe,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC,OAAO,CAAC;IAW9C,mBAAmB,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;IAYrD,YAAY;;;;;;IAWZ,OAAO,CAAC,YAAY;CAarB;AAID;;;;;;;;;;;GAWG;AACH,wBAAgB,qBAAqB,CAAC,OAAO,EAAE,sBAAsB,GAAG,eAAe,CAEtF"}
|