@classytic/arc 2.16.0 → 2.17.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 +22 -2
- package/dist/{BaseController-DlCCTIxJ.mjs → BaseController-Dmeh4Y_y.mjs} +5 -47
- package/dist/{QueryCache-SvmT_9ti.d.mts → QueryCache-8YmAQ-zW.d.mts} +1 -1
- package/dist/{ResourceRegistry-f48hFk3m.mjs → ResourceRegistry-CzSzVK1m.mjs} +3 -0
- package/dist/audit/index.d.mts +1 -1
- package/dist/audit/index.mjs +2 -1
- package/dist/auth/index.d.mts +4 -4
- package/dist/auth/index.mjs +2 -2
- package/dist/auth/redis-session.d.mts +1 -1
- package/dist/auth/redis-session.mjs +28 -2
- package/dist/{buildHandler-BZX6zzDM.mjs → buildHandler-C_SkBNF3.mjs} +6 -4
- package/dist/cache/index.d.mts +3 -3
- package/dist/cli/commands/describe.d.mts +1 -1
- package/dist/cli/commands/describe.mjs +1 -1
- package/dist/cli/commands/docs.mjs +2 -2
- package/dist/cli/commands/generate.d.mts +1 -1
- package/dist/cli/commands/generate.mjs +1 -1
- package/dist/cli/commands/init.d.mts +1 -1
- package/dist/cli/commands/init.mjs +1 -1
- package/dist/cli/commands/introspect.mjs +1 -1
- package/dist/cli/index.d.mts +2 -2
- package/dist/cli/index.mjs +2 -2
- package/dist/core/index.d.mts +2 -2
- package/dist/core/index.mjs +3 -3
- package/dist/{core-DBJ_j6rX.mjs → core-DYJvu7aI.mjs} +174 -31
- package/dist/{createActionRouter-DUpN3Dd1.mjs → createActionRouter-BHUMb1-y.mjs} +6 -3
- package/dist/{createAggregationRouter-Dq-TUCuY.mjs → createAggregationRouter-BYVvhfc5.mjs} +3 -3
- package/dist/{createApp-DNccuhyI.mjs → createApp-CzCEEsEm.mjs} +9 -9
- package/dist/{defineEvent-DRwY0fYm.mjs → defineEvent-NPKUJRt8.mjs} +1 -1
- package/dist/docs/index.d.mts +2 -2
- package/dist/docs/index.mjs +2 -2
- package/dist/{elevation-Dci0AYLT.mjs → elevation-6mfTLBC-.mjs} +1 -1
- package/dist/{errorHandler-DpoXQHZ9.mjs → errorHandler-wQejMBT3.mjs} +45 -4
- package/dist/{eventPlugin-C2cGqtRO.mjs → eventPlugin-CnJCUyM0.mjs} +1 -1
- package/dist/{eventPlugin-CtHC_av1.d.mts → eventPlugin-DjEx_J3j.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-stream-entry.mjs +34 -1
- package/dist/events/transports/redis.d.mts +1 -1
- package/dist/events/transports/redis.mjs +17 -1
- package/dist/factory/index.d.mts +2 -2
- package/dist/factory/index.mjs +4 -3
- package/dist/fastify-augmentation-7LIR0nDv.d.mts +42 -0
- package/dist/{fields-Anj0xdih.d.mts → fields-DqZLp5d6.d.mts} +1 -1
- package/dist/hooks/index.d.mts +1 -1
- package/dist/idempotency/index.d.mts +2 -2
- package/dist/idempotency/index.mjs +21 -5
- package/dist/idempotency/redis.d.mts +1 -1
- package/dist/{index-3oIimXQn.d.mts → index-3L-adFJO.d.mts} +3 -3
- package/dist/{index-CkW0flkU.d.mts → index-C1JSBan-.d.mts} +256 -8
- package/dist/{index-B-ulKx5P.d.mts → index-CruZir1o.d.mts} +36 -16
- package/dist/index.d.mts +5 -5
- package/dist/index.mjs +5 -5
- 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.d.mts +36 -1
- package/dist/integrations/jobs.mjs +82 -9
- package/dist/integrations/mcp/index.d.mts +67 -5
- package/dist/integrations/mcp/index.mjs +93 -2
- package/dist/integrations/mcp/testing.d.mts +1 -1
- package/dist/integrations/mcp/testing.mjs +1 -1
- package/dist/integrations/websocket-redis.d.mts +1 -1
- package/dist/integrations/websocket.d.mts +1 -1
- package/dist/integrations/websocket.mjs +2 -1
- package/dist/{loadResourcesFromEntry-BLMEI2Xa.mjs → loadResourcesFromEntry-DCjYMgfC.mjs} +1 -1
- package/dist/{mcpPlugin-7vGV51ED.mjs → mcpPlugin-hZE00H74.mjs} +114 -11
- package/dist/middleware/index.d.mts +1 -1
- package/dist/middleware/index.mjs +1 -1
- package/dist/{multipartBody-BOvVSVCD.mjs → multipartBody-VjVWmZl8.mjs} +2 -1
- package/dist/{openapi-34T9yNwd.mjs → openapi-DKDs9NfD.mjs} +37 -10
- package/dist/permissions/index.d.mts +2 -2
- package/dist/permissions/index.mjs +2 -2
- package/dist/{permissions-CTxMrreC.mjs → permissions-B1Qy55fg.mjs} +169 -169
- package/dist/pipeline/index.d.mts +1 -1
- package/dist/plugins/index.d.mts +5 -59
- package/dist/plugins/index.mjs +7 -7
- package/dist/plugins/tracing-entry.d.mts +1 -1
- package/dist/plugins/tracing-entry.mjs +1 -1
- package/dist/preloadResources-BOXzPhKU.mjs +45 -0
- 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/search.d.mts +2 -2
- package/dist/presets/search.mjs +1 -1
- package/dist/{presets-C9BE6WaZ.mjs → presets-BYbPobbF.mjs} +5 -3
- package/dist/{queryCachePlugin-Biqzfbi5.d.mts → queryCachePlugin-Do4P_WBC.d.mts} +2 -2
- package/dist/{redis-Cyzrz6SX.d.mts → redis-cpqVb85w.d.mts} +1 -1
- package/dist/{redis-stream-DT-YjzrB.d.mts → redis-stream-BuiAS3aE.d.mts} +1 -1
- package/dist/registry/index.d.mts +30 -8
- package/dist/registry/index.mjs +2 -2
- package/dist/{registry-BBE23CDj.mjs → registry-BrlHkrRS.mjs} +14 -8
- package/dist/{routerShared-CZV5aabX.mjs → routerShared-nCyJGOKX.mjs} +164 -17
- package/dist/scope/index.d.mts +2 -2
- package/dist/scope/index.mjs +2 -2
- package/dist/testing/index.d.mts +2 -25
- package/dist/testing/index.mjs +2 -45
- package/dist/testing/storageContract.d.mts +1 -1
- package/dist/types/index.d.mts +4 -4
- package/dist/types/storage.d.mts +1 -1
- package/dist/{types-D-fYtKjb.d.mts → types-BRmS8TNa.d.mts} +34 -11
- package/dist/{types-BsJMEQ4D.d.mts → types-CoMjNmkF.d.mts} +12 -1
- package/dist/utils/index.d.mts +83 -5
- package/dist/utils/index.mjs +2 -2
- package/dist/{utils-DC5ycPfr.mjs → utils-CvD3glpM.mjs} +168 -3
- package/dist/{validate-By96rH0r.mjs → validate-DFIP2VrM.mjs} +65 -3
- package/dist/{versioning-ZwX9tmbS.d.mts → versioning-5H4930tj.d.mts} +1 -1
- package/package.json +3 -3
- package/skills/arc/SKILL.md +43 -1
- package/skills/arc/references/api-reference.md +2 -0
- package/skills/arc/references/mcp.md +30 -1
- /package/dist/{EventTransport-C-2oAHtw.d.mts → EventTransport-B1GFiYUy.d.mts} +0 -0
- /package/dist/{EventTransport-Hxvv5QQz.mjs → EventTransport-ClEDfiMS.mjs} +0 -0
- /package/dist/{caching-TeHE8G-v.mjs → caching-8MfrrUYU.mjs} +0 -0
- /package/dist/{elevation-0YBpa663.d.mts → elevation-DvwtjDwu.d.mts} +0 -0
- /package/dist/{errorHandler-mHuyWzZE.d.mts → errorHandler-DuqTqeqn.d.mts} +0 -0
- /package/dist/{errors-C1lX_jlm.d.mts → errors-4ZloYFvm.d.mts} +0 -0
- /package/dist/{externalPaths-DFg-2KTp.d.mts → externalPaths-ClykOQqa.d.mts} +0 -0
- /package/dist/{generate-CYac-OLv.mjs → generate-CcM24hoq.mjs} +0 -0
- /package/dist/{generate-BWFwgcCM.d.mts → generate-cRnsafMS.d.mts} +0 -0
- /package/dist/{init-Dv71MsJr.d.mts → init-D-vrTZ1G.d.mts} +0 -0
- /package/dist/{init-HDvoO9L5.mjs → init-DYzfniuX.mjs} +0 -0
- /package/dist/{interface-CH0OQudo.d.mts → interface-BVkZsvEe.d.mts} +0 -0
- /package/dist/{interface-NwJ_qPlY.d.mts → interface-DoE6UoC2.d.mts} +0 -0
- /package/dist/{loadResources-ChQEj8ih.mjs → loadResources-fol6JuvW.mjs} +0 -0
- /package/dist/{metrics-TuOmguhi.mjs → metrics-B-PU4-Yu.mjs} +0 -0
- /package/dist/{replyHelpers-C-gD32oF.mjs → replyHelpers-boS5Rx26.mjs} +0 -0
- /package/dist/{schemaIR-Ctc89DSn.mjs → schemaIR-DAOvBWgT.mjs} +0 -0
- /package/dist/{sessionManager-BqFegc0W.d.mts → sessionManager-DlmaHUAl.d.mts} +0 -0
- /package/dist/{sse-BY6sTy4P.mjs → sse-jh5OqFjR.mjs} +0 -0
- /package/dist/{storage-D2KZJAmn.d.mts → storage-HcAgP5Ni.d.mts} +0 -0
- /package/dist/{store-helpers-B0sunfZZ.mjs → store-helpers-5JgaxieH.mjs} +0 -0
- /package/dist/{tracing-Dm8n7Cnn.d.mts → tracing-bRqqUxFO.d.mts} +0 -0
- /package/dist/{types-DVfpSfx2.d.mts → types-CfapW2t8.d.mts} +0 -0
- /package/dist/{types-D57iXYb8.mjs → types-DV9WDfeg.mjs} +0 -0
- /package/dist/{versioning-B6mimogM.mjs → versioning-CuIwUQZ2.mjs} +0 -0
- /package/dist/{websocket-BkjeGZRn.d.mts → websocket-BJ0w-3Dt.d.mts} +0 -0
|
@@ -1,11 +1,11 @@
|
|
|
1
1
|
import { s as DEFAULT_UPDATE_METHOD, t as CRUD_OPERATIONS } from "./constants-TrJVIJl0.mjs";
|
|
2
2
|
import { arcLog } from "./logger/index.mjs";
|
|
3
|
-
import {
|
|
4
|
-
import { t as BaseController } from "./BaseController-
|
|
5
|
-
import { t as applyPresets } from "./presets-
|
|
3
|
+
import { N as assertValidConfig, f as getDefaultCrudSchemas, i as pipeUIMessageStreamToReply, r as isReadableStream } from "./utils-CvD3glpM.mjs";
|
|
4
|
+
import { t as BaseController } from "./BaseController-Dmeh4Y_y.mjs";
|
|
5
|
+
import { t as applyPresets } from "./presets-BYbPobbF.mjs";
|
|
6
6
|
import { n as convertRouteSchema, t as convertOpenApiSchemas } from "./schemaConverter-De34B1ZG.mjs";
|
|
7
7
|
import { t as hasEvents } from "./typeGuards-BzkXkvVv.mjs";
|
|
8
|
-
import {
|
|
8
|
+
import { C as buildRequestScopeProjection, _ as createCrudHandlers, c as buildPipelineHandler, d as methodCarriesBody, f as resolvePipelineSteps, g as tryRegisterRoute, h as selectPluginMw, i as buildAuthMiddleware, l as buildPreHandlerChain, m as resolveRouterPluginMw, o as buildCrudPermissionMw, p as resolveRoutePreHandlers, r as buildArcDecorator, s as buildFieldWritePreHandler, u as buildRateLimitConfig, v as createFastifyHandler, y as createRequestContext } from "./routerShared-nCyJGOKX.mjs";
|
|
9
9
|
import { t as pluralize } from "./pluralize-B9M8xvy-.mjs";
|
|
10
10
|
import { t as resolveActionPermission } from "./actionPermissions-Bjmvn7Eb.mjs";
|
|
11
11
|
//#region src/core/aggregation/defineAggregation.ts
|
|
@@ -60,7 +60,7 @@ function defineAggregation(config) {
|
|
|
60
60
|
* `wrapHandler` is derived inline from `!route.raw`.
|
|
61
61
|
*/
|
|
62
62
|
function createCustomRoutes(fastify, routes, controller, options) {
|
|
63
|
-
const { tag, resourceName, arcDecorator, rateLimitConfig, pluginMw, pipeline, routeGuards, tenantScopeMw } = options;
|
|
63
|
+
const { tag, resourceName, arcDecorator, rateLimitConfig, pluginMw, pipeline, routeGuards, tenantScopeMw, fieldPermissions, onFieldWriteDenied } = options;
|
|
64
64
|
for (const route of routes) {
|
|
65
65
|
const routeWithRefs = route;
|
|
66
66
|
const hasHandler = route.handler !== void 0;
|
|
@@ -103,6 +103,10 @@ function createCustomRoutes(fastify, routes, controller, options) {
|
|
|
103
103
|
if (!tenantScopeMw || tenantScopeMw.length === 0) throw new Error(`Route ${route.method} ${route.path}: \`tenantScope: true\` requires a multi-tenant preset. Add \`multiTenantPreset()\` (or \`flexibleMultiTenantPreset()\`) to the resource's \`presets\`, or remove the \`tenantScope\` flag from this route.`);
|
|
104
104
|
customPreHandlers.unshift(...tenantScopeMw);
|
|
105
105
|
}
|
|
106
|
+
if (route.raw !== true && route.fieldWrite !== false && methodCarriesBody(route.method)) {
|
|
107
|
+
const fieldWriteMw = buildFieldWritePreHandler(fieldPermissions, onFieldWriteDenied);
|
|
108
|
+
if (fieldWriteMw) customPreHandlers.push(fieldWriteMw);
|
|
109
|
+
}
|
|
106
110
|
const preHandler = buildPreHandlerChain({
|
|
107
111
|
preAuth: route.preAuth ?? [],
|
|
108
112
|
arcDecorator,
|
|
@@ -113,7 +117,7 @@ function createCustomRoutes(fastify, routes, controller, options) {
|
|
|
113
117
|
customMws: customPreHandlers
|
|
114
118
|
});
|
|
115
119
|
const isStream = route.streamResponse === true;
|
|
116
|
-
fastify
|
|
120
|
+
tryRegisterRoute(fastify, {
|
|
117
121
|
method: route.method,
|
|
118
122
|
url: route.path,
|
|
119
123
|
schema,
|
|
@@ -122,9 +126,14 @@ function createCustomRoutes(fastify, routes, controller, options) {
|
|
|
122
126
|
reply.raw.setHeader("Content-Type", "text/event-stream");
|
|
123
127
|
reply.raw.setHeader("Cache-Control", "no-cache");
|
|
124
128
|
reply.raw.setHeader("Connection", "keep-alive");
|
|
125
|
-
|
|
129
|
+
const result = await handler(request, reply);
|
|
130
|
+
if (isReadableStream(result)) await pipeUIMessageStreamToReply(reply, result);
|
|
131
|
+
return result;
|
|
126
132
|
} : handler,
|
|
127
133
|
...rateLimitConfig ? { config: rateLimitConfig } : {}
|
|
134
|
+
}, {
|
|
135
|
+
resourceName,
|
|
136
|
+
op: opName
|
|
128
137
|
});
|
|
129
138
|
}
|
|
130
139
|
}
|
|
@@ -138,7 +147,7 @@ function createCustomRoutes(fastify, routes, controller, options) {
|
|
|
138
147
|
* @param options - Router configuration
|
|
139
148
|
*/
|
|
140
149
|
function createCrudRouter(fastify, controller, options = {}) {
|
|
141
|
-
const { tag = "Resource", schemas = {}, permissions = {}, middlewares = {}, routeGuards = [], routes: customRoutes = [], disableDefaultRoutes = false, disabledRoutes = [], resourceName = "unknown", schemaOptions, rateLimit, pipe: pipeline, fields: fieldPermissions, updateMethod = DEFAULT_UPDATE_METHOD, idField } = options;
|
|
150
|
+
const { tag = "Resource", schemas = {}, permissions = {}, middlewares = {}, routeGuards = [], routes: customRoutes = [], disableDefaultRoutes = false, disabledRoutes = [], resourceName = "unknown", schemaOptions, rateLimit, pipe: pipeline, fields: fieldPermissions, onFieldWriteDenied, updateMethod = DEFAULT_UPDATE_METHOD, idField } = options;
|
|
142
151
|
const rateLimitConfig = buildRateLimitConfig(rateLimit);
|
|
143
152
|
const resourceHasQueryCache = fastify.hasDecorator("queryCache") && controller && typeof controller._cacheConfig !== "undefined" && controller._cacheConfig !== void 0;
|
|
144
153
|
const pluginMw = resolveRouterPluginMw(fastify, Boolean(resourceHasQueryCache));
|
|
@@ -229,7 +238,7 @@ function createCrudRouter(fastify, controller, options = {}) {
|
|
|
229
238
|
const methodsToRegister = spec.op === "update" ? updateMethod === "both" ? ["PUT", "PATCH"] : [updateMethod] : [spec.method];
|
|
230
239
|
for (const method of methodsToRegister) {
|
|
231
240
|
const summary = spec.op === "update" ? `${method === "PUT" ? "Replace" : "Update"} ${tag}` : spec.summary;
|
|
232
|
-
fastify
|
|
241
|
+
tryRegisterRoute(fastify, {
|
|
233
242
|
method,
|
|
234
243
|
url: spec.url,
|
|
235
244
|
schema: buildSchema({
|
|
@@ -240,6 +249,9 @@ function createCrudRouter(fastify, controller, options = {}) {
|
|
|
240
249
|
preHandler: preHandler.length > 0 ? preHandler : void 0,
|
|
241
250
|
handler: handlers[spec.op],
|
|
242
251
|
...rateLimitConfig ? { config: rateLimitConfig } : {}
|
|
252
|
+
}, {
|
|
253
|
+
resourceName,
|
|
254
|
+
op: spec.op
|
|
243
255
|
});
|
|
244
256
|
}
|
|
245
257
|
}
|
|
@@ -252,7 +264,9 @@ function createCrudRouter(fastify, controller, options = {}) {
|
|
|
252
264
|
pluginMw,
|
|
253
265
|
pipeline,
|
|
254
266
|
routeGuards,
|
|
255
|
-
tenantScopeMw
|
|
267
|
+
tenantScopeMw,
|
|
268
|
+
fieldPermissions,
|
|
269
|
+
onFieldWriteDenied
|
|
256
270
|
});
|
|
257
271
|
}
|
|
258
272
|
function buildCrudHandlers(ctrl, pipeline, resourceName) {
|
|
@@ -442,11 +456,14 @@ function buildBaseController(resolvedConfig, adapter, repository) {
|
|
|
442
456
|
const limitProp = qp.getQuerySchema()?.properties?.limit;
|
|
443
457
|
if (limitProp?.maximum) maxLimitFromParser = limitProp.maximum;
|
|
444
458
|
}
|
|
459
|
+
const resourceConfigMaxLimit = resolvedConfig.maxLimit;
|
|
460
|
+
const resourceConfigDefaultLimit = resolvedConfig.defaultLimit;
|
|
445
461
|
return new BaseController(repository, {
|
|
446
462
|
resourceName: resolvedConfig.name,
|
|
447
463
|
schemaOptions: resolvedConfig.schemaOptions,
|
|
448
464
|
queryParser: qp,
|
|
449
|
-
maxLimit: maxLimitFromParser,
|
|
465
|
+
maxLimit: resourceConfigMaxLimit ?? maxLimitFromParser,
|
|
466
|
+
...resourceConfigDefaultLimit !== void 0 ? { defaultLimit: resourceConfigDefaultLimit } : {},
|
|
450
467
|
tenantField: resolvedConfig.tenantField,
|
|
451
468
|
idField: resolvedConfig.idField,
|
|
452
469
|
...resolvedConfig.defaultSort !== void 0 ? { defaultSort: resolvedConfig.defaultSort } : {},
|
|
@@ -1033,10 +1050,21 @@ function buildResourcePlugin(resource) {
|
|
|
1033
1050
|
handler: hook.handler,
|
|
1034
1051
|
priority: hook.priority
|
|
1035
1052
|
});
|
|
1036
|
-
if (isFirstMount && resource.cache?.invalidateOn
|
|
1053
|
+
if (isFirstMount && resource.cache?.invalidateOn) if (fastify.registerCacheInvalidationRule) for (const [pattern, tags] of Object.entries(resource.cache.invalidateOn)) fastify.registerCacheInvalidationRule({
|
|
1037
1054
|
pattern,
|
|
1038
1055
|
tags
|
|
1039
1056
|
});
|
|
1057
|
+
else fastify.log?.warn?.(`[Arc] Resource '${resource.name}' declares \`cache.invalidateOn\` but \`queryCachePlugin\` is not registered. Cache-invalidation rules are silently ignored. Either register the plugin (\`await fastify.register(queryCachePlugin, ...)\`) or remove the \`cache.invalidateOn\` config to clean up the resource definition.`);
|
|
1058
|
+
if (isFirstMount && resource.audit) {
|
|
1059
|
+
if (!(fastify.hasDecorator("audit") && typeof fastify.audit?.create === "function" && fastify.audit?._noop !== true)) fastify.log?.warn?.(`[Arc] Resource '${resource.name}' declares \`audit: ${JSON.stringify(resource.audit)}\` but \`auditPlugin\` is not registered (or is registered with a noop logger). Audit events for this resource will NOT be recorded. Either register \`auditPlugin\` (\`await fastify.register(auditPlugin, { store: ... })\`) or remove the \`audit\` flag from the resource definition.`);
|
|
1060
|
+
}
|
|
1061
|
+
if (isFirstMount && resource.events && Object.keys(resource.events).length > 0) {
|
|
1062
|
+
const eventsDecor = fastify.events;
|
|
1063
|
+
if (!(eventsDecor != null && typeof eventsDecor.publish === "function")) {
|
|
1064
|
+
const eventNames = Object.keys(resource.events).join(", ");
|
|
1065
|
+
fastify.log?.warn?.(`[Arc] Resource '${resource.name}' declares events (${eventNames}) but \`eventPlugin\` is not registered. The events will silently NOT be emitted on CRUD mutations. Either register the plugin (\`await fastify.register(eventPlugin, { transport: ... })\`) or remove the \`events\` declaration from the resource.`);
|
|
1066
|
+
}
|
|
1067
|
+
}
|
|
1040
1068
|
await fastify.register(async (instance) => {
|
|
1041
1069
|
const typedInstance = instance;
|
|
1042
1070
|
let schemas = buildGeneratedCrudSchemas(resource._registryMeta?.openApiSchemas, resource.customSchemas);
|
|
@@ -1061,10 +1089,11 @@ function buildResourcePlugin(resource) {
|
|
|
1061
1089
|
updateMethod: resource.updateMethod,
|
|
1062
1090
|
pipe: resource.pipe,
|
|
1063
1091
|
fields: resource.fields,
|
|
1092
|
+
onFieldWriteDenied: resource.onFieldWriteDenied,
|
|
1064
1093
|
idField: resource.idField
|
|
1065
1094
|
});
|
|
1066
1095
|
if (resource.actions && Object.keys(resource.actions).length > 0) {
|
|
1067
|
-
const { createActionRouter } = await import("./createActionRouter-
|
|
1096
|
+
const { createActionRouter } = await import("./createActionRouter-BHUMb1-y.mjs").then((n) => n.n);
|
|
1068
1097
|
createActionRouter(typedInstance, {
|
|
1069
1098
|
...normalizeActionsToRouterConfig(resource.actions, resource.actionPermissions, resource.tag, resource.permissions, resource.name, typedInstance.log),
|
|
1070
1099
|
resourceName: resource.name,
|
|
@@ -1078,7 +1107,7 @@ function buildResourcePlugin(resource) {
|
|
|
1078
1107
|
});
|
|
1079
1108
|
}
|
|
1080
1109
|
if (resource.aggregations && Object.keys(resource.aggregations).length > 0) {
|
|
1081
|
-
const [{ createAggregationRouter }, { adapterSupportsAggregate, ArcAggregationConfigError }] = await Promise.all([import("./createAggregationRouter-
|
|
1110
|
+
const [{ createAggregationRouter }, { adapterSupportsAggregate, ArcAggregationConfigError }] = await Promise.all([import("./createAggregationRouter-BYVvhfc5.mjs"), import("./validate-DFIP2VrM.mjs").then((n) => n.i)]);
|
|
1082
1111
|
const repoForAgg = resource.controller?.repository ?? resource.repository;
|
|
1083
1112
|
if (!adapterSupportsAggregate(repoForAgg)) {
|
|
1084
1113
|
const undispatchable = Object.entries(resource.aggregations).filter(([, cfg]) => !cfg.materialized).map(([name]) => name);
|
|
@@ -1142,7 +1171,16 @@ var ResourceDefinition = class {
|
|
|
1142
1171
|
updateMethod;
|
|
1143
1172
|
pipe;
|
|
1144
1173
|
fields;
|
|
1174
|
+
/**
|
|
1175
|
+
* Policy for field-write denials — `'reject'` (default, secure) raises 403
|
|
1176
|
+
* listing the denied fields; `'strip'` silently drops them. Honored by
|
|
1177
|
+
* both `BodySanitizer` (auto-CRUD) and `buildFieldWritePreHandler`
|
|
1178
|
+
* (custom routes) so the policy is consistent across all body-bearing
|
|
1179
|
+
* routes for the resource.
|
|
1180
|
+
*/
|
|
1181
|
+
onFieldWriteDenied;
|
|
1145
1182
|
cache;
|
|
1183
|
+
referenceData;
|
|
1146
1184
|
/**
|
|
1147
1185
|
* Per-resource MCP opt-out. `false` keeps the resource out of every
|
|
1148
1186
|
* `mcpPlugin` registration regardless of the plugin's `expose` /
|
|
@@ -1174,9 +1212,37 @@ var ResourceDefinition = class {
|
|
|
1174
1212
|
* `buildResourcePlugin` so the host's configured logger owns the
|
|
1175
1213
|
* output — the framework never speaks to `console.*` from `src/`
|
|
1176
1214
|
* outside of the CLI.
|
|
1215
|
+
*
|
|
1216
|
+
* Prefer the public {@link warnings} getter for programmatic access
|
|
1217
|
+
* (CI fail-on-warnings, lint integrations) — it returns a frozen
|
|
1218
|
+
* snapshot so callers can't accidentally mutate the diagnostic list.
|
|
1177
1219
|
*/
|
|
1178
1220
|
_diagnostics;
|
|
1179
1221
|
/**
|
|
1222
|
+
* Public view of {@link _diagnostics} for programmatic consumption.
|
|
1223
|
+
*
|
|
1224
|
+
* Returns the collected define-time diagnostics for this resource.
|
|
1225
|
+
* Empty when the resource validated cleanly. Useful for CI gates that
|
|
1226
|
+
* want to fail the build on any non-fatal warning:
|
|
1227
|
+
*
|
|
1228
|
+
* @example
|
|
1229
|
+
* ```ts
|
|
1230
|
+
* import { orderResource } from './resources/order.resource.js';
|
|
1231
|
+
* if (orderResource.warnings.length > 0) {
|
|
1232
|
+
* for (const w of orderResource.warnings) console.error(w.message);
|
|
1233
|
+
* process.exit(1);
|
|
1234
|
+
* }
|
|
1235
|
+
* ```
|
|
1236
|
+
*
|
|
1237
|
+
* **Scope.** Covers define-time only — diagnostics emitted at
|
|
1238
|
+
* first-mount (e.g. `cache.invalidateOn` without `queryCachePlugin`)
|
|
1239
|
+
* surface through `fastify.log.warn` instead and are not collected
|
|
1240
|
+
* here; they require a Fastify instance to detect.
|
|
1241
|
+
*/
|
|
1242
|
+
get warnings() {
|
|
1243
|
+
return this._diagnostics ?? [];
|
|
1244
|
+
}
|
|
1245
|
+
/**
|
|
1180
1246
|
* Per-host idempotency guard used by `buildResourcePlugin` to
|
|
1181
1247
|
* skip duplicate shared-state writes when the same resource is
|
|
1182
1248
|
* mounted at multiple prefixes (`/v1`, `/v2`). See the plugin
|
|
@@ -1209,8 +1275,10 @@ var ResourceDefinition = class {
|
|
|
1209
1275
|
this.updateMethod = config.updateMethod;
|
|
1210
1276
|
this.pipe = config.pipe;
|
|
1211
1277
|
this.fields = config.fields;
|
|
1278
|
+
this.onFieldWriteDenied = config.onFieldWriteDenied;
|
|
1212
1279
|
this.cache = config.cache;
|
|
1213
1280
|
this.mcp = config.mcp !== false;
|
|
1281
|
+
this.referenceData = config.referenceData === true;
|
|
1214
1282
|
this.tenantField = config.tenantField;
|
|
1215
1283
|
this.onTenantDelete = config.onTenantDelete;
|
|
1216
1284
|
this.resolvedTenantPurge = resolveTenantPurge({
|
|
@@ -1364,6 +1432,7 @@ function resolveOpenApiSchemas(resolvedConfig) {
|
|
|
1364
1432
|
openApiSchemas = stripSystemManagedFromBodyRequired(openApiSchemas, resolvedConfig.schemaOptions);
|
|
1365
1433
|
openApiSchemas = cleanLegacyObjectIdParams(openApiSchemas, resolvedConfig.idField);
|
|
1366
1434
|
openApiSchemas = layerQueryParserListQuery(openApiSchemas, resolvedConfig.queryParser);
|
|
1435
|
+
openApiSchemas = applyResourcePaginationCaps(openApiSchemas, resolvedConfig);
|
|
1367
1436
|
openApiSchemas = mergeUserOpenApiOverrides(openApiSchemas, resolvedConfig.openApiSchemas);
|
|
1368
1437
|
if (openApiSchemas) openApiSchemas = convertOpenApiSchemas(openApiSchemas);
|
|
1369
1438
|
return {
|
|
@@ -1435,6 +1504,45 @@ function layerQueryParserListQuery(openApiSchemas, queryParser) {
|
|
|
1435
1504
|
};
|
|
1436
1505
|
}
|
|
1437
1506
|
/**
|
|
1507
|
+
* Apply resource-level `defaultLimit` / `maxLimit` (2.17.0) onto the
|
|
1508
|
+
* listQuery schema. Resource-level caps win over both the framework
|
|
1509
|
+
* default and the parser-derived value because they're the most-explicit
|
|
1510
|
+
* declaration — a host that wrote `defaultLimit: 1000` on the resource
|
|
1511
|
+
* means "this resource specifically returns up to 1000", regardless of
|
|
1512
|
+
* what the parser's default would have been. The `referenceData: true`
|
|
1513
|
+
* shorthand lifts both to 1000 so reference-data resources don't need
|
|
1514
|
+
* a custom queryParser just to get the caps right.
|
|
1515
|
+
*
|
|
1516
|
+
* Bootstraps a minimal listQuery slot when none exists (no queryParser
|
|
1517
|
+
* registered, no adapter-generated schema) so the AJV layer still gets
|
|
1518
|
+
* a `maximum:` and surfaces the cap in the 400 envelope via
|
|
1519
|
+
* `errorHandler.describePaginationCap()` instead of producing the
|
|
1520
|
+
* generic "Bad Request" we ate pre-2.17.0.
|
|
1521
|
+
*/
|
|
1522
|
+
function applyResourcePaginationCaps(openApiSchemas, resolvedConfig) {
|
|
1523
|
+
const cfg = resolvedConfig;
|
|
1524
|
+
if (cfg.defaultLimit === void 0 && cfg.maxLimit === void 0) return openApiSchemas;
|
|
1525
|
+
const existing = openApiSchemas?.listQuery;
|
|
1526
|
+
const existingProps = existing?.properties ?? {};
|
|
1527
|
+
const patchedLimit = { ...existingProps.limit ?? {
|
|
1528
|
+
type: "integer",
|
|
1529
|
+
minimum: 1
|
|
1530
|
+
} };
|
|
1531
|
+
if (cfg.maxLimit !== void 0) patchedLimit.maximum = cfg.maxLimit;
|
|
1532
|
+
if (cfg.defaultLimit !== void 0) patchedLimit.default = cfg.defaultLimit;
|
|
1533
|
+
return {
|
|
1534
|
+
...openApiSchemas,
|
|
1535
|
+
listQuery: {
|
|
1536
|
+
type: "object",
|
|
1537
|
+
...existing ?? {},
|
|
1538
|
+
properties: {
|
|
1539
|
+
...existingProps,
|
|
1540
|
+
limit: patchedLimit
|
|
1541
|
+
}
|
|
1542
|
+
}
|
|
1543
|
+
};
|
|
1544
|
+
}
|
|
1545
|
+
/**
|
|
1438
1546
|
* Apply per-resource `openApiSchemas` overrides on top of the kit's
|
|
1439
1547
|
* generated schemas. Shallow merge by slot — users who want field-level
|
|
1440
1548
|
* surgery should compose at the schema-options layer before this point.
|
|
@@ -1547,24 +1655,8 @@ function validateActionsShape(config) {
|
|
|
1547
1655
|
}
|
|
1548
1656
|
//#endregion
|
|
1549
1657
|
//#region src/core/defineResource.ts
|
|
1550
|
-
/**
|
|
1551
|
-
* `TDoc` is **unconstrained** at this layer. The previous `TDoc
|
|
1552
|
-
* extends AnyRecord` bound leaked out of `BaseController`'s
|
|
1553
|
-
* mixin-composition requirement into every host's adapter boundary:
|
|
1554
|
-
* Mongoose's `HydratedDocument<T>`, Prisma's generated row types,
|
|
1555
|
-
* and any domain interface without an explicit index signature all
|
|
1556
|
-
* failed to satisfy `Record<string, unknown>` even though at runtime
|
|
1557
|
-
* they ARE string-keyed objects. Hosts were forced to cast at every
|
|
1558
|
-
* adapter (`as RepositoryLike<Record<string, unknown>>`) — a type
|
|
1559
|
-
* escape with no runtime purpose, since arc's pipeline only reads
|
|
1560
|
-
* known envelope fields.
|
|
1561
|
-
*
|
|
1562
|
-
* The cast moved inside `resolveOrAutoCreateController` where
|
|
1563
|
-
* `BaseController<TDoc extends AnyRecord>` actually requires it.
|
|
1564
|
-
* One internal boundary cast replaces N host-side casts.
|
|
1565
|
-
*/
|
|
1566
1658
|
function defineResource(config) {
|
|
1567
|
-
const normalisedConfig = resolveCrudAllowList(config);
|
|
1659
|
+
const normalisedConfig = resolveCrudAllowList(expandCustomRoutesOnly(expandReferenceData(config)));
|
|
1568
1660
|
let diagnostics = [];
|
|
1569
1661
|
if (!normalisedConfig.skipValidation) diagnostics = validateDefineResourceConfig(normalisedConfig);
|
|
1570
1662
|
const repository = normalisedConfig.adapter?.repository;
|
|
@@ -1606,6 +1698,57 @@ function defineResource(config) {
|
|
|
1606
1698
|
* doesn't false-positive "Data adapter required when CRUD routes are
|
|
1607
1699
|
* enabled" on a host that explicitly opted CRUD out.
|
|
1608
1700
|
*/
|
|
1701
|
+
/**
|
|
1702
|
+
* Expand the `referenceData: true` shorthand. Reference data is the
|
|
1703
|
+
* recurring "small static catalogue" shape (currencies, plans,
|
|
1704
|
+
* pipeline stages, credential types) where callers want "fetch all"
|
|
1705
|
+
* semantics and aggressive caching — pre-2.17.0 hosts hand-wired
|
|
1706
|
+
* `crud` + `defaultLimit` + `maxLimit` + `cache` on every such
|
|
1707
|
+
* resource. The shorthand pins canonical defaults; explicit narrow
|
|
1708
|
+
* settings always win so a host can opt INTO mutations
|
|
1709
|
+
* (`referenceData: true, crud: { list: true, get: true, create: true }`)
|
|
1710
|
+
* or override the cache window without giving up the read-only +
|
|
1711
|
+
* fetch-all defaults.
|
|
1712
|
+
*/
|
|
1713
|
+
function expandReferenceData(config) {
|
|
1714
|
+
if (config.referenceData !== true) return config;
|
|
1715
|
+
return {
|
|
1716
|
+
...config,
|
|
1717
|
+
crud: config.crud ?? {
|
|
1718
|
+
list: true,
|
|
1719
|
+
get: true
|
|
1720
|
+
},
|
|
1721
|
+
defaultLimit: config.defaultLimit ?? 1e3,
|
|
1722
|
+
maxLimit: config.maxLimit ?? 1e3,
|
|
1723
|
+
cache: config.cache ?? {
|
|
1724
|
+
staleTime: 300,
|
|
1725
|
+
gcTime: 600
|
|
1726
|
+
}
|
|
1727
|
+
};
|
|
1728
|
+
}
|
|
1729
|
+
/**
|
|
1730
|
+
* Expand the `customRoutesOnly: true` shorthand into its three primitive
|
|
1731
|
+
* flags. The shorthand exists because hosts wiring a "service resource"
|
|
1732
|
+
* (custom routes only, no adapter, no auto-CRUD) had to remember to set
|
|
1733
|
+
* `disableDefaultRoutes` + `skipValidation` + `skipRegistry` in lockstep —
|
|
1734
|
+
* forgetting any one of them produced confusing errors ("controller
|
|
1735
|
+
* required when CRUD routes are enabled" for a resource that ships none).
|
|
1736
|
+
*
|
|
1737
|
+
* Explicit narrow settings always win — the shorthand only fills in
|
|
1738
|
+
* flags that were not set so power users can opt back into a specific
|
|
1739
|
+
* primitive (`customRoutesOnly: true, skipRegistry: false` keeps OpenAPI
|
|
1740
|
+
* docs while still skipping CRUD + validation).
|
|
1741
|
+
*/
|
|
1742
|
+
function expandCustomRoutesOnly(config) {
|
|
1743
|
+
if (config.customRoutesOnly !== true) return config;
|
|
1744
|
+
return {
|
|
1745
|
+
...config,
|
|
1746
|
+
customRoutesOnly: void 0,
|
|
1747
|
+
disableDefaultRoutes: config.disableDefaultRoutes ?? true,
|
|
1748
|
+
skipValidation: config.skipValidation ?? true,
|
|
1749
|
+
skipRegistry: config.skipRegistry ?? true
|
|
1750
|
+
};
|
|
1751
|
+
}
|
|
1609
1752
|
function resolveCrudAllowList(config) {
|
|
1610
1753
|
const { crud, disabledRoutes: legacyDisabled, disableDefaultRoutes: legacyDisableAll } = config;
|
|
1611
1754
|
if (crud === void 0) return config;
|
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
import { t as __exportAll } from "./chunk-BpYLSNr0.mjs";
|
|
2
2
|
import "./constants-TrJVIJl0.mjs";
|
|
3
3
|
import { f as createError } from "./errors-j4aJm1Wg.mjs";
|
|
4
|
-
import { a as buildAuthMiddlewareForPermissions,
|
|
5
|
-
import { n as schemaIRToJsonSchemaBranch, t as normalizeSchemaIR } from "./schemaIR-
|
|
4
|
+
import { S as sendControllerResponse, a as buildAuthMiddlewareForPermissions, f as resolvePipelineSteps, g as tryRegisterRoute, h as selectPluginMw, l as buildPreHandlerChain, m as resolveRouterPluginMw, n as buildActionPipelineHandler, r as buildArcDecorator, t as buildActionPermissionMw, u as buildRateLimitConfig } from "./routerShared-nCyJGOKX.mjs";
|
|
5
|
+
import { n as schemaIRToJsonSchemaBranch, t as normalizeSchemaIR } from "./schemaIR-DAOvBWgT.mjs";
|
|
6
6
|
//#region src/core/createActionRouter.ts
|
|
7
7
|
var createActionRouter_exports = /* @__PURE__ */ __exportAll({
|
|
8
8
|
buildActionBodySchema: () => buildActionBodySchema,
|
|
@@ -114,7 +114,7 @@ function createActionRouter(fastify, config) {
|
|
|
114
114
|
pluginMw: selectPluginMw("POST", pluginMw),
|
|
115
115
|
routeGuards
|
|
116
116
|
})];
|
|
117
|
-
fastify
|
|
117
|
+
tryRegisterRoute(fastify, {
|
|
118
118
|
method: "POST",
|
|
119
119
|
url: urlPath,
|
|
120
120
|
schema: routeSchema,
|
|
@@ -152,6 +152,9 @@ function createActionRouter(fastify, config) {
|
|
|
152
152
|
throw createError(statusCode, err.message || `Failed to execute '${action}' action`, { code: errorCode });
|
|
153
153
|
}
|
|
154
154
|
}
|
|
155
|
+
}, {
|
|
156
|
+
resourceName,
|
|
157
|
+
op: `action${urlPath}`
|
|
155
158
|
});
|
|
156
159
|
fastify.log.debug({
|
|
157
160
|
actions: actionSubset,
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { f as createError, l as UnauthorizedError, r as ForbiddenError } from "./errors-j4aJm1Wg.mjs";
|
|
2
|
-
import {
|
|
3
|
-
import { r as validateAggregations } from "./validate-
|
|
4
|
-
import { t as buildAggregationHandler } from "./buildHandler-
|
|
2
|
+
import { h as selectPluginMw, i as buildAuthMiddleware, l as buildPreHandlerChain, m as resolveRouterPluginMw, r as buildArcDecorator, u as buildRateLimitConfig } from "./routerShared-nCyJGOKX.mjs";
|
|
3
|
+
import { r as validateAggregations } from "./validate-DFIP2VrM.mjs";
|
|
4
|
+
import { t as buildAggregationHandler } from "./buildHandler-C_SkBNF3.mjs";
|
|
5
5
|
//#region src/core/aggregation/createAggregationRouter.ts
|
|
6
6
|
/**
|
|
7
7
|
* Register one Fastify route per aggregation. No-op when the map is
|
|
@@ -202,7 +202,7 @@ async function registerArcCore(fastify, config, trackPlugin) {
|
|
|
202
202
|
await fastify.register(arcCorePlugin, { emitEvents: config.arcPlugins?.emitEvents !== false });
|
|
203
203
|
trackPlugin("arc-core");
|
|
204
204
|
if (config.arcPlugins?.events !== false) {
|
|
205
|
-
const { default: eventPlugin } = await import("./eventPlugin-
|
|
205
|
+
const { default: eventPlugin } = await import("./eventPlugin-CnJCUyM0.mjs").then((n) => n.n);
|
|
206
206
|
const eventOpts = typeof config.arcPlugins?.events === "object" ? config.arcPlugins.events : {};
|
|
207
207
|
await fastify.register(eventPlugin, {
|
|
208
208
|
...eventOpts,
|
|
@@ -239,7 +239,7 @@ async function registerArcPlugins(fastify, config, trackPlugin, modules) {
|
|
|
239
239
|
trackPlugin("arc-graceful-shutdown");
|
|
240
240
|
}
|
|
241
241
|
if (config.arcPlugins?.caching) {
|
|
242
|
-
const { default: cachingPlugin } = await import("./caching-
|
|
242
|
+
const { default: cachingPlugin } = await import("./caching-8MfrrUYU.mjs").then((n) => n.r);
|
|
243
243
|
const opts = config.arcPlugins.caching === true ? {} : config.arcPlugins.caching;
|
|
244
244
|
await fastify.register(cachingPlugin, opts);
|
|
245
245
|
trackPlugin("arc-caching", opts);
|
|
@@ -256,19 +256,19 @@ async function registerArcPlugins(fastify, config, trackPlugin, modules) {
|
|
|
256
256
|
}
|
|
257
257
|
if (config.arcPlugins?.sse) if (config.arcPlugins?.events === false) fastify.log.warn("SSE plugin requires events plugin (arcPlugins.events). SSE disabled.");
|
|
258
258
|
else {
|
|
259
|
-
const { default: ssePlugin } = await import("./sse-
|
|
259
|
+
const { default: ssePlugin } = await import("./sse-jh5OqFjR.mjs").then((n) => n.r);
|
|
260
260
|
const opts = config.arcPlugins.sse === true ? {} : config.arcPlugins.sse;
|
|
261
261
|
await fastify.register(ssePlugin, opts);
|
|
262
262
|
trackPlugin("arc-sse", opts);
|
|
263
263
|
}
|
|
264
264
|
if (config.arcPlugins?.metrics) {
|
|
265
|
-
const { default: metricsPlugin } = await import("./metrics-
|
|
265
|
+
const { default: metricsPlugin } = await import("./metrics-B-PU4-Yu.mjs").then((n) => n.r);
|
|
266
266
|
const opts = config.arcPlugins.metrics === true ? {} : config.arcPlugins.metrics;
|
|
267
267
|
await fastify.register(metricsPlugin, opts);
|
|
268
268
|
trackPlugin("arc-metrics", opts);
|
|
269
269
|
}
|
|
270
270
|
if (config.arcPlugins?.versioning) {
|
|
271
|
-
const { default: versioningPlugin } = await import("./versioning-
|
|
271
|
+
const { default: versioningPlugin } = await import("./versioning-CuIwUQZ2.mjs").then((n) => n.r);
|
|
272
272
|
await fastify.register(versioningPlugin, config.arcPlugins.versioning);
|
|
273
273
|
trackPlugin("arc-versioning", config.arcPlugins.versioning);
|
|
274
274
|
}
|
|
@@ -337,7 +337,7 @@ async function registerAuth(fastify, config, trackPlugin) {
|
|
|
337
337
|
*/
|
|
338
338
|
async function registerElevation(fastify, config, trackPlugin) {
|
|
339
339
|
if (!config.elevation) return;
|
|
340
|
-
const { elevationPlugin } = await import("./elevation-
|
|
340
|
+
const { elevationPlugin } = await import("./elevation-6mfTLBC-.mjs").then((n) => n.r);
|
|
341
341
|
await fastify.register(elevationPlugin, config.elevation);
|
|
342
342
|
trackPlugin("arc-elevation", config.elevation);
|
|
343
343
|
fastify.log.debug("Elevation plugin enabled");
|
|
@@ -347,7 +347,7 @@ async function registerElevation(fastify, config, trackPlugin) {
|
|
|
347
347
|
*/
|
|
348
348
|
async function registerErrorHandler(fastify, config, trackPlugin) {
|
|
349
349
|
if (config.errorHandler === false) return;
|
|
350
|
-
const { errorHandlerPlugin } = await import("./errorHandler-
|
|
350
|
+
const { errorHandlerPlugin } = await import("./errorHandler-wQejMBT3.mjs").then((n) => n.r);
|
|
351
351
|
const errorOpts = typeof config.errorHandler === "object" ? config.errorHandler : { includeStack: config.preset !== "production" };
|
|
352
352
|
await fastify.register(errorHandlerPlugin, errorOpts);
|
|
353
353
|
trackPlugin("arc-error-handler", errorOpts);
|
|
@@ -448,7 +448,7 @@ async function registerResources(fastify, config) {
|
|
|
448
448
|
let discoveryPath;
|
|
449
449
|
let discoveryYieldedZero = false;
|
|
450
450
|
if (resolvedResources === void 0 && config.resourceDir) {
|
|
451
|
-
const { loadResources } = await import("./loadResources-
|
|
451
|
+
const { loadResources } = await import("./loadResources-fol6JuvW.mjs").then((n) => n.n);
|
|
452
452
|
const { resolve, dirname } = await import("node:path");
|
|
453
453
|
const { fileURLToPath } = await import("node:url");
|
|
454
454
|
const rawDir = config.resourceDir;
|
|
@@ -874,7 +874,7 @@ async function createApp(options) {
|
|
|
874
874
|
await registerErrorHandler(fastify, config, trackPlugin);
|
|
875
875
|
await registerResources(fastify, config);
|
|
876
876
|
if (config.replyHelpers) {
|
|
877
|
-
const { replyHelpersPlugin } = await import("./replyHelpers-
|
|
877
|
+
const { replyHelpersPlugin } = await import("./replyHelpers-boS5Rx26.mjs").then((n) => n.n);
|
|
878
878
|
await fastify.register(replyHelpersPlugin);
|
|
879
879
|
}
|
|
880
880
|
if (config.serializeBigInt) {
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { t as __exportAll } from "./chunk-BpYLSNr0.mjs";
|
|
2
|
-
import { n as createEvent } from "./EventTransport-
|
|
2
|
+
import { n as createEvent } from "./EventTransport-ClEDfiMS.mjs";
|
|
3
3
|
//#region src/events/defineEvent.ts
|
|
4
4
|
/**
|
|
5
5
|
* defineEvent — Typed Event Definitions with Optional Schema Validation
|
package/dist/docs/index.d.mts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import { p as RegistryEntry } from "../index-
|
|
2
|
-
import { t as ExternalOpenApiPaths } from "../externalPaths-
|
|
1
|
+
import { p as RegistryEntry } from "../index-C1JSBan-.mjs";
|
|
2
|
+
import { t as ExternalOpenApiPaths } from "../externalPaths-ClykOQqa.mjs";
|
|
3
3
|
import { FastifyPluginAsync } from "fastify";
|
|
4
4
|
|
|
5
5
|
//#region src/docs/openapi/types.d.ts
|
package/dist/docs/index.mjs
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import { t as getUserRoles } from "../types-
|
|
2
|
-
import { n as openApiPlugin, r as openapi_default, t as buildOpenApiSpec } from "../openapi-
|
|
1
|
+
import { t as getUserRoles } from "../types-DV9WDfeg.mjs";
|
|
2
|
+
import { n as openApiPlugin, r as openapi_default, t as buildOpenApiSpec } from "../openapi-DKDs9NfD.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
2
|
import { arcLog } from "./logger/index.mjs";
|
|
3
|
-
import { t as getUserRoles } from "./types-
|
|
3
|
+
import { t as getUserRoles } from "./types-DV9WDfeg.mjs";
|
|
4
4
|
import fp from "fastify-plugin";
|
|
5
5
|
//#region src/scope/elevation.ts
|
|
6
6
|
var elevation_exports = /* @__PURE__ */ __exportAll({
|
|
@@ -39,13 +39,48 @@ function fastifyValidationDetails(error) {
|
|
|
39
39
|
return (error.validation ?? []).map((v) => {
|
|
40
40
|
const missingProperty = v.params?.missingProperty;
|
|
41
41
|
const path = v.instancePath?.replace(/^\//, "") || (typeof missingProperty === "string" ? missingProperty : void 0);
|
|
42
|
+
const meta = {};
|
|
43
|
+
if (v.keyword === "maximum" || v.keyword === "minimum" || v.keyword === "exclusiveMaximum" || v.keyword === "exclusiveMinimum") {
|
|
44
|
+
const bound = v.params?.limit;
|
|
45
|
+
if (typeof bound === "number") meta.bound = bound;
|
|
46
|
+
}
|
|
47
|
+
if (v.keyword === "enum") {
|
|
48
|
+
const allowed = v.params?.allowedValues;
|
|
49
|
+
if (Array.isArray(allowed)) meta.allowedValues = allowed;
|
|
50
|
+
}
|
|
42
51
|
return {
|
|
43
52
|
...path ? { path } : {},
|
|
44
53
|
code: v.keyword ?? "invalid",
|
|
45
|
-
message: v.message || "Invalid value"
|
|
54
|
+
message: v.message || "Invalid value",
|
|
55
|
+
...Object.keys(meta).length > 0 ? { meta } : {}
|
|
46
56
|
};
|
|
47
57
|
});
|
|
48
58
|
}
|
|
59
|
+
/**
|
|
60
|
+
* Hoist a context-aware message + machine-readable `meta` for the
|
|
61
|
+
* common pagination-cap miss (`?limit=200` against a maxLimit=100
|
|
62
|
+
* resource). Pre-2.17.0 the wire envelope carried "Validation failed"
|
|
63
|
+
* + a detail with the AJV string; hosts had to grep the detail's
|
|
64
|
+
* message to surface the cap. Now the top-level message is
|
|
65
|
+
* "limit must be <= 100 (got 200)" AND `meta.cap` / `meta.received`
|
|
66
|
+
* are stamped on the contract for programmatic callers.
|
|
67
|
+
*/
|
|
68
|
+
function describePaginationCap(error, details) {
|
|
69
|
+
if (!Array.isArray(error.validation) || error.validation.length === 0) return null;
|
|
70
|
+
const v = error.validation.find((entry) => (entry.instancePath === "/limit" || entry.instancePath === "/page") && (entry.keyword === "maximum" || entry.keyword === "minimum"));
|
|
71
|
+
if (!v) return null;
|
|
72
|
+
const field = v.instancePath === "/limit" ? "limit" : "page";
|
|
73
|
+
const bound = v.params?.limit;
|
|
74
|
+
if (typeof bound !== "number") return null;
|
|
75
|
+
const detail = details.find((d) => d.path === field);
|
|
76
|
+
return {
|
|
77
|
+
message: detail?.message ? `Query parameter '${field}' ${detail.message} (cap is ${bound})` : `Query parameter '${field}' violates bound ${bound}`,
|
|
78
|
+
meta: {
|
|
79
|
+
field,
|
|
80
|
+
cap: bound
|
|
81
|
+
}
|
|
82
|
+
};
|
|
83
|
+
}
|
|
49
84
|
/** Map Mongoose `ValidationError.errors` → canonical `ErrorDetail[]`. */
|
|
50
85
|
function mongooseValidationDetails(errors) {
|
|
51
86
|
return Object.entries(errors).map(([field, e]) => ({
|
|
@@ -116,13 +151,19 @@ function classify(error, ctx) {
|
|
|
116
151
|
const fastifyErr = error;
|
|
117
152
|
if (Array.isArray(fastifyErr.validation)) {
|
|
118
153
|
const arcInternalCode = typeof fastifyErr.code === "string" && fastifyErr.code.startsWith("arc.") ? fastifyErr.code : void 0;
|
|
119
|
-
const
|
|
154
|
+
const details = fastifyValidationDetails(fastifyErr);
|
|
155
|
+
const paginationCap = arcInternalCode ? null : describePaginationCap(fastifyErr, details);
|
|
156
|
+
const message = paginationCap?.message ?? (arcInternalCode ? error.message || "Validation failed" : "Validation failed");
|
|
157
|
+
const mergedMeta = {
|
|
158
|
+
...fastifyErr.meta ?? {},
|
|
159
|
+
...paginationCap?.meta ?? {}
|
|
160
|
+
};
|
|
120
161
|
return {
|
|
121
162
|
code: arcInternalCode ?? "arc.validation_error",
|
|
122
163
|
message,
|
|
123
164
|
status: typeof fastifyErr.statusCode === "number" ? fastifyErr.statusCode : 400,
|
|
124
|
-
details
|
|
125
|
-
...
|
|
165
|
+
details,
|
|
166
|
+
...Object.keys(mergedMeta).length > 0 ? { meta: mergedMeta } : {}
|
|
126
167
|
};
|
|
127
168
|
}
|
|
128
169
|
if (typeof fastifyErr.statusCode === "number") return {
|
|
@@ -2,7 +2,7 @@ import { t as __exportAll } from "./chunk-BpYLSNr0.mjs";
|
|
|
2
2
|
import { arcLog } from "./logger/index.mjs";
|
|
3
3
|
import { d as createDomainError } from "./errors-j4aJm1Wg.mjs";
|
|
4
4
|
import { t as requestContext } from "./requestContext-SSaaTgW8.mjs";
|
|
5
|
-
import { n as createEvent, t as MemoryEventTransport } from "./EventTransport-
|
|
5
|
+
import { n as createEvent, t as MemoryEventTransport } from "./EventTransport-ClEDfiMS.mjs";
|
|
6
6
|
import fp from "fastify-plugin";
|
|
7
7
|
//#region src/events/retry.ts
|
|
8
8
|
/**
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { a as EventTransport, i as EventLogger, n as DomainEvent, r as EventHandler } from "./EventTransport-
|
|
1
|
+
import { a as EventTransport, i as EventLogger, n as DomainEvent, r as EventHandler } from "./EventTransport-B1GFiYUy.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,7 +1,7 @@
|
|
|
1
|
-
import { a as EventTransport, i as EventLogger, n as DomainEvent, o as MemoryEventTransport, r as EventHandler, s as MemoryEventTransportOptions, t as DeadLetteredEvent } from "../EventTransport-
|
|
2
|
-
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 { a as EventTransport, i as EventLogger, n as DomainEvent, o as MemoryEventTransport, r as EventHandler, s as MemoryEventTransportOptions, t as DeadLetteredEvent } from "../EventTransport-B1GFiYUy.mjs";
|
|
2
|
+
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-DjEx_J3j.mjs";
|
|
3
3
|
import { RedisEventTransportOptions, RedisLike } from "./transports/redis.mjs";
|
|
4
|
-
import { r as RedisStreamTransportOptions, t as RedisStreamLike } from "../redis-stream-
|
|
4
|
+
import { r as RedisStreamTransportOptions, t as RedisStreamLike } from "../redis-stream-BuiAS3aE.mjs";
|
|
5
5
|
import { RepositoryLike } from "@classytic/repo-core/adapter";
|
|
6
6
|
|
|
7
7
|
//#region src/events/eventTypes.d.ts
|
package/dist/events/index.mjs
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
|
-
import { t as MemoryEventTransport } from "../EventTransport-
|
|
2
|
-
import { n as defineEvent, t as createEventRegistry } from "../defineEvent-
|
|
3
|
-
import { i as withRetry, r as createDeadLetterPublisher, t as eventPlugin } from "../eventPlugin-
|
|
4
|
-
import { n as createSafeGetOne, t as createIsDuplicateKeyError } from "../store-helpers-
|
|
1
|
+
import { t as MemoryEventTransport } from "../EventTransport-ClEDfiMS.mjs";
|
|
2
|
+
import { n as defineEvent, t as createEventRegistry } from "../defineEvent-NPKUJRt8.mjs";
|
|
3
|
+
import { i as withRetry, r as createDeadLetterPublisher, t as eventPlugin } from "../eventPlugin-CnJCUyM0.mjs";
|
|
4
|
+
import { n as createSafeGetOne, t as createIsDuplicateKeyError } from "../store-helpers-5JgaxieH.mjs";
|
|
5
5
|
import { and, anyOf, eq, lte, ne, or } from "@classytic/repo-core/filter";
|
|
6
6
|
import { update } from "@classytic/repo-core/update";
|
|
7
7
|
//#region src/events/eventTypes.ts
|
|
@@ -716,7 +716,7 @@ function wrapWithSchema(definition, handler, options = {}) {
|
|
|
716
716
|
if (validate && definition.schema) result = validate(definition.schema, event.payload);
|
|
717
717
|
else if (registry) result = registry.validate(definition.name, event.payload, eventVersion);
|
|
718
718
|
else if (definition.schema) {
|
|
719
|
-
const { createEventRegistry } = await import("../defineEvent-
|
|
719
|
+
const { createEventRegistry } = await import("../defineEvent-NPKUJRt8.mjs").then((n) => n.r);
|
|
720
720
|
const adhoc = createEventRegistry();
|
|
721
721
|
adhoc.register(definition);
|
|
722
722
|
result = adhoc.validate(definition.name, event.payload);
|
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
import { n as RedisStreamTransport, r as RedisStreamTransportOptions, t as RedisStreamLike } from "../../redis-stream-
|
|
1
|
+
import { n as RedisStreamTransport, r as RedisStreamTransportOptions, t as RedisStreamLike } from "../../redis-stream-BuiAS3aE.mjs";
|
|
2
2
|
export { type RedisStreamLike, RedisStreamTransport, type RedisStreamTransportOptions };
|