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.
Files changed (139) hide show
  1. package/CHANGELOG.md +234 -0
  2. package/MIGRATION.md +141 -0
  3. package/README.md +334 -428
  4. package/SECURITY.md +56 -0
  5. package/dist/core/act.cjs +630 -126
  6. package/dist/core/act.d.ts +6 -84
  7. package/dist/core/act.d.ts.map +1 -1
  8. package/dist/core/act.js +630 -129
  9. package/dist/core/executor.cjs +1 -26
  10. package/dist/core/executor.d.ts +2 -38
  11. package/dist/core/executor.d.ts.map +1 -1
  12. package/dist/core/executor.js +1 -27
  13. package/dist/core/health.cjs +163 -0
  14. package/dist/core/health.d.ts +33 -0
  15. package/dist/core/health.d.ts.map +1 -0
  16. package/dist/core/health.js +150 -0
  17. package/dist/core/shutdown.cjs +71 -0
  18. package/dist/core/shutdown.d.ts +5 -0
  19. package/dist/core/shutdown.d.ts.map +1 -0
  20. package/dist/core/shutdown.js +65 -0
  21. package/dist/core/tenant.cjs +113 -0
  22. package/dist/core/tenant.d.ts +24 -0
  23. package/dist/core/tenant.d.ts.map +1 -0
  24. package/dist/core/tenant.js +109 -0
  25. package/dist/errors.cjs +149 -0
  26. package/dist/errors.d.ts +94 -0
  27. package/dist/errors.d.ts.map +1 -0
  28. package/dist/errors.js +134 -0
  29. package/dist/index.cjs +51 -8
  30. package/dist/index.d.ts +22 -3
  31. package/dist/index.d.ts.map +1 -1
  32. package/dist/index.js +14 -6
  33. package/dist/observability.cjs +6 -0
  34. package/dist/observability.d.ts +86 -0
  35. package/dist/observability.d.ts.map +1 -0
  36. package/dist/observability.js +3 -0
  37. package/dist/policies/bulkhead.cjs +137 -0
  38. package/dist/policies/bulkhead.d.ts +3 -0
  39. package/dist/policies/bulkhead.d.ts.map +1 -0
  40. package/dist/policies/bulkhead.js +134 -0
  41. package/dist/policies/cache.cjs +84 -67
  42. package/dist/policies/cache.d.ts +0 -33
  43. package/dist/policies/cache.d.ts.map +1 -1
  44. package/dist/policies/cache.js +84 -68
  45. package/dist/policies/circuitBreaker.cjs +179 -0
  46. package/dist/policies/circuitBreaker.d.ts +3 -0
  47. package/dist/policies/circuitBreaker.d.ts.map +1 -0
  48. package/dist/policies/circuitBreaker.js +176 -0
  49. package/dist/policies/dedupe.cjs +46 -64
  50. package/dist/policies/dedupe.d.ts +0 -41
  51. package/dist/policies/dedupe.d.ts.map +1 -1
  52. package/dist/policies/dedupe.js +46 -65
  53. package/dist/policies/noop.cjs +6 -0
  54. package/dist/policies/noop.d.ts +3 -0
  55. package/dist/policies/noop.d.ts.map +1 -0
  56. package/dist/policies/noop.js +3 -0
  57. package/dist/policies/rateLimit.cjs +45 -0
  58. package/dist/policies/rateLimit.d.ts +3 -0
  59. package/dist/policies/rateLimit.d.ts.map +1 -0
  60. package/dist/policies/rateLimit.js +42 -0
  61. package/dist/policies/retry.cjs +129 -57
  62. package/dist/policies/retry.d.ts +0 -27
  63. package/dist/policies/retry.d.ts.map +1 -1
  64. package/dist/policies/retry.js +129 -58
  65. package/dist/policies/timeout.cjs +47 -103
  66. package/dist/policies/timeout.d.ts +2 -46
  67. package/dist/policies/timeout.d.ts.map +1 -1
  68. package/dist/policies/timeout.js +46 -102
  69. package/dist/stores/base.cjs +0 -10
  70. package/dist/stores/base.d.ts +4 -67
  71. package/dist/stores/base.d.ts.map +1 -1
  72. package/dist/stores/base.js +0 -11
  73. package/dist/stores/memory.cjs +147 -87
  74. package/dist/stores/memory.d.ts +11 -59
  75. package/dist/stores/memory.d.ts.map +1 -1
  76. package/dist/stores/memory.js +146 -88
  77. package/dist/testing/index.cjs +56 -0
  78. package/dist/testing/index.d.ts +6 -0
  79. package/dist/testing/index.d.ts.map +1 -0
  80. package/dist/testing/index.js +52 -0
  81. package/dist/types/index.cjs +0 -1
  82. package/dist/types/index.d.ts +62 -179
  83. package/dist/types/index.d.ts.map +1 -1
  84. package/dist/types/index.js +0 -2
  85. package/dist/utils/abort.cjs +46 -73
  86. package/dist/utils/abort.d.ts +4 -51
  87. package/dist/utils/abort.d.ts.map +1 -1
  88. package/dist/utils/abort.js +46 -74
  89. package/dist/utils/abortPool.cjs +25 -0
  90. package/dist/utils/abortPool.d.ts +4 -0
  91. package/dist/utils/abortPool.d.ts.map +1 -0
  92. package/dist/utils/abortPool.js +20 -0
  93. package/dist/utils/backoff.cjs +8 -15
  94. package/dist/utils/backoff.d.ts +0 -11
  95. package/dist/utils/backoff.d.ts.map +1 -1
  96. package/dist/utils/backoff.js +8 -16
  97. package/dist/utils/decorator.cjs +44 -0
  98. package/dist/utils/decorator.d.ts +3 -0
  99. package/dist/utils/decorator.d.ts.map +1 -0
  100. package/dist/utils/decorator.js +41 -0
  101. package/dist/utils/key.cjs +45 -0
  102. package/dist/utils/key.d.ts +2 -0
  103. package/dist/utils/key.d.ts.map +1 -0
  104. package/dist/utils/key.js +42 -0
  105. package/dist/utils/limits.cjs +22 -0
  106. package/dist/utils/limits.d.ts +21 -0
  107. package/dist/utils/limits.d.ts.map +1 -0
  108. package/dist/utils/limits.js +19 -0
  109. package/dist/utils/safeCall.cjs +28 -0
  110. package/dist/utils/safeCall.d.ts +2 -0
  111. package/dist/utils/safeCall.d.ts.map +1 -0
  112. package/dist/utils/safeCall.js +25 -0
  113. package/dist/utils/sanitize.cjs +50 -0
  114. package/dist/utils/sanitize.d.ts +3 -0
  115. package/dist/utils/sanitize.d.ts.map +1 -0
  116. package/dist/utils/sanitize.js +46 -0
  117. package/dist/utils/validate.cjs +163 -26
  118. package/dist/utils/validate.d.ts +6 -8
  119. package/dist/utils/validate.d.ts.map +1 -1
  120. package/dist/utils/validate.js +158 -27
  121. package/package.json +42 -7
  122. package/dist/core/act.js.map +0 -1
  123. package/dist/core/executor.js.map +0 -1
  124. package/dist/index.js.map +0 -1
  125. package/dist/policies/cache.js.map +0 -1
  126. package/dist/policies/dedupe.js.map +0 -1
  127. package/dist/policies/retry.js.map +0 -1
  128. package/dist/policies/timeout.js.map +0 -1
  129. package/dist/state/store.cjs +0 -14
  130. package/dist/state/store.d.ts +0 -11
  131. package/dist/state/store.d.ts.map +0 -1
  132. package/dist/state/store.js +0 -11
  133. package/dist/state/store.js.map +0 -1
  134. package/dist/stores/base.js.map +0 -1
  135. package/dist/stores/memory.js.map +0 -1
  136. package/dist/types/index.js.map +0 -1
  137. package/dist/utils/abort.js.map +0 -1
  138. package/dist/utils/backoff.js.map +0 -1
  139. package/dist/utils/validate.js.map +0 -1
