@classytic/arc 2.8.5 → 2.9.1

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 (140) hide show
  1. package/README.md +88 -5
  2. package/dist/{BaseController-DAGGc5Xn.mjs → BaseController-Vu2yc56T.mjs} +188 -102
  3. package/dist/EventTransport-CqZ8FyM_.d.mts +293 -0
  4. package/dist/adapters/index.d.mts +2 -2
  5. package/dist/audit/index.d.mts +100 -11
  6. package/dist/audit/index.mjs +71 -18
  7. package/dist/auth/index.d.mts +16 -8
  8. package/dist/auth/index.mjs +13 -6
  9. package/dist/auth/redis-session.d.mts +1 -1
  10. package/dist/{betterAuthOpenApi-BuUcUEJq.mjs → betterAuthOpenApi--rdY15Ld.mjs} +1 -1
  11. package/dist/cache/index.d.mts +2 -2
  12. package/dist/cache/index.mjs +2 -2
  13. package/dist/cli/commands/docs.mjs +2 -2
  14. package/dist/cli/commands/introspect.mjs +1 -1
  15. package/dist/core/index.d.mts +3 -3
  16. package/dist/core/index.mjs +4 -5
  17. package/dist/{core-F0QoWBt2.mjs → core-DNncu0xF.mjs} +1 -1
  18. package/dist/{createActionRouter-BORM8f17.mjs → createActionRouter-DH1YFL9m.mjs} +3 -3
  19. package/dist/{createApp-B1EY8zxa.mjs → createApp-CBJUJKGP.mjs} +13 -12
  20. package/dist/{defineResource-tcgySDo1.mjs → defineResource-C__jkwvs.mjs} +22 -57
  21. package/dist/docs/index.d.mts +2 -2
  22. package/dist/docs/index.mjs +1 -1
  23. package/dist/dynamic/index.d.mts +1 -1
  24. package/dist/dynamic/index.mjs +3 -3
  25. package/dist/{elevation-DtFxrG0s.mjs → elevation-DxQ6ACbt.mjs} +21 -7
  26. package/dist/{errorHandler-f869_8PQ.mjs → errorHandler-CZDW4EXS.mjs} +59 -7
  27. package/dist/{errorHandler-Bah5JhBd.d.mts → errorHandler-DixGcttC.d.mts} +37 -2
  28. package/dist/{eventPlugin-D9DKB2zM.d.mts → eventPlugin-BxvaCIZF.d.mts} +14 -2
  29. package/dist/{eventPlugin-CDjVTM82.mjs → eventPlugin-Dl7MoVWH.mjs} +83 -5
  30. package/dist/events/index.d.mts +147 -36
  31. package/dist/events/index.mjs +338 -101
  32. package/dist/events/transports/redis-stream-entry.d.mts +1 -1
  33. package/dist/events/transports/redis.d.mts +1 -1
  34. package/dist/factory/index.d.mts +1 -1
  35. package/dist/factory/index.mjs +2 -2
  36. package/dist/{fields-DpZQa_Q3.d.mts → fields-BC7zcmI9.d.mts} +15 -3
  37. package/dist/{fields-ipsbIRPK.mjs → fields-CU6FlaDV.mjs} +18 -5
  38. package/dist/{filesUpload-C7r7HIeA.mjs → filesUpload-q8oHt--L.mjs} +65 -7
  39. package/dist/hooks/index.d.mts +1 -1
  40. package/dist/hooks/index.mjs +1 -1
  41. package/dist/idempotency/index.d.mts +29 -5
  42. package/dist/idempotency/index.mjs +111 -2
  43. package/dist/idempotency/redis.d.mts +1 -1
  44. package/dist/{index-BLXBmWud.d.mts → index-C-xjcA6F.d.mts} +1 -1
  45. package/dist/{index-DtDzOBn8.d.mts → index-Cibkchnx.d.mts} +3 -134
  46. package/dist/{index-C1meYuDn.d.mts → index-CtGKT0lf.d.mts} +1 -1
  47. package/dist/index.d.mts +7 -7
  48. package/dist/index.mjs +9 -9
  49. package/dist/integrations/event-gateway.d.mts +1 -1
  50. package/dist/integrations/event-gateway.mjs +1 -1
  51. package/dist/integrations/index.d.mts +1 -1
  52. package/dist/integrations/mcp/index.d.mts +26 -8
  53. package/dist/integrations/mcp/index.mjs +96 -17
  54. package/dist/integrations/mcp/testing.d.mts +1 -1
  55. package/dist/integrations/mcp/testing.mjs +1 -1
  56. package/dist/integrations/webhooks.d.mts +5 -0
  57. package/dist/integrations/webhooks.mjs +6 -0
  58. package/dist/{interface-CMRutPfe.d.mts → interface-YrWsmKqE.d.mts} +287 -179
  59. package/dist/{openapi-CbKUJY_m.mjs → openapi-CXuTG1M9.mjs} +2 -2
  60. package/dist/org/index.d.mts +1 -1
  61. package/dist/permissions/index.d.mts +2 -2
  62. package/dist/permissions/index.mjs +3 -3
  63. package/dist/{permissions-CH4cNwJi.mjs → permissions-oNZawnkR.mjs} +1 -1
  64. package/dist/plugins/index.d.mts +7 -7
  65. package/dist/plugins/index.mjs +11 -11
  66. package/dist/plugins/response-cache.mjs +1 -1
  67. package/dist/plugins/tracing-entry.d.mts +1 -1
  68. package/dist/plugins/tracing-entry.mjs +1 -1
  69. package/dist/policies/index.d.mts +25 -32
  70. package/dist/presets/filesUpload.d.mts +26 -4
  71. package/dist/presets/filesUpload.mjs +1 -1
  72. package/dist/presets/index.d.mts +3 -2
  73. package/dist/presets/index.mjs +4 -3
  74. package/dist/presets/multiTenant.d.mts +1 -1
  75. package/dist/presets/multiTenant.mjs +1 -1
  76. package/dist/presets/search.d.mts +91 -0
  77. package/dist/presets/search.mjs +150 -0
  78. package/dist/{presets-C2xgzW6x.mjs → presets-hM4WhNWY.mjs} +1 -1
  79. package/dist/{queryCachePlugin-BJJGBTlu.d.mts → queryCachePlugin-CnTZZTC5.d.mts} +1 -1
  80. package/dist/{queryCachePlugin-BH-fidlv.mjs → queryCachePlugin-DbUVroUG.mjs} +2 -2
  81. package/dist/{redis-BM00zaPB.d.mts → redis-MXLp1oOf.d.mts} +1 -1
  82. package/dist/{redis-stream-CrsfUmPt.d.mts → redis-stream-Bz-4q96t.d.mts} +1 -1
  83. package/dist/registry/index.d.mts +1 -1
  84. package/dist/registry/index.mjs +2 -2
  85. package/dist/{resourceToTools-8s-EsCCe.mjs → resourceToTools-C3cWymnW.mjs} +64 -47
  86. package/dist/rpc/index.d.mts +1 -1
  87. package/dist/rpc/index.mjs +1 -1
  88. package/dist/{schemaConverter-Y7nCYaLJ.mjs → schemaConverter-BxFDdtXu.mjs} +1 -1
  89. package/dist/scope/index.mjs +1 -1
  90. package/dist/{sse-Ad7ypl9e.mjs → sse-CJpt7LGI.mjs} +1 -1
  91. package/dist/store-helpers-DFiZl5TL.mjs +57 -0
  92. package/dist/testing/index.d.mts +5 -14
  93. package/dist/testing/index.mjs +21 -75
  94. package/dist/testing/storageContract.d.mts +1 -1
  95. package/dist/types/index.d.mts +2 -2
  96. package/dist/types/storage.d.mts +1 -1
  97. package/dist/{types-BsbNMEDR.d.mts → types-CoSzA-s-.d.mts} +1 -1
  98. package/dist/{types-Ch9pTQbf.d.mts → types-CunEX4UX.d.mts} +10 -8
  99. package/dist/utils/index.d.mts +4 -4
  100. package/dist/utils/index.mjs +6 -6
  101. package/dist/{utils-yYT3HDXt.mjs → utils-B7FuRr9w.mjs} +1 -1
  102. package/package.json +8 -11
  103. package/skills/arc/SKILL.md +92 -14
  104. package/skills/arc/references/auth.md +94 -0
  105. package/skills/arc/references/events.md +200 -12
  106. package/skills/arc/references/mcp.md +4 -17
  107. package/skills/arc/references/multi-tenancy.md +43 -0
  108. package/skills/arc/references/production.md +34 -19
  109. package/dist/EventTransport-BXja8NOc.d.mts +0 -135
  110. package/dist/audit/mongodb.d.mts +0 -2
  111. package/dist/audit/mongodb.mjs +0 -2
  112. package/dist/idempotency/mongodb.d.mts +0 -2
  113. package/dist/idempotency/mongodb.mjs +0 -123
  114. package/dist/mongodb-BsP-WbhN.d.mts +0 -127
  115. package/dist/mongodb-CTcp0hQZ.d.mts +0 -80
  116. package/dist/mongodb-Utc5k_-0.mjs +0 -90
  117. /package/dist/{HookSystem-HprTmvVY.mjs → HookSystem-BjFu7zf1.mjs} +0 -0
  118. /package/dist/{ResourceRegistry-C6uXlWe3.mjs → ResourceRegistry-Dq3_zBQP.mjs} +0 -0
  119. /package/dist/{applyPermissionResult-D6GPMsvh.mjs → applyPermissionResult-bqGpo9ML.mjs} +0 -0
  120. /package/dist/{caching-IMuYVjTL.mjs → caching-CjybdRwx.mjs} +0 -0
  121. /package/dist/{circuitBreaker-dTtG-UyS.d.mts → circuitBreaker-CvXkjfrW.d.mts} +0 -0
  122. /package/dist/{circuitBreaker-cmi5XDv5.mjs → circuitBreaker-l18oRgL5.mjs} +0 -0
  123. /package/dist/{errors-Ck2h67pm.d.mts → errors-BI8kEKsO.d.mts} +0 -0
  124. /package/dist/{errors-BF2bIOIS.mjs → errors-CqWnSqM-.mjs} +0 -0
  125. /package/dist/{externalPaths-BnkYrNzp.d.mts → externalPaths-Bapitwvd.d.mts} +0 -0
  126. /package/dist/{interface-DfLGcus7.d.mts → interface-B-pe8fhj.d.mts} +0 -0
  127. /package/dist/{interface-4y979v99.d.mts → interface-DplgQO2e.d.mts} +0 -0
  128. /package/dist/{loadResources-PWd0OCpV.mjs → loadResources-Bksk8ydA.mjs} +0 -0
  129. /package/dist/{logger-D1YrIImS.mjs → logger-CDjpjySd.mjs} +0 -0
  130. /package/dist/{memory-Cp7_cAko.mjs → memory-BFAYkf8H.mjs} +0 -0
  131. /package/dist/{metrics-B-PU4-Yu.mjs → metrics-TuOmguhi.mjs} +0 -0
  132. /package/dist/{queryParser-CgCtsjti.mjs → queryParser-Cs-6SHQK.mjs} +0 -0
  133. /package/dist/{registry-BiTKT1Dg.mjs → registry-B0Wl7uVV.mjs} +0 -0
  134. /package/dist/{replyHelpers-CxkYGT81.mjs → replyHelpers-BLojtuvR.mjs} +0 -0
  135. /package/dist/{requestContext-DYvHl113.mjs → requestContext-DYtmNpm5.mjs} +0 -0
  136. /package/dist/{sessionManager-DDCmiNIo.d.mts → sessionManager-D-oNWHz3.d.mts} +0 -0
  137. /package/dist/{storage-Dfzt4VTl.d.mts → storage-BwGQXUpd.d.mts} +0 -0
  138. /package/dist/{tracing-DdN2-wHJ.d.mts → tracing-xqXzWeaf.d.mts} +0 -0
  139. /package/dist/{typeGuards-CcFZXgU7.mjs → typeGuards-Cj5Rgvlg.mjs} +0 -0
  140. /package/dist/{versioning-CDugduqI.mjs → versioning-Cm8qoFDg.mjs} +0 -0
