@classytic/arc 2.40.0 → 2.41.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 (187) hide show
  1. package/dist/{BaseController-BqJtbiUA.mjs → BaseController-CYEwjyjN.mjs} +20 -13
  2. package/dist/{EventTransport-BPOQsEer.d.mts → EventTransport-CbUKDzZZ.d.mts} +25 -0
  3. package/dist/{EventTransport-BUkTwY_x.mjs → EventTransport-DSqD_Xsg.mjs} +11 -2
  4. package/dist/{HookSystem-96nzZLmk.mjs → HookSystem-CMF94SEA.mjs} +2 -2
  5. package/dist/{ResourceRegistry-B9WnuG2Q.mjs → ResourceRegistry-MGpXoxHZ.mjs} +2 -2
  6. package/dist/{actionPermissions-4OGbe3Hf.mjs → actionPermissions-BoPGQ5-4.mjs} +7 -6
  7. package/dist/audit/index.d.mts +2 -2
  8. package/dist/audit/index.mjs +10 -3
  9. package/dist/auth/audit.mjs +1 -1
  10. package/dist/auth/index.d.mts +2 -2
  11. package/dist/auth/index.mjs +12 -6
  12. package/dist/auth/redis-session.mjs +1 -1
  13. package/dist/{authorizationDecision-D_LFvMlm.mjs → authorizationDecision-DawKGoXb.mjs} +2 -2
  14. package/dist/{base-C-NL2fTi.d.mts → base-CxrV1LmG.d.mts} +10 -0
  15. package/dist/{buildHandler-BG_CHXqQ.mjs → buildHandler-CvxKOc0m.mjs} +4 -4
  16. package/dist/cache/index.d.mts +4 -2
  17. package/dist/cache/index.mjs +4 -4
  18. package/dist/{caching-Bqcn3Brl.mjs → caching-DbCBxlgw.mjs} +2 -2
  19. package/dist/cleanup/index.d.mts +2 -2
  20. package/dist/cleanup/index.mjs +4 -4
  21. package/dist/cli/commands/describe.d.mts +1 -1
  22. package/dist/cli/commands/describe.mjs +3 -3
  23. package/dist/cli/commands/docs.mjs +3 -3
  24. package/dist/cli/commands/doctor.mjs +1 -1
  25. package/dist/cli/commands/generate.mjs +2 -2
  26. package/dist/cli/commands/init.mjs +2 -2
  27. package/dist/cli/commands/introspect.mjs +2 -2
  28. package/dist/cli/index.mjs +3 -3
  29. package/dist/{constants-BQ2FbhPB.mjs → constants-D5Ubn1fe.mjs} +1 -1
  30. package/dist/context/index.mjs +36 -11
  31. package/dist/core/index.d.mts +2 -2
  32. package/dist/core/index.mjs +7 -7
  33. package/dist/{core-CsZR037F.mjs → core-5lazhva9.mjs} +3 -3
  34. package/dist/{createActionRouter-BR93gCKg.mjs → createActionRouter-DNqoaK81.mjs} +7 -7
  35. package/dist/{createAggregationRouter-CF40Aw95.mjs → createAggregationRouter-BcbQJf10.mjs} +7 -7
  36. package/dist/{createMcpServer-BVTs2S5G.d.mts → createMcpServer-CbDWkZ1T.d.mts} +1 -1
  37. package/dist/{defineAggregation-BDA2Tw7v.mjs → defineAggregation-D_eJjm5A.mjs} +1 -1
  38. package/dist/{defineResource-DaIySrQr.mjs → defineResource-DxkMTjRn.mjs} +18 -18
  39. package/dist/discovery/index.mjs +1 -1
  40. package/dist/docs/index.d.mts +1 -1
  41. package/dist/docs/index.mjs +3 -3
  42. package/dist/{elevation-CZy6n50b.mjs → elevation-CkvG0LG2.mjs} +4 -4
  43. package/dist/encryption/index.mjs +3 -3
  44. package/dist/{errorHandler-By9keRvX.mjs → errorHandler-D1GHKGlV.mjs} +3 -3
  45. package/dist/{errors-BHD3L8of.mjs → errors-BH7YU_9O.mjs} +1 -1
  46. package/dist/{eventPlugin-DfHaKEWQ.d.mts → eventPlugin-B52abJ8W.d.mts} +1 -1
  47. package/dist/{eventPlugin-DB26V7uX.mjs → eventPlugin-zZY-unhr.mjs} +5 -5
  48. package/dist/events/index.d.mts +4 -4
  49. package/dist/events/index.mjs +5 -5
  50. package/dist/events/transports/redis-stream-entry.d.mts +1 -1
  51. package/dist/events/transports/redis-stream-entry.mjs +15 -6
  52. package/dist/events/transports/redis.d.mts +1 -1
  53. package/dist/events/transports/redis.mjs +2 -2
  54. package/dist/factory/index.d.mts +5 -5
  55. package/dist/factory/index.mjs +5 -5
  56. package/dist/{fastify-augmentation-HncZMmze.d.mts → fastify-augmentation-Cq2nHVaW.d.mts} +3 -3
  57. package/dist/{fields-fA-Dkou5.mjs → fields-BHrkoF5y.mjs} +1 -1
  58. package/dist/{generate-BSSdtiOy.mjs → generate-BLRf2m3y.mjs} +2 -2
  59. package/dist/{headers-BFEEshDS.mjs → headers-BIXWKrzQ.mjs} +2 -2
  60. package/dist/{health-BlGHjkqZ.mjs → health-ZRHeDHXH.mjs} +10 -2
  61. package/dist/hooks/index.d.mts +1 -1
  62. package/dist/hooks/index.mjs +2 -2
  63. package/dist/idempotency/index.d.mts +1 -1
  64. package/dist/idempotency/index.mjs +4 -4
  65. package/dist/idempotency/redis.d.mts +1 -1
  66. package/dist/idempotency/redis.mjs +1 -1
  67. package/dist/{index-BtYoMip1.d.mts → index-BHdZD8zK.d.mts} +10 -2
  68. package/dist/{index-DbMglURO.d.mts → index-CFfLKMH4.d.mts} +1 -1
  69. package/dist/{index-eyg5WEcy.d.mts → index-CbJiYrfv.d.mts} +1 -1
  70. package/dist/{index-SCsOJxWB.d.mts → index-D4u76eQW.d.mts} +3 -3
  71. package/dist/{index-D3sIPcD4.d.mts → index-DHbyPo5j.d.mts} +6 -2
  72. package/dist/index.d.mts +5 -5
  73. package/dist/index.mjs +11 -11
  74. package/dist/{init-pl88PHzb.mjs → init-CpLkjU18.mjs} +1 -1
  75. package/dist/integrations/event-gateway.d.mts +1 -1
  76. package/dist/integrations/event-gateway.mjs +2 -2
  77. package/dist/integrations/index.d.mts +2 -2
  78. package/dist/integrations/index.mjs +1 -1
  79. package/dist/integrations/jobs/index.d.mts +1 -1
  80. package/dist/integrations/jobs/index.mjs +2 -2
  81. package/dist/integrations/mcp/index.d.mts +4 -4
  82. package/dist/integrations/mcp/index.mjs +2 -2
  83. package/dist/integrations/mcp/testing.d.mts +2 -2
  84. package/dist/integrations/mcp/testing.mjs +2 -2
  85. package/dist/integrations/streamline.mjs +3 -3
  86. package/dist/integrations/webhooks.d.mts +2 -2
  87. package/dist/integrations/webhooks.mjs +3 -3
  88. package/dist/integrations/websocket-pushref-redis.d.mts +1 -1
  89. package/dist/integrations/websocket-pushref-redis.mjs +1 -1
  90. package/dist/integrations/websocket-redis.mjs +1 -1
  91. package/dist/integrations/websocket.mjs +12 -1
  92. package/dist/{interface-DjtFnZxH.mjs → interface-YzIbF7Ii.mjs} +1 -1
  93. package/dist/{jsonBody-Dx6jH_Nb.mjs → jsonBody-ELR9lfeU.mjs} +2 -2
  94. package/dist/{keys-Ds3ekBiu.mjs → keys-cpZCtcIE.mjs} +1 -1
  95. package/dist/{loadResources-E5UFlRrT.mjs → loadResources-BPfWiI5s.mjs} +2 -2
  96. package/dist/{loadResourcesFromEntry-C2jrJbWG.mjs → loadResourcesFromEntry-ixtfL3J2.mjs} +2 -2
  97. package/dist/logger/index.mjs +1 -1
  98. package/dist/{mcpPlugin-D-3OLxNz.mjs → mcpPlugin-BX5PujI0.mjs} +15 -15
  99. package/dist/{memory-CNvjxzB2.mjs → memory-Iotz0cCx.mjs} +2 -2
  100. package/dist/{metrics-P37sNV5H.mjs → metrics-NMsmpRmY.mjs} +2 -2
  101. package/dist/middleware/index.d.mts +1 -1
  102. package/dist/middleware/index.mjs +3 -3
  103. package/dist/migrations/index.mjs +2 -2
  104. package/dist/{module-CNcwCKUJ.mjs → module-DpLJG2_N.mjs} +1 -1
  105. package/dist/{multipartBody-Bl_gU3Wg.mjs → multipartBody-DtRWM6_U.mjs} +1 -1
  106. package/dist/{openapi-BYrL8zvY.mjs → openapi-CmFf3yq_.mjs} +6 -6
  107. package/dist/outbox-admin/index.d.mts +2 -2
  108. package/dist/outbox-admin/index.mjs +4 -4
  109. package/dist/{outbox-module-CzsKRBvO.d.mts → outbox-module-BHdUnuuK.d.mts} +6 -2
  110. package/dist/{outbox-module-6DMC_WNu.mjs → outbox-module-D2z5WkDY.mjs} +9 -6
  111. package/dist/permissions/index.d.mts +2 -2
  112. package/dist/permissions/index.mjs +5 -5
  113. package/dist/{permissions-C0GpDsB_.mjs → permissions-D8TBH44E.mjs} +12 -6
  114. package/dist/{pipe-4_XamfxI.mjs → pipe-FMWNheSO.mjs} +2 -2
  115. package/dist/pipeline/index.d.mts +1 -1
  116. package/dist/pipeline/index.mjs +2 -2
  117. package/dist/plugins/index.d.mts +6 -36
  118. package/dist/plugins/index.mjs +33 -22
  119. package/dist/plugins/response-cache.mjs +9 -2
  120. package/dist/plugins/tracing-entry.mjs +2 -2
  121. package/dist/{pluralize-BaQ62hOW.mjs → pluralize-CXWZIzTv.mjs} +1 -1
  122. package/dist/{preloadResources-DVgPCNcD.d.mts → preloadResources-CjS2OfpU.d.mts} +13 -8
  123. package/dist/{preloadResources-BAx5s9EF.mjs → preloadResources-f3boenVZ.mjs} +33 -26
  124. package/dist/presets/filesUpload.d.mts +2 -2
  125. package/dist/presets/filesUpload.mjs +5 -5
  126. package/dist/presets/index.d.mts +2 -2
  127. package/dist/presets/index.mjs +2 -2
  128. package/dist/presets/multiTenant.d.mts +38 -1
  129. package/dist/presets/multiTenant.mjs +24 -6
  130. package/dist/presets/search.d.mts +2 -2
  131. package/dist/presets/search.mjs +2 -2
  132. package/dist/{presets-CAaqVVcW.mjs → presets-ZJSPgmT2.mjs} +4 -4
  133. package/dist/{projection-qbCOEvsh.mjs → projection-BzIgzoKZ.mjs} +2 -2
  134. package/dist/{queryCachePlugin-2Pp9w-8D.mjs → queryCachePlugin-DZHPOm9p.mjs} +11 -5
  135. package/dist/{rateLimitKey-umrS6-vu.mjs → rateLimitKey-BvOvcBDi.mjs} +1 -1
  136. package/dist/{realtime-zzoNLDtj.mjs → realtime-8tIQWZz4.mjs} +9 -9
  137. package/dist/{redis-CduhKsJi.d.mts → redis-Bzi1y3F9.d.mts} +10 -2
  138. package/dist/{redis-stream-DSsPyYzd.d.mts → redis-stream-BPKscsTB.d.mts} +6 -6
  139. package/dist/{registerRoute-_6zHiz0x.mjs → registerRoute-Bsnk6NiZ.mjs} +6 -6
  140. package/dist/registry/index.d.mts +2 -2
  141. package/dist/registry/index.mjs +3 -3
  142. package/dist/{registry-CEpoRpLP.mjs → registry-DNkgGQcK.mjs} +2 -2
  143. package/dist/{renewingLease-B5Grm0pI.mjs → renewingLease-yF8BdW4b.mjs} +1 -1
  144. package/dist/{replyHelpers-9h-a592u.mjs → replyHelpers-CNdvMfM4.mjs} +2 -2
  145. package/dist/{requestContext-D4PuGhEw.mjs → requestContext-cDkAUr-P.mjs} +1 -1
  146. package/dist/{rolldown-runtime-DzE6ikSH.mjs → rolldown-runtime-eysyzFAF.mjs} +1 -1
  147. package/dist/{runtimeCapabilities-Bo7rSKjm.mjs → runtimeCapabilities-CkhS6jn6.mjs} +31 -5
  148. package/dist/{schedules-Dm21pqHU.mjs → schedules-j6uXXAeJ.mjs} +4 -4
  149. package/dist/{schemaConverter-DENbixyA.mjs → schemaConverter-BVA7cFRG.mjs} +1 -1
  150. package/dist/{schemaIR-C2qUNzC6.mjs → schemaIR-CTvPDoPB.mjs} +2 -2
  151. package/dist/schemas/index.mjs +1 -1
  152. package/dist/scim/index.mjs +2 -2
  153. package/dist/scope/index.mjs +5 -5
  154. package/dist/{scope-CdYxQVxt.mjs → scope-BUi1xV1I.mjs} +4 -4
  155. package/dist/{simpleEqualityMatcher-Cldubvul.mjs → simpleEqualityMatcher-C9bzq2gO.mjs} +1 -1
  156. package/dist/{sse-DbyXoQun.mjs → sse-BeqVKQKu.mjs} +5 -5
  157. package/dist/{sseStream-BfZtL5-m.mjs → sseStream-Ppzo-WUH.mjs} +2 -2
  158. package/dist/{store-helpers-BfyOd5i6.mjs → store-helpers-dW71XVWL.mjs} +1 -1
  159. package/dist/{streaming-CFwCzEvU.mjs → streaming-IcRFjWQU.mjs} +1 -1
  160. package/dist/{subscribe-helpers-KgVEcn8L.mjs → subscribe-helpers-DatDkcuG.mjs} +2 -2
  161. package/dist/sync/changelog.mjs +1 -1
  162. package/dist/testing/cleanupStoreContract.mjs +1 -1
  163. package/dist/testing/index.d.mts +5 -5
  164. package/dist/testing/index.mjs +10 -10
  165. package/dist/testing/outboxStoreContract.mjs +1 -1
  166. package/dist/testing/storageContract.mjs +1 -1
  167. package/dist/{transactionContext-46KyUADP.mjs → transactionContext-CPbdlnVw.mjs} +1 -1
  168. package/dist/{typeGuards-t_CVgRXN.mjs → typeGuards-BLjPgVBm.mjs} +1 -1
  169. package/dist/types/index.d.mts +2 -2
  170. package/dist/types/index.mjs +1 -1
  171. package/dist/types/storage.mjs +1 -1
  172. package/dist/{types-9rvxn5Ym.d.mts → types-CWQn9-IC.d.mts} +1 -1
  173. package/dist/{types-CNQyOrLk.mjs → types-CgenB1gv.mjs} +4 -4
  174. package/dist/{types-BVlowlm7.d.mts → types-DfVDvkdP.d.mts} +19 -1
  175. package/dist/usage/index.d.mts +58 -2
  176. package/dist/usage/index.mjs +55 -4
  177. package/dist/{userHelpers-DLueLQdQ.mjs → userHelpers-ChRvkuSZ.mjs} +1 -1
  178. package/dist/utils/index.d.mts +107 -5
  179. package/dist/utils/index.mjs +12 -12
  180. package/dist/{utils-CUFBrhq-.mjs → utils-CxcctrAh.mjs} +50 -9
  181. package/dist/{validate-DV3hRbsz.mjs → validate-DrYWnOzI.mjs} +2 -2
  182. package/dist/{versioning-P3V6ATyC.d.mts → versioning-D6JIK8Tk.d.mts} +58 -2
  183. package/dist/{versioning-BXwmj5SD.mjs → versioning-DKkT4hNJ.mjs} +3 -3
  184. package/dist/{workScope-CoGpuzDX.mjs → workScope-CxKqoIFn.mjs} +3 -4
  185. package/package.json +5 -5
  186. package/skills/arc/SKILL.md +8 -4
  187. package/skills/arc/references/production.md +94 -9