@@ -0,0 +1,113 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.createTenantStore = createTenantStore;
4
+ exports.createAsyncTenantStore = createAsyncTenantStore;
5
+ const memory_js_1 = require("../stores/memory.js");
6
+ const act_js_1 = require("./act.js");
7
+ const limits_js_1 = require("../utils/limits.js");
8
+ function createTenantStore(options = {}) {
9
+ const maxSize = options.maxSize ?? limits_js_1.LIMITS.DEFAULT_STORE_MAX_SIZE;
10
+ const autoCleanup = options.autoCleanup ?? true;
11
+ const cleanupIntervalMs = options.cleanupIntervalMs ?? 60_000;
12
+ const maxTenants = options.maxTenants ?? limits_js_1.LIMITS.MAX_TENANTS;
13
+ const tenants = new Map();
14
+ return {
15
+ get(tenantId) {
16
+ let entry = tenants.get(tenantId);
17
+ if (entry) {
18
+ tenants.delete(tenantId);
19
+ tenants.set(tenantId, entry);
20
+ return entry.scoped;
21
+ }
22
+ if (maxTenants !== Number.POSITIVE_INFINITY && tenants.size >= maxTenants) {
23
+ const lruId = tenants.keys().next().value;
24
+ if (lruId !== undefined) {
25
+ const lru = tenants.get(lruId);
26
+ if (lru) {
27
+ try {
28
+ lru.store.destroy();
29
+ }
30
+ catch { }
31
+ }
32
+ tenants.delete(lruId);
33
+ }
34
+ }
35
+ const store = new memory_js_1.InMemoryStore({ maxSize, autoCleanup, cleanupIntervalMs });
36
+ const scoped = (0, act_js_1.withStore)(store);
37
+ entry = { store, scoped };
38
+ tenants.set(tenantId, entry);
39
+ return entry.scoped;
40
+ },
41
+ evict(tenantId) {
42
+ const entry = tenants.get(tenantId);
43
+ if (entry) {
44
+ entry.store.destroy();
45
+ tenants.delete(tenantId);
46
+ }
47
+ },
48
+ size() {
49
+ return tenants.size;
50
+ },
51
+ destroy() {
52
+ for (const [, entry] of tenants) {
53
+ entry.store.destroy();
54
+ }
55
+ tenants.clear();
56
+ },
57
+ };
58
+ }
59
+ function createAsyncTenantStore(storeFactory, options = {}) {
60
+ const maxTenants = options.maxTenants ?? limits_js_1.LIMITS.MAX_TENANTS;
61
+ const tenants = new Map();
62
+ const safeDestroy = (store) => {
63
+ try {
64
+ const result = store.destroy?.();
65
+ if (result && typeof result.then === 'function') {
66
+ ;
67
+ result.catch(() => { });
68
+ }
69
+ }
70
+ catch {
71
+ }
72
+ };
73
+ return {
74
+ get(tenantId) {
75
+ let entry = tenants.get(tenantId);
76
+ if (entry) {
77
+ tenants.delete(tenantId);
78
+ tenants.set(tenantId, entry);
79
+ return entry.scoped;
80
+ }
81
+ if (maxTenants !== Number.POSITIVE_INFINITY && tenants.size >= maxTenants) {
82
+ const lruId = tenants.keys().next().value;
83
+ if (lruId !== undefined) {
84
+ const lru = tenants.get(lruId);
85
+ if (lru)
86
+ safeDestroy(lru.store);
87
+ tenants.delete(lruId);
88
+ }
89
+ }
90
+ const store = storeFactory(tenantId);
91
+ const scoped = (0, act_js_1.withStore)(store);
92
+ entry = { store, scoped };
93
+ tenants.set(tenantId, entry);
94
+ return entry.scoped;
95
+ },
96
+ evict(tenantId) {
97
+ const entry = tenants.get(tenantId);
98
+ if (entry) {
99
+ safeDestroy(entry.store);
100
+ tenants.delete(tenantId);
101
+ }
102
+ },
103
+ size() {
104
+ return tenants.size;
105
+ },
106
+ destroy() {
107
+ for (const [, entry] of tenants) {
108
+ safeDestroy(entry.store);
109
+ }
110
+ tenants.clear();
111
+ },
112
+ };
113
+ }
@@ -0,0 +1,24 @@
1
+ import type { ScopedActSync, ScopedActAsync } from './act.js';
2
+ import type { AsyncStateStore } from '../stores/base.js';
3
+ export interface TenantStoreOptions {
4
+ maxSize?: number;
5
+ autoCleanup?: boolean;
6
+ cleanupIntervalMs?: number;
7
+ maxTenants?: number;
8
+ }
9
+ export interface TenantManager {
10
+ get(tenantId: string): ScopedActSync | ScopedActAsync;
11
+ evict(tenantId: string): void;
12
+ size(): number;
13
+ destroy(): void;
14
+ }
15
+ export declare function createTenantStore(options?: TenantStoreOptions): TenantManager;
16
+ export declare function createAsyncTenantStore(storeFactory: (tenantId: string) => AsyncStateStore, options?: {
17
+ maxTenants?: number;
18
+ }): {
19
+ get: (tenantId: string) => ScopedActAsync;
20
+ evict: (tenantId: string) => void;
21
+ size: () => number;
22
+ destroy: () => void;
23
+ };
24
+ //# sourceMappingURL=tenant.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"tenant.d.ts","sourceRoot":"","sources":["../../src/core/tenant.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,aAAa,EAAE,cAAc,EAAE,MAAM,UAAU,CAAA;AAC7D,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,mBAAmB,CAAA;AAGxD,MAAM,WAAW,kBAAkB;IACjC,OAAO,CAAC,EAAE,MAAM,CAAA;IAChB,WAAW,CAAC,EAAE,OAAO,CAAA;IACrB,iBAAiB,CAAC,EAAE,MAAM,CAAA;IAa1B,UAAU,CAAC,EAAE,MAAM,CAAA;CACpB;AAED,MAAM,WAAW,aAAa;IAC5B,GAAG,CAAC,QAAQ,EAAE,MAAM,GAAG,aAAa,GAAG,cAAc,CAAA;IACrD,KAAK,CAAC,QAAQ,EAAE,MAAM,GAAG,IAAI,CAAA;IAC7B,IAAI,IAAI,MAAM,CAAA;IACd,OAAO,IAAI,IAAI,CAAA;CAChB;AAWD,wBAAgB,iBAAiB,CAAC,OAAO,GAAE,kBAAuB,GAAG,aAAa,CAwDjF;AAOD,wBAAgB,sBAAsB,CACpC,YAAY,EAAE,CAAC,QAAQ,EAAE,MAAM,KAAK,eAAe,EACnD,OAAO,GAAE;IAAE,UAAU,CAAC,EAAE,MAAM,CAAA;CAAO,GACpC;IACD,GAAG,EAAE,CAAC,QAAQ,EAAE,MAAM,KAAK,cAAc,CAAA;IACzC,KAAK,EAAE,CAAC,QAAQ,EAAE,MAAM,KAAK,IAAI,CAAA;IACjC,IAAI,EAAE,MAAM,MAAM,CAAA;IAClB,OAAO,EAAE,MAAM,IAAI,CAAA;CACpB,CAkEA"}
@@ -0,0 +1,109 @@
1
+ import { InMemoryStore } from '../stores/memory.js';
2
+ import { withStore } from './act.js';
3
+ import { LIMITS } from '../utils/limits.js';
4
+ export function createTenantStore(options = {}) {
5
+ const maxSize = options.maxSize ?? LIMITS.DEFAULT_STORE_MAX_SIZE;
6
+ const autoCleanup = options.autoCleanup ?? true;
7
+ const cleanupIntervalMs = options.cleanupIntervalMs ?? 60_000;
8
+ const maxTenants = options.maxTenants ?? LIMITS.MAX_TENANTS;
9
+ const tenants = new Map();
10
+ return {
11
+ get(tenantId) {
12
+ let entry = tenants.get(tenantId);
13
+ if (entry) {
14
+ tenants.delete(tenantId);
15
+ tenants.set(tenantId, entry);
16
+ return entry.scoped;
17
+ }
18
+ if (maxTenants !== Number.POSITIVE_INFINITY && tenants.size >= maxTenants) {
19
+ const lruId = tenants.keys().next().value;
20
+ if (lruId !== undefined) {
21
+ const lru = tenants.get(lruId);
22
+ if (lru) {
23
+ try {
24
+ lru.store.destroy();
25
+ }
26
+ catch { }
27
+ }
28
+ tenants.delete(lruId);
29
+ }
30
+ }
31
+ const store = new InMemoryStore({ maxSize, autoCleanup, cleanupIntervalMs });
32
+ const scoped = withStore(store);
33
+ entry = { store, scoped };
34
+ tenants.set(tenantId, entry);
35
+ return entry.scoped;
36
+ },
37
+ evict(tenantId) {
38
+ const entry = tenants.get(tenantId);
39
+ if (entry) {
40
+ entry.store.destroy();
41
+ tenants.delete(tenantId);
42
+ }
43
+ },
44
+ size() {
45
+ return tenants.size;
46
+ },
47
+ destroy() {
48
+ for (const [, entry] of tenants) {
49
+ entry.store.destroy();
50
+ }
51
+ tenants.clear();
52
+ },
53
+ };
54
+ }
55
+ export function createAsyncTenantStore(storeFactory, options = {}) {
56
+ const maxTenants = options.maxTenants ?? LIMITS.MAX_TENANTS;
57
+ const tenants = new Map();
58
+ const safeDestroy = (store) => {
59
+ try {
60
+ const result = store.destroy?.();
61
+ if (result && typeof result.then === 'function') {
62
+ ;
63
+ result.catch(() => { });
64
+ }
65
+ }
66
+ catch {
67
+ }
68
+ };
69
+ return {
70
+ get(tenantId) {
71
+ let entry = tenants.get(tenantId);
72
+ if (entry) {
73
+ tenants.delete(tenantId);
74
+ tenants.set(tenantId, entry);
75
+ return entry.scoped;
76
+ }
77
+ if (maxTenants !== Number.POSITIVE_INFINITY && tenants.size >= maxTenants) {
78
+ const lruId = tenants.keys().next().value;
79
+ if (lruId !== undefined) {
80
+ const lru = tenants.get(lruId);
81
+ if (lru)
82
+ safeDestroy(lru.store);
83
+ tenants.delete(lruId);
84
+ }
85
+ }
86
+ const store = storeFactory(tenantId);
87
+ const scoped = withStore(store);
88
+ entry = { store, scoped };
89
+ tenants.set(tenantId, entry);
90
+ return entry.scoped;
91
+ },
92
+ evict(tenantId) {
93
+ const entry = tenants.get(tenantId);
94
+ if (entry) {
95
+ safeDestroy(entry.store);
96
+ tenants.delete(tenantId);
97
+ }
98
+ },
99
+ size() {
100
+ return tenants.size;
101
+ },
102
+ destroy() {
103
+ for (const [, entry] of tenants) {
104
+ safeDestroy(entry.store);
105
+ }
106
+ tenants.clear();
107
+ },
108
+ };
109
+ }
@@ -0,0 +1,149 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.HedgeTimeoutError = exports.ResourceExhaustedError = exports.RateLimitError = exports.BulkheadOverflowError = exports.CircuitBreakerOpenError = exports.ValidationError = exports.RetryExhaustedError = exports.TotalTimeoutError = exports.TimeoutError = exports.ActlyAbortError = exports.ActlyError = void 0;
4
+ exports.isActlyError = isActlyError;
5
+ class ActlyError extends Error {
6
+ key;
7
+ constructor(message, options) {
8
+ super(message, options?.cause !== undefined ? { cause: options.cause } : undefined);
9
+ this.name = new.target.name;
10
+ if (options?.key !== undefined) {
11
+ Object.defineProperty(this, 'key', { value: options.key, enumerable: true });
12
+ }
13
+ Object.setPrototypeOf(this, new.target.prototype);
14
+ }
15
+ toJSON(opts) {
16
+ const obj = {
17
+ name: this.name,
18
+ code: this.code,
19
+ message: opts?.redact ? sanitizeErrorMessageForJSON(this.message) : this.message,
20
+ };
21
+ if (this.key !== undefined)
22
+ obj.key = this.key;
23
+ if (this.stack !== undefined)
24
+ obj.stack = this.stack;
25
+ for (const prop of Object.keys(this)) {
26
+ if (!(prop in obj)) {
27
+ try {
28
+ obj[prop] = this[prop];
29
+ }
30
+ catch {
31
+ }
32
+ }
33
+ }
34
+ return obj;
35
+ }
36
+ }
37
+ exports.ActlyError = ActlyError;
38
+ function isActlyError(e) {
39
+ return (e != null &&
40
+ typeof e === 'object' &&
41
+ typeof e.code === 'string' &&
42
+ String(e.code).startsWith('ACTLY_'));
43
+ }
44
+ function sanitizeErrorMessageForJSON(msg) {
45
+ let str;
46
+ if (msg instanceof Error) {
47
+ str = String(msg.message ?? '');
48
+ }
49
+ else {
50
+ str = String(msg ?? '');
51
+ }
52
+ if (str.length > 4096)
53
+ str = str.slice(0, 4096) + '…[truncated]';
54
+ return str.replace(/[<>]/g, (c) => (c === '<' ? '&lt;' : '&gt;'));
55
+ }
56
+ class ActlyAbortError extends ActlyError {
57
+ code = 'ACTLY_ABORT';
58
+ constructor(options) {
59
+ const causeMsg = options?.cause instanceof Error ? options.cause.message : String(options?.cause ?? 'aborted');
60
+ super(`Actly operation aborted: ${causeMsg}`, options);
61
+ }
62
+ }
63
+ exports.ActlyAbortError = ActlyAbortError;
64
+ class TimeoutError extends ActlyError {
65
+ code = 'ACTLY_TIMEOUT';
66
+ ms;
67
+ constructor(ms, options) {
68
+ super(`ACT timed out after ${ms}ms`, options);
69
+ this.ms = ms;
70
+ }
71
+ }
72
+ exports.TimeoutError = TimeoutError;
73
+ class TotalTimeoutError extends ActlyError {
74
+ code = 'ACTLY_TOTAL_TIMEOUT';
75
+ ms;
76
+ constructor(ms, options) {
77
+ super(`ACT total timeout exceeded after ${ms}ms`, options);
78
+ this.ms = ms;
79
+ }
80
+ }
81
+ exports.TotalTimeoutError = TotalTimeoutError;
82
+ class RetryExhaustedError extends ActlyError {
83
+ code = 'ACTLY_RETRY_EXHAUSTED';
84
+ attempts;
85
+ lastError;
86
+ errors;
87
+ constructor(options) {
88
+ const lastMsg = options.lastError instanceof Error ? options.lastError.message : String(options.lastError);
89
+ super(`ACT retry exhausted after ${options.attempts} attempts; last error: ${lastMsg}`, { key: options.key, cause: options.lastError });
90
+ this.attempts = options.attempts;
91
+ this.lastError = options.lastError;
92
+ this.errors = options.errors;
93
+ }
94
+ }
95
+ exports.RetryExhaustedError = RetryExhaustedError;
96
+ class ValidationError extends ActlyError {
97
+ code = 'ACTLY_VALIDATION';
98
+ constructor(message, options) {
99
+ super(message, options);
100
+ if (options?.field !== undefined) {
101
+ Object.defineProperty(this, 'field', { value: options.field, enumerable: true });
102
+ }
103
+ }
104
+ field;
105
+ }
106
+ exports.ValidationError = ValidationError;
107
+ class CircuitBreakerOpenError extends ActlyError {
108
+ code = 'ACTLY_CIRCUIT_OPEN';
109
+ constructor(key, ms, options) {
110
+ super(`Circuit breaker open for key "${key}" — retry after ${ms}ms`, { key, cause: options?.cause });
111
+ }
112
+ }
113
+ exports.CircuitBreakerOpenError = CircuitBreakerOpenError;
114
+ class BulkheadOverflowError extends ActlyError {
115
+ code = 'ACTLY_BULKHEAD_FULL';
116
+ constructor(key, maxConcurrent, options) {
117
+ super(`Bulkhead full for key "${key}" — maxConcurrent ${maxConcurrent} reached`, { key, cause: options?.cause });
118
+ }
119
+ }
120
+ exports.BulkheadOverflowError = BulkheadOverflowError;
121
+ class RateLimitError extends ActlyError {
122
+ code = 'ACTLY_RATE_LIMIT';
123
+ constructor(key, maxCalls, windowMs, options) {
124
+ super(`Rate limit exceeded for key "${key}" — ${maxCalls} calls per ${windowMs}ms`, { key, cause: options?.cause });
125
+ }
126
+ }
127
+ exports.RateLimitError = RateLimitError;
128
+ class ResourceExhaustedError extends ActlyError {
129
+ code = 'ACTLY_RESOURCE_EXHAUSTED';
130
+ current;
131
+ limit;
132
+ constructor(current, limit, options) {
133
+ super(`Actly: resource exhausted — ${current} in-flight calls exceed process limit ${limit}. ` +
134
+ `Set ACTLY_NO_INFLIGHT_LIMIT=1 to disable this guard (at your own risk).`, options);
135
+ this.current = current;
136
+ this.limit = limit;
137
+ }
138
+ }
139
+ exports.ResourceExhaustedError = ResourceExhaustedError;
140
+ class HedgeTimeoutError extends ActlyError {
141
+ code = 'ACTLY_HEDGE_TIMEOUT';
142
+ delayMs;
143
+ constructor(options) {
144
+ const ms = options?.delayMs ?? 0;
145
+ super(`ACT hedge timed out after ${ms}ms`, options);
146
+ this.delayMs = ms;
147
+ }
148
+ }
149
+ exports.HedgeTimeoutError = HedgeTimeoutError;
@@ -0,0 +1,94 @@
1
+ export declare abstract class ActlyError extends Error {
2
+ abstract readonly code: string;
3
+ readonly key?: string;
4
+ constructor(message: string, options?: {
5
+ key?: string;
6
+ cause?: unknown;
7
+ });
8
+ toJSON(opts?: {
9
+ redact?: boolean;
10
+ }): Record<string, unknown>;
11
+ }
12
+ export declare function isActlyError(e: unknown): e is ActlyError;
13
+ export declare class ActlyAbortError extends ActlyError {
14
+ readonly code: "ACTLY_ABORT";
15
+ constructor(options?: {
16
+ key?: string;
17
+ cause?: unknown;
18
+ });
19
+ }
20
+ export declare class TimeoutError extends ActlyError {
21
+ readonly code: "ACTLY_TIMEOUT";
22
+ readonly ms: number;
23
+ constructor(ms: number, options?: {
24
+ key?: string;
25
+ cause?: unknown;
26
+ });
27
+ }
28
+ export declare class TotalTimeoutError extends ActlyError {
29
+ readonly code: "ACTLY_TOTAL_TIMEOUT";
30
+ readonly ms: number;
31
+ constructor(ms: number, options?: {
32
+ key?: string;
33
+ cause?: unknown;
34
+ });
35
+ }
36
+ export declare class RetryExhaustedError extends ActlyError {
37
+ readonly code: "ACTLY_RETRY_EXHAUSTED";
38
+ readonly attempts: number;
39
+ readonly lastError: unknown;
40
+ readonly errors: readonly unknown[];
41
+ constructor(options: {
42
+ key?: string;
43
+ attempts: number;
44
+ lastError: unknown;
45
+ errors: readonly unknown[];
46
+ });
47
+ }
48
+ export declare class ValidationError extends ActlyError {
49
+ readonly code: "ACTLY_VALIDATION";
50
+ constructor(message: string, options?: {
51
+ field?: string;
52
+ cause?: unknown;
53
+ });
54
+ readonly field?: string;
55
+ }
56
+ export declare class CircuitBreakerOpenError extends ActlyError {
57
+ readonly code: "ACTLY_CIRCUIT_OPEN";
58
+ readonly key: string;
59
+ constructor(key: string, ms: number, options?: {
60
+ cause?: unknown;
61
+ });
62
+ }
63
+ export declare class BulkheadOverflowError extends ActlyError {
64
+ readonly code: "ACTLY_BULKHEAD_FULL";
65
+ readonly key: string;
66
+ constructor(key: string, maxConcurrent: number, options?: {
67
+ cause?: unknown;
68
+ });
69
+ }
70
+ export declare class RateLimitError extends ActlyError {
71
+ readonly code: "ACTLY_RATE_LIMIT";
72
+ readonly key: string;
73
+ constructor(key: string, maxCalls: number, windowMs: number, options?: {
74
+ cause?: unknown;
75
+ });
76
+ }
77
+ export declare class ResourceExhaustedError extends ActlyError {
78
+ readonly code: "ACTLY_RESOURCE_EXHAUSTED";
79
+ readonly current: number;
80
+ readonly limit: number;
81
+ constructor(current: number, limit: number, options?: {
82
+ cause?: unknown;
83
+ });
84
+ }
85
+ export declare class HedgeTimeoutError extends ActlyError {
86
+ readonly code: "ACTLY_HEDGE_TIMEOUT";
87
+ readonly delayMs: number;
88
+ constructor(options?: {
89
+ key?: string;
90
+ delayMs?: number;
91
+ cause?: unknown;
92
+ });
93
+ }
94
+ //# sourceMappingURL=errors.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"errors.d.ts","sourceRoot":"","sources":["../src/errors.ts"],"names":[],"mappings":"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"}
package/dist/errors.js ADDED
@@ -0,0 +1,134 @@
1
+ export class ActlyError extends Error {
2
+ key;
3
+ constructor(message, options) {
4
+ super(message, options?.cause !== undefined ? { cause: options.cause } : undefined);
5
+ this.name = new.target.name;
6
+ if (options?.key !== undefined) {
7
+ Object.defineProperty(this, 'key', { value: options.key, enumerable: true });
8
+ }
9
+ Object.setPrototypeOf(this, new.target.prototype);
10
+ }
11
+ toJSON(opts) {
12
+ const obj = {
13
+ name: this.name,
14
+ code: this.code,
15
+ message: opts?.redact ? sanitizeErrorMessageForJSON(this.message) : this.message,
16
+ };
17
+ if (this.key !== undefined)
18
+ obj.key = this.key;
19
+ if (this.stack !== undefined)
20
+ obj.stack = this.stack;
21
+ for (const prop of Object.keys(this)) {
22
+ if (!(prop in obj)) {
23
+ try {
24
+ obj[prop] = this[prop];
25
+ }
26
+ catch {
27
+ }
28
+ }
29
+ }
30
+ return obj;
31
+ }
32
+ }
33
+ export function isActlyError(e) {
34
+ return (e != null &&
35
+ typeof e === 'object' &&
36
+ typeof e.code === 'string' &&
37
+ String(e.code).startsWith('ACTLY_'));
38
+ }
39
+ function sanitizeErrorMessageForJSON(msg) {
40
+ let str;
41
+ if (msg instanceof Error) {
42
+ str = String(msg.message ?? '');
43
+ }
44
+ else {
45
+ str = String(msg ?? '');
46
+ }
47
+ if (str.length > 4096)
48
+ str = str.slice(0, 4096) + '…[truncated]';
49
+ return str.replace(/[<>]/g, (c) => (c === '<' ? '&lt;' : '&gt;'));
50
+ }
51
+ export class ActlyAbortError extends ActlyError {
52
+ code = 'ACTLY_ABORT';
53
+ constructor(options) {
54
+ const causeMsg = options?.cause instanceof Error ? options.cause.message : String(options?.cause ?? 'aborted');
55
+ super(`Actly operation aborted: ${causeMsg}`, options);
56
+ }
57
+ }
58
+ export class TimeoutError extends ActlyError {
59
+ code = 'ACTLY_TIMEOUT';
60
+ ms;
61
+ constructor(ms, options) {
62
+ super(`ACT timed out after ${ms}ms`, options);
63
+ this.ms = ms;
64
+ }
65
+ }
66
+ export class TotalTimeoutError extends ActlyError {
67
+ code = 'ACTLY_TOTAL_TIMEOUT';
68
+ ms;
69
+ constructor(ms, options) {
70
+ super(`ACT total timeout exceeded after ${ms}ms`, options);
71
+ this.ms = ms;
72
+ }
73
+ }
74
+ export class RetryExhaustedError extends ActlyError {
75
+ code = 'ACTLY_RETRY_EXHAUSTED';
76
+ attempts;
77
+ lastError;
78
+ errors;
79
+ constructor(options) {
80
+ const lastMsg = options.lastError instanceof Error ? options.lastError.message : String(options.lastError);
81
+ super(`ACT retry exhausted after ${options.attempts} attempts; last error: ${lastMsg}`, { key: options.key, cause: options.lastError });
82
+ this.attempts = options.attempts;
83
+ this.lastError = options.lastError;
84
+ this.errors = options.errors;
85
+ }
86
+ }
87
+ export class ValidationError extends ActlyError {
88
+ code = 'ACTLY_VALIDATION';
89
+ constructor(message, options) {
90
+ super(message, options);
91
+ if (options?.field !== undefined) {
92
+ Object.defineProperty(this, 'field', { value: options.field, enumerable: true });
93
+ }
94
+ }
95
+ field;
96
+ }
97
+ export class CircuitBreakerOpenError extends ActlyError {
98
+ code = 'ACTLY_CIRCUIT_OPEN';
99
+ constructor(key, ms, options) {
100
+ super(`Circuit breaker open for key "${key}" — retry after ${ms}ms`, { key, cause: options?.cause });
101
+ }
102
+ }
103
+ export class BulkheadOverflowError extends ActlyError {
104
+ code = 'ACTLY_BULKHEAD_FULL';
105
+ constructor(key, maxConcurrent, options) {
106
+ super(`Bulkhead full for key "${key}" — maxConcurrent ${maxConcurrent} reached`, { key, cause: options?.cause });
107
+ }
108
+ }
109
+ export class RateLimitError extends ActlyError {
110
+ code = 'ACTLY_RATE_LIMIT';
111
+ constructor(key, maxCalls, windowMs, options) {
112
+ super(`Rate limit exceeded for key "${key}" — ${maxCalls} calls per ${windowMs}ms`, { key, cause: options?.cause });
113
+ }
114
+ }
115
+ export class ResourceExhaustedError extends ActlyError {
116
+ code = 'ACTLY_RESOURCE_EXHAUSTED';
117
+ current;
118
+ limit;
119
+ constructor(current, limit, options) {
120
+ super(`Actly: resource exhausted — ${current} in-flight calls exceed process limit ${limit}. ` +
121
+ `Set ACTLY_NO_INFLIGHT_LIMIT=1 to disable this guard (at your own risk).`, options);
122
+ this.current = current;
123
+ this.limit = limit;
124
+ }
125
+ }
126
+ export class HedgeTimeoutError extends ActlyError {
127
+ code = 'ACTLY_HEDGE_TIMEOUT';
128
+ delayMs;
129
+ constructor(options) {
130
+ const ms = options?.delayMs ?? 0;
131
+ super(`ACT hedge timed out after ${ms}ms`, options);
132
+ this.delayMs = ms;
133
+ }
134
+ }