@classytic/arc 2.41.1 → 2.44.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 (248) hide show
  1. package/dist/{EventTransport-CbUKDzZZ.d.mts → EventTransport-CiicKz2Z.d.mts} +1 -1
  2. package/dist/{EventTransport-DSqD_Xsg.mjs → EventTransport-DZWDsaPa.mjs} +22 -17
  3. package/dist/{HookSystem-CMF94SEA.mjs → HookSystem-yjEgn7Cm.mjs} +2 -2
  4. package/dist/{QueryCache-DFA-QrU9.d.mts → QueryCache--5uAuYPW.d.mts} +7 -3
  5. package/dist/{ResourceRegistry-MGpXoxHZ.mjs → ResourceRegistry-DJxHUpE4.mjs} +44 -13
  6. package/dist/acquire-BozJ-9yt.mjs +35 -0
  7. package/dist/actionPermissions-5N04zBL4.mjs +772 -0
  8. package/dist/audit/index.d.mts +46 -8
  9. package/dist/audit/index.mjs +48 -26
  10. package/dist/auth/audit.d.mts +8 -8
  11. package/dist/auth/audit.mjs +1 -1
  12. package/dist/auth/index.d.mts +46 -16
  13. package/dist/auth/index.mjs +134 -156
  14. package/dist/auth/redis-session.d.mts +4 -5
  15. package/dist/auth/redis-session.mjs +1 -1
  16. package/dist/{authorizationDecision-DawKGoXb.mjs → authorizationDecision-wX3SA9w_.mjs} +7 -102
  17. package/dist/{base-CxrV1LmG.d.mts → base-CZyixEQR.d.mts} +27 -5
  18. package/dist/boundedMap-DWiKW7lP.mjs +57 -0
  19. package/dist/{buildHandler-CvxKOc0m.mjs → buildHandler-Bswu_vNb.mjs} +4 -4
  20. package/dist/cache/index.d.mts +31 -43
  21. package/dist/cache/index.mjs +4 -4
  22. package/dist/{caching-DbCBxlgw.mjs → caching-Drp68DIS.mjs} +3 -3
  23. package/dist/claimed-DYvQoZLv.mjs +8 -0
  24. package/dist/cleanup/index.d.mts +18 -22
  25. package/dist/cleanup/index.mjs +27 -73
  26. package/dist/cli/commands/describe.d.mts +7 -5
  27. package/dist/cli/commands/describe.mjs +7 -4
  28. package/dist/cli/commands/docs.d.mts +2 -3
  29. package/dist/cli/commands/docs.mjs +3 -3
  30. package/dist/cli/commands/doctor.d.mts +2 -3
  31. package/dist/cli/commands/doctor.mjs +1 -1
  32. package/dist/cli/commands/generate.mjs +2 -3
  33. package/dist/cli/commands/init.mjs +2 -3
  34. package/dist/cli/commands/introspect.d.mts +2 -3
  35. package/dist/cli/commands/introspect.mjs +2 -2
  36. package/dist/cli/index.mjs +3 -3
  37. package/dist/{constants-D5Ubn1fe.mjs → constants-B2QHb6u6.mjs} +4 -2
  38. package/dist/context/index.d.mts +11 -14
  39. package/dist/context/index.mjs +18 -38
  40. package/dist/core/index.d.mts +3 -3
  41. package/dist/core/index.mjs +8 -8
  42. package/dist/{core-5lazhva9.mjs → core-COcqo0Cp.mjs} +15 -5
  43. package/dist/{createActionRouter-DNqoaK81.mjs → createActionRouter-zG13rpPJ.mjs} +97 -39
  44. package/dist/{createAggregationRouter-BcbQJf10.mjs → createAggregationRouter-CWRz96Vr.mjs} +11 -8
  45. package/dist/{preloadResources-f3boenVZ.mjs → createApp-BGLYtErt.mjs} +305 -394
  46. package/dist/{createMcpServer-CbDWkZ1T.d.mts → createMcpServer-DSztofa9.d.mts} +1 -1
  47. package/dist/{defineAggregation-D_eJjm5A.mjs → defineAggregation--3xguLkk.mjs} +1 -1
  48. package/dist/{defineResource-DxkMTjRn.mjs → defineResource-gUp9ug66.mjs} +118 -1024
  49. package/dist/discovery/index.d.mts +6 -7
  50. package/dist/discovery/index.mjs +1 -1
  51. package/dist/docs/index.d.mts +11 -5
  52. package/dist/docs/index.mjs +4 -4
  53. package/dist/{elevation-CkvG0LG2.mjs → elevation-BG4gnMsJ.mjs} +41 -12
  54. package/dist/{elevation-BXCfVcNG.d.mts → elevation-BW2J3tNj.d.mts} +1 -2
  55. package/dist/encryption/index.d.mts +5 -5
  56. package/dist/encryption/index.mjs +3 -3
  57. package/dist/{errorHandler-D1GHKGlV.mjs → errorHandler-COHw3N1b.mjs} +26 -5
  58. package/dist/{errors-BH7YU_9O.mjs → errors-CW4anJBy.mjs} +58 -23
  59. package/dist/{errors-BGdVwP25.d.mts → errors-JYc9vwqE.d.mts} +9 -0
  60. package/dist/{eventPlugin-B52abJ8W.d.mts → eventPlugin-BtmA4-3P.d.mts} +65 -9
  61. package/dist/{eventPlugin-zZY-unhr.mjs → eventPlugin-BweHAVws.mjs} +82 -21
  62. package/dist/{subscribe-helpers-DatDkcuG.mjs → eventTypes-N4-omQFR.mjs} +151 -17
  63. package/dist/events/index.d.mts +43 -18
  64. package/dist/events/index.mjs +49 -53
  65. package/dist/events/transports/redis-stream-entry.d.mts +1 -1
  66. package/dist/events/transports/redis-stream-entry.mjs +139 -157
  67. package/dist/events/transports/redis.d.mts +5 -6
  68. package/dist/events/transports/redis.mjs +2 -2
  69. package/dist/factory/index.d.mts +42 -27
  70. package/dist/factory/index.mjs +213 -32
  71. package/dist/{fastify-augmentation-Cq2nHVaW.d.mts → fastify-augmentation-DRlCIWE9.d.mts} +11 -25
  72. package/dist/{fields-BHrkoF5y.mjs → fields-A5crhsJb.mjs} +1 -1
  73. package/dist/{fields-I_EeRWND.d.mts → fields-BBSpf8pS.d.mts} +1 -1
  74. package/dist/filter-merge-DGM-GQ1J.mjs +99 -0
  75. package/dist/{generate-BLRf2m3y.mjs → generate-BXKo2Gc8.mjs} +2 -2
  76. package/dist/{headers-BIXWKrzQ.mjs → headers-DKZQKL-Z.mjs} +2 -2
  77. package/dist/health-BYAwA6YS.mjs +22 -0
  78. package/dist/{health-ZRHeDHXH.mjs → health-CrqUp3sz.mjs} +115 -55
  79. package/dist/hooks/index.d.mts +2 -2
  80. package/dist/hooks/index.mjs +2 -3
  81. package/dist/idempotency/index.d.mts +36 -10
  82. package/dist/idempotency/index.mjs +230 -111
  83. package/dist/idempotency/redis.d.mts +1 -1
  84. package/dist/idempotency/redis.mjs +77 -12
  85. package/dist/{index-D4u76eQW.d.mts → index-Beg3aNnK.d.mts} +23 -101
  86. package/dist/{preloadResources-CjS2OfpU.d.mts → index-BkV55WlV.d.mts} +37 -38
  87. package/dist/{index-BHdZD8zK.d.mts → index-Bxriq3so.d.mts} +84 -21
  88. package/dist/index-CUJvoyw3.d.mts +510 -0
  89. package/dist/{index-CbJiYrfv.d.mts → index-Chgz3d0i.d.mts} +1 -1
  90. package/dist/{index-DHbyPo5j.d.mts → index-WH7xgvxS.d.mts} +304 -254
  91. package/dist/index.d.mts +9 -9
  92. package/dist/index.mjs +11 -11
  93. package/dist/{init-CpLkjU18.mjs → init-BPzK5Lnr.mjs} +1 -1
  94. package/dist/integrations/event-gateway.d.mts +11 -7
  95. package/dist/integrations/event-gateway.mjs +4 -4
  96. package/dist/integrations/index.d.mts +3 -3
  97. package/dist/integrations/index.mjs +1 -1
  98. package/dist/integrations/jobs/index.d.mts +1 -1
  99. package/dist/integrations/jobs/index.mjs +18 -5
  100. package/dist/integrations/mcp/index.d.mts +102 -89
  101. package/dist/integrations/mcp/index.mjs +19 -4
  102. package/dist/integrations/mcp/testing.d.mts +8 -9
  103. package/dist/integrations/mcp/testing.mjs +2 -2
  104. package/dist/integrations/streamline.d.mts +85 -144
  105. package/dist/integrations/streamline.mjs +131 -55
  106. package/dist/integrations/webhooks.d.mts +83 -143
  107. package/dist/integrations/webhooks.mjs +36 -80
  108. package/dist/integrations/websocket-pushref-redis.d.mts +5 -6
  109. package/dist/integrations/websocket-pushref-redis.mjs +1 -1
  110. package/dist/integrations/websocket-redis.d.mts +5 -6
  111. package/dist/integrations/websocket-redis.mjs +1 -1
  112. package/dist/integrations/websocket.d.mts +3 -3
  113. package/dist/integrations/websocket.mjs +2 -2
  114. package/dist/{interface-DEWNOMza.d.mts → interface-JJzwH4Ro.d.mts} +34 -2
  115. package/dist/{jsonBody-ELR9lfeU.mjs → jsonBody-YIq-vGLv.mjs} +2 -2
  116. package/dist/{keys-cpZCtcIE.mjs → keys-CpoiZ8hU.mjs} +7 -18
  117. package/dist/{loadResources-BPfWiI5s.mjs → loadResources-6jO52-RR.mjs} +3 -3
  118. package/dist/{loadResourcesFromEntry-ixtfL3J2.mjs → loadResourcesFromEntry-MP8xEppu.mjs} +2 -2
  119. package/dist/logger/index.d.mts +9 -17
  120. package/dist/logger/index.mjs +2 -153
  121. package/dist/logger-D4V1zGLI.mjs +160 -0
  122. package/dist/{mcpPlugin-BX5PujI0.mjs → mcpPlugin-srPc7ItN.mjs} +446 -170
  123. package/dist/{memory-Iotz0cCx.mjs → memory-CTa5pOCW.mjs} +36 -34
  124. package/dist/{metrics-NMsmpRmY.mjs → metrics-DsZrvnpB.mjs} +39 -72
  125. package/dist/middleware/index.d.mts +5 -5
  126. package/dist/middleware/index.mjs +3 -3
  127. package/dist/migrations/index.d.mts +13 -13
  128. package/dist/migrations/index.mjs +2 -2
  129. package/dist/{module-DpLJG2_N.mjs → module--L0iBIZz.mjs} +8 -8
  130. package/dist/mongodb-memory-server-CHhYSq5y.mjs +35539 -0
  131. package/dist/mongoose-CYJCLyi-.mjs +67451 -0
  132. package/dist/multiTenant-CAtYlW6H.d.mts +104 -0
  133. package/dist/{multipartBody-DtRWM6_U.mjs → multipartBody-Cc7eZ9cq.mjs} +46 -7
  134. package/dist/node-D8UZv_8c.mjs +41236 -0
  135. package/dist/{openapi-CmFf3yq_.mjs → openapi-CMegfsm8.mjs} +48 -10
  136. package/dist/outbox-admin/index.d.mts +5 -5
  137. package/dist/outbox-admin/index.mjs +6 -18
  138. package/dist/{outbox-module-D2z5WkDY.mjs → outbox-module-Ryk270jd.mjs} +203 -33
  139. package/dist/{outbox-module-BHdUnuuK.d.mts → outbox-module-gZCHoE4H.d.mts} +79 -6
  140. package/dist/permissions/index.d.mts +5 -4
  141. package/dist/permissions/index.mjs +8 -6
  142. package/dist/{permissions-D8TBH44E.mjs → permissions-C5ijIz5Q.mjs} +149 -33
  143. package/dist/{pipe-FMWNheSO.mjs → pipe-D81zMtWs.mjs} +2 -2
  144. package/dist/pipeline/index.d.mts +7 -7
  145. package/dist/pipeline/index.mjs +2 -2
  146. package/dist/plan-CJGMKoui.mjs +565 -0
  147. package/dist/{BaseController-CYEwjyjN.mjs → plugin-DALd9dcF.mjs} +1269 -346
  148. package/dist/plugins/index.d.mts +17 -14
  149. package/dist/plugins/index.mjs +44 -22
  150. package/dist/plugins/response-cache.d.mts +5 -6
  151. package/dist/plugins/response-cache.mjs +8 -14
  152. package/dist/plugins/tracing-entry.mjs +2 -2
  153. package/dist/{pluralize-CXWZIzTv.mjs → pluralize-COUNYo3d.mjs} +1 -1
  154. package/dist/presets/filesUpload.d.mts +8 -8
  155. package/dist/presets/filesUpload.mjs +5 -5
  156. package/dist/presets/index.d.mts +35 -40
  157. package/dist/presets/index.mjs +4 -4
  158. package/dist/presets/multiTenant.d.mts +2 -107
  159. package/dist/presets/multiTenant.mjs +22 -162
  160. package/dist/presets/search.d.mts +9 -10
  161. package/dist/presets/search.mjs +2 -2
  162. package/dist/{presets-ZJSPgmT2.mjs → presets-CONf2Qem.mjs} +99 -107
  163. package/dist/{projection-BzIgzoKZ.mjs → projection-wFoLqdss.mjs} +2 -2
  164. package/dist/{pushref-registry-Dov9l4qE.d.mts → pushref-registry-UxCkoA4C.d.mts} +1 -1
  165. package/dist/{queryCachePlugin-DZHPOm9p.mjs → queryCachePlugin-BATECpyh.mjs} +12 -14
  166. package/dist/{queryCachePlugin-BzqxconG.d.mts → queryCachePlugin-C2lZjTxk.d.mts} +1 -1
  167. package/dist/{rateLimitKey-BvOvcBDi.mjs → rateLimitKey-DVEZ_UpV.mjs} +1 -1
  168. package/dist/{realtime-8tIQWZz4.mjs → realtime-xyubMKSB.mjs} +25 -21
  169. package/dist/{redis-Bzi1y3F9.d.mts → redis-BDgv9Glw.d.mts} +17 -24
  170. package/dist/redis-stream-BMk-bpQS.d.mts +219 -0
  171. package/dist/redisShapes-CQksY67q.d.mts +55 -0
  172. package/dist/registry/index.d.mts +15 -10
  173. package/dist/registry/index.mjs +4 -4
  174. package/dist/{registry-DNkgGQcK.mjs → registry-Bdzg-hdm.mjs} +13 -10
  175. package/dist/{renewingLease-yF8BdW4b.mjs → renewingLease-BFJxGPuh.mjs} +1 -1
  176. package/dist/{replyHelpers-CNdvMfM4.mjs → replyHelpers-Bnc6lJyy.mjs} +2 -2
  177. package/dist/{requestContext-cDkAUr-P.mjs → requestContext-C10UeFe5.mjs} +1 -1
  178. package/dist/requirement-Bu50QALH.mjs +35 -0
  179. package/dist/requirement-CbiLdQ1n.d.mts +22 -0
  180. package/dist/resourceRateLimit-Wf6d6FL9.d.mts +30 -0
  181. package/dist/resourceRateLimit-urWPJxrt.mjs +180 -0
  182. package/dist/rolldown-runtime-MYrLNGnq.mjs +45 -0
  183. package/dist/routeValidator-DHtpzrzk.mjs +70 -0
  184. package/dist/{runtimeCapabilities-CkhS6jn6.mjs → runtimeCapabilities-DIMKMV2G.mjs} +1 -1
  185. package/dist/{schedules-j6uXXAeJ.mjs → schedules-CKTs5ys8.mjs} +46 -37
  186. package/dist/{schemaConverter-BVA7cFRG.mjs → schemaConverter-BVjkQ5DD.mjs} +2 -2
  187. package/dist/{schemaIR-CTvPDoPB.mjs → schemaIR-BA0Hb21Y.mjs} +2 -2
  188. package/dist/schemas/index.d.mts +20 -10
  189. package/dist/schemas/index.mjs +8 -3
  190. package/dist/scim/index.d.mts +13 -13
  191. package/dist/scim/index.mjs +2 -2
  192. package/dist/scope/index.d.mts +4 -4
  193. package/dist/scope/index.mjs +6 -6
  194. package/dist/{scope-BUi1xV1I.mjs → scope-DINaj7Ax.mjs} +4 -4
  195. package/dist/shutdown-LpLMH8T1.mjs +59 -0
  196. package/dist/{simpleEqualityMatcher-C9bzq2gO.mjs → simpleEqualityMatcher-DbwIM1f1.mjs} +1 -1
  197. package/dist/{sse-BeqVKQKu.mjs → sse-BzJFPX_1.mjs} +11 -15
  198. package/dist/{sseStream-Ppzo-WUH.mjs → sseStream-DQChzGTs.mjs} +2 -2
  199. package/dist/{store-helpers-dW71XVWL.mjs → store-helpers-Coj9RhOZ.mjs} +1 -1
  200. package/dist/{streaming-IcRFjWQU.mjs → streaming-JzB5CAga.mjs} +1 -1
  201. package/dist/subscriberIdentity-DeyppIif.mjs +28 -0
  202. package/dist/tenancy-DxV8OuX2.mjs +43 -0
  203. package/dist/tenantPreparation-CBvvAcAj.mjs +67 -0
  204. package/dist/testing/cleanupStoreContract.d.mts +4 -5
  205. package/dist/testing/cleanupStoreContract.mjs +1 -1
  206. package/dist/testing/index.d.mts +215 -197
  207. package/dist/testing/index.mjs +289 -214
  208. package/dist/testing/outboxStoreContract.d.mts +4 -5
  209. package/dist/testing/outboxStoreContract.mjs +137 -2
  210. package/dist/testing/requiredPorts.d.mts +16 -0
  211. package/dist/testing/requiredPorts.mjs +65 -0
  212. package/dist/testing/storageContract.d.mts +4 -5
  213. package/dist/testing/storageContract.mjs +1 -1
  214. package/dist/timing-CntavTlR.mjs +46 -0
  215. package/dist/{transactionContext-CPbdlnVw.mjs → transactionContext-orvnGu4F.mjs} +1 -1
  216. package/dist/{typeGuards-BLjPgVBm.mjs → typeGuards-kEaP_WmO.mjs} +1 -1
  217. package/dist/types/index.d.mts +4 -4
  218. package/dist/types/index.mjs +1 -1
  219. package/dist/types/storage.mjs +1 -1
  220. package/dist/{types-DfVDvkdP.d.mts → types-BJ-2XqVF.d.mts} +189 -204
  221. package/dist/{types-CWQn9-IC.d.mts → types-D3Ixsh00.d.mts} +22 -9
  222. package/dist/{types-CgenB1gv.mjs → types-fvO6Ovzx.mjs} +4 -4
  223. package/dist/usage/index.d.mts +5 -6
  224. package/dist/usage/index.mjs +5 -5
  225. package/dist/{userHelpers-ChRvkuSZ.mjs → userHelpers-C76zD5pa.mjs} +1 -1
  226. package/dist/utils/index.d.mts +125 -264
  227. package/dist/utils/index.mjs +13 -12
  228. package/dist/{utils-CxcctrAh.mjs → utils-BpkVZ8rB.mjs} +315 -404
  229. package/dist/{validate-DrYWnOzI.mjs → validate-CWp-lsrm.mjs} +6 -14
  230. package/dist/{versioning-D6JIK8Tk.d.mts → versioning-Bsb51gGT.d.mts} +28 -13
  231. package/dist/{versioning-DKkT4hNJ.mjs → versioning-CHhL9P3o.mjs} +4 -4
  232. package/dist/{websocket-BOXVggMf.d.mts → websocket-Cs4awVyD.d.mts} +1 -1
  233. package/dist/{workScope-CxKqoIFn.mjs → workScope-n9a7d_lZ.mjs} +2 -2
  234. package/package.json +57 -59
  235. package/skills/arc/SKILL.md +13 -4
  236. package/skills/arc/references/auth.md +8 -5
  237. package/skills/arc/references/integrations.md +1 -1
  238. package/skills/arc/references/mcp.md +8 -6
  239. package/skills/arc/references/multi-tenancy.md +19 -14
  240. package/skills/arc/references/production.md +27 -10
  241. package/dist/actionPermissions-BoPGQ5-4.mjs +0 -350
  242. package/dist/index-CFfLKMH4.d.mts +0 -290
  243. package/dist/interface-YzIbF7Ii.mjs +0 -17
  244. package/dist/redis-stream-BPKscsTB.d.mts +0 -260
  245. package/dist/registerRoute-Bsnk6NiZ.mjs +0 -364
  246. package/dist/rolldown-runtime-eysyzFAF.mjs +0 -15
  247. package/dist/sync/changelog.d.mts +0 -3
  248. package/dist/sync/changelog.mjs +0 -3