@@ -1,10 +1,10 @@
1
1
 
2
- import { t as AnyRecord } from "../base-C-NL2fTi.mjs";
3
- import { At as RouteDefinition, J as PresetHook, Q as MiddlewareConfig, et as RouteSchemaOptions } from "../index-D3sIPcD4.mjs";
2
+ import { t as AnyRecord } from "../base-CxrV1LmG.mjs";
3
+ import { At as RouteDefinition, J as PresetHook, Q as MiddlewareConfig, et as RouteSchemaOptions } from "../index-DHbyPo5j.mjs";
4
4
  import { i as errorHandlerPlugin, n as ErrorMapper, r as defaultIsDuplicateKeyError, t as ErrorHandlerOptions } from "../errorHandler--4rSMMvq.mjs";
5
- import { _ as HealthCheck, b as healthPlugin, f as ScheduleDefinition, g as schedulesPlugin, h as _default$8, m as SchedulesPluginOptions, p as ScheduleStats, v as HealthOptions, y as _default$3 } from "../types-BVlowlm7.mjs";
6
- import { a as arcCorePlugin, i as _default, n as ArcCorePluginOptions, r as PluginMeta, t as ArcCore } from "../fastify-augmentation-HncZMmze.mjs";
7
- import { _ as CachingRule, a as MetricsCollector, c as metricsPlugin, d as _default$6, f as realtimePlugin, g as CachingOptions, h as ssePlugin, i as MetricEntry, l as ChangeOperation, m as _default$9, n as _default$10, o as MetricsOptions, p as SSEOptions, r as versioningPlugin, s as _default$4, t as VersioningOptions, u as RealtimeOptions, v as _default$1, y as cachingPlugin } from "../versioning-P3V6ATyC.mjs";
5
+ import { _ as HealthCheck, b as healthPlugin, f as ScheduleDefinition, g as schedulesPlugin, h as _default$8, m as SchedulesPluginOptions, p as ScheduleStats, v as HealthOptions, y as _default$3 } from "../types-DfVDvkdP.mjs";
6
+ import { a as arcCorePlugin, i as _default, n as ArcCorePluginOptions, r as PluginMeta, t as ArcCore } from "../fastify-augmentation-Cq2nHVaW.mjs";
7
+ import { C as gracefulShutdownPlugin, S as _default$2, _ as CachingRule, a as MetricsCollector, b as GracefulShutdownOptions, c as metricsPlugin, d as _default$6, f as realtimePlugin, g as CachingOptions, h as ssePlugin, i as MetricEntry, l as ChangeOperation, m as _default$9, n as _default$10, o as MetricsOptions, p as SSEOptions, r as versioningPlugin, s as _default$4, t as VersioningOptions, u as RealtimeOptions, v as _default$1, x as ShutdownState, y as cachingPlugin } from "../versioning-D6JIK8Tk.mjs";
8
8
  import { t as TracingOptions } from "../tracing-Dro5eH6j.mjs";