@@ -1,5 +1,5 @@
1
- import { i as CacheStore, n as CacheSetOptions, r as CacheStats, t as CacheLogger } from "../interface-4y979v99.mjs";
2
- import { a as CacheEnvelope, c as QueryCache, i as queryCachePlugin, l as QueryCacheConfig, n as QueryCacheDefaults, o as CacheResult, r as QueryCachePluginOptions, s as CacheStatus, t as CrossResourceRule } from "../queryCachePlugin-BJJGBTlu.mjs";
1
+ import { i as CacheStore, n as CacheSetOptions, r as CacheStats, t as CacheLogger } from "../interface-DplgQO2e.mjs";
2
+ import { a as CacheEnvelope, c as QueryCache, i as queryCachePlugin, l as QueryCacheConfig, n as QueryCacheDefaults, o as CacheResult, r as QueryCachePluginOptions, s as CacheStatus, t as CrossResourceRule } from "../queryCachePlugin-CnTZZTC5.mjs";
3
3
 
4
4
  //#region src/cache/keys.d.ts
5
5
  /**
@@ -1,6 +1,6 @@
1
1
  import { i as versionKey, n as hashParams, r as tagVersionKey, t as buildQueryKey } from "../keys-qcD-TVJl.mjs";
2
- import { t as MemoryCacheStore } from "../memory-Cp7_cAko.mjs";
3
- import { r as QueryCache, t as queryCachePlugin } from "../queryCachePlugin-BH-fidlv.mjs";
2
+ import { t as MemoryCacheStore } from "../memory-BFAYkf8H.mjs";
3
+ import { r as QueryCache, t as queryCachePlugin } from "../queryCachePlugin-DbUVroUG.mjs";
4
4
  //#region src/cache/redis.ts
5
5
  /**
6
6
  * Redis-backed cache store.
@@ -1,5 +1,5 @@
1
- import { t as ResourceRegistry } from "../../ResourceRegistry-C6uXlWe3.mjs";
2
- import { t as buildOpenApiSpec } from "../../openapi-CbKUJY_m.mjs";
1
+ import { t as ResourceRegistry } from "../../ResourceRegistry-Dq3_zBQP.mjs";
2
+ import { t as buildOpenApiSpec } from "../../openapi-CXuTG1M9.mjs";
3
3
  import { dirname, resolve } from "node:path";
4
4
  import { pathToFileURL } from "node:url";
5
5
  import { mkdirSync, writeFileSync } from "node:fs";
@@ -1,4 +1,4 @@
1
- import { t as ResourceRegistry } from "../../ResourceRegistry-C6uXlWe3.mjs";
1
+ import { t as ResourceRegistry } from "../../ResourceRegistry-Dq3_zBQP.mjs";
2
2
  import { resolve } from "node:path";
3
3
  import { pathToFileURL } from "node:url";
4
4
  //#region src/cli/commands/introspect.ts
@@ -1,3 +1,3 @@
1
- import { At as BaseController, Ft as BodySanitizerConfig, It as AccessControl, Jt as defineResource, Lt as AccessControlConfig, Mt as QueryResolver, Nt as QueryResolverConfig, Pt as BodySanitizer, jt as BaseControllerOptions, qt as ResourceDefinition } from "../interface-CMRutPfe.mjs";
2
- import { A as MUTATION_OPERATIONS, C as HOOK_OPERATIONS, D as MAX_FILTER_DEPTH, E as HookPhase, M as RESERVED_QUERY_PARAMS, N as SYSTEM_FIELDS, O as MAX_REGEX_LENGTH, S as DEFAULT_UPDATE_METHOD, T as HookOperation, _ as DEFAULT_ID_FIELD, a as getControllerScope, b as DEFAULT_SORT, c as createCrudRouter, d as ActionRouterConfig, f as IdempotencyService, g as CrudOperation, h as CRUD_OPERATIONS, i as getControllerContext, j as MutationOperation, k as MAX_SEARCH_LENGTH, l as createPermissionMiddleware, m as createActionRouter, n as createFastifyHandler, o as sendControllerResponse, p as buildActionBodySchema, r as createRequestContext, s as defineResourceVariants, t as createCrudHandlers, u as ActionHandler, v as DEFAULT_LIMIT, w as HOOK_PHASES, x as DEFAULT_TENANT_FIELD, y as DEFAULT_MAX_LIMIT } from "../index-DtDzOBn8.mjs";
3
- export { AccessControl, AccessControlConfig, ActionHandler, ActionRouterConfig, BaseController, BaseControllerOptions, BodySanitizer, BodySanitizerConfig, CRUD_OPERATIONS, CrudOperation, DEFAULT_ID_FIELD, DEFAULT_LIMIT, DEFAULT_MAX_LIMIT, DEFAULT_SORT, DEFAULT_TENANT_FIELD, DEFAULT_UPDATE_METHOD, HOOK_OPERATIONS, HOOK_PHASES, HookOperation, HookPhase, IdempotencyService, MAX_FILTER_DEPTH, MAX_REGEX_LENGTH, MAX_SEARCH_LENGTH, MUTATION_OPERATIONS, MutationOperation, QueryResolver, QueryResolverConfig, RESERVED_QUERY_PARAMS, ResourceDefinition, SYSTEM_FIELDS, buildActionBodySchema, createActionRouter, createCrudHandlers, createCrudRouter, createFastifyHandler, createPermissionMiddleware, createRequestContext, defineResource, defineResourceVariants, getControllerContext, getControllerScope, sendControllerResponse };
1
+ import { At as BaseControllerOptions, Ft as AccessControl, It as AccessControlConfig, Kt as ResourceDefinition, Mt as QueryResolverConfig, Nt as BodySanitizer, Pt as BodySanitizerConfig, jt as QueryResolver, kt as BaseController, qt as defineResource } from "../interface-YrWsmKqE.mjs";
2
+ import { C as MAX_REGEX_LENGTH, D as RESERVED_QUERY_PARAMS, E as MutationOperation, O as SYSTEM_FIELDS, S as MAX_FILTER_DEPTH, T as MUTATION_OPERATIONS, _ as DEFAULT_UPDATE_METHOD, a as getControllerScope, b as HookOperation, c as createCrudRouter, d as CrudOperation, f as DEFAULT_ID_FIELD, g as DEFAULT_TENANT_FIELD, h as DEFAULT_SORT, i as getControllerContext, l as createPermissionMiddleware, m as DEFAULT_MAX_LIMIT, n as createFastifyHandler, o as sendControllerResponse, p as DEFAULT_LIMIT, r as createRequestContext, s as defineResourceVariants, t as createCrudHandlers, u as CRUD_OPERATIONS, v as HOOK_OPERATIONS, w as MAX_SEARCH_LENGTH, x as HookPhase, y as HOOK_PHASES } from "../index-Cibkchnx.mjs";
3
+ export { AccessControl, AccessControlConfig, BaseController, BaseControllerOptions, BodySanitizer, BodySanitizerConfig, CRUD_OPERATIONS, CrudOperation, DEFAULT_ID_FIELD, DEFAULT_LIMIT, DEFAULT_MAX_LIMIT, DEFAULT_SORT, DEFAULT_TENANT_FIELD, DEFAULT_UPDATE_METHOD, HOOK_OPERATIONS, HOOK_PHASES, HookOperation, HookPhase, MAX_FILTER_DEPTH, MAX_REGEX_LENGTH, MAX_SEARCH_LENGTH, MUTATION_OPERATIONS, MutationOperation, QueryResolver, QueryResolverConfig, RESERVED_QUERY_PARAMS, ResourceDefinition, SYSTEM_FIELDS, createCrudHandlers, createCrudRouter, createFastifyHandler, createPermissionMiddleware, createRequestContext, defineResource, defineResourceVariants, getControllerContext, getControllerScope, sendControllerResponse };
@@ -1,6 +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-Cxde4rpC.mjs";
2
- import { i as AccessControl, n as QueryResolver, r as BodySanitizer, t as BaseController } from "../BaseController-DAGGc5Xn.mjs";
3
- import { n as createActionRouter, t as buildActionBodySchema } from "../createActionRouter-BORM8f17.mjs";
4
- import { c as createCrudHandlers, d as getControllerContext, f as getControllerScope, l as createFastifyHandler, n as defineResource, o as createCrudRouter, p as sendControllerResponse, s as createPermissionMiddleware, t as ResourceDefinition, u as createRequestContext } from "../defineResource-tcgySDo1.mjs";
5
- import { t as defineResourceVariants } from "../core-F0QoWBt2.mjs";
6
- export { AccessControl, BaseController, BodySanitizer, 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, buildActionBodySchema, createActionRouter, createCrudHandlers, createCrudRouter, createFastifyHandler, createPermissionMiddleware, createRequestContext, defineResource, defineResourceVariants, getControllerContext, getControllerScope, sendControllerResponse };
2
+ import { i as AccessControl, n as QueryResolver, r as BodySanitizer, t as BaseController } from "../BaseController-Vu2yc56T.mjs";
3
+ import { c as createCrudHandlers, d as getControllerContext, f as getControllerScope, l as createFastifyHandler, n as defineResource, o as createCrudRouter, p as sendControllerResponse, s as createPermissionMiddleware, t as ResourceDefinition, u as createRequestContext } from "../defineResource-C__jkwvs.mjs";
4
+ import { t as defineResourceVariants } from "../core-DNncu0xF.mjs";
5
+ export { AccessControl, BaseController, BodySanitizer, 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, createCrudHandlers, createCrudRouter, createFastifyHandler, createPermissionMiddleware, createRequestContext, defineResource, defineResourceVariants, getControllerContext, getControllerScope, sendControllerResponse };
@@ -1,4 +1,4 @@
1
- import { n as defineResource } from "./defineResource-tcgySDo1.mjs";
1
+ import { n as defineResource } from "./defineResource-C__jkwvs.mjs";
2
2
  //#region src/core/defineResourceVariants.ts
3
3
  /**
4
4
  * Define multiple resources from a shared base config and per-variant overrides.
@@ -1,6 +1,6 @@
1
1
  import { t as __exportAll } from "./chunk-BpYLSNr0.mjs";
2
- import { n as normalizePermissionResult, t as applyPermissionResult } from "./applyPermissionResult-D6GPMsvh.mjs";
3
- import { a as toJsonSchema } from "./schemaConverter-Y7nCYaLJ.mjs";
2
+ import { n as normalizePermissionResult, t as applyPermissionResult } from "./applyPermissionResult-bqGpo9ML.mjs";
3
+ import { a as toJsonSchema } from "./schemaConverter-BxFDdtXu.mjs";
4
4
  //#region src/core/createActionRouter.ts
5
5
  var createActionRouter_exports = /* @__PURE__ */ __exportAll({
6
6
  buildActionBodySchema: () => buildActionBodySchema,
@@ -246,4 +246,4 @@ function buildActionDescription(actions, actionPermissions) {
246
246
  return lines.join("\n");
247
247
  }
248
248
  //#endregion
249
- export { createActionRouter as n, createActionRouter_exports as r, buildActionBodySchema as t };
249
+ export { createActionRouter_exports as n, buildActionBodySchema as t };
@@ -207,7 +207,7 @@ async function registerArcCore(fastify, config, trackPlugin) {
207
207
  await fastify.register(arcCorePlugin, { emitEvents: config.arcPlugins?.emitEvents !== false });
208
208
  trackPlugin("arc-core");
209
209
  if (config.arcPlugins?.events !== false) {
210
- const { default: eventPlugin } = await import("./eventPlugin-CDjVTM82.mjs").then((n) => n.n);
210
+ const { default: eventPlugin } = await import("./eventPlugin-Dl7MoVWH.mjs").then((n) => n.n);
211
211
  const eventOpts = typeof config.arcPlugins?.events === "object" ? config.arcPlugins.events : {};
212
212
  await fastify.register(eventPlugin, {
213
213
  ...eventOpts,
@@ -243,15 +243,15 @@ async function registerArcPlugins(fastify, config, trackPlugin, modules) {
243
243
  trackPlugin("arc-graceful-shutdown");
244
244
  }
245
245
  if (config.arcPlugins?.caching) {
246
- const { default: cachingPlugin } = await import("./caching-IMuYVjTL.mjs").then((n) => n.r);
246
+ const { default: cachingPlugin } = await import("./caching-CjybdRwx.mjs").then((n) => n.r);
247
247
  const opts = config.arcPlugins.caching === true ? {} : config.arcPlugins.caching;
248
248
  await fastify.register(cachingPlugin, opts);
249
249
  trackPlugin("arc-caching", opts);
250
250
  }
251
251
  if (config.arcPlugins?.queryCache) {
252
- const { queryCachePlugin } = await import("./queryCachePlugin-BH-fidlv.mjs").then((n) => n.n);
252
+ const { queryCachePlugin } = await import("./queryCachePlugin-DbUVroUG.mjs").then((n) => n.n);
253
253
  const opts = config.arcPlugins.queryCache === true ? {} : config.arcPlugins.queryCache;
254
- const store = config.stores?.queryCache ?? new (await (import("./memory-Cp7_cAko.mjs").then((n) => n.n))).MemoryCacheStore();
254
+ const store = config.stores?.queryCache ?? new (await (import("./memory-BFAYkf8H.mjs").then((n) => n.n))).MemoryCacheStore();
255
255
  await fastify.register(queryCachePlugin, {
256
256
  store,
257
257
  ...opts
@@ -260,19 +260,19 @@ async function registerArcPlugins(fastify, config, trackPlugin, modules) {
260
260
  }
261
261
  if (config.arcPlugins?.sse) if (config.arcPlugins?.events === false) fastify.log.warn("SSE plugin requires events plugin (arcPlugins.events). SSE disabled.");
262
262
  else {
263
- const { default: ssePlugin } = await import("./sse-Ad7ypl9e.mjs").then((n) => n.r);
263
+ const { default: ssePlugin } = await import("./sse-CJpt7LGI.mjs").then((n) => n.r);
264
264
  const opts = config.arcPlugins.sse === true ? {} : config.arcPlugins.sse;
265
265
  await fastify.register(ssePlugin, opts);
266
266
  trackPlugin("arc-sse", opts);
267
267
  }
268
268
  if (config.arcPlugins?.metrics) {
269
- const { default: metricsPlugin } = await import("./metrics-B-PU4-Yu.mjs").then((n) => n.r);
269
+ const { default: metricsPlugin } = await import("./metrics-TuOmguhi.mjs").then((n) => n.r);
270
270
  const opts = config.arcPlugins.metrics === true ? {} : config.arcPlugins.metrics;
271
271
  await fastify.register(metricsPlugin, opts);
272
272
  trackPlugin("arc-metrics", opts);
273
273
  }
274
274
  if (config.arcPlugins?.versioning) {
275
- const { default: versioningPlugin } = await import("./versioning-CDugduqI.mjs").then((n) => n.r);
275
+ const { default: versioningPlugin } = await import("./versioning-Cm8qoFDg.mjs").then((n) => n.r);
276
276
  await fastify.register(versioningPlugin, config.arcPlugins.versioning);
277
277
  trackPlugin("arc-versioning", config.arcPlugins.versioning);
278
278
  }
@@ -340,7 +340,7 @@ async function registerAuth(fastify, config, trackPlugin) {
340
340
  */
341
341
  async function registerElevation(fastify, config, trackPlugin) {
342
342
  if (!config.elevation) return;
343
- const { elevationPlugin } = await import("./elevation-DtFxrG0s.mjs").then((n) => n.r);
343
+ const { elevationPlugin } = await import("./elevation-DxQ6ACbt.mjs").then((n) => n.r);
344
344
  await fastify.register(elevationPlugin, config.elevation);
345
345
  trackPlugin("arc-elevation", config.elevation);
346
346
  fastify.log.debug("Elevation plugin enabled");
@@ -350,7 +350,7 @@ async function registerElevation(fastify, config, trackPlugin) {
350
350
  */
351
351
  async function registerErrorHandler(fastify, config, trackPlugin) {
352
352
  if (config.errorHandler === false) return;
353
- const { errorHandlerPlugin } = await import("./errorHandler-f869_8PQ.mjs").then((n) => n.n);
353
+ const { errorHandlerPlugin } = await import("./errorHandler-CZDW4EXS.mjs").then((n) => n.r);
354
354
  const errorOpts = typeof config.errorHandler === "object" ? config.errorHandler : { includeStack: config.preset !== "production" };
355
355
  await fastify.register(errorHandlerPlugin, errorOpts);
356
356
  trackPlugin("arc-error-handler", errorOpts);
@@ -417,7 +417,7 @@ async function registerResources(fastify, config) {
417
417
  fastify.log.debug(`${config.bootstrap.length} bootstrap function(s) executed`);
418
418
  }
419
419
  if (!config.resources?.length && config.resourceDir) {
420
- const { loadResources } = await import("./loadResources-PWd0OCpV.mjs").then((n) => n.n);
420
+ const { loadResources } = await import("./loadResources-Bksk8ydA.mjs").then((n) => n.n);
421
421
  const { resolve } = await import("node:path");
422
422
  const dir = resolve(config.resourceDir);
423
423
  config = {
@@ -676,7 +676,7 @@ function validateDistributedRuntime(options) {
676
676
  */
677
677
  async function createApp(options) {
678
678
  if (options.debug !== void 0 && options.debug !== false) {
679
- const { configureArcLogger } = await import("./logger-D1YrIImS.mjs").then((n) => n.r);
679
+ const { configureArcLogger } = await import("./logger-CDjpjySd.mjs").then((n) => n.r);
680
680
  configureArcLogger({ debug: options.debug });
681
681
  }
682
682
  validateAuthOptions(options);
@@ -688,6 +688,7 @@ async function createApp(options) {
688
688
  const fastify = Fastify({
689
689
  logger: config.logger ?? true,
690
690
  trustProxy: config.trustProxy ?? false,
691
+ pluginTimeout: config.pluginTimeout ?? 1e4,
691
692
  routerOptions: { querystringParser: (str) => qs.parse(str) },
692
693
  ajv: { customOptions: {
693
694
  coerceTypes: true,
@@ -729,7 +730,7 @@ async function createApp(options) {
729
730
  await registerErrorHandler(fastify, config, trackPlugin);
730
731
  await registerResources(fastify, config);
731
732
  if (config.replyHelpers) {
732
- const { replyHelpersPlugin } = await import("./replyHelpers-CxkYGT81.mjs").then((n) => n.n);
733
+ const { replyHelpersPlugin } = await import("./replyHelpers-BLojtuvR.mjs").then((n) => n.n);
733
734
  await fastify.register(replyHelpersPlugin);
734
735
  }
735
736
  if (config.serializeBigInt) fastify.addHook("preSerialization", async (_request, _reply, payload) => {
@@ -1,15 +1,15 @@
1
1
  import { s as DEFAULT_UPDATE_METHOD, t as CRUD_OPERATIONS } from "./constants-Cxde4rpC.mjs";
2
2
  import { _ as isElevated, n as PUBLIC_SCOPE, v as isMember } from "./types-AOD8fxIw.mjs";
3
- import { t as BaseController } from "./BaseController-DAGGc5Xn.mjs";
4
- import { i as resolveEffectiveRoles, t as applyFieldReadPermissions } from "./fields-ipsbIRPK.mjs";
3
+ import { t as BaseController } from "./BaseController-Vu2yc56T.mjs";
4
+ import { i as resolveEffectiveRoles, t as applyFieldReadPermissions } from "./fields-CU6FlaDV.mjs";
5
5
  import { t as getUserRoles } from "./types-ZUu_h0jp.mjs";
6
- import { n as normalizePermissionResult, t as applyPermissionResult } from "./applyPermissionResult-D6GPMsvh.mjs";
7
- import { n as convertRouteSchema, t as convertOpenApiSchemas } from "./schemaConverter-Y7nCYaLJ.mjs";
8
- import { t as requestContext } from "./requestContext-DYvHl113.mjs";
9
- import { i as getDefaultCrudSchemas } from "./utils-yYT3HDXt.mjs";
10
- import { r as ForbiddenError } from "./errors-BF2bIOIS.mjs";
11
- import { t as hasEvents } from "./typeGuards-CcFZXgU7.mjs";
12
- import { r as getAvailablePresets, t as applyPresets } from "./presets-C2xgzW6x.mjs";
6
+ import { r as ForbiddenError } from "./errors-CqWnSqM-.mjs";
7
+ import { t as requestContext } from "./requestContext-DYtmNpm5.mjs";
8
+ import { n as normalizePermissionResult, t as applyPermissionResult } from "./applyPermissionResult-bqGpo9ML.mjs";
9
+ import { i as getDefaultCrudSchemas } from "./utils-B7FuRr9w.mjs";
10
+ import { n as convertRouteSchema, t as convertOpenApiSchemas } from "./schemaConverter-BxFDdtXu.mjs";
11
+ import { t as hasEvents } from "./typeGuards-Cj5Rgvlg.mjs";
12
+ import { r as getAvailablePresets, t as applyPresets } from "./presets-hM4WhNWY.mjs";
13
13
  //#region src/pipeline/pipe.ts
14
14
  /**
15
15
  * Compose pipeline steps into an ordered array.
@@ -387,24 +387,26 @@ function buildPermissionMiddleware(permissionCheck, resourceName, action) {
387
387
  };
388
388
  }
389
389
  /**
390
- * Create additional routes from preset/custom definitions
390
+ * Mount custom routes (from presets or user-defined `routes`) on Fastify.
391
+ * `wrapHandler` is derived inline from `!route.raw`.
391
392
  */
392
- function createAdditionalRoutes(fastify, routes, controller, options) {
393
+ function createCustomRoutes(fastify, routes, controller, options) {
393
394
  const { tag, resourceName, arcDecorator, rateLimitConfig, cacheMw, idempotencyMw, pipeline, routeGuards } = options;
394
395
  for (const route of routes) {
395
396
  const opName = route.operation ?? (typeof route.handler === "string" ? route.handler : `${route.method.toLowerCase()}${route.path.replace(/[/:]/g, "_")}`);
397
+ const wrapHandler = !route.raw;
396
398
  let handler;
397
399
  if (typeof route.handler === "string") {
398
400
  if (!controller) throw new Error(`Route ${route.method} ${route.path}: string handler '${route.handler}' requires a controller. Either provide a controller or use a function handler instead.`);
399
401
  const method = controller[route.handler];
400
402
  if (typeof method !== "function") throw new Error(`Handler '${route.handler}' not found on controller`);
401
403
  const boundMethod = method.bind(controller);
402
- if (route.wrapHandler) {
404
+ if (wrapHandler) {
403
405
  const steps = pipeline ? resolvePipelineSteps(pipeline, opName) : [];
404
406
  if (steps.length > 0) handler = createPipelineHandler(boundMethod, steps, opName, resourceName);
405
407
  else handler = createFastifyHandler(boundMethod);
406
408
  } else handler = boundMethod;
407
- } else if (route.wrapHandler) {
409
+ } else if (wrapHandler) {
408
410
  const steps = pipeline ? resolvePipelineSteps(pipeline, opName) : [];
409
411
  if (steps.length > 0) handler = createPipelineHandler(route.handler, steps, opName, resourceName);
410
412
  else handler = createFastifyHandler(route.handler);
@@ -480,7 +482,7 @@ function createPipelineHandler(controllerMethod, steps, operation, resourceName)
480
482
  * @param options - Router configuration
481
483
  */
482
484
  function createCrudRouter(fastify, controller, options = {}) {
483
- const { tag = "Resource", schemas = {}, permissions = {}, middlewares = {}, routeGuards = [], additionalRoutes = [], disableDefaultRoutes = false, disabledRoutes = [], resourceName = "unknown", schemaOptions, rateLimit, pipe: pipeline, fields: fieldPermissions, updateMethod = DEFAULT_UPDATE_METHOD } = options;
485
+ const { tag = "Resource", schemas = {}, permissions = {}, middlewares = {}, routeGuards = [], routes: customRoutes = [], disableDefaultRoutes = false, disabledRoutes = [], resourceName = "unknown", schemaOptions, rateLimit, pipe: pipeline, fields: fieldPermissions, updateMethod = DEFAULT_UPDATE_METHOD } = options;
484
486
  const rateLimitConfig = buildRateLimitConfig(rateLimit);
485
487
  const cacheMw = !(fastify.hasDecorator("queryCache") && controller && typeof controller._cacheConfig !== "undefined" && controller._cacheConfig !== void 0) && fastify.hasDecorator("responseCache") ? fastify.responseCache.middleware : null;
486
488
  const idempotencyMw = fastify.hasDecorator("idempotency") ? fastify.idempotency.middleware : null;
@@ -646,7 +648,7 @@ function createCrudRouter(fastify, controller, options = {}) {
646
648
  });
647
649
  }
648
650
  }
649
- if (additionalRoutes.length > 0) createAdditionalRoutes(fastify, additionalRoutes, controller, {
651
+ if (customRoutes.length > 0) createCustomRoutes(fastify, customRoutes, controller, {
650
652
  tag,
651
653
  resourceName,
652
654
  arcDecorator,
@@ -941,6 +943,7 @@ function defineResource(config) {
941
943
  idField: resolvedConfig.idField,
942
944
  matchesFilter: config.adapter?.matchesFilter,
943
945
  cache: resolvedConfig.cache,
946
+ onFieldWriteDenied: resolvedConfig.onFieldWriteDenied,
944
947
  presetFields: resolvedConfig._controllerOptions ? {
945
948
  slugField: resolvedConfig._controllerOptions.slugField,
946
949
  parentField: resolvedConfig._controllerOptions.parentField
@@ -1088,16 +1091,6 @@ var ResourceDefinition = class {
1088
1091
  schemaOptions;
1089
1092
  customSchemas;
1090
1093
  permissions;
1091
- additionalRoutes;
1092
- /**
1093
- * Original v2.8 `routes` declaration — retained for downstream consumers
1094
- * (OpenAPI, MCP, registry, CLI introspect). Preserves fields dropped during
1095
- * normalization to `additionalRoutes` (notably `mcp`, `description`,
1096
- * `annotations`). Undefined when the resource was defined with the legacy
1097
- * `additionalRoutes` shape.
1098
- *
1099
- * Added in 2.8.1 — the source-of-truth fix for "canonical resource manifest".
1100
- */
1101
1094
  routes;
1102
1095
  middlewares;
1103
1096
  routeGuards;
@@ -1130,8 +1123,7 @@ var ResourceDefinition = class {
1130
1123
  this.schemaOptions = config.schemaOptions ?? {};
1131
1124
  this.customSchemas = config.customSchemas ?? {};
1132
1125
  this.permissions = config.permissions ?? {};
1133
- this.routes = config.routes;
1134
- this.additionalRoutes = config.routes ? convertRoutesToAdditionalRoutes(config.routes) : [];
1126
+ this.routes = config.routes ?? [];
1135
1127
  this.middlewares = config.middlewares ?? {};
1136
1128
  this.routeGuards = config.routeGuards;
1137
1129
  this.disableDefaultRoutes = config.disableDefaultRoutes ?? false;
@@ -1150,7 +1142,6 @@ var ResourceDefinition = class {
1150
1142
  this.queryParser = config.queryParser;
1151
1143
  this._appliedPresets = config._appliedPresets ?? [];
1152
1144
  this._pendingHooks = config._pendingHooks ?? [];
1153
- if (config.onRegister) this._onRegister = config.onRegister;
1154
1145
  }
1155
1146
  /** Get repository from adapter (if available) */
1156
1147
  get repository() {
@@ -1166,7 +1157,7 @@ var ResourceDefinition = class {
1166
1157
  const ctrl = this.controller;
1167
1158
  for (const method of enabledCrudRoutes) if (typeof ctrl[method] !== "function") errors.push(`CRUD method '${method}' not found on controller`);
1168
1159
  }
1169
- for (const route of this.additionalRoutes) if (typeof route.handler === "string") {
1160
+ for (const route of this.routes) if (typeof route.handler === "string") {
1170
1161
  if (!this.controller) errors.push(`Route ${route.method} ${route.path}: string handler '${route.handler}' requires a controller`);
1171
1162
  else if (typeof this.controller[route.handler] !== "function") errors.push(`Route ${route.method} ${route.path}: handler '${route.handler}' not found`);
1172
1163
  }
@@ -1207,8 +1198,6 @@ var ResourceDefinition = class {
1207
1198
  });
1208
1199
  await fastify.register(async (instance) => {
1209
1200
  const typedInstance = instance;
1210
- const onRegister = self._onRegister;
1211
- if (onRegister) await onRegister(instance);
1212
1201
  let schemas = null;
1213
1202
  const openApi = self._registryMeta?.openApiSchemas;
1214
1203
  if (openApi && (!self.customSchemas || Object.keys(self.customSchemas).length === 0)) {
@@ -1281,14 +1270,13 @@ var ResourceDefinition = class {
1281
1270
  schemas = schemas ?? {};
1282
1271
  schemas.list = schemas.list ? deepMergeSchemas({ querystring: normalizedSchema }, schemas.list) : { querystring: normalizedSchema };
1283
1272
  }
1284
- const resolvedRoutes = self.additionalRoutes;
1285
1273
  createCrudRouter(typedInstance, self.controller, {
1286
1274
  tag: self.tag,
1287
1275
  schemas: schemas ?? void 0,
1288
1276
  permissions: self.permissions,
1289
1277
  middlewares: self.middlewares,
1290
1278
  routeGuards: self.routeGuards,
1291
- additionalRoutes: resolvedRoutes,
1279
+ routes: self.routes,
1292
1280
  disableDefaultRoutes: self.disableDefaultRoutes,
1293
1281
  disabledRoutes: self.disabledRoutes,
1294
1282
  resourceName: self.name,
@@ -1299,7 +1287,7 @@ var ResourceDefinition = class {
1299
1287
  fields: self.fields
1300
1288
  });
1301
1289
  if (self.actions && Object.keys(self.actions).length > 0) {
1302
- const { createActionRouter } = await import("./createActionRouter-BORM8f17.mjs").then((n) => n.r);
1290
+ const { createActionRouter } = await import("./createActionRouter-DH1YFL9m.mjs").then((n) => n.n);
1303
1291
  createActionRouter(instance, normalizeActionsToRouterConfig(self.actions, self.actionPermissions, self.tag));
1304
1292
  }
1305
1293
  if (self.events && Object.keys(self.events).length > 0) typedInstance.log?.debug?.(`Resource '${self.name}' defined ${Object.keys(self.events).length} events`);
@@ -1366,29 +1354,6 @@ function capitalize(str) {
1366
1354
  return str.charAt(0).toUpperCase() + str.slice(1);
1367
1355
  }
1368
1356
  /**
1369
- * Convert v2.8 RouteDefinition[] to internal AdditionalRoute[] format.
1370
- * The internal format is what createCrudRouter understands.
1371
- */
1372
- function convertRoutesToAdditionalRoutes(routes) {
1373
- return routes.map((route) => ({
1374
- method: route.method,
1375
- path: route.path,
1376
- handler: route.handler,
1377
- permissions: route.permissions,
1378
- wrapHandler: !route.raw,
1379
- operation: route.operation,
1380
- summary: route.summary,
1381
- description: route.description,
1382
- tags: route.tags,
1383
- preHandler: route.preHandler,
1384
- preAuth: route.preAuth,
1385
- streamResponse: route.streamResponse,
1386
- schema: route.schema,
1387
- mcpHandler: route.mcpHandler,
1388
- mcp: route.mcp
1389
- }));
1390
- }
1391
- /**
1392
1357
  * Normalize ActionsMap into the ActionRouterConfig shape that createActionRouter expects.
1393
1358
  */
1394
1359
  function normalizeActionsToRouterConfig(actions, globalAuth, tag) {
@@ -1,5 +1,5 @@
1
- import { it as RegistryEntry } from "../interface-CMRutPfe.mjs";
2
- import { t as ExternalOpenApiPaths } from "../externalPaths-BnkYrNzp.mjs";
1
+ import { rt as RegistryEntry } from "../interface-YrWsmKqE.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
@@ -1,5 +1,5 @@
1
1
  import { t as getUserRoles } from "../types-ZUu_h0jp.mjs";
2
- import { n as openApiPlugin, r as openapi_default, t as buildOpenApiSpec } from "../openapi-CbKUJY_m.mjs";
2
+ import { n as openApiPlugin, r as openapi_default, t as buildOpenApiSpec } from "../openapi-CXuTG1M9.mjs";
3
3
  import fp from "fastify-plugin";
4
4
  //#region src/docs/scalar.ts
5
5
  const scalarPlugin = async (fastify, opts = {}) => {
@@ -1,4 +1,4 @@
1
- import { qt as ResourceDefinition, r as DataAdapter } from "../interface-CMRutPfe.mjs";
1
+ import { Kt as ResourceDefinition, r as DataAdapter } from "../interface-YrWsmKqE.mjs";
2
2
  import { t as PermissionCheck } from "../types-DZi1aYhm.mjs";
3
3
 
4
4
  //#region src/dynamic/ArcDynamicLoader.d.ts
@@ -1,6 +1,6 @@
1
- import { t as ArcQueryParser } from "../queryParser-CgCtsjti.mjs";
2
- import { n as defineResource } from "../defineResource-tcgySDo1.mjs";
3
- import { C as publicRead, T as readOnly, b as fullPublic, v as adminOnly, w as publicReadAdminWrite, x as ownerWithAdminBypass, y as authenticated } from "../permissions-CH4cNwJi.mjs";
1
+ import { t as ArcQueryParser } from "../queryParser-Cs-6SHQK.mjs";
2
+ import { n as defineResource } from "../defineResource-C__jkwvs.mjs";
3
+ import { C as publicRead, T as readOnly, b as fullPublic, v as adminOnly, w as publicReadAdminWrite, x as ownerWithAdminBypass, y as authenticated } from "../permissions-oNZawnkR.mjs";
4
4
  //#region src/dynamic/ArcDynamicLoader.ts
5
5
  const VALID_FIELD_TYPES = new Set([
6
6
  "string",
@@ -1,6 +1,6 @@
1
1
  import { t as __exportAll } from "./chunk-BpYLSNr0.mjs";
2
2
  import { t as getUserRoles } from "./types-ZUu_h0jp.mjs";
3
- import { t as arcLog } from "./logger-D1YrIImS.mjs";
3
+ import { t as arcLog } from "./logger-CDjpjySd.mjs";
4
4
  import fp from "fastify-plugin";
5
5
  //#region src/scope/elevation.ts
6
6
  var elevation_exports = /* @__PURE__ */ __exportAll({
@@ -57,13 +57,27 @@ const elevationPlugin = async (fastify, opts = {}) => {
57
57
  userId,
58
58
  organizationId: orgId
59
59
  });
60
- if (onElevation) try {
61
- await onElevation({
62
- userId,
63
- organizationId: orgId || void 0,
64
- request,
65
- timestamp: /* @__PURE__ */ new Date()
60
+ const event = {
61
+ userId,
62
+ organizationId: orgId || void 0,
63
+ request,
64
+ timestamp: /* @__PURE__ */ new Date()
65
+ };
66
+ const publish = fastify.events?.publish;
67
+ if (publish) try {
68
+ await publish("arc.scope.elevated", {
69
+ userId: event.userId,
70
+ organizationId: event.organizationId,
71
+ route: request.routeOptions?.url ?? request.url,
72
+ method: request.method,
73
+ requestId: request.id,
74
+ timestamp: event.timestamp.toISOString()
66
75
  });
76
+ } catch (err) {
77
+ log.warn("Failed to publish arc.scope.elevated event", { error: err instanceof Error ? err.message : String(err) });
78
+ }
79
+ if (onElevation) try {
80
+ await onElevation(event);
67
81
  } catch {
68
82
  log.warn("onElevation callback threw — continuing request");
69
83
  }
@@ -1,11 +1,61 @@
1
1
  import { t as __exportAll } from "./chunk-BpYLSNr0.mjs";
2
- import { p as isArcError } from "./errors-BF2bIOIS.mjs";
2
+ import { p as isArcError } from "./errors-CqWnSqM-.mjs";
3
3
  import fp from "fastify-plugin";
4
4
  //#region src/plugins/errorHandler.ts
5
- var errorHandler_exports = /* @__PURE__ */ __exportAll({ errorHandlerPlugin: () => errorHandlerPlugin });
5
+ var errorHandler_exports = /* @__PURE__ */ __exportAll({
6
+ defaultIsDuplicateKeyError: () => defaultIsDuplicateKeyError,
7
+ errorHandlerPlugin: () => errorHandlerPlugin
8
+ });
9
+ /**
10
+ * Default duplicate-key detector covering the mainstream drivers arc sees
11
+ * most. Detection is strictly by known driver codes — never by message
12
+ * string matching — because false positives on dup-key silently mask real
13
+ * errors (WriteConflict, NotWritablePrimary, etc.) as 409s. For long-tail
14
+ * drivers (Neo4j, MSSQL, DynamoDB, custom kits), compose rather than
15
+ * replace:
16
+ *
17
+ * ```ts
18
+ * import { defaultIsDuplicateKeyError } from '@classytic/arc/plugins';
19
+ *
20
+ * errorHandler: {
21
+ * isDuplicateKeyError: (err) =>
22
+ * defaultIsDuplicateKeyError(err) || isNeo4jDupKey(err),
23
+ * }
24
+ * ```
25
+ *
26
+ * Drizzle apps get coverage transitively (Drizzle doesn't wrap driver
27
+ * errors — pg/mysql2/better-sqlite3 codes propagate as-is). Neon is
28
+ * Postgres-wire-compatible → `23505` covers `@neondatabase/serverless`.
29
+ */
30
+ function defaultIsDuplicateKeyError(err) {
31
+ if (!err || typeof err !== "object") return false;
32
+ const e = err;
33
+ if (e.code === 11e3 || e.codeName === "DuplicateKey") return true;
34
+ if (e.code === "P2002") return true;
35
+ if (e.code === "23505") return true;
36
+ if (e.code === "ER_DUP_ENTRY" || e.errno === 1062) return true;
37
+ if (e.code === "SQLITE_CONSTRAINT_UNIQUE" || e.code === "SQLITE_CONSTRAINT_PRIMARYKEY") return true;
38
+ return false;
39
+ }
40
+ /**
41
+ * Extract the duplicate-field names for the `details.duplicateFields`
42
+ * response. Only called when the caller has opted into detail exposure
43
+ * (`includeStack: true`) — shape differs per driver.
44
+ */
45
+ function extractDuplicateFields(err) {
46
+ if (!err || typeof err !== "object") return null;
47
+ const e = err;
48
+ if (e.keyValue && typeof e.keyValue === "object") return Object.keys(e.keyValue);
49
+ if (e.meta?.target) {
50
+ if (Array.isArray(e.meta.target)) return e.meta.target.map(String);
51
+ if (typeof e.meta.target === "string") return [e.meta.target];
52
+ }
53
+ if (typeof e.constraint === "string") return [e.constraint];
54
+ return null;
55
+ }
6
56
  async function errorHandlerPluginFn(fastify, options = {}) {
7
57
  const isProduction = process.env.NODE_ENV === "production";
8
- const { includeStack = !isProduction, onError, errorMap = {}, errorMappers = [] } = options;
58
+ const { includeStack = !isProduction, onError, errorMap = {}, errorMappers = [], isDuplicateKeyError = defaultIsDuplicateKeyError } = options;
9
59
  fastify.setErrorHandler(async (error, request, reply) => {
10
60
  if (onError) try {
11
61
  await onError(error, request);
@@ -75,12 +125,14 @@ async function errorHandlerPluginFn(fastify, options = {}) {
75
125
  statusCode = 400;
76
126
  response.code = "INVALID_ID";
77
127
  response.error = "Invalid identifier format";
78
- } else if (error.name === "MongoServerError" && error.code === 11e3) {
128
+ } else if (isDuplicateKeyError(error)) {
79
129
  statusCode = 409;
80
130
  response.code = "DUPLICATE_KEY";
81
131
  response.error = "Resource already exists";
82
- const keyValue = error.keyValue;
83
- if (keyValue && includeStack) response.details = { duplicateFields: Object.keys(keyValue) };
132
+ if (includeStack) {
133
+ const duplicateFields = extractDuplicateFields(error);
134
+ if (duplicateFields && duplicateFields.length > 0) response.details = { duplicateFields };
135
+ }
84
136
  }
85
137
  if (includeStack && error.stack) response.stack = error.stack;
86
138
  if (statusCode >= 500) request.log.error({
@@ -118,4 +170,4 @@ const errorHandlerPlugin = fp(errorHandlerPluginFn, {
118
170
  fastify: "5.x"
119
171
  });
120
172
  //#endregion
121
- export { errorHandler_exports as n, errorHandlerPlugin as t };
173
+ export { errorHandlerPlugin as n, errorHandler_exports as r, defaultIsDuplicateKeyError as t };
@@ -1,4 +1,4 @@
1
- import { t as DomainEvent } from "./EventTransport-BXja8NOc.mjs";
1
+ import { n as DomainEvent } from "./EventTransport-CqZ8FyM_.mjs";
2
2
  import { FastifyInstance, FastifyPluginAsync, FastifyRequest } from "fastify";
3
3
 
4
4
  //#region src/plugins/caching.d.ts
@@ -176,8 +176,43 @@ interface ErrorHandlerOptions {
176
176
  * ```
177
177
  */
178
178
  errorMappers?: ErrorMapper[];
179
+ /**
180
+ * Classify an error as a duplicate-key / unique-constraint violation →
181
+ * mapped to `409 Conflict` with `code: "DUPLICATE_KEY"`.
182
+ *
183
+ * Mirrors `RepositoryLike.isDuplicateKeyError` for the Fastify layer: errors
184
+ * that escape a controller (custom routes, user hooks, raw driver calls)
185
+ * still land here, so the classifier is duplicated at the edge. Defaults
186
+ * cover MongoDB (`code 11000` / `codeName "DuplicateKey"`), Prisma
187
+ * (`code "P2002"`), and Postgres (`code "23505"`). Override to add other
188
+ * backends (DynamoDB `ConditionalCheckFailedException`, etc.) or to disable
189
+ * the built-in detection.
190
+ */
191
+ isDuplicateKeyError?: (err: unknown) => boolean;
179
192
  }
193
+ /**
194
+ * Default duplicate-key detector covering the mainstream drivers arc sees
195
+ * most. Detection is strictly by known driver codes — never by message
196
+ * string matching — because false positives on dup-key silently mask real
197
+ * errors (WriteConflict, NotWritablePrimary, etc.) as 409s. For long-tail
198
+ * drivers (Neo4j, MSSQL, DynamoDB, custom kits), compose rather than
199
+ * replace:
200
+ *
201
+ * ```ts
202
+ * import { defaultIsDuplicateKeyError } from '@classytic/arc/plugins';
203
+ *
204
+ * errorHandler: {
205
+ * isDuplicateKeyError: (err) =>
206
+ * defaultIsDuplicateKeyError(err) || isNeo4jDupKey(err),
207
+ * }
208
+ * ```
209
+ *
210
+ * Drizzle apps get coverage transitively (Drizzle doesn't wrap driver
211
+ * errors — pg/mysql2/better-sqlite3 codes propagate as-is). Neon is
212
+ * Postgres-wire-compatible → `23505` covers `@neondatabase/serverless`.
213
+ */
214
+ declare function defaultIsDuplicateKeyError(err: unknown): boolean;
180
215
  declare function errorHandlerPluginFn(fastify: FastifyInstance, options?: ErrorHandlerOptions): Promise<void>;
181
216
  declare const errorHandlerPlugin: typeof errorHandlerPluginFn;
182
217
  //#endregion
183
- export { _default$3 as _, _default as a, MetricsCollector as c, metricsPlugin as d, SSEOptions as f, CachingRule as g, CachingOptions as h, VersioningOptions as i, MetricsOptions as l, ssePlugin as m, ErrorMapper as n, versioningPlugin as o, _default$2 as p, errorHandlerPlugin as r, MetricEntry as s, ErrorHandlerOptions as t, _default$1 as u, cachingPlugin as v };
218
+ export { CachingRule as _, VersioningOptions as a, MetricEntry as c, _default$1 as d, metricsPlugin as f, CachingOptions as g, ssePlugin as h, errorHandlerPlugin as i, MetricsCollector as l, _default$2 as m, ErrorMapper as n, _default as o, SSEOptions as p, defaultIsDuplicateKeyError as r, versioningPlugin as s, ErrorHandlerOptions as t, MetricsOptions as u, _default$3 as v, cachingPlugin as y };
@@ -1,4 +1,4 @@
1
- import { i as EventTransport, n as EventHandler, r as EventLogger, t as DomainEvent } from "./EventTransport-BXja8NOc.mjs";
1
+ import { i as EventLogger, n as DomainEvent, o as EventTransport, r as EventHandler } from "./EventTransport-CqZ8FyM_.mjs";
2
2
  import { FastifyPluginAsync } from "fastify";
3
3
 
4
4
  //#region src/events/defineEvent.d.ts
@@ -123,13 +123,25 @@ interface RetryOptions {
123
123
  * @default 0.1
124
124
  */
125
125
  jitter?: number;
126
+ /**
127
+ * Transport to route dead-lettered events to. When set and the transport
128
+ * implements {@link EventTransport.deadLetter}, exhausted events are
129
+ * auto-wrapped in a {@link DeadLetteredEvent} envelope and routed to the
130
+ * transport's native DLQ (Kafka DLQ topic, SQS DLQ, etc.). No custom
131
+ * plumbing needed for Kafka/SQS/Streams users.
132
+ *
133
+ * Works alongside {@link onDead} — both fire if both are set.
134
+ */
135
+ transport?: Pick<EventTransport, "deadLetter">;
126
136
  /**
127
137
  * Callback when all retries are exhausted. The event is "dead".
128
138
  * Use this to publish to a `$deadLetter` channel, log, alert, etc.
139
+ *
140
+ * Fires in addition to {@link transport} routing if both are set.
129
141
  */
130
142
  onDead?: (event: DomainEvent, errors: Error[]) => void | Promise<void>;
131
143
  /**
132
- * Optional name for logging/debugging.
144
+ * Optional name for logging + written into `DeadLetteredEvent.handlerName`.
133
145
  */
134
146
  name?: string;
135
147
  /**