@@ -0,0 +1,180 @@
1
+ /*! @classytic/arc v2.44.0 | MIT | https://classytic.com */
2
+ import { t as createTenantKeyGenerator } from "./rateLimitKey-DVEZ_UpV.mjs";
3
+ //#region src/permissions/resourceRateLimit.ts
4
+ function decision(allowed, max, remaining, ttlSeconds) {
5
+ const reset = Math.max(1, ttlSeconds);
6
+ return {
7
+ allowed,
8
+ retryAfterSeconds: reset,
9
+ headers: {
10
+ "x-ratelimit-limit": max,
11
+ "x-ratelimit-remaining": remaining,
12
+ "x-ratelimit-reset": reset,
13
+ ...!allowed ? { "retry-after": reset } : {}
14
+ }
15
+ };
16
+ }
17
+ /** Policy namespacing must reach the store key, not merely the local limiter cache. */
18
+ function policyIdentity(resource, policy) {
19
+ if (!Number.isSafeInteger(policy.max) || policy.max <= 0) throw new Error("rateLimit.max must be a positive safe integer.");
20
+ return JSON.stringify([
21
+ "arc-resource",
22
+ resource,
23
+ policy.bucket ?? "resource",
24
+ policy.max,
25
+ resourceRateLimitWindowMs(policy.timeWindow)
26
+ ]);
27
+ }
28
+ const RESOURCE_RATE_LIMITER = Symbol.for("arc.resourceRateLimiter");
29
+ const tenantKey = createTenantKeyGenerator();
30
+ function resourceRateLimitKey(resource, context) {
31
+ const actor = tenantKey({
32
+ scope: context.scope,
33
+ ip: context.ip ?? "anonymous"
34
+ });
35
+ const dimension = context.scope && context.scope.kind !== "public" && "organizationId" in context.scope && context.scope.organizationId ? "tenant" : context.scope?.kind ?? "public";
36
+ return JSON.stringify([
37
+ resource,
38
+ dimension,
39
+ actor
40
+ ]);
41
+ }
42
+ const RATE_LIMITING_DISABLED = Symbol.for("arc.rateLimitingDisabled");
43
+ /**
44
+ * The host said `rateLimit: false`. A resource's declared limit is then a recorded no-op, and
45
+ * the resources are named once at ready. Absent this mark, a declared limit with no limiter
46
+ * refuses to boot: nobody decided, so nothing would enforce it.
47
+ */
48
+ function markRateLimitingDisabled(app) {
49
+ if (Reflect.get(app, RATE_LIMITING_DISABLED)) return;
50
+ const unenforced = /* @__PURE__ */ new Set();
51
+ Reflect.set(app, RATE_LIMITING_DISABLED, unenforced);
52
+ app.addHook("onReady", async () => {
53
+ if (unenforced.size === 0) return;
54
+ app.log.info({ resources: [...unenforced] }, `[arc] rate limiting is disabled for this app: declared rateLimit not enforced on ${[...unenforced].join(", ")}`);
55
+ });
56
+ }
57
+ /** Record a resource whose declared limit the host disabled; `false` when the host never decided. */
58
+ function recordUnenforcedRateLimit(app, resource) {
59
+ let owner = app;
60
+ while (owner) {
61
+ const unenforced = Reflect.get(owner, RATE_LIMITING_DISABLED);
62
+ if (unenforced instanceof Set) {
63
+ unenforced.add(resource);
64
+ return true;
65
+ }
66
+ owner = Object.getPrototypeOf(owner);
67
+ }
68
+ return false;
69
+ }
70
+ /** Uses createRateLimit's store factory, preserving Redis/custom-store configuration. */
71
+ function resolveResourceRateLimiter(app, durability = "memory") {
72
+ if (!app.hasDecorator("createRateLimit")) return void 0;
73
+ let owner = app;
74
+ while (!Object.hasOwn(owner, "createRateLimit")) {
75
+ const parent = Object.getPrototypeOf(owner);
76
+ if (!parent || typeof parent !== "object" || !("createRateLimit" in parent)) break;
77
+ owner = parent;
78
+ }
79
+ const existing = Reflect.get(owner, RESOURCE_RATE_LIMITER);
80
+ if (existing) return existing;
81
+ const limiters = /* @__PURE__ */ new Map();
82
+ const limiter = {
83
+ durability,
84
+ async consume(resource, policy, context) {
85
+ const identity = policyIdentity(resource, policy);
86
+ let consume = limiters.get(identity);
87
+ if (!consume) {
88
+ consume = owner.createRateLimit({
89
+ max: policy.max,
90
+ timeWindow: resourceRateLimitWindowMs(policy.timeWindow),
91
+ allowList: [],
92
+ skipOnError: false,
93
+ keyGenerator: (req) => req.ip
94
+ });
95
+ limiters.set(identity, consume);
96
+ }
97
+ const req = {
98
+ ip: JSON.stringify([identity, resourceRateLimitKey(resource, context)]),
99
+ routeOptions: { config: {} }
100
+ };
101
+ const result = await consume(req);
102
+ return result.isAllowed ? decision(true, policy.max, policy.max, Math.ceil(resourceRateLimitWindowMs(policy.timeWindow) / 1e3)) : decision(!result.isExceeded, result.max, result.remaining, result.ttlInSeconds);
103
+ }
104
+ };
105
+ Reflect.set(owner, RESOURCE_RATE_LIMITER, limiter);
106
+ return limiter;
107
+ }
108
+ /** Standalone tools may use memory; wired distributed apps use the host's shared store. */
109
+ function createMemoryResourceRateLimiter(capacity = 1e4) {
110
+ if (!Number.isSafeInteger(capacity) || capacity <= 0) throw new Error("rateLimit memory capacity must be a positive safe integer.");
111
+ const windows = /* @__PURE__ */ new Map();
112
+ return {
113
+ durability: "memory",
114
+ async consume(resource, policy, context) {
115
+ const now = Date.now();
116
+ const windowMs = resourceRateLimitWindowMs(policy.timeWindow);
117
+ const key = JSON.stringify([policyIdentity(resource, policy), resourceRateLimitKey(resource, context)]);
118
+ let slot = windows.get(key);
119
+ if (!slot || slot.expires <= now) {
120
+ if (windows.size >= capacity) {
121
+ for (const [entry, window] of windows) if (window.expires <= now) windows.delete(entry);
122
+ if (!windows.has(key) && windows.size >= capacity) {
123
+ let retryMs = Number.POSITIVE_INFINITY;
124
+ for (const entry of windows.values()) retryMs = Math.min(retryMs, entry.expires - now);
125
+ return decision(false, policy.max, 0, Math.ceil(retryMs / 1e3));
126
+ }
127
+ }
128
+ slot = {
129
+ expires: now + windowMs,
130
+ count: 0
131
+ };
132
+ windows.set(key, slot);
133
+ }
134
+ slot.count++;
135
+ return decision(slot.count <= policy.max, policy.max, Math.max(0, policy.max - slot.count), Math.ceil((slot.expires - now) / 1e3));
136
+ }
137
+ };
138
+ }
139
+ function resourceRateLimitWindowMs(window) {
140
+ const match = /^\s*((?:\d+(?:\.\d+)?|\.\d+))\s*(ms|milliseconds?|msecs?|s|secs?|seconds?|m|mins?|minutes?|h|hrs?|hours?|d|days?|w|wks?|weeks?|y|yrs?|years?)?\s*$/i.exec(window);
141
+ const value = Number(match?.[1]) * ({
142
+ ms: 1,
143
+ millisecond: 1,
144
+ milliseconds: 1,
145
+ msec: 1,
146
+ msecs: 1,
147
+ s: 1e3,
148
+ sec: 1e3,
149
+ secs: 1e3,
150
+ second: 1e3,
151
+ seconds: 1e3,
152
+ m: 6e4,
153
+ min: 6e4,
154
+ mins: 6e4,
155
+ minute: 6e4,
156
+ minutes: 6e4,
157
+ h: 36e5,
158
+ hr: 36e5,
159
+ hrs: 36e5,
160
+ hour: 36e5,
161
+ hours: 36e5,
162
+ d: 864e5,
163
+ day: 864e5,
164
+ days: 864e5,
165
+ w: 6048e5,
166
+ wk: 6048e5,
167
+ wks: 6048e5,
168
+ week: 6048e5,
169
+ weeks: 6048e5,
170
+ y: 315576e5,
171
+ yr: 315576e5,
172
+ yrs: 315576e5,
173
+ year: 315576e5,
174
+ years: 315576e5
175
+ }[match?.[2]?.toLowerCase() ?? "ms"] ?? 0);
176
+ if (!Number.isSafeInteger(value) || value <= 0) throw new Error(`rateLimit.timeWindow '${window}' must be a positive duration in milliseconds.`);
177
+ return value;
178
+ }
179
+ //#endregion
180
+ export { resourceRateLimitWindowMs as a, resolveResourceRateLimiter as i, markRateLimitingDisabled as n, recordUnenforcedRateLimit as r, createMemoryResourceRateLimiter as t };
@@ -0,0 +1,45 @@
1
+ /*! @classytic/arc v2.44.0 | MIT | https://classytic.com */
2
+ import { createRequire } from "node:module";
3
+ //#region \0rolldown/runtime.js
4
+ var __create = Object.create;
5
+ var __defProp = Object.defineProperty;
6
+ var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
7
+ var __getOwnPropNames = Object.getOwnPropertyNames;
8
+ var __getProtoOf = Object.getPrototypeOf;
9
+ var __hasOwnProp = Object.prototype.hasOwnProperty;
10
+ var __esmMin = (fn, res, err) => () => {
11
+ if (err) throw err[0];
12
+ try {
13
+ return fn && (res = fn(fn = 0)), res;
14
+ } catch (e) {
15
+ throw err = [e], e;
16
+ }
17
+ };
18
+ var __commonJSMin = (cb, mod) => () => (mod || (cb((mod = { exports: {} }).exports, mod, mod.exports), cb = null), mod.exports);
19
+ var __exportAll = (all, no_symbols) => {
20
+ let target = {};
21
+ for (var name in all) __defProp(target, name, {
22
+ get: all[name],
23
+ enumerable: true
24
+ });
25
+ if (!no_symbols) __defProp(target, Symbol.toStringTag, { value: "Module" });
26
+ return target;
27
+ };
28
+ var __copyProps = (to, from, except, desc) => {
29
+ if (from && typeof from === "object" || typeof from === "function") for (var keys = __getOwnPropNames(from), i = 0, n = keys.length, key; i < n; i++) {
30
+ key = keys[i];
31
+ if (!__hasOwnProp.call(to, key) && key !== except) __defProp(to, key, {
32
+ get: ((k) => from[k]).bind(null, key),
33
+ enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable
34
+ });
35
+ }
36
+ return to;
37
+ };
38
+ var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(isNodeMode || !mod || !mod.__esModule || !__hasOwnProp.call(mod, "default") ? __defProp(target, "default", {
39
+ value: mod,
40
+ enumerable: true
41
+ }) : target, mod));
42
+ var __toCommonJS = (mod) => __hasOwnProp.call(mod, "module.exports") ? mod["module.exports"] : __copyProps(__defProp({}, "__esModule", { value: true }), mod);
43
+ var __require = /* #__PURE__ */ (() => createRequire(import.meta.url))();
44
+ //#endregion
45
+ export { __toCommonJS as a, __require as i, __esmMin as n, __toESM as o, __exportAll as r, __commonJSMin as t };
@@ -0,0 +1,70 @@
1
+ /*! @classytic/arc v2.44.0 | MIT | https://classytic.com */
2
+ import { AjvCompiler } from "@fastify/ajv-compiler";
3
+ //#region src/core/routeValidator.ts
4
+ /**
5
+ * Arc's request validator — the AJV settings every route arc builds is validated with, on ANY host.
6
+ *
7
+ * Fastify's stock AJV runs `removeAdditional: true`, which turns `additionalProperties: false`
8
+ * into a silent STRIP: an unknown key in a strict action body or filter querystring reaches the
9
+ * handler as if it were never sent, and a dropped filter is a WIDER read. Arc's routes therefore
10
+ * carry their own `validatorCompiler` unless the instance was built by `createApp`, whose root
11
+ * AJV already runs these options (plus the host's `ajv.keywords`).
12
+ * Pinned by `tests/core/route-validator-bare-host.test.ts`.
13
+ */
14
+ /** The AJV options `createApp` configures and arc's own routes are compiled with. */
15
+ function arcAjvCustomOptions(extraKeywords = []) {
16
+ return {
17
+ coerceTypes: true,
18
+ useDefaults: true,
19
+ removeAdditional: false,
20
+ strictTypes: false,
21
+ keywords: ["example", ...extraKeywords]
22
+ };
23
+ }
24
+ /** Mark an instance whose ROOT AJV was configured with `arcAjvCustomOptions` (createApp only). */
25
+ function markArcRootValidator(fastify) {
26
+ fastify.decorate("arcRootValidator", true);
27
+ }
28
+ /** One pool for the process: `@fastify/ajv-compiler` caches an AJV per (shared schemas, options). */
29
+ const buildFromPool = AjvCompiler();
30
+ /**
31
+ * The `validatorCompiler` an arc-built route registers with — `undefined` under `createApp`
32
+ * (the root already validates this way). Schemas added with `fastify.addSchema` stay
33
+ * `$ref`-able: the shared pool is read when Fastify compiles the route.
34
+ */
35
+ function arcRouteValidatorCompiler(fastify) {
36
+ if (fastify.arcRootValidator === true) return void 0;
37
+ return ({ schema, httpPart }) => {
38
+ return buildFromPool(sharedSchemas(fastify), { customOptions: arcAjvCustomOptions() })({ schema: httpPart === "headers" ? lowerCaseHeaderSchema(schema) : schema });
39
+ };
40
+ }
41
+ /** The instance's `addSchema` pool, in the shape AJV accepts (a schema is an object or boolean). */
42
+ function sharedSchemas(fastify) {
43
+ const out = {};
44
+ for (const [id, schema] of Object.entries(fastify.getSchemas())) if (isSchemaValue(schema)) out[id] = schema;
45
+ return out;
46
+ }
47
+ function isSchemaValue(value) {
48
+ return typeof value === "boolean" || typeof value === "object" && value !== null;
49
+ }
50
+ /**
51
+ * Fastify lower-cases a headers schema's keys before compiling it with its OWN compiler and
52
+ * skips that for a custom one — so a route-level compiler must do it, or `Authorization` in a
53
+ * schema never matches the `authorization` header Node delivers.
54
+ */
55
+ function lowerCaseHeaderSchema(schema) {
56
+ if (!isPlainObject(schema)) return schema;
57
+ const out = { ...schema };
58
+ if (Array.isArray(schema.required)) out.required = schema.required.map((h) => typeof h === "string" ? h.toLowerCase() : h);
59
+ if (isPlainObject(schema.properties)) {
60
+ const properties = {};
61
+ for (const [key, value] of Object.entries(schema.properties)) properties[key.toLowerCase()] = value;
62
+ out.properties = properties;
63
+ }
64
+ return out;
65
+ }
66
+ function isPlainObject(value) {
67
+ return typeof value === "object" && value !== null && Object.getPrototypeOf(value) === Object.prototype;
68
+ }
69
+ //#endregion
70
+ export { arcRouteValidatorCompiler as n, markArcRootValidator as r, arcAjvCustomOptions as t };
@@ -1,4 +1,4 @@
1
- /*! @classytic/arc v2.41.1 | MIT | https://classytic.com */
1
+ /*! @classytic/arc v2.44.0 | MIT | https://classytic.com */
2
2
  //#region src/utils/runtimeCapabilities.ts