9
9
  import { PaginatedResult } from "@classytic/repo-core/pagination";
10
10
  import { FastifyInstance, FastifyPluginAsync } from "fastify";
@@ -59,36 +59,6 @@ interface ArcPlugin<TRootOpts extends AnyRecord = AnyRecord, TResourceOpts exten
59
59
  */
60
60
  declare function createPlugin<TRootOpts extends AnyRecord = AnyRecord, TResourceOpts extends AnyRecord = AnyRecord>(name: string, definition: CreatePluginDefinition<TRootOpts, TResourceOpts>): ArcPlugin<TRootOpts, TResourceOpts>;
61
61
  //#endregion
62
- //#region src/plugins/gracefulShutdown.d.ts
63
- interface GracefulShutdownOptions {
64
- /** Maximum time to wait for graceful shutdown in ms (default: 30000) */
65
- timeout?: number;
66
- /** Custom cleanup function called before exit */
67
- onShutdown?: () => Promise<void> | void;
68
- /** Signals to handle (default: ['SIGTERM', 'SIGINT']) */
69
- signals?: NodeJS.Signals[];
70
- /** Whether to log shutdown events (default: true) */
71
- logEvents?: boolean;
72
- /**
73
- * Called when shutdown times out or encounters an error.
74
- * Defaults to `process.exit(1)` — appropriate for production but dangerous in:
75
- * - **Tests**: kills the test runner. Pass `() => {}` or `() => { throw … }`.
76
- * - **Shared runtimes** (e.g., serverless): may kill unrelated handlers.
77
- *
78
- * @param reason - `'timeout'` if shutdown exceeded `timeout` ms,
79
- * `'error'` if `onShutdown` or `fastify.close()` threw.
80
- */
81
- onForceExit?: (reason: "timeout" | "error") => void;
82
- }
83
- declare const gracefulShutdownPlugin: FastifyPluginAsync<GracefulShutdownOptions>;
84
- declare module "fastify" {
85
- interface FastifyInstance {
86
- /** Trigger graceful shutdown manually */
87
- shutdown: () => Promise<void>;
88
- }
89
- }
90
- declare const _default$2: FastifyPluginAsync<GracefulShutdownOptions>;
91
- //#endregion
92
62
  //#region src/plugins/pressure.d.ts
