@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.
Files changed (136) hide show
  1. package/CHANGELOG.md +37 -0
  2. package/LICENSE +21 -0
  3. package/README.md +846 -0
  4. package/dist/cjs/adapters/memory/index.js +134 -0
  5. package/dist/cjs/adapters/memory/index.js.map +1 -0
  6. package/dist/cjs/adapters/mongodb/index.js +189 -0
  7. package/dist/cjs/adapters/mongodb/index.js.map +1 -0
  8. package/dist/cjs/adapters/mongoose/index.js +199 -0
  9. package/dist/cjs/adapters/mongoose/index.js.map +1 -0
  10. package/dist/cjs/adapters/postgres/index.js +202 -0
  11. package/dist/cjs/adapters/postgres/index.js.map +1 -0
  12. package/dist/cjs/adapters/prisma/index.js +176 -0
  13. package/dist/cjs/adapters/prisma/index.js.map +1 -0
  14. package/dist/cjs/adapters/redis/index.js +178 -0
  15. package/dist/cjs/adapters/redis/index.js.map +1 -0
  16. package/dist/cjs/cleanup/engine.js +100 -0
  17. package/dist/cjs/cleanup/engine.js.map +1 -0
  18. package/dist/cjs/core/concurrencyGuard.js +50 -0
  19. package/dist/cjs/core/concurrencyGuard.js.map +1 -0
  20. package/dist/cjs/core/metrics.js +39 -0
  21. package/dist/cjs/core/metrics.js.map +1 -0
  22. package/dist/cjs/core/stateMachine.js +46 -0
  23. package/dist/cjs/core/stateMachine.js.map +1 -0
  24. package/dist/cjs/errors/index.js +127 -0
  25. package/dist/cjs/errors/index.js.map +1 -0
  26. package/dist/cjs/http/express.js +84 -0
  27. package/dist/cjs/http/express.js.map +1 -0
  28. package/dist/cjs/http/fastify.js +70 -0
  29. package/dist/cjs/http/fastify.js.map +1 -0
  30. package/dist/cjs/idempotency/engine.js +266 -0
  31. package/dist/cjs/idempotency/engine.js.map +1 -0
  32. package/dist/cjs/index.js +19 -0
  33. package/dist/cjs/index.js.map +1 -0
  34. package/dist/cjs/lock/engine.js +187 -0
  35. package/dist/cjs/lock/engine.js.map +1 -0
  36. package/dist/cjs/observability/metrics.js +92 -0
  37. package/dist/cjs/observability/metrics.js.map +1 -0
  38. package/dist/cjs/resilience/circuitBreaker.js +129 -0
  39. package/dist/cjs/resilience/circuitBreaker.js.map +1 -0
  40. package/dist/cjs/types/index.js +13 -0
  41. package/dist/cjs/types/index.js.map +1 -0
  42. package/dist/cjs/utils/crypto.js +64 -0
  43. package/dist/cjs/utils/crypto.js.map +1 -0
  44. package/dist/cjs/utils/keys.js +40 -0
  45. package/dist/cjs/utils/keys.js.map +1 -0
  46. package/dist/cjs/utils/sleep.js +25 -0
  47. package/dist/cjs/utils/sleep.js.map +1 -0
  48. package/dist/esm/adapters/memory/index.js +129 -0
  49. package/dist/esm/adapters/memory/index.js.map +1 -0
  50. package/dist/esm/adapters/mongodb/index.js +184 -0
  51. package/dist/esm/adapters/mongodb/index.js.map +1 -0
  52. package/dist/esm/adapters/mongoose/index.js +193 -0
  53. package/dist/esm/adapters/mongoose/index.js.map +1 -0
  54. package/dist/esm/adapters/postgres/index.js +197 -0
  55. package/dist/esm/adapters/postgres/index.js.map +1 -0
  56. package/dist/esm/adapters/prisma/index.js +171 -0
  57. package/dist/esm/adapters/prisma/index.js.map +1 -0
  58. package/dist/esm/adapters/redis/index.js +173 -0
  59. package/dist/esm/adapters/redis/index.js.map +1 -0
  60. package/dist/esm/cleanup/engine.js +95 -0
  61. package/dist/esm/cleanup/engine.js.map +1 -0
  62. package/dist/esm/core/concurrencyGuard.js +46 -0
  63. package/dist/esm/core/concurrencyGuard.js.map +1 -0
  64. package/dist/esm/core/metrics.js +35 -0
  65. package/dist/esm/core/metrics.js.map +1 -0
  66. package/dist/esm/core/stateMachine.js +40 -0
  67. package/dist/esm/core/stateMachine.js.map +1 -0
  68. package/dist/esm/errors/index.js +114 -0
  69. package/dist/esm/errors/index.js.map +1 -0
  70. package/dist/esm/http/express.js +81 -0
  71. package/dist/esm/http/express.js.map +1 -0
  72. package/dist/esm/http/fastify.js +67 -0
  73. package/dist/esm/http/fastify.js.map +1 -0
  74. package/dist/esm/idempotency/engine.js +261 -0
  75. package/dist/esm/idempotency/engine.js.map +1 -0
  76. package/dist/esm/index.js +9 -0
  77. package/dist/esm/index.js.map +1 -0
  78. package/dist/esm/lock/engine.js +182 -0
  79. package/dist/esm/lock/engine.js.map +1 -0
  80. package/dist/esm/observability/metrics.js +89 -0
  81. package/dist/esm/observability/metrics.js.map +1 -0
  82. package/dist/esm/resilience/circuitBreaker.js +124 -0
  83. package/dist/esm/resilience/circuitBreaker.js.map +1 -0
  84. package/dist/esm/types/index.js +10 -0
  85. package/dist/esm/types/index.js.map +1 -0
  86. package/dist/esm/utils/crypto.js +58 -0
  87. package/dist/esm/utils/crypto.js.map +1 -0
  88. package/dist/esm/utils/keys.js +35 -0
  89. package/dist/esm/utils/keys.js.map +1 -0
  90. package/dist/esm/utils/sleep.js +20 -0
  91. package/dist/esm/utils/sleep.js.map +1 -0
  92. package/dist/types/adapters/memory/index.d.ts +49 -0
  93. package/dist/types/adapters/memory/index.d.ts.map +1 -0
  94. package/dist/types/adapters/mongodb/index.d.ts +97 -0
  95. package/dist/types/adapters/mongodb/index.d.ts.map +1 -0
  96. package/dist/types/adapters/mongoose/index.d.ts +107 -0
  97. package/dist/types/adapters/mongoose/index.d.ts.map +1 -0
  98. package/dist/types/adapters/postgres/index.d.ts +85 -0
  99. package/dist/types/adapters/postgres/index.d.ts.map +1 -0
  100. package/dist/types/adapters/prisma/index.d.ts +73 -0
  101. package/dist/types/adapters/prisma/index.d.ts.map +1 -0
  102. package/dist/types/adapters/redis/index.d.ts +77 -0
  103. package/dist/types/adapters/redis/index.d.ts.map +1 -0
  104. package/dist/types/cleanup/engine.d.ts +41 -0
  105. package/dist/types/cleanup/engine.d.ts.map +1 -0
  106. package/dist/types/core/concurrencyGuard.d.ts +28 -0
  107. package/dist/types/core/concurrencyGuard.d.ts.map +1 -0
  108. package/dist/types/core/metrics.d.ts +13 -0
  109. package/dist/types/core/metrics.d.ts.map +1 -0
  110. package/dist/types/core/stateMachine.d.ts +20 -0
  111. package/dist/types/core/stateMachine.d.ts.map +1 -0
  112. package/dist/types/errors/index.d.ts +32 -0
  113. package/dist/types/errors/index.d.ts.map +1 -0
  114. package/dist/types/http/express.d.ts +50 -0
  115. package/dist/types/http/express.d.ts.map +1 -0
  116. package/dist/types/http/fastify.d.ts +48 -0
  117. package/dist/types/http/fastify.d.ts.map +1 -0
  118. package/dist/types/idempotency/engine.d.ts +24 -0
  119. package/dist/types/idempotency/engine.d.ts.map +1 -0
  120. package/dist/types/index.d.ts +8 -0
  121. package/dist/types/index.d.ts.map +1 -0
  122. package/dist/types/lock/engine.d.ts +28 -0
  123. package/dist/types/lock/engine.d.ts.map +1 -0
  124. package/dist/types/observability/metrics.d.ts +45 -0
  125. package/dist/types/observability/metrics.d.ts.map +1 -0
  126. package/dist/types/resilience/circuitBreaker.d.ts +48 -0
  127. package/dist/types/resilience/circuitBreaker.d.ts.map +1 -0
  128. package/dist/types/types/index.d.ts +170 -0
  129. package/dist/types/types/index.d.ts.map +1 -0
  130. package/dist/types/utils/crypto.d.ts +20 -0
  131. package/dist/types/utils/crypto.d.ts.map +1 -0
  132. package/dist/types/utils/keys.d.ts +15 -0
  133. package/dist/types/utils/keys.d.ts.map +1 -0
  134. package/dist/types/utils/sleep.d.ts +13 -0
  135. package/dist/types/utils/sleep.d.ts.map +1 -0
  136. package/package.json +140 -0
