@classytic/arc 2.9.1 → 2.10.8
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/README.md +20 -91
- package/dist/{BaseController-Vu2yc56T.mjs → BaseController-DVNKvoX4.mjs} +154 -170
- package/dist/{ResourceRegistry-Dq3_zBQP.mjs → ResourceRegistry-CcN2LVrc.mjs} +1 -1
- package/dist/actionPermissions-TUVR3uiZ.mjs +22 -0
- package/dist/adapters/index.d.mts +3 -3
- package/dist/adapters/index.mjs +2 -2
- package/dist/{adapters-BBqAVvPK.mjs → adapters-BXY4i-hw.mjs} +210 -41
- package/dist/audit/index.d.mts +38 -3
- package/dist/audit/index.mjs +54 -22
- package/dist/auth/index.d.mts +2 -2
- package/dist/auth/index.mjs +3 -3
- package/dist/cache/index.d.mts +17 -15
- package/dist/cache/index.mjs +16 -15
- package/dist/{caching-CjybdRwx.mjs → caching-3h93rkJM.mjs} +8 -3
- package/dist/cli/commands/describe.mjs +1 -1
- package/dist/cli/commands/docs.mjs +2 -2
- package/dist/cli/commands/init.mjs +1 -1
- package/dist/cli/commands/introspect.mjs +1 -1
- package/dist/context/index.d.mts +58 -0
- package/dist/context/index.mjs +2 -0
- package/dist/core/index.d.mts +2 -2
- package/dist/core/index.mjs +3 -4
- package/dist/{defineResource-C__jkwvs.mjs → core-3MWJosCH.mjs} +174 -94
- package/dist/{createActionRouter-DH1YFL9m.mjs → createActionRouter-C8UUB3Px.mjs} +1 -1
- package/dist/{createApp-CBJUJKGP.mjs → createApp-BwnEAO2h.mjs} +53 -19
- package/dist/docs/index.d.mts +1 -1
- package/dist/docs/index.mjs +2 -2
- package/dist/{elevation-DxQ6ACbt.mjs → elevation-Dci0AYLT.mjs} +2 -2
- package/dist/errorHandler-2ii4RIYr.d.mts +114 -0
- package/dist/{errorHandler-CZDW4EXS.mjs → errorHandler-CSxe7KIM.mjs} +1 -1
- package/dist/{eventPlugin-Dl7MoVWH.mjs → eventPlugin-ByU4Cv0e.mjs} +1 -1
- package/dist/{eventPlugin-BxvaCIZF.d.mts → eventPlugin-D1ThQ1Pp.d.mts} +1 -1
- package/dist/events/index.d.mts +8 -5
- package/dist/events/index.mjs +87 -52
- package/dist/events/transports/redis-stream-entry.d.mts +1 -1
- package/dist/events/transports/redis.d.mts +1 -1
- package/dist/factory/index.d.mts +1 -1
- package/dist/factory/index.mjs +1 -1
- package/dist/{types-DZi1aYhm.d.mts → fields-C8Y0XLAu.d.mts} +122 -2
- package/dist/hooks/index.d.mts +1 -1
- package/dist/idempotency/index.d.mts +5 -2
- package/dist/idempotency/index.mjs +46 -37
- package/dist/{interface-YrWsmKqE.d.mts → index-BGbpGVyM.d.mts} +2107 -2756
- package/dist/{index-CtGKT0lf.d.mts → index-BziRPS4H.d.mts} +81 -7
- package/dist/{index-C-xjcA6F.d.mts → index-C_Noptz-.d.mts} +284 -409
- package/dist/{index-Cibkchnx.d.mts → index-EqQN6p0W.d.mts} +3 -3
- package/dist/index.d.mts +6 -219
- package/dist/index.mjs +10 -131
- package/dist/integrations/event-gateway.d.mts +1 -1
- package/dist/integrations/event-gateway.mjs +1 -1
- package/dist/integrations/index.d.mts +1 -1
- package/dist/integrations/mcp/index.d.mts +2 -2
- package/dist/integrations/mcp/index.mjs +1 -1
- package/dist/integrations/mcp/testing.d.mts +1 -1
- package/dist/integrations/mcp/testing.mjs +1 -1
- package/dist/interface-yhyb_pLY.d.mts +77 -0
- package/dist/logger/index.d.mts +81 -0
- package/dist/{logger-CDjpjySd.mjs → logger/index.mjs} +1 -6
- package/dist/{memory-BFAYkf8H.mjs → memory-DqI-449b.mjs} +23 -8
- package/dist/middleware/index.d.mts +109 -0
- package/dist/middleware/index.mjs +70 -0
- package/dist/multipartBody-CUQGVlM_.mjs +123 -0
- package/dist/{openapi-CXuTG1M9.mjs → openapi-DpNpqBmo.mjs} +9 -7
- package/dist/org/index.d.mts +2 -2
- package/dist/permissions/index.d.mts +3 -4
- package/dist/permissions/index.mjs +5 -5
- package/dist/{permissions-oNZawnkR.mjs → permissions-wkqRwicB.mjs} +315 -397
- package/dist/pipe-CGJxqDGx.mjs +62 -0
- package/dist/pipeline/index.d.mts +62 -0
- package/dist/pipeline/index.mjs +53 -0
- package/dist/plugins/index.d.mts +23 -3
- package/dist/plugins/index.mjs +9 -11
- package/dist/plugins/response-cache.mjs +1 -1
- package/dist/plugins/tracing-entry.mjs +1 -1
- package/dist/presets/filesUpload.d.mts +3 -3
- package/dist/presets/filesUpload.mjs +255 -1
- package/dist/presets/index.d.mts +1 -1
- package/dist/presets/index.mjs +2 -2
- package/dist/presets/multiTenant.d.mts +1 -1
- package/dist/presets/multiTenant.mjs +43 -9
- package/dist/presets/search.d.mts +91 -4
- package/dist/presets/search.mjs +1 -1
- package/dist/{presets-hM4WhNWY.mjs → presets-CrwOvuXI.mjs} +1 -1
- package/dist/{queryCachePlugin-DbUVroUG.mjs → queryCachePlugin-ChLNZvFT.mjs} +9 -9
- package/dist/{queryCachePlugin-CnTZZTC5.d.mts → queryCachePlugin-Dumka73q.d.mts} +1 -1
- package/dist/{queryParser-Cs-6SHQK.mjs → queryParser-NR__Qiju.mjs} +69 -2
- package/dist/{redis-stream-Bz-4q96t.d.mts → redis-stream-bkO88VHx.d.mts} +1 -1
- package/dist/registry/index.d.mts +1 -1
- package/dist/registry/index.mjs +1 -1
- package/dist/{requestContext-DYtmNpm5.mjs → requestContext-C38GskNt.mjs} +1 -1
- package/dist/{resourceToTools-C3cWymnW.mjs → resourceToTools-BhF3JV5p.mjs} +8 -3
- package/dist/scope/index.d.mts +2 -2
- package/dist/scope/index.mjs +2 -2
- package/dist/{sse-CJpt7LGI.mjs → sse-D8UeDwis.mjs} +1 -1
- package/dist/{store-helpers-DFiZl5TL.mjs → store-helpers-DYYUQbQN.mjs} +4 -0
- package/dist/testing/index.d.mts +6 -5
- package/dist/testing/index.mjs +17 -10
- package/dist/types/index.d.mts +5 -5
- package/dist/types/index.mjs +1 -31
- package/dist/types-CDnTEpga.mjs +27 -0
- package/dist/{types-CoSzA-s-.d.mts → types-CVKBssX5.d.mts} +1 -1
- package/dist/{types-CunEX4UX.d.mts → types-CVdgPXBW.d.mts} +20 -7
- package/dist/utils/index.d.mts +277 -3
- package/dist/utils/index.mjs +4 -5
- package/dist/{utils-B7FuRr9w.mjs → utils-LMwVidKy.mjs} +303 -2
- package/dist/{versioning-Cm8qoFDg.mjs → versioning-B6mimogM.mjs} +3 -5
- package/dist/versioning-CeUXHfjw.d.mts +117 -0
- package/package.json +31 -18
- package/skills/arc/SKILL.md +8 -12
- package/skills/arc/references/production.md +0 -41
- package/dist/circuitBreaker-CvXkjfrW.d.mts +0 -206
- package/dist/circuitBreaker-l18oRgL5.mjs +0 -284
- package/dist/core-DNncu0xF.mjs +0 -34
- package/dist/dynamic/index.d.mts +0 -93
- package/dist/dynamic/index.mjs +0 -122
- package/dist/errorHandler-DixGcttC.d.mts +0 -218
- package/dist/fields-BC7zcmI9.d.mts +0 -121
- package/dist/filesUpload-q8oHt--L.mjs +0 -377
- package/dist/interface-DplgQO2e.d.mts +0 -54
- package/dist/policies/index.d.mts +0 -425
- package/dist/policies/index.mjs +0 -318
- package/dist/rpc/index.d.mts +0 -90
- package/dist/rpc/index.mjs +0 -248
- /package/dist/{EventTransport-CqZ8FyM_.d.mts → EventTransport-CfVEGaEl.d.mts} +0 -0
- /package/dist/{applyPermissionResult-bqGpo9ML.mjs → applyPermissionResult-QhV1Pa-g.mjs} +0 -0
- /package/dist/{constants-Cxde4rpC.mjs → constants-BhY1OHoH.mjs} +0 -0
- /package/dist/{elevation-B6S5csVA.d.mts → elevation-s5ykdNHr.d.mts} +0 -0
- /package/dist/{errors-CqWnSqM-.mjs → errors-BqdUDja_.mjs} +0 -0
- /package/dist/{fields-CU6FlaDV.mjs → fields-CTMWOUDt.mjs} +0 -0
- /package/dist/{keys-qcD-TVJl.mjs → keys-nWQGUTu1.mjs} +0 -0
- /package/dist/{types-ZUu_h0jp.mjs → types-D57iXYb8.mjs} +0 -0
- /package/dist/{types-BD85MlEK.d.mts → types-tgR4Pt8F.d.mts} +0 -0
|
@@ -1,11 +1,12 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import { n as ElevationOptions } from "./elevation-
|
|
1
|
+
import { Gt as Authenticator } from "./index-BGbpGVyM.mjs";
|
|
2
|
+
import { n as ElevationOptions } from "./elevation-s5ykdNHr.mjs";
|
|
3
|
+
import { o as EventTransport } from "./EventTransport-CfVEGaEl.mjs";
|
|
3
4
|
import { t as ExternalOpenApiPaths } from "./externalPaths-Bapitwvd.mjs";
|
|
4
|
-
import {
|
|
5
|
-
import { r as QueryCachePluginOptions } from "./queryCachePlugin-
|
|
6
|
-
import {
|
|
7
|
-
import { t as
|
|
8
|
-
import {
|
|
5
|
+
import { r as CacheStore } from "./interface-yhyb_pLY.mjs";
|
|
6
|
+
import { r as QueryCachePluginOptions } from "./queryCachePlugin-Dumka73q.mjs";
|
|
7
|
+
import { t as EventPluginOptions } from "./eventPlugin-D1ThQ1Pp.mjs";
|
|
8
|
+
import { f as CachingOptions, l as SSEOptions, o as MetricsOptions, t as VersioningOptions } from "./versioning-CeUXHfjw.mjs";
|
|
9
|
+
import { t as ErrorHandlerOptions } from "./errorHandler-2ii4RIYr.mjs";
|
|
9
10
|
import { r as IdempotencyStore } from "./interface-B-pe8fhj.mjs";
|
|
10
11
|
import { FastifyInstance, FastifyPluginAsync, FastifyReply, FastifyRequest, FastifyServerOptions } from "fastify";
|
|
11
12
|
|
|
@@ -149,6 +150,18 @@ type HelmetOptions = Record<string, unknown>;
|
|
|
149
150
|
type RateLimitOpts = Record<string, unknown> & {
|
|
150
151
|
max?: number;
|
|
151
152
|
timeWindow?: string | number;
|
|
153
|
+
/**
|
|
154
|
+
* Path patterns to exempt from rate limiting. Supports exact match
|
|
155
|
+
* (`/health`) or prefix match with trailing `*` (`/api/auth/*`).
|
|
156
|
+
*
|
|
157
|
+
* Implemented by synthesising a `@fastify/rate-limit` `allowList`
|
|
158
|
+
* function. When combined with a user-supplied `allowList`, both
|
|
159
|
+
* are OR-ed together (path OR ip/custom match skips the limit).
|
|
160
|
+
*
|
|
161
|
+
* Use this for endpoints that are hit frequently but independently
|
|
162
|
+
* of user traffic — session heartbeat, webhooks, health probes.
|
|
163
|
+
*/
|
|
164
|
+
skipPaths?: string[];
|
|
152
165
|
};
|
|
153
166
|
/**
|
|
154
167
|
* Arc's built-in JWT auth
|
package/dist/utils/index.d.mts
CHANGED
|
@@ -1,8 +1,213 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { S as QueryParserInterface, Yt as AnyRecord, b as ParsedQuery, mt as OpenApiSchemas } from "../index-BGbpGVyM.mjs";
|
|
2
|
+
import { n as ErrorMapper } from "../errorHandler-2ii4RIYr.mjs";
|
|
2
3
|
import { a as NotFoundError, c as RateLimitError, d as ValidationError, f as createDomainError, i as ForbiddenError, l as ServiceUnavailableError, m as isArcError, n as ConflictError, o as OrgAccessDeniedError, p as createError, r as ErrorDetails, s as OrgRequiredError, t as ArcError, u as UnauthorizedError } from "../errors-BI8kEKsO.mjs";
|
|
3
|
-
import { a as CircuitBreakerStats, c as createCircuitBreakerRegistry, i as CircuitBreakerRegistry, n as CircuitBreakerError, o as CircuitState, r as CircuitBreakerOptions, s as createCircuitBreaker, t as CircuitBreaker } from "../circuitBreaker-CvXkjfrW.mjs";
|
|
4
4
|
import { FastifyInstance, FastifyReply, FastifyRequest, RouteHandlerMethod } from "fastify";
|
|
5
5
|
|
|
6
|
+
//#region src/utils/circuitBreaker.d.ts
|
|
7
|
+
/**
|
|
8
|
+
* Circuit Breaker Pattern
|
|
9
|
+
*
|
|
10
|
+
* Wraps external service calls with failure protection.
|
|
11
|
+
* Prevents cascading failures by "opening" the circuit when
|
|
12
|
+
* a service is failing, allowing it time to recover.
|
|
13
|
+
*
|
|
14
|
+
* States:
|
|
15
|
+
* - CLOSED: Normal operation, requests pass through
|
|
16
|
+
* - OPEN: Too many failures, all requests fail fast
|
|
17
|
+
* - HALF_OPEN: Testing if service recovered, limited requests
|
|
18
|
+
*
|
|
19
|
+
* @example
|
|
20
|
+
* import { CircuitBreaker } from '@classytic/arc/utils';
|
|
21
|
+
*
|
|
22
|
+
* const paymentBreaker = new CircuitBreaker(async (amount) => {
|
|
23
|
+
* return await stripe.charges.create({ amount });
|
|
24
|
+
* }, {
|
|
25
|
+
* failureThreshold: 5,
|
|
26
|
+
* resetTimeout: 30000,
|
|
27
|
+
* timeout: 5000,
|
|
28
|
+
* });
|
|
29
|
+
*
|
|
30
|
+
* try {
|
|
31
|
+
* const result = await paymentBreaker.call(100);
|
|
32
|
+
* } catch (error) {
|
|
33
|
+
* // Handle failure or circuit open
|
|
34
|
+
* }
|
|
35
|
+
*/
|
|
36
|
+
declare const CircuitState: {
|
|
37
|
+
readonly CLOSED: "CLOSED";
|
|
38
|
+
readonly OPEN: "OPEN";
|
|
39
|
+
readonly HALF_OPEN: "HALF_OPEN";
|
|
40
|
+
};
|
|
41
|
+
type CircuitState = (typeof CircuitState)[keyof typeof CircuitState];
|
|
42
|
+
interface CircuitBreakerOptions {
|
|
43
|
+
/**
|
|
44
|
+
* Number of failures before opening circuit
|
|
45
|
+
* @default 5
|
|
46
|
+
*/
|
|
47
|
+
failureThreshold?: number;
|
|
48
|
+
/**
|
|
49
|
+
* Time in ms before attempting to close circuit
|
|
50
|
+
* @default 60000 (60 seconds)
|
|
51
|
+
*/
|
|
52
|
+
resetTimeout?: number;
|
|
53
|
+
/**
|
|
54
|
+
* Request timeout in ms
|
|
55
|
+
* @default 10000 (10 seconds)
|
|
56
|
+
*/
|
|
57
|
+
timeout?: number;
|
|
58
|
+
/**
|
|
59
|
+
* Number of successful requests in HALF_OPEN before closing
|
|
60
|
+
* @default 1
|
|
61
|
+
*/
|
|
62
|
+
successThreshold?: number;
|
|
63
|
+
/**
|
|
64
|
+
* Fallback function when circuit is open.
|
|
65
|
+
* Receives the same arguments as the wrapped function.
|
|
66
|
+
*/
|
|
67
|
+
fallback?: (...args: unknown[]) => Promise<unknown>;
|
|
68
|
+
/**
|
|
69
|
+
* Callback when state changes
|
|
70
|
+
*/
|
|
71
|
+
onStateChange?: (from: CircuitState, to: CircuitState) => void;
|
|
72
|
+
/**
|
|
73
|
+
* Callback on error
|
|
74
|
+
*/
|
|
75
|
+
onError?: (error: Error) => void;
|
|
76
|
+
/**
|
|
77
|
+
* Name for logging/monitoring
|
|
78
|
+
*/
|
|
79
|
+
name?: string;
|
|
80
|
+
}
|
|
81
|
+
interface CircuitBreakerStats {
|
|
82
|
+
name?: string;
|
|
83
|
+
state: CircuitState;
|
|
84
|
+
failures: number;
|
|
85
|
+
successes: number;
|
|
86
|
+
totalCalls: number;
|
|
87
|
+
openedAt: number | null;
|
|
88
|
+
lastCallAt: number | null;
|
|
89
|
+
}
|
|
90
|
+
declare class CircuitBreakerError extends Error {
|
|
91
|
+
state: CircuitState;
|
|
92
|
+
constructor(message: string, state: CircuitState);
|
|
93
|
+
}
|
|
94
|
+
declare class CircuitBreaker<T extends (...args: any[]) => Promise<any>> {
|
|
95
|
+
private state;
|
|
96
|
+
private failures;
|
|
97
|
+
private successes;
|
|
98
|
+
private totalCalls;
|
|
99
|
+
private nextAttempt;
|
|
100
|
+
private lastCallAt;
|
|
101
|
+
private openedAt;
|
|
102
|
+
private readonly failureThreshold;
|
|
103
|
+
private readonly resetTimeout;
|
|
104
|
+
private readonly timeout;
|
|
105
|
+
private readonly successThreshold;
|
|
106
|
+
private readonly fallback?;
|
|
107
|
+
private readonly onStateChange?;
|
|
108
|
+
private readonly onError?;
|
|
109
|
+
private readonly name;
|
|
110
|
+
private readonly fn;
|
|
111
|
+
constructor(fn: T, options?: CircuitBreakerOptions);
|
|
112
|
+
/**
|
|
113
|
+
* Call the wrapped function with circuit breaker protection
|
|
114
|
+
*/
|
|
115
|
+
call(...args: Parameters<T>): Promise<ReturnType<T>>;
|
|
116
|
+
/**
|
|
117
|
+
* Execute function with timeout
|
|
118
|
+
*/
|
|
119
|
+
private executeWithTimeout;
|
|
120
|
+
/**
|
|
121
|
+
* Handle successful call
|
|
122
|
+
*/
|
|
123
|
+
private onSuccess;
|
|
124
|
+
/**
|
|
125
|
+
* Handle failed call
|
|
126
|
+
*/
|
|
127
|
+
private onFailure;
|
|
128
|
+
/**
|
|
129
|
+
* Change circuit state
|
|
130
|
+
*/
|
|
131
|
+
private setState;
|
|
132
|
+
/**
|
|
133
|
+
* Manually open the circuit
|
|
134
|
+
*/
|
|
135
|
+
open(): void;
|
|
136
|
+
/**
|
|
137
|
+
* Manually close the circuit
|
|
138
|
+
*/
|
|
139
|
+
close(): void;
|
|
140
|
+
/**
|
|
141
|
+
* Get current statistics
|
|
142
|
+
*/
|
|
143
|
+
getStats(): CircuitBreakerStats;
|
|
144
|
+
/**
|
|
145
|
+
* Get current state
|
|
146
|
+
*/
|
|
147
|
+
getState(): CircuitState;
|
|
148
|
+
/**
|
|
149
|
+
* Check if circuit is open
|
|
150
|
+
*/
|
|
151
|
+
isOpen(): boolean;
|
|
152
|
+
/**
|
|
153
|
+
* Check if circuit is closed
|
|
154
|
+
*/
|
|
155
|
+
isClosed(): boolean;
|
|
156
|
+
/**
|
|
157
|
+
* Reset statistics
|
|
158
|
+
*/
|
|
159
|
+
reset(): void;
|
|
160
|
+
}
|
|
161
|
+
/**
|
|
162
|
+
* Create a circuit breaker with sensible defaults
|
|
163
|
+
*
|
|
164
|
+
* @example
|
|
165
|
+
* const emailBreaker = createCircuitBreaker(
|
|
166
|
+
* async (to, subject, body) => sendEmail(to, subject, body),
|
|
167
|
+
* { name: 'email-service' }
|
|
168
|
+
* );
|
|
169
|
+
*/
|
|
170
|
+
declare function createCircuitBreaker<T extends (...args: any[]) => Promise<any>>(fn: T, options?: CircuitBreakerOptions): CircuitBreaker<T>;
|
|
171
|
+
/**
|
|
172
|
+
* Circuit breaker registry for managing multiple breakers
|
|
173
|
+
*/
|
|
174
|
+
declare class CircuitBreakerRegistry {
|
|
175
|
+
private breakers;
|
|
176
|
+
/**
|
|
177
|
+
* Register a circuit breaker
|
|
178
|
+
*/
|
|
179
|
+
register<T extends (...args: any[]) => Promise<any>>(name: string, fn: T, options?: Omit<CircuitBreakerOptions, "name">): CircuitBreaker<T>;
|
|
180
|
+
/**
|
|
181
|
+
* Get a circuit breaker by name
|
|
182
|
+
*/
|
|
183
|
+
get(name: string): CircuitBreaker<any> | undefined;
|
|
184
|
+
/**
|
|
185
|
+
* Get all breakers
|
|
186
|
+
*/
|
|
187
|
+
getAll(): Map<string, CircuitBreaker<any>>;
|
|
188
|
+
/**
|
|
189
|
+
* Get statistics for all breakers
|
|
190
|
+
*/
|
|
191
|
+
getAllStats(): Record<string, CircuitBreakerStats>;
|
|
192
|
+
/**
|
|
193
|
+
* Reset all breakers
|
|
194
|
+
*/
|
|
195
|
+
resetAll(): void;
|
|
196
|
+
/**
|
|
197
|
+
* Open all breakers
|
|
198
|
+
*/
|
|
199
|
+
openAll(): void;
|
|
200
|
+
/**
|
|
201
|
+
* Close all breakers
|
|
202
|
+
*/
|
|
203
|
+
closeAll(): void;
|
|
204
|
+
}
|
|
205
|
+
/**
|
|
206
|
+
* Create a new CircuitBreakerRegistry instance.
|
|
207
|
+
* Use this instead of a global singleton — attach to fastify.arc or pass explicitly.
|
|
208
|
+
*/
|
|
209
|
+
declare function createCircuitBreakerRegistry(): CircuitBreakerRegistry;
|
|
210
|
+
//#endregion
|
|
6
211
|
//#region src/utils/compensation.d.ts
|
|
7
212
|
/**
|
|
8
213
|
* Compensating Transaction — In-Process Rollback Primitive
|
|
@@ -92,6 +297,22 @@ interface CompensationDefinition<TCtx extends Record<string, unknown> = Record<s
|
|
|
92
297
|
}
|
|
93
298
|
declare function defineCompensation<TCtx extends Record<string, unknown> = Record<string, unknown>>(name: string, steps: readonly CompensationStep<TCtx>[]): CompensationDefinition<TCtx>;
|
|
94
299
|
//#endregion
|
|
300
|
+
//#region src/utils/defineErrorMapper.d.ts
|
|
301
|
+
/**
|
|
302
|
+
* Register an `ErrorMapper` with its domain-specific generic argument and
|
|
303
|
+
* have it assign cleanly into `ErrorMapper[]` (no `as unknown as ErrorMapper`).
|
|
304
|
+
*
|
|
305
|
+
* The returned mapper is identical at runtime — `type` and `toResponse` are
|
|
306
|
+
* passed through untouched. Only the declared type widens from
|
|
307
|
+
* `ErrorMapper<T>` to `ErrorMapper` so the array inference works.
|
|
308
|
+
*
|
|
309
|
+
* Safety: the `errorHandlerPlugin` dispatches via `error instanceof mapper.type`
|
|
310
|
+
* before invoking `toResponse`, so the widened callback signature is never
|
|
311
|
+
* called with a non-`T` error at runtime. This helper codifies that invariant
|
|
312
|
+
* in one place.
|
|
313
|
+
*/
|
|
314
|
+
declare function defineErrorMapper<T extends Error>(mapper: ErrorMapper<T>): ErrorMapper;
|
|
315
|
+
//#endregion
|
|
95
316
|
//#region src/utils/defineGuard.d.ts
|
|
96
317
|
interface GuardConfig<T> {
|
|
97
318
|
/** Unique name — used as the storage key on the request. */
|
|
@@ -561,6 +782,59 @@ declare function convertOpenApiSchemas(schemas: OpenApiSchemas, target?: JsonSch
|
|
|
561
782
|
*/
|
|
562
783
|
declare function convertRouteSchema(schema: Record<string, unknown>, target?: JsonSchemaTarget): Record<string, unknown>;
|
|
563
784
|
//#endregion
|
|
785
|
+
//#region src/utils/simpleEqualityMatcher.d.ts
|
|
786
|
+
/**
|
|
787
|
+
* `simpleEqualityMatcher` — a minimal, dialect-agnostic flat-key equality
|
|
788
|
+
* matcher for `DataAdapter.matchesFilter` / `BaseController({ matchesFilter })`.
|
|
789
|
+
*
|
|
790
|
+
* **What it does:** for each `[key, expected]` in the filter, compares
|
|
791
|
+
* `item[key]` to `expected` via string coercion (so Mongo `ObjectId` values
|
|
792
|
+
* match their string representation) and returns `true` only if every
|
|
793
|
+
* filter entry matches. Array item values are matched implicitly (contains).
|
|
794
|
+
*
|
|
795
|
+
* **What it does NOT do:**
|
|
796
|
+
* - No `$eq` / `$ne` / `$in` / `$nin` / `$gt` / `$lt` / `$regex` / `$exists`
|
|
797
|
+
* - No `$and` / `$or`
|
|
798
|
+
* - No dot-path traversal (`"owner.id"`)
|
|
799
|
+
* - No schema-specific coercion
|
|
800
|
+
*
|
|
801
|
+
* **Why it exists:** 95%+ of arc's `_policyFilters` are produced by built-in
|
|
802
|
+
* permission helpers and are shaped like `{ ownerId: "u1" }` or
|
|
803
|
+
* `{ organizationId: "org_x" }` — flat equality. For that common shape,
|
|
804
|
+
* this helper is a safe, tested, 15-line defense-in-depth matcher that
|
|
805
|
+
* hosts using minimal repos (no `getOne(compoundFilter)` DB path) can opt
|
|
806
|
+
* into without arc shipping a full Mongo-syntax engine.
|
|
807
|
+
*
|
|
808
|
+
* **When to use:**
|
|
809
|
+
* - Your adapter/repo doesn't natively filter on `getOne(compoundFilter)`
|
|
810
|
+
* - Your `_policyFilters` are flat equality (from arc's built-in permission helpers)
|
|
811
|
+
* - You want defense-in-depth on `validateItemAccess` / `fetchDetailed`'s `getById` fallback
|
|
812
|
+
*
|
|
813
|
+
* **When NOT to use:**
|
|
814
|
+
* - Your `_policyFilters` use operators (`$in`, `$ne`, etc.) — supply a
|
|
815
|
+
* native matcher (mongokit's repo does the filter at the DB layer; for
|
|
816
|
+
* custom repos, wrap the kit's own predicate engine).
|
|
817
|
+
* - You're a mongokit / sqlitekit / Prisma user — the DB-level filter
|
|
818
|
+
* applied by `getOne(compoundFilter)` already covers this.
|
|
819
|
+
*
|
|
820
|
+
* @example
|
|
821
|
+
* ```ts
|
|
822
|
+
* import { simpleEqualityMatcher } from '@classytic/arc/utils';
|
|
823
|
+
*
|
|
824
|
+
* // On a custom adapter
|
|
825
|
+
* const adapter: DataAdapter = {
|
|
826
|
+
* repository,
|
|
827
|
+
* type: 'custom',
|
|
828
|
+
* name: 'in-memory',
|
|
829
|
+
* matchesFilter: simpleEqualityMatcher,
|
|
830
|
+
* };
|
|
831
|
+
*
|
|
832
|
+
* // Or directly on BaseController for ad-hoc controllers
|
|
833
|
+
* new BaseController(repo, { matchesFilter: simpleEqualityMatcher });
|
|
834
|
+
* ```
|
|
835
|
+
*/
|
|
836
|
+
declare function simpleEqualityMatcher(item: unknown, filters: Record<string, unknown>): boolean;
|
|
837
|
+
//#endregion
|
|
564
838
|
//#region src/utils/stateMachine.d.ts
|
|
565
839
|
/**
|
|
566
840
|
* State Machine Utility
|
|
@@ -691,4 +965,4 @@ declare function hasEvents(instance: FastifyInstance): instance is FastifyInstan
|
|
|
691
965
|
events: EventsDecorator;
|
|
692
966
|
};
|
|
693
967
|
//#endregion
|
|
694
|
-
export { ArcError, ArcQueryParser, type ArcQueryParserOptions, CircuitBreaker, CircuitBreakerError, type CircuitBreakerOptions, CircuitBreakerRegistry, type CircuitBreakerStats, CircuitState, type CompensationDefinition, type CompensationError, type CompensationHooks, type CompensationResult, type CompensationStep, ConflictError, type ErrorDetails, type EventsDecorator, ForbiddenError, type Guard, type GuardConfig, type JsonSchema, type JsonSchemaTarget, NotFoundError, OrgAccessDeniedError, OrgRequiredError, RateLimitError, ServiceUnavailableError, type StateMachine, type TransitionConfig, UnauthorizedError, ValidationError, convertOpenApiSchemas, convertRouteSchema, createCircuitBreaker, createCircuitBreakerRegistry, createDomainError, createError, createQueryParser, createStateMachine, defineCompensation, defineGuard, deleteResponse, errorResponseSchema, getDefaultCrudSchemas, getListQueryParams, handleRaw, hasEvents, isArcError, isJsonSchema, isZodSchema, itemResponse, listResponse, mutationResponse, paginationSchema, queryParams, responses, successResponseSchema, toJsonSchema, withCompensation, wrapResponse };
|
|
968
|
+
export { ArcError, ArcQueryParser, type ArcQueryParserOptions, CircuitBreaker, CircuitBreakerError, type CircuitBreakerOptions, CircuitBreakerRegistry, type CircuitBreakerStats, CircuitState, type CompensationDefinition, type CompensationError, type CompensationHooks, type CompensationResult, type CompensationStep, ConflictError, type ErrorDetails, type EventsDecorator, ForbiddenError, type Guard, type GuardConfig, type JsonSchema, type JsonSchemaTarget, NotFoundError, OrgAccessDeniedError, OrgRequiredError, RateLimitError, ServiceUnavailableError, type StateMachine, type TransitionConfig, UnauthorizedError, ValidationError, convertOpenApiSchemas, convertRouteSchema, createCircuitBreaker, createCircuitBreakerRegistry, createDomainError, createError, createQueryParser, createStateMachine, defineCompensation, defineErrorMapper, defineGuard, deleteResponse, errorResponseSchema, getDefaultCrudSchemas, getListQueryParams, handleRaw, hasEvents, isArcError, isJsonSchema, isZodSchema, itemResponse, listResponse, mutationResponse, paginationSchema, queryParams, responses, simpleEqualityMatcher, successResponseSchema, toJsonSchema, withCompensation, wrapResponse };
|
package/dist/utils/index.mjs
CHANGED
|
@@ -1,7 +1,6 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import { n as
|
|
3
|
-
import {
|
|
4
|
-
import { _ as withCompensation, a as getListQueryParams, c as mutationResponse, d as responses, f as successResponseSchema, g as defineCompensation, h as defineGuard, i as getDefaultCrudSchemas, l as paginationSchema, m as handleRaw, n as deleteResponse, o as itemResponse, p as wrapResponse, r as errorResponseSchema, s as listResponse, t as createStateMachine, u as queryParams } from "../utils-B7FuRr9w.mjs";
|
|
1
|
+
import { n as createQueryParser, r as simpleEqualityMatcher, t as ArcQueryParser } from "../queryParser-NR__Qiju.mjs";
|
|
2
|
+
import { a as OrgAccessDeniedError, c as ServiceUnavailableError, d as createDomainError, f as createError, i as NotFoundError, l as UnauthorizedError, n as ConflictError, o as OrgRequiredError, p as isArcError, r as ForbiddenError, s as RateLimitError, t as ArcError, u as ValidationError } from "../errors-BqdUDja_.mjs";
|
|
3
|
+
import { C as createCircuitBreaker, S as CircuitState, _ as defineCompensation, a as getListQueryParams, b as CircuitBreakerError, c as mutationResponse, d as responses, f as successResponseSchema, g as defineErrorMapper, h as defineGuard, i as getDefaultCrudSchemas, l as paginationSchema, m as handleRaw, n as deleteResponse, o as itemResponse, p as wrapResponse, r as errorResponseSchema, s as listResponse, t as createStateMachine, u as queryParams, v as withCompensation, w as createCircuitBreakerRegistry, x as CircuitBreakerRegistry, y as CircuitBreaker } from "../utils-LMwVidKy.mjs";
|
|
5
4
|
import { a as toJsonSchema, i as isZodSchema, n as convertRouteSchema, r as isJsonSchema, t as convertOpenApiSchemas } from "../schemaConverter-BxFDdtXu.mjs";
|
|
6
5
|
import { t as hasEvents } from "../typeGuards-Cj5Rgvlg.mjs";
|
|
7
|
-
export { ArcError, ArcQueryParser, CircuitBreaker, CircuitBreakerError, CircuitBreakerRegistry, CircuitState, ConflictError, ForbiddenError, NotFoundError, OrgAccessDeniedError, OrgRequiredError, RateLimitError, ServiceUnavailableError, UnauthorizedError, ValidationError, convertOpenApiSchemas, convertRouteSchema, createCircuitBreaker, createCircuitBreakerRegistry, createDomainError, createError, createQueryParser, createStateMachine, defineCompensation, defineGuard, deleteResponse, errorResponseSchema, getDefaultCrudSchemas, getListQueryParams, handleRaw, hasEvents, isArcError, isJsonSchema, isZodSchema, itemResponse, listResponse, mutationResponse, paginationSchema, queryParams, responses, successResponseSchema, toJsonSchema, withCompensation, wrapResponse };
|
|
6
|
+
export { ArcError, ArcQueryParser, CircuitBreaker, CircuitBreakerError, CircuitBreakerRegistry, CircuitState, ConflictError, ForbiddenError, NotFoundError, OrgAccessDeniedError, OrgRequiredError, RateLimitError, ServiceUnavailableError, UnauthorizedError, ValidationError, convertOpenApiSchemas, convertRouteSchema, createCircuitBreaker, createCircuitBreakerRegistry, createDomainError, createError, createQueryParser, createStateMachine, defineCompensation, defineErrorMapper, defineGuard, deleteResponse, errorResponseSchema, getDefaultCrudSchemas, getListQueryParams, handleRaw, hasEvents, isArcError, isJsonSchema, isZodSchema, itemResponse, listResponse, mutationResponse, paginationSchema, queryParams, responses, simpleEqualityMatcher, successResponseSchema, toJsonSchema, withCompensation, wrapResponse };
|
|
@@ -1,4 +1,287 @@
|
|
|
1
|
-
import { t as ArcError } from "./errors-
|
|
1
|
+
import { t as ArcError } from "./errors-BqdUDja_.mjs";
|
|
2
|
+
//#region src/utils/circuitBreaker.ts
|
|
3
|
+
/**
|
|
4
|
+
* Circuit Breaker Pattern
|
|
5
|
+
*
|
|
6
|
+
* Wraps external service calls with failure protection.
|
|
7
|
+
* Prevents cascading failures by "opening" the circuit when
|
|
8
|
+
* a service is failing, allowing it time to recover.
|
|
9
|
+
*
|
|
10
|
+
* States:
|
|
11
|
+
* - CLOSED: Normal operation, requests pass through
|
|
12
|
+
* - OPEN: Too many failures, all requests fail fast
|
|
13
|
+
* - HALF_OPEN: Testing if service recovered, limited requests
|
|
14
|
+
*
|
|
15
|
+
* @example
|
|
16
|
+
* import { CircuitBreaker } from '@classytic/arc/utils';
|
|
17
|
+
*
|
|
18
|
+
* const paymentBreaker = new CircuitBreaker(async (amount) => {
|
|
19
|
+
* return await stripe.charges.create({ amount });
|
|
20
|
+
* }, {
|
|
21
|
+
* failureThreshold: 5,
|
|
22
|
+
* resetTimeout: 30000,
|
|
23
|
+
* timeout: 5000,
|
|
24
|
+
* });
|
|
25
|
+
*
|
|
26
|
+
* try {
|
|
27
|
+
* const result = await paymentBreaker.call(100);
|
|
28
|
+
* } catch (error) {
|
|
29
|
+
* // Handle failure or circuit open
|
|
30
|
+
* }
|
|
31
|
+
*/
|
|
32
|
+
const CircuitState = {
|
|
33
|
+
CLOSED: "CLOSED",
|
|
34
|
+
OPEN: "OPEN",
|
|
35
|
+
HALF_OPEN: "HALF_OPEN"
|
|
36
|
+
};
|
|
37
|
+
var CircuitBreakerError = class extends Error {
|
|
38
|
+
state;
|
|
39
|
+
constructor(message, state) {
|
|
40
|
+
super(message);
|
|
41
|
+
this.name = "CircuitBreakerError";
|
|
42
|
+
this.state = state;
|
|
43
|
+
}
|
|
44
|
+
};
|
|
45
|
+
var CircuitBreaker = class {
|
|
46
|
+
state = CircuitState.CLOSED;
|
|
47
|
+
failures = 0;
|
|
48
|
+
successes = 0;
|
|
49
|
+
totalCalls = 0;
|
|
50
|
+
nextAttempt = 0;
|
|
51
|
+
lastCallAt = null;
|
|
52
|
+
openedAt = null;
|
|
53
|
+
failureThreshold;
|
|
54
|
+
resetTimeout;
|
|
55
|
+
timeout;
|
|
56
|
+
successThreshold;
|
|
57
|
+
fallback;
|
|
58
|
+
onStateChange;
|
|
59
|
+
onError;
|
|
60
|
+
name;
|
|
61
|
+
fn;
|
|
62
|
+
constructor(fn, options = {}) {
|
|
63
|
+
this.fn = fn;
|
|
64
|
+
this.failureThreshold = options.failureThreshold ?? 5;
|
|
65
|
+
this.resetTimeout = options.resetTimeout ?? 6e4;
|
|
66
|
+
this.timeout = options.timeout ?? 1e4;
|
|
67
|
+
this.successThreshold = options.successThreshold ?? 1;
|
|
68
|
+
this.fallback = options.fallback;
|
|
69
|
+
this.onStateChange = options.onStateChange;
|
|
70
|
+
this.onError = options.onError;
|
|
71
|
+
this.name = options.name ?? "CircuitBreaker";
|
|
72
|
+
}
|
|
73
|
+
/**
|
|
74
|
+
* Call the wrapped function with circuit breaker protection
|
|
75
|
+
*/
|
|
76
|
+
async call(...args) {
|
|
77
|
+
this.totalCalls++;
|
|
78
|
+
this.lastCallAt = Date.now();
|
|
79
|
+
if (this.state === CircuitState.OPEN) {
|
|
80
|
+
if (Date.now() < this.nextAttempt) {
|
|
81
|
+
const error = new CircuitBreakerError(`Circuit breaker is OPEN for ${this.name}`, CircuitState.OPEN);
|
|
82
|
+
if (this.fallback) return this.fallback(...args);
|
|
83
|
+
throw error;
|
|
84
|
+
}
|
|
85
|
+
this.setState(CircuitState.HALF_OPEN);
|
|
86
|
+
}
|
|
87
|
+
try {
|
|
88
|
+
const result = await this.executeWithTimeout(args);
|
|
89
|
+
this.onSuccess();
|
|
90
|
+
return result;
|
|
91
|
+
} catch (err) {
|
|
92
|
+
this.onFailure(err instanceof Error ? err : new Error(String(err)));
|
|
93
|
+
throw err;
|
|
94
|
+
}
|
|
95
|
+
}
|
|
96
|
+
/**
|
|
97
|
+
* Execute function with timeout
|
|
98
|
+
*/
|
|
99
|
+
async executeWithTimeout(args) {
|
|
100
|
+
return new Promise((resolve, reject) => {
|
|
101
|
+
const timeoutId = setTimeout(() => {
|
|
102
|
+
reject(/* @__PURE__ */ new Error(`Request timeout after ${this.timeout}ms`));
|
|
103
|
+
}, this.timeout);
|
|
104
|
+
this.fn(...args).then((result) => {
|
|
105
|
+
clearTimeout(timeoutId);
|
|
106
|
+
resolve(result);
|
|
107
|
+
}).catch((error) => {
|
|
108
|
+
clearTimeout(timeoutId);
|
|
109
|
+
reject(error);
|
|
110
|
+
});
|
|
111
|
+
});
|
|
112
|
+
}
|
|
113
|
+
/**
|
|
114
|
+
* Handle successful call
|
|
115
|
+
*/
|
|
116
|
+
onSuccess() {
|
|
117
|
+
this.failures = 0;
|
|
118
|
+
this.successes++;
|
|
119
|
+
if (this.state === CircuitState.HALF_OPEN) {
|
|
120
|
+
if (this.successes >= this.successThreshold) {
|
|
121
|
+
this.setState(CircuitState.CLOSED);
|
|
122
|
+
this.successes = 0;
|
|
123
|
+
}
|
|
124
|
+
}
|
|
125
|
+
}
|
|
126
|
+
/**
|
|
127
|
+
* Handle failed call
|
|
128
|
+
*/
|
|
129
|
+
onFailure(error) {
|
|
130
|
+
this.failures++;
|
|
131
|
+
this.successes = 0;
|
|
132
|
+
if (this.onError) this.onError(error);
|
|
133
|
+
if (this.state === CircuitState.HALF_OPEN || this.failures >= this.failureThreshold) {
|
|
134
|
+
this.setState(CircuitState.OPEN);
|
|
135
|
+
this.nextAttempt = Date.now() + this.resetTimeout;
|
|
136
|
+
this.openedAt = Date.now();
|
|
137
|
+
}
|
|
138
|
+
}
|
|
139
|
+
/**
|
|
140
|
+
* Change circuit state
|
|
141
|
+
*/
|
|
142
|
+
setState(newState) {
|
|
143
|
+
const oldState = this.state;
|
|
144
|
+
if (oldState !== newState) {
|
|
145
|
+
this.state = newState;
|
|
146
|
+
if (this.onStateChange) this.onStateChange(oldState, newState);
|
|
147
|
+
}
|
|
148
|
+
}
|
|
149
|
+
/**
|
|
150
|
+
* Manually open the circuit
|
|
151
|
+
*/
|
|
152
|
+
open() {
|
|
153
|
+
this.setState(CircuitState.OPEN);
|
|
154
|
+
this.nextAttempt = Date.now() + this.resetTimeout;
|
|
155
|
+
this.openedAt = Date.now();
|
|
156
|
+
}
|
|
157
|
+
/**
|
|
158
|
+
* Manually close the circuit
|
|
159
|
+
*/
|
|
160
|
+
close() {
|
|
161
|
+
this.failures = 0;
|
|
162
|
+
this.successes = 0;
|
|
163
|
+
this.setState(CircuitState.CLOSED);
|
|
164
|
+
this.openedAt = null;
|
|
165
|
+
}
|
|
166
|
+
/**
|
|
167
|
+
* Get current statistics
|
|
168
|
+
*/
|
|
169
|
+
getStats() {
|
|
170
|
+
return {
|
|
171
|
+
name: this.name,
|
|
172
|
+
state: this.state,
|
|
173
|
+
failures: this.failures,
|
|
174
|
+
successes: this.successes,
|
|
175
|
+
totalCalls: this.totalCalls,
|
|
176
|
+
openedAt: this.openedAt,
|
|
177
|
+
lastCallAt: this.lastCallAt
|
|
178
|
+
};
|
|
179
|
+
}
|
|
180
|
+
/**
|
|
181
|
+
* Get current state
|
|
182
|
+
*/
|
|
183
|
+
getState() {
|
|
184
|
+
return this.state;
|
|
185
|
+
}
|
|
186
|
+
/**
|
|
187
|
+
* Check if circuit is open
|
|
188
|
+
*/
|
|
189
|
+
isOpen() {
|
|
190
|
+
return this.state === CircuitState.OPEN;
|
|
191
|
+
}
|
|
192
|
+
/**
|
|
193
|
+
* Check if circuit is closed
|
|
194
|
+
*/
|
|
195
|
+
isClosed() {
|
|
196
|
+
return this.state === CircuitState.CLOSED;
|
|
197
|
+
}
|
|
198
|
+
/**
|
|
199
|
+
* Reset statistics
|
|
200
|
+
*/
|
|
201
|
+
reset() {
|
|
202
|
+
this.failures = 0;
|
|
203
|
+
this.successes = 0;
|
|
204
|
+
this.totalCalls = 0;
|
|
205
|
+
this.lastCallAt = null;
|
|
206
|
+
this.openedAt = null;
|
|
207
|
+
this.setState(CircuitState.CLOSED);
|
|
208
|
+
}
|
|
209
|
+
};
|
|
210
|
+
/**
|
|
211
|
+
* Create a circuit breaker with sensible defaults
|
|
212
|
+
*
|
|
213
|
+
* @example
|
|
214
|
+
* const emailBreaker = createCircuitBreaker(
|
|
215
|
+
* async (to, subject, body) => sendEmail(to, subject, body),
|
|
216
|
+
* { name: 'email-service' }
|
|
217
|
+
* );
|
|
218
|
+
*/
|
|
219
|
+
function createCircuitBreaker(fn, options) {
|
|
220
|
+
return new CircuitBreaker(fn, options);
|
|
221
|
+
}
|
|
222
|
+
/**
|
|
223
|
+
* Circuit breaker registry for managing multiple breakers
|
|
224
|
+
*/
|
|
225
|
+
var CircuitBreakerRegistry = class {
|
|
226
|
+
breakers = /* @__PURE__ */ new Map();
|
|
227
|
+
/**
|
|
228
|
+
* Register a circuit breaker
|
|
229
|
+
*/
|
|
230
|
+
register(name, fn, options) {
|
|
231
|
+
const breaker = new CircuitBreaker(fn, {
|
|
232
|
+
...options,
|
|
233
|
+
name
|
|
234
|
+
});
|
|
235
|
+
this.breakers.set(name, breaker);
|
|
236
|
+
return breaker;
|
|
237
|
+
}
|
|
238
|
+
/**
|
|
239
|
+
* Get a circuit breaker by name
|
|
240
|
+
*/
|
|
241
|
+
get(name) {
|
|
242
|
+
return this.breakers.get(name);
|
|
243
|
+
}
|
|
244
|
+
/**
|
|
245
|
+
* Get all breakers
|
|
246
|
+
*/
|
|
247
|
+
getAll() {
|
|
248
|
+
return this.breakers;
|
|
249
|
+
}
|
|
250
|
+
/**
|
|
251
|
+
* Get statistics for all breakers
|
|
252
|
+
*/
|
|
253
|
+
getAllStats() {
|
|
254
|
+
const stats = {};
|
|
255
|
+
for (const [name, breaker] of this.breakers.entries()) stats[name] = breaker.getStats();
|
|
256
|
+
return stats;
|
|
257
|
+
}
|
|
258
|
+
/**
|
|
259
|
+
* Reset all breakers
|
|
260
|
+
*/
|
|
261
|
+
resetAll() {
|
|
262
|
+
for (const breaker of this.breakers.values()) breaker.reset();
|
|
263
|
+
}
|
|
264
|
+
/**
|
|
265
|
+
* Open all breakers
|
|
266
|
+
*/
|
|
267
|
+
openAll() {
|
|
268
|
+
for (const breaker of this.breakers.values()) breaker.open();
|
|
269
|
+
}
|
|
270
|
+
/**
|
|
271
|
+
* Close all breakers
|
|
272
|
+
*/
|
|
273
|
+
closeAll() {
|
|
274
|
+
for (const breaker of this.breakers.values()) breaker.close();
|
|
275
|
+
}
|
|
276
|
+
};
|
|
277
|
+
/**
|
|
278
|
+
* Create a new CircuitBreakerRegistry instance.
|
|
279
|
+
* Use this instead of a global singleton — attach to fastify.arc or pass explicitly.
|
|
280
|
+
*/
|
|
281
|
+
function createCircuitBreakerRegistry() {
|
|
282
|
+
return new CircuitBreakerRegistry();
|
|
283
|
+
}
|
|
284
|
+
//#endregion
|
|
2
285
|
//#region src/utils/compensation.ts
|
|
3
286
|
/**
|
|
4
287
|
* Run steps in order with automatic compensation on failure.
|
|
@@ -70,6 +353,24 @@ function defineCompensation(name, steps) {
|
|
|
70
353
|
};
|
|
71
354
|
}
|
|
72
355
|
//#endregion
|
|
356
|
+
//#region src/utils/defineErrorMapper.ts
|
|
357
|
+
/**
|
|
358
|
+
* Register an `ErrorMapper` with its domain-specific generic argument and
|
|
359
|
+
* have it assign cleanly into `ErrorMapper[]` (no `as unknown as ErrorMapper`).
|
|
360
|
+
*
|
|
361
|
+
* The returned mapper is identical at runtime — `type` and `toResponse` are
|
|
362
|
+
* passed through untouched. Only the declared type widens from
|
|
363
|
+
* `ErrorMapper<T>` to `ErrorMapper` so the array inference works.
|
|
364
|
+
*
|
|
365
|
+
* Safety: the `errorHandlerPlugin` dispatches via `error instanceof mapper.type`
|
|
366
|
+
* before invoking `toResponse`, so the widened callback signature is never
|
|
367
|
+
* called with a non-`T` error at runtime. This helper codifies that invariant
|
|
368
|
+
* in one place.
|
|
369
|
+
*/
|
|
370
|
+
function defineErrorMapper(mapper) {
|
|
371
|
+
return mapper;
|
|
372
|
+
}
|
|
373
|
+
//#endregion
|
|
73
374
|
//#region src/utils/defineGuard.ts
|
|
74
375
|
/** Hidden property key for guard context storage on the request object. */
|
|
75
376
|
const GUARD_STORE_KEY = "__arcGuardContext";
|
|
@@ -643,4 +944,4 @@ function createStateMachine(name, transitions = {}, options = {}) {
|
|
|
643
944
|
};
|
|
644
945
|
}
|
|
645
946
|
//#endregion
|
|
646
|
-
export {
|
|
947
|
+
export { createCircuitBreaker as C, CircuitState as S, defineCompensation as _, getListQueryParams as a, CircuitBreakerError as b, mutationResponse as c, responses as d, successResponseSchema as f, defineErrorMapper as g, defineGuard as h, getDefaultCrudSchemas as i, paginationSchema as l, handleRaw as m, deleteResponse as n, itemResponse as o, wrapResponse as p, errorResponseSchema as r, listResponse as s, createStateMachine as t, queryParams as u, withCompensation as v, createCircuitBreakerRegistry as w, CircuitBreakerRegistry as x, CircuitBreaker as y };
|