@classytic/arc 2.10.3 → 2.11.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 -1
- package/dist/{BaseController-CbKKIflT.mjs → BaseController-JNV08qOT.mjs} +595 -537
- package/dist/{queryCachePlugin-BKbWjgDG.d.mts → QueryCache-DOBNHBE0.d.mts} +2 -32
- package/dist/actionPermissions-C8YYU92K.mjs +22 -0
- package/dist/adapters/index.d.mts +2 -2
- package/dist/adapters/index.mjs +1 -1
- package/dist/{adapters-BXY4i-hw.mjs → adapters-D0tT2Tyo.mjs} +54 -0
- package/dist/audit/index.d.mts +2 -2
- package/dist/audit/index.mjs +15 -17
- package/dist/auth/index.d.mts +4 -4
- package/dist/auth/index.mjs +3 -3
- package/dist/auth/redis-session.d.mts +1 -1
- package/dist/{betterAuthOpenApi-BBRVhjQN.mjs → betterAuthOpenApi-DwxtK3uG.mjs} +1 -1
- package/dist/cache/index.d.mts +3 -2
- package/dist/cache/index.mjs +3 -3
- package/dist/cli/commands/docs.mjs +2 -2
- package/dist/cli/commands/generate.mjs +37 -27
- package/dist/cli/commands/init.mjs +47 -34
- 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 +3 -3
- package/dist/core/index.mjs +4 -3
- package/dist/core-DXdSSFW-.mjs +1037 -0
- package/dist/createActionRouter-BwaSM0No.mjs +166 -0
- package/dist/{createApp-BuvPma24.mjs → createApp-DvNYEhpb.mjs} +118 -36
- package/dist/docs/index.d.mts +2 -2
- package/dist/docs/index.mjs +1 -1
- package/dist/{elevation-C7hgL_aI.mjs → elevation-DOFoxoDs.mjs} +1 -1
- package/dist/errorHandler-Co3lnVmJ.d.mts +114 -0
- package/dist/{eventPlugin-DCUjuiQT.mjs → eventPlugin--5HIkdPU.mjs} +1 -1
- package/dist/{eventPlugin-CxWgpd6K.d.mts → eventPlugin-CUNjYYRY.d.mts} +1 -1
- package/dist/events/index.d.mts +4 -4
- package/dist/events/index.mjs +69 -51
- 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-Lo1VUDpt.d.mts → fields-C8Y0XLAu.d.mts} +1 -1
- package/dist/hooks/index.d.mts +1 -1
- package/dist/hooks/index.mjs +1 -1
- package/dist/idempotency/index.d.mts +3 -3
- package/dist/idempotency/index.mjs +38 -27
- package/dist/idempotency/redis.d.mts +1 -1
- package/dist/{index-ChIw3776.d.mts → index-BYCqHCVu.d.mts} +4 -4
- package/dist/{index-Cl0uoKd5.d.mts → index-Cm0vUrr_.d.mts} +2100 -1688
- package/dist/{index-DStwgFUK.d.mts → index-DAushRTt.d.mts} +29 -10
- package/dist/index-DsJ1MNfC.d.mts +1179 -0
- package/dist/{index-8qw4y6ff.d.mts → index-t8pLpPFW.d.mts} +13 -10
- package/dist/index.d.mts +7 -251
- package/dist/index.mjs +8 -128
- 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/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/integrations/streamline.d.mts +46 -5
- package/dist/integrations/streamline.mjs +50 -21
- package/dist/integrations/websocket-redis.d.mts +1 -1
- package/dist/integrations/websocket.d.mts +2 -154
- package/dist/integrations/websocket.mjs +292 -224
- package/dist/{keys-qcD-TVJl.mjs → keys-CARyUjiR.mjs} +2 -0
- package/dist/{loadResources-BAzJItAJ.mjs → loadResources-YNwKHvRA.mjs} +3 -1
- package/dist/logger/index.d.mts +81 -0
- package/dist/{logger-DLg8-Ueg.mjs → logger/index.mjs} +1 -6
- package/dist/middleware/index.d.mts +109 -0
- package/dist/middleware/index.mjs +70 -0
- package/dist/multipartBody-CvTR1Un6.mjs +123 -0
- package/dist/{openapi-B5F8AddX.mjs → openapi-C0L9ar7m.mjs} +9 -7
- package/dist/org/index.d.mts +2 -2
- package/dist/permissions/index.d.mts +2 -2
- package/dist/permissions/index.mjs +1 -3
- package/dist/{permissions-Dk6mshja.mjs → permissions-B4vU9L0Q.mjs} +220 -2
- package/dist/pipe-DVoIheVC.mjs +62 -0
- package/dist/pipeline/index.d.mts +62 -0
- package/dist/pipeline/index.mjs +53 -0
- package/dist/plugins/index.d.mts +25 -5
- package/dist/plugins/index.mjs +10 -10
- package/dist/plugins/response-cache.mjs +1 -1
- package/dist/plugins/tracing-entry.d.mts +1 -1
- package/dist/plugins/tracing-entry.mjs +42 -24
- package/dist/presets/filesUpload.d.mts +4 -4
- 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 +48 -8
- package/dist/presets/search.d.mts +2 -2
- package/dist/presets/search.mjs +1 -1
- package/dist/{presets-fLJVXdVn.mjs → presets-k604Lj99.mjs} +1 -1
- package/dist/queryCachePlugin-BUXBSm4F.d.mts +34 -0
- package/dist/{queryCachePlugin-DQCEfJis.mjs → queryCachePlugin-Bq6bO6vc.mjs} +3 -3
- package/dist/{redis-DqyeggCa.d.mts → redis-Cm1gnRDf.d.mts} +1 -1
- package/dist/{redis-stream-CakIQmwR.d.mts → redis-stream-CM8TXTix.d.mts} +1 -1
- package/dist/registry/index.d.mts +1 -1
- package/dist/registry/index.mjs +2 -2
- package/dist/{requestContext-xHIKedG6.mjs → requestContext-CfRkaxwf.mjs} +1 -1
- package/dist/{resourceToTools-BElv3xPT.mjs → resourceToTools--okX6QBr.mjs} +534 -415
- package/dist/routerShared-DeESFp4a.mjs +515 -0
- package/dist/schemaIR-BlG9bY7v.mjs +137 -0
- package/dist/scope/index.d.mts +2 -2
- package/dist/scope/index.mjs +1 -1
- package/dist/{sse-yBCgOLGu.mjs → sse-V7aXc3bW.mjs} +1 -1
- package/dist/{store-helpers-ZCSMJJAX.mjs → store-helpers-BhrzxvyQ.mjs} +4 -0
- package/dist/testing/index.d.mts +367 -711
- package/dist/testing/index.mjs +646 -1434
- package/dist/testing/storageContract.d.mts +1 -1
- package/dist/{tracing-65B51Dw3.d.mts → tracing-DokiEsuz.d.mts} +9 -4
- package/dist/types/index.d.mts +5 -5
- package/dist/types/index.mjs +1 -3
- package/dist/types/storage.d.mts +1 -1
- package/dist/{types-Co8k3NyS.d.mts → types-CgikqKAj.d.mts} +133 -21
- package/dist/{types-Btdda02s.d.mts → types-D9NqiYIw.d.mts} +1 -1
- package/dist/utils/index.d.mts +2 -898
- package/dist/utils/index.mjs +4 -5
- package/dist/utils-D3Yxnrwr.mjs +1639 -0
- package/dist/versioning-M9lNLhO8.d.mts +117 -0
- package/dist/websocket-CyJ1VIFI.d.mts +186 -0
- package/package.json +26 -8
- package/skills/arc/SKILL.md +124 -39
- package/skills/arc/references/testing.md +212 -183
- package/dist/applyPermissionResult-QhV1Pa-g.mjs +0 -37
- package/dist/core-CcR01lup.mjs +0 -1411
- package/dist/createActionRouter-Bp_5c_2b.mjs +0 -249
- package/dist/errorHandler-DRQ3EqfL.d.mts +0 -218
- package/dist/errors-CCSsMpXE.d.mts +0 -140
- package/dist/fields-bxkeltzz.mjs +0 -126
- package/dist/filesUpload-t21LS-py.mjs +0 -377
- package/dist/queryParser-DBqBB6AC.mjs +0 -352
- package/dist/types-Csi3FLfq.mjs +0 -27
- package/dist/utils-B2fNOD_i.mjs +0 -929
- /package/dist/{EventTransport-CUw5NNWe.d.mts → EventTransport-CfVEGaEl.d.mts} +0 -0
- /package/dist/{HookSystem-BNYKnrXF.mjs → HookSystem-CGsMd6oK.mjs} +0 -0
- /package/dist/{ResourceRegistry-BPd6NQDm.mjs → ResourceRegistry-DkAeAuTX.mjs} +0 -0
- /package/dist/{caching-CBpK_SCM.mjs → caching-CheW3m-S.mjs} +0 -0
- /package/dist/{elevation-C5SwtkAn.d.mts → elevation-s5ykdNHr.d.mts} +0 -0
- /package/dist/{errorHandler-Bb49BvPD.mjs → errorHandler-BQm8ZxTK.mjs} +0 -0
- /package/dist/{externalPaths-BQ8QijNH.d.mts → externalPaths-Bapitwvd.d.mts} +0 -0
- /package/dist/{interface-CSbZdv_3.d.mts → interface-CkkWm5uR.d.mts} +0 -0
- /package/dist/{interface-D218ikEo.d.mts → interface-Da0r7Lna.d.mts} +0 -0
- /package/dist/{memory-B5Amv9A1.mjs → memory-DikHSvWa.mjs} +0 -0
- /package/dist/{metrics-DuhiSEZI.mjs → metrics-Csh4nsvv.mjs} +0 -0
- /package/dist/{pluralize-A0tWEl1K.mjs → pluralize-BneOJkpi.mjs} +0 -0
- /package/dist/{registry-B3lRFBWo.mjs → registry-D63ee7fl.mjs} +0 -0
- /package/dist/{replyHelpers-CXtJDAZ0.mjs → replyHelpers-ByllIXXV.mjs} +0 -0
- /package/dist/{schemaConverter-BxFDdtXu.mjs → schemaConverter-B0oKLuqI.mjs} +0 -0
- /package/dist/{sessionManager-BkzVU8h2.d.mts → sessionManager-D-oNWHz3.d.mts} +0 -0
- /package/dist/{storage-CVk_SEn2.d.mts → storage-BwGQXUpd.d.mts} +0 -0
- /package/dist/{typeGuards-Cj5Rgvlg.mjs → typeGuards-CcFZXgU7.mjs} +0 -0
- /package/dist/{types-BD85MlEK.d.mts → types-tgR4Pt8F.d.mts} +0 -0
- /package/dist/{versioning-C2U_bLY0.mjs → versioning-CGPjkqAg.mjs} +0 -0
|
@@ -0,0 +1,166 @@
|
|
|
1
|
+
import { t as __exportAll } from "./chunk-BpYLSNr0.mjs";
|
|
2
|
+
import { a as buildAuthMiddlewareForPermissions, c as buildPreHandlerChain, d as resolveRouterPluginMw, f as selectPluginMw, l as buildRateLimitConfig, n as buildActionPipelineHandler, r as buildArcDecorator, t as buildActionPermissionMw, u as resolvePipelineSteps, v as sendControllerResponse } from "./routerShared-DeESFp4a.mjs";
|
|
3
|
+
import { n as schemaIRToJsonSchemaBranch, t as normalizeSchemaIR } from "./schemaIR-BlG9bY7v.mjs";
|
|
4
|
+
//#region src/core/createActionRouter.ts
|
|
5
|
+
var createActionRouter_exports = /* @__PURE__ */ __exportAll({
|
|
6
|
+
buildActionBodySchema: () => buildActionBodySchema,
|
|
7
|
+
createActionRouter: () => createActionRouter
|
|
8
|
+
});
|
|
9
|
+
/**
|
|
10
|
+
* Register the unified action endpoint: `POST /:id/action`.
|
|
11
|
+
*
|
|
12
|
+
* Shares every lifecycle primitive with the CRUD router — the preHandler
|
|
13
|
+
* chain, the arc decorator, idempotency, rate-limit, and the response
|
|
14
|
+
* shaper. The only thing that stays local is the dynamic permission check
|
|
15
|
+
* (keyed by `body.action` at request time).
|
|
16
|
+
*/
|
|
17
|
+
function createActionRouter(fastify, config) {
|
|
18
|
+
const { tag, resourceName = tag ?? "action", actions, actionPermissions = {}, actionSchemas = {}, globalAuth, onError, fields: fieldPermissions, schemaOptions, permissions: resourcePermissions, routeGuards = [], pipeline, rateLimit } = config;
|
|
19
|
+
const actionEnum = Object.keys(actions);
|
|
20
|
+
if (actionEnum.length === 0) {
|
|
21
|
+
fastify.log.warn("[createActionRouter] No actions defined, skipping route creation");
|
|
22
|
+
return;
|
|
23
|
+
}
|
|
24
|
+
const bodySchema = buildActionBodySchema(actionEnum, actionSchemas);
|
|
25
|
+
const routeSchema = {
|
|
26
|
+
tags: tag ? [tag] : void 0,
|
|
27
|
+
summary: `Perform action (${actionEnum.join("/")})`,
|
|
28
|
+
description: buildActionDescription(actions, actionPermissions),
|
|
29
|
+
params: {
|
|
30
|
+
type: "object",
|
|
31
|
+
properties: { id: {
|
|
32
|
+
type: "string",
|
|
33
|
+
description: "Resource ID"
|
|
34
|
+
} },
|
|
35
|
+
required: ["id"]
|
|
36
|
+
},
|
|
37
|
+
body: bodySchema
|
|
38
|
+
};
|
|
39
|
+
const arcDecorator = buildArcDecorator({
|
|
40
|
+
resourceName,
|
|
41
|
+
schemaOptions,
|
|
42
|
+
permissions: resourcePermissions,
|
|
43
|
+
hooks: fastify.arc?.hooks,
|
|
44
|
+
events: fastify.events,
|
|
45
|
+
fields: fieldPermissions
|
|
46
|
+
});
|
|
47
|
+
const authMw = buildAuthMiddlewareForPermissions(fastify, actionEnum.map((name) => actionPermissions[name] ?? globalAuth));
|
|
48
|
+
const pluginMw = resolveRouterPluginMw(fastify, false);
|
|
49
|
+
const wrappedHandlers = /* @__PURE__ */ new Map();
|
|
50
|
+
for (const [name, handler] of Object.entries(actions)) {
|
|
51
|
+
const steps = resolvePipelineSteps(pipeline, name);
|
|
52
|
+
wrappedHandlers.set(name, buildActionPipelineHandler(handler, steps, name, resourceName));
|
|
53
|
+
}
|
|
54
|
+
const preHandler = buildPreHandlerChain({
|
|
55
|
+
arcDecorator,
|
|
56
|
+
authMw,
|
|
57
|
+
permissionMw: buildActionPermissionMw(actionEnum, actionPermissions, globalAuth, resourceName),
|
|
58
|
+
pluginMw: selectPluginMw("POST", pluginMw),
|
|
59
|
+
routeGuards
|
|
60
|
+
});
|
|
61
|
+
const rateLimitConfig = buildRateLimitConfig(rateLimit);
|
|
62
|
+
fastify.route({
|
|
63
|
+
method: "POST",
|
|
64
|
+
url: "/:id/action",
|
|
65
|
+
schema: routeSchema,
|
|
66
|
+
preHandler: preHandler.length > 0 ? preHandler : void 0,
|
|
67
|
+
...rateLimitConfig ? { config: rateLimitConfig } : {},
|
|
68
|
+
handler: async (req, reply) => {
|
|
69
|
+
const { action, ...data } = req.body;
|
|
70
|
+
const { id } = req.params;
|
|
71
|
+
const handler = wrappedHandlers.get(action);
|
|
72
|
+
if (!handler) return sendControllerResponse(reply, {
|
|
73
|
+
success: false,
|
|
74
|
+
status: 400,
|
|
75
|
+
error: `Invalid action '${action}'. Valid actions: ${actionEnum.join(", ")}`,
|
|
76
|
+
meta: { validActions: actionEnum }
|
|
77
|
+
}, req);
|
|
78
|
+
try {
|
|
79
|
+
return sendControllerResponse(reply, await handler(id, data, req), req);
|
|
80
|
+
} catch (error) {
|
|
81
|
+
if (onError) {
|
|
82
|
+
const { statusCode, error: errorMsg, code } = onError(error, action, id);
|
|
83
|
+
return sendControllerResponse(reply, {
|
|
84
|
+
success: false,
|
|
85
|
+
status: statusCode,
|
|
86
|
+
error: errorMsg,
|
|
87
|
+
...code ? { meta: { code } } : {}
|
|
88
|
+
}, req);
|
|
89
|
+
}
|
|
90
|
+
const err = error;
|
|
91
|
+
const statusCode = err.statusCode || err.status || 500;
|
|
92
|
+
const errorCode = err.code || "ACTION_FAILED";
|
|
93
|
+
if (statusCode >= 500) req.log.error({
|
|
94
|
+
err: error,
|
|
95
|
+
action,
|
|
96
|
+
id
|
|
97
|
+
}, "Action handler error");
|
|
98
|
+
return sendControllerResponse(reply, {
|
|
99
|
+
success: false,
|
|
100
|
+
status: statusCode,
|
|
101
|
+
error: err.message || `Failed to execute '${action}' action`,
|
|
102
|
+
meta: { code: errorCode }
|
|
103
|
+
}, req);
|
|
104
|
+
}
|
|
105
|
+
}
|
|
106
|
+
});
|
|
107
|
+
fastify.log.debug({
|
|
108
|
+
actions: actionEnum,
|
|
109
|
+
tag,
|
|
110
|
+
resourceName
|
|
111
|
+
}, "[createActionRouter] Registered action endpoint: POST /:id/action");
|
|
112
|
+
}
|
|
113
|
+
/**
|
|
114
|
+
* Build a discriminated body schema for the unified action endpoint.
|
|
115
|
+
*
|
|
116
|
+
* Produces a schema of the form:
|
|
117
|
+
* ```json
|
|
118
|
+
* {
|
|
119
|
+
* "type": "object",
|
|
120
|
+
* "required": ["action"],
|
|
121
|
+
* "oneOf": [
|
|
122
|
+
* { "properties": { "action": { "const": "dispatch" }, "carrier": {...} }, "required": ["action", "carrier"] },
|
|
123
|
+
* { "properties": { "action": { "const": "approve" } }, "required": ["action"] }
|
|
124
|
+
* ]
|
|
125
|
+
* }
|
|
126
|
+
* ```
|
|
127
|
+
*
|
|
128
|
+
* AJV validates this natively, so an action call missing required fields is
|
|
129
|
+
* rejected with HTTP 400 before the handler ever runs.
|
|
130
|
+
*
|
|
131
|
+
* Exported so OpenAPI generation and MCP tool generation can reuse the same
|
|
132
|
+
* schema shape (single source of truth).
|
|
133
|
+
*/
|
|
134
|
+
function buildActionBodySchema(actionEnum, actionSchemas = {}) {
|
|
135
|
+
const branches = [];
|
|
136
|
+
for (const actionName of actionEnum) {
|
|
137
|
+
const ir = normalizeSchemaIR(actionSchemas[actionName]);
|
|
138
|
+
branches.push(schemaIRToJsonSchemaBranch(ir, {
|
|
139
|
+
properties: { action: {
|
|
140
|
+
type: "string",
|
|
141
|
+
const: actionName
|
|
142
|
+
} },
|
|
143
|
+
required: ["action"]
|
|
144
|
+
}));
|
|
145
|
+
}
|
|
146
|
+
return {
|
|
147
|
+
type: "object",
|
|
148
|
+
required: ["action"],
|
|
149
|
+
oneOf: branches
|
|
150
|
+
};
|
|
151
|
+
}
|
|
152
|
+
/**
|
|
153
|
+
* Build OpenAPI description with action list + role hints.
|
|
154
|
+
* Reads `_roles` metadata from permission checks for docs.
|
|
155
|
+
*/
|
|
156
|
+
function buildActionDescription(actions, actionPermissions) {
|
|
157
|
+
const lines = ["Unified action endpoint for state transitions.\n\n**Available actions:**"];
|
|
158
|
+
Object.keys(actions).forEach((action) => {
|
|
159
|
+
const roles = actionPermissions[action]?._roles;
|
|
160
|
+
const roleStr = roles?.length ? ` (requires: ${roles.join(" or ")})` : "";
|
|
161
|
+
lines.push(`- \`${action}\`${roleStr}`);
|
|
162
|
+
});
|
|
163
|
+
return lines.join("\n");
|
|
164
|
+
}
|
|
165
|
+
//#endregion
|
|
166
|
+
export { createActionRouter_exports as n, buildActionBodySchema as t };
|
|
@@ -116,10 +116,7 @@ const developmentPreset = {
|
|
|
116
116
|
"x-request-id"
|
|
117
117
|
]
|
|
118
118
|
},
|
|
119
|
-
rateLimit:
|
|
120
|
-
max: 1e3,
|
|
121
|
-
timeWindow: "1 minute"
|
|
122
|
-
},
|
|
119
|
+
rateLimit: false,
|
|
123
120
|
underPressure: {
|
|
124
121
|
exposeStatusRoute: true,
|
|
125
122
|
maxEventLoopDelay: 5e3
|
|
@@ -207,7 +204,7 @@ async function registerArcCore(fastify, config, trackPlugin) {
|
|
|
207
204
|
await fastify.register(arcCorePlugin, { emitEvents: config.arcPlugins?.emitEvents !== false });
|
|
208
205
|
trackPlugin("arc-core");
|
|
209
206
|
if (config.arcPlugins?.events !== false) {
|
|
210
|
-
const { default: eventPlugin } = await import("./eventPlugin
|
|
207
|
+
const { default: eventPlugin } = await import("./eventPlugin--5HIkdPU.mjs").then((n) => n.n);
|
|
211
208
|
const eventOpts = typeof config.arcPlugins?.events === "object" ? config.arcPlugins.events : {};
|
|
212
209
|
await fastify.register(eventPlugin, {
|
|
213
210
|
...eventOpts,
|
|
@@ -243,15 +240,15 @@ async function registerArcPlugins(fastify, config, trackPlugin, modules) {
|
|
|
243
240
|
trackPlugin("arc-graceful-shutdown");
|
|
244
241
|
}
|
|
245
242
|
if (config.arcPlugins?.caching) {
|
|
246
|
-
const { default: cachingPlugin } = await import("./caching-
|
|
243
|
+
const { default: cachingPlugin } = await import("./caching-CheW3m-S.mjs").then((n) => n.r);
|
|
247
244
|
const opts = config.arcPlugins.caching === true ? {} : config.arcPlugins.caching;
|
|
248
245
|
await fastify.register(cachingPlugin, opts);
|
|
249
246
|
trackPlugin("arc-caching", opts);
|
|
250
247
|
}
|
|
251
248
|
if (config.arcPlugins?.queryCache) {
|
|
252
|
-
const { queryCachePlugin } = await import("./queryCachePlugin-
|
|
249
|
+
const { queryCachePlugin } = await import("./queryCachePlugin-Bq6bO6vc.mjs").then((n) => n.n);
|
|
253
250
|
const opts = config.arcPlugins.queryCache === true ? {} : config.arcPlugins.queryCache;
|
|
254
|
-
const store = config.stores?.queryCache ?? new (await (import("./memory-
|
|
251
|
+
const store = config.stores?.queryCache ?? new (await (import("./memory-DikHSvWa.mjs").then((n) => n.n))).MemoryCacheStore();
|
|
255
252
|
await fastify.register(queryCachePlugin, {
|
|
256
253
|
store,
|
|
257
254
|
...opts
|
|
@@ -260,19 +257,19 @@ async function registerArcPlugins(fastify, config, trackPlugin, modules) {
|
|
|
260
257
|
}
|
|
261
258
|
if (config.arcPlugins?.sse) if (config.arcPlugins?.events === false) fastify.log.warn("SSE plugin requires events plugin (arcPlugins.events). SSE disabled.");
|
|
262
259
|
else {
|
|
263
|
-
const { default: ssePlugin } = await import("./sse-
|
|
260
|
+
const { default: ssePlugin } = await import("./sse-V7aXc3bW.mjs").then((n) => n.r);
|
|
264
261
|
const opts = config.arcPlugins.sse === true ? {} : config.arcPlugins.sse;
|
|
265
262
|
await fastify.register(ssePlugin, opts);
|
|
266
263
|
trackPlugin("arc-sse", opts);
|
|
267
264
|
}
|
|
268
265
|
if (config.arcPlugins?.metrics) {
|
|
269
|
-
const { default: metricsPlugin } = await import("./metrics-
|
|
266
|
+
const { default: metricsPlugin } = await import("./metrics-Csh4nsvv.mjs").then((n) => n.r);
|
|
270
267
|
const opts = config.arcPlugins.metrics === true ? {} : config.arcPlugins.metrics;
|
|
271
268
|
await fastify.register(metricsPlugin, opts);
|
|
272
269
|
trackPlugin("arc-metrics", opts);
|
|
273
270
|
}
|
|
274
271
|
if (config.arcPlugins?.versioning) {
|
|
275
|
-
const { default: versioningPlugin } = await import("./versioning-
|
|
272
|
+
const { default: versioningPlugin } = await import("./versioning-CGPjkqAg.mjs").then((n) => n.r);
|
|
276
273
|
await fastify.register(versioningPlugin, config.arcPlugins.versioning);
|
|
277
274
|
trackPlugin("arc-versioning", config.arcPlugins.versioning);
|
|
278
275
|
}
|
|
@@ -303,7 +300,8 @@ async function registerAuth(fastify, config, trackPlugin) {
|
|
|
303
300
|
const { plugin, openapi } = authConfig.betterAuth;
|
|
304
301
|
await fastify.register(plugin);
|
|
305
302
|
trackPlugin("auth-better-auth");
|
|
306
|
-
|
|
303
|
+
const arc = fastify.arc;
|
|
304
|
+
if (arc && openapi && !arc.externalOpenApiPaths.includes(openapi)) arc.externalOpenApiPaths.push(openapi);
|
|
307
305
|
fastify.log.debug("Better Auth authentication enabled");
|
|
308
306
|
break;
|
|
309
307
|
}
|
|
@@ -340,7 +338,7 @@ async function registerAuth(fastify, config, trackPlugin) {
|
|
|
340
338
|
*/
|
|
341
339
|
async function registerElevation(fastify, config, trackPlugin) {
|
|
342
340
|
if (!config.elevation) return;
|
|
343
|
-
const { elevationPlugin } = await import("./elevation-
|
|
341
|
+
const { elevationPlugin } = await import("./elevation-DOFoxoDs.mjs").then((n) => n.r);
|
|
344
342
|
await fastify.register(elevationPlugin, config.elevation);
|
|
345
343
|
trackPlugin("arc-elevation", config.elevation);
|
|
346
344
|
fastify.log.debug("Elevation plugin enabled");
|
|
@@ -350,7 +348,7 @@ async function registerElevation(fastify, config, trackPlugin) {
|
|
|
350
348
|
*/
|
|
351
349
|
async function registerErrorHandler(fastify, config, trackPlugin) {
|
|
352
350
|
if (config.errorHandler === false) return;
|
|
353
|
-
const { errorHandlerPlugin } = await import("./errorHandler-
|
|
351
|
+
const { errorHandlerPlugin } = await import("./errorHandler-BQm8ZxTK.mjs").then((n) => n.r);
|
|
354
352
|
const errorOpts = typeof config.errorHandler === "object" ? config.errorHandler : { includeStack: config.preset !== "production" };
|
|
355
353
|
await fastify.register(errorHandlerPlugin, errorOpts);
|
|
356
354
|
trackPlugin("arc-error-handler", errorOpts);
|
|
@@ -388,6 +386,9 @@ function createOptionalAuthenticate(authenticate) {
|
|
|
388
386
|
}
|
|
389
387
|
//#endregion
|
|
390
388
|
//#region src/factory/registerResources.ts
|
|
389
|
+
function isResourcesFactory(value) {
|
|
390
|
+
return typeof value === "function";
|
|
391
|
+
}
|
|
391
392
|
/** Register a single resource with descriptive error on failure. */
|
|
392
393
|
async function registerOne(parent, resource) {
|
|
393
394
|
const name = resource.name ?? "unknown";
|
|
@@ -396,18 +397,31 @@ async function registerOne(parent, resource) {
|
|
|
396
397
|
} catch (err) {
|
|
397
398
|
const msg = err instanceof Error ? err.message : String(err);
|
|
398
399
|
parent.log.error(`Failed to register resource "${name}": ${msg}`);
|
|
399
|
-
throw new Error(`Resource "${name}" failed to register: ${msg}. Check the resource definition, adapter, and permissions
|
|
400
|
+
throw new Error(`Resource "${name}" failed to register: ${msg}. Check the resource definition, adapter, and permissions.`, { cause: err });
|
|
400
401
|
}
|
|
401
402
|
}
|
|
402
403
|
/**
|
|
403
404
|
* Execute the full resource lifecycle:
|
|
404
|
-
* 1. plugins()
|
|
405
|
-
* 2. bootstrap[]
|
|
406
|
-
* 3. resources
|
|
407
|
-
*
|
|
408
|
-
*
|
|
405
|
+
* 1. plugins() — infra (DB, docs, webhooks)
|
|
406
|
+
* 2. bootstrap[] — domain init (singletons, event handlers)
|
|
407
|
+
* 3. resources factory (if any) — resolved AFTER bootstrap, so engine-backed
|
|
408
|
+
* adapters can `await ensureEngine()` and pass
|
|
409
|
+
* live models/repos into `defineResource(...)`
|
|
410
|
+
* 4. resources[] — register each (split by prefix)
|
|
411
|
+
* 5. afterResources() — post-registration wiring
|
|
412
|
+
* 6. onReady/onClose — lifecycle hooks
|
|
409
413
|
*/
|
|
410
414
|
async function registerResources(fastify, config) {
|
|
415
|
+
if (config.preset === "production") {
|
|
416
|
+
if (config.strictResources === void 0) config = {
|
|
417
|
+
...config,
|
|
418
|
+
strictResources: true
|
|
419
|
+
};
|
|
420
|
+
if (config.strictResourceDir === void 0) config = {
|
|
421
|
+
...config,
|
|
422
|
+
strictResourceDir: true
|
|
423
|
+
};
|
|
424
|
+
}
|
|
411
425
|
if (config.plugins) {
|
|
412
426
|
await config.plugins(fastify);
|
|
413
427
|
fastify.log.debug("Custom plugins registered");
|
|
@@ -416,33 +430,65 @@ async function registerResources(fastify, config) {
|
|
|
416
430
|
for (const init of config.bootstrap) await init(fastify);
|
|
417
431
|
fastify.log.debug(`${config.bootstrap.length} bootstrap function(s) executed`);
|
|
418
432
|
}
|
|
419
|
-
|
|
420
|
-
|
|
421
|
-
|
|
422
|
-
|
|
433
|
+
let resolvedResources;
|
|
434
|
+
if (isResourcesFactory(config.resources)) {
|
|
435
|
+
try {
|
|
436
|
+
resolvedResources = await config.resources(fastify);
|
|
437
|
+
} catch (err) {
|
|
438
|
+
const msg = err instanceof Error ? err.message : String(err);
|
|
439
|
+
fastify.log.error(`Resources factory threw during boot: ${msg}`);
|
|
440
|
+
throw new Error(`[arc] resources factory threw: ${msg}. Check engine bootstrap order (did you forget a bootstrap step?) and that \`defineResource(...)\` calls inside the factory receive fully-booted adapters / repositories.`, { cause: err });
|
|
441
|
+
}
|
|
423
442
|
config = {
|
|
424
443
|
...config,
|
|
425
|
-
resources:
|
|
444
|
+
resources: resolvedResources
|
|
426
445
|
};
|
|
446
|
+
} else resolvedResources = config.resources;
|
|
447
|
+
let discoveryRawDir;
|
|
448
|
+
let discoveryPath;
|
|
449
|
+
let discoveryYieldedZero = false;
|
|
450
|
+
if (resolvedResources === void 0 && config.resourceDir) {
|
|
451
|
+
const { loadResources } = await import("./loadResources-YNwKHvRA.mjs").then((n) => n.n);
|
|
452
|
+
const { resolve, dirname } = await import("node:path");
|
|
453
|
+
const { fileURLToPath } = await import("node:url");
|
|
454
|
+
const rawDir = config.resourceDir;
|
|
455
|
+
const dir = rawDir.startsWith("file://") ? dirname(fileURLToPath(rawDir)) : resolve(rawDir);
|
|
456
|
+
discoveryRawDir = rawDir;
|
|
457
|
+
discoveryPath = dir;
|
|
458
|
+
const discovered = await loadResources(dir, { logger: fastify.log });
|
|
459
|
+
if (discovered.length === 0) {
|
|
460
|
+
if (config.strictResourceDir) throw new Error(`[arc] loadResources: resourceDir "${rawDir}" resolved to "${dir}" but yielded 0 resources. Check the path, file naming (*.resource.{ts,js,mts,mjs}), and runtime layout (src/ vs dist/). Use \`strictResourceDir: true\` to fail boot.`);
|
|
461
|
+
discoveryYieldedZero = true;
|
|
462
|
+
}
|
|
463
|
+
resolvedResources = discovered;
|
|
427
464
|
}
|
|
428
|
-
if (
|
|
465
|
+
if (resolvedResources && resolvedResources.length > 0) {
|
|
429
466
|
const seen = /* @__PURE__ */ new Set();
|
|
430
|
-
for (const resource of
|
|
431
|
-
if (seen.has(resource.name))
|
|
467
|
+
for (const resource of resolvedResources) if (resource.name) {
|
|
468
|
+
if (seen.has(resource.name)) {
|
|
469
|
+
const msg = `Duplicate resource name "${resource.name}" detected. This will cause route conflicts. Check your resources array and loadResources() output. Common cause: stale compiled files in dist/ alongside src/. Use \`strictResources: true\` to fail boot.`;
|
|
470
|
+
if (config.strictResources) throw new Error(msg);
|
|
471
|
+
fastify.log.warn(msg);
|
|
472
|
+
}
|
|
432
473
|
seen.add(resource.name);
|
|
433
474
|
}
|
|
434
475
|
const prefixed = [];
|
|
435
476
|
const root = [];
|
|
436
|
-
for (const resource of
|
|
477
|
+
for (const resource of resolvedResources) if (resource.skipGlobalPrefix) root.push(resource);
|
|
437
478
|
else prefixed.push(resource);
|
|
438
479
|
for (const resource of root) await registerOne(fastify, resource);
|
|
439
480
|
if (prefixed.length) if (config.resourcePrefix) await fastify.register(async (scoped) => {
|
|
440
481
|
for (const resource of prefixed) await registerOne(scoped, resource);
|
|
441
482
|
}, { prefix: config.resourcePrefix });
|
|
442
483
|
else for (const resource of prefixed) await registerOne(fastify, resource);
|
|
443
|
-
const names =
|
|
484
|
+
const names = resolvedResources.map((r) => r.name ?? "?").join(", ");
|
|
485
|
+
const prefix = config.resourcePrefix ? ` (prefix: ${config.resourcePrefix})` : "";
|
|
486
|
+
fastify.log.info(`${resolvedResources.length} resource(s) registered${prefix}: ${names}`);
|
|
487
|
+
} else {
|
|
444
488
|
const prefix = config.resourcePrefix ? ` (prefix: ${config.resourcePrefix})` : "";
|
|
445
|
-
|
|
489
|
+
const scanned = discoveryPath ? ` — resourceDir "${discoveryRawDir}" resolved to "${discoveryPath}"` : "";
|
|
490
|
+
const hints = discoveryYieldedZero ? ` but yielded 0 resources. Check the path, file naming (*.resource.{ts,js,mts,mjs}), and runtime layout (src/ vs dist/). Use \`strictResourceDir: true\` to fail boot.` : "";
|
|
491
|
+
fastify.log.warn(`0 resources registered${prefix}${scanned}${hints}`);
|
|
446
492
|
}
|
|
447
493
|
if (config.afterResources) {
|
|
448
494
|
await config.afterResources(fastify);
|
|
@@ -463,6 +509,40 @@ async function registerResources(fastify, config) {
|
|
|
463
509
|
}
|
|
464
510
|
//#endregion
|
|
465
511
|
//#region src/factory/registerSecurity.ts
|
|
512
|
+
/**
|
|
513
|
+
* Translate `skipPaths` sugar into a `@fastify/rate-limit` `allowList`
|
|
514
|
+
* function. A user-supplied `allowList` (array of IPs or function) is
|
|
515
|
+
* preserved and OR-ed with the path match.
|
|
516
|
+
*/
|
|
517
|
+
function buildRateLimitOpts(input) {
|
|
518
|
+
const { skipPaths, allowList, ...rest } = input;
|
|
519
|
+
if (!skipPaths || skipPaths.length === 0) return allowList === void 0 ? rest : {
|
|
520
|
+
...rest,
|
|
521
|
+
allowList
|
|
522
|
+
};
|
|
523
|
+
const matchesPath = compilePathMatcher(skipPaths);
|
|
524
|
+
const combined = async (req, key) => {
|
|
525
|
+
if (matchesPath((req.url ?? "").split("?", 1)[0] ?? "")) return true;
|
|
526
|
+
if (typeof allowList === "function") return await allowList(req, key);
|
|
527
|
+
if (Array.isArray(allowList)) return allowList.includes(key);
|
|
528
|
+
return false;
|
|
529
|
+
};
|
|
530
|
+
return {
|
|
531
|
+
...rest,
|
|
532
|
+
allowList: combined
|
|
533
|
+
};
|
|
534
|
+
}
|
|
535
|
+
function compilePathMatcher(patterns) {
|
|
536
|
+
const prefixes = [];
|
|
537
|
+
const exact = /* @__PURE__ */ new Set();
|
|
538
|
+
for (const p of patterns) if (p.endsWith("*")) prefixes.push(p.slice(0, -1));
|
|
539
|
+
else exact.add(p);
|
|
540
|
+
return (path) => {
|
|
541
|
+
if (exact.has(path)) return true;
|
|
542
|
+
for (const pre of prefixes) if (path.startsWith(pre)) return true;
|
|
543
|
+
return false;
|
|
544
|
+
};
|
|
545
|
+
}
|
|
466
546
|
const PLUGIN_REGISTRY = {
|
|
467
547
|
cors: {
|
|
468
548
|
package: "@fastify/cors",
|
|
@@ -532,10 +612,10 @@ async function registerSecurityPlugins(fastify, config) {
|
|
|
532
612
|
} else fastify.log.warn("CORS disabled");
|
|
533
613
|
if (config.rateLimit !== false) {
|
|
534
614
|
const rateLimit = await loadPlugin("rateLimit");
|
|
535
|
-
const rateLimitOpts = config.rateLimit ?? {
|
|
615
|
+
const rateLimitOpts = buildRateLimitOpts(config.rateLimit ?? {
|
|
536
616
|
max: 100,
|
|
537
617
|
timeWindow: "1 minute"
|
|
538
|
-
};
|
|
618
|
+
});
|
|
539
619
|
await fastify.register(rateLimit, rateLimitOpts);
|
|
540
620
|
if (!(typeof rateLimitOpts === "object" && "store" in rateLimitOpts)) {
|
|
541
621
|
if (config.runtime === "distributed") throw new Error("[Arc] runtime: 'distributed' with rate limiting requires a shared store.\nProvide rateLimit: { store: new RedisStore({ ... }) } or disable rate limiting: rateLimit: false");
|
|
@@ -676,7 +756,7 @@ function validateDistributedRuntime(options) {
|
|
|
676
756
|
*/
|
|
677
757
|
async function createApp(options) {
|
|
678
758
|
if (options.debug !== void 0 && options.debug !== false) {
|
|
679
|
-
const { configureArcLogger } = await import("./logger
|
|
759
|
+
const { configureArcLogger } = await import("./logger/index.mjs");
|
|
680
760
|
configureArcLogger({ debug: options.debug });
|
|
681
761
|
}
|
|
682
762
|
validateAuthOptions(options);
|
|
@@ -717,7 +797,9 @@ async function createApp(options) {
|
|
|
717
797
|
await registerSecurityPlugins(fastify, config);
|
|
718
798
|
await registerUtilityPlugins(fastify, config);
|
|
719
799
|
const trackPlugin = (name, opts) => {
|
|
720
|
-
fastify.arc
|
|
800
|
+
const arc = fastify.arc;
|
|
801
|
+
if (!arc) return;
|
|
802
|
+
arc.plugins.set(name, {
|
|
721
803
|
name,
|
|
722
804
|
options: opts,
|
|
723
805
|
registeredAt: (/* @__PURE__ */ new Date()).toISOString()
|
|
@@ -730,7 +812,7 @@ async function createApp(options) {
|
|
|
730
812
|
await registerErrorHandler(fastify, config, trackPlugin);
|
|
731
813
|
await registerResources(fastify, config);
|
|
732
814
|
if (config.replyHelpers) {
|
|
733
|
-
const { replyHelpersPlugin } = await import("./replyHelpers-
|
|
815
|
+
const { replyHelpersPlugin } = await import("./replyHelpers-ByllIXXV.mjs").then((n) => n.n);
|
|
734
816
|
await fastify.register(replyHelpersPlugin);
|
|
735
817
|
}
|
|
736
818
|
if (config.serializeBigInt) fastify.addHook("preSerialization", async (_request, _reply, payload) => {
|
package/dist/docs/index.d.mts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import { t as ExternalOpenApiPaths } from "../externalPaths-
|
|
1
|
+
import { p as RegistryEntry } from "../index-Cm0vUrr_.mjs";
|
|
2
|
+
import { t as ExternalOpenApiPaths } from "../externalPaths-Bapitwvd.mjs";
|
|
3
3
|
import { FastifyPluginAsync } from "fastify";
|
|
4
4
|
|
|
5
5
|
//#region src/docs/openapi.d.ts
|
package/dist/docs/index.mjs
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { t as getUserRoles } from "../types-DV9WDfeg.mjs";
|
|
2
|
-
import { n as openApiPlugin, r as openapi_default, t as buildOpenApiSpec } from "../openapi-
|
|
2
|
+
import { n as openApiPlugin, r as openapi_default, t as buildOpenApiSpec } from "../openapi-C0L9ar7m.mjs";
|
|
3
3
|
import fp from "fastify-plugin";
|
|
4
4
|
//#region src/docs/scalar.ts
|
|
5
5
|
const scalarPlugin = async (fastify, opts = {}) => {
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { t as __exportAll } from "./chunk-BpYLSNr0.mjs";
|
|
2
|
+
import { arcLog } from "./logger/index.mjs";
|
|
2
3
|
import { t as getUserRoles } from "./types-DV9WDfeg.mjs";
|
|
3
|
-
import { t as arcLog } from "./logger-DLg8-Ueg.mjs";
|
|
4
4
|
import fp from "fastify-plugin";
|
|
5
5
|
//#region src/scope/elevation.ts
|
|
6
6
|
var elevation_exports = /* @__PURE__ */ __exportAll({
|
|
@@ -0,0 +1,114 @@
|
|
|
1
|
+
import { FastifyInstance, FastifyRequest } from "fastify";
|
|
2
|
+
|
|
3
|
+
//#region src/plugins/errorHandler.d.ts
|
|
4
|
+
/** Class-based error mapper — maps thrown error instances to HTTP responses */
|
|
5
|
+
interface ErrorMapper<T extends Error = Error> {
|
|
6
|
+
/**
|
|
7
|
+
* Error class to match. Checked at runtime via `instanceof` — the constructor
|
|
8
|
+
* arity/signature is not called by the plugin, so the signature is typed
|
|
9
|
+
* permissively to accept real-world error classes:
|
|
10
|
+
*
|
|
11
|
+
* - **Abstract classes** (e.g. base domain errors) — `abstract new` is accepted.
|
|
12
|
+
* - **Specific constructor signatures** (e.g. `new InvalidTransitionError(from, to, id?)`)
|
|
13
|
+
* — `any[]` avoids forcing consumers to widen to `unknown[]` or cast.
|
|
14
|
+
*
|
|
15
|
+
* What matters for dispatch is the `instanceof` check, not the ctor shape.
|
|
16
|
+
*/
|
|
17
|
+
type: abstract new (...args: any[]) => T;
|
|
18
|
+
/** Convert the error to an HTTP response shape */
|
|
19
|
+
toResponse: (error: T) => {
|
|
20
|
+
status: number;
|
|
21
|
+
code?: string;
|
|
22
|
+
message?: string;
|
|
23
|
+
details?: Record<string, unknown>;
|
|
24
|
+
};
|
|
25
|
+
}
|
|
26
|
+
interface ErrorHandlerOptions {
|
|
27
|
+
/**
|
|
28
|
+
* Include stack trace in error responses (default: false in production)
|
|
29
|
+
*/
|
|
30
|
+
includeStack?: boolean;
|
|
31
|
+
/**
|
|
32
|
+
* Custom error callback for logging to external services
|
|
33
|
+
*/
|
|
34
|
+
onError?: (error: Error, request: FastifyRequest) => void | Promise<void>;
|
|
35
|
+
/**
|
|
36
|
+
* Map specific error types to custom responses (by error.name string)
|
|
37
|
+
*/
|
|
38
|
+
errorMap?: Record<string, {
|
|
39
|
+
statusCode: number;
|
|
40
|
+
code: string;
|
|
41
|
+
message?: string;
|
|
42
|
+
}>;
|
|
43
|
+
/**
|
|
44
|
+
* Class-based error mappers — checked via `instanceof`, highest priority.
|
|
45
|
+
*
|
|
46
|
+
* Register your domain error classes once; Arc auto-catches and maps them
|
|
47
|
+
* in every handler. Handlers just `throw` — no try/catch needed.
|
|
48
|
+
*
|
|
49
|
+
* @example
|
|
50
|
+
* ```typescript
|
|
51
|
+
* class AccountingError extends Error {
|
|
52
|
+
* constructor(message: string, public status: number, public code: string) {
|
|
53
|
+
* super(message);
|
|
54
|
+
* }
|
|
55
|
+
* }
|
|
56
|
+
*
|
|
57
|
+
* const app = await createApp({
|
|
58
|
+
* errorHandler: {
|
|
59
|
+
* errorMappers: [
|
|
60
|
+
* {
|
|
61
|
+
* type: AccountingError,
|
|
62
|
+
* toResponse: (err) => ({ status: err.status, code: err.code, message: err.message }),
|
|
63
|
+
* },
|
|
64
|
+
* ],
|
|
65
|
+
* },
|
|
66
|
+
* });
|
|
67
|
+
*
|
|
68
|
+
* // Now handlers just throw:
|
|
69
|
+
* handler: async (req) => {
|
|
70
|
+
* await ledger.post(id); // throws AccountingError → Arc maps to proper HTTP response
|
|
71
|
+
* }
|
|
72
|
+
* ```
|
|
73
|
+
*/
|
|
74
|
+
errorMappers?: ErrorMapper[];
|
|
75
|
+
/**
|
|
76
|
+
* Classify an error as a duplicate-key / unique-constraint violation →
|
|
77
|
+
* mapped to `409 Conflict` with `code: "DUPLICATE_KEY"`.
|
|
78
|
+
*
|
|
79
|
+
* Mirrors `RepositoryLike.isDuplicateKeyError` for the Fastify layer: errors
|
|
80
|
+
* that escape a controller (custom routes, user hooks, raw driver calls)
|
|
81
|
+
* still land here, so the classifier is duplicated at the edge. Defaults
|
|
82
|
+
* cover MongoDB (`code 11000` / `codeName "DuplicateKey"`), Prisma
|
|
83
|
+
* (`code "P2002"`), and Postgres (`code "23505"`). Override to add other
|
|
84
|
+
* backends (DynamoDB `ConditionalCheckFailedException`, etc.) or to disable
|
|
85
|
+
* the built-in detection.
|
|
86
|
+
*/
|
|
87
|
+
isDuplicateKeyError?: (err: unknown) => boolean;
|
|
88
|
+
}
|
|
89
|
+
/**
|
|
90
|
+
* Default duplicate-key detector covering the mainstream drivers arc sees
|
|
91
|
+
* most. Detection is strictly by known driver codes — never by message
|
|
92
|
+
* string matching — because false positives on dup-key silently mask real
|
|
93
|
+
* errors (WriteConflict, NotWritablePrimary, etc.) as 409s. For long-tail
|
|
94
|
+
* drivers (Neo4j, MSSQL, DynamoDB, custom kits), compose rather than
|
|
95
|
+
* replace:
|
|
96
|
+
*
|
|
97
|
+
* ```ts
|
|
98
|
+
* import { defaultIsDuplicateKeyError } from '@classytic/arc/plugins';
|
|
99
|
+
*
|
|
100
|
+
* errorHandler: {
|
|
101
|
+
* isDuplicateKeyError: (err) =>
|
|
102
|
+
* defaultIsDuplicateKeyError(err) || isNeo4jDupKey(err),
|
|
103
|
+
* }
|
|
104
|
+
* ```
|
|
105
|
+
*
|
|
106
|
+
* Drizzle apps get coverage transitively (Drizzle doesn't wrap driver
|
|
107
|
+
* errors — pg/mysql2/better-sqlite3 codes propagate as-is). Neon is
|
|
108
|
+
* Postgres-wire-compatible → `23505` covers `@neondatabase/serverless`.
|
|
109
|
+
*/
|
|
110
|
+
declare function defaultIsDuplicateKeyError(err: unknown): boolean;
|
|
111
|
+
declare function errorHandlerPluginFn(fastify: FastifyInstance, options?: ErrorHandlerOptions): Promise<void>;
|
|
112
|
+
declare const errorHandlerPlugin: typeof errorHandlerPluginFn;
|
|
113
|
+
//#endregion
|
|
114
|
+
export { errorHandlerPlugin as i, ErrorMapper as n, defaultIsDuplicateKeyError as r, ErrorHandlerOptions as t };
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { t as __exportAll } from "./chunk-BpYLSNr0.mjs";
|
|
2
|
-
import { t as requestContext } from "./requestContext-
|
|
2
|
+
import { t as requestContext } from "./requestContext-CfRkaxwf.mjs";
|
|
3
3
|
import fp from "fastify-plugin";
|
|
4
4
|
//#region src/events/EventTransport.ts
|
|
5
5
|
/**
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { i as EventLogger, n as DomainEvent, o as EventTransport, r as EventHandler } from "./EventTransport-
|
|
1
|
+
import { i as EventLogger, n as DomainEvent, o as EventTransport, r as EventHandler } from "./EventTransport-CfVEGaEl.mjs";
|
|
2
2
|
import { FastifyPluginAsync } from "fastify";
|
|
3
3
|
|
|
4
4
|
//#region src/events/defineEvent.d.ts
|
package/dist/events/index.d.mts
CHANGED
|
@@ -1,8 +1,8 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import { a as EventMeta, c as MemoryEventTransportOptions, d as createEvent, i as EventLogger, l as PublishManyResult, n as DomainEvent, o as EventTransport, r as EventHandler, s as MemoryEventTransport, t as DeadLetteredEvent, u as createChildEvent } from "../EventTransport-
|
|
3
|
-
import { a as withRetry, c as EventDefinitionOutput, d as EventSchema, f as ValidationResult, i as createDeadLetterPublisher, l as EventRegistry, m as defineEvent, n as eventPlugin, o as CustomValidator, p as createEventRegistry, r as RetryOptions, s as EventDefinitionInput, t as EventPluginOptions, u as EventRegistryOptions } from "../eventPlugin-
|
|
1
|
+
import { jn as RepositoryLike } from "../index-Cm0vUrr_.mjs";
|
|
2
|
+
import { a as EventMeta, c as MemoryEventTransportOptions, d as createEvent, i as EventLogger, l as PublishManyResult, n as DomainEvent, o as EventTransport, r as EventHandler, s as MemoryEventTransport, t as DeadLetteredEvent, u as createChildEvent } from "../EventTransport-CfVEGaEl.mjs";
|
|
3
|
+
import { a as withRetry, c as EventDefinitionOutput, d as EventSchema, f as ValidationResult, i as createDeadLetterPublisher, l as EventRegistry, m as defineEvent, n as eventPlugin, o as CustomValidator, p as createEventRegistry, r as RetryOptions, s as EventDefinitionInput, t as EventPluginOptions, u as EventRegistryOptions } from "../eventPlugin-CUNjYYRY.mjs";
|
|
4
4
|
import { RedisEventTransportOptions, RedisLike } from "./transports/redis.mjs";
|
|
5
|
-
import { r as RedisStreamTransportOptions, t as RedisStreamLike } from "../redis-stream-
|
|
5
|
+
import { r as RedisStreamTransportOptions, t as RedisStreamLike } from "../redis-stream-CM8TXTix.mjs";
|
|
6
6
|
|
|
7
7
|
//#region src/events/eventTypes.d.ts
|
|
8
8
|
/**
|