@classytic/arc 2.18.5 → 2.19.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (44) hide show
  1. package/dist/{actionPermissions-h7DYDNMg.mjs → actionPermissions-D7NGwcAR.mjs} +1 -0
  2. package/dist/auth/index.d.mts +1 -1
  3. package/dist/auth/index.mjs +10 -1
  4. package/dist/cli/commands/describe.d.mts +1 -1
  5. package/dist/cli/commands/docs.mjs +1 -1
  6. package/dist/core/index.d.mts +2 -2
  7. package/dist/core/index.mjs +2 -2
  8. package/dist/{core-ZnQBa5rh.mjs → core-Ba-6Sue6.mjs} +16 -11
  9. package/dist/{createActionRouter-CagItpxI.mjs → createActionRouter-B_WzBFyf.mjs} +6 -5
  10. package/dist/{createAggregationRouter-DVKroXhC.mjs → createAggregationRouter-B_G_nIjm.mjs} +1 -1
  11. package/dist/docs/index.d.mts +1 -1
  12. package/dist/docs/index.mjs +1 -1
  13. package/dist/encryption/index.d.mts +222 -0
  14. package/dist/encryption/index.mjs +270 -0
  15. package/dist/factory/index.d.mts +1 -1
  16. package/dist/{fastify-augmentation-Cxb0ROIw.d.mts → fastify-augmentation-D-5_yQJi.d.mts} +1 -1
  17. package/dist/hooks/index.d.mts +1 -1
  18. package/dist/{index-Dh0_RSTd.d.mts → index-CP6A1Ffj.d.mts} +7 -7
  19. package/dist/{index-CHNQIhcb.d.mts → index-CUTG2Cuo.d.mts} +83 -1
  20. package/dist/index.d.mts +2 -2
  21. package/dist/index.mjs +3 -3
  22. package/dist/integrations/index.d.mts +1 -1
  23. package/dist/integrations/mcp/index.d.mts +2 -2
  24. package/dist/integrations/mcp/index.mjs +1 -1
  25. package/dist/integrations/mcp/testing.d.mts +1 -1
  26. package/dist/integrations/mcp/testing.mjs +1 -1
  27. package/dist/{mcpPlugin-D1wuRD2c.mjs → mcpPlugin-C088Nd-G.mjs} +1 -1
  28. package/dist/middleware/index.d.mts +1 -1
  29. package/dist/{openapi-xVJibapr.mjs → openapi-DZ-5a5Op.mjs} +2 -2
  30. package/dist/pipeline/index.d.mts +1 -1
  31. package/dist/plugins/index.d.mts +2 -2
  32. package/dist/plugins/tracing-entry.mjs +1 -1
  33. package/dist/presets/filesUpload.d.mts +1 -1
  34. package/dist/presets/index.d.mts +1 -1
  35. package/dist/presets/multiTenant.d.mts +1 -1
  36. package/dist/presets/search.d.mts +1 -1
  37. package/dist/{registerRoute-CPmdkZGu.mjs → registerRoute-BdV8DDwU.mjs} +19 -1
  38. package/dist/registry/index.d.mts +1 -1
  39. package/dist/testing/index.d.mts +2 -2
  40. package/dist/types/index.d.mts +2 -2
  41. package/dist/{types-CmkknLzI.d.mts → types-9_wtNPsI.d.mts} +1 -1
  42. package/dist/{types-D60PFwfG.d.mts → types-DpvSF5Hh.d.mts} +1 -1
  43. package/dist/utils/index.d.mts +1 -1
  44. package/package.json +10 -1
