@classytic/arc 2.33.0 → 2.35.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 (184) hide show
  1. package/dist/{BaseController-CaQC5EXX.mjs → BaseController-C-pofd0v.mjs} +131 -16
  2. package/dist/{EventTransport-tq4CEB-q.mjs → EventTransport-BrPeGUil.mjs} +1 -1
  3. package/dist/{HookSystem-DqXkMXkB.mjs → HookSystem-LlPm3Anm.mjs} +6 -4
  4. package/dist/{ResourceRegistry-DoALUlOO.mjs → ResourceRegistry-yuD1Ra53.mjs} +2 -2
  5. package/dist/{pipeline-q69DZzgG.mjs → actionPermissions-LFuOra7W.mjs} +30 -7
  6. package/dist/audit/index.d.mts +2 -2
  7. package/dist/audit/index.mjs +2 -2
  8. package/dist/auth/audit.mjs +1 -1
  9. package/dist/auth/index.d.mts +2 -2
  10. package/dist/auth/index.mjs +6 -6
  11. package/dist/auth/redis-session.mjs +1 -1
  12. package/dist/{authorizationDecision-B3mP_QTy.mjs → authorizationDecision-DYXBBgtg.mjs} +6 -2
  13. package/dist/{base-Csjc2oF-.d.mts → base-GZU3LlzP.d.mts} +1 -1
  14. package/dist/{buildHandler-DLE9xX-V.mjs → buildHandler-Do9aMIvL.mjs} +4 -4
  15. package/dist/cache/index.mjs +4 -4
  16. package/dist/{caching-BcGmvnHh.mjs → caching-cN5X9VEL.mjs} +2 -2
  17. package/dist/cleanup/index.d.mts +2 -2
  18. package/dist/cleanup/index.mjs +4 -4
  19. package/dist/cli/commands/describe.d.mts +1 -1
  20. package/dist/cli/commands/describe.mjs +3 -3
  21. package/dist/cli/commands/docs.mjs +3 -3
  22. package/dist/cli/commands/doctor.mjs +1 -1
  23. package/dist/cli/commands/generate.mjs +2 -2
  24. package/dist/cli/commands/init.mjs +2 -2
  25. package/dist/cli/commands/introspect.mjs +2 -2
  26. package/dist/cli/index.mjs +3 -3
  27. package/dist/{constants-BxCoxmiN.mjs → constants-BhzLXCfN.mjs} +1 -1
  28. package/dist/context/index.d.mts +16 -1
  29. package/dist/context/index.mjs +64 -4
  30. package/dist/core/index.d.mts +3 -3
  31. package/dist/core/index.mjs +8 -8
  32. package/dist/{core-dxhcHhUD.mjs → core-DdEvmmkY.mjs} +3 -3
  33. package/dist/{createActionRouter-DVpoA0do.mjs → createActionRouter-EYWgd0ly.mjs} +27 -64
  34. package/dist/{createAggregationRouter-B_B48JZ-.mjs → createAggregationRouter-ByOK9Iev.mjs} +7 -7
  35. package/dist/defineAggregation-DoIijIFq.mjs +25 -0
  36. package/dist/{defineResource-DR9vsUYy.mjs → defineResource-BOrGeocE.mjs} +62 -66
  37. package/dist/discovery/index.mjs +1 -1
  38. package/dist/docs/index.d.mts +1 -1
  39. package/dist/docs/index.mjs +3 -3
  40. package/dist/{elevation-B0tlY8y1.mjs → elevation-tGIAldco.mjs} +4 -4
  41. package/dist/encryption/index.mjs +3 -3
  42. package/dist/{errorHandler-C7xBqHsI.mjs → errorHandler-BbQ1nQdk.mjs} +3 -3
  43. package/dist/{errors-Cd_yy7aW.mjs → errors-CLid6atr.mjs} +1 -1
  44. package/dist/{eventPlugin-DSNEZWfV.d.mts → eventPlugin-B47sfCJ8.d.mts} +1 -1
  45. package/dist/{eventPlugin-Dar8qRaj.mjs → eventPlugin-ClPn5kjn.mjs} +5 -5
  46. package/dist/events/index.d.mts +2 -2
  47. package/dist/events/index.mjs +5 -5
  48. package/dist/events/transports/redis-stream-entry.mjs +1 -1
  49. package/dist/events/transports/redis.mjs +1 -1
  50. package/dist/factory/index.d.mts +5 -5
  51. package/dist/factory/index.mjs +5 -6
  52. package/dist/{fastify-augmentation-DVsTAy6X.d.mts → fastify-augmentation-CMMTL3cj.d.mts} +3 -3
  53. package/dist/{fields-CojeUkS4.mjs → fields-UtpiJO5R.mjs} +1 -1
  54. package/dist/{generate-DiB0nHPR.mjs → generate-BoAVy3_b.mjs} +2 -2
  55. package/dist/{headers-CveNO9gR.mjs → headers-DODWf7aC.mjs} +2 -2
  56. package/dist/{health-CasG2A28.mjs → health-_Zs3R_Sb.mjs} +1 -1
  57. package/dist/hooks/index.d.mts +1 -1
  58. package/dist/hooks/index.mjs +2 -2
  59. package/dist/idempotency/index.mjs +23 -58
  60. package/dist/idempotency/redis.mjs +1 -1
  61. package/dist/{index-C5NnMEKF.d.mts → index-C50BUxKs.d.mts} +1 -1
  62. package/dist/{index-BcR2bFZQ.d.mts → index-C5K78SXo.d.mts} +33 -105
  63. package/dist/{index-ugr1tQlW.d.mts → index-CJCcbADG.d.mts} +1 -1
  64. package/dist/{index-B-DvUzsr.d.mts → index-K7auNNW0.d.mts} +2 -2
  65. package/dist/{index-Brin0ODU.d.mts → index-h-mTgMqy.d.mts} +21 -31
  66. package/dist/index.d.mts +6 -6
  67. package/dist/index.mjs +12 -12
  68. package/dist/{init-mdJ-v4Xp.mjs → init-Ce4R0ZGK.mjs} +1 -1
  69. package/dist/integrations/event-gateway.mjs +2 -2
  70. package/dist/integrations/index.d.mts +2 -2
  71. package/dist/integrations/index.mjs +1 -1
  72. package/dist/integrations/jobs/index.d.mts +1 -1
  73. package/dist/integrations/jobs/index.mjs +2 -2
  74. package/dist/integrations/mcp/index.d.mts +4 -4
  75. package/dist/integrations/mcp/index.mjs +2 -2
  76. package/dist/integrations/mcp/testing.d.mts +1 -1
  77. package/dist/integrations/mcp/testing.mjs +3 -3
  78. package/dist/integrations/streamline.mjs +3 -3
  79. package/dist/integrations/webhooks.d.mts +1 -1
  80. package/dist/integrations/webhooks.mjs +11 -24
  81. package/dist/integrations/websocket-pushref-redis.mjs +1 -1
  82. package/dist/integrations/websocket-redis.mjs +1 -1
  83. package/dist/integrations/websocket.mjs +1 -1
  84. package/dist/{interface-DznJrm9R.mjs → interface-my7sxjw5.mjs} +1 -1
  85. package/dist/{jsonBody-CfGH-GxO.mjs → jsonBody-CVKA7C5m.mjs} +2 -2
  86. package/dist/{keys-WeKvY1dE.mjs → keys-GlU7XpmG.mjs} +1 -1
  87. package/dist/{loadResources-Bj-aSsXi.mjs → loadResources-DdQb0qgV.mjs} +2 -2
  88. package/dist/{loadResourcesFromEntry-DwaxNcE2.mjs → loadResourcesFromEntry-s6aGs6-0.mjs} +2 -2
  89. package/dist/logger/index.d.mts +23 -4
  90. package/dist/logger/index.mjs +50 -7
  91. package/dist/{mcpPlugin-aJg5TqHi.mjs → mcpPlugin-DkzZ7Nfx.mjs} +15 -16
  92. package/dist/{memory-qYSB7rMQ.mjs → memory-BwedEXau.mjs} +2 -2
  93. package/dist/{metrics-D7uchLjN.mjs → metrics-5LNPvL3P.mjs} +2 -2
  94. package/dist/middleware/index.d.mts +1 -1
  95. package/dist/middleware/index.mjs +3 -3
  96. package/dist/migrations/index.mjs +7 -4
  97. package/dist/{module-CwKsgZXp.mjs → module-Bh5OC7ww.mjs} +1 -1
  98. package/dist/{multipartBody-4w-S3-PU.mjs → multipartBody-DjilSDAT.mjs} +1 -1
  99. package/dist/{openapi-BN5cQaO2.mjs → openapi-ItLH12_L.mjs} +6 -6
  100. package/dist/outbox-admin/index.d.mts +2 -2
  101. package/dist/outbox-admin/index.mjs +4 -4
  102. package/dist/{outbox-module-CXB79bvt.mjs → outbox-module-SGIZz6oS.mjs} +14 -5
  103. package/dist/{outbox-module-D-VFq37T.d.mts → outbox-module-b1TyDGGi.d.mts} +40 -3
  104. package/dist/permissions/index.d.mts +2 -2
  105. package/dist/permissions/index.mjs +5 -5
  106. package/dist/{permissions-BAfwgBxc.mjs → permissions-CpSBlFXZ.mjs} +6 -6
  107. package/dist/{pipe-BzzoEhHj.mjs → pipe-DNOuCfAs.mjs} +2 -2
  108. package/dist/pipeline/index.d.mts +1 -1
  109. package/dist/pipeline/index.mjs +2 -2
  110. package/dist/plugins/index.d.mts +92 -7
  111. package/dist/plugins/index.mjs +168 -17
  112. package/dist/plugins/response-cache.mjs +2 -2
  113. package/dist/plugins/tracing-entry.mjs +2 -2
  114. package/dist/{pluralize-B7xf8qZF.mjs → pluralize-DGAQQKL8.mjs} +1 -1
  115. package/dist/{preloadResources-g07ldtzh.d.mts → preloadResources--ZtY9pe9.d.mts} +54 -270
  116. package/dist/{createApp-CLGegqiG.mjs → preloadResources-DYMzmUk1.mjs} +112 -31
  117. package/dist/presets/filesUpload.d.mts +3 -3
  118. package/dist/presets/filesUpload.mjs +5 -5
  119. package/dist/presets/index.d.mts +2 -2
  120. package/dist/presets/index.mjs +2 -2
  121. package/dist/presets/multiTenant.d.mts +1 -1
  122. package/dist/presets/multiTenant.mjs +3 -3
  123. package/dist/presets/search.d.mts +9 -47
  124. package/dist/presets/search.mjs +2 -2
  125. package/dist/{presets-D-h0uWtP.mjs → presets-Bi0Z6Iq8.mjs} +4 -4
  126. package/dist/{projection-BKM0Ui-s.mjs → projection-aevMua7Q.mjs} +2 -2
  127. package/dist/{queryCachePlugin-DhsZ0ZIc.mjs → queryCachePlugin-BI7Z88Cx.mjs} +5 -5
  128. package/dist/{rateLimitKey-XngqVNEV.mjs → rateLimitKey-pyevMUVf.mjs} +1 -1
  129. package/dist/{realtime-CTkv3O59.mjs → realtime-Cfl2bVjj.mjs} +9 -9
  130. package/dist/{rateLimit-D4-ASwsI.mjs → registerRoute-u1opU66a.mjs} +114 -3
  131. package/dist/registry/index.d.mts +2 -2
  132. package/dist/registry/index.mjs +3 -3
  133. package/dist/{registry-CLxCR5LF.mjs → registry-B4sf9Rfm.mjs} +15 -56
  134. package/dist/{renewingLease-BnWtbA2U.mjs → renewingLease-5ESPMxp0.mjs} +14 -2
  135. package/dist/{replyHelpers-BSBVCwL3.mjs → replyHelpers-2jfmMnnV.mjs} +2 -2
  136. package/dist/{requestContext-uYE35JxI.mjs → requestContext-DzOUGNdY.mjs} +1 -1
  137. package/dist/{rolldown-runtime-DNg-KNCk.mjs → rolldown-runtime-CEvvVXTC.mjs} +1 -1
  138. package/dist/{runtimeCapabilities-Dm88DaTW.mjs → runtimeCapabilities-BagWc3TI.mjs} +1 -1
  139. package/dist/{schedules-CAcZgia0.mjs → schedules-BLpZLWGF.mjs} +6 -4
  140. package/dist/{schemaConverter-XH1RZRyu.mjs → schemaConverter-D2_bHGil.mjs} +19 -32
  141. package/dist/{schemaIR-CDvclAew.mjs → schemaIR-s3-ouZmP.mjs} +2 -2
  142. package/dist/schemas/index.mjs +1 -1
  143. package/dist/scim/index.mjs +2 -2
  144. package/dist/scope/index.d.mts +1 -1
  145. package/dist/scope/index.mjs +5 -5
  146. package/dist/{scope-BaWcqBvr.mjs → scope-DgepeCv6.mjs} +4 -4
  147. package/dist/simpleEqualityMatcher-BO-sFTQf.mjs +43 -0
  148. package/dist/{sse-Cf0jUHsh.mjs → sse-DUIP1XH6.mjs} +5 -5
  149. package/dist/{sseStream-Bvpi-UXQ.mjs → sseStream-ByFGyIVr.mjs} +2 -2
  150. package/dist/{store-helpers-B88p99Z2.mjs → store-helpers-kIJCY0wI.mjs} +1 -1
  151. package/dist/{streaming-DsT1B5zJ.mjs → streaming-DOpZVht6.mjs} +1 -1
  152. package/dist/{subscribe-helpers-CLIOHsOK.mjs → subscribe-helpers-Dh2BnzJf.mjs} +12 -46
  153. package/dist/sync/changelog.mjs +1 -1
  154. package/dist/testing/cleanupStoreContract.mjs +1 -1
  155. package/dist/testing/index.d.mts +6 -6
  156. package/dist/testing/index.mjs +10 -11
  157. package/dist/testing/outboxStoreContract.mjs +1 -1
  158. package/dist/testing/storageContract.mjs +1 -1
  159. package/dist/{transactionContext-BYtWnwAS.mjs → transactionContext-BTZzEcRo.mjs} +1 -1
  160. package/dist/{typeGuards-jjKJDN13.mjs → typeGuards-B-_jqaMi.mjs} +1 -1
  161. package/dist/types/index.d.mts +3 -3
  162. package/dist/types/index.mjs +1 -1
  163. package/dist/types/storage.mjs +1 -1
  164. package/dist/{types-BcTumAz_.d.mts → types-DDD5RKTu.d.mts} +30 -87
  165. package/dist/{types-BfC83KsP.d.mts → types-DSvHH0uR.d.mts} +1 -1
  166. package/dist/{types-ChuShpCP.d.mts → types-Ddl9OjmE.d.mts} +18 -121
  167. package/dist/{types-CoqIRrkY.mjs → types-GY4kATJb.mjs} +10 -53
  168. package/dist/usage/index.d.mts +2 -2
  169. package/dist/usage/index.mjs +0 -0
  170. package/dist/{userHelpers-CWF5pbZD.mjs → userHelpers-AKcB4V03.mjs} +1 -1
  171. package/dist/utils/index.d.mts +16 -42
  172. package/dist/utils/index.mjs +11 -11
  173. package/dist/{utils-h1V_sT-t.mjs → utils-CBK9HQOI.mjs} +3 -3
  174. package/dist/{validate-CdQlz52F.mjs → validate-dosAIvQU.mjs} +2 -2
  175. package/dist/{versioning-rZbyLVD-.mjs → versioning-BbTs4pwM.mjs} +3 -3
  176. package/package.json +8 -8
  177. package/skills/arc/SKILL.md +9 -9
  178. package/skills/arc/references/api-reference.md +1 -1
  179. package/dist/actionPermissions-CUeCmpUd.mjs +0 -25
  180. package/dist/defineAggregation-Nv-DwfOf.mjs +0 -48
  181. package/dist/preloadResources-BkyqeTDz.mjs +0 -46
  182. package/dist/registerRoute-DNnd3vkz.mjs +0 -113
  183. package/dist/repositoryFilter-DKny7PDn.mjs +0 -45
  184. package/dist/simpleEqualityMatcher-Se8VrsaP.mjs +0 -69