93
63
  /**
94
64
  * One measurable source of load, normalised by whoever owns it.
@@ -253,4 +223,4 @@ declare function createRequestIdGenerator(opts?: Pick<RequestIdOptions, "header"
253
223
  declare const requestIdPlugin: FastifyPluginAsync<RequestIdOptions>;
254
224
  declare const _default$7: FastifyPluginAsync<RequestIdOptions>;
255
225
  //#endregion
256
- export { type ArcCore, type ArcCorePluginOptions, type ArcPlugin, type CachingOptions, type CachingRule, type ChangeOperation, type CreatePluginDefinition, type ErrorHandlerOptions, type ErrorMapper, type GracefulShutdownOptions, type HealthCheck, type HealthOptions, type MetricEntry, type MetricsCollector, type MetricsOptions, type PluginMeta, type PluginResourceResult, type PressureApi, type PressureOptions, type PressureSignal, type PressureSnapshot, type PressureState, type PressureThresholds, type RealtimeOptions, type RequestIdOptions, type SSEOptions, type ScheduleDefinition, type ScheduleStats, type SchedulesPluginOptions, type TracingOptions, type VersioningOptions, _default as arcCorePlugin, arcCorePlugin as arcCorePluginFn, _default$1 as cachingPlugin, cachingPlugin as cachingPluginFn, createPlugin, createRequestIdGenerator, defaultIsDuplicateKeyError, errorHandlerPlugin, errorHandlerPlugin as errorHandlerPluginFn, _default$2 as gracefulShutdownPlugin, gracefulShutdownPlugin as gracefulShutdownPluginFn, _default$3 as healthPlugin, healthPlugin as healthPluginFn, _default$4 as metricsPlugin, metricsPlugin as metricsPluginFn, _default$5 as pressurePlugin, pressurePlugin as pressurePluginFn, _default$6 as realtimePlugin, realtimePlugin as realtimePluginFn, replyHelpersPlugin, _default$7 as requestIdPlugin, requestIdPlugin as requestIdPluginFn, sanitizeRequestId, _default$8 as schedulesPlugin, schedulesPlugin as schedulesPluginFn, _default$9 as ssePlugin, ssePlugin as ssePluginFn, _default$10 as versioningPlugin, versioningPlugin as versioningPluginFn };
226
+ export { type ArcCore, type ArcCorePluginOptions, type ArcPlugin, type CachingOptions, type CachingRule, type ChangeOperation, type CreatePluginDefinition, type ErrorHandlerOptions, type ErrorMapper, type GracefulShutdownOptions, type HealthCheck, type HealthOptions, type MetricEntry, type MetricsCollector, type MetricsOptions, type PluginMeta, type PluginResourceResult, type PressureApi, type PressureOptions, type PressureSignal, type PressureSnapshot, type PressureState, type PressureThresholds, type RealtimeOptions, type RequestIdOptions, type SSEOptions, type ScheduleDefinition, type ScheduleStats, type SchedulesPluginOptions, type ShutdownState, type TracingOptions, type VersioningOptions, _default as arcCorePlugin, arcCorePlugin as arcCorePluginFn, _default$1 as cachingPlugin, cachingPlugin as cachingPluginFn, createPlugin, createRequestIdGenerator, defaultIsDuplicateKeyError, errorHandlerPlugin, errorHandlerPlugin as errorHandlerPluginFn, _default$2 as gracefulShutdownPlugin, gracefulShutdownPlugin as gracefulShutdownPluginFn, _default$3 as healthPlugin, healthPlugin as healthPluginFn, _default$4 as metricsPlugin, metricsPlugin as metricsPluginFn, _default$5 as pressurePlugin, pressurePlugin as pressurePluginFn, _default$6 as realtimePlugin, realtimePlugin as realtimePluginFn, replyHelpersPlugin, _default$7 as requestIdPlugin, requestIdPlugin as requestIdPluginFn, sanitizeRequestId, _default$8 as schedulesPlugin, schedulesPlugin as schedulesPluginFn, _default$9 as ssePlugin, ssePlugin as ssePluginFn, _default$10 as versioningPlugin, versioningPlugin as versioningPluginFn };
@@ -1,20 +1,20 @@
1
- /*! @classytic/arc v2.40.0 | MIT | https://classytic.com */
2
- import { p as MUTATION_OPERATIONS } from "../constants-BQ2FbhPB.mjs";
1
+ /*! @classytic/arc v2.41.1 | MIT | https://classytic.com */
2
+ import { p as MUTATION_OPERATIONS } from "../constants-D5Ubn1fe.mjs";
3
3
  import { arcLog } from "../logger/index.mjs";
4
- import { c as getOrgId } from "../types-CNQyOrLk.mjs";
5
- import { n as requestContext } from "../requestContext-D4PuGhEw.mjs";
6
- import { t as hasEvents } from "../typeGuards-t_CVgRXN.mjs";
7
- import { t as HookSystem } from "../HookSystem-96nzZLmk.mjs";
8
- import { t as ResourceRegistry } from "../ResourceRegistry-B9WnuG2Q.mjs";
9
- import { a as requestIdPlugin, i as createRequestIdGenerator, n as health_default, o as requestId_default, s as sanitizeRequestId, t as healthPlugin } from "../health-BlGHjkqZ.mjs";
10
- import { n as caching_default, t as cachingPlugin } from "../caching-Bqcn3Brl.mjs";
11
- import { n as errorHandlerPlugin, t as defaultIsDuplicateKeyError } from "../errorHandler-By9keRvX.mjs";
12
- import { n as metrics_default, t as metricsPlugin } from "../metrics-P37sNV5H.mjs";
13
- import { n as realtime_default, t as realtimePlugin } from "../realtime-zzoNLDtj.mjs";
14
- import { t as replyHelpersPlugin } from "../replyHelpers-9h-a592u.mjs";
15
- import { n as schedules_default, t as schedulesPlugin } from "../schedules-Dm21pqHU.mjs";
16
- import { n as sse_default, t as ssePlugin } from "../sse-DbyXoQun.mjs";
17
- import { n as versioning_default, t as versioningPlugin } from "../versioning-BXwmj5SD.mjs";
4
+ import { c as getOrgId } from "../types-CgenB1gv.mjs";
5
+ import { n as requestContext } from "../requestContext-cDkAUr-P.mjs";
6
+ import { t as hasEvents } from "../typeGuards-BLjPgVBm.mjs";
7
+ import { t as HookSystem } from "../HookSystem-CMF94SEA.mjs";
8
+ import { t as ResourceRegistry } from "../ResourceRegistry-MGpXoxHZ.mjs";
9
+ import { a as requestIdPlugin, i as createRequestIdGenerator, n as health_default, o as requestId_default, s as sanitizeRequestId, t as healthPlugin } from "../health-ZRHeDHXH.mjs";
10
+ import { n as caching_default, t as cachingPlugin } from "../caching-DbCBxlgw.mjs";
11
+ import { n as errorHandlerPlugin, t as defaultIsDuplicateKeyError } from "../errorHandler-D1GHKGlV.mjs";
12
+ import { n as metrics_default, t as metricsPlugin } from "../metrics-NMsmpRmY.mjs";
13
+ import { n as realtime_default, t as realtimePlugin } from "../realtime-8tIQWZz4.mjs";
14
+ import { t as replyHelpersPlugin } from "../replyHelpers-CNdvMfM4.mjs";
15
+ import { n as schedules_default, t as schedulesPlugin } from "../schedules-j6uXXAeJ.mjs";
16
+ import { n as sse_default, t as ssePlugin } from "../sse-BeqVKQKu.mjs";
17
+ import { n as versioning_default, t as versioningPlugin } from "../versioning-DKkT4hNJ.mjs";
18
18
  import fp from "fastify-plugin";
19
19
  import { performance as performance$1 } from "node:perf_hooks";
20
20
  //#region src/core/arcCorePlugin.ts
