@classytic/arc 2.15.4 → 2.16.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +1 -0
- package/bin/arc.js +12 -0
- package/dist/{BaseController-dx3m2J8V.mjs → BaseController-DlCCTIxJ.mjs} +61 -19
- package/dist/{HookSystem-Iiebom92.mjs → HookSystem-Cmf7-Etp.mjs} +8 -4
- package/dist/{QueryCache-D41bfdBB.d.mts → QueryCache-SvmT_9ti.d.mts} +1 -1
- package/dist/{ResourceRegistry-CTERg_2x.mjs → ResourceRegistry-f48hFk3m.mjs} +52 -9
- package/dist/audit/index.d.mts +1 -1
- package/dist/audit/index.mjs +4 -2
- package/dist/auth/index.d.mts +4 -4
- package/dist/auth/index.mjs +4 -4
- package/dist/auth/redis-session.d.mts +1 -1
- package/dist/{betterAuthOpenApi--M_i87dQ.mjs → betterAuthOpenApi-ClWxaceA.mjs} +10 -6
- package/dist/buildHandler-BZX6zzDM.mjs +300 -0
- package/dist/cache/index.d.mts +3 -3
- package/dist/cache/index.mjs +3 -3
- package/dist/{caching-SM8gghN6.mjs → caching-TeHE8G-v.mjs} +1 -1
- package/dist/cli/commands/describe.d.mts +35 -1
- package/dist/cli/commands/describe.mjs +52 -12
- package/dist/cli/commands/docs.d.mts +1 -4
- package/dist/cli/commands/docs.mjs +4 -16
- package/dist/cli/commands/generate.d.mts +2 -20
- package/dist/cli/commands/generate.mjs +1 -546
- package/dist/cli/commands/init.d.mts +2 -40
- package/dist/cli/commands/init.mjs +1 -3045
- package/dist/cli/commands/introspect.mjs +53 -64
- package/dist/cli/index.d.mts +2 -2
- package/dist/cli/index.mjs +2 -2
- package/dist/{constants-Cxde4rpC.mjs → constants-TrJVIJl0.mjs} +7 -0
- package/dist/core/index.d.mts +3 -3
- package/dist/core/index.mjs +5 -5
- package/dist/{core-CvmOqEms.mjs → core-DBJ_j6rX.mjs} +222 -44
- package/dist/createActionRouter-DUpN3Dd1.mjs +288 -0
- package/dist/{createAggregationRouter-B0bPDf5b.mjs → createAggregationRouter-Dq-TUCuY.mjs} +3 -2
- package/dist/{createApp-PFegs47-.mjs → createApp-DNccuhyI.mjs} +16 -14
- package/dist/{defineEvent-D5h7EvAx.mjs → defineEvent-DRwY0fYm.mjs} +1 -1
- package/dist/docs/index.d.mts +2 -2
- package/dist/docs/index.mjs +1 -1
- package/dist/{errorHandler-Bk-AGhkU.mjs → errorHandler-DpoXQHZ9.mjs} +17 -14
- package/dist/errors-C1lX_jlm.d.mts +91 -0
- package/dist/{eventPlugin-CaKTYkYM.mjs → eventPlugin-C2cGqtRO.mjs} +1 -1
- package/dist/{eventPlugin-qXpqTebY.d.mts → eventPlugin-CtHC_av1.d.mts} +1 -1
- package/dist/events/index.d.mts +3 -3
- package/dist/events/index.mjs +5 -5
- 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 +2 -2
- package/dist/{fields-COhcH3fk.d.mts → fields-Anj0xdih.d.mts} +1 -1
- package/dist/generate-BWFwgcCM.d.mts +38 -0
- package/dist/generate-CYac-OLv.mjs +654 -0
- package/dist/hooks/index.d.mts +1 -1
- package/dist/hooks/index.mjs +1 -1
- package/dist/idempotency/index.d.mts +2 -2
- package/dist/idempotency/index.mjs +1 -1
- package/dist/idempotency/redis.d.mts +1 -1
- package/dist/{index-BTqLEvhu.d.mts → index-3oIimXQn.d.mts} +12 -12
- package/dist/{index-BstGxcc3.d.mts → index-B-ulKx5P.d.mts} +55 -4
- package/dist/{index-BswOSJCE.d.mts → index-CkW0flkU.d.mts} +355 -16
- package/dist/index.d.mts +6 -6
- package/dist/index.mjs +7 -8
- package/dist/init-Dv71MsJr.d.mts +71 -0
- package/dist/init-HDvoO9L5.mjs +3098 -0
- package/dist/integrations/event-gateway.d.mts +2 -2
- package/dist/integrations/event-gateway.mjs +1 -1
- package/dist/integrations/index.d.mts +2 -2
- package/dist/integrations/jobs.mjs +3 -3
- package/dist/integrations/mcp/index.d.mts +239 -7
- package/dist/integrations/mcp/index.mjs +2 -528
- package/dist/integrations/mcp/testing.d.mts +2 -2
- package/dist/integrations/mcp/testing.mjs +6 -10
- package/dist/integrations/streamline.mjs +26 -1
- package/dist/integrations/websocket-redis.d.mts +1 -1
- package/dist/integrations/websocket.d.mts +1 -1
- package/dist/integrations/websocket.mjs +1 -0
- package/dist/loadResourcesFromEntry-BLMEI2Xa.mjs +51 -0
- package/dist/{resourceToTools-tFYUNmM0.mjs → mcpPlugin-7vGV51ED.mjs} +1021 -318
- package/dist/{memory-UBydS5ku.mjs → memory-QOLe11D5.mjs} +2 -0
- package/dist/middleware/index.d.mts +1 -1
- package/dist/middleware/index.mjs +1 -1
- package/dist/{openapi-BHXhoX8O.mjs → openapi-34T9yNwd.mjs} +47 -36
- package/dist/permissions/index.d.mts +2 -2
- package/dist/permissions/index.mjs +1 -1
- package/dist/{permissions-ohQyv50e.mjs → permissions-CTxMrreC.mjs} +2 -2
- package/dist/{pipe-Zr0KXjQe.mjs → pipe-DiCyvyPN.mjs} +1 -0
- package/dist/pipeline/index.d.mts +1 -1
- package/dist/pipeline/index.mjs +1 -1
- package/dist/plugins/index.d.mts +5 -5
- package/dist/plugins/index.mjs +10 -10
- package/dist/plugins/response-cache.mjs +5 -5
- package/dist/plugins/tracing-entry.d.mts +1 -1
- package/dist/plugins/tracing-entry.mjs +1 -1
- package/dist/{pluralize-DQgqgifU.mjs → pluralize-B9M8xvy-.mjs} +2 -1
- package/dist/presets/filesUpload.d.mts +4 -4
- package/dist/presets/filesUpload.mjs +2 -2
- package/dist/presets/index.d.mts +1 -1
- package/dist/presets/index.mjs +1 -1
- package/dist/presets/multiTenant.d.mts +1 -1
- package/dist/presets/multiTenant.mjs +4 -3
- package/dist/presets/search.d.mts +2 -2
- package/dist/presets/search.mjs +1 -1
- package/dist/{presets-BbkjdPeH.mjs → presets-C9BE6WaZ.mjs} +2 -2
- package/dist/{queryCachePlugin-m1XsgAIJ.mjs → queryCachePlugin-B4XMSSe7.mjs} +2 -2
- package/dist/{queryCachePlugin-CqMdLI2-.d.mts → queryCachePlugin-Biqzfbi5.d.mts} +2 -2
- package/dist/{redis-DiMkdHEl.d.mts → redis-Cyzrz6SX.d.mts} +1 -1
- package/dist/{redis-stream-D6HzR1Z_.d.mts → redis-stream-DT-YjzrB.d.mts} +1 -1
- package/dist/registry/index.d.mts +319 -2
- package/dist/registry/index.mjs +3 -3
- package/dist/registry-BBE23CDj.mjs +576 -0
- package/dist/{routerShared-DrOa-26E.mjs → routerShared-CZV5aabX.mjs} +3 -3
- package/dist/scope/index.d.mts +3 -3
- package/dist/scope/index.mjs +3 -3
- package/dist/{sse-Bz-5ZeTt.mjs → sse-BY6sTy4P.mjs} +1 -1
- package/dist/testing/index.d.mts +2 -2
- package/dist/testing/index.mjs +16 -7
- package/dist/testing/storageContract.d.mts +1 -1
- package/dist/types/index.d.mts +5 -5
- package/dist/types/storage.d.mts +1 -1
- package/dist/{types-C_s5moIu.mjs → types-Bi0r0vjG.mjs} +53 -1
- package/dist/{types-BQsjgQzS.d.mts → types-BsJMEQ4D.d.mts} +106 -12
- package/dist/{types-DrBaUwyV.d.mts → types-D-fYtKjb.d.mts} +33 -10
- package/dist/{types-CTYvcwHe.d.mts → types-DVfpSfx2.d.mts} +42 -1
- package/dist/utils/index.d.mts +1286 -2
- package/dist/utils/index.mjs +1 -1
- package/dist/{utils-_h9B3c57.mjs → utils-DC5ycPfr.mjs} +89 -40
- package/dist/{buildHandler-CcFOpJLh.mjs → validate-By96rH0r.mjs} +8 -299
- package/dist/{versioning-hmkPcDlX.d.mts → versioning-ZwX9tmbS.d.mts} +1 -1
- package/package.json +21 -28
- package/skills/arc/SKILL.md +300 -706
- package/skills/arc/references/auth.md +19 -7
- package/skills/arc-code-review/SKILL.md +1 -1
- package/skills/arc-code-review/references/arc-cheatsheet.md +100 -322
- package/dist/createActionRouter-S3MLVYot.mjs +0 -220
- package/dist/index-bRjYu21O.d.mts +0 -1320
- package/dist/org/index.d.mts +0 -66
- package/dist/org/index.mjs +0 -486
- package/dist/org/types.d.mts +0 -82
- package/dist/org/types.mjs +0 -1
- package/dist/registry-I-ogLgL9.mjs +0 -46
- /package/dist/{EventTransport-CT_52aWU.d.mts → EventTransport-C-2oAHtw.d.mts} +0 -0
- /package/dist/{EventTransport-DLWoUMHy.mjs → EventTransport-Hxvv5QQz.mjs} +0 -0
- /package/dist/{actionPermissions-CyUkQu6O.mjs → actionPermissions-Bjmvn7Eb.mjs} +0 -0
- /package/dist/{elevation-BXOWoGCF.d.mts → elevation-0YBpa663.d.mts} +0 -0
- /package/dist/{elevation-DgoeTyfX.mjs → elevation-Dci0AYLT.mjs} +0 -0
- /package/dist/{errorHandler-DFr45ZG4.d.mts → errorHandler-mHuyWzZE.d.mts} +0 -0
- /package/dist/{externalPaths-BD5nw6St.d.mts → externalPaths-DFg-2KTp.d.mts} +0 -0
- /package/dist/{interface-beEtJyWM.d.mts → interface-CH0OQudo.d.mts} +0 -0
- /package/dist/{interface-DfLGcus7.d.mts → interface-NwJ_qPlY.d.mts} +0 -0
- /package/dist/{keys-CGcCbNyu.mjs → keys-DopsCuyQ.mjs} +0 -0
- /package/dist/{loadResources-DBMQg_Aj.mjs → loadResources-ChQEj8ih.mjs} +0 -0
- /package/dist/{metrics-Qnvwc-LQ.mjs → metrics-TuOmguhi.mjs} +0 -0
- /package/dist/{replyHelpers-CK-FNO8E.mjs → replyHelpers-C-gD32oF.mjs} +0 -0
- /package/dist/{schemaIR-lYhC2gE5.mjs → schemaIR-Ctc89DSn.mjs} +0 -0
- /package/dist/{sessionManager-C4Le_UB3.d.mts → sessionManager-BqFegc0W.d.mts} +0 -0
- /package/dist/{storage-Dfzt4VTl.d.mts → storage-D2KZJAmn.d.mts} +0 -0
- /package/dist/{store-helpers-BkIN9-vu.mjs → store-helpers-B0sunfZZ.mjs} +0 -0
- /package/dist/{tracing-QJVprktp.d.mts → tracing-Dm8n7Cnn.d.mts} +0 -0
- /package/dist/{versioning-BUrT5aP4.mjs → versioning-B6mimogM.mjs} +0 -0
- /package/dist/{websocket-ChC2rqe1.d.mts → websocket-BkjeGZRn.d.mts} +0 -0
|
@@ -1,1320 +0,0 @@
|
|
|
1
|
-
import { $ as OpenApiSchemas, S as QueryParserInterface, Wt as AnyRecord, Yt as UserLike, at as ResourceConfig, b as ParsedQuery } from "./index-BswOSJCE.mjs";
|
|
2
|
-
import { n as ErrorMapper } from "./errorHandler-DFr45ZG4.mjs";
|
|
3
|
-
import { HttpError, errorContractSchema as errorContractSchema$1, errorDetailSchema as errorDetailSchema$1 } from "@classytic/repo-core/errors";
|
|
4
|
-
import { FastifyInstance, FastifyReply, FastifyRequest, RouteHandlerMethod } from "fastify";
|
|
5
|
-
|
|
6
|
-
//#region src/utils/errors.d.ts
|
|
7
|
-
interface ErrorOptions {
|
|
8
|
-
code?: string;
|
|
9
|
-
statusCode?: number;
|
|
10
|
-
details?: Record<string, unknown>;
|
|
11
|
-
cause?: Error;
|
|
12
|
-
}
|
|
13
|
-
/**
|
|
14
|
-
* Base Arc Error. Implements the canonical `HttpError` contract — `status`
|
|
15
|
-
* mirrors `statusCode` and `meta` mirrors `details`, so consumers reading
|
|
16
|
-
* either name see the same value without adapter glue.
|
|
17
|
-
*/
|
|
18
|
-
declare class ArcError extends Error implements HttpError {
|
|
19
|
-
name: string;
|
|
20
|
-
readonly code: string;
|
|
21
|
-
readonly statusCode: number;
|
|
22
|
-
readonly details?: Record<string, unknown>;
|
|
23
|
-
readonly cause?: Error;
|
|
24
|
-
constructor(message: string, options?: ErrorOptions);
|
|
25
|
-
/** `HttpError.status` mirror — repo-core's `toErrorContract` reads this. */
|
|
26
|
-
get status(): number;
|
|
27
|
-
/** `HttpError.meta` mirror — `details` under the canonical name. */
|
|
28
|
-
get meta(): Record<string, unknown> | undefined;
|
|
29
|
-
}
|
|
30
|
-
declare class NotFoundError extends ArcError {
|
|
31
|
-
constructor(resource: string, identifier?: string);
|
|
32
|
-
}
|
|
33
|
-
declare class ValidationError extends ArcError {
|
|
34
|
-
readonly errors: ReadonlyArray<{
|
|
35
|
-
field: string;
|
|
36
|
-
message: string;
|
|
37
|
-
}>;
|
|
38
|
-
constructor(message: string, errors?: Array<{
|
|
39
|
-
field: string;
|
|
40
|
-
message: string;
|
|
41
|
-
}>);
|
|
42
|
-
}
|
|
43
|
-
declare class UnauthorizedError extends ArcError {
|
|
44
|
-
constructor(message?: string);
|
|
45
|
-
}
|
|
46
|
-
declare class ForbiddenError extends ArcError {
|
|
47
|
-
constructor(message?: string);
|
|
48
|
-
}
|
|
49
|
-
declare class ConflictError extends ArcError {
|
|
50
|
-
constructor(message: string, field?: string);
|
|
51
|
-
}
|
|
52
|
-
declare class OrgRequiredError extends ArcError {
|
|
53
|
-
readonly organizations?: ReadonlyArray<{
|
|
54
|
-
id: string;
|
|
55
|
-
roles?: string[];
|
|
56
|
-
}>;
|
|
57
|
-
constructor(message: string, organizations?: Array<{
|
|
58
|
-
id: string;
|
|
59
|
-
roles?: string[];
|
|
60
|
-
}>);
|
|
61
|
-
}
|
|
62
|
-
declare class OrgAccessDeniedError extends ArcError {
|
|
63
|
-
constructor(orgId?: string);
|
|
64
|
-
}
|
|
65
|
-
declare class RateLimitError extends ArcError {
|
|
66
|
-
readonly retryAfter?: number;
|
|
67
|
-
constructor(message?: string, retryAfter?: number);
|
|
68
|
-
}
|
|
69
|
-
declare class ServiceUnavailableError extends ArcError {
|
|
70
|
-
constructor(message?: string);
|
|
71
|
-
}
|
|
72
|
-
/**
|
|
73
|
-
* Quick `ArcError` constructor when the bundled subclasses don't fit.
|
|
74
|
-
*
|
|
75
|
-
* If `details.code` is a string, it's lifted to the top-level `error.code`
|
|
76
|
-
* (the canonical wire slot for business signals — `'ORG_CONTEXT_REQUIRED'`,
|
|
77
|
-
* `'ALL_FIELDS_STRIPPED'`, etc). The same code stays in `details` so
|
|
78
|
-
* in-process consumers reading `error.details.code` still work. Without
|
|
79
|
-
* this lift, business codes get buried in `details` and the wire envelope
|
|
80
|
-
* carries only the status-derived `arc.forbidden` / `arc.bad_request`
|
|
81
|
-
* fallback — `repo-core`'s `toErrorContract` doesn't surface free-form
|
|
82
|
-
* `details` objects (its `details[]` array is reserved for validation /
|
|
83
|
-
* duplicate-key items).
|
|
84
|
-
*/
|
|
85
|
-
declare function createError(statusCode: number, message: string, details?: Record<string, unknown>): ArcError;
|
|
86
|
-
/**
|
|
87
|
-
* Domain-error escape hatch. Use a hierarchical code that scopes the error
|
|
88
|
-
* to your package (`'commerce.cart.locked'`, `'payment.gateway.timeout'`).
|
|
89
|
-
*/
|
|
90
|
-
declare function createDomainError(code: string, message: string, statusCode?: number, details?: Record<string, unknown>): ArcError;
|
|
91
|
-
/** Type guard. */
|
|
92
|
-
declare function isArcError(error: unknown): error is ArcError;
|
|
93
|
-
//#endregion
|
|
94
|
-
//#region src/core/validateResourceConfig.d.ts
|
|
95
|
-
interface ConfigError {
|
|
96
|
-
field: string;
|
|
97
|
-
message: string;
|
|
98
|
-
suggestion?: string;
|
|
99
|
-
}
|
|
100
|
-
interface ValidationResult {
|
|
101
|
-
valid: boolean;
|
|
102
|
-
errors: ConfigError[];
|
|
103
|
-
warnings: ConfigError[];
|
|
104
|
-
}
|
|
105
|
-
interface ValidateOptions {
|
|
106
|
-
/** Skip controller method validation (for testing) */
|
|
107
|
-
skipControllerCheck?: boolean;
|
|
108
|
-
/** Allow unknown preset names */
|
|
109
|
-
allowUnknownPresets?: boolean;
|
|
110
|
-
/** Custom valid permission keys beyond CRUD */
|
|
111
|
-
additionalPermissionKeys?: string[];
|
|
112
|
-
}
|
|
113
|
-
/**
|
|
114
|
-
* Validate a resource configuration
|
|
115
|
-
*/
|
|
116
|
-
declare function validateResourceConfig(config: ResourceConfig, options?: ValidateOptions): ValidationResult;
|
|
117
|
-
/**
|
|
118
|
-
* Format validation errors for display
|
|
119
|
-
*/
|
|
120
|
-
declare function formatValidationErrors(resourceName: string, result: ValidationResult): string;
|
|
121
|
-
/**
|
|
122
|
-
* Validate and throw if invalid
|
|
123
|
-
*/
|
|
124
|
-
declare function assertValidConfig(config: ResourceConfig, options?: ValidateOptions): void;
|
|
125
|
-
//#endregion
|
|
126
|
-
//#region src/utils/circuitBreaker.d.ts
|
|
127
|
-
/**
|
|
128
|
-
* Circuit Breaker Pattern
|
|
129
|
-
*
|
|
130
|
-
* Wraps external service calls with failure protection.
|
|
131
|
-
* Prevents cascading failures by "opening" the circuit when
|
|
132
|
-
* a service is failing, allowing it time to recover.
|
|
133
|
-
*
|
|
134
|
-
* States:
|
|
135
|
-
* - CLOSED: Normal operation, requests pass through
|
|
136
|
-
* - OPEN: Too many failures, all requests fail fast
|
|
137
|
-
* - HALF_OPEN: Testing if service recovered, limited requests
|
|
138
|
-
*
|
|
139
|
-
* @example
|
|
140
|
-
* import { CircuitBreaker } from '@classytic/arc/utils';
|
|
141
|
-
*
|
|
142
|
-
* const paymentBreaker = new CircuitBreaker(async (amount) => {
|
|
143
|
-
* return await stripe.charges.create({ amount });
|
|
144
|
-
* }, {
|
|
145
|
-
* failureThreshold: 5,
|
|
146
|
-
* resetTimeout: 30000,
|
|
147
|
-
* timeout: 5000,
|
|
148
|
-
* });
|
|
149
|
-
*
|
|
150
|
-
* try {
|
|
151
|
-
* const result = await paymentBreaker.call(100);
|
|
152
|
-
* } catch (error) {
|
|
153
|
-
* // Handle failure or circuit open
|
|
154
|
-
* }
|
|
155
|
-
*/
|
|
156
|
-
declare const CircuitState: {
|
|
157
|
-
readonly CLOSED: "CLOSED";
|
|
158
|
-
readonly OPEN: "OPEN";
|
|
159
|
-
readonly HALF_OPEN: "HALF_OPEN";
|
|
160
|
-
};
|
|
161
|
-
type CircuitState = (typeof CircuitState)[keyof typeof CircuitState];
|
|
162
|
-
interface CircuitBreakerOptions {
|
|
163
|
-
/**
|
|
164
|
-
* Number of failures before opening circuit
|
|
165
|
-
* @default 5
|
|
166
|
-
*/
|
|
167
|
-
failureThreshold?: number;
|
|
168
|
-
/**
|
|
169
|
-
* Time in ms before attempting to close circuit
|
|
170
|
-
* @default 60000 (60 seconds)
|
|
171
|
-
*/
|
|
172
|
-
resetTimeout?: number;
|
|
173
|
-
/**
|
|
174
|
-
* Request timeout in ms
|
|
175
|
-
* @default 10000 (10 seconds)
|
|
176
|
-
*/
|
|
177
|
-
timeout?: number;
|
|
178
|
-
/**
|
|
179
|
-
* Number of successful requests in HALF_OPEN before closing
|
|
180
|
-
* @default 1
|
|
181
|
-
*/
|
|
182
|
-
successThreshold?: number;
|
|
183
|
-
/**
|
|
184
|
-
* Fallback function when circuit is open.
|
|
185
|
-
* Receives the same arguments as the wrapped function.
|
|
186
|
-
*/
|
|
187
|
-
fallback?: (...args: unknown[]) => Promise<unknown>;
|
|
188
|
-
/**
|
|
189
|
-
* Callback when state changes
|
|
190
|
-
*/
|
|
191
|
-
onStateChange?: (from: CircuitState, to: CircuitState) => void;
|
|
192
|
-
/**
|
|
193
|
-
* Callback on error
|
|
194
|
-
*/
|
|
195
|
-
onError?: (error: Error) => void;
|
|
196
|
-
/**
|
|
197
|
-
* Name for logging/monitoring
|
|
198
|
-
*/
|
|
199
|
-
name?: string;
|
|
200
|
-
}
|
|
201
|
-
interface CircuitBreakerStats {
|
|
202
|
-
name?: string;
|
|
203
|
-
state: CircuitState;
|
|
204
|
-
failures: number;
|
|
205
|
-
successes: number;
|
|
206
|
-
totalCalls: number;
|
|
207
|
-
openedAt: number | null;
|
|
208
|
-
lastCallAt: number | null;
|
|
209
|
-
}
|
|
210
|
-
declare class CircuitBreakerError extends Error {
|
|
211
|
-
state: CircuitState;
|
|
212
|
-
constructor(message: string, state: CircuitState);
|
|
213
|
-
}
|
|
214
|
-
declare class CircuitBreaker<T extends (...args: any[]) => Promise<any>> {
|
|
215
|
-
private state;
|
|
216
|
-
private failures;
|
|
217
|
-
private successes;
|
|
218
|
-
private totalCalls;
|
|
219
|
-
private nextAttempt;
|
|
220
|
-
private lastCallAt;
|
|
221
|
-
private openedAt;
|
|
222
|
-
private readonly failureThreshold;
|
|
223
|
-
private readonly resetTimeout;
|
|
224
|
-
private readonly timeout;
|
|
225
|
-
private readonly successThreshold;
|
|
226
|
-
private readonly fallback?;
|
|
227
|
-
private readonly onStateChange?;
|
|
228
|
-
private readonly onError?;
|
|
229
|
-
private readonly name;
|
|
230
|
-
private readonly fn;
|
|
231
|
-
constructor(fn: T, options?: CircuitBreakerOptions);
|
|
232
|
-
/**
|
|
233
|
-
* Call the wrapped function with circuit breaker protection
|
|
234
|
-
*/
|
|
235
|
-
call(...args: Parameters<T>): Promise<ReturnType<T>>;
|
|
236
|
-
/**
|
|
237
|
-
* Execute function with timeout
|
|
238
|
-
*/
|
|
239
|
-
private executeWithTimeout;
|
|
240
|
-
/**
|
|
241
|
-
* Handle successful call
|
|
242
|
-
*/
|
|
243
|
-
private onSuccess;
|
|
244
|
-
/**
|
|
245
|
-
* Handle failed call
|
|
246
|
-
*/
|
|
247
|
-
private onFailure;
|
|
248
|
-
/**
|
|
249
|
-
* Change circuit state
|
|
250
|
-
*/
|
|
251
|
-
private setState;
|
|
252
|
-
/**
|
|
253
|
-
* Manually open the circuit
|
|
254
|
-
*/
|
|
255
|
-
open(): void;
|
|
256
|
-
/**
|
|
257
|
-
* Manually close the circuit
|
|
258
|
-
*/
|
|
259
|
-
close(): void;
|
|
260
|
-
/**
|
|
261
|
-
* Get current statistics
|
|
262
|
-
*/
|
|
263
|
-
getStats(): CircuitBreakerStats;
|
|
264
|
-
/**
|
|
265
|
-
* Get current state
|
|
266
|
-
*/
|
|
267
|
-
getState(): CircuitState;
|
|
268
|
-
/**
|
|
269
|
-
* Check if circuit is open
|
|
270
|
-
*/
|
|
271
|
-
isOpen(): boolean;
|
|
272
|
-
/**
|
|
273
|
-
* Check if circuit is closed
|
|
274
|
-
*/
|
|
275
|
-
isClosed(): boolean;
|
|
276
|
-
/**
|
|
277
|
-
* Reset statistics
|
|
278
|
-
*/
|
|
279
|
-
reset(): void;
|
|
280
|
-
}
|
|
281
|
-
/**
|
|
282
|
-
* Create a circuit breaker with sensible defaults
|
|
283
|
-
*
|
|
284
|
-
* @example
|
|
285
|
-
* const emailBreaker = createCircuitBreaker(
|
|
286
|
-
* async (to, subject, body) => sendEmail(to, subject, body),
|
|
287
|
-
* { name: 'email-service' }
|
|
288
|
-
* );
|
|
289
|
-
*/
|
|
290
|
-
declare function createCircuitBreaker<T extends (...args: any[]) => Promise<any>>(fn: T, options?: CircuitBreakerOptions): CircuitBreaker<T>;
|
|
291
|
-
/**
|
|
292
|
-
* Circuit breaker registry for managing multiple breakers
|
|
293
|
-
*/
|
|
294
|
-
declare class CircuitBreakerRegistry {
|
|
295
|
-
private breakers;
|
|
296
|
-
/**
|
|
297
|
-
* Register a circuit breaker
|
|
298
|
-
*/
|
|
299
|
-
register<T extends (...args: any[]) => Promise<any>>(name: string, fn: T, options?: Omit<CircuitBreakerOptions, "name">): CircuitBreaker<T>;
|
|
300
|
-
/**
|
|
301
|
-
* Get a circuit breaker by name
|
|
302
|
-
*/
|
|
303
|
-
get(name: string): CircuitBreaker<any> | undefined;
|
|
304
|
-
/**
|
|
305
|
-
* Get all breakers
|
|
306
|
-
*/
|
|
307
|
-
getAll(): Map<string, CircuitBreaker<any>>;
|
|
308
|
-
/**
|
|
309
|
-
* Get statistics for all breakers
|
|
310
|
-
*/
|
|
311
|
-
getAllStats(): Record<string, CircuitBreakerStats>;
|
|
312
|
-
/**
|
|
313
|
-
* Reset all breakers
|
|
314
|
-
*/
|
|
315
|
-
resetAll(): void;
|
|
316
|
-
/**
|
|
317
|
-
* Open all breakers
|
|
318
|
-
*/
|
|
319
|
-
openAll(): void;
|
|
320
|
-
/**
|
|
321
|
-
* Close all breakers
|
|
322
|
-
*/
|
|
323
|
-
closeAll(): void;
|
|
324
|
-
}
|
|
325
|
-
/**
|
|
326
|
-
* Create a new CircuitBreakerRegistry instance.
|
|
327
|
-
* Use this instead of a global singleton — attach to fastify.arc or pass explicitly.
|
|
328
|
-
*/
|
|
329
|
-
declare function createCircuitBreakerRegistry(): CircuitBreakerRegistry;
|
|
330
|
-
//#endregion
|
|
331
|
-
//#region src/utils/compensation.d.ts
|
|
332
|
-
/**
|
|
333
|
-
* Compensating Transaction — In-Process Rollback Primitive
|
|
334
|
-
*
|
|
335
|
-
* Runs steps in order. If any step fails, runs compensating actions
|
|
336
|
-
* for already-completed steps in reverse. Zero dependencies.
|
|
337
|
-
*
|
|
338
|
-
* Type-safe: generic context type gives autocomplete across steps.
|
|
339
|
-
* Discriminated union result: compiler enforces checking success before
|
|
340
|
-
* accessing failedStep/error.
|
|
341
|
-
*
|
|
342
|
-
* For distributed sagas across services, use Temporal, Inngest, or Streamline.
|
|
343
|
-
*
|
|
344
|
-
* @example
|
|
345
|
-
* ```typescript
|
|
346
|
-
* interface CheckoutCtx {
|
|
347
|
-
* orderId: string;
|
|
348
|
-
* reservationId?: string;
|
|
349
|
-
* }
|
|
350
|
-
*
|
|
351
|
-
* const result = await withCompensation<CheckoutCtx>('checkout', [
|
|
352
|
-
* {
|
|
353
|
-
* name: 'reserve',
|
|
354
|
-
* execute: async (ctx) => {
|
|
355
|
-
* const res = await inventoryService.reserve(ctx.orderId);
|
|
356
|
-
* ctx.reservationId = res.id;
|
|
357
|
-
* return res;
|
|
358
|
-
* },
|
|
359
|
-
* compensate: async (ctx) => {
|
|
360
|
-
* await inventoryService.release(ctx.reservationId!);
|
|
361
|
-
* },
|
|
362
|
-
* },
|
|
363
|
-
* { name: 'notify', execute: sendEmail, fireAndForget: true },
|
|
364
|
-
* ], { orderId: 'ord-123' });
|
|
365
|
-
*
|
|
366
|
-
* if (result.success) {
|
|
367
|
-
* // result.results available, no failedStep
|
|
368
|
-
* } else {
|
|
369
|
-
* // result.failedStep and result.error guaranteed
|
|
370
|
-
* }
|
|
371
|
-
* ```
|
|
372
|
-
*/
|
|
373
|
-
/** Step definition with typed context and typed result */
|
|
374
|
-
interface CompensationStep<TCtx = Record<string, unknown>, TResult = unknown> {
|
|
375
|
-
/** Step name — used in results, logs, and hooks */
|
|
376
|
-
readonly name: string;
|
|
377
|
-
/** Execute the step — return value stored in results[name] */
|
|
378
|
-
readonly execute: (ctx: TCtx) => Promise<TResult>;
|
|
379
|
-
/** Rollback on failure — receives context and this step's own result */
|
|
380
|
-
readonly compensate?: (ctx: TCtx, stepResult: TResult) => Promise<void>;
|
|
381
|
-
/** Fire-and-forget — don't await, don't block, swallow errors, skip in rollback */
|
|
382
|
-
readonly fireAndForget?: boolean;
|
|
383
|
-
}
|
|
384
|
-
/** Lifecycle hooks for observability — wire to Arc events, metrics, or logging */
|
|
385
|
-
interface CompensationHooks {
|
|
386
|
-
readonly onStepComplete?: (stepName: string, result: unknown) => void;
|
|
387
|
-
readonly onStepFailed?: (stepName: string, error: Error) => void;
|
|
388
|
-
readonly onCompensate?: (stepName: string) => void;
|
|
389
|
-
}
|
|
390
|
-
/** Error from a compensation action that failed during rollback */
|
|
391
|
-
interface CompensationError {
|
|
392
|
-
readonly step: string;
|
|
393
|
-
readonly error: string;
|
|
394
|
-
}
|
|
395
|
-
/** Discriminated union — success and failure are mutually exclusive */
|
|
396
|
-
type CompensationResult = {
|
|
397
|
-
readonly success: true;
|
|
398
|
-
readonly completedSteps: readonly string[];
|
|
399
|
-
readonly results: Readonly<Record<string, unknown>>;
|
|
400
|
-
} | {
|
|
401
|
-
readonly success: false;
|
|
402
|
-
readonly completedSteps: readonly string[];
|
|
403
|
-
readonly results: Readonly<Record<string, unknown>>;
|
|
404
|
-
readonly failedStep: string;
|
|
405
|
-
readonly error: string;
|
|
406
|
-
readonly compensationErrors?: readonly CompensationError[];
|
|
407
|
-
};
|
|
408
|
-
/**
|
|
409
|
-
* Run steps in order with automatic compensation on failure.
|
|
410
|
-
*
|
|
411
|
-
* @typeParam TCtx - Context type shared across steps (defaults to Record<string, unknown>)
|
|
412
|
-
*/
|
|
413
|
-
declare function withCompensation<TCtx extends Record<string, unknown> = Record<string, unknown>>(_name: string, steps: readonly CompensationStep<TCtx>[], initialContext?: TCtx, hooks?: CompensationHooks): Promise<CompensationResult>;
|
|
414
|
-
interface CompensationDefinition<TCtx extends Record<string, unknown> = Record<string, unknown>> {
|
|
415
|
-
readonly name: string;
|
|
416
|
-
readonly execute: (initialContext?: TCtx, hooks?: CompensationHooks) => Promise<CompensationResult>;
|
|
417
|
-
}
|
|
418
|
-
declare function defineCompensation<TCtx extends Record<string, unknown> = Record<string, unknown>>(name: string, steps: readonly CompensationStep<TCtx>[]): CompensationDefinition<TCtx>;
|
|
419
|
-
//#endregion
|
|
420
|
-
//#region src/utils/defineErrorMapper.d.ts
|
|
421
|
-
/**
|
|
422
|
-
* Register an `ErrorMapper` with its domain-specific generic argument and
|
|
423
|
-
* have it assign cleanly into `ErrorMapper[]` (no `as unknown as ErrorMapper`).
|
|
424
|
-
*
|
|
425
|
-
* The returned mapper is identical at runtime — `type` and `toResponse` are
|
|
426
|
-
* passed through untouched. Only the declared type widens from
|
|
427
|
-
* `ErrorMapper<T>` to `ErrorMapper` so the array inference works.
|
|
428
|
-
*
|
|
429
|
-
* Safety: the `errorHandlerPlugin` dispatches via `error instanceof mapper.type`
|
|
430
|
-
* before invoking `toResponse`, so the widened callback signature is never
|
|
431
|
-
* called with a non-`T` error at runtime. This helper codifies that invariant
|
|
432
|
-
* in one place.
|
|
433
|
-
*/
|
|
434
|
-
declare function defineErrorMapper<T extends Error>(mapper: ErrorMapper<T>): ErrorMapper;
|
|
435
|
-
//#endregion
|
|
436
|
-
//#region src/utils/defineGuard.d.ts
|
|
437
|
-
interface GuardConfig<T> {
|
|
438
|
-
/** Unique name — used as the storage key on the request. */
|
|
439
|
-
readonly name: string;
|
|
440
|
-
/**
|
|
441
|
-
* Resolve the guard context from the request. Throw to abort the request
|
|
442
|
-
* (Fastify's error handler will produce the appropriate HTTP response).
|
|
443
|
-
* Return a value to stash it for `from()` extraction.
|
|
444
|
-
*/
|
|
445
|
-
readonly resolve: (req: FastifyRequest, reply: FastifyReply) => T | Promise<T>;
|
|
446
|
-
}
|
|
447
|
-
interface Guard<T> {
|
|
448
|
-
/** Use in `routeGuards` or per-route `preHandler` arrays. */
|
|
449
|
-
readonly preHandler: RouteHandlerMethod;
|
|
450
|
-
/**
|
|
451
|
-
* Extract the resolved context from a request. Throws if the guard
|
|
452
|
-
* hasn't run yet (i.e. not in the preHandler chain).
|
|
453
|
-
*/
|
|
454
|
-
from(req: FastifyRequest): T;
|
|
455
|
-
/** The guard name (for debugging). */
|
|
456
|
-
readonly name: string;
|
|
457
|
-
}
|
|
458
|
-
/**
|
|
459
|
-
* Create a typed guard. See module JSDoc for usage.
|
|
460
|
-
*/
|
|
461
|
-
declare function defineGuard<T>(config: GuardConfig<T>): Guard<T>;
|
|
462
|
-
//#endregion
|
|
463
|
-
//#region src/utils/handleRaw.d.ts
|
|
464
|
-
/**
|
|
465
|
-
* Wrap a raw Fastify handler with Arc's response shape and error handling.
|
|
466
|
-
*
|
|
467
|
-
* @param handler - Async function that receives `(request, reply)` and returns data.
|
|
468
|
-
* The return value is sent raw (no envelope). If it returns `undefined`,
|
|
469
|
-
* the response body is empty (HTTP status only).
|
|
470
|
-
* @param statusCode - HTTP status code for successful responses (default: 200)
|
|
471
|
-
*/
|
|
472
|
-
declare function handleRaw<T>(handler: (request: FastifyRequest, reply: FastifyReply) => Promise<T>, statusCode?: number): (request: FastifyRequest, reply: FastifyReply) => Promise<void>;
|
|
473
|
-
//#endregion
|
|
474
|
-
//#region src/utils/queryParser.d.ts
|
|
475
|
-
interface ArcQueryParserOptions {
|
|
476
|
-
/** Maximum allowed limit value (default: 1000) */
|
|
477
|
-
maxLimit?: number;
|
|
478
|
-
/** Default limit for pagination (default: 20) */
|
|
479
|
-
defaultLimit?: number;
|
|
480
|
-
/** Maximum regex pattern length (default: 500) */
|
|
481
|
-
maxRegexLength?: number;
|
|
482
|
-
/** Maximum search query length (default: 200) */
|
|
483
|
-
maxSearchLength?: number;
|
|
484
|
-
/** Maximum filter nesting depth (default: 10) */
|
|
485
|
-
maxFilterDepth?: number;
|
|
486
|
-
/**
|
|
487
|
-
* Whitelist of fields that can be filtered on.
|
|
488
|
-
* When set, only these fields are accepted as filters — all others are silently dropped.
|
|
489
|
-
* Also used by MCP to auto-derive filterable fields in tool schemas.
|
|
490
|
-
*/
|
|
491
|
-
allowedFilterFields?: string[];
|
|
492
|
-
/**
|
|
493
|
-
* Whitelist of fields that can be sorted on.
|
|
494
|
-
* When set, sort fields not in this list are silently dropped.
|
|
495
|
-
* Also used by MCP to describe available sort options.
|
|
496
|
-
*/
|
|
497
|
-
allowedSortFields?: string[];
|
|
498
|
-
/**
|
|
499
|
-
* Whitelist of filter operators (e.g. ['eq', 'ne', 'gt', 'lt', 'in']).
|
|
500
|
-
* When set, only these operators are accepted — all others are dropped.
|
|
501
|
-
* Also used by MCP to enrich list tool descriptions.
|
|
502
|
-
*/
|
|
503
|
-
allowedOperators?: string[];
|
|
504
|
-
}
|
|
505
|
-
/**
|
|
506
|
-
* Arc's default query parser
|
|
507
|
-
*
|
|
508
|
-
* Converts URL query parameters to a structured query format:
|
|
509
|
-
* - Pagination: ?page=1&limit=20
|
|
510
|
-
* - Sorting: ?sort=-createdAt,name (- prefix = descending)
|
|
511
|
-
* - Filtering: ?status=active&price[gte]=100&price[lte]=500
|
|
512
|
-
* - Search: ?search=keyword
|
|
513
|
-
* - Populate: ?populate=author,category
|
|
514
|
-
* - Field selection: ?select=name,price,status
|
|
515
|
-
* - Keyset pagination: ?after=cursor_value
|
|
516
|
-
*
|
|
517
|
-
* For advanced MongoDB features ($lookup, aggregations), use MongoKit's QueryParser.
|
|
518
|
-
*/
|
|
519
|
-
declare class ArcQueryParser implements QueryParserInterface {
|
|
520
|
-
private readonly maxLimit;
|
|
521
|
-
private readonly defaultLimit;
|
|
522
|
-
private readonly maxRegexLength;
|
|
523
|
-
private readonly maxSearchLength;
|
|
524
|
-
private readonly maxFilterDepth;
|
|
525
|
-
private readonly _allowedFilterFields?;
|
|
526
|
-
private readonly _allowedSortFields?;
|
|
527
|
-
private readonly _allowedOperators?;
|
|
528
|
-
/** Allowed filter fields (used by MCP for auto-derive) */
|
|
529
|
-
readonly allowedFilterFields?: readonly string[];
|
|
530
|
-
/** Allowed sort fields (used by MCP for sort descriptions) */
|
|
531
|
-
readonly allowedSortFields?: readonly string[];
|
|
532
|
-
/** Allowed operators (used by MCP for operator descriptions) */
|
|
533
|
-
readonly allowedOperators?: readonly string[];
|
|
534
|
-
/** Supported filter operators */
|
|
535
|
-
private readonly operators;
|
|
536
|
-
constructor(options?: ArcQueryParserOptions);
|
|
537
|
-
/**
|
|
538
|
-
* Parse URL query parameters into structured query options
|
|
539
|
-
*/
|
|
540
|
-
parse(query: Record<string, unknown> | null | undefined): ParsedQuery;
|
|
541
|
-
private parseNumber;
|
|
542
|
-
private parseString;
|
|
543
|
-
/**
|
|
544
|
-
* Parse populate parameter — handles both simple string and bracket notation.
|
|
545
|
-
*
|
|
546
|
-
* Simple: ?populate=author,category → { populate: 'author,category' }
|
|
547
|
-
* Bracket: ?populate[author][select]=name,email → { populateOptions: [{ path: 'author', select: 'name email' }] }
|
|
548
|
-
*/
|
|
549
|
-
private parsePopulate;
|
|
550
|
-
private parseSort;
|
|
551
|
-
private parseSearch;
|
|
552
|
-
private parseSelect;
|
|
553
|
-
/**
|
|
554
|
-
* Check if a value exceeds the maximum nesting depth.
|
|
555
|
-
* Prevents filter bombs where deeply nested objects consume excessive memory/CPU.
|
|
556
|
-
*/
|
|
557
|
-
private exceedsDepth;
|
|
558
|
-
private parseFilters;
|
|
559
|
-
private parseFilterValue;
|
|
560
|
-
private coerceValue;
|
|
561
|
-
/**
|
|
562
|
-
* Generate OpenAPI-compatible JSON Schema for query parameters.
|
|
563
|
-
* Arc's defineResource() auto-detects this method and uses it
|
|
564
|
-
* to document list endpoint query parameters in OpenAPI/Swagger.
|
|
565
|
-
*/
|
|
566
|
-
getQuerySchema(): {
|
|
567
|
-
type: "object";
|
|
568
|
-
properties: Record<string, unknown>;
|
|
569
|
-
required?: string[];
|
|
570
|
-
};
|
|
571
|
-
private sanitizeRegex;
|
|
572
|
-
}
|
|
573
|
-
/**
|
|
574
|
-
* Create a new ArcQueryParser instance
|
|
575
|
-
*/
|
|
576
|
-
declare function createQueryParser(options?: ArcQueryParserOptions): ArcQueryParser;
|
|
577
|
-
//#endregion
|
|
578
|
-
//#region src/utils/responseSchemas.d.ts
|
|
579
|
-
interface JsonSchema {
|
|
580
|
-
/**
|
|
581
|
-
* Optional because JSON Schema allows top-level combinator-only schemas
|
|
582
|
-
* (`{ oneOf: [...] }`, `{ anyOf: [...] }`, `{ allOf: [...] }`) — see
|
|
583
|
-
* `listResponse()`, which emits a `oneOf` of the four canonical list
|
|
584
|
-
* shapes with no top-level `type`.
|
|
585
|
-
*/
|
|
586
|
-
type?: string | string[];
|
|
587
|
-
properties?: Record<string, JsonSchema | AnyRecord>;
|
|
588
|
-
required?: string[];
|
|
589
|
-
items?: JsonSchema | AnyRecord;
|
|
590
|
-
additionalProperties?: boolean | JsonSchema;
|
|
591
|
-
description?: string;
|
|
592
|
-
example?: unknown;
|
|
593
|
-
oneOf?: JsonSchema[];
|
|
594
|
-
anyOf?: JsonSchema[];
|
|
595
|
-
allOf?: JsonSchema[];
|
|
596
|
-
[key: string]: unknown;
|
|
597
|
-
}
|
|
598
|
-
/**
|
|
599
|
-
* Pagination schema - matches MongoKit/Arc runtime format
|
|
600
|
-
*
|
|
601
|
-
* Runtime format (flat fields):
|
|
602
|
-
* { page, limit, total, pages, hasNext, hasPrev }
|
|
603
|
-
*/
|
|
604
|
-
declare const paginationSchema: JsonSchema;
|
|
605
|
-
/**
|
|
606
|
-
* List response schema — full union of every wire shape `toCanonicalList`
|
|
607
|
-
* can emit. Hosts who know their endpoint only ever produces one variant
|
|
608
|
-
* can pin to a narrower helper:
|
|
609
|
-
* - `offsetListResponse(item)` — `{ method: 'offset', data, page, limit, total, pages, hasNext, hasPrev }`
|
|
610
|
-
* - `keysetListResponse(item)` — `{ method: 'keyset', data, limit, hasMore, next: string|null }`
|
|
611
|
-
* - `aggregateListResponse(item)` — `{ method: 'aggregate', ...offset fields }`
|
|
612
|
-
* - `bareListResponse(item)` — `{ data }`
|
|
613
|
-
*
|
|
614
|
-
* The default `listResponse(item)` is the union (`oneOf`) of all four so
|
|
615
|
-
* Fastify validation accepts any canonical kit shape — pre-2.13 this only
|
|
616
|
-
* modelled offset and rejected keyset/aggregate/bare lists at the
|
|
617
|
-
* response-schema gate.
|
|
618
|
-
*/
|
|
619
|
-
declare function listResponse(itemSchema: JsonSchema): JsonSchema;
|
|
620
|
-
/** Offset variant — `{ method: 'offset', data, page, limit, total, pages, hasNext, hasPrev }`. */
|
|
621
|
-
declare function offsetListResponse(itemSchema: JsonSchema): JsonSchema;
|
|
622
|
-
/** Keyset variant — `{ method: 'keyset', data, limit, hasMore, next: string | null }`. */
|
|
623
|
-
declare function keysetListResponse(itemSchema: JsonSchema): JsonSchema;
|
|
624
|
-
/** Aggregate variant — same shape as offset, discriminated by `method: 'aggregate'`. */
|
|
625
|
-
declare function aggregateListResponse(itemSchema: JsonSchema): JsonSchema;
|
|
626
|
-
/** Bare variant — `{ data }`, no `method` discriminant. */
|
|
627
|
-
declare function bareListResponse(itemSchema: JsonSchema): JsonSchema;
|
|
628
|
-
/** Delete response — flat shape mirroring the canonical delete envelope. */
|
|
629
|
-
declare function deleteResponse(): JsonSchema;
|
|
630
|
-
declare const responses: {
|
|
631
|
-
200: (schema: JsonSchema) => {
|
|
632
|
-
description: string;
|
|
633
|
-
content: {
|
|
634
|
-
"application/json": {
|
|
635
|
-
schema: JsonSchema;
|
|
636
|
-
};
|
|
637
|
-
};
|
|
638
|
-
};
|
|
639
|
-
201: (schema: JsonSchema) => {
|
|
640
|
-
description: string;
|
|
641
|
-
content: {
|
|
642
|
-
"application/json": {
|
|
643
|
-
schema: {
|
|
644
|
-
additionalProperties: boolean;
|
|
645
|
-
/**
|
|
646
|
-
* Optional because JSON Schema allows top-level combinator-only schemas
|
|
647
|
-
* (`{ oneOf: [...] }`, `{ anyOf: [...] }`, `{ allOf: [...] }`) — see
|
|
648
|
-
* `listResponse()`, which emits a `oneOf` of the four canonical list
|
|
649
|
-
* shapes with no top-level `type`.
|
|
650
|
-
*/
|
|
651
|
-
type?: string | string[];
|
|
652
|
-
properties?: Record<string, JsonSchema | AnyRecord>;
|
|
653
|
-
required?: string[];
|
|
654
|
-
items?: JsonSchema | AnyRecord;
|
|
655
|
-
description?: string;
|
|
656
|
-
example?: unknown;
|
|
657
|
-
oneOf?: JsonSchema[];
|
|
658
|
-
anyOf?: JsonSchema[];
|
|
659
|
-
allOf?: JsonSchema[];
|
|
660
|
-
};
|
|
661
|
-
};
|
|
662
|
-
};
|
|
663
|
-
};
|
|
664
|
-
400: {
|
|
665
|
-
description: string;
|
|
666
|
-
content: {
|
|
667
|
-
"application/json": {
|
|
668
|
-
schema: {
|
|
669
|
-
readonly type: "object";
|
|
670
|
-
readonly properties: {
|
|
671
|
-
readonly code: {
|
|
672
|
-
readonly type: "string";
|
|
673
|
-
readonly description: "Hierarchical machine-readable code (e.g. 'arc.not_found').";
|
|
674
|
-
};
|
|
675
|
-
readonly message: {
|
|
676
|
-
readonly type: "string";
|
|
677
|
-
readonly description: "Human-readable, safe-for-client message.";
|
|
678
|
-
};
|
|
679
|
-
readonly status: {
|
|
680
|
-
readonly type: "integer";
|
|
681
|
-
readonly description: "Suggested HTTP status code (hosts may override).";
|
|
682
|
-
};
|
|
683
|
-
readonly details: {
|
|
684
|
-
readonly type: "array";
|
|
685
|
-
readonly description: "Field-scoped structured details (validation failures, duplicate keys, multi-code domain errors).";
|
|
686
|
-
readonly items: {
|
|
687
|
-
readonly type: "object";
|
|
688
|
-
readonly properties: {
|
|
689
|
-
readonly path: {
|
|
690
|
-
readonly type: "string";
|
|
691
|
-
readonly description: "Dot-path to the offending field, e.g. 'lines.0.quantity'.";
|
|
692
|
-
};
|
|
693
|
-
readonly code: {
|
|
694
|
-
readonly type: "string";
|
|
695
|
-
};
|
|
696
|
-
readonly message: {
|
|
697
|
-
readonly type: "string";
|
|
698
|
-
};
|
|
699
|
-
readonly meta: {
|
|
700
|
-
readonly type: "object";
|
|
701
|
-
readonly description: "Non-PII per-detail diagnostics (safe to log + return).";
|
|
702
|
-
};
|
|
703
|
-
};
|
|
704
|
-
readonly required: readonly ["code", "message"];
|
|
705
|
-
};
|
|
706
|
-
};
|
|
707
|
-
readonly correlationId: {
|
|
708
|
-
readonly type: "string";
|
|
709
|
-
readonly description: "Request id for support lookups.";
|
|
710
|
-
};
|
|
711
|
-
readonly meta: {
|
|
712
|
-
readonly type: "object";
|
|
713
|
-
readonly description: "Non-PII diagnostics (safe to log + return).";
|
|
714
|
-
};
|
|
715
|
-
};
|
|
716
|
-
readonly required: readonly ["code", "message"];
|
|
717
|
-
};
|
|
718
|
-
};
|
|
719
|
-
};
|
|
720
|
-
};
|
|
721
|
-
401: {
|
|
722
|
-
description: string;
|
|
723
|
-
content: {
|
|
724
|
-
"application/json": {
|
|
725
|
-
schema: {
|
|
726
|
-
readonly type: "object";
|
|
727
|
-
readonly properties: {
|
|
728
|
-
readonly code: {
|
|
729
|
-
readonly type: "string";
|
|
730
|
-
readonly description: "Hierarchical machine-readable code (e.g. 'arc.not_found').";
|
|
731
|
-
};
|
|
732
|
-
readonly message: {
|
|
733
|
-
readonly type: "string";
|
|
734
|
-
readonly description: "Human-readable, safe-for-client message.";
|
|
735
|
-
};
|
|
736
|
-
readonly status: {
|
|
737
|
-
readonly type: "integer";
|
|
738
|
-
readonly description: "Suggested HTTP status code (hosts may override).";
|
|
739
|
-
};
|
|
740
|
-
readonly details: {
|
|
741
|
-
readonly type: "array";
|
|
742
|
-
readonly description: "Field-scoped structured details (validation failures, duplicate keys, multi-code domain errors).";
|
|
743
|
-
readonly items: {
|
|
744
|
-
readonly type: "object";
|
|
745
|
-
readonly properties: {
|
|
746
|
-
readonly path: {
|
|
747
|
-
readonly type: "string";
|
|
748
|
-
readonly description: "Dot-path to the offending field, e.g. 'lines.0.quantity'.";
|
|
749
|
-
};
|
|
750
|
-
readonly code: {
|
|
751
|
-
readonly type: "string";
|
|
752
|
-
};
|
|
753
|
-
readonly message: {
|
|
754
|
-
readonly type: "string";
|
|
755
|
-
};
|
|
756
|
-
readonly meta: {
|
|
757
|
-
readonly type: "object";
|
|
758
|
-
readonly description: "Non-PII per-detail diagnostics (safe to log + return).";
|
|
759
|
-
};
|
|
760
|
-
};
|
|
761
|
-
readonly required: readonly ["code", "message"];
|
|
762
|
-
};
|
|
763
|
-
};
|
|
764
|
-
readonly correlationId: {
|
|
765
|
-
readonly type: "string";
|
|
766
|
-
readonly description: "Request id for support lookups.";
|
|
767
|
-
};
|
|
768
|
-
readonly meta: {
|
|
769
|
-
readonly type: "object";
|
|
770
|
-
readonly description: "Non-PII diagnostics (safe to log + return).";
|
|
771
|
-
};
|
|
772
|
-
};
|
|
773
|
-
readonly required: readonly ["code", "message"];
|
|
774
|
-
};
|
|
775
|
-
};
|
|
776
|
-
};
|
|
777
|
-
};
|
|
778
|
-
403: {
|
|
779
|
-
description: string;
|
|
780
|
-
content: {
|
|
781
|
-
"application/json": {
|
|
782
|
-
schema: {
|
|
783
|
-
readonly type: "object";
|
|
784
|
-
readonly properties: {
|
|
785
|
-
readonly code: {
|
|
786
|
-
readonly type: "string";
|
|
787
|
-
readonly description: "Hierarchical machine-readable code (e.g. 'arc.not_found').";
|
|
788
|
-
};
|
|
789
|
-
readonly message: {
|
|
790
|
-
readonly type: "string";
|
|
791
|
-
readonly description: "Human-readable, safe-for-client message.";
|
|
792
|
-
};
|
|
793
|
-
readonly status: {
|
|
794
|
-
readonly type: "integer";
|
|
795
|
-
readonly description: "Suggested HTTP status code (hosts may override).";
|
|
796
|
-
};
|
|
797
|
-
readonly details: {
|
|
798
|
-
readonly type: "array";
|
|
799
|
-
readonly description: "Field-scoped structured details (validation failures, duplicate keys, multi-code domain errors).";
|
|
800
|
-
readonly items: {
|
|
801
|
-
readonly type: "object";
|
|
802
|
-
readonly properties: {
|
|
803
|
-
readonly path: {
|
|
804
|
-
readonly type: "string";
|
|
805
|
-
readonly description: "Dot-path to the offending field, e.g. 'lines.0.quantity'.";
|
|
806
|
-
};
|
|
807
|
-
readonly code: {
|
|
808
|
-
readonly type: "string";
|
|
809
|
-
};
|
|
810
|
-
readonly message: {
|
|
811
|
-
readonly type: "string";
|
|
812
|
-
};
|
|
813
|
-
readonly meta: {
|
|
814
|
-
readonly type: "object";
|
|
815
|
-
readonly description: "Non-PII per-detail diagnostics (safe to log + return).";
|
|
816
|
-
};
|
|
817
|
-
};
|
|
818
|
-
readonly required: readonly ["code", "message"];
|
|
819
|
-
};
|
|
820
|
-
};
|
|
821
|
-
readonly correlationId: {
|
|
822
|
-
readonly type: "string";
|
|
823
|
-
readonly description: "Request id for support lookups.";
|
|
824
|
-
};
|
|
825
|
-
readonly meta: {
|
|
826
|
-
readonly type: "object";
|
|
827
|
-
readonly description: "Non-PII diagnostics (safe to log + return).";
|
|
828
|
-
};
|
|
829
|
-
};
|
|
830
|
-
readonly required: readonly ["code", "message"];
|
|
831
|
-
};
|
|
832
|
-
};
|
|
833
|
-
};
|
|
834
|
-
};
|
|
835
|
-
404: {
|
|
836
|
-
description: string;
|
|
837
|
-
content: {
|
|
838
|
-
"application/json": {
|
|
839
|
-
schema: {
|
|
840
|
-
readonly type: "object";
|
|
841
|
-
readonly properties: {
|
|
842
|
-
readonly code: {
|
|
843
|
-
readonly type: "string";
|
|
844
|
-
readonly description: "Hierarchical machine-readable code (e.g. 'arc.not_found').";
|
|
845
|
-
};
|
|
846
|
-
readonly message: {
|
|
847
|
-
readonly type: "string";
|
|
848
|
-
readonly description: "Human-readable, safe-for-client message.";
|
|
849
|
-
};
|
|
850
|
-
readonly status: {
|
|
851
|
-
readonly type: "integer";
|
|
852
|
-
readonly description: "Suggested HTTP status code (hosts may override).";
|
|
853
|
-
};
|
|
854
|
-
readonly details: {
|
|
855
|
-
readonly type: "array";
|
|
856
|
-
readonly description: "Field-scoped structured details (validation failures, duplicate keys, multi-code domain errors).";
|
|
857
|
-
readonly items: {
|
|
858
|
-
readonly type: "object";
|
|
859
|
-
readonly properties: {
|
|
860
|
-
readonly path: {
|
|
861
|
-
readonly type: "string";
|
|
862
|
-
readonly description: "Dot-path to the offending field, e.g. 'lines.0.quantity'.";
|
|
863
|
-
};
|
|
864
|
-
readonly code: {
|
|
865
|
-
readonly type: "string";
|
|
866
|
-
};
|
|
867
|
-
readonly message: {
|
|
868
|
-
readonly type: "string";
|
|
869
|
-
};
|
|
870
|
-
readonly meta: {
|
|
871
|
-
readonly type: "object";
|
|
872
|
-
readonly description: "Non-PII per-detail diagnostics (safe to log + return).";
|
|
873
|
-
};
|
|
874
|
-
};
|
|
875
|
-
readonly required: readonly ["code", "message"];
|
|
876
|
-
};
|
|
877
|
-
};
|
|
878
|
-
readonly correlationId: {
|
|
879
|
-
readonly type: "string";
|
|
880
|
-
readonly description: "Request id for support lookups.";
|
|
881
|
-
};
|
|
882
|
-
readonly meta: {
|
|
883
|
-
readonly type: "object";
|
|
884
|
-
readonly description: "Non-PII diagnostics (safe to log + return).";
|
|
885
|
-
};
|
|
886
|
-
};
|
|
887
|
-
readonly required: readonly ["code", "message"];
|
|
888
|
-
};
|
|
889
|
-
};
|
|
890
|
-
};
|
|
891
|
-
};
|
|
892
|
-
409: {
|
|
893
|
-
description: string;
|
|
894
|
-
content: {
|
|
895
|
-
"application/json": {
|
|
896
|
-
schema: {
|
|
897
|
-
readonly type: "object";
|
|
898
|
-
readonly properties: {
|
|
899
|
-
readonly code: {
|
|
900
|
-
readonly type: "string";
|
|
901
|
-
readonly description: "Hierarchical machine-readable code (e.g. 'arc.not_found').";
|
|
902
|
-
};
|
|
903
|
-
readonly message: {
|
|
904
|
-
readonly type: "string";
|
|
905
|
-
readonly description: "Human-readable, safe-for-client message.";
|
|
906
|
-
};
|
|
907
|
-
readonly status: {
|
|
908
|
-
readonly type: "integer";
|
|
909
|
-
readonly description: "Suggested HTTP status code (hosts may override).";
|
|
910
|
-
};
|
|
911
|
-
readonly details: {
|
|
912
|
-
readonly type: "array";
|
|
913
|
-
readonly description: "Field-scoped structured details (validation failures, duplicate keys, multi-code domain errors).";
|
|
914
|
-
readonly items: {
|
|
915
|
-
readonly type: "object";
|
|
916
|
-
readonly properties: {
|
|
917
|
-
readonly path: {
|
|
918
|
-
readonly type: "string";
|
|
919
|
-
readonly description: "Dot-path to the offending field, e.g. 'lines.0.quantity'.";
|
|
920
|
-
};
|
|
921
|
-
readonly code: {
|
|
922
|
-
readonly type: "string";
|
|
923
|
-
};
|
|
924
|
-
readonly message: {
|
|
925
|
-
readonly type: "string";
|
|
926
|
-
};
|
|
927
|
-
readonly meta: {
|
|
928
|
-
readonly type: "object";
|
|
929
|
-
readonly description: "Non-PII per-detail diagnostics (safe to log + return).";
|
|
930
|
-
};
|
|
931
|
-
};
|
|
932
|
-
readonly required: readonly ["code", "message"];
|
|
933
|
-
};
|
|
934
|
-
};
|
|
935
|
-
readonly correlationId: {
|
|
936
|
-
readonly type: "string";
|
|
937
|
-
readonly description: "Request id for support lookups.";
|
|
938
|
-
};
|
|
939
|
-
readonly meta: {
|
|
940
|
-
readonly type: "object";
|
|
941
|
-
readonly description: "Non-PII diagnostics (safe to log + return).";
|
|
942
|
-
};
|
|
943
|
-
};
|
|
944
|
-
readonly required: readonly ["code", "message"];
|
|
945
|
-
};
|
|
946
|
-
};
|
|
947
|
-
};
|
|
948
|
-
};
|
|
949
|
-
500: {
|
|
950
|
-
description: string;
|
|
951
|
-
content: {
|
|
952
|
-
"application/json": {
|
|
953
|
-
schema: {
|
|
954
|
-
readonly type: "object";
|
|
955
|
-
readonly properties: {
|
|
956
|
-
readonly code: {
|
|
957
|
-
readonly type: "string";
|
|
958
|
-
readonly description: "Hierarchical machine-readable code (e.g. 'arc.not_found').";
|
|
959
|
-
};
|
|
960
|
-
readonly message: {
|
|
961
|
-
readonly type: "string";
|
|
962
|
-
readonly description: "Human-readable, safe-for-client message.";
|
|
963
|
-
};
|
|
964
|
-
readonly status: {
|
|
965
|
-
readonly type: "integer";
|
|
966
|
-
readonly description: "Suggested HTTP status code (hosts may override).";
|
|
967
|
-
};
|
|
968
|
-
readonly details: {
|
|
969
|
-
readonly type: "array";
|
|
970
|
-
readonly description: "Field-scoped structured details (validation failures, duplicate keys, multi-code domain errors).";
|
|
971
|
-
readonly items: {
|
|
972
|
-
readonly type: "object";
|
|
973
|
-
readonly properties: {
|
|
974
|
-
readonly path: {
|
|
975
|
-
readonly type: "string";
|
|
976
|
-
readonly description: "Dot-path to the offending field, e.g. 'lines.0.quantity'.";
|
|
977
|
-
};
|
|
978
|
-
readonly code: {
|
|
979
|
-
readonly type: "string";
|
|
980
|
-
};
|
|
981
|
-
readonly message: {
|
|
982
|
-
readonly type: "string";
|
|
983
|
-
};
|
|
984
|
-
readonly meta: {
|
|
985
|
-
readonly type: "object";
|
|
986
|
-
readonly description: "Non-PII per-detail diagnostics (safe to log + return).";
|
|
987
|
-
};
|
|
988
|
-
};
|
|
989
|
-
readonly required: readonly ["code", "message"];
|
|
990
|
-
};
|
|
991
|
-
};
|
|
992
|
-
readonly correlationId: {
|
|
993
|
-
readonly type: "string";
|
|
994
|
-
readonly description: "Request id for support lookups.";
|
|
995
|
-
};
|
|
996
|
-
readonly meta: {
|
|
997
|
-
readonly type: "object";
|
|
998
|
-
readonly description: "Non-PII diagnostics (safe to log + return).";
|
|
999
|
-
};
|
|
1000
|
-
};
|
|
1001
|
-
readonly required: readonly ["code", "message"];
|
|
1002
|
-
};
|
|
1003
|
-
};
|
|
1004
|
-
};
|
|
1005
|
-
};
|
|
1006
|
-
};
|
|
1007
|
-
declare const queryParams: {
|
|
1008
|
-
pagination: {
|
|
1009
|
-
page: {
|
|
1010
|
-
type: string;
|
|
1011
|
-
minimum: number;
|
|
1012
|
-
default: number;
|
|
1013
|
-
description: string;
|
|
1014
|
-
};
|
|
1015
|
-
limit: {
|
|
1016
|
-
type: string;
|
|
1017
|
-
minimum: number;
|
|
1018
|
-
maximum: number;
|
|
1019
|
-
default: number;
|
|
1020
|
-
description: string;
|
|
1021
|
-
};
|
|
1022
|
-
};
|
|
1023
|
-
sorting: {
|
|
1024
|
-
sort: {
|
|
1025
|
-
type: string;
|
|
1026
|
-
description: string;
|
|
1027
|
-
example: string;
|
|
1028
|
-
};
|
|
1029
|
-
};
|
|
1030
|
-
filtering: {
|
|
1031
|
-
select: {
|
|
1032
|
-
description: string;
|
|
1033
|
-
example: string;
|
|
1034
|
-
};
|
|
1035
|
-
populate: {
|
|
1036
|
-
description: string;
|
|
1037
|
-
example: string;
|
|
1038
|
-
};
|
|
1039
|
-
};
|
|
1040
|
-
};
|
|
1041
|
-
/**
|
|
1042
|
-
* Get standard list query parameters schema
|
|
1043
|
-
*/
|
|
1044
|
-
declare function getListQueryParams(): AnyRecord;
|
|
1045
|
-
/**
|
|
1046
|
-
* Get default response schemas for all CRUD operations.
|
|
1047
|
-
*
|
|
1048
|
-
* When routes have response schemas, Fastify compiles them with
|
|
1049
|
-
* fast-json-stringify for 2-3x faster serialization and prevents
|
|
1050
|
-
* accidental field disclosure.
|
|
1051
|
-
*
|
|
1052
|
-
* These defaults use `additionalProperties: true` so all fields pass through.
|
|
1053
|
-
* Override with specific schemas for full serialization performance + safety.
|
|
1054
|
-
*
|
|
1055
|
-
* Note: `example` properties are stripped from defaults so they work with
|
|
1056
|
-
* any Fastify instance (not just createApp which adds `keywords: ['example']`).
|
|
1057
|
-
*/
|
|
1058
|
-
declare function getDefaultCrudSchemas(): Record<string, Record<string, unknown>>;
|
|
1059
|
-
//#endregion
|
|
1060
|
-
//#region src/utils/runtime.d.ts
|
|
1061
|
-
/**
|
|
1062
|
-
* Portable "run on next tick" scheduler. `setImmediate` is Node-only — not
|
|
1063
|
-
* available in Bun workers, Deno, Cloudflare Workers, or edge runtimes.
|
|
1064
|
-
*/
|
|
1065
|
-
declare const scheduleBackground: (cb: () => void) => void;
|
|
1066
|
-
//#endregion
|
|
1067
|
-
//#region src/utils/schemaConverter.d.ts
|
|
1068
|
-
/**
|
|
1069
|
-
* Supported JSON Schema output targets for Zod v4's `toJSONSchema()`.
|
|
1070
|
-
* - `draft-7`: Fastify/AJV validation (default)
|
|
1071
|
-
* - `draft-2020-12`: AJV 2020 (opt-in, requires ajv/dist/2020)
|
|
1072
|
-
* - `openapi-3.0`: OpenAPI 3.0 document generation
|
|
1073
|
-
* - `openapi-3.1`: OpenAPI 3.1 document generation
|
|
1074
|
-
*/
|
|
1075
|
-
type JsonSchemaTarget = "draft-7" | "draft-2020-12" | "openapi-3.0" | "openapi-3.1";
|
|
1076
|
-
/**
|
|
1077
|
-
* Check if an object is already a plain JSON Schema.
|
|
1078
|
-
* Returns true if it has JSON Schema markers (`type`, `properties`, `$ref`,
|
|
1079
|
-
* `allOf`, `anyOf`, `oneOf`, `items`, `enum`) and does NOT have Zod markers.
|
|
1080
|
-
*/
|
|
1081
|
-
declare function isJsonSchema(input: unknown): input is Record<string, unknown>;
|
|
1082
|
-
/**
|
|
1083
|
-
* Check if an object is a Zod schema (has `_zod` marker from Zod v4).
|
|
1084
|
-
*/
|
|
1085
|
-
declare function isZodSchema(input: unknown): boolean;
|
|
1086
|
-
/**
|
|
1087
|
-
* Convert any schema input to JSON Schema.
|
|
1088
|
-
*
|
|
1089
|
-
* Detection order:
|
|
1090
|
-
* 1. `null`/`undefined` → `undefined`
|
|
1091
|
-
* 2. Already JSON Schema → pass through as-is (zero overhead)
|
|
1092
|
-
* 3. Zod v4 schema → `z.toJSONSchema(schema, { target })`
|
|
1093
|
-
* 4. Unrecognized object → return as-is (treat as opaque schema)
|
|
1094
|
-
*
|
|
1095
|
-
* @param input Schema (Zod, plain JSON Schema, or opaque object)
|
|
1096
|
-
* @param target Output target — defaults to `draft-7` for Fastify compatibility.
|
|
1097
|
-
* Pass `openapi-3.0`/`openapi-3.1` for OpenAPI document generation.
|
|
1098
|
-
*/
|
|
1099
|
-
declare function toJsonSchema(input: unknown, target?: JsonSchemaTarget): Record<string, unknown> | undefined;
|
|
1100
|
-
/**
|
|
1101
|
-
* Convert all schema fields in an OpenApiSchemas object.
|
|
1102
|
-
* JSON Schema values pass through unchanged. Only Zod schemas are converted.
|
|
1103
|
-
*
|
|
1104
|
-
* Defaults to the `openapi-3.0` target since this function feeds OpenAPI doc
|
|
1105
|
-
* generation, not Fastify route validation.
|
|
1106
|
-
*/
|
|
1107
|
-
declare function convertOpenApiSchemas(schemas: OpenApiSchemas, target?: JsonSchemaTarget): OpenApiSchemas;
|
|
1108
|
-
/**
|
|
1109
|
-
* Convert schema values in a Fastify route schema record.
|
|
1110
|
-
*
|
|
1111
|
-
* Handles `body`, `querystring`, `params`, `headers` (top-level conversion)
|
|
1112
|
-
* and `response` (iterates by status code — each value converted individually).
|
|
1113
|
-
*
|
|
1114
|
-
* JSON Schema values pass through unchanged. Only Zod schemas are converted.
|
|
1115
|
-
*
|
|
1116
|
-
* Used for both custom routes and customSchemas (CRUD overrides).
|
|
1117
|
-
*
|
|
1118
|
-
* Defaults to `draft-7` so Fastify v5's bundled AJV 8 accepts the output.
|
|
1119
|
-
* Pass `openapi-3.0` (or `openapi-3.1`) when generating OpenAPI documents.
|
|
1120
|
-
*/
|
|
1121
|
-
declare function convertRouteSchema(schema: Record<string, unknown>, target?: JsonSchemaTarget): Record<string, unknown>;
|
|
1122
|
-
//#endregion
|
|
1123
|
-
//#region src/utils/simpleEqualityMatcher.d.ts
|
|
1124
|
-
/**
|
|
1125
|
-
* `simpleEqualityMatcher` — a minimal, dialect-agnostic flat-key equality
|
|
1126
|
-
* matcher for `DataAdapter.matchesFilter` / `BaseController({ matchesFilter })`.
|
|
1127
|
-
*
|
|
1128
|
-
* **What it does:** for each `[key, expected]` in the filter, compares
|
|
1129
|
-
* `item[key]` to `expected` via string coercion (so Mongo `ObjectId` values
|
|
1130
|
-
* match their string representation) and returns `true` only if every
|
|
1131
|
-
* filter entry matches. Array item values are matched implicitly (contains).
|
|
1132
|
-
*
|
|
1133
|
-
* **What it does NOT do:**
|
|
1134
|
-
* - No `$eq` / `$ne` / `$in` / `$nin` / `$gt` / `$lt` / `$regex` / `$exists`
|
|
1135
|
-
* - No `$and` / `$or`
|
|
1136
|
-
* - No dot-path traversal (`"owner.id"`)
|
|
1137
|
-
* - No schema-specific coercion
|
|
1138
|
-
*
|
|
1139
|
-
* **Why it exists:** 95%+ of arc's `_policyFilters` are produced by built-in
|
|
1140
|
-
* permission helpers and are shaped like `{ ownerId: "u1" }` or
|
|
1141
|
-
* `{ organizationId: "org_x" }` — flat equality. For that common shape,
|
|
1142
|
-
* this helper is a safe, tested, 15-line defense-in-depth matcher that
|
|
1143
|
-
* hosts using minimal repos (no `getOne(compoundFilter)` DB path) can opt
|
|
1144
|
-
* into without arc shipping a full Mongo-syntax engine.
|
|
1145
|
-
*
|
|
1146
|
-
* **When to use:**
|
|
1147
|
-
* - Your adapter/repo doesn't natively filter on `getOne(compoundFilter)`
|
|
1148
|
-
* - Your `_policyFilters` are flat equality (from arc's built-in permission helpers)
|
|
1149
|
-
* - You want defense-in-depth on `validateItemAccess` / `fetchDetailed`'s `getById` fallback
|
|
1150
|
-
*
|
|
1151
|
-
* **When NOT to use:**
|
|
1152
|
-
* - Your `_policyFilters` use operators (`$in`, `$ne`, etc.) — supply a
|
|
1153
|
-
* native matcher (mongokit's repo does the filter at the DB layer; for
|
|
1154
|
-
* custom repos, wrap the kit's own predicate engine).
|
|
1155
|
-
* - You're a mongokit / sqlitekit / Prisma user — the DB-level filter
|
|
1156
|
-
* applied by `getOne(compoundFilter)` already covers this.
|
|
1157
|
-
*
|
|
1158
|
-
* @example
|
|
1159
|
-
* ```ts
|
|
1160
|
-
* import { simpleEqualityMatcher } from '@classytic/arc/utils';
|
|
1161
|
-
*
|
|
1162
|
-
* // On a custom adapter
|
|
1163
|
-
* const adapter: DataAdapter = {
|
|
1164
|
-
* repository,
|
|
1165
|
-
* type: 'custom',
|
|
1166
|
-
* name: 'in-memory',
|
|
1167
|
-
* matchesFilter: simpleEqualityMatcher,
|
|
1168
|
-
* };
|
|
1169
|
-
*
|
|
1170
|
-
* // Or directly on BaseController for ad-hoc controllers
|
|
1171
|
-
* new BaseController(repo, { matchesFilter: simpleEqualityMatcher });
|
|
1172
|
-
* ```
|
|
1173
|
-
*/
|
|
1174
|
-
declare function simpleEqualityMatcher(item: unknown, filters: Record<string, unknown>): boolean;
|
|
1175
|
-
//#endregion
|
|
1176
|
-
//#region src/utils/stateMachine.d.ts
|
|
1177
|
-
/**
|
|
1178
|
-
* State Machine Utility
|
|
1179
|
-
*
|
|
1180
|
-
* Pure utility for validating state transitions in workflow systems.
|
|
1181
|
-
* Zero dependencies, framework-agnostic.
|
|
1182
|
-
*
|
|
1183
|
-
* @example
|
|
1184
|
-
* const orderState = createStateMachine('Order', {
|
|
1185
|
-
* approve: ['pending', 'draft'],
|
|
1186
|
-
* cancel: ['pending', 'approved'],
|
|
1187
|
-
* fulfill: ['approved'],
|
|
1188
|
-
* });
|
|
1189
|
-
*
|
|
1190
|
-
* // Check if transition is allowed
|
|
1191
|
-
* if (orderState.can('approve', currentStatus)) {
|
|
1192
|
-
* // Perform approval
|
|
1193
|
-
* }
|
|
1194
|
-
*
|
|
1195
|
-
* // Assert transition (throws if invalid)
|
|
1196
|
-
* orderState.assert('approve', currentStatus, ValidationError);
|
|
1197
|
-
*/
|
|
1198
|
-
interface StateMachine {
|
|
1199
|
-
/**
|
|
1200
|
-
* Synchronously check if action can be performed from current status.
|
|
1201
|
-
* Only checks the transition map — does NOT evaluate guards.
|
|
1202
|
-
* Use `canAsync()` when guards need to be evaluated.
|
|
1203
|
-
*/
|
|
1204
|
-
can(action: string, status: string | null | undefined): boolean;
|
|
1205
|
-
/**
|
|
1206
|
-
* Asynchronously check if action can be performed, including guard evaluation.
|
|
1207
|
-
* Falls back to simple transition check when no guard is defined.
|
|
1208
|
-
*/
|
|
1209
|
-
canAsync(action: string, status: string | null | undefined, context?: Record<string, unknown>): Promise<boolean>;
|
|
1210
|
-
/**
|
|
1211
|
-
* Assert action can be performed, throw error if invalid
|
|
1212
|
-
* @param action - Action to perform
|
|
1213
|
-
* @param status - Current status
|
|
1214
|
-
* @param errorFactory - Optional error constructor
|
|
1215
|
-
* @param message - Optional custom error message
|
|
1216
|
-
*/
|
|
1217
|
-
assert(action: string, status: string | null | undefined, errorFactory?: (msg: string) => Error, message?: string): void;
|
|
1218
|
-
/**
|
|
1219
|
-
* Get transition history
|
|
1220
|
-
*/
|
|
1221
|
-
getHistory?(): TransitionHistoryEntry[];
|
|
1222
|
-
/**
|
|
1223
|
-
* Record a transition
|
|
1224
|
-
*/
|
|
1225
|
-
recordTransition?(from: string, to: string, action: string, metadata?: Record<string, unknown>): void;
|
|
1226
|
-
/**
|
|
1227
|
-
* Clear history
|
|
1228
|
-
*/
|
|
1229
|
-
clearHistory?(): void;
|
|
1230
|
-
/**
|
|
1231
|
-
* Get available actions for current status
|
|
1232
|
-
*/
|
|
1233
|
-
getAvailableActions?(status: string): string[];
|
|
1234
|
-
}
|
|
1235
|
-
interface TransitionHistoryEntry {
|
|
1236
|
-
from: string;
|
|
1237
|
-
to: string;
|
|
1238
|
-
action: string;
|
|
1239
|
-
timestamp: Date;
|
|
1240
|
-
metadata?: Record<string, unknown>;
|
|
1241
|
-
}
|
|
1242
|
-
/** Context passed to transition guards and actions */
|
|
1243
|
-
interface TransitionContext {
|
|
1244
|
-
from: string;
|
|
1245
|
-
to: string;
|
|
1246
|
-
action: string;
|
|
1247
|
-
data?: Record<string, unknown>;
|
|
1248
|
-
}
|
|
1249
|
-
type TransitionGuard = (context: TransitionContext) => boolean | Promise<boolean>;
|
|
1250
|
-
type TransitionAction = (context: TransitionContext) => void | Promise<void>;
|
|
1251
|
-
type TransitionConfig = Record<string, string[] | {
|
|
1252
|
-
from: string[];
|
|
1253
|
-
to?: string;
|
|
1254
|
-
guard?: TransitionGuard;
|
|
1255
|
-
before?: TransitionAction;
|
|
1256
|
-
after?: TransitionAction;
|
|
1257
|
-
}>;
|
|
1258
|
-
/**
|
|
1259
|
-
* Create a state machine for validating transitions
|
|
1260
|
-
*
|
|
1261
|
-
* @param name - Name of the state machine (used in error messages)
|
|
1262
|
-
* @param transitions - Map of actions to allowed source statuses
|
|
1263
|
-
* @param options - Additional options (history, guards, actions)
|
|
1264
|
-
* @returns State machine with can() and assert() methods
|
|
1265
|
-
*
|
|
1266
|
-
* @example
|
|
1267
|
-
* // Basic usage
|
|
1268
|
-
* const transferState = createStateMachine('Transfer', {
|
|
1269
|
-
* approve: ['draft'],
|
|
1270
|
-
* dispatch: ['approved'],
|
|
1271
|
-
* receive: ['dispatched', 'in_transit'],
|
|
1272
|
-
* cancel: ['draft', 'approved'],
|
|
1273
|
-
* });
|
|
1274
|
-
*
|
|
1275
|
-
* @example
|
|
1276
|
-
* // With guards and actions
|
|
1277
|
-
* const orderState = createStateMachine('Order', {
|
|
1278
|
-
* approve: {
|
|
1279
|
-
* from: ['pending'],
|
|
1280
|
-
* to: 'approved',
|
|
1281
|
-
* guard: ({ data }) => data.paymentConfirmed,
|
|
1282
|
-
* before: ({ from, to }) => console.log(`Approving order from ${from} to ${to}`),
|
|
1283
|
-
* after: ({ data }) => sendApprovalEmail(data.customerId),
|
|
1284
|
-
* },
|
|
1285
|
-
* }, { trackHistory: true });
|
|
1286
|
-
*/
|
|
1287
|
-
declare function createStateMachine(name: string, transitions?: TransitionConfig, options?: {
|
|
1288
|
-
trackHistory?: boolean;
|
|
1289
|
-
}): StateMachine;
|
|
1290
|
-
//#endregion
|
|
1291
|
-
//#region src/utils/typeGuards.d.ts
|
|
1292
|
-
interface EventsDecorator {
|
|
1293
|
-
publish: <T>(type: string, payload: T, meta?: Record<string, unknown>) => Promise<void>;
|
|
1294
|
-
subscribe: (pattern: string, handler: (event: {
|
|
1295
|
-
type: string;
|
|
1296
|
-
payload: unknown;
|
|
1297
|
-
meta: Record<string, unknown>;
|
|
1298
|
-
}) => Promise<void>) => Promise<() => void>;
|
|
1299
|
-
transportName: string;
|
|
1300
|
-
}
|
|
1301
|
-
/** Check if fastify has the events plugin registered */
|
|
1302
|
-
declare function hasEvents(instance: FastifyInstance): instance is FastifyInstance & {
|
|
1303
|
-
events: EventsDecorator;
|
|
1304
|
-
};
|
|
1305
|
-
//#endregion
|
|
1306
|
-
//#region src/utils/userHelpers.d.ts
|
|
1307
|
-
/**
|
|
1308
|
-
* Extract a user ID from a user object. Accepts `id` or `_id` — returns
|
|
1309
|
-
* `undefined` when neither is present. Used by arc's controllers to
|
|
1310
|
-
* populate `createdBy` / `updatedBy` fields and for cache scoping.
|
|
1311
|
-
*
|
|
1312
|
-
* @example
|
|
1313
|
-
* ```ts
|
|
1314
|
-
* import { getUserId } from '@classytic/arc/utils';
|
|
1315
|
-
* const uid = getUserId(request.user);
|
|
1316
|
-
* ```
|
|
1317
|
-
*/
|
|
1318
|
-
declare function getUserId(user: UserLike | null | undefined): string | undefined;
|
|
1319
|
-
//#endregion
|
|
1320
|
-
export { ValidateOptions as $, ArcQueryParserOptions as A, CompensationResult as B, keysetListResponse as C, queryParams as D, paginationSchema as E, defineGuard as F, CircuitBreakerError as G, defineCompensation as H, defineErrorMapper as I, CircuitBreakerStats as J, CircuitBreakerOptions as K, CompensationDefinition as L, handleRaw as M, Guard as N, responses as O, GuardConfig as P, ConfigError as Q, CompensationError as R, getListQueryParams as S, offsetListResponse as T, withCompensation as U, CompensationStep as V, CircuitBreaker as W, createCircuitBreaker as X, CircuitState as Y, createCircuitBreakerRegistry as Z, bareListResponse as _, isArcError as _t, TransitionConfig as a, ConflictError as at, errorDetailSchema$1 as b, JsonSchemaTarget as c, NotFoundError as ct, isJsonSchema as d, RateLimitError as dt, ValidationResult as et, isZodSchema as f, ServiceUnavailableError as ft, aggregateListResponse as g, createError as gt, JsonSchema as h, createDomainError as ht, StateMachine as i, ArcError as it, createQueryParser as j, ArcQueryParser as k, convertOpenApiSchemas as l, OrgAccessDeniedError as lt, scheduleBackground as m, ValidationError as mt, EventsDecorator as n, formatValidationErrors as nt, createStateMachine as o, ErrorOptions as ot, toJsonSchema as p, UnauthorizedError as pt, CircuitBreakerRegistry as q, hasEvents as r, validateResourceConfig as rt, simpleEqualityMatcher as s, ForbiddenError as st, getUserId as t, assertValidConfig as tt, convertRouteSchema as u, OrgRequiredError as ut, deleteResponse as v, listResponse as w, getDefaultCrudSchemas as x, errorContractSchema$1 as y, CompensationHooks as z };
|