@@ -1,12 +1,12 @@
1
- /*! @classytic/arc v2.34.0 | MIT | https://classytic.com */
2
- import { t as __exportAll } from "./rolldown-runtime-DNg-KNCk.mjs";
3
- import { arcLog, configureArcLogger, createPinoWriter } from "./logger/index.mjs";
4
- import { n as PUBLIC_SCOPE } from "./types-CoqIRrkY.mjs";
5
- import { t as auditRuntimeCapabilities } from "./runtimeCapabilities-Dm88DaTW.mjs";
6
- import { t as createTenantKeyGenerator } from "./rateLimitKey-XngqVNEV.mjs";
7
- import { i as createRequestIdGenerator, r as mergeHealthChecks } from "./health-CasG2A28.mjs";
8
- import { t as parseJsonBody } from "./jsonBody-CfGH-GxO.mjs";
9
- import { r as wrapWithBoundary } from "./subscribe-helpers-CLIOHsOK.mjs";
1
+ /*! @classytic/arc v2.35.0 | MIT | https://classytic.com */
2
+ import { t as __exportAll } from "./rolldown-runtime-CEvvVXTC.mjs";
3
+ import { arcLog, configureArcLogger, createPinoWriter, enterArcLoggerScope, runWithArcLogger } from "./logger/index.mjs";
4
+ import { n as PUBLIC_SCOPE } from "./types-GY4kATJb.mjs";
5
+ import { t as auditRuntimeCapabilities } from "./runtimeCapabilities-BagWc3TI.mjs";
6
+ import { t as createTenantKeyGenerator } from "./rateLimitKey-pyevMUVf.mjs";
7
+ import { i as createRequestIdGenerator, r as mergeHealthChecks } from "./health-_Zs3R_Sb.mjs";
8
+ import { t as parseJsonBody } from "./jsonBody-CVKA7C5m.mjs";
9
+ import { r as wrapWithBoundary } from "./subscribe-helpers-Dh2BnzJf.mjs";
10
10
  import { isProductionEnv } from "@classytic/primitives/environment";