@@ -35,6 +35,7 @@ const arcCorePlugin = async (fastify, opts = {}) => {
35
35
  for (const field of [
36
36
  "_policyFilters",
37
37
  "_ownershipCheck",
38
+ "_crossTenantRead",
38
39
  "fieldMask",
39
40
  "policyMetadata",
40
41
  "document"
@@ -154,18 +155,20 @@ function createPlugin(name, definition) {
154
155
  //#endregion
155
156
  //#region src/plugins/gracefulShutdown.ts
156
157
  const gracefulShutdownPlugin = async (fastify, opts = {}) => {
157
- const { timeout = 3e4, onShutdown, signals = ["SIGTERM", "SIGINT"], logEvents = true, onForceExit = () => process.exit(1) } = opts;
158
- let isShuttingDown = false;
158
+ const { timeout = 3e4, onShutdown, signals = ["SIGTERM", "SIGINT"], logEvents = true, drainDelayMs = 0, onForceExit = () => process.exit(1) } = opts;
159
+ const state = { draining: false };
159
160
  const signalHandlers = /* @__PURE__ */ new Map();
160
161
  const shutdown = async (signal) => {
161
- if (isShuttingDown) {
162
+ if (state.draining) {
162
163
  if (logEvents) fastify.log?.warn?.({ signal }, "Shutdown already in progress, ignoring signal");
163
164
  return;
164
165
  }
165
- isShuttingDown = true;
166
+ state.draining = true;
167
+ state.since = /* @__PURE__ */ new Date();
166
168
  if (logEvents) fastify.log?.info?.({
167
169
  signal,
168
- timeout
170
+ timeout,
171
+ drainDelayMs
169
172
  }, "Shutdown signal received, starting graceful shutdown");
170
173
  const forceExitTimer = setTimeout(() => {
171
174
  if (logEvents) fastify.log?.error?.("Graceful shutdown timeout exceeded, forcing exit");
@@ -173,6 +176,10 @@ const gracefulShutdownPlugin = async (fastify, opts = {}) => {
173
176
  }, timeout);
174
177
  forceExitTimer.unref();
175
178
  try {
179
+ if (drainDelayMs > 0) {
180
+ if (logEvents) fastify.log?.info?.({ drainDelayMs }, "Draining — readiness failing, still serving");
181
+ await new Promise((resolve) => setTimeout(resolve, drainDelayMs));
182
+ }
176
183
  if (logEvents) fastify.log?.info?.("Closing server to new connections");
177
184
  await fastify.close();
178
185
  if (onShutdown) {
@@ -201,7 +208,11 @@ const gracefulShutdownPlugin = async (fastify, opts = {}) => {
201
208
  fastify.decorate("shutdown", async () => {
202
209
  await shutdown("MANUAL");
203
210
  });
204
- if (logEvents) fastify.log?.debug?.({ signals }, "Graceful shutdown plugin registered");
211
+ fastify.decorate("shutdownState", state);
212
+ if (logEvents) fastify.log?.debug?.({
213
+ signals,
214
+ drainDelayMs
215
+ }, "Graceful shutdown plugin registered");
205
216
  };
206
217
  var gracefulShutdown_default = fp(gracefulShutdownPlugin, {
207
218
  name: "arc-graceful-shutdown",
@@ -1,5 +1,6 @@
1
- /*! @classytic/arc v2.40.0 | MIT | https://classytic.com */
2
- import { t as hasEvents } from "../typeGuards-t_CVgRXN.mjs";
1
+ /*! @classytic/arc v2.41.1 | MIT | https://classytic.com */
2
+ import { n as declareRuntimeCapability } from "../runtimeCapabilities-CkhS6jn6.mjs";
3
+ import { t as hasEvents } from "../typeGuards-BLjPgVBm.mjs";
3
4
  import fp from "fastify-plugin";
4
5
  //#region src/plugins/response-cache.ts
5
6
  /**
@@ -96,6 +97,12 @@ const responseCachePluginImpl = async (fastify, opts = {}) => {
96
97
  if (maxEntries > 5e3) fastify.log.warn(`[arc] responseCache maxEntries=${maxEntries}: prefix invalidation scans all entries synchronously on every mutating request — expect event-loop pauses at this size. This cache is an instance-local micro-cache; for large shared caching use the queryCache with a Redis store instead.`);
97
98
  const cache = new LRUCache(maxEntries);
98
99
  const invalidateMethods = new Set(invalidateOn.map((m) => m.toUpperCase()));
100
+ declareRuntimeCapability(fastify, {
101
+ subsystem: "http.response-cache",
102
+ durability: "memory",
103
+ accepted: true,
104
+ detail: "per-replica micro-cache; correctness comes from short TTLs, not shared state"
105
+ });
99
106
  if (!fastify.hasRequestDecorator("__arcCacheTTL")) fastify.decorateRequest("__arcCacheTTL", void 0);
100
107
  /** Find TTL for a given URL path (seconds) */
101
108
  function getTTL(url) {
@@ -1,4 +1,4 @@
1
- /*! @classytic/arc v2.40.0 | MIT | https://classytic.com */
1
+ /*! @classytic/arc v2.41.1 | MIT | https://classytic.com */
2
2
  import { createRequire } from "node:module";
3
3
  import fp from "fastify-plugin";
4
4
  //#region src/plugins/tracing.ts
@@ -59,7 +59,7 @@ try {
59
59
  function createTracerProvider(options) {
60
60
  if (!isAvailable || !NodeTracerProvider || !BatchSpanProcessor || !OTLPTraceExporter) return null;
61
61
  const { serviceName = "@classytic/arc", serviceVersion, exporterUrl = "http://localhost:4318/v1/traces" } = options;
62
- const resolvedVersion = serviceVersion ?? "2.40.0";
62
+ const resolvedVersion = serviceVersion ?? "2.41.1";
63
63
  const exporter = new OTLPTraceExporter({ url: exporterUrl });
64
64
  const provider = new NodeTracerProvider({ resource: { attributes: {
65
65
  "service.name": serviceName,
@@ -1,4 +1,4 @@
1
- /*! @classytic/arc v2.40.0 | MIT | https://classytic.com */
1
+ /*! @classytic/arc v2.41.1 | MIT | https://classytic.com */
2
2
  //#region src/utils/pluralize.ts
3
3
  /**
4
4
  * Lightweight English pluralization for the Arc CLI.
@@ -1,15 +1,15 @@
1
1
 
2
- import { It as Authenticator } from "./index-D3sIPcD4.mjs";
2
+ import { It as Authenticator } from "./index-DHbyPo5j.mjs";
3
3
  import { r as CacheStore } from "./interface-BOk3YnTz.mjs";
4
4
  import { n as ElevationOptions } from "./elevation-BXCfVcNG.mjs";
5
- import { a as EventTransport } from "./EventTransport-BPOQsEer.mjs";
5
+ import { a as EventTransport } from "./EventTransport-CbUKDzZZ.mjs";
6
6
  import { t as ExternalOpenApiPaths } from "./externalPaths-CmGOxOtD.mjs";
7
7
  import { r as QueryCachePluginOptions } from "./queryCachePlugin-BzqxconG.mjs";
8
8
  import { t as ErrorHandlerOptions } from "./errorHandler--4rSMMvq.mjs";
9
- import { l as ResourceLike, m as SchedulesPluginOptions, n as ArcModuleInput, v as HealthOptions } from "./types-BVlowlm7.mjs";
10
- import "./index-DbMglURO.mjs";
11
- import { t as EventPluginOptions } from "./eventPlugin-DfHaKEWQ.mjs";
12
- import { g as CachingOptions, o as MetricsOptions, p as SSEOptions, t as VersioningOptions, u as RealtimeOptions } from "./versioning-P3V6ATyC.mjs";
9
+ import { l as ResourceLike, m as SchedulesPluginOptions, n as ArcModuleInput, v as HealthOptions } from "./types-DfVDvkdP.mjs";
10
+ import "./index-CFfLKMH4.mjs";
11
+ import { t as EventPluginOptions } from "./eventPlugin-B52abJ8W.mjs";
12
+ import { b as GracefulShutdownOptions, g as CachingOptions, o as MetricsOptions, p as SSEOptions, t as VersioningOptions, u as RealtimeOptions } from "./versioning-D6JIK8Tk.mjs";
13
13
  import { r as IdempotencyStore } from "./interface-DEWNOMza.mjs";
14
14
  import { FastifyInstance, FastifyPluginAsync, FastifyReply, FastifyRequest, FastifyServerOptions } from "fastify";
15
15
  //#region src/factory/types/assets.d.ts
@@ -652,8 +652,13 @@ interface CreateAppOptions {
652
652
  * ```
653
653
  */
654
654
  health?: boolean | HealthOptions;
655
- /** Graceful shutdown handling (default: true) */
656
- gracefulShutdown?: boolean;
655
+ /**
656
+ * Graceful shutdown handling (default: true). Pass `GracefulShutdownOptions`
657
+ * to configure it inline — in particular `drainDelayMs`, the lame-duck
658
+ * window behind a load balancer (readiness fails first, the server keeps
659
+ * serving until the LB has deregistered the instance, then closes).
660
+ */
661
+ gracefulShutdown?: boolean | GracefulShutdownOptions;
657
662
  /** Emit events for CRUD operations (default: true) */
658
663
  emitEvents?: boolean;
659
664
  /**
@@ -1,12 +1,12 @@
1
- /*! @classytic/arc v2.40.0 | MIT | https://classytic.com */
2
- import { t as __exportAll } from "./rolldown-runtime-DzE6ikSH.mjs";
1
+ /*! @classytic/arc v2.41.1 | MIT | https://classytic.com */
2
+ import { t as __exportAll } from "./rolldown-runtime-eysyzFAF.mjs";
3
3
  import { arcLog, configureArcLogger, createPinoWriter, enterArcLoggerScope, runWithArcLogger } from "./logger/index.mjs";
4
- import { n as PUBLIC_SCOPE } from "./types-CNQyOrLk.mjs";
5
- import { t as auditRuntimeCapabilities } from "./runtimeCapabilities-Bo7rSKjm.mjs";
6
- import { n as isScopeAwareKeyGenerator, t as createTenantKeyGenerator } from "./rateLimitKey-umrS6-vu.mjs";
7
- import { i as createRequestIdGenerator, r as mergeHealthChecks } from "./health-BlGHjkqZ.mjs";
8
- import { t as parseJsonBody } from "./jsonBody-Dx6jH_Nb.mjs";
9
- import { r as wrapWithBoundary } from "./subscribe-helpers-KgVEcn8L.mjs";
4
+ import { n as PUBLIC_SCOPE } from "./types-CgenB1gv.mjs";
5
+ import { r as initRuntimeCapabilityRegistry, t as auditRuntimeCapabilities } from "./runtimeCapabilities-CkhS6jn6.mjs";
6
+ import { n as isScopeAwareKeyGenerator, t as createTenantKeyGenerator } from "./rateLimitKey-BvOvcBDi.mjs";
7
+ import { i as createRequestIdGenerator, r as mergeHealthChecks } from "./health-ZRHeDHXH.mjs";
8
+ import { t as parseJsonBody } from "./jsonBody-ELR9lfeU.mjs";
9
+ import { r as wrapWithBoundary } from "./subscribe-helpers-DatDkcuG.mjs";
10
10
  import { isProductionEnv } from "@classytic/primitives/environment";
11
11
  import Fastify from "fastify";
12
12
  import qs from "qs";
@@ -851,7 +851,7 @@ async function registerArcCore(fastify, config, trackPlugin) {
851
851
  await fastify.register(arcCorePlugin, { emitEvents: config.arcPlugins?.emitEvents !== false });
852
852
  trackPlugin("arc-core");
853
853
  if (config.arcPlugins?.events !== false) {
854
- const { default: eventPlugin } = await import("./eventPlugin-DB26V7uX.mjs").then((n) => n.n);
854
+ const { default: eventPlugin } = await import("./eventPlugin-zZY-unhr.mjs").then((n) => n.n);
855
855
  const eventOpts = typeof config.arcPlugins?.events === "object" ? config.arcPlugins.events : {};
856
856
  await fastify.register(eventPlugin, {
857
857
  /**
@@ -906,19 +906,20 @@ async function registerArcPlugins(fastify, config, trackPlugin, modules, moduleH
906
906
  trackPlugin("arc-health", { checkCount: healthOpts.checks?.length ?? 0 });
907
907
  }
908
908
  if (config.arcPlugins?.gracefulShutdown !== false) {
909
- await fastify.register(gracefulShutdownPlugin);
910
- trackPlugin("arc-graceful-shutdown");
909
+ const shutdownOpts = typeof config.arcPlugins?.gracefulShutdown === "object" ? config.arcPlugins.gracefulShutdown : {};
910
+ await fastify.register(gracefulShutdownPlugin, shutdownOpts);
911
+ trackPlugin("arc-graceful-shutdown", shutdownOpts);
911
912
  }
912
913
  if (config.arcPlugins?.caching) {
913
- const { default: cachingPlugin } = await import("./caching-Bqcn3Brl.mjs").then((n) => n.r);
914
+ const { default: cachingPlugin } = await import("./caching-DbCBxlgw.mjs").then((n) => n.r);
914
915
  const opts = config.arcPlugins.caching === true ? {} : config.arcPlugins.caching;
915
916
  await fastify.register(cachingPlugin, opts);
916
917
  trackPlugin("arc-caching", opts);
917
918
  }
918
919
  if (config.arcPlugins?.queryCache) {
919
- const { queryCachePlugin } = await import("./queryCachePlugin-2Pp9w-8D.mjs").then((n) => n.n);
920
+ const { queryCachePlugin } = await import("./queryCachePlugin-DZHPOm9p.mjs").then((n) => n.n);
920
921
  const opts = config.arcPlugins.queryCache === true ? {} : config.arcPlugins.queryCache;
921
- const store = config.stores?.queryCache ?? new (await (import("./memory-CNvjxzB2.mjs").then((n) => n.n))).MemoryCacheStore();
922
+ const store = config.stores?.queryCache ?? new (await (import("./memory-Iotz0cCx.mjs").then((n) => n.n))).MemoryCacheStore();
922
923
  await fastify.register(queryCachePlugin, {
923
924
  store,
924
925
  ...opts
@@ -928,7 +929,7 @@ async function registerArcPlugins(fastify, config, trackPlugin, modules, moduleH
928
929
  if (config.arcPlugins?.sse) {
929
930
  if (config.arcPlugins?.events === false) fastify.log.warn("SSE plugin requires events plugin (arcPlugins.events). SSE disabled.");
930
931
  else {
931
- const { default: ssePlugin } = await import("./sse-DbyXoQun.mjs").then((n) => n.r);
932
+ const { default: ssePlugin } = await import("./sse-BeqVKQKu.mjs").then((n) => n.r);
932
933
  const opts = config.arcPlugins.sse === true ? {} : config.arcPlugins.sse;
933
934
  await fastify.register(ssePlugin, opts);
934
935
  trackPlugin("arc-sse", opts);
@@ -937,20 +938,20 @@ async function registerArcPlugins(fastify, config, trackPlugin, modules, moduleH
937
938
  if (config.arcPlugins?.realtime) {
938
939
  if (config.arcPlugins?.events === false) fastify.log.warn("Realtime plugin requires events plugin (arcPlugins.events). Realtime disabled.");
939
940
  else {
940
- const { default: realtimePlugin } = await import("./realtime-zzoNLDtj.mjs").then((n) => n.r);
941
+ const { default: realtimePlugin } = await import("./realtime-8tIQWZz4.mjs").then((n) => n.r);
941
942
  const opts = config.arcPlugins.realtime === true ? {} : config.arcPlugins.realtime;
942
943
  await fastify.register(realtimePlugin, opts);
943
944
  trackPlugin("arc-realtime", opts);
944
945
  }
945
946
  }
946
947
  if (config.arcPlugins?.metrics) {
947
- const { default: metricsPlugin } = await import("./metrics-P37sNV5H.mjs").then((n) => n.r);
948
+ const { default: metricsPlugin } = await import("./metrics-NMsmpRmY.mjs").then((n) => n.r);
948
949
  const opts = config.arcPlugins.metrics === true ? {} : config.arcPlugins.metrics;
949
950
  await fastify.register(metricsPlugin, opts);
950
951
  trackPlugin("arc-metrics", opts);
951
952
  }
952
953
  if (config.arcPlugins?.versioning) {
953
- const { default: versioningPlugin } = await import("./versioning-BXwmj5SD.mjs").then((n) => n.r);
954
+ const { default: versioningPlugin } = await import("./versioning-DKkT4hNJ.mjs").then((n) => n.r);
954
955
  await fastify.register(versioningPlugin, config.arcPlugins.versioning);
955
956
  trackPlugin("arc-versioning", config.arcPlugins.versioning);
956
957
  }
@@ -1027,7 +1028,7 @@ async function registerAuth(fastify, config, trackPlugin) {
1027
1028
  */
1028
1029
  async function registerElevation(fastify, config, trackPlugin) {
1029
1030
  if (!config.elevation) return;
1030
- const { elevationPlugin } = await import("./elevation-CZy6n50b.mjs").then((n) => n.r);
1031
+ const { elevationPlugin } = await import("./elevation-CkvG0LG2.mjs").then((n) => n.r);
1031
1032
  await fastify.register(elevationPlugin, config.elevation);
1032
1033
  trackPlugin("arc-elevation", config.elevation);
1033
1034
  fastify.log.debug("Elevation plugin enabled");
@@ -1042,7 +1043,7 @@ async function registerElevation(fastify, config, trackPlugin) {
1042
1043
  */
1043
1044
  async function registerErrorHandler(fastify, config, trackPlugin, moduleErrorMappers) {
1044
1045
  if (config.errorHandler === false) return;
1045
- const { errorHandlerPlugin } = await import("./errorHandler-By9keRvX.mjs").then((n) => n.r);
1046
+ const { errorHandlerPlugin } = await import("./errorHandler-D1GHKGlV.mjs").then((n) => n.r);
1046
1047
  /**
1047
1048
  * DISCLOSURE DEFAULTS MERGE — they used to be REPLACED, and that was two bugs.
1048
1049
  *
@@ -1266,7 +1267,7 @@ async function registerResources(fastify, config, preResolvedModules) {
1266
1267
  let discoveryPath;
1267
1268
  let discoveryYieldedZero = false;
1268
1269
  if (resolvedResources === void 0 && config.resourceDir) {
1269
- const { loadResources } = await import("./loadResources-E5UFlRrT.mjs").then((n) => n.n);
1270
+ const { loadResources } = await import("./loadResources-BPfWiI5s.mjs").then((n) => n.n);
1270
1271
  const { resolve, dirname } = await import("node:path");
1271
1272
  const { fileURLToPath } = await import("node:url");
1272
1273
  const rawDir = config.resourceDir;
@@ -1392,7 +1393,7 @@ Resources supplied by "${m.name}": ${provided}.`);
1392
1393
  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.");
1393
1394
  const frozenJobs = Object.freeze(scheduledJobs.map((job) => Object.freeze({ ...job })));
1394
1395
  const scheduleOptions = typeof config.arcPlugins?.schedules === "object" ? config.arcPlugins.schedules : {};
1395
- const { default: schedulesPlugin } = await import("./schedules-Dm21pqHU.mjs").then((n) => n.r);
1396
+ const { default: schedulesPlugin } = await import("./schedules-j6uXXAeJ.mjs").then((n) => n.r);
1396
1397
  await fastify.register(schedulesPlugin, {
1397
1398
  ...scheduleOptions,
1398
1399
  schedules: frozenJobs
@@ -1648,8 +1649,9 @@ async function registerSecurityPlugins(fastify, config) {
1648
1649
  timeWindow: "1 minute"
1649
1650
  });
1650
1651
  await fastify.register(rateLimit, rateLimitOpts);
1651
- if (!(typeof rateLimitOpts === "object" && "store" in rateLimitOpts)) {
1652
- if (config.runtime === "distributed") throw new Error("[Arc] runtime: 'distributed' with rate limiting requires a shared store.\nProvide rateLimit: { store: new RedisStore({ ... }) } or disable rate limiting: rateLimit: false");
1652
+ const rateLimitRecord = typeof rateLimitOpts === "object" && rateLimitOpts !== null ? rateLimitOpts : void 0;
1653
+ if (!(rateLimitRecord !== void 0 && (rateLimitRecord.store != null || rateLimitRecord.redis != null))) {
1654
+ if (config.runtime === "distributed") throw new Error("[Arc] runtime: 'distributed' with rate limiting requires a shared store.\nProvide rateLimit: { redis: ioredisClient } (or { store: CustomStore }) or disable rate limiting: rateLimit: false");
1653
1655
  else if (config.preset === "production") fastify.log.warn("Rate limiting is using in-memory store. In multi-instance deployments, each instance tracks limits independently. Configure a Redis store for distributed rate limiting.");
1654
1656
  }
1655
1657
  fastify.log.debug("Rate limiting enabled");
@@ -1790,6 +1792,7 @@ const DEFAULT_LOGGER_REDACT_PATHS = [
1790
1792
  "req.headers.authorization",
1791
1793
  "req.headers[\"x-api-key\"]",
1792
1794
  "req.headers[\"x-internal-api-key\"]",
1795
+ "req.headers[\"x-device-token\"]",
1793
1796
  "req.headers.cookie",
1794
1797
  "req.headers[\"set-cookie\"]",
1795
1798
  "res.headers[\"set-cookie\"]",
@@ -1861,7 +1864,10 @@ function validateDistributedRuntime(options) {
1861
1864
  });
1862
1865
  }
1863
1866
  const schedules = options.arcPlugins?.schedules;
1864
- if (schedules && typeof schedules === "object" && schedules.enabled !== false && !schedules.lock) deferredWarnings.push("runtime: 'distributed' — schedules configured without a `lock` adapter. EVERY replica will fire every schedule tick. Pass an ecosystem lock (e.g. createMongoLockAdapter) under arcPlugins.schedules.lock for single-firer leases.");
1867
+ if (schedules && typeof schedules === "object" && schedules.enabled !== false && !schedules.lock && !schedules.singleReplica) missing.push({
1868
+ key: "arcPlugins.schedules.lock",
1869
+ hint: "a LockAdapter for single-firer leases (e.g. createMongoLockAdapter) — or `arcPlugins.schedules.singleReplica: true` if exactly one replica arms schedules"
1870
+ });
1865
1871
  if (missing.length > 0) {
1866
1872
  const lines = missing.map((m) => ` • ${m.key.padEnd(20)} → ${m.hint}`).join("\n");
1867
1873
  throw new Error(`[Arc] runtime: 'distributed' requires durable (Redis/MongoDB) adapters, not memory.
@@ -1986,6 +1992,7 @@ async function buildApp(options, loggerScope, onInstance) {
1986
1992
  } }
1987
1993
  });
1988
1994
  onInstance(fastify);
1995
+ initRuntimeCapabilityRegistry(fastify);
1989
1996
  if (resolvedLogger !== false) {
1990
1997
  const writer = createPinoWriter(fastify.log);
1991
1998
  loggerScope.writer = writer;
@@ -2048,7 +2055,7 @@ async function buildApp(options, loggerScope, onInstance) {
2048
2055
  await registerErrorHandler(fastify, config, trackPlugin, moduleErrorMappers);
2049
2056
  await registerResources(fastify, config, resolvedModules);
2050
2057
  if (config.replyHelpers) {
2051
- const { replyHelpersPlugin } = await import("./replyHelpers-9h-a592u.mjs").then((n) => n.n);
2058
+ const { replyHelpersPlugin } = await import("./replyHelpers-CNdvMfM4.mjs").then((n) => n.n);
2052
2059
  await fastify.register(replyHelpersPlugin);
2053
2060
  }
2054
2061
  if (config.serializeBigInt) {
@@ -1,7 +1,7 @@
1
1
 
2
2
  import { i as RequestScope } from "../types-Ddl9OjmE.mjs";
3
- import { l as PermissionCheck } from "../base-C-NL2fTi.mjs";
4
- import { Y as PresetResult } from "../index-D3sIPcD4.mjs";
3
+ import { l as PermissionCheck } from "../base-CxrV1LmG.mjs";
4
+ import { Y as PresetResult } from "../index-DHbyPo5j.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.40.0 | MIT | https://classytic.com */
2
- import { i as NotFoundError, u as ValidationError } from "../errors-BHD3L8of.mjs";
3
- import { c as getOrgId, g as getUserId } from "../types-CNQyOrLk.mjs";
4
- import { B as requireAuth, F as allowPublic } from "../permissions-C0GpDsB_.mjs";
5
- import { t as multipartBody } from "../multipartBody-Bl_gU3Wg.mjs";
1
+ /*! @classytic/arc v2.41.1 | MIT | https://classytic.com */
2
+ import { i as NotFoundError, u as ValidationError } from "../errors-BH7YU_9O.mjs";
3
+ import { c as getOrgId, g as getUserId } from "../types-CgenB1gv.mjs";
4
+ import { B as requireAuth, F as allowPublic } from "../permissions-D8TBH44E.mjs";
5
+ import { t as multipartBody } from "../multipartBody-DtRWM6_U.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-C-NL2fTi.mjs";
3
- import { Fn as IControllerResponse, In as IRequestContext, U as ResourceConfig, Y as PresetResult, d as PaginationResult } from "../index-D3sIPcD4.mjs";
2
+ import { t as AnyRecord } from "../base-CxrV1LmG.mjs";
3
+ import { Fn as IControllerResponse, In as IRequestContext, U as ResourceConfig, Y as PresetResult, d as PaginationResult } from "../index-DHbyPo5j.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.40.0 | MIT | https://classytic.com */
1
+ /*! @classytic/arc v2.41.1 | 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-CAaqVVcW.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-ZJSPgmT2.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-D3sIPcD4.mjs";
2
+ import { X as CrudRouteKey, Y as PresetResult } from "../index-DHbyPo5j.mjs";
3
3
  //#region src/presets/multiTenant.d.ts
4
4
  /**
5
5
  * One tenant dimension for multi-field filtering. Discriminated by source:
@@ -65,6 +65,43 @@ interface MultiTenantOptions {
65
65
  * multiTenantPreset({ allowPublic: ['list', 'get'] })
66
66
  */
67
67
  allowPublic?: CrudRouteKey[];
68
+ /**
69
+ * Reads that are NOT scoped to a tenant at all — a marketplace listing.
70
+ *
71
+ * `allowPublic` answers "may an anonymous caller reach this route?". That is a different
72
+ * question from "is this listing scoped to a tenant?", and until this option there was no way
73
+ * to answer the second one with "no". An `allowPublic` route still filters by the caller's
74
+ * organization whenever they have one, and a signed-in user of a multi-org product ALWAYS has
75
+ * one, because choosing an organization writes it onto the session. So a cross-tenant catalog
76
+ * silently became single-tenant the moment one of its own sellers browsed it: the same URL,
77
+ * with no header and no query parameter to explain the difference, returned a different world
78
+ * depending on who was asking.
79
+ *
80
+ * The only escape was to drop this preset from the resource entirely, which also drops tenant
81
+ * INJECTION on create/update — the thing that stops a member moving a document into another
82
+ * organization. Trading a write guarantee for a read behaviour is not a real choice.
83
+ *
84
+ * Listed routes get no tenant middleware: no filter, and nothing stashed for the repo layer.
85
+ * Everything else keeps its filter, so writes are unaffected.
86
+ *
87
+ * READS ONLY, by type. A cross-tenant `create` or `update` is not a listing, it is an escape
88
+ * from tenancy, and it should never be one option away by accident.
89
+ *
90
+ * THIS DOES NOT AUTHORIZE ANYTHING. Removing the tenant filter widens what the query can
91
+ * return, so the resource owes a row-level `policy` from its permission check saying what a
92
+ * stranger may see (`{ status: 'active' }`, `{ visibility: 'public' }`). Declaring this without
93
+ * one publishes every row in every tenant.
94
+ *
95
+ * @default []
96
+ * @example
97
+ * // A public catalog: anyone may browse every seller's LIVE products,
98
+ * // while create/update/delete stay pinned to the caller's own shop.
99
+ * multiTenantPreset({
100
+ * allowPublic: ['list', 'get'],
101
+ * crossTenant: ['list', 'get'],
102
+ * })
103
+ */
104
+ crossTenant?: readonly Extract<CrudRouteKey, "list" | "get">[];
68
105
  }
69
106
  declare function multiTenantPreset(options?: MultiTenantOptions): PresetResult;
70
107
  //#endregion
@@ -1,6 +1,6 @@
1
- /*! @classytic/arc v2.40.0 | MIT | https://classytic.com */
2
- import "../constants-BQ2FbhPB.mjs";
3
- import { b as isElevated, c as getOrgId, d as getScopeContext, m as getTeamId, u as getRequestScope, v as hasOrgAccess } from "../types-CNQyOrLk.mjs";
1
+ /*! @classytic/arc v2.41.1 | MIT | https://classytic.com */
2
+ import "../constants-D5Ubn1fe.mjs";
3
+ import { b as isElevated, c as getOrgId, d as getScopeContext, m as getTeamId, u as getRequestScope, v as hasOrgAccess } from "../types-CgenB1gv.mjs";
4
4
  //#region src/presets/multiTenant.ts
5
5
  /**
6
6
  * Resolve a single TenantFieldSpec against the current scope.
@@ -162,7 +162,7 @@ function createTenantInjection(specs) {
162
162
  };
163
163
  }
164
164
  function multiTenantPreset(options = {}) {
165
- const { tenantField, tenantFields, allowPublic = [] } = options;
165
+ const { tenantField, tenantFields, allowPublic = [], crossTenant = [] } = options;
166
166
  if (tenantField !== void 0 && tenantFields !== void 0) throw new Error("multiTenantPreset: pass either `tenantField` (single-field) or `tenantFields` (multi-field), not both");
167
167
  const specs = tenantFields ?? [{
168
168
  field: tenantField ?? "organizationId",
@@ -173,6 +173,24 @@ function multiTenantPreset(options = {}) {
173
173
  const flexibleTenantFilter = createFlexibleTenantFilter(specs);
174
174
  const tenantInjection = createTenantInjection(specs);
175
175
  const getFilter = (route) => allowPublic.includes(route) ? flexibleTenantFilter : strictTenantFilter;
176
+ /**
177
+ * A cross-tenant read MARKS the request rather than simply skipping the filter.
178
+ *
179
+ * Skipping is not enough, because this middleware is only the first of four places that scope a
180
+ * read, and the other three each re-derive the organization from the request scope on their own:
181
+ * `QueryResolver` adds the tenant filter for `list`, `buildTenantRepoOptions` forwards it to the
182
+ * repository as tenant context, and `checkOrgScope` re-checks it post-fetch on `get`. Removing
183
+ * one of four changes nothing observable, which is exactly what made this look unfixable from the
184
+ * preset: the listing stayed single-tenant no matter what the preset decided.
185
+ *
186
+ * So the decision travels on the request instead, and each of those layers honours it. The
187
+ * repository layer needs no change of its own — kits already accept `bypassTenant`, which is the
188
+ * same door elevated cross-tenant reads have always used.
189
+ */
190
+ const markCrossTenantRead = async (request) => {
191
+ request._crossTenantRead = true;
192
+ };
193
+ const readMiddleware = (route) => crossTenant.includes(route) ? [markCrossTenantRead] : [getFilter(route)];
176
194
  const fieldRules = {};
177
195
  for (const spec of specs) fieldRules[spec.field] = {
178
196
  systemManaged: true,
@@ -182,8 +200,8 @@ function multiTenantPreset(options = {}) {
182
200
  name: "multiTenant",
183
201
  schemaOptions: { fieldRules },
184
202
  middlewares: {
185
- list: [getFilter("list")],
186
- get: [getFilter("get")],
203
+ list: readMiddleware("list"),
204
+ get: readMiddleware("get"),
187
205
  create: [tenantInjection],
188
206
  update: [getFilter("update"), tenantInjection],
189
207
  delete: [getFilter("delete")],
@@ -1,6 +1,6 @@
1
1
 
2
- import { l as PermissionCheck } from "../base-C-NL2fTi.mjs";
3
- import { At as RouteDefinition, Y as PresetResult, jn as ControllerHandler, jt as RouteMcpConfig } from "../index-D3sIPcD4.mjs";
2
+ import { l as PermissionCheck } from "../base-CxrV1LmG.mjs";
3
+ import { At as RouteDefinition, Y as PresetResult, jn as ControllerHandler, jt as RouteMcpConfig } from "../index-DHbyPo5j.mjs";
4
4
  //#region src/presets/search.d.ts
5
5
  /**
6
6
  * Search Preset — backend-agnostic search / vector / embed routes
@@ -1,5 +1,5 @@
1
- /*! @classytic/arc v2.40.0 | MIT | https://classytic.com */
2
- import { B as requireAuth, F as allowPublic } from "../permissions-C0GpDsB_.mjs";
1
+ /*! @classytic/arc v2.41.1 | MIT | https://classytic.com */
2
+ import { B as requireAuth, F as allowPublic } from "../permissions-D8TBH44E.mjs";
3
3
  //#region src/presets/search.ts
4
4
  const BUILTINS = [
5
5
  {
@@ -1,7 +1,7 @@
1
- /*! @classytic/arc v2.40.0 | MIT | https://classytic.com */
2
- import { l as UnauthorizedError } from "./errors-BHD3L8of.mjs";
3
- import { b as isElevated, n as PUBLIC_SCOPE } from "./types-CNQyOrLk.mjs";
4
- import { B as requireAuth, F as allowPublic, U as requireRoles } from "./permissions-C0GpDsB_.mjs";
1
+ /*! @classytic/arc v2.41.1 | MIT | https://classytic.com */
2
+ import { l as UnauthorizedError } from "./errors-BH7YU_9O.mjs";
3
+ import { b as isElevated, n as PUBLIC_SCOPE } from "./types-CgenB1gv.mjs";
4
+ import { B as requireAuth, F as allowPublic, U as requireRoles } from "./permissions-D8TBH44E.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.40.0 | MIT | https://classytic.com */
2
- import { c as getOrgId, f as getScopeContextMap, g as getUserId, m as getTeamId, x as isMember } from "./types-CNQyOrLk.mjs";
1
+ /*! @classytic/arc v2.41.1 | MIT | https://classytic.com */
2
+ import { c as getOrgId, f as getScopeContextMap, g as getUserId, m as getTeamId, x as isMember } from "./types-CgenB1gv.mjs";
3
3
  //#region src/scope/projection.ts
4
4
  /**
5
5
  * Compute the request-scope projection. Returns `undefined` when no