@@ -247,6 +247,7 @@ function sendControllerResponse(reply, response, request) {
247
247
  function createFastifyHandler(controllerMethod) {
248
248
  return async (req, reply) => {
249
249
  sendControllerResponse(reply, await controllerMethod(createRequestContext(req)), req);
250
+ return reply;
250
251
  };
251
252
  }
252
253
  /**
@@ -1,4 +1,4 @@
1
- import { Bt as AuthHelpers, Vt as AuthPluginOptions } from "../index-CHNQIhcb.mjs";
1
+ import { Ht as AuthPluginOptions, Vt as AuthHelpers } from "../index-CUTG2Cuo.mjs";
2
2
  import { c as PermissionCheck } from "../fields-C7ahdGYf.mjs";
3
3
  import { t as ExternalOpenApiPaths } from "../externalPaths-BE6wFpYv.mjs";
4
4
  import { a as SessionManagerOptions, c as createSessionManager, i as SessionData, n as MemorySessionStoreOptions, o as SessionManagerResult, r as SessionCookieOptions, s as SessionStore, t as MemorySessionStore } from "../sessionManager-BXG-HGwG.mjs";
@@ -317,11 +317,20 @@ function toFetchRequest(request) {
317
317
  else if (contentType.includes("application/json") || contentType.includes("text/") || !contentType) body = JSON.stringify(request.body);
318
318
  else request.log?.warn?.("toFetchRequest: cannot reconstruct %s body without rawBody plugin", contentType);
319
319
  }
320
- return new Request(url, {
320
+ const init = {
321
321
  method: request.method,
322
322
  headers,
323
323
  body
324
+ };
325
+ const fetchRequest = new Request(url, init);
326
+ Object.defineProperty(fetchRequest, "clone", {
327
+ configurable: true,
328
+ writable: true,
329
+ value() {
330
+ return new Request(url, init);
331
+ }
324
332
  });
333
+ return fetchRequest;
325
334
  }
326
335
  /**
327
336
  * Pipe a Fetch API Response back into Fastify's reply.
@@ -1,4 +1,4 @@
1
- import { V as ResourceDefinition, it as RateLimitConfig, mt as RouteSchemaOptions } from "../../index-CHNQIhcb.mjs";
1
+ import { V as ResourceDefinition, ht as RouteSchemaOptions, it as RateLimitConfig } from "../../index-CUTG2Cuo.mjs";
2
2
 
3
3
  //#region src/cli/commands/describe.d.ts
4
4
  interface DescribedResource {
@@ -1,4 +1,4 @@
1
- import { t as buildOpenApiSpec } from "../../openapi-xVJibapr.mjs";
1
+ import { t as buildOpenApiSpec } from "../../openapi-DZ-5a5Op.mjs";
2
2
  import { t as loadResourcesFromEntry } from "../../loadResourcesFromEntry-BTlFEx05.mjs";
3
3
  import { dirname, resolve } from "node:path";
4
4
  import { mkdirSync, writeFileSync } from "node:fs";
@@ -1,3 +1,3 @@
1
- import { $t as BaseController, At as AggregationRateLimit, B as defineResource, Ct as AggMeasureShorthand, Dt as AggregationIndexHint, Et as AggregationDateRangeRequirement, Fn as ControllerConfigurableOptions, In as ControllerConstructionOptions, Ln as ListResult, Ot as AggregationMaterializedContext, Pn as BaseControllerOptions, Rn as BodySanitizer, St as AggMeasureInput, Tt as AggregationConfig, V as ResourceDefinition, an as SlugMixin, cn as BaseCrudController, dn as AccessControl, en as SoftDeleteExt, fn as AccessControlConfig, in as SlugExt, jt as AggregationsMap, kt as AggregationMaterializedResult, ln as QueryResolver, nn as TreeExt, on as BulkExt, rn as TreeMixin, sn as BulkMixin, tn as SoftDeleteMixin, un as QueryResolverConfig, wt as AggregationCacheConfig, zn as BodySanitizerConfig } from "../index-CHNQIhcb.mjs";
2
- import { A as MAX_FILTER_DEPTH, C as DEFAULT_SORT, D as HOOK_PHASES, E as HOOK_OPERATIONS, F as RESERVED_QUERY_PARAMS, I as SYSTEM_FIELDS, M as MAX_SEARCH_LENGTH, N as MUTATION_OPERATIONS, O as HookOperation, P as MutationOperation, S as DEFAULT_MAX_LIMIT, T as DEFAULT_UPDATE_METHOD, _ as defineAggregation, a as createRequestContext, b as DEFAULT_ID_FIELD, c as sendControllerResponse, d as getEntityQuery, f as defineResourceVariants, g as createPermissionMiddleware, h as createCrudRouter, i as createFastifyHandler, j as MAX_REGEX_LENGTH, k as HookPhase, l as getEntityId, m as defineAction, n as isFieldReadable, o as getControllerContext, p as DefineActionConfig, r as createCrudHandlers, s as getControllerScope, t as collectReadBlockedFields, u as getEntityIdField, v as CRUD_OPERATIONS, w as DEFAULT_TENANT_FIELD, x as DEFAULT_LIMIT, y as CrudOperation } from "../index-Dh0_RSTd.mjs";
1
+ import { At as AggregationMaterializedResult, B as defineResource, Bn as BodySanitizerConfig, Ct as AggMeasureInput, Dt as AggregationDateRangeRequirement, Et as AggregationConfig, Fn as BaseControllerOptions, In as ControllerConfigurableOptions, Ln as ControllerConstructionOptions, Mt as AggregationsMap, Ot as AggregationIndexHint, Rn as ListResult, Tt as AggregationCacheConfig, V as ResourceDefinition, an as SlugExt, cn as BulkMixin, dn as QueryResolverConfig, en as BaseController, fn as AccessControl, in as TreeMixin, jt as AggregationRateLimit, kt as AggregationMaterializedContext, ln as BaseCrudController, nn as SoftDeleteMixin, on as SlugMixin, pn as AccessControlConfig, rn as TreeExt, sn as BulkExt, tn as SoftDeleteExt, un as QueryResolver, wt as AggMeasureShorthand, zn as BodySanitizer } from "../index-CUTG2Cuo.mjs";
2
+ import { A as MAX_FILTER_DEPTH, C as DEFAULT_SORT, D as HOOK_PHASES, E as HOOK_OPERATIONS, F as RESERVED_QUERY_PARAMS, I as SYSTEM_FIELDS, M as MAX_SEARCH_LENGTH, N as MUTATION_OPERATIONS, O as HookOperation, P as MutationOperation, S as DEFAULT_MAX_LIMIT, T as DEFAULT_UPDATE_METHOD, _ as defineAggregation, a as createRequestContext, b as DEFAULT_ID_FIELD, c as sendControllerResponse, d as getEntityQuery, f as defineResourceVariants, g as createPermissionMiddleware, h as createCrudRouter, i as createFastifyHandler, j as MAX_REGEX_LENGTH, k as HookPhase, l as getEntityId, m as defineAction, n as isFieldReadable, o as getControllerContext, p as DefineActionConfig, r as createCrudHandlers, s as getControllerScope, t as collectReadBlockedFields, u as getEntityIdField, v as CRUD_OPERATIONS, w as DEFAULT_TENANT_FIELD, x as DEFAULT_LIMIT, y as CrudOperation } from "../index-CP6A1Ffj.mjs";
3
3
  export { AccessControl, type AccessControlConfig, type AggMeasureInput, type AggMeasureShorthand, type AggregationCacheConfig, type AggregationConfig, type AggregationDateRangeRequirement, type AggregationIndexHint, type AggregationMaterializedContext, type AggregationMaterializedResult, type AggregationRateLimit, type AggregationsMap, BaseController, type BaseControllerOptions, BaseCrudController, BodySanitizer, type BodySanitizerConfig, type BulkExt, BulkMixin, CRUD_OPERATIONS, type ControllerConfigurableOptions, type ControllerConstructionOptions, CrudOperation, DEFAULT_ID_FIELD, DEFAULT_LIMIT, DEFAULT_MAX_LIMIT, DEFAULT_SORT, DEFAULT_TENANT_FIELD, DEFAULT_UPDATE_METHOD, type DefineActionConfig, HOOK_OPERATIONS, HOOK_PHASES, HookOperation, HookPhase, type ListResult, MAX_FILTER_DEPTH, MAX_REGEX_LENGTH, MAX_SEARCH_LENGTH, MUTATION_OPERATIONS, MutationOperation, QueryResolver, type QueryResolverConfig, RESERVED_QUERY_PARAMS, ResourceDefinition, SYSTEM_FIELDS, type SlugExt, SlugMixin, type SoftDeleteExt, SoftDeleteMixin, type TreeExt, TreeMixin, collectReadBlockedFields, createCrudHandlers, createCrudRouter, createFastifyHandler, createPermissionMiddleware, createRequestContext, defineAction, defineAggregation, defineResource, defineResourceVariants, getControllerContext, getControllerScope, getEntityId, getEntityIdField, getEntityQuery, isFieldReadable, sendControllerResponse };
@@ -1,5 +1,5 @@
1
1
  import { a as DEFAULT_SORT, c as HOOK_OPERATIONS, d as MAX_REGEX_LENGTH, f as MAX_SEARCH_LENGTH, h as SYSTEM_FIELDS, i as DEFAULT_MAX_LIMIT, l as HOOK_PHASES, m as RESERVED_QUERY_PARAMS, n as DEFAULT_ID_FIELD, o as DEFAULT_TENANT_FIELD, p as MUTATION_OPERATIONS, r as DEFAULT_LIMIT, s as DEFAULT_UPDATE_METHOD, t as CRUD_OPERATIONS, u as MAX_FILTER_DEPTH } from "../constants-gaEeMlz-.mjs";
2
2
  import { a as BulkMixin, c as collectReadBlockedFields, f as AccessControl, i as SlugMixin, l as isFieldReadable, n as TreeMixin, o as BaseCrudController, r as SoftDeleteMixin, s as QueryResolver, t as BaseController, u as BodySanitizer } from "../BaseController-Dy6EQHjM.mjs";
3
- import { a as defineResource, c as createCrudRouter, i as defineResourceVariants, l as createPermissionMiddleware, n as getEntityIdField, o as ResourceDefinition, r as getEntityQuery, s as defineAction, t as getEntityId, u as defineAggregation } from "../core-ZnQBa5rh.mjs";
4
- import { a as createCrudHandlers, c as getControllerContext, l as getControllerScope, o as createFastifyHandler, s as createRequestContext, u as sendControllerResponse } from "../actionPermissions-h7DYDNMg.mjs";
3
+ import { a as defineResource, c as createCrudRouter, i as defineResourceVariants, l as createPermissionMiddleware, n as getEntityIdField, o as ResourceDefinition, r as getEntityQuery, s as defineAction, t as getEntityId, u as defineAggregation } from "../core-Ba-6Sue6.mjs";
4
+ import { a as createCrudHandlers, c as getControllerContext, l as getControllerScope, o as createFastifyHandler, s as createRequestContext, u as sendControllerResponse } from "../actionPermissions-D7NGwcAR.mjs";
5
5
  export { AccessControl, BaseController, BaseCrudController, BodySanitizer, BulkMixin, CRUD_OPERATIONS, DEFAULT_ID_FIELD, DEFAULT_LIMIT, DEFAULT_MAX_LIMIT, DEFAULT_SORT, DEFAULT_TENANT_FIELD, DEFAULT_UPDATE_METHOD, HOOK_OPERATIONS, HOOK_PHASES, MAX_FILTER_DEPTH, MAX_REGEX_LENGTH, MAX_SEARCH_LENGTH, MUTATION_OPERATIONS, QueryResolver, RESERVED_QUERY_PARAMS, ResourceDefinition, SYSTEM_FIELDS, SlugMixin, SoftDeleteMixin, TreeMixin, collectReadBlockedFields, createCrudHandlers, createCrudRouter, createFastifyHandler, createPermissionMiddleware, createRequestContext, defineAction, defineAggregation, defineResource, defineResourceVariants, getControllerContext, getControllerScope, getEntityId, getEntityIdField, getEntityQuery, isFieldReadable, sendControllerResponse };
@@ -8,8 +8,8 @@ import { F as applyFieldWritePermissions, L as resolveEffectiveRoles } from "./p
8
8
  import { t as getUserRoles } from "./types-XTApa8b6.mjs";
9
9
  import { n as convertRouteSchema, t as convertOpenApiSchemas } from "./schemaConverter-U0MnO9vd.mjs";
10
10
  import { i as pipeUIMessageStreamToReply, r as isReadableStream } from "./streaming-BQcW6_Kq.mjs";
11
- import { a as createCrudHandlers, d as buildRequestScopeProjection, i as resolvePipelineSteps, o as createFastifyHandler, r as buildPipelineHandler, s as createRequestContext, t as resolveActionPermission } from "./actionPermissions-h7DYDNMg.mjs";
12
- import { c as resolveRoutePreHandlers, d as buildArcDecorator, i as selectPluginMw, l as buildAuthMiddleware, n as buildRateLimitConfig, o as buildCrudPermissionMw, r as resolveRouterPluginMw, s as buildPreHandlerChain, t as tryRegisterRoute } from "./registerRoute-CPmdkZGu.mjs";
11
+ import { a as createCrudHandlers, d as buildRequestScopeProjection, i as resolvePipelineSteps, o as createFastifyHandler, r as buildPipelineHandler, s as createRequestContext, t as resolveActionPermission } from "./actionPermissions-D7NGwcAR.mjs";
12
+ import { a as selectPluginMw, c as buildPreHandlerChain, f as buildArcDecorator, i as resolveRouterPluginMw, l as resolveRoutePreHandlers, n as buildRateLimitConfig, r as buildRouteConfig, s as buildCrudPermissionMw, t as tryRegisterRoute, u as buildAuthMiddleware } from "./registerRoute-BdV8DDwU.mjs";
13
13
  import { t as applyPresets } from "./presets-BtyFwTO4.mjs";
14
14
  import { t as hasEvents } from "./typeGuards-C9KXUly6.mjs";
15
15
  import { t as pluralize } from "./pluralize-DccFV9R7.mjs";
@@ -104,7 +104,7 @@ function methodCarriesBody(method) {
104
104
  * `wrapHandler` is derived inline from `!route.raw`.
105
105
  */
106
106
  function createCustomRoutes(fastify, routes, controller, options) {
107
- const { tag, resourceName, arcDecorator, rateLimitConfig, pluginMw, pipeline, routeGuards, tenantScopeMw, fieldPermissions, onFieldWriteDenied } = options;
107
+ const { tag, resourceName, arcDecorator, rateLimitConfig, pluginMw, pipeline, routeGuards, tenantScopeMw, fieldPermissions, onFieldWriteDenied, extensions } = options;
108
108
  for (const route of routes) {
109
109
  const routeWithRefs = route;
110
110
  const hasHandler = route.handler !== void 0;
@@ -177,7 +177,7 @@ function createCustomRoutes(fastify, routes, controller, options) {
177
177
  }
178
178
  return result;
179
179
  } : handler,
180
- ...rateLimitConfig ? { config: rateLimitConfig } : {}
180
+ ...buildRouteConfig(rateLimitConfig, extensions)
181
181
  }, {
182
182
  resourceName,
183
183
  op: opName
@@ -194,7 +194,7 @@ function createCustomRoutes(fastify, routes, controller, options) {
194
194
  * @param options - Router configuration
195
195
  */
196
196
  function createCrudRouter(fastify, controller, options = {}) {
197
- 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;
197
+ 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, extensions } = options;
198
198
  const rateLimitConfig = buildRateLimitConfig(rateLimit);
199
199
  const resourceHasQueryCache = fastify.hasDecorator("queryCache") && controller && typeof controller._cacheConfig !== "undefined" && controller._cacheConfig !== void 0;
200
200
  const pluginMw = resolveRouterPluginMw(fastify, Boolean(resourceHasQueryCache));
@@ -295,7 +295,7 @@ function createCrudRouter(fastify, controller, options = {}) {
295
295
  }, defaultSchemas[spec.op], schemas[spec.op]),
296
296
  preHandler: preHandler.length > 0 ? preHandler : void 0,
297
297
  handler: handlers[spec.op],
298
- ...rateLimitConfig ? { config: rateLimitConfig } : {}
298
+ ...buildRouteConfig(rateLimitConfig, extensions)
299
299
  }, {
300
300
  resourceName,
301
301
  op: spec.op
@@ -313,7 +313,8 @@ function createCrudRouter(fastify, controller, options = {}) {
313
313
  routeGuards,
314
314
  tenantScopeMw,
315
315
  fieldPermissions,
316
- onFieldWriteDenied
316
+ onFieldWriteDenied,
317
+ extensions
317
318
  });
318
319
  }