11
11
  import Fastify from "fastify";
12
12
  import qs from "qs";
@@ -801,7 +801,7 @@ async function registerArcCore(fastify, config, trackPlugin) {
801
801
  await fastify.register(arcCorePlugin, { emitEvents: config.arcPlugins?.emitEvents !== false });
802
802
  trackPlugin("arc-core");
803
803
  if (config.arcPlugins?.events !== false) {
804
- const { default: eventPlugin } = await import("./eventPlugin-Dar8qRaj.mjs").then((n) => n.n);
804
+ const { default: eventPlugin } = await import("./eventPlugin-ClPn5kjn.mjs").then((n) => n.n);
805
805
  const eventOpts = typeof config.arcPlugins?.events === "object" ? config.arcPlugins.events : {};
806
806
  await fastify.register(eventPlugin, {
807
807
  /**
@@ -860,15 +860,15 @@ async function registerArcPlugins(fastify, config, trackPlugin, modules, moduleH
860
860
  trackPlugin("arc-graceful-shutdown");
861
861
  }
862
862
  if (config.arcPlugins?.caching) {
863
- const { default: cachingPlugin } = await import("./caching-BcGmvnHh.mjs").then((n) => n.r);
863
+ const { default: cachingPlugin } = await import("./caching-cN5X9VEL.mjs").then((n) => n.r);
864
864
  const opts = config.arcPlugins.caching === true ? {} : config.arcPlugins.caching;
865
865
  await fastify.register(cachingPlugin, opts);
866
866
  trackPlugin("arc-caching", opts);
867
867
  }
868
868
  if (config.arcPlugins?.queryCache) {
869
- const { queryCachePlugin } = await import("./queryCachePlugin-DhsZ0ZIc.mjs").then((n) => n.n);
869
+ const { queryCachePlugin } = await import("./queryCachePlugin-BI7Z88Cx.mjs").then((n) => n.n);
870
870
  const opts = config.arcPlugins.queryCache === true ? {} : config.arcPlugins.queryCache;
871
- const store = config.stores?.queryCache ?? new (await (import("./memory-qYSB7rMQ.mjs").then((n) => n.n))).MemoryCacheStore();
871
+ const store = config.stores?.queryCache ?? new (await (import("./memory-BwedEXau.mjs").then((n) => n.n))).MemoryCacheStore();
872
872
  await fastify.register(queryCachePlugin, {
873
873
  store,
874
874
  ...opts
@@ -878,7 +878,7 @@ async function registerArcPlugins(fastify, config, trackPlugin, modules, moduleH
878
878
  if (config.arcPlugins?.sse) {
879
879
  if (config.arcPlugins?.events === false) fastify.log.warn("SSE plugin requires events plugin (arcPlugins.events). SSE disabled.");
880
880
  else {
881
- const { default: ssePlugin } = await import("./sse-Cf0jUHsh.mjs").then((n) => n.r);
881
+ const { default: ssePlugin } = await import("./sse-DUIP1XH6.mjs").then((n) => n.r);
882
882
  const opts = config.arcPlugins.sse === true ? {} : config.arcPlugins.sse;
883
883
  await fastify.register(ssePlugin, opts);
884
884
  trackPlugin("arc-sse", opts);
@@ -887,20 +887,20 @@ async function registerArcPlugins(fastify, config, trackPlugin, modules, moduleH
887
887
  if (config.arcPlugins?.realtime) {
888
888
  if (config.arcPlugins?.events === false) fastify.log.warn("Realtime plugin requires events plugin (arcPlugins.events). Realtime disabled.");
889
889
  else {
890
- const { default: realtimePlugin } = await import("./realtime-CTkv3O59.mjs").then((n) => n.r);
890
+ const { default: realtimePlugin } = await import("./realtime-Cfl2bVjj.mjs").then((n) => n.r);
891
891
  const opts = config.arcPlugins.realtime === true ? {} : config.arcPlugins.realtime;
892
892
  await fastify.register(realtimePlugin, opts);
893
893
  trackPlugin("arc-realtime", opts);
894
894
  }
895
895
  }
896
896
  if (config.arcPlugins?.metrics) {
897
- const { default: metricsPlugin } = await import("./metrics-D7uchLjN.mjs").then((n) => n.r);
897
+ const { default: metricsPlugin } = await import("./metrics-5LNPvL3P.mjs").then((n) => n.r);
898
898
  const opts = config.arcPlugins.metrics === true ? {} : config.arcPlugins.metrics;
899
899
  await fastify.register(metricsPlugin, opts);
900
900
  trackPlugin("arc-metrics", opts);
901
901
  }
902
902
  if (config.arcPlugins?.versioning) {
903
- const { default: versioningPlugin } = await import("./versioning-rZbyLVD-.mjs").then((n) => n.r);
903
+ const { default: versioningPlugin } = await import("./versioning-BbTs4pwM.mjs").then((n) => n.r);
904
904
  await fastify.register(versioningPlugin, config.arcPlugins.versioning);
905
905
  trackPlugin("arc-versioning", config.arcPlugins.versioning);
906
906
  }
@@ -977,7 +977,7 @@ async function registerAuth(fastify, config, trackPlugin) {
977
977
  */
978
978
  async function registerElevation(fastify, config, trackPlugin) {
979
979
  if (!config.elevation) return;
980
- const { elevationPlugin } = await import("./elevation-B0tlY8y1.mjs").then((n) => n.r);
980
+ const { elevationPlugin } = await import("./elevation-tGIAldco.mjs").then((n) => n.r);
981
981
  await fastify.register(elevationPlugin, config.elevation);
982
982
  trackPlugin("arc-elevation", config.elevation);
983
983
  fastify.log.debug("Elevation plugin enabled");
@@ -992,7 +992,7 @@ async function registerElevation(fastify, config, trackPlugin) {
992
992
  */
993
993
  async function registerErrorHandler(fastify, config, trackPlugin, moduleErrorMappers) {
994
994
  if (config.errorHandler === false) return;
995
- const { errorHandlerPlugin } = await import("./errorHandler-C7xBqHsI.mjs").then((n) => n.r);
995
+ const { errorHandlerPlugin } = await import("./errorHandler-BbQ1nQdk.mjs").then((n) => n.r);
996
996
  /**
997
997
  * DISCLOSURE DEFAULTS MERGE — they used to be REPLACED, and that was two bugs.
998
998
  *
@@ -1216,7 +1216,7 @@ async function registerResources(fastify, config, preResolvedModules) {
1216
1216
  let discoveryPath;
1217
1217
  let discoveryYieldedZero = false;
1218
1218
  if (resolvedResources === void 0 && config.resourceDir) {
1219
- const { loadResources } = await import("./loadResources-Bj-aSsXi.mjs").then((n) => n.n);
1219
+ const { loadResources } = await import("./loadResources-DdQb0qgV.mjs").then((n) => n.n);
1220
1220
  const { resolve, dirname } = await import("node:path");
1221
1221
  const { fileURLToPath } = await import("node:url");
1222
1222
  const rawDir = config.resourceDir;
@@ -1342,7 +1342,7 @@ Resources supplied by "${m.name}": ${provided}.`);
1342
1342
  if (fastify.hasDecorator("getScheduleStats")) throw new Error("[arc] modules declare scheduledJobs but schedulesPlugin was already registered manually. Configure their runner through arcPlugins.schedules so Arc can compose one schedule table.");
1343
1343
  const frozenJobs = Object.freeze(scheduledJobs.map((job) => Object.freeze({ ...job })));
1344
1344
  const scheduleOptions = typeof config.arcPlugins?.schedules === "object" ? config.arcPlugins.schedules : {};
1345
- const { default: schedulesPlugin } = await import("./schedules-CAcZgia0.mjs").then((n) => n.r);
1345
+ const { default: schedulesPlugin } = await import("./schedules-BLpZLWGF.mjs").then((n) => n.r);
1346
1346
  await fastify.register(schedulesPlugin, {
1347
1347
  ...scheduleOptions,
1348
1348
  schedules: frozenJobs
@@ -1696,7 +1696,7 @@ const MEMORY_STORE_NAMES = /* @__PURE__ */ new Set(["memory", "memory-cache"]);
1696
1696
  * Hosts that need NO redaction (test fixtures, security audits) opt out
1697
1697
  * with `logger: { redact: [] }`. Hosts that need MORE redaction supply
1698
1698
  * their own `redact` and arc steps aside — this default never overrides
1699
- * an explicit setting. (2.15.1)
1699
+ * an explicit setting.
1700
1700
  */
1701
1701
  const DEFAULT_LOGGER_REDACT_PATHS = [
1702
1702
  "req.headers.authorization",
@@ -1813,11 +1813,22 @@ async function createApp(options) {
1813
1813
  */
1814
1814
  const declaredRole = options.role ?? "all";
1815
1815
  if ((declaredRole === "relay" || declaredRole === "scheduler") && (options.resources !== void 0 || options.resourceDir !== void 0)) throw new Error(`createApp: role '${declaredRole}' declares HTTP resources — a ${declaredRole} process must not serve routes. Remove \`resources\`/\`resourceDir\`, or change the role.`);
1816
+ /**
1817
+ * This app's own `arcLog` config, entered for the WHOLE boot.
1818
+ *
1819
+ * Held by reference and mutated in `buildApp` once `fastify.log` exists:
1820
+ * boot starts before Fastify does, so a copied snapshot would send every
1821
+ * post-construction boot warning to the console instead of this app's
1822
+ * logger. Scoping it per app is what stops two apps in one process from
1823
+ * overwriting each other's writer — see `src/logger/index.ts`.
1824
+ */
1825
+ const loggerScope = {};
1826
+ if (options.debug !== void 0 && options.debug !== false) loggerScope.debug = options.debug;
1816
1827
  let partial;
1817
1828
  try {
1818
- return await buildApp(options, (instance) => {
1829
+ return await runWithArcLogger(loggerScope, () => buildApp(options, loggerScope, (instance) => {
1819
1830
  partial = instance;
1820
- });
1831
+ }));
1821
1832
  } catch (err) {
1822
1833
  if (partial) try {
1823
1834
  await partial.close();
@@ -1827,8 +1838,7 @@ async function createApp(options) {
1827
1838
  throw err;
1828
1839
  }
1829
1840
  }
1830
- async function buildApp(options, onInstance) {
1831
- if (options.debug !== void 0 && options.debug !== false) configureArcLogger({ debug: options.debug });
1841
+ async function buildApp(options, loggerScope, onInstance) {
1832
1842
  validateAuthOptions(options);
1833
1843
  const deferredWarnings = validateDistributedRuntime(options);
1834
1844
  if (options.beforeBoot) await options.beforeBoot();
@@ -1837,6 +1847,25 @@ async function buildApp(options, onInstance) {
1837
1847
  ...options
1838
1848
  };
1839
1849
  const inheritedTrustProxyDefault = options.preset === "production" && options.trustProxy === void 0;
1850
+ /**
1851
+ * A NUMERIC `trustProxy` is a no-op on Fastify >= 5.12.1.
1852
+ *
1853
+ * Fastify made hop-count trust fail-closed on purpose — the comment in
1854
+ * `lib/request.js` is explicit: "Hop-count-only trust cannot validate the
1855
+ * immediate peer. Fail closed so direct clients cannot spoof X-Forwarded-*
1856
+ * values by supplying enough hops." So `trustProxy: 1` now trusts NOTHING.
1857
+ *
1858
+ * That is the right call upstream and the wrong thing to inherit silently:
1859
+ * `request.ip` becomes the proxy's address, which is the exact failure the
1860
+ * production-preset warning below describes — rate-limit keys collapse onto
1861
+ * one bucket and audit rows record the wrong client. The host asked for
1862
+ * proxy trust and got none, with no error.
1863
+ *
1864
+ * Warn rather than refuse: the app still serves, and a hard boot failure on
1865
+ * a transitive `fastify` bump would be a harsher surprise than the one being
1866
+ * reported. The replacement is named so the fix is a copy-paste.
1867
+ */
1868
+ const numericTrustProxy = typeof config.trustProxy === "number";
1840
1869
  const resolvedModules = orderModules(await Promise.all((config.modules ?? []).map((m) => resolveModule(m))));
1841
1870
  const moduleErrorMappers = resolvedModules.flatMap((m) => m.errorMappers ?? []);
1842
1871
  const moduleHealthChecks = collectModuleHealthChecks(resolvedModules);
@@ -1869,11 +1898,19 @@ async function buildApp(options, onInstance) {
1869
1898
  } }
1870
1899
  });
1871
1900
  onInstance(fastify);
1872
- if (resolvedLogger !== false) configureArcLogger({
1873
- ...options.debug !== void 0 && options.debug !== false ? { debug: options.debug } : {},
1874
- writer: createPinoWriter(fastify.log)
1901
+ if (resolvedLogger !== false) {
1902
+ const writer = createPinoWriter(fastify.log);
1903
+ loggerScope.writer = writer;
1904
+ configureArcLogger({
1905
+ ...options.debug !== void 0 && options.debug !== false ? { debug: options.debug } : {},
1906
+ writer
1907
+ });
1908
+ }
1909
+ fastify.addHook("onRequest", (_request, _reply, done) => {
1910
+ enterArcLoggerScope(loggerScope, done);
1875
1911
  });
1876
1912
  for (const warning of deferredWarnings) fastify.log.warn(warning);
1913
+ if (numericTrustProxy) fastify.log.warn(`[arc] trustProxy: ${String(config.trustProxy)} (a hop COUNT) trusts nothing on Fastify >= 5.12.1 — hop-count trust cannot validate the immediate peer, so it fails closed upstream to stop clients spoofing X-Forwarded-* with padded hops. \`request.ip\` is therefore the PROXY's address: rate-limit keys collapse onto one bucket and audit rows record the wrong client. Name the proxies instead — trustProxy: 'loopback', '10.0.0.0/8', a comma-separated list, or \`true\` if every upstream hop is genuinely trusted.`);
1877
1914
  if (inheritedTrustProxyDefault) fastify.log.warn("[arc] preset: 'production' now defaults to trustProxy: false (fail-closed; was true before 2.24). If this app runs behind a load balancer or reverse proxy, request.ip is currently the PROXY's address — rate-limit keys collapse onto one bucket and audit logs record the wrong client. Set trustProxy explicitly: a hop count (trustProxy: 1), a CIDR allow-list ('10.0.0.0/8'), a resolver function, or false if clients truly connect directly.");
1878
1915
  fastify.removeContentTypeParser("application/json");
1879
1916
  fastify.addContentTypeParser("application/json", { parseAs: "string" }, (_req, body, done) => {
@@ -1907,7 +1944,7 @@ async function buildApp(options, onInstance) {
1907
1944
  await registerErrorHandler(fastify, config, trackPlugin, moduleErrorMappers);
1908
1945
  await registerResources(fastify, config, resolvedModules);
1909
1946
  if (config.replyHelpers) {
1910
- const { replyHelpersPlugin } = await import("./replyHelpers-BSBVCwL3.mjs").then((n) => n.n);
1947
+ const { replyHelpersPlugin } = await import("./replyHelpers-2jfmMnnV.mjs").then((n) => n.n);
1911
1948
  await fastify.register(replyHelpersPlugin);
1912
1949
  }
1913
1950
  if (config.serializeBigInt) {
@@ -1959,4 +1996,48 @@ const ArcFactory = {
1959
1996
  }
1960
1997
  };
1961
1998
  //#endregion
1962
- export { collectModuleScheduledJobs as _, developmentPreset as a, resolveModule as b, productionPreset as c, getModuleState as d, hasModule as f, collectModuleHealthChecks as g, describeResolvedModule as h, createApp_exports as i, testingPreset as l, subscribeModuleEventHandlers as m, DEFAULT_LOGGER_REDACT_PATHS as n, edgePreset as o, orderModules as p, createApp as r, getPreset as s, ArcFactory as t, workerPreset as u, collectModuleWorkflows as v, resolveContribution as y };
1999
+ //#region src/factory/preloadResources.ts
2000
+ /**
2001
+ * Normalize an eager `import.meta.glob` result into a `ResourceLike[]`.
2002
+ *
2003
+ * Accepts either:
2004
+ * - `{ import: 'default' }` form: values are the resource directly
2005
+ * - default form: values are the full module — picks first export with `toPlugin()`
2006
+ *
2007
+ * Throws if any module doesn't yield a valid `ResourceLike`.
2008
+ */
2009
+ function preloadResources(globResult) {
2010
+ const resources = [];
2011
+ for (const [path, value] of Object.entries(globResult)) {
2012
+ const resource = pickResource(value);
2013
+ if (!resource) throw new Error(`preloadResources: ${path} does not export a valid resource.\n Expected: a default export OR a named export with toPlugin().`);
2014
+ resources.push(resource);
2015
+ }
2016
+ return resources.sort((a, b) => (a.name ?? "").localeCompare(b.name ?? ""));
2017
+ }
2018
+ /**
2019
+ * Normalize a lazy `import.meta.glob` result into a `Promise<ResourceLike[]>`.
2020
+ *
2021
+ * Use this when resources depend on prior bootstrap (e.g., engine init) and
2022
+ * cannot be evaluated at import time of the preload file.
2023
+ */
2024
+ async function preloadResourcesAsync(globResult) {
2025
+ return (await Promise.all(Object.entries(globResult).map(async ([path, loader]) => {
2026
+ const resource = pickResource(await loader());
2027
+ if (!resource) throw new Error(`preloadResourcesAsync: ${path} does not export a valid resource.\n Expected: a default export OR a named export with toPlugin().`);
2028
+ return resource;
2029
+ }))).sort((a, b) => (a.name ?? "").localeCompare(b.name ?? ""));
2030
+ }
2031
+ function pickResource(value) {
2032
+ if (!value || typeof value !== "object") return void 0;
2033
+ if (typeof value.toPlugin === "function") return value;
2034
+ const mod = value;
2035
+ const candidates = [
2036
+ mod.default,
2037
+ mod.resource,
2038
+ ...Object.values(mod)
2039
+ ];
2040
+ for (const c of candidates) if (c && typeof c === "object" && typeof c.toPlugin === "function") return c;
2041
+ }
2042
+ //#endregion
2043
+ export { resolveModule as S, describeResolvedModule as _, createApp as a, collectModuleWorkflows as b, edgePreset as c, testingPreset as d, workerPreset as f, subscribeModuleEventHandlers as g, orderModules as h, DEFAULT_LOGGER_REDACT_PATHS as i, getPreset as l, hasModule as m, preloadResourcesAsync as n, createApp_exports as o, getModuleState as p, ArcFactory as r, developmentPreset as s, preloadResources as t, productionPreset as u, collectModuleHealthChecks as v, resolveContribution as x, collectModuleScheduledJobs as y };
@@ -1,7 +1,7 @@
1
1
 
2
- import { i as RequestScope } from "../types-ChuShpCP.mjs";
3
- import { l as PermissionCheck } from "../base-Csjc2oF-.mjs";
4
- import { Y as PresetResult } from "../index-BcR2bFZQ.mjs";
2
+ import { i as RequestScope } from "../types-Ddl9OjmE.mjs";
3
+ import { l as PermissionCheck } from "../base-GZU3LlzP.mjs";
4
+ import { Y as PresetResult } from "../index-C5K78SXo.mjs";
5
5
  import { a as StorageReadResult, i as StorageReadRange, n as StorageContext, o as StorageUploadInput, r as StorageFile, t as Storage } from "../storage-Cx9Sz3Ou.mjs";
6
6
  //#region src/presets/filesUpload.d.ts
7
7
  interface FilesUploadPresetRoutes {
@@ -1,8 +1,8 @@
1
- /*! @classytic/arc v2.34.0 | MIT | https://classytic.com */
2
- import { i as NotFoundError, u as ValidationError } from "../errors-Cd_yy7aW.mjs";
3
- import { c as getOrgId, g as getUserId } from "../types-CoqIRrkY.mjs";
4
- import { B as requireAuth, F as allowPublic } from "../permissions-BAfwgBxc.mjs";
5
- import { t as multipartBody } from "../multipartBody-4w-S3-PU.mjs";
1
+ /*! @classytic/arc v2.35.0 | MIT | https://classytic.com */
2
+ import { i as NotFoundError, u as ValidationError } from "../errors-CLid6atr.mjs";
3
+ import { c as getOrgId, g as getUserId } from "../types-GY4kATJb.mjs";
4
+ import { B as requireAuth, F as allowPublic } from "../permissions-CpSBlFXZ.mjs";
5
+ import { t as multipartBody } from "../multipartBody-DjilSDAT.mjs";
6
6
  //#region src/presets/filesUpload.ts
7
7
  const DEFAULT_FIELD_NAME = "file";
8
8
  const DEFAULT_MAX_FILE_SIZE = 10485760;
@@ -1,6 +1,6 @@
1
1
 
2
- import { t as AnyRecord } from "../base-Csjc2oF-.mjs";
3
- import { Fn as IControllerResponse, In as IRequestContext, U as ResourceConfig, Y as PresetResult, d as PaginationResult } from "../index-BcR2bFZQ.mjs";
2
+ import { t as AnyRecord } from "../base-GZU3LlzP.mjs";
3
+ import { Fn as IControllerResponse, In as IRequestContext, U as ResourceConfig, Y as PresetResult, d as PaginationResult } from "../index-C5K78SXo.mjs";
4
4
  import { FilesUploadPresetOptions, FilesUploadPresetPermissions, FilesUploadPresetRoutes, filesUploadPreset } from "./filesUpload.mjs";
5
5
  import { MultiTenantOptions, TenantFieldSpec, multiTenantPreset } from "./multiTenant.mjs";
6
6
  import { SearchHandler, SearchPresetOptions, SearchRouteConfig, searchPreset } from "./search.mjs";
@@ -1,6 +1,6 @@
1
- /*! @classytic/arc v2.34.0 | MIT | https://classytic.com */
1
+ /*! @classytic/arc v2.35.0 | MIT | https://classytic.com */
2
2
  import { multiTenantPreset } from "./multiTenant.mjs";
3
- import { a as registerPreset, c as auditedPreset, d as ownedByUserPreset, i as getPreset, l as softDeletePreset, n as flexibleMultiTenantPreset, o as treePreset, r as getAvailablePresets, s as bulkPreset, t as applyPresets, u as slugLookupPreset } from "../presets-D-h0uWtP.mjs";
3
+ import { a as registerPreset, c as auditedPreset, d as ownedByUserPreset, i as getPreset, l as softDeletePreset, n as flexibleMultiTenantPreset, o as treePreset, r as getAvailablePresets, s as bulkPreset, t as applyPresets, u as slugLookupPreset } from "../presets-Bi0Z6Iq8.mjs";
4
4
  import { filesUploadPreset } from "./filesUpload.mjs";
5
5
  import { searchPreset } from "./search.mjs";
6
6
  export { applyPresets, auditedPreset, bulkPreset, filesUploadPreset, flexibleMultiTenantPreset, getAvailablePresets, getPreset, multiTenantPreset, ownedByUserPreset, registerPreset, searchPreset, slugLookupPreset, softDeletePreset, treePreset };
@@ -1,5 +1,5 @@
1
1
 
2
- import { X as CrudRouteKey, Y as PresetResult } from "../index-BcR2bFZQ.mjs";
2
+ import { X as CrudRouteKey, Y as PresetResult } from "../index-C5K78SXo.mjs";
3
3
  //#region src/presets/multiTenant.d.ts
4
4
  /**
5
5
  * One tenant dimension for multi-field filtering. Discriminated by source:
@@ -1,6 +1,6 @@
1
- /*! @classytic/arc v2.34.0 | MIT | https://classytic.com */
2
- import "../constants-BxCoxmiN.mjs";
3
- import { b as isElevated, c as getOrgId, d as getScopeContext, m as getTeamId, u as getRequestScope, v as hasOrgAccess } from "../types-CoqIRrkY.mjs";
1
+ /*! @classytic/arc v2.35.0 | MIT | https://classytic.com */
2
+ import "../constants-BhzLXCfN.mjs";
3
+ import { b as isElevated, c as getOrgId, d as getScopeContext, m as getTeamId, u as getRequestScope, v as hasOrgAccess } from "../types-GY4kATJb.mjs";
4
4
  //#region src/presets/multiTenant.ts
5
5
  /**
6
6
  * Resolve a single TenantFieldSpec against the current scope.
@@ -1,6 +1,6 @@
1
1
 
2
- import { l as PermissionCheck } from "../base-Csjc2oF-.mjs";
3
- import { At as RouteDefinition, Y as PresetResult, jn as ControllerHandler, jt as RouteMcpConfig } from "../index-BcR2bFZQ.mjs";
2
+ import { l as PermissionCheck } from "../base-GZU3LlzP.mjs";
3
+ import { At as RouteDefinition, Y as PresetResult, jn as ControllerHandler, jt as RouteMcpConfig } from "../index-C5K78SXo.mjs";
4
4
  //#region src/presets/search.d.ts
5
5
  /**
6
6
  * Search Preset — backend-agnostic search / vector / embed routes
@@ -23,55 +23,17 @@ import { At as RouteDefinition, Y as PresetResult, jn as ControllerHandler, jt a
23
23
  * GET-based autocomplete, pass `path`/`method` overrides or use `routes` for
24
24
  * fully bespoke endpoints.
25
25
  *
26
- * @example MongoKit wiring (elasticSearchPlugin + vectorPlugin)
27
- * ```typescript
28
- * import { Repository, methodRegistryPlugin, elasticSearchPlugin } from '@classytic/mongokit';
29
- * import { vectorPlugin } from '@classytic/mongokit/ai';
30
- * import { searchPreset } from '@classytic/arc/presets/search';
31
- *
32
- * const productRepo = new Repository(Product, [
33
- * methodRegistryPlugin(),
34
- * elasticSearchPlugin({ client: esClient, indexName: 'products' }),
35
- * vectorPlugin({ fields: [{ path: 'embedding', dimensions: 1536 }], embedFn }),
36
- * ]);
37
- *
38
- * defineResource({
39
- * name: 'product',
40
- * adapter: createMongooseAdapter({ model: Product, repository: productRepo }),
41
- * presets: [
42
- * searchPreset({
43
- * search: { handler: (req) => productRepo.search(req.body.query, req.body) },
44
- * similar: { handler: (req) => productRepo.searchSimilar(req.body.query, req.body) },
45
- * }),
46
- * ],
47
- * });
48
- * ```
49
- *
50
- * @example Custom vector backend (Pinecone)
26
+ * @example
51
27
  * ```typescript
52
28
  * searchPreset({
29
+ * search: { handler: (req) => repo.search(req.body.query, req.body) },
53
30
  * similar: {
54
- * path: '/vector-search', // custom path
55
- * handler: async (req) => {
56
- * const hits = await pinecone.query({
57
- * vector: req.body.vector,
58
- * topK: req.body.topK ?? 10,
59
- * });
60
- * return hits.matches;
61
- * },
62
- * schema: { body: { type: 'object', properties: { vector: { type: 'array' }, topK: { type: 'integer' } } } },
63
- * mcp: false, // no MCP tool for this one
31
+ * path: '/vector-search', // override the default path
32
+ * mcp: false, // and skip the MCP tool for this one
33
+ * handler: (req) => pinecone.query({ vector: req.body.vector }),
64
34
  * },
65
- * // Extra app-specific route
66
- * routes: [
67
- * {
68
- * method: 'GET',
69
- * path: '/autocomplete',
70
- * handler: async (req) => algolia.suggest(req.query.q as string),
71
- * permissions: allowPublic(),
72
- * },
73
- * ],
74
- * });
35
+ * routes: [{ method: 'GET', path: '/autocomplete', handler, permissions }],
36
+ * })
75
37
  * ```
76
38
  */
77
39
  /**
@@ -1,5 +1,5 @@
1
- /*! @classytic/arc v2.34.0 | MIT | https://classytic.com */
2
- import { B as requireAuth, F as allowPublic } from "../permissions-BAfwgBxc.mjs";
1
+ /*! @classytic/arc v2.35.0 | MIT | https://classytic.com */
2
+ import { B as requireAuth, F as allowPublic } from "../permissions-CpSBlFXZ.mjs";
3
3
  //#region src/presets/search.ts
4
4
  const BUILTINS = [
5
5
  {
@@ -1,7 +1,7 @@
1
- /*! @classytic/arc v2.34.0 | MIT | https://classytic.com */
2
- import { l as UnauthorizedError } from "./errors-Cd_yy7aW.mjs";
3
- import { b as isElevated, n as PUBLIC_SCOPE } from "./types-CoqIRrkY.mjs";
4
- import { B as requireAuth, F as allowPublic, U as requireRoles } from "./permissions-BAfwgBxc.mjs";
1
+ /*! @classytic/arc v2.35.0 | MIT | https://classytic.com */
2
+ import { l as UnauthorizedError } from "./errors-CLid6atr.mjs";
3
+ import { b as isElevated, n as PUBLIC_SCOPE } from "./types-GY4kATJb.mjs";
4
+ import { B as requireAuth, F as allowPublic, U as requireRoles } from "./permissions-CpSBlFXZ.mjs";
5
5
  import { multiTenantPreset } from "./presets/multiTenant.mjs";
6
6
  //#region src/presets/ownedByUser.ts
7
7
  /**
@@ -1,5 +1,5 @@
1
- /*! @classytic/arc v2.34.0 | MIT | https://classytic.com */
2
- import { c as getOrgId, f as getScopeContextMap, g as getUserId, m as getTeamId, x as isMember } from "./types-CoqIRrkY.mjs";
1
+ /*! @classytic/arc v2.35.0 | MIT | https://classytic.com */
2
+ import { c as getOrgId, f as getScopeContextMap, g as getUserId, m as getTeamId, x as isMember } from "./types-GY4kATJb.mjs";
3
3
  //#region src/scope/projection.ts
4
4
  /**
5
5
  * Compute the request-scope projection. Returns `undefined` when no
@@ -1,8 +1,8 @@
1
- /*! @classytic/arc v2.34.0 | MIT | https://classytic.com */
2
- import { t as __exportAll } from "./rolldown-runtime-DNg-KNCk.mjs";
3
- import { t as hasEvents } from "./typeGuards-jjKJDN13.mjs";
4
- import { i as versionKey, r as tagVersionKey } from "./keys-WeKvY1dE.mjs";
5
- import { t as MemoryCacheStore } from "./memory-qYSB7rMQ.mjs";
1
+ /*! @classytic/arc v2.35.0 | MIT | https://classytic.com */
2
+ import { t as __exportAll } from "./rolldown-runtime-CEvvVXTC.mjs";
3
+ import { t as hasEvents } from "./typeGuards-B-_jqaMi.mjs";
4
+ import { i as versionKey, r as tagVersionKey } from "./keys-GlU7XpmG.mjs";
5
+ import { t as MemoryCacheStore } from "./memory-BwedEXau.mjs";
6
6
  import fp from "fastify-plugin";
7
7
  //#region src/cache/QueryCache.ts
8
8
  var QueryCache = class {
@@ -1,4 +1,4 @@
1
- /*! @classytic/arc v2.34.0 | MIT | https://classytic.com */
1
+ /*! @classytic/arc v2.35.0 | MIT | https://classytic.com */
2
2
  //#region src/scope/rateLimitKey.ts
3
3
  function createTenantKeyGenerator(opts) {
4
4
  if (opts?.strategy) return opts.strategy;
@@ -1,13 +1,13 @@
1
- /*! @classytic/arc v2.34.0 | MIT | https://classytic.com */
2
- import { t as __exportAll } from "./rolldown-runtime-DNg-KNCk.mjs";
1
+ /*! @classytic/arc v2.35.0 | MIT | https://classytic.com */
2
+ import { t as __exportAll } from "./rolldown-runtime-CEvvVXTC.mjs";
3
3
  import { arcLog } from "./logger/index.mjs";
4
- import { n as evaluateAndApplyPermission } from "./authorizationDecision-B3mP_QTy.mjs";
5
- import { n as getUserRoles } from "./userHelpers-CWF5pbZD.mjs";
6
- import { c as getOrgId } from "./types-CoqIRrkY.mjs";
7
- import { t as simpleEqualityMatcher } from "./simpleEqualityMatcher-Se8VrsaP.mjs";
8
- import { t as applyFieldReadPermissions } from "./fields-CojeUkS4.mjs";
9
- import { a as promoteStreamTokenToHeader } from "./streaming-DsT1B5zJ.mjs";
10
- import { t as openSseStream } from "./sseStream-Bvpi-UXQ.mjs";
4
+ import { n as evaluateAndApplyPermission } from "./authorizationDecision-DYXBBgtg.mjs";
5
+ import { n as getUserRoles } from "./userHelpers-AKcB4V03.mjs";
6
+ import { c as getOrgId } from "./types-GY4kATJb.mjs";
7
+ import { t as simpleEqualityMatcher } from "./simpleEqualityMatcher-BO-sFTQf.mjs";
8
+ import { t as applyFieldReadPermissions } from "./fields-UtpiJO5R.mjs";
9
+ import { a as promoteStreamTokenToHeader } from "./streaming-DOpZVht6.mjs";
10
+ import { t as openSseStream } from "./sseStream-ByFGyIVr.mjs";
11
11
  import fp from "fastify-plugin";
12
12
  //#region src/plugins/realtime.ts
13
13
  var realtime_exports = /* @__PURE__ */ __exportAll({
@@ -1,5 +1,8 @@
1
- /*! @classytic/arc v2.34.0 | MIT | https://classytic.com */
2
- import { n as requestContext } from "./requestContext-uYE35JxI.mjs";
1
+ /*! @classytic/arc v2.35.0 | MIT | https://classytic.com */
2
+ import { n as evaluateAndApplyPermission } from "./authorizationDecision-DYXBBgtg.mjs";
3
+ import { f as createError } from "./errors-CLid6atr.mjs";
4
+ import { u as getRequestScope } from "./types-GY4kATJb.mjs";
5
+ import { n as requestContext } from "./requestContext-DzOUGNdY.mjs";
3
6
  //#region src/core/middlewares/arcDecorator.ts
4
7
  /**
5
8
  * Build the `arcDecorator` preHandler for a resource.
@@ -178,6 +181,79 @@ function describeValue(v) {
178
181
  return `${typeof v} (${JSON.stringify(v).slice(0, 80)})`;
179
182
  }
180
183
  //#endregion
184
+ //#region src/core/middlewares/permissions.ts
185
+ /**
186
+ * Build a PermissionContext from a Fastify request. Extracted so the CRUD
187
+ * permission middleware and the dynamic action-permission check use the same
188
+ * field layout — divergence here silently broke policy filters for actions.
189
+ */
190
+ function buildPermissionContext(req, opts) {
191
+ const reqWithExtras = req;
192
+ const params = req.params;
193
+ return {
194
+ user: reqWithExtras.user ?? null,
195
+ scope: getRequestScope(req),
196
+ request: req,
197
+ resource: opts.resource,
198
+ action: opts.action,
199
+ resourceId: opts.resourceId ?? params?.id,
200
+ params,
201
+ data: opts.data ?? req.body
202
+ };
203
+ }
204
+ /**
205
+ * Static per-route CRUD permission gate. The permission and action are known
206
+ * at route-registration time, so the gate is a plain preHandler.
207
+ *
208
+ * Actions use the dynamic counterpart `buildActionPermissionMw` — their
209
+ * permission is resolved from `body.action` at request time.
210
+ */
211
+ function buildCrudPermissionMw(permissionCheck, resourceName, action) {
212
+ if (!permissionCheck) return null;
213
+ return async (req, reply) => {
214
+ const context = buildPermissionContext(req, {
215
+ resource: resourceName,
216
+ action
217
+ });
218
+ await evaluateAndApplyPermission(permissionCheck, context, req, reply);
219
+ };
220
+ }
221
+ /**
222
+ * Dynamic per-action permission gate for the action router.
223
+ *
224
+ * Resolves the permission from `body.action` at request time and runs
225
+ * `evaluateAndApplyPermission` from the canonical `permissionMw` slot — so
226
+ * `_policyFilters` and `request.scope` are installed BEFORE `pluginMw`
227
+ * (idempotency) and `routeGuards` run. Previously this check lived inside
228
+ * the main action handler, which meant idempotency recorded unauthorized
229
+ * requests and route guards saw unfiltered scope — the very divergence
230
+ * routerShared exists to prevent.
231
+ *
232
+ * Also acts as a defensive fallback for invalid action names — the
233
+ * `oneOf` body schema normally rejects these at AJV validation, but
234
+ * hosts that disable schema validation still get a 400 here.
235
+ */
236
+ function buildActionPermissionMw(actionEnum, actionPermissions, globalAuth, resourceName) {
237
+ const enumSet = new Set(actionEnum);
238
+ const validActions = [...actionEnum];
239
+ return async (req, reply) => {
240
+ const body = req.body ?? {};
241
+ const action = body.action;
242
+ if (!action || !enumSet.has(action)) throw createError(400, `Invalid action '${action ?? ""}'. Valid actions: ${validActions.join(", ")}`, { validActions });
243
+ const permissionCheck = actionPermissions[action] ?? globalAuth;
244
+ if (!permissionCheck) return;
245
+ const { action: _discard, ...data } = body;
246
+ const params = req.params;
247
+ const context = buildPermissionContext(req, {
248
+ resource: resourceName,
249
+ action,
250
+ resourceId: params?.id,
251
+ data
252
+ });
253
+ await evaluateAndApplyPermission(permissionCheck, context, req, reply, { defaultDenialMessage: (user) => user ? `Permission denied for '${action}'` : "Authentication required" });
254
+ };
255
+ }
256
+ //#endregion
181
257
  //#region src/core/middlewares/pluginMw.ts
182
258
  /**
183
259
  * Pick the request-lifecycle plugin middleware for an HTTP method:
@@ -246,4 +322,39 @@ function buildRateLimitConfig(rateLimit) {
246
322
  } };
247
323
  }
248
324
  //#endregion
249
- export { buildRouteHooks as a, buildAuthMiddleware as c, selectPluginMw as i, buildAuthMiddlewareForPermissions as l, buildRouteConfig as n, resolveRoutePreHandlers as o, resolveRouterPluginMw as r, routeHookOptions as s, buildRateLimitConfig as t, buildArcDecorator as u };
325
+ //#region src/core/middlewares/registerRoute.ts
326
+ /**
327
+ * Register a route with friendly handling of `FST_ERR_DUPLICATED_ROUTE`.
328
+ *
329
+ * Boot-time `validateRouteCrudCollisions()` catches custom-route → auto-CRUD
330
+ * overlaps before Fastify ever runs, but a few cases still slip through to
331
+ * Fastify itself: two presets contributing the same `routes` entry, hosts
332
+ * mounting two resources at the same prefix, custom routes that share a
333
+ * URL with another plugin. Fastify's default message ("Method 'GET'
334
+ * already declared for route '/'") gives no hint at the resource-shaped
335
+ * fix — `disabledRoutes: ['list']` — so wrap it with one.
336
+ */
337
+ function tryRegisterRoute(fastify, opts, context) {
338
+ try {
339
+ fastify.route(opts);
340
+ } catch (err) {
341
+ const fastifyErr = err;
342
+ if (fastifyErr?.code !== "FST_ERR_DUPLICATED_ROUTE") throw err;
343
+ const method = opts.method;
344
+ const url = opts.url;
345
+ const opHint = context.op ? ` (op: ${context.op})` : "";
346
+ const fixHint = context.op && [
347
+ "list",
348
+ "get",
349
+ "create",
350
+ "update",
351
+ "delete"
352
+ ].includes(context.op) ? ` Suppress the auto-CRUD route with \`disabledRoutes: ['${context.op}']\`.` : " If a custom route in `routes:` collides with auto-CRUD, add `disabledRoutes: ['list' | 'get' | 'create' | 'update' | 'delete']` to `defineResource()` to suppress the matching auto-CRUD route.";
353
+ const enhanced = /* @__PURE__ */ new Error(`[arc] Duplicate route ${method} ${url} on resource "${context.resourceName}"${opHint}.${fixHint} Original: ${fastifyErr.message ?? "FST_ERR_DUPLICATED_ROUTE"}`);
354
+ enhanced.code = "FST_ERR_DUPLICATED_ROUTE";
355
+ enhanced.cause = err;
356
+ throw enhanced;
357
+ }
358
+ }
359
+ //#endregion
360
+ export { selectPluginMw as a, buildRouteHooks as c, buildAuthMiddleware as d, buildAuthMiddlewareForPermissions as f, resolveRouterPluginMw as i, resolveRoutePreHandlers as l, buildRateLimitConfig as n, buildActionPermissionMw as o, buildArcDecorator as p, buildRouteConfig as r, buildCrudPermissionMw as s, tryRegisterRoute as t, routeHookOptions as u };
@@ -1,6 +1,6 @@
1
1
 
2
- import { K as ResolvedTenantPurge, R as RegisterOptions, V as ResourceDefinition, X as CrudRouteKey, k as IntrospectionPluginOptions, p as RegistryEntry, z as ResourceRegistry } from "../index-BcR2bFZQ.mjs";
3
- import "../fastify-augmentation-DVsTAy6X.mjs";
2
+ import { K as ResolvedTenantPurge, R as RegisterOptions, V as ResourceDefinition, X as CrudRouteKey, k as IntrospectionPluginOptions, p as RegistryEntry, z as ResourceRegistry } from "../index-C5K78SXo.mjs";
3
+ import "../fastify-augmentation-CMMTL3cj.mjs";
4
4
  import { TenantPurgeProgress, TenantPurgeStrategy } from "@classytic/repo-core/repository";
5
5
  import { FastifyPluginAsync } from "fastify";
6
6
  //#region src/registry/assertNoTenantData.d.ts