3
3
  /**
4
4
  * `Symbol.for` so declarations from two arc copies in one dependency graph
@@ -1,7 +1,8 @@
1
- /*! @classytic/arc v2.41.1 | MIT | https://classytic.com */
2
- import { t as __exportAll } from "./rolldown-runtime-eysyzFAF.mjs";
3
- import { t as runWorkScope } from "./workScope-CxKqoIFn.mjs";
4
- import { t as startRenewingLease } from "./renewingLease-yF8BdW4b.mjs";
1
+ /*! @classytic/arc v2.44.0 | MIT | https://classytic.com */
2
+ import { r as __exportAll } from "./rolldown-runtime-MYrLNGnq.mjs";
3
+ import { a as runShutdown, i as runAdmittedWork, r as registerShutdownParticipant } from "./shutdown-LpLMH8T1.mjs";
4
+ import { t as runWorkScope } from "./workScope-n9a7d_lZ.mjs";
5
+ import { t as startRenewingLease } from "./renewingLease-BFJxGPuh.mjs";
5
6
  import fp from "fastify-plugin";
6
7
  //#region src/plugins/schedules.ts
7
8
  var schedules_exports = /* @__PURE__ */ __exportAll({
@@ -61,10 +62,10 @@ const schedulesPlugin = async (fastify, opts) => {
61
62
  }
62
63
  stat.runs++;
63
64
  stat.lastRunAt = (/* @__PURE__ */ new Date()).toISOString();
64
- await runWorkScope({
65
+ await runAdmittedWork(() => runWorkScope({
65
66
  kind: "job",
66
67
  id: s.name
67
- }, () => s.handler(fastify));
68
+ }, () => s.handler(fastify)));
68
69
  stat.lastError = null;
69
70
  } catch (err) {
70
71
  stat.failures++;
@@ -105,37 +106,45 @@ const schedulesPlugin = async (fastify, opts) => {
105
106
  }
106
107
  if (stats.size > 0) fastify.log.debug(`[arc-schedules] ${stats.size} schedule(s) armed${lock ? ` (leader-safe, holder ${holderId})` : ""}`);
107
108
  });