@@ -0,0 +1,73 @@
1
+ /**
2
+ * Prisma Storage Adapter for @periodic/vanadium
3
+ *
4
+ * Peer dependency: "@prisma/client" >= 5.0.0
5
+ * Import: import { createPrismaAdapter } from '@periodic/vanadium/adapters/prisma'
6
+ *
7
+ * Required Prisma schema model:
8
+ * ```prisma
9
+ * model VanadiumRecord {
10
+ * key String @id
11
+ * status String
12
+ * result Json?
13
+ * payloadHash String?
14
+ * ownerToken String?
15
+ * attempts Int @default(0)
16
+ * createdAt BigInt
17
+ * updatedAt BigInt
18
+ * expiresAt BigInt?
19
+ *
20
+ * @@map("vanadium_records")
21
+ * }
22
+ * ```
23
+ */
24
+ import type { StorageAdapter, StoredRecord } from '../../types/index.js';
25
+ interface PrismaClientLike {
26
+ $transaction<T>(fn: (tx: PrismaClientLike) => Promise<T>): Promise<T>;
27
+ [key: string]: any;
28
+ }
29
+ export interface PrismaAdapterOptions {
30
+ /** PrismaClient instance */
31
+ prisma: PrismaClientLike;
32
+ /** Model name as it appears on PrismaClient (default: "vanadiumRecord") */
33
+ modelName?: string;
34
+ /** Use interactive transactions (default: true) */
35
+ useInteractiveTransactions?: boolean;
36
+ /** Clock function for deterministic testing */
37
+ clock?: () => number;
38
+ }
39
+ export declare class PrismaAdapter implements StorageAdapter {
40
+ readonly name = "prisma";
41
+ private readonly prisma;
42
+ private readonly modelName;
43
+ private readonly useTransactions;
44
+ private readonly clock;
45
+ constructor(options: PrismaAdapterOptions);
46
+ private get model();
47
+ get<T = unknown>(key: string): Promise<StoredRecord<T> | null>;
48
+ set<T = unknown>(key: string, value: StoredRecord<T>, ttlMs?: number): Promise<void>;
49
+ delete(key: string): Promise<void>;
50
+ compareAndSet<T = unknown>(key: string, expectedOwnerToken: string, newValue: StoredRecord<T>, ttlMs?: number): Promise<boolean>;
51
+ capabilities(): {
52
+ transactions: boolean;
53
+ cas: boolean;
54
+ ttl: boolean;
55
+ advisoryLocks: boolean;
56
+ };
57
+ private _rowToRecord;
58
+ }
59
+ /**
60
+ * Create a Prisma storage adapter.
61
+ *
62
+ * @example
63
+ * ```ts
64
+ * import { PrismaClient } from '@prisma/client';
65
+ * import { createPrismaAdapter } from '@periodic/vanadium/adapters/prisma';
66
+ *
67
+ * const prisma = new PrismaClient();
68
+ * const adapter = createPrismaAdapter({ prisma });
69
+ * ```
70
+ */
71
+ export declare function createPrismaAdapter(options: PrismaAdapterOptions): PrismaAdapter;
72
+ export {};
73
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../../src/adapters/prisma/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;GAsBG;AAEH,OAAO,KAAK,EAAE,cAAc,EAAE,YAAY,EAAE,MAAM,sBAAsB,CAAC;AAczE,UAAU,gBAAgB;IACxB,YAAY,CAAC,CAAC,EAAE,EAAE,EAAE,CAAC,EAAE,EAAE,gBAAgB,KAAK,OAAO,CAAC,CAAC,CAAC,GAAG,OAAO,CAAC,CAAC,CAAC,CAAC;IACtE,CAAC,GAAG,EAAE,MAAM,GAAG,GAAG,CAAC;CACpB;AAiBD,MAAM,WAAW,oBAAoB;IACnC,4BAA4B;IAC5B,MAAM,EAAE,gBAAgB,CAAC;IACzB,2EAA2E;IAC3E,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,mDAAmD;IACnD,0BAA0B,CAAC,EAAE,OAAO,CAAC;IACrC,+CAA+C;IAC/C,KAAK,CAAC,EAAE,MAAM,MAAM,CAAC;CACtB;AAID,qBAAa,aAAc,YAAW,cAAc;IAClD,SAAgB,IAAI,YAAY;IAEhC,OAAO,CAAC,QAAQ,CAAC,MAAM,CAAmB;IAC1C,OAAO,CAAC,QAAQ,CAAC,SAAS,CAAS;IACnC,OAAO,CAAC,QAAQ,CAAC,eAAe,CAAU;IAC1C,OAAO,CAAC,QAAQ,CAAC,KAAK,CAAe;gBAEzB,OAAO,EAAE,oBAAoB;IAczC,OAAO,KAAK,KAAK,GAUhB;IAIK,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;IAwCpF,MAAM,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;IAWlC,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;IA8BnB,YAAY;;;;;;IAWZ,OAAO,CAAC,YAAY;CAarB;AAID;;;;;;;;;;;GAWG;AACH,wBAAgB,mBAAmB,CAAC,OAAO,EAAE,oBAAoB,GAAG,aAAa,CAEhF"}
@@ -0,0 +1,77 @@
1
+ /**
2
+ * Redis Storage Adapter for @periodic/vanadium
3
+ *
4
+ * Peer dependency: "redis" >= 4.0.0
5
+ * Import: import { createRedisAdapter } from '@periodic/vanadium/adapters/redis'
6
+ *
7
+ * The caller is responsible for creating and connecting the Redis client.
8
+ * This adapter NEVER creates its own connection.
9
+ */
10
+ import type { StorageAdapter, StoredRecord } from '../../types/index.js';
11
+ type RedisClientLike = {
12
+ set(key: string, value: string, options?: any): Promise<string | null>;
13
+ get(key: string): Promise<string | null>;
14
+ del(key: string | string[]): Promise<number>;
15
+ eval(script: string, options: {
16
+ keys: string[];
17
+ arguments: string[];
18
+ }): Promise<any>;
19
+ evalSha?(sha: string, options: {
20
+ keys: string[];
21
+ arguments: string[];
22
+ }): Promise<any>;
23
+ scriptLoad?(script: string): Promise<string>;
24
+ };
25
+ export interface RedisAdapterOptions {
26
+ /** Pre-connected Redis client (redis >= 4.0.0) */
27
+ client: RedisClientLike;
28
+ /** Key prefix (default: "vanadium:") */
29
+ keyPrefix?: string;
30
+ /** Optional namespace segment */
31
+ namespace?: string;
32
+ /** Use Lua scripts for atomic compare-and-set (default: true) */
33
+ useLua?: boolean;
34
+ /** Clock function for deterministic testing */
35
+ clock?: () => number;
36
+ }
37
+ export declare class RedisAdapter implements StorageAdapter {
38
+ readonly name = "redis";
39
+ private readonly client;
40
+ private readonly keyPrefix;
41
+ private readonly namespace;
42
+ private readonly useLua;
43
+ private readonly clock;
44
+ constructor(options: RedisAdapterOptions);
45
+ get<T = unknown>(key: string): Promise<StoredRecord<T> | null>;
46
+ set<T = unknown>(key: string, value: StoredRecord<T>, ttlMs?: number): Promise<void>;
47
+ delete(key: string): Promise<void>;
48
+ compareAndSet<T = unknown>(key: string, expectedOwnerToken: string, newValue: StoredRecord<T>, ttlMs?: number): Promise<boolean>;
49
+ /**
50
+ * Safe owner-only delete using Lua. Use in lock release.
51
+ */
52
+ safeDelete(key: string, ownerToken: string): Promise<boolean>;
53
+ capabilities(): {
54
+ transactions: boolean;
55
+ cas: boolean;
56
+ ttl: boolean;
57
+ advisoryLocks: boolean;
58
+ };
59
+ private _buildKey;
60
+ }
61
+ /**
62
+ * Create a Redis storage adapter.
63
+ *
64
+ * @example
65
+ * ```ts
66
+ * import { createClient } from 'redis';
67
+ * import { createRedisAdapter } from '@periodic/vanadium/adapters/redis';
68
+ *
69
+ * const client = createClient({ url: process.env.REDIS_URL });
70
+ * await client.connect();
71
+ *
72
+ * const adapter = createRedisAdapter({ client, namespace: 'payments' });
73
+ * ```
74
+ */
75
+ export declare function createRedisAdapter(options: RedisAdapterOptions): RedisAdapter;
76
+ export {};
77
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../../src/adapters/redis/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AAEH,OAAO,KAAK,EAAE,cAAc,EAAE,YAAY,EAAE,MAAM,sBAAsB,CAAC;AAQzE,KAAK,eAAe,GAAG;IACrB,GAAG,CAAC,GAAG,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,EAAE,OAAO,CAAC,EAAE,GAAG,GAAG,OAAO,CAAC,MAAM,GAAG,IAAI,CAAC,CAAC;IACvE,GAAG,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,GAAG,IAAI,CAAC,CAAC;IACzC,GAAG,CAAC,GAAG,EAAE,MAAM,GAAG,MAAM,EAAE,GAAG,OAAO,CAAC,MAAM,CAAC,CAAC;IAC7C,IAAI,CAAC,MAAM,EAAE,MAAM,EAAE,OAAO,EAAE;QAAE,IAAI,EAAE,MAAM,EAAE,CAAC;QAAC,SAAS,EAAE,MAAM,EAAE,CAAA;KAAE,GAAG,OAAO,CAAC,GAAG,CAAC,CAAC;IACrF,OAAO,CAAC,CAAC,GAAG,EAAE,MAAM,EAAE,OAAO,EAAE;QAAE,IAAI,EAAE,MAAM,EAAE,CAAC;QAAC,SAAS,EAAE,MAAM,EAAE,CAAA;KAAE,GAAG,OAAO,CAAC,GAAG,CAAC,CAAC;IACtF,UAAU,CAAC,CAAC,MAAM,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC,CAAC;CAC9C,CAAC;AAKF,MAAM,WAAW,mBAAmB;IAClC,kDAAkD;IAClD,MAAM,EAAE,eAAe,CAAC;IACxB,wCAAwC;IACxC,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,iCAAiC;IACjC,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,iEAAiE;IACjE,MAAM,CAAC,EAAE,OAAO,CAAC;IACjB,+CAA+C;IAC/C,KAAK,CAAC,EAAE,MAAM,MAAM,CAAC;CACtB;AA8BD,qBAAa,YAAa,YAAW,cAAc;IACjD,SAAgB,IAAI,WAAW;IAE/B,OAAO,CAAC,QAAQ,CAAC,MAAM,CAAkB;IACzC,OAAO,CAAC,QAAQ,CAAC,SAAS,CAAS;IACnC,OAAO,CAAC,QAAQ,CAAC,SAAS,CAAS;IACnC,OAAO,CAAC,QAAQ,CAAC,MAAM,CAAU;IACjC,OAAO,CAAC,QAAQ,CAAC,KAAK,CAAe;gBAEzB,OAAO,EAAE,mBAAmB;IAalC,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;IAoBpF,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;IA+BnB;;OAEG;IACG,UAAU,CAAC,GAAG,EAAE,MAAM,EAAE,UAAU,EAAE,MAAM,GAAG,OAAO,CAAC,OAAO,CAAC;IAenE,YAAY;;;;;;IAWZ,OAAO,CAAC,SAAS;CAGlB;AAID;;;;;;;;;;;;;GAaG;AACH,wBAAgB,kBAAkB,CAAC,OAAO,EAAE,mBAAmB,GAAG,YAAY,CAE7E"}
@@ -0,0 +1,41 @@
1
+ import type { CleanupOptions } from '../types/index.js';
2
+ /**
3
+ * Optional background cleanup engine.
4
+ *
5
+ * Scans for orphaned IN_PROGRESS records and removes or marks them FAILED.
6
+ * Does NOT break distributed semantics — TTL handles the authoritative expiry.
7
+ * This is purely a maintenance utility.
8
+ *
9
+ * @example
10
+ * ```ts
11
+ * const cleanup = createCleanupEngine({ adapter, intervalMs: 30_000 });
12
+ * cleanup.start();
13
+ * process.on('SIGTERM', () => cleanup.stop());
14
+ * ```
15
+ */
16
+ export declare class CleanupEngine {
17
+ private intervalHandle;
18
+ private running;
19
+ private readonly adapter;
20
+ private readonly intervalMs;
21
+ private readonly staleThresholdMs;
22
+ private readonly deleteOnClean;
23
+ private readonly clock;
24
+ constructor(options: CleanupOptions);
25
+ /**
26
+ * Start the cleanup timer.
27
+ */
28
+ start(): void;
29
+ /**
30
+ * Stop the cleanup timer gracefully.
31
+ */
32
+ stop(): void;
33
+ /**
34
+ * Run a single cleanup pass manually (useful for testing).
35
+ */
36
+ runOnce(keysToCheck: string[]): Promise<number>;
37
+ private _runCleanup;
38
+ private _cleanKeys;
39
+ }
40
+ export declare function createCleanupEngine(options: CleanupOptions): CleanupEngine;
41
+ //# sourceMappingURL=engine.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"engine.d.ts","sourceRoot":"","sources":["../../../src/cleanup/engine.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,cAAc,EAAkB,MAAM,mBAAmB,CAAC;AAOxE;;;;;;;;;;;;;GAaG;AACH,qBAAa,aAAa;IACxB,OAAO,CAAC,cAAc,CAA+C;IACrE,OAAO,CAAC,OAAO,CAAS;IAExB,OAAO,CAAC,QAAQ,CAAC,OAAO,CAAiB;IACzC,OAAO,CAAC,QAAQ,CAAC,UAAU,CAAS;IACpC,OAAO,CAAC,QAAQ,CAAC,gBAAgB,CAAS;IAC1C,OAAO,CAAC,QAAQ,CAAC,aAAa,CAAU;IACxC,OAAO,CAAC,QAAQ,CAAC,KAAK,CAAe;gBAEzB,OAAO,EAAE,cAAc;IAQnC;;OAEG;IACH,KAAK,IAAI,IAAI;IAab;;OAEG;IACH,IAAI,IAAI,IAAI;IAQZ;;OAEG;IACG,OAAO,CAAC,WAAW,EAAE,MAAM,EAAE,GAAG,OAAO,CAAC,MAAM,CAAC;YAMvC,WAAW;YAOX,UAAU;CAwBzB;AAID,wBAAgB,mBAAmB,CAAC,OAAO,EAAE,cAAc,GAAG,aAAa,CAE1E"}
@@ -0,0 +1,28 @@
1
+ /**
2
+ * Local concurrency guard — prevents redundant concurrent calls to the
3
+ * distributed adapter for the same key within a single process.
4
+ *
5
+ * This is a performance optimization ONLY. It does NOT replace the
6
+ * distributed guarantee provided by the storage adapter.
7
+ */
8
+ export declare class ConcurrencyGuard {
9
+ private readonly inFlight;
10
+ /**
11
+ * If a promise for this key is already in-flight, return it.
12
+ * Otherwise, register the new promise and clean up when it settles.
13
+ */
14
+ wrap<T>(key: string, fn: () => Promise<T>): Promise<T>;
15
+ /**
16
+ * Check if a key is currently in-flight locally.
17
+ */
18
+ isInFlight(key: string): boolean;
19
+ /**
20
+ * Return the number of keys currently tracked.
21
+ */
22
+ size(): number;
23
+ /**
24
+ * Clear all tracked keys (for testing / cleanup).
25
+ */
26
+ clear(): void;
27
+ }
28
+ //# sourceMappingURL=concurrencyGuard.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"concurrencyGuard.d.ts","sourceRoot":"","sources":["../../../src/core/concurrencyGuard.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AACH,qBAAa,gBAAgB;IAC3B,OAAO,CAAC,QAAQ,CAAC,QAAQ,CAAuC;IAEhE;;;OAGG;IACH,IAAI,CAAC,CAAC,EAAE,GAAG,EAAE,MAAM,EAAE,EAAE,EAAE,MAAM,OAAO,CAAC,CAAC,CAAC,GAAG,OAAO,CAAC,CAAC,CAAC;IActD;;OAEG;IACH,UAAU,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO;IAIhC;;OAEG;IACH,IAAI,IAAI,MAAM;IAId;;OAEG;IACH,KAAK,IAAI,IAAI;CAGd"}
@@ -0,0 +1,13 @@
1
+ import type { VanadiumMetrics } from '../types/index.js';
2
+ /**
3
+ * Mutable metrics container. One instance per engine.
4
+ * Never uses global state.
5
+ */
6
+ export declare class MetricsStore {
7
+ private data;
8
+ increment(key: keyof VanadiumMetrics, amount?: number): void;
9
+ decrement(key: keyof VanadiumMetrics, amount?: number): void;
10
+ get(): Readonly<VanadiumMetrics>;
11
+ reset(): void;
12
+ }
13
+ //# sourceMappingURL=metrics.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"metrics.d.ts","sourceRoot":"","sources":["../../../src/core/metrics.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,mBAAmB,CAAC;AAIzD;;;GAGG;AACH,qBAAa,YAAY;IACvB,OAAO,CAAC,IAAI,CAUV;IAEF,SAAS,CAAC,GAAG,EAAE,MAAM,eAAe,EAAE,MAAM,SAAI,GAAG,IAAI;IAIvD,SAAS,CAAC,GAAG,EAAE,MAAM,eAAe,EAAE,MAAM,SAAI,GAAG,IAAI;IAIvD,GAAG,IAAI,QAAQ,CAAC,eAAe,CAAC;IAIhC,KAAK,IAAI,IAAI;CAKd"}
@@ -0,0 +1,20 @@
1
+ import type { ExecutionStatus } from '../types/index.js';
2
+ /**
3
+ * Assert that a transition from one execution status to another is valid.
4
+ * Throws a VanadiumError(STATE_TRANSITION_ERROR) on illegal transitions.
5
+ */
6
+ export declare function assertValidTransition(from: ExecutionStatus, to: ExecutionStatus, key: string, adapterName: string, clock?: () => number): void;
7
+ /**
8
+ * Check if a transition is valid without throwing.
9
+ */
10
+ export declare function isValidTransition(from: ExecutionStatus, to: ExecutionStatus): boolean;
11
+ /**
12
+ * Determine whether a stored record has expired based on current time.
13
+ */
14
+ export declare function isExpired(expiresAt: number | undefined, clock?: () => number): boolean;
15
+ /**
16
+ * Determine whether an IN_PROGRESS record can be taken over.
17
+ * Requires: status is IN_PROGRESS AND expiresAt has passed.
18
+ */
19
+ export declare function canTakeover(status: ExecutionStatus, expiresAt: number | undefined, clock?: () => number): boolean;
20
+ //# sourceMappingURL=stateMachine.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"stateMachine.d.ts","sourceRoot":"","sources":["../../../src/core/stateMachine.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,mBAAmB,CAAC;AAazD;;;GAGG;AACH,wBAAgB,qBAAqB,CACnC,IAAI,EAAE,eAAe,EACrB,EAAE,EAAE,eAAe,EACnB,GAAG,EAAE,MAAM,EACX,WAAW,EAAE,MAAM,EACnB,KAAK,GAAE,MAAM,MAAiB,GAC7B,IAAI,CAKN;AAED;;GAEG;AACH,wBAAgB,iBAAiB,CAAC,IAAI,EAAE,eAAe,EAAE,EAAE,EAAE,eAAe,GAAG,OAAO,CAErF;AAED;;GAEG;AACH,wBAAgB,SAAS,CAAC,SAAS,EAAE,MAAM,GAAG,SAAS,EAAE,KAAK,GAAE,MAAM,MAAiB,GAAG,OAAO,CAGhG;AAED;;;GAGG;AACH,wBAAgB,WAAW,CACzB,MAAM,EAAE,eAAe,EACvB,SAAS,EAAE,MAAM,GAAG,SAAS,EAC7B,KAAK,GAAE,MAAM,MAAiB,GAC7B,OAAO,CAET"}
@@ -0,0 +1,32 @@
1
+ import type { VanadiumErrorType } from '../types/index.js';
2
+ export interface VanadiumErrorDetails {
3
+ type: VanadiumErrorType;
4
+ key: string;
5
+ adapterName: string;
6
+ timestamp: number;
7
+ message: string;
8
+ originalError?: unknown;
9
+ attempts?: number;
10
+ payloadHash?: string;
11
+ }
12
+ export declare class VanadiumError extends Error {
13
+ readonly type: VanadiumErrorType;
14
+ readonly key: string;
15
+ readonly adapterName: string;
16
+ readonly timestamp: number;
17
+ readonly originalError?: unknown;
18
+ readonly attempts?: number;
19
+ readonly payloadHash?: string;
20
+ constructor(details: VanadiumErrorDetails);
21
+ toJSON(): Record<string, unknown>;
22
+ }
23
+ export declare function createDuplicateExecutionError(key: string, adapterName: string, clock?: () => number): VanadiumError;
24
+ export declare function createInProgressError(key: string, adapterName: string, attempts: number, clock?: () => number): VanadiumError;
25
+ export declare function createLockAcquisitionFailedError(key: string, adapterName: string, clock?: () => number): VanadiumError;
26
+ export declare function createLockTimeoutError(key: string, adapterName: string, maxWaitMs: number, clock?: () => number): VanadiumError;
27
+ export declare function createPayloadMismatchError(key: string, adapterName: string, storedHash: string | undefined, incomingHash: string, clock?: () => number): VanadiumError;
28
+ export declare function createConfigurationError(key: string, adapterName: string, message: string, clock?: () => number): VanadiumError;
29
+ export declare function createStorageError(key: string, adapterName: string, originalError: unknown, clock?: () => number): VanadiumError;
30
+ export declare function createStateTransitionError(key: string, adapterName: string, from: string, to: string, clock?: () => number): VanadiumError;
31
+ export declare function isVanadiumError(err: unknown): err is VanadiumError;
32
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/errors/index.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,mBAAmB,CAAC;AAI3D,MAAM,WAAW,oBAAoB;IACnC,IAAI,EAAE,iBAAiB,CAAC;IACxB,GAAG,EAAE,MAAM,CAAC;IACZ,WAAW,EAAE,MAAM,CAAC;IACpB,SAAS,EAAE,MAAM,CAAC;IAClB,OAAO,EAAE,MAAM,CAAC;IAChB,aAAa,CAAC,EAAE,OAAO,CAAC;IACxB,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,WAAW,CAAC,EAAE,MAAM,CAAC;CACtB;AAED,qBAAa,aAAc,SAAQ,KAAK;IACtC,SAAgB,IAAI,EAAE,iBAAiB,CAAC;IACxC,SAAgB,GAAG,EAAE,MAAM,CAAC;IAC5B,SAAgB,WAAW,EAAE,MAAM,CAAC;IACpC,SAAgB,SAAS,EAAE,MAAM,CAAC;IAClC,SAAgB,aAAa,CAAC,EAAE,OAAO,CAAC;IACxC,SAAgB,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClC,SAAgB,WAAW,CAAC,EAAE,MAAM,CAAC;gBAEzB,OAAO,EAAE,oBAAoB;IAmBzC,MAAM,IAAI,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC;CAYlC;AAID,wBAAgB,6BAA6B,CAC3C,GAAG,EAAE,MAAM,EACX,WAAW,EAAE,MAAM,EACnB,KAAK,GAAE,MAAM,MAAiB,GAC7B,aAAa,CAQf;AAED,wBAAgB,qBAAqB,CACnC,GAAG,EAAE,MAAM,EACX,WAAW,EAAE,MAAM,EACnB,QAAQ,EAAE,MAAM,EAChB,KAAK,GAAE,MAAM,MAAiB,GAC7B,aAAa,CASf;AAED,wBAAgB,gCAAgC,CAC9C,GAAG,EAAE,MAAM,EACX,WAAW,EAAE,MAAM,EACnB,KAAK,GAAE,MAAM,MAAiB,GAC7B,aAAa,CAQf;AAED,wBAAgB,sBAAsB,CACpC,GAAG,EAAE,MAAM,EACX,WAAW,EAAE,MAAM,EACnB,SAAS,EAAE,MAAM,EACjB,KAAK,GAAE,MAAM,MAAiB,GAC7B,aAAa,CAQf;AAED,wBAAgB,0BAA0B,CACxC,GAAG,EAAE,MAAM,EACX,WAAW,EAAE,MAAM,EACnB,UAAU,EAAE,MAAM,GAAG,SAAS,EAC9B,YAAY,EAAE,MAAM,EACpB,KAAK,GAAE,MAAM,MAAiB,GAC7B,aAAa,CAYf;AAED,wBAAgB,wBAAwB,CACtC,GAAG,EAAE,MAAM,EACX,WAAW,EAAE,MAAM,EACnB,OAAO,EAAE,MAAM,EACf,KAAK,GAAE,MAAM,MAAiB,GAC7B,aAAa,CAQf;AAED,wBAAgB,kBAAkB,CAChC,GAAG,EAAE,MAAM,EACX,WAAW,EAAE,MAAM,EACnB,aAAa,EAAE,OAAO,EACtB,KAAK,GAAE,MAAM,MAAiB,GAC7B,aAAa,CAUf;AAED,wBAAgB,0BAA0B,CACxC,GAAG,EAAE,MAAM,EACX,WAAW,EAAE,MAAM,EACnB,IAAI,EAAE,MAAM,EACZ,EAAE,EAAE,MAAM,EACV,KAAK,GAAE,MAAM,MAAiB,GAC7B,aAAa,CAQf;AAID,wBAAgB,eAAe,CAAC,GAAG,EAAE,OAAO,GAAG,GAAG,IAAI,aAAa,CAElE"}
@@ -0,0 +1,50 @@
1
+ /**
2
+ * Express middleware for @periodic/vanadium
3
+ *
4
+ * Automatically wraps HTTP handlers with idempotency semantics.
5
+ * Reads the Idempotency-Key header and caches successful responses.
6
+ *
7
+ * @example
8
+ * ```ts
9
+ * import express from 'express';
10
+ * import { createIdempotency, createMemoryAdapter } from '@periodic/vanadium';
11
+ * import { vanadiumMiddleware } from '@periodic/vanadium/http/express';
12
+ *
13
+ * const app = express();
14
+ * const idempotency = createIdempotency({ adapter: createMemoryAdapter(), ttlMs: 86_400_000 });
15
+ *
16
+ * app.post('/payments', vanadiumMiddleware(idempotency), async (req, res) => {
17
+ * const result = await chargeCard(req.body);
18
+ * res.json(result);
19
+ * });
20
+ * ```
21
+ */
22
+ import type { IdempotencyEngine } from '../types/index.js';
23
+ interface Request {
24
+ headers: Record<string, string | string[] | undefined>;
25
+ method: string;
26
+ path: string;
27
+ }
28
+ interface Response {
29
+ status(code: number): Response;
30
+ json(body: any): void;
31
+ set(headers: Record<string, string>): Response;
32
+ statusCode: number;
33
+ end(): void;
34
+ }
35
+ type NextFunction = (err?: any) => void;
36
+ type RequestHandler = (req: Request, res: Response, next: NextFunction) => void;
37
+ export interface VanadiumMiddlewareOptions {
38
+ /** Header name to read idempotency key from (default: "idempotency-key") */
39
+ headerName?: string;
40
+ /** If true, return 409 when key is IN_PROGRESS (default: true) */
41
+ rejectInProgress?: boolean;
42
+ /** HTTP methods to apply middleware to (default: ['POST', 'PUT', 'PATCH']) */
43
+ methods?: string[];
44
+ }
45
+ /**
46
+ * Create Express middleware that enforces idempotency on HTTP handlers.
47
+ */
48
+ export declare function vanadiumMiddleware(idempotency: IdempotencyEngine, options?: VanadiumMiddlewareOptions): RequestHandler;
49
+ export {};
50
+ //# sourceMappingURL=express.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"express.d.ts","sourceRoot":"","sources":["../../../src/http/express.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;GAoBG;AAEH,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,mBAAmB,CAAC;AAM3D,UAAU,OAAO;IACf,OAAO,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,GAAG,MAAM,EAAE,GAAG,SAAS,CAAC,CAAC;IACvD,MAAM,EAAE,MAAM,CAAC;IACf,IAAI,EAAE,MAAM,CAAC;CACd;AAED,UAAU,QAAQ;IAChB,MAAM,CAAC,IAAI,EAAE,MAAM,GAAG,QAAQ,CAAC;IAC/B,IAAI,CAAC,IAAI,EAAE,GAAG,GAAG,IAAI,CAAC;IACtB,GAAG,CAAC,OAAO,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,GAAG,QAAQ,CAAC;IAC/C,UAAU,EAAE,MAAM,CAAC;IACnB,GAAG,IAAI,IAAI,CAAC;CACb;AAED,KAAK,YAAY,GAAG,CAAC,GAAG,CAAC,EAAE,GAAG,KAAK,IAAI,CAAC;AACxC,KAAK,cAAc,GAAG,CAAC,GAAG,EAAE,OAAO,EAAE,GAAG,EAAE,QAAQ,EAAE,IAAI,EAAE,YAAY,KAAK,IAAI,CAAC;AAKhF,MAAM,WAAW,yBAAyB;IACxC,4EAA4E;IAC5E,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,kEAAkE;IAClE,gBAAgB,CAAC,EAAE,OAAO,CAAC;IAC3B,8EAA8E;IAC9E,OAAO,CAAC,EAAE,MAAM,EAAE,CAAC;CACpB;AAWD;;GAEG;AACH,wBAAgB,kBAAkB,CAChC,WAAW,EAAE,iBAAiB,EAC9B,OAAO,GAAE,yBAA8B,GACtC,cAAc,CA4DhB"}
@@ -0,0 +1,48 @@
1
+ /**
2
+ * Fastify plugin for @periodic/vanadium
3
+ *
4
+ * @example
5
+ * ```ts
6
+ * import Fastify from 'fastify';
7
+ * import { createIdempotency, createMemoryAdapter } from '@periodic/vanadium';
8
+ * import { vanadiumFastifyPlugin } from '@periodic/vanadium/http/fastify';
9
+ *
10
+ * const app = Fastify();
11
+ * const idempotency = createIdempotency({ adapter: createMemoryAdapter(), ttlMs: 86_400_000 });
12
+ *
13
+ * await app.register(vanadiumFastifyPlugin, { idempotency });
14
+ *
15
+ * app.post('/payments', async (request, reply) => {
16
+ * const result = await chargeCard(request.body);
17
+ * return result;
18
+ * });
19
+ * ```
20
+ */
21
+ import type { IdempotencyEngine } from '../types/index.js';
22
+ interface FastifyRequest {
23
+ method: string;
24
+ url: string;
25
+ headers: Record<string, string | string[] | undefined>;
26
+ }
27
+ interface FastifyReply {
28
+ code(statusCode: number): FastifyReply;
29
+ send(payload?: any): void;
30
+ statusCode: number;
31
+ sent: boolean;
32
+ }
33
+ type FastifyHook = (request: FastifyRequest, reply: FastifyReply) => Promise<void>;
34
+ interface FastifyInstance {
35
+ addHook(hookName: string, fn: FastifyHook): void;
36
+ setErrorHandler(fn: (err: any, req: FastifyRequest, reply: FastifyReply) => void): void;
37
+ }
38
+ export interface VanadiumFastifyOptions {
39
+ idempotency: IdempotencyEngine;
40
+ headerName?: string;
41
+ methods?: string[];
42
+ }
43
+ /**
44
+ * Register Vanadium idempotency as a Fastify plugin.
45
+ */
46
+ export declare function vanadiumFastifyPlugin(fastify: FastifyInstance, options: VanadiumFastifyOptions): Promise<void>;
47
+ export {};
48
+ //# sourceMappingURL=fastify.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"fastify.d.ts","sourceRoot":"","sources":["../../../src/http/fastify.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;GAmBG;AAEH,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,mBAAmB,CAAC;AAM3D,UAAU,cAAc;IACtB,MAAM,EAAE,MAAM,CAAC;IACf,GAAG,EAAE,MAAM,CAAC;IACZ,OAAO,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,GAAG,MAAM,EAAE,GAAG,SAAS,CAAC,CAAC;CACxD;AAED,UAAU,YAAY;IACpB,IAAI,CAAC,UAAU,EAAE,MAAM,GAAG,YAAY,CAAC;IACvC,IAAI,CAAC,OAAO,CAAC,EAAE,GAAG,GAAG,IAAI,CAAC;IAC1B,UAAU,EAAE,MAAM,CAAC;IACnB,IAAI,EAAE,OAAO,CAAC;CACf;AAED,KAAK,WAAW,GAAG,CAAC,OAAO,EAAE,cAAc,EAAE,KAAK,EAAE,YAAY,KAAK,OAAO,CAAC,IAAI,CAAC,CAAC;AAEnF,UAAU,eAAe;IACvB,OAAO,CAAC,QAAQ,EAAE,MAAM,EAAE,EAAE,EAAE,WAAW,GAAG,IAAI,CAAC;IACjD,eAAe,CAAC,EAAE,EAAE,CAAC,GAAG,EAAE,GAAG,EAAE,GAAG,EAAE,cAAc,EAAE,KAAK,EAAE,YAAY,KAAK,IAAI,GAAG,IAAI,CAAC;CACzF;AAKD,MAAM,WAAW,sBAAsB;IACrC,WAAW,EAAE,iBAAiB,CAAC;IAC/B,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,OAAO,CAAC,EAAE,MAAM,EAAE,CAAC;CACpB;AAID;;GAEG;AACH,wBAAsB,qBAAqB,CACzC,OAAO,EAAE,eAAe,EACxB,OAAO,EAAE,sBAAsB,GAC9B,OAAO,CAAC,IAAI,CAAC,CA0Cf"}
@@ -0,0 +1,24 @@
1
+ import type { IdempotencyOptions, VanadiumMetrics, IdempotencyEngine as IIdempotencyEngine } from '../types/index.js';
2
+ export declare class IdempotencyEngineImpl implements IIdempotencyEngine {
3
+ private readonly opts;
4
+ private readonly metrics;
5
+ private readonly guard;
6
+ constructor(options: IdempotencyOptions);
7
+ execute<T>(key: string, fn: () => Promise<T>, payload?: unknown): Promise<T>;
8
+ getMetrics(): VanadiumMetrics;
9
+ resetMetrics(): void;
10
+ private _executeInternal;
11
+ private _runFunction;
12
+ private _runHook;
13
+ }
14
+ /**
15
+ * Create a new idempotency engine instance.
16
+ *
17
+ * @example
18
+ * ```ts
19
+ * const idempotency = createIdempotency({ adapter, ttlMs: 60_000 });
20
+ * const result = await idempotency.execute('payment:123', () => chargeCard());
21
+ * ```
22
+ */
23
+ export declare function createIdempotency(options: IdempotencyOptions): IdempotencyEngineImpl;
24
+ //# sourceMappingURL=engine.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"engine.d.ts","sourceRoot":"","sources":["../../../src/idempotency/engine.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EACV,kBAAkB,EAElB,eAAe,EACf,iBAAiB,IAAI,kBAAkB,EACxC,MAAM,mBAAmB,CAAC;AAkB3B,qBAAa,qBAAsB,YAAW,kBAAkB;IAC9D,OAAO,CAAC,QAAQ,CAAC,IAAI,CAC+B;IACpD,OAAO,CAAC,QAAQ,CAAC,OAAO,CAAsB;IAC9C,OAAO,CAAC,QAAQ,CAAC,KAAK,CAA0B;gBAEpC,OAAO,EAAE,kBAAkB;IAejC,OAAO,CAAC,CAAC,EAAE,GAAG,EAAE,MAAM,EAAE,EAAE,EAAE,MAAM,OAAO,CAAC,CAAC,CAAC,EAAE,OAAO,CAAC,EAAE,OAAO,GAAG,OAAO,CAAC,CAAC,CAAC;IAKlF,UAAU,IAAI,eAAe;IAI7B,YAAY,IAAI,IAAI;YAMN,gBAAgB;YAoJhB,YAAY;YA6FZ,QAAQ;CAgBvB;AAID;;;;;;;;GAQG;AACH,wBAAgB,iBAAiB,CAAC,OAAO,EAAE,kBAAkB,GAAG,qBAAqB,CAEpF"}
@@ -0,0 +1,8 @@
1
+ export { createIdempotency } from './idempotency/engine.js';
2
+ export { createLock } from './lock/engine.js';
3
+ export { createMemoryAdapter } from './adapters/memory/index.js';
4
+ export { createCircuitBreaker } from './resilience/circuitBreaker.js';
5
+ export { createVanadiumMetrics } from './observability/metrics.js';
6
+ export { VanadiumError, isVanadiumError } from './errors/index.js';
7
+ export type { StorageAdapter, StoredRecord, IdempotencyOptions, LockOptions, VanadiumMetrics, VanadiumErrorType, IdempotencyHooks, LockHooks, CircuitBreakerState, ExecutionStatus, } from './types/index.js';
8
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/index.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,iBAAiB,EAAE,MAAM,yBAAyB,CAAC;AAC5D,OAAO,EAAE,UAAU,EAAE,MAAM,kBAAkB,CAAC;AAC9C,OAAO,EAAE,mBAAmB,EAAE,MAAM,4BAA4B,CAAC;AACjE,OAAO,EAAE,oBAAoB,EAAE,MAAM,gCAAgC,CAAC;AACtE,OAAO,EAAE,qBAAqB,EAAE,MAAM,4BAA4B,CAAC;AAGnE,OAAO,EAAE,aAAa,EAAE,eAAe,EAAE,MAAM,mBAAmB,CAAC;AAGnE,YAAY,EACV,cAAc,EACd,YAAY,EACZ,kBAAkB,EAClB,WAAW,EACX,eAAe,EACf,iBAAiB,EACjB,gBAAgB,EAChB,SAAS,EACT,mBAAmB,EACnB,eAAe,GAChB,MAAM,kBAAkB,CAAC"}
@@ -0,0 +1,28 @@
1
+ import type { LockOptions, VanadiumMetrics } from '../types/index.js';
2
+ export declare class LockEngineImpl {
3
+ private readonly opts;
4
+ private readonly metrics;
5
+ constructor(options: LockOptions);
6
+ /**
7
+ * Acquire an exclusive lock for `key`, execute `fn`, then release the lock.
8
+ * Guarantees only one concurrent execution per key across all nodes.
9
+ *
10
+ * @throws {VanadiumError} LOCK_ACQUISITION_FAILED | LOCK_TIMEOUT
11
+ */
12
+ acquire<T>(key: string, fn: () => Promise<T>): Promise<T>;
13
+ getMetrics(): Pick<VanadiumMetrics, 'totalLocksAcquired' | 'totalLockFailures'>;
14
+ private _tryAcquire;
15
+ private _safeRelease;
16
+ private _runHook;
17
+ }
18
+ /**
19
+ * Create a new distributed lock engine.
20
+ *
21
+ * @example
22
+ * ```ts
23
+ * const lock = createLock({ adapter, ttlMs: 10_000, maxWaitMs: 5_000 });
24
+ * const result = await lock.acquire('order:123', () => processOrder());
25
+ * ```
26
+ */
27
+ export declare function createLock(options: LockOptions): LockEngineImpl;
28
+ //# sourceMappingURL=engine.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"engine.d.ts","sourceRoot":"","sources":["../../../src/lock/engine.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,WAAW,EAAE,eAAe,EAAgB,MAAM,mBAAmB,CAAC;AAgBpF,qBAAa,cAAc;IACzB,OAAO,CAAC,QAAQ,CAAC,IAAI,CAAoE;IACzF,OAAO,CAAC,QAAQ,CAAC,OAAO,CAAsB;gBAElC,OAAO,EAAE,WAAW;IAahC;;;;;OAKG;IACG,OAAO,CAAC,CAAC,EAAE,GAAG,EAAE,MAAM,EAAE,EAAE,EAAE,MAAM,OAAO,CAAC,CAAC,CAAC,GAAG,OAAO,CAAC,CAAC,CAAC;IA+D/D,UAAU,IAAI,IAAI,CAAC,eAAe,EAAE,oBAAoB,GAAG,mBAAmB,CAAC;YAUjE,WAAW;YA0DX,YAAY;YAgBZ,QAAQ;CAgBvB;AAID;;;;;;;;GAQG;AACH,wBAAgB,UAAU,CAAC,OAAO,EAAE,WAAW,GAAG,cAAc,CAE/D"}
@@ -0,0 +1,45 @@
1
+ /**
2
+ * OpenTelemetry-compatible metrics for @periodic/vanadium
3
+ *
4
+ * Peer dependency (optional): "@opentelemetry/api" >= 1.0.0
5
+ *
6
+ * If OpenTelemetry is not configured, all metrics gracefully no-op.
7
+ *
8
+ * @example
9
+ * ```ts
10
+ * import { VanadiumInstrumentation } from '@periodic/vanadium';
11
+ * // or in your setup:
12
+ * import { createVanadiumMetrics } from '@periodic/vanadium/observability';
13
+ *
14
+ * const metrics = createVanadiumMetrics('my-service');
15
+ * metrics.recordExecution('payment:123', 'payments', 42);
16
+ * ```
17
+ */
18
+ interface OtelMeter {
19
+ createCounter(name: string, options?: any): {
20
+ add(value: number, attributes?: any): void;
21
+ };
22
+ createHistogram(name: string, options?: any): {
23
+ record(value: number, attributes?: any): void;
24
+ };
25
+ createUpDownCounter(name: string, options?: any): {
26
+ add(value: number, attributes?: any): void;
27
+ };
28
+ }
29
+ export interface VanadiumMetricsInstruments {
30
+ recordExecution(key: string, adapterName: string, durationMs: number): void;
31
+ recordDuplicate(key: string, adapterName: string): void;
32
+ recordTakeover(key: string, adapterName: string): void;
33
+ recordStorageError(key: string, adapterName: string): void;
34
+ recordPayloadMismatch(key: string, adapterName: string): void;
35
+ recordLockAcquired(key: string, adapterName: string, durationMs: number): void;
36
+ recordLockFailed(key: string, adapterName: string, reason: string): void;
37
+ setInProgressCount(count: number, adapterName: string): void;
38
+ }
39
+ /**
40
+ * Create metrics instruments. Pass an OTel Meter if you have one,
41
+ * otherwise all instruments are graceful no-ops.
42
+ */
43
+ export declare function createVanadiumMetrics(meter?: OtelMeter, scopeName?: string): VanadiumMetricsInstruments;
44
+ export {};
45
+ //# sourceMappingURL=metrics.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"metrics.d.ts","sourceRoot":"","sources":["../../../src/observability/metrics.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;GAgBG;AAKH,UAAU,SAAS;IACjB,aAAa,CAAC,IAAI,EAAE,MAAM,EAAE,OAAO,CAAC,EAAE,GAAG,GAAG;QAAE,GAAG,CAAC,KAAK,EAAE,MAAM,EAAE,UAAU,CAAC,EAAE,GAAG,GAAG,IAAI,CAAA;KAAE,CAAC;IAC3F,eAAe,CAAC,IAAI,EAAE,MAAM,EAAE,OAAO,CAAC,EAAE,GAAG,GAAG;QAAE,MAAM,CAAC,KAAK,EAAE,MAAM,EAAE,UAAU,CAAC,EAAE,GAAG,GAAG,IAAI,CAAA;KAAE,CAAC;IAChG,mBAAmB,CAAC,IAAI,EAAE,MAAM,EAAE,OAAO,CAAC,EAAE,GAAG,GAAG;QAAE,GAAG,CAAC,KAAK,EAAE,MAAM,EAAE,UAAU,CAAC,EAAE,GAAG,GAAG,IAAI,CAAA;KAAE,CAAC;CAClG;AAWD,MAAM,WAAW,0BAA0B;IACzC,eAAe,CAAC,GAAG,EAAE,MAAM,EAAE,WAAW,EAAE,MAAM,EAAE,UAAU,EAAE,MAAM,GAAG,IAAI,CAAC;IAC5E,eAAe,CAAC,GAAG,EAAE,MAAM,EAAE,WAAW,EAAE,MAAM,GAAG,IAAI,CAAC;IACxD,cAAc,CAAC,GAAG,EAAE,MAAM,EAAE,WAAW,EAAE,MAAM,GAAG,IAAI,CAAC;IACvD,kBAAkB,CAAC,GAAG,EAAE,MAAM,EAAE,WAAW,EAAE,MAAM,GAAG,IAAI,CAAC;IAC3D,qBAAqB,CAAC,GAAG,EAAE,MAAM,EAAE,WAAW,EAAE,MAAM,GAAG,IAAI,CAAC;IAC9D,kBAAkB,CAAC,GAAG,EAAE,MAAM,EAAE,WAAW,EAAE,MAAM,EAAE,UAAU,EAAE,MAAM,GAAG,IAAI,CAAC;IAC/E,gBAAgB,CAAC,GAAG,EAAE,MAAM,EAAE,WAAW,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,GAAG,IAAI,CAAC;IACzE,kBAAkB,CAAC,KAAK,EAAE,MAAM,EAAE,WAAW,EAAE,MAAM,GAAG,IAAI,CAAC;CAC9D;AAED;;;GAGG;AACH,wBAAgB,qBAAqB,CACnC,KAAK,CAAC,EAAE,SAAS,EACjB,SAAS,SAAsB,GAC9B,0BAA0B,CAiF5B"}
@@ -0,0 +1,48 @@
1
+ import type { CircuitBreakerState, CircuitBreakerOptions } from '../types/index.js';
2
+ import type { StorageAdapter, StoredRecord } from '../types/index.js';
3
+ /**
4
+ * Wraps a StorageAdapter with a circuit breaker pattern.
5
+ * Protects the application from repeated storage failures.
6
+ *
7
+ * States:
8
+ * - CLOSED: Normal operation. Failures counted.
9
+ * - OPEN: Storage blocked. All calls throw immediately.
10
+ * - HALF_OPEN: Probe mode. Limited calls allowed to test recovery.
11
+ */
12
+ export declare class CircuitBreakerAdapter implements StorageAdapter {
13
+ readonly name: string;
14
+ private state;
15
+ private failureCount;
16
+ private successCount;
17
+ private lastFailureAt;
18
+ private halfOpenCalls;
19
+ private readonly adapter;
20
+ private readonly failureThreshold;
21
+ private readonly resetTimeoutMs;
22
+ private readonly halfOpenMaxCalls;
23
+ private readonly clock;
24
+ constructor(adapter: StorageAdapter, options?: CircuitBreakerOptions, clock?: () => number);
25
+ get<T = unknown>(key: string): Promise<StoredRecord<T> | null>;
26
+ set<T = unknown>(key: string, value: StoredRecord<T>, ttlMs?: number): Promise<void>;
27
+ delete(key: string): Promise<void>;
28
+ compareAndSet<T = unknown>(key: string, expectedOwnerToken: string, newValue: StoredRecord<T>, ttlMs?: number): Promise<boolean>;
29
+ getState(): CircuitBreakerState;
30
+ reset(): void;
31
+ private _execute;
32
+ private _onSuccess;
33
+ private _onFailure;
34
+ private _checkReset;
35
+ }
36
+ /**
37
+ * Wrap any StorageAdapter with a circuit breaker.
38
+ *
39
+ * @example
40
+ * ```ts
41
+ * const protectedAdapter = createCircuitBreaker(redisAdapter, {
42
+ * failureThreshold: 3,
43
+ * resetTimeoutMs: 15_000,
44
+ * });
45
+ * ```
46
+ */
47
+ export declare function createCircuitBreaker(adapter: StorageAdapter, options?: CircuitBreakerOptions, clock?: () => number): CircuitBreakerAdapter;
48
+ //# sourceMappingURL=circuitBreaker.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"circuitBreaker.d.ts","sourceRoot":"","sources":["../../../src/resilience/circuitBreaker.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,mBAAmB,EAAE,qBAAqB,EAAE,MAAM,mBAAmB,CAAC;AACpF,OAAO,KAAK,EAAE,cAAc,EAAE,YAAY,EAAE,MAAM,mBAAmB,CAAC;AAUtE;;;;;;;;GAQG;AACH,qBAAa,qBAAsB,YAAW,cAAc;IAC1D,SAAgB,IAAI,EAAE,MAAM,CAAC;IAE7B,OAAO,CAAC,KAAK,CAAiC;IAC9C,OAAO,CAAC,YAAY,CAAK;IACzB,OAAO,CAAC,YAAY,CAAK;IACzB,OAAO,CAAC,aAAa,CAAS;IAC9B,OAAO,CAAC,aAAa,CAAK;IAE1B,OAAO,CAAC,QAAQ,CAAC,OAAO,CAAiB;IACzC,OAAO,CAAC,QAAQ,CAAC,gBAAgB,CAAS;IAC1C,OAAO,CAAC,QAAQ,CAAC,cAAc,CAAS;IACxC,OAAO,CAAC,QAAQ,CAAC,gBAAgB,CAAS;IAC1C,OAAO,CAAC,QAAQ,CAAC,KAAK,CAAe;gBAGnC,OAAO,EAAE,cAAc,EACvB,OAAO,GAAE,qBAA0B,EACnC,KAAK,GAAE,MAAM,MAAiB;IAa1B,GAAG,CAAC,CAAC,GAAG,OAAO,EAAE,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC,YAAY,CAAC,CAAC,CAAC,GAAG,IAAI,CAAC;IAI9D,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;IAIpF,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;IAiBnB,QAAQ,IAAI,mBAAmB;IAK/B,KAAK,IAAI,IAAI;YASC,QAAQ;IAsCtB,OAAO,CAAC,UAAU;IASlB,OAAO,CAAC,UAAU;IAWlB,OAAO,CAAC,WAAW;CASpB;AAID;;;;;;;;;;GAUG;AACH,wBAAgB,oBAAoB,CAClC,OAAO,EAAE,cAAc,EACvB,OAAO,CAAC,EAAE,qBAAqB,EAC/B,KAAK,CAAC,EAAE,MAAM,MAAM,GACnB,qBAAqB,CAEvB"}