319
320
  function buildCrudHandlers(ctrl, pipeline, resourceName) {
@@ -1242,10 +1243,11 @@ function buildResourcePlugin(resource) {
1242
1243
  pipe: resource.pipe,
1243
1244
  fields: resource.fields,
1244
1245
  onFieldWriteDenied: resource.onFieldWriteDenied,
1245
- idField: resource.idField
1246
+ idField: resource.idField,
1247
+ extensions: resource.extensions
1246
1248
  });
1247
1249
  if (resource.actions && Object.keys(resource.actions).length > 0) {
1248
- const { createActionRouter } = await import("./createActionRouter-CagItpxI.mjs").then((n) => n.n);
1250
+ const { createActionRouter } = await import("./createActionRouter-B_WzBFyf.mjs").then((n) => n.n);
1249
1251
  createActionRouter(typedInstance, {
1250
1252
  ...normalizeActionsToRouterConfig(resource.actions, resource.actionPermissions, resource.tag, resource.permissions, resource.name, typedInstance.log),
1251
1253
  resourceName: resource.name,
@@ -1255,11 +1257,12 @@ function buildResourcePlugin(resource) {
1255
1257
  permissions: resource.permissions,
1256
1258
  routeGuards: resource.routeGuards,
1257
1259
  pipeline: resource.pipe,
1258
- rateLimit: resource.rateLimit
1260
+ rateLimit: resource.rateLimit,
1261
+ extensions: resource.extensions
1259
1262
  });
1260
1263
  }
1261
1264
  if (resource.aggregations && Object.keys(resource.aggregations).length > 0) {
1262
- const [{ createAggregationRouter }, { adapterSupportsAggregate, ArcAggregationConfigError }] = await Promise.all([import("./createAggregationRouter-DVKroXhC.mjs"), import("./validate-CRcaWzp2.mjs").then((n) => n.r)]);
1265
+ const [{ createAggregationRouter }, { adapterSupportsAggregate, ArcAggregationConfigError }] = await Promise.all([import("./createAggregationRouter-B_G_nIjm.mjs"), import("./validate-CRcaWzp2.mjs").then((n) => n.r)]);
1263
1266
  const repoForAgg = resource.controller?.repository ?? resource.repository;
1264
1267
  if (!adapterSupportsAggregate(repoForAgg)) {
1265
1268
  const undispatchable = Object.entries(resource.aggregations).filter(([, cfg]) => !cfg.materialized).map(([name]) => name);
@@ -1332,6 +1335,7 @@ var ResourceDefinition = class {
1332
1335
  */
1333
1336
  onFieldWriteDenied;
1334
1337
  cache;
1338
+ extensions;
1335
1339
  referenceData;
1336
1340
  /**
1337
1341
  * Per-resource MCP opt-out. `false` keeps the resource out of every
@@ -1429,6 +1433,7 @@ var ResourceDefinition = class {
1429
1433
  this.fields = config.fields;
1430
1434
  this.onFieldWriteDenied = config.onFieldWriteDenied;
1431
1435
  this.cache = config.cache;
1436
+ this.extensions = config.extensions ? Object.freeze({ ...config.extensions }) : void 0;
1432
1437
  this.mcp = config.mcp !== false;
1433
1438
  this.referenceData = config.referenceData === true;
1434
1439
  this.tenantField = config.tenantField;
@@ -1,8 +1,8 @@
1
1
  import { t as __exportAll } from "./rolldown-runtime-8H4AJuhK.mjs";
2
2
  import "./constants-gaEeMlz-.mjs";
3
3
  import { f as createError } from "./errors-DkO8Nk6-.mjs";
4
- import { i as resolvePipelineSteps, n as buildActionPipelineHandler, u as sendControllerResponse } from "./actionPermissions-h7DYDNMg.mjs";
5
- import { a as buildActionPermissionMw, d as buildArcDecorator, i as selectPluginMw, n as buildRateLimitConfig, r as resolveRouterPluginMw, s as buildPreHandlerChain, t as tryRegisterRoute, u as buildAuthMiddlewareForPermissions } from "./registerRoute-CPmdkZGu.mjs";
4
+ import { i as resolvePipelineSteps, n as buildActionPipelineHandler, u as sendControllerResponse } from "./actionPermissions-D7NGwcAR.mjs";
5
+ import { a as selectPluginMw, c as buildPreHandlerChain, d as buildAuthMiddlewareForPermissions, f as buildArcDecorator, i as resolveRouterPluginMw, n as buildRateLimitConfig, o as buildActionPermissionMw, r as buildRouteConfig, t as tryRegisterRoute } from "./registerRoute-BdV8DDwU.mjs";
6
6
  import { n as schemaIRToJsonSchemaBranch, t as normalizeSchemaIR } from "./schemaIR-D1MJwqmp.mjs";
7
7
  //#region src/core/createActionRouter.ts
8
8
  var createActionRouter_exports = /* @__PURE__ */ __exportAll({
@@ -18,7 +18,7 @@ var createActionRouter_exports = /* @__PURE__ */ __exportAll({
18
18
  * (keyed by `body.action` at request time).
19
19
  */
20
20
  function createActionRouter(fastify, config) {
21
- const { tag, resourceName = tag ?? "action", actions, actionPermissions = {}, actionSchemas = {}, globalAuth, onError, fields: fieldPermissions, schemaOptions, idField = "_id", permissions: resourcePermissions, routeGuards = [], pipeline, rateLimit, idLessActionNames = [] } = config;
21
+ const { tag, resourceName = tag ?? "action", actions, actionPermissions = {}, actionSchemas = {}, globalAuth, onError, fields: fieldPermissions, schemaOptions, idField = "_id", permissions: resourcePermissions, routeGuards = [], pipeline, rateLimit, idLessActionNames = [], extensions } = config;
22
22
  const allActionNames = Object.keys(actions);
23
23
  if (allActionNames.length === 0) {
24
24
  fastify.log.warn("[createActionRouter] No actions defined, skipping route creation");
@@ -121,7 +121,7 @@ function createActionRouter(fastify, config) {
121
121
  schema: routeSchema,
122
122
  attachValidation: true,
123
123
  preHandler: preHandler.length > 0 ? preHandler : void 0,
124
- ...rateLimitConfig ? { config: rateLimitConfig } : {},
124
+ ...buildRouteConfig(rateLimitConfig, extensions),
125
125
  handler: async (req, reply) => {
126
126
  const { action, ...data } = req.body;
127
127
  const id = hasId ? req.params.id : "";
@@ -135,7 +135,8 @@ function createActionRouter(fastify, config) {
135
135
  const handler = wrappedHandlers.get(action);
136
136
  if (!handler) throw createError(400, `Invalid action '${action}'. Valid actions: ${actionSubset.join(", ")}`, { validActions: actionSubset });
137
137
  try {
138
- return sendControllerResponse(reply, await handler(id, data, req), req);
138
+ sendControllerResponse(reply, await handler(id, data, req), req);
139
+ return reply;
139
140
  } catch (error) {
140
141
  if (onError) {
141
142
  const { statusCode, error: errorMsg, code } = onError(error, action, id);
@@ -1,5 +1,5 @@
1
1
  import { f as createError, l as UnauthorizedError, r as ForbiddenError } from "./errors-DkO8Nk6-.mjs";
2
- import { d as buildArcDecorator, i as selectPluginMw, l as buildAuthMiddleware, n as buildRateLimitConfig, r as resolveRouterPluginMw, s as buildPreHandlerChain } from "./registerRoute-CPmdkZGu.mjs";
2
+ import { a as selectPluginMw, c as buildPreHandlerChain, f as buildArcDecorator, i as resolveRouterPluginMw, n as buildRateLimitConfig, u as buildAuthMiddleware } from "./registerRoute-BdV8DDwU.mjs";
3
3
  import { n as validateAggregations } from "./validate-CRcaWzp2.mjs";
4
4
  import { t as buildAggregationHandler } from "./buildHandler-B0qvAguf.mjs";
5
5
  //#region src/core/aggregation/createAggregationRouter.ts
@@ -1,4 +1,4 @@
1
- import { p as RegistryEntry } from "../index-CHNQIhcb.mjs";
1
+ import { p as RegistryEntry } from "../index-CUTG2Cuo.mjs";
2
2
  import { t as ExternalOpenApiPaths } from "../externalPaths-BE6wFpYv.mjs";
3
3
  import { FastifyPluginAsync } from "fastify";
4
4
 
@@ -1,5 +1,5 @@
1
1
  import { t as getUserRoles } from "../types-XTApa8b6.mjs";
2
- import { n as openApiPlugin, r as openapi_default, t as buildOpenApiSpec } from "../openapi-xVJibapr.mjs";
2
+ import { n as openApiPlugin, r as openapi_default, t as buildOpenApiSpec } from "../openapi-DZ-5a5Op.mjs";
3
3
  import fp from "fastify-plugin";
4
4
  //#region src/docs/scalar.ts
5
5
  const scalarPlugin = async (fastify, opts = {}) => {
@@ -0,0 +1,222 @@
1
+ import { FastifyPluginAsync, FastifyRequest } from "fastify";
2
+
3
+ //#region src/encryption/types.d.ts
4
+ /** JWE key-management algorithm — the protected-header `alg`. */
5
+ type JweAlg = "RSA-OAEP-256" | "RSA-OAEP-384" | "RSA-OAEP-512" | "ECDH-ES" | "ECDH-ES+A256KW" | "A256GCMKW" | "dir";
6
+ /** JWE content-encryption algorithm — the protected-header `enc`. AEAD only. */
7
+ type JweEnc = "A128GCM" | "A192GCM" | "A256GCM";
8
+ /** Encryption strategy for a route's response. */
9
+ type EncryptionMode = "jwe" | "fields";
10
+ /**
11
+ * Opaque key material. For `jwe` mode this is a `jose` `KeyLike` /
12
+ * `CryptoKey` / `Uint8Array`; for `fields` mode a 32-byte secret
13
+ * (`Buffer` / `Uint8Array`). Kept `unknown` at the contract layer so the
14
+ * `KeyProvider` interface carries no hard `jose` type dependency — hosts
15
+ * without `jose` installed can still implement field-mode providers.
16
+ */
17
+ type KeyMaterial = unknown;
18
+ /** A key paired with its identifier — what encrypt operations consume. */
19
+ interface ActiveKey {
20
+ /** Key id, stamped into the JWE `kid` header / field envelope for rotation. */
21
+ readonly kid: string;
22
+ /** The key material (public key for JWE, 32-byte secret for fields). */
23
+ readonly key: KeyMaterial;
24
+ /** Optional per-key `alg` override (else the plugin default applies). */
25
+ readonly alg?: JweAlg;
26
+ }
27
+ /** Context handed to the key provider so resolution can be request-scoped. */
28
+ interface KeyContext {
29
+ readonly request: FastifyRequest;
30
+ }
31
+ type MaybePromise<T> = T | Promise<T>;
32
+ /**
33
+ * Pluggable key resolution. Arc owns the request-time plumbing — `kid` →
34
+ * key, algorithm allowlist enforcement; the host owns the trust store —
35
+ * which keys are valid and where they live (file / env / AWS KMS / GCP KMS
36
+ * / Vault). Encryption and decryption are deliberately separate operations
37
+ * because they often use DIFFERENT keys (JWE: a public key encrypts the
38
+ * outbound response, a private key by `kid` decrypts the inbound request —
39
+ * exactly Visa's two-key-pair model).
40
+ *
41
+ * Rotation mirrors `@fastify/secure-session`: the active key encrypts; any
42
+ * key resolvable by `kid` can decrypt, so you can roll keys without dropping
43
+ * in-flight traffic.
44
+ */
45
+ interface KeyProvider {
46
+ /** Active key for encrypting OUTBOUND responses. */
47
+ encryptionKey(ctx: KeyContext): MaybePromise<ActiveKey>;
48
+ /** Resolve a DECRYPTION key by the inbound message's `kid`. */
49
+ decryptionKey(kid: string, ctx: KeyContext): MaybePromise<KeyMaterial | undefined>;
50
+ }
51
+ /**
52
+ * Per-resource encryption directive — the typed slice plugins register on
53
+ * `ResourceExtensions`. Declared on a resource via
54
+ * `defineResource({ extensions: { encryption: { … } } })`; arc threads it
55
+ * to request time on `request.routeOptions.config.arcExtensions.encryption`.
56
+ */
57
+ interface EncryptionDirective {
58
+ /** Strategy. Defaults to the plugin-level `mode` (default `'jwe'`). */
59
+ readonly mode?: EncryptionMode;
60
+ /**
61
+ * For `mode: 'fields'` — dot-paths of fields to encrypt, resolved against
62
+ * the serialized response body (e.g. `'cardNumber'`, `'account.iban'`).
63
+ * Ignored for `mode: 'jwe'` (the whole body is encrypted).
64
+ */
65
+ readonly fields?: readonly string[];
66
+ /**
67
+ * Explicit opt-out — set `false` to disable encryption for this resource
68
+ * even when a plugin-level default (`routes`) would otherwise apply.
69
+ */
70
+ readonly enabled?: boolean;
71
+ }
72
+ /**
73
+ * Plugin-level options. `keyProvider` is the only required field; everything
74
+ * else has a secure default.
75
+ */
76
+ interface EncryptionOptions {
77
+ /**
78
+ * Master on/off. Default `true`. Set `false` to register the plugin as a
79
+ * typed no-op (config-gated environments) — same pattern as
80
+ * `idempotencyPlugin`.
81
+ */
82
+ readonly enabled?: boolean;
83
+ /** Key provider — resolves `kid` → key for encrypt/decrypt. Required. */
84
+ readonly keyProvider: KeyProvider;
85
+ /** Default mode when a route opts in without naming one. Default `'jwe'`. */
86
+ readonly mode?: EncryptionMode;
87
+ /** JWE key-management algorithm. Default `'RSA-OAEP-256'`. */
88
+ readonly alg?: JweAlg;
89
+ /** JWE content-encryption algorithm. Default `'A256GCM'`. */
90
+ readonly enc?: JweEnc;
91
+ /**
92
+ * Inbound-decryption algorithm allowlists — block algorithm-substitution
93
+ * downgrade attacks. Default `[alg]` / `[enc]`. Set explicitly only when a
94
+ * client legitimately uses more than one algorithm.
95
+ */
96
+ readonly allowedAlgs?: readonly JweAlg[];
97
+ readonly allowedEncs?: readonly JweEnc[];
98
+ /**
99
+ * Fallback opt-in for routes that don't declare
100
+ * `extensions.encryption`. Return a directive (or `true` for the default
101
+ * mode) to encrypt, `false`/`undefined` to skip. The per-resource
102
+ * `extensions.encryption` ALWAYS wins when present. Use this to scope ALE
103
+ * to sensitive prefixes without touching every resource.
104
+ *
105
+ * @example confine ALE to `/payments` and `/accounts`
106
+ * ```ts
107
+ * routes: (req) => /^\/(payments|accounts)\b/.test(req.url)
108
+ * ```
109
+ */
110
+ readonly routes?: (request: FastifyRequest) => EncryptionDirective | boolean | undefined;
111
+ /** Decrypt inbound `application/jose` request bodies. Default `true`. */
112
+ readonly decryptRequests?: boolean;
113
+ /** Media type that signals an encrypted REQUEST body. Default `'application/jose'`. */
114
+ readonly contentType?: string;
115
+ /** Content-Type set on full-body JWE RESPONSES. Default `'application/jose'`. */
116
+ readonly responseContentType?: string;
117
+ /**
118
+ * Response header advertising that the body is encrypted, so generic
119
+ * client SDKs know to decrypt. Default `'x-encrypted'`; set `false` to omit.
120
+ */
121
+ readonly flagHeader?: string | false;
122
+ }
123
+ /** Request decorations added by the plugin. */
124
+ declare module "fastify" {
125
+ interface FastifyRequest {
126
+ /** `true` when the inbound body was decrypted from a JWE by this plugin. */
127
+ requestDecrypted?: boolean;
128
+ /** `true` when the outbound body was encrypted by this plugin. */
129
+ responseEncrypted?: boolean;
130
+ }
131
+ }
132
+ //#endregion
133
+ //#region src/encryption/encryptionPlugin.d.ts
134
+ declare const encryptionPlugin: FastifyPluginAsync<EncryptionOptions>;
135
+ declare const _default: FastifyPluginAsync<EncryptionOptions>;
136
+ //#endregion
137
+ //#region src/encryption/fieldCipher.d.ts
138
+ /**
139
+ * Field-level cipher — `node:crypto` AES-256-GCM, zero external deps.
140
+ *
141
+ * Used by `mode: 'fields'` to encrypt individual field values in place while
142
+ * leaving the JSON structure intact. The envelope is an authenticated,
143
+ * versioned, `kid`-tagged token:
144
+ *
145
+ * arc.v1.<b64url(kid)>.<b64url(iv)>.<b64url(ciphertext)>.<b64url(tag)>
146
+ *
147
+ * This is NOT a hand-rolled cipher — it's the correct use of a vetted AEAD
148
+ * primitive (AES-256-GCM): a fresh random 96-bit IV per operation (never
149
+ * caller-supplied — IV reuse under GCM is catastrophic), authentication via
150
+ * the GCM tag, and a version prefix so the format can evolve. Same shape, in
151
+ * spirit, as MongoDB CSFLE's BSON-binary subtype-6 envelope. For
152
+ * cross-vendor interop or asymmetric keys use `mode: 'jwe'` (RFC 7516)
153
+ * instead.
154
+ */
155
+ /** Encrypt a UTF-8 string into the `arc.v1` field envelope. */
156
+ declare function encryptField(plaintext: string, kid: string, key: unknown): string;
157
+ /** Parsed envelope header — exposes `kid` for key resolution before decrypt. */
158
+ interface ParsedFieldEnvelope {
159
+ readonly kid: string;
160
+ readonly iv: Buffer;
161
+ readonly ciphertext: Buffer;
162
+ readonly tag: Buffer;
163
+ }
164
+ /** Returns the parsed envelope, or `null` when the token isn't `arc.v1`. */
165
+ declare function parseFieldEnvelope(token: string): ParsedFieldEnvelope | null;
166
+ /** Decrypt a parsed `arc.v1` envelope with the resolved key. Throws on tamper. */
167
+ declare function decryptField(envelope: ParsedFieldEnvelope, key: unknown): string;
168
+ //#endregion
169
+ //#region src/encryption/keyProvider.d.ts
170
+ interface StaticKeyProviderOptions {
171
+ /**
172
+ * Active key used to ENCRYPT outbound responses. For `jwe` mode this is the
173
+ * recipient's PUBLIC key; for `fields` mode a 32-byte symmetric secret.
174
+ */
175
+ readonly encryptionKey: ActiveKey;
176
+ /**
177
+ * Keys used to DECRYPT inbound requests, indexed by `kid`. For `jwe` mode
178
+ * these are PRIVATE keys; for `fields` mode the same symmetric secrets.
179
+ *
180
+ * Defaults to `{ [encryptionKey.kid]: encryptionKey.key }` — correct for
181
+ * symmetric field mode where one key both encrypts and decrypts. For
182
+ * asymmetric JWE you MUST pass the private key(s) here; the public
183
+ * `encryptionKey` cannot decrypt. Keep 2–3 entries during a rotation so
184
+ * in-flight messages signed with the previous `kid` still decrypt.
185
+ */
186
+ readonly decryptionKeys?: Readonly<Record<string, KeyMaterial>>;
187
+ }
188
+ /**
189
+ * A static, in-memory key provider with `kid`-based rotation. Keys are held
190
+ * for the process lifetime — resolve them from env / a secrets file at boot
191
+ * and pass the imported key material in (don't re-parse PEM per request).
192
+ *
193
+ * @example symmetric field mode
194
+ * ```ts
195
+ * const provider = createStaticKeyProvider({
196
+ * encryptionKey: { kid: "2026-06", key: Buffer.from(process.env.FIELD_KEY!, "base64") },
197
+ * });
198
+ * ```
199
+ *
200
+ * @example asymmetric JWE (jose key material)
201
+ * ```ts
202
+ * const provider = createStaticKeyProvider({
203
+ * encryptionKey: { kid: "client-1", key: await importSPKI(clientPubPem, "RSA-OAEP-256") },
204
+ * decryptionKeys: { "server-1": await importPKCS8(serverPrivPem, "RSA-OAEP-256") },
205
+ * });
206
+ * ```
207
+ */
208
+ declare function createStaticKeyProvider(options: StaticKeyProviderOptions): KeyProvider;
209
+ //#endregion
210
+ //#region src/encryption/index.d.ts
211
+ declare module "../types/resource.js" {
212
+ interface ResourceExtensions {
213
+ /**
214
+ * Encrypt this resource's responses — full-body JWE or specific fields.
215
+ * Threaded to request time on
216
+ * `request.routeOptions.config.arcExtensions.encryption`.
217
+ */
218
+ encryption?: EncryptionDirective;
219
+ }
220
+ }
221
+ //#endregion
222
+ export { type ActiveKey, type EncryptionDirective, type EncryptionMode, type EncryptionOptions, type JweAlg, type JweEnc, type KeyContext, type KeyMaterial, type KeyProvider, type ParsedFieldEnvelope, type StaticKeyProviderOptions, createStaticKeyProvider, decryptField, _default as default, encryptField, encryptionPlugin, parseFieldEnvelope };