109
+ registerShutdownParticipant(fastify, {
110
+ name: "schedules",
111
+ stop: () => {
112
+ closing = true;
113
+ for (const timer of timers.values()) clearTimeout(timer);
114
+ timers.clear();
115
+ },
116
+ drain: async () => {
117
+ /**
118
+ * Let in-flight runs settle so shutdown never truncates a sweep mid-write —
119
+ * but BOUNDED, because a tick has no cancellation contract and its duration
120
+ * is not this plugin's to predict. An outbox relay whose subscriber retries
121
+ * on a backoff ladder holds one tick for minutes, and an unbounded await
122
+ * here contradicts the `unref` two functions up: pending scheduled work is
123
+ * already declared as something that must not hold the process.
124
+ *
125
+ * Waiting longer than the orchestrator's grace period buys nothing anyway —
126
+ * SIGKILL truncates the same write, without the log line naming what was
127
+ * still running.
128
+ */
129
+ const pending = [...inFlight.entries()];
130
+ if (pending.length === 0) return;
131
+ let timeout;
132
+ const expired = Symbol("drain-timeout");
133
+ const budget = new Promise((resolve) => {
134
+ timeout = setTimeout(() => resolve(expired), drainTimeoutMs);
135
+ timeout.unref?.();
136
+ });
137
+ const settled = Promise.allSettled(pending.map(([, run]) => run)).then(() => void 0);
138
+ const outcome = await Promise.race([settled, budget]);
139
+ if (timeout) clearTimeout(timeout);
140
+ if (outcome === expired) fastify.log.warn({
141
+ schedules: pending.map(([name]) => name),
142
+ drainTimeoutMs
143
+ }, "[arc-schedules] shutdown drain budget exceeded — abandoning in-flight run(s). Durable work (outbox rows, leased sweeps) is retried on the next boot; raise `drainTimeoutMs` only if a listed job is genuinely non-resumable.");
144
+ }
145
+ });
108
146
  fastify.addHook("onClose", async () => {
109
- closing = true;
110
- for (const timer of timers.values()) clearTimeout(timer);
111
- timers.clear();
112
- /**
113
- * Let in-flight runs settle so shutdown never truncates a sweep mid-write —
114
- * but BOUNDED, because a tick has no cancellation contract and its duration
115
- * is not this plugin's to predict. An outbox relay whose subscriber retries
116
- * on a backoff ladder holds one tick for minutes, and an unbounded await
117
- * here contradicts the `unref` two functions up: pending scheduled work is
118
- * already declared as something that must not hold the process.
119
- *
120
- * Waiting longer than the orchestrator's grace period buys nothing anyway —
121
- * SIGKILL truncates the same write, without the log line naming what was
122
- * still running.
123
- */
124
- const pending = [...inFlight.entries()];
125
- if (pending.length === 0) return;
126
- let timeout;
127
- const expired = Symbol("drain-timeout");
128
- const budget = new Promise((resolve) => {
129
- timeout = setTimeout(() => resolve(expired), drainTimeoutMs);
130
- timeout.unref?.();
131
- });
132
- const settled = Promise.allSettled(pending.map(([, run]) => run)).then(() => void 0);
133
- const outcome = await Promise.race([settled, budget]);
134
- if (timeout) clearTimeout(timeout);
135
- if (outcome === expired) fastify.log.warn({
136
- schedules: pending.map(([name]) => name),
137
- drainTimeoutMs
138
- }, "[arc-schedules] shutdown drain budget exceeded — abandoning in-flight run(s). Durable work (outbox rows, leased sweeps) is retried on the next boot; raise `drainTimeoutMs` only if a listed job is genuinely non-resumable.");
147
+ await runShutdown(fastify);
139
148
  });
140
149
  };
141
150
  var schedules_default = fp(schedulesPlugin, {
@@ -143,4 +152,4 @@ var schedules_default = fp(schedulesPlugin, {
143
152
  fastify: "5.x"
144
153
  });
145
154
  //#endregion
146
- export { schedules_default as n, schedules_exports as r, schedulesPlugin as t };
155
+ export { schedules_exports as n, schedules_default as t };
@@ -1,5 +1,5 @@
1
- /*! @classytic/arc v2.41.1 | MIT | https://classytic.com */
2
- import { arcLog } from "./logger/index.mjs";
1
+ /*! @classytic/arc v2.44.0 | MIT | https://classytic.com */
2
+ import { t as arcLog } from "./logger-D4V1zGLI.mjs";
3
3
  //#region src/utils/schemaConverter.ts
4
4
  /**
5
5
  * Schema Converter — Detect-First, Convert-Only-When-Needed
@@ -1,5 +1,5 @@
1
- /*! @classytic/arc v2.41.1 | MIT | https://classytic.com */
2
- import { o as toJsonSchema } from "./schemaConverter-BVA7cFRG.mjs";
1
+ /*! @classytic/arc v2.44.0 | MIT | https://classytic.com */
2
+ import { o as toJsonSchema } from "./schemaConverter-BVjkQ5DD.mjs";
3
3
  //#region src/core/schemaIR.ts
4
4
  /**
5
5
  * Schema IR — one canonical representation, two adapters.
@@ -17,7 +17,7 @@ import { FastifyPluginAsyncTypebox, FastifyPluginCallbackTypebox, TypeBoxTypePro
17
17
  * variant when your endpoint never emits the others. HTTP status
18
18
  * discriminates success vs error — no `success` field.
19
19
  */
20
- declare function ArcListResponse<T extends TSchema$1>(itemSchema: T): Type$1.TUnion<[Type$1.TObject<{
20
+ export declare function ArcListResponse<T extends TSchema$1>(itemSchema: T): Type$1.TUnion<[Type$1.TObject<{
21
21
  method: Type$1.TLiteral<"offset">;
22
22
  data: Type$1.TArray<T>;
23
23
  page: Type$1.TInteger;
@@ -26,12 +26,17 @@ declare function ArcListResponse<T extends TSchema$1>(itemSchema: T): Type$1.TUn
26
26
  pages: Type$1.TInteger;
27
27
  hasNext: Type$1.TBoolean;
28
28
  hasPrev: Type$1.TBoolean;
29
+ totalIsEstimate: Type$1.TOptional<Type$1.TBoolean>;
30
+ countedAt: Type$1.TOptional<Type$1.TUnion<[Type$1.TString, Type$1.TNull]>>;
29
31
  }>, Type$1.TObject<{
30
32
  method: Type$1.TLiteral<"keyset">;
31
33
  data: Type$1.TArray<T>;
32
34
  limit: Type$1.TInteger;
33
35
  hasMore: Type$1.TBoolean;
34
36
  next: Type$1.TUnion<[Type$1.TString, Type$1.TNull]>;
37
+ prev: Type$1.TOptional<Type$1.TUnion<[Type$1.TString, Type$1.TNull]>>;
38
+ hasPrev: Type$1.TOptional<Type$1.TBoolean>;
39
+ end: Type$1.TOptional<Type$1.TUnion<[Type$1.TString, Type$1.TNull]>>;
35
40
  }>, Type$1.TObject<{
36
41
  method: Type$1.TLiteral<"aggregate">;
37
42
  data: Type$1.TArray<T>;
@@ -45,7 +50,7 @@ declare function ArcListResponse<T extends TSchema$1>(itemSchema: T): Type$1.TUn
45
50
  data: Type$1.TArray<T>;
46
51
  }>]>;
47
52
  /** Offset variant — `{ method: 'offset', data, page, limit, total, pages, hasNext, hasPrev }`. */
48
- declare function ArcOffsetListResponse<T extends TSchema$1>(itemSchema: T): Type$1.TObject<{
53
+ export declare function ArcOffsetListResponse<T extends TSchema$1>(itemSchema: T): Type$1.TObject<{
49
54
  method: Type$1.TLiteral<"offset">;
50
55
  data: Type$1.TArray<T>;
51
56
  page: Type$1.TInteger;
@@ -54,17 +59,22 @@ declare function ArcOffsetListResponse<T extends TSchema$1>(itemSchema: T): Type
54
59
  pages: Type$1.TInteger;
55
60
  hasNext: Type$1.TBoolean;
56
61
  hasPrev: Type$1.TBoolean;
62
+ totalIsEstimate: Type$1.TOptional<Type$1.TBoolean>;
63
+ countedAt: Type$1.TOptional<Type$1.TUnion<[Type$1.TString, Type$1.TNull]>>;
57
64
  }>;
58
65
  /** Keyset variant — `{ method: 'keyset', data, limit, hasMore, next: string | null }`. */
59
- declare function ArcKeysetListResponse<T extends TSchema$1>(itemSchema: T): Type$1.TObject<{
66
+ export declare function ArcKeysetListResponse<T extends TSchema$1>(itemSchema: T): Type$1.TObject<{
60
67
  method: Type$1.TLiteral<"keyset">;
61
68
  data: Type$1.TArray<T>;
62
69
  limit: Type$1.TInteger;
63
70
  hasMore: Type$1.TBoolean;
64
71
  next: Type$1.TUnion<[Type$1.TString, Type$1.TNull]>;
72
+ prev: Type$1.TOptional<Type$1.TUnion<[Type$1.TString, Type$1.TNull]>>;
73
+ hasPrev: Type$1.TOptional<Type$1.TBoolean>;
74
+ end: Type$1.TOptional<Type$1.TUnion<[Type$1.TString, Type$1.TNull]>>;
65
75
  }>;
66
76
  /** Aggregate variant — `{ method: 'aggregate', ...same as offset }`. */
67
- declare function ArcAggregateListResponse<T extends TSchema$1>(itemSchema: T): Type$1.TObject<{
77
+ export declare function ArcAggregateListResponse<T extends TSchema$1>(itemSchema: T): Type$1.TObject<{
68
78
  method: Type$1.TLiteral<"aggregate">;
69
79
  data: Type$1.TArray<T>;
70
80
  page: Type$1.TInteger;
@@ -75,13 +85,13 @@ declare function ArcAggregateListResponse<T extends TSchema$1>(itemSchema: T): T
75
85
  hasPrev: Type$1.TBoolean;
76
86
  }>;
77
87
  /** Bare variant — `{ data }`, no `method` discriminant. */
78
- declare function ArcBareListResponse<T extends TSchema$1>(itemSchema: T): Type$1.TObject<{
88
+ export declare function ArcBareListResponse<T extends TSchema$1>(itemSchema: T): Type$1.TObject<{
79
89
  data: Type$1.TArray<T>;
80
90
  }>;
81
91
  /**
82
92
  * Delete response — `{ message, id?, soft? }` raw at the top level.
83
93
  */
84
- declare function ArcDeleteResponse(): Type$1.TObject<{
94
+ export declare function ArcDeleteResponse(): Type$1.TObject<{
85
95
  message: Type$1.TString;
86
96
  id: Type$1.TOptional<Type$1.TString>;
87
97
  soft: Type$1.TOptional<Type$1.TBoolean>;
@@ -96,7 +106,7 @@ declare function ArcDeleteResponse(): Type$1.TObject<{
96
106
  * Exported standalone so hosts can embed it in custom 422 / 409 response
97
107
  * schemas.
98
108
  */
99
- declare function ArcErrorDetail(): Type$1.TUnsafe<ErrorDetail>;
109
+ export declare function ArcErrorDetail(): Type$1.TUnsafe<ErrorDetail>;
100
110
  /**
101
111
  * Error response schema — `Type.Unsafe<ErrorContract>` over the canonical
102
112
  * JSON Schema `errorContractSchema` from `@classytic/repo-core/errors`.
@@ -105,7 +115,7 @@ declare function ArcErrorDetail(): Type$1.TUnsafe<ErrorDetail>;
105
115
  * (`errorContractSchema`) and this TypeBox helper cannot drift —
106
116
  * literally one source.
107
117
  */
108
- declare function ArcErrorResponse(): Type$1.TUnsafe<ErrorContract>;
118
+ export declare function ArcErrorResponse(): Type$1.TUnsafe<ErrorContract>;
109
119
  /**
110
120
  * Standard pagination + sorting + filtering query parameters.
111
121
  * Matches Arc's list endpoint conventions.
@@ -121,7 +131,7 @@ declare function ArcErrorResponse(): Type$1.TUnsafe<ErrorContract>;
121
131
  * projection forms at the request-validation gate even though arc passes
122
132
  * them through unchanged.
123
133
  */
124
- declare function ArcPaginationQuery(): Type$1.TObject<{
134
+ export declare function ArcPaginationQuery(): Type$1.TObject<{
125
135
  page: Type$1.TOptional<Type$1.TInteger>;
126
136
  limit: Type$1.TOptional<Type$1.TInteger>;
127
137
  sort: Type$1.TOptional<Type$1.TString>;
@@ -129,4 +139,4 @@ declare function ArcPaginationQuery(): Type$1.TObject<{
129
139
  populate: Type$1.TOptional<Type$1.TAny>;
130
140
  }>;
131
141
  //#endregion
132
- export { ArcAggregateListResponse, ArcBareListResponse, ArcDeleteResponse, ArcErrorDetail, ArcErrorResponse, ArcKeysetListResponse, ArcListResponse, ArcOffsetListResponse, ArcPaginationQuery, type FastifyPluginAsyncTypebox, type FastifyPluginCallbackTypebox, type Static, type TObject, type TSchema, Type, type TypeBoxTypeProvider };
142
+ export { type FastifyPluginAsyncTypebox, type FastifyPluginCallbackTypebox, type Static, type TObject, type TSchema, Type, type TypeBoxTypeProvider };
@@ -1,4 +1,4 @@
1
- /*! @classytic/arc v2.41.1 | MIT | https://classytic.com */
1
+ /*! @classytic/arc v2.44.0 | MIT | https://classytic.com */
2
2
  import { errorContractSchema, errorDetailSchema } from "@classytic/repo-core/errors";
3
3
  import { Type, Type as Type$1 } from "typebox";
4
4
  //#region src/schemas/index.ts
@@ -34,7 +34,9 @@ function ArcOffsetListResponse(itemSchema) {
34
34
  total: Type$1.Integer(),
35
35
  pages: Type$1.Integer(),
36
36
  hasNext: Type$1.Boolean(),
37
- hasPrev: Type$1.Boolean()
37
+ hasPrev: Type$1.Boolean(),
38
+ totalIsEstimate: Type$1.Optional(Type$1.Boolean()),
39
+ countedAt: Type$1.Optional(Type$1.Union([Type$1.String({ format: "date-time" }), Type$1.Null()]))
38
40
  });
39
41
  }
40
42
  /** Keyset variant — `{ method: 'keyset', data, limit, hasMore, next: string | null }`. */
@@ -44,7 +46,10 @@ function ArcKeysetListResponse(itemSchema) {
44
46
  data: Type$1.Array(itemSchema),
45
47
  limit: Type$1.Integer(),
46
48
  hasMore: Type$1.Boolean(),
47
- next: Type$1.Union([Type$1.String(), Type$1.Null()])
49
+ next: Type$1.Union([Type$1.String(), Type$1.Null()]),
50
+ prev: Type$1.Optional(Type$1.Union([Type$1.String(), Type$1.Null()])),
51
+ hasPrev: Type$1.Optional(Type$1.Boolean()),
52
+ end: Type$1.Optional(Type$1.Union([Type$1.String(), Type$1.Null()]))
48
53
  });
49
54
  }
50
55
  /** Aggregate variant — `{ method: 'aggregate', ...same as offset }`. */
@@ -33,15 +33,15 @@ interface ScimResourceMapping {
33
33
  /** Custom outbound transform — runs after attribute mapping. */
34
34
  toScim?: (resource: Record<string, unknown>, mapped: Record<string, unknown>) => Record<string, unknown>;
35
35
  }
36
- declare const SCIM_USER_SCHEMA = "urn:ietf:params:scim:schemas:core:2.0:User";
37
- declare const SCIM_GROUP_SCHEMA = "urn:ietf:params:scim:schemas:core:2.0:Group";
38
- declare const SCIM_ENTERPRISE_USER_SCHEMA = "urn:ietf:params:scim:schemas:extension:enterprise:2.0:User";
39
- declare const DEFAULT_USER_MAPPING: ScimResourceMapping;
40
- declare const DEFAULT_GROUP_MAPPING: ScimResourceMapping;
36
+ export declare const SCIM_USER_SCHEMA = "urn:ietf:params:scim:schemas:core:2.0:User";
37
+ export declare const SCIM_GROUP_SCHEMA = "urn:ietf:params:scim:schemas:core:2.0:Group";
38
+ export declare const SCIM_ENTERPRISE_USER_SCHEMA = "urn:ietf:params:scim:schemas:extension:enterprise:2.0:User";
39
+ export declare const DEFAULT_USER_MAPPING: ScimResourceMapping;
40
+ export declare const DEFAULT_GROUP_MAPPING: ScimResourceMapping;
41
41
  /** Translate inbound SCIM JSON → resource shape. */
42
- declare function scimToResource(scim: Record<string, unknown>, mapping: ScimResourceMapping): Record<string, unknown>;
42
+ export declare function scimToResource(scim: Record<string, unknown>, mapping: ScimResourceMapping): Record<string, unknown>;
43
43
  /** Translate resource → SCIM JSON. */
44
- declare function resourceToScim(resource: Record<string, unknown>, mapping: ScimResourceMapping, baseUrl: string): Record<string, unknown>;
44
+ export declare function resourceToScim(resource: Record<string, unknown>, mapping: ScimResourceMapping, baseUrl: string): Record<string, unknown>;
45
45
  //#endregion
46
46
  //#region src/scim/types.d.ts
47
47
  /**
@@ -138,7 +138,7 @@ interface ScimObservedEvent {
138
138
  * ```
139
139
  */
140
140
  type ScimType = "invalidFilter" | "tooMany" | "uniqueness" | "mutability" | "invalidSyntax" | "invalidPath" | "noTarget" | "invalidValue" | "invalidVers" | "sensitive";
141
- declare class ScimError extends Error {
141
+ export declare class ScimError extends Error {
142
142
  readonly statusCode: number;
143
143
  readonly scimType?: ScimType;
144
144
  constructor(statusCode: number, scimType: ScimType | undefined, detail: string);
@@ -197,9 +197,9 @@ type FilterNode = {
197
197
  * `undefined` to deny (yields 400 invalidFilter). Use `IDENTITY_MAP` when
198
198
  * the resource exposes SCIM-named attributes directly.
199
199
  */
200
- declare function parseScimFilter(filter: string, mapAttr: (scimAttr: string) => string | undefined): Record<string, unknown>;
200
+ export declare function parseScimFilter(filter: string, mapAttr: (scimAttr: string) => string | undefined): Record<string, unknown>;
201
201
  /** Pass-through mapper for resources that already use SCIM attribute names. */
202
- declare const IDENTITY_MAP: (a: string) => string;
202
+ export declare const IDENTITY_MAP: (a: string) => string;
203
203
  //#endregion
204
204
  //#region src/scim/patch.d.ts
205
205
  /**
@@ -248,17 +248,17 @@ interface ScimUpdate {
248
248
  /** Field unset (remove without filter). */
249
249
  $unset: Record<string, true>;
250
250
  }
251
- declare function parseScimPatch(req: ScimPatchRequest): ScimUpdate;
251
+ export declare function parseScimPatch(req: ScimPatchRequest): ScimUpdate;
252
252
  /**
253
253
  * Flatten a {@link ScimUpdate} into a plain `{ field: value }` object suitable
254
254
  * for arc resource `PATCH` handlers that expect a partial document. Drops
255
255
  * `$push` / `$pull` / `$unset` semantics — use {@link parseScimPatch} directly
256
256
  * when the host needs the full op stream (e.g. to issue array mutations).
257
257
  */
258
- declare function scimUpdateToFlatPatch(update: ScimUpdate): Record<string, unknown>;
258
+ export declare function scimUpdateToFlatPatch(update: ScimUpdate): Record<string, unknown>;
259
259
  //#endregion
260
260
  //#region src/scim/index.d.ts
261
261
  declare const scimPlugin: FastifyPluginAsync<ScimPluginOptions>;
262
262
  declare const _default: FastifyPluginAsync<ScimPluginOptions>;
263
263
  //#endregion
264
- export { DEFAULT_GROUP_MAPPING, DEFAULT_USER_MAPPING, type FilterNode, IDENTITY_MAP, SCIM_ENTERPRISE_USER_SCHEMA, SCIM_GROUP_SCHEMA, SCIM_USER_SCHEMA, ScimError, type ScimObservedEvent, type ScimPatchRequest, type ScimPluginOptions, type ScimResourceBinding, type ScimResourceMapping, type ScimType, type ScimUpdate, _default as default, parseScimFilter, parseScimPatch, resourceToScim, scimPlugin, scimToResource, scimUpdateToFlatPatch };
264
+ export { type FilterNode, type ScimObservedEvent, type ScimPatchRequest, type ScimPluginOptions, type ScimResourceBinding, type ScimResourceMapping, type ScimType, type ScimUpdate, _default as default, scimPlugin };
@@ -1,5 +1,5 @@
1
- /*! @classytic/arc v2.41.1 | MIT | https://classytic.com */
2
- import { t as parseJsonBody } from "../jsonBody-ELR9lfeU.mjs";
1
+ /*! @classytic/arc v2.44.0 | MIT | https://classytic.com */
2
+ import { t as parseJsonBody } from "../jsonBody-YIq-vGLv.mjs";
3
3
  import fp from "fastify-plugin";
4
4
  //#region src/scim/errors.ts
5
5
  var ScimError = class extends Error {