@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
@@ -1,6 +1,6 @@
1
1
 
2
- import { d as UserBase } from "../base-CxrV1LmG.mjs";
3
- import "../index-DHbyPo5j.mjs";
2
+ import { d as UserBase } from "../base-CZyixEQR.mjs";
3
+ import "../index-WH7xgvxS.mjs";
4
4
  import { FastifyPluginAsync } from "fastify";
5
5
  import { RepositoryLike } from "@classytic/repo-core/adapter";
6
6
  //#region src/audit/stores/interface.d.ts
@@ -37,6 +37,8 @@ interface AuditEntry {
37
37
  }
38
38
  interface AuditContext {
39
39
  user?: UserBase;
40
+ /** The actor from the request's VALIDATED scope (user or service client). Wins over `user`. */
41
+ actorId?: string;
40
42
  organizationId?: string;
41
43
  requestId?: string;
42
44
  ipAddress?: string;
@@ -86,7 +88,7 @@ interface AuditQueryOptions {
86
88
  /**
87
89
  * Create audit entry from context
88
90
  */
89
- declare function createAuditEntry(resource: string, documentId: string, action: AuditAction, context: AuditContext, data?: {
91
+ export declare function createAuditEntry(resource: string, documentId: string, action: AuditAction, context: AuditContext, data?: {
90
92
  before?: Record<string, unknown>;
91
93
  after?: Record<string, unknown>;
92
94
  metadata?: Record<string, unknown>;
@@ -191,8 +193,13 @@ declare module "fastify" {
191
193
  audit: AuditLogger;
192
194
  }
193
195
  interface FastifyRequest {
194
- /** Audit context for current request */
196
+ /**
197
+ * Audit context for the current request. The actor and organization are read from
198
+ * the VALIDATED scope when this is read — never captured before auth ran.
199
+ */
195
200
  auditContext?: AuditContext;
201
+ /** Extra fields a handler attaches to the audit row arc writes for this request. */
202
+ auditMetadata?: Record<string, unknown>;
196
203
  }
197
204
  }
198
205
  interface AuditLogger {
@@ -206,6 +213,11 @@ interface AuditLogger {
206
213
  restore: (resource: string, documentId: string, data: Record<string, unknown>, context?: AuditContext) => Promise<void>;
207
214
  /** Log a custom action */
208
215
  custom: (resource: string, documentId: string, action: string, data?: Record<string, unknown>, context?: AuditContext) => Promise<void>;
216
+ /**
217
+ * A logger bound to one request: every entry's actor and organization come from that
218
+ * request's validated scope, so a caller can neither forget nor forge them.
219
+ */
220
+ forRequest: (request: AuditRequestLike) => RequestAuditLogger;
209
221
  /** Query audit logs (if stores support it) */
210
222
  query: (options: AuditQueryOptions) => Promise<AuditEntry[]>;
211
223
  /**
@@ -215,7 +227,33 @@ interface AuditLogger {
215
227
  */
216
228
  purge: (cutoff: Date) => Promise<number>;
217
229
  }
218
- declare const auditPlugin: FastifyPluginAsync<AuditPluginOptions>;
230
+ /** {@link AuditLogger} writes with the context bound to one request. */
231
+ interface RequestAuditLogger {
232
+ create: (resource: string, documentId: string, data: Record<string, unknown>) => Promise<void>;
233
+ update: (resource: string, documentId: string, before: Record<string, unknown>, after: Record<string, unknown>) => Promise<void>;
234
+ delete: (resource: string, documentId: string, data: Record<string, unknown>) => Promise<void>;
235
+ restore: (resource: string, documentId: string, data: Record<string, unknown>) => Promise<void>;
236
+ custom: (resource: string, documentId: string, action: string, data?: Record<string, unknown>) => Promise<void>;
237
+ }
238
+ /** What {@link auditContextOf} reads — a FastifyRequest, or arc's `IRequestContext`. */
239
+ interface AuditRequestLike {
240
+ readonly scope?: unknown;
241
+ readonly user?: unknown;
242
+ readonly id?: string;
243
+ readonly ip?: string;
244
+ readonly headers?: Record<string, unknown>;
245
+ readonly method?: string;
246
+ readonly url?: string;
247
+ readonly routeOptions?: {
248
+ readonly url?: string;
249
+ };
250
+ readonly metadata?: unknown;
251
+ }
252
+ /**
253
+ * The audit context of a request, from its VALIDATED scope: the actor is the scope's user
254
+ * (or service client) and the organization is the scope's active organization.
255
+ */
256
+ export declare function auditContextOf(request: AuditRequestLike): AuditContext;
219
257
  declare const _default: FastifyPluginAsync<AuditPluginOptions>;
220
258
  //#endregion
221
259
  //#region src/audit/repository-audit-adapter.d.ts
@@ -237,14 +275,14 @@ interface RepositoryAuditStoreOptions {
237
275
  */
238
276
  readonly sessionProvider?: () => unknown;
239
277
  }
240
- declare function repositoryAsAuditStore(repository: RepositoryLike, options?: RepositoryAuditStoreOptions): AuditStore;
278
+ export declare function repositoryAsAuditStore(repository: RepositoryLike, options?: RepositoryAuditStoreOptions): AuditStore;
241
279
  //#endregion
242
280
  //#region src/audit/stores/memory.d.ts
243
281
  interface MemoryAuditStoreOptions {
244
282
  /** Maximum entries to keep (default: 1000) */
245
283
  maxEntries?: number;
246
284
  }
247
- declare class MemoryAuditStore implements AuditStore {
285
+ export declare class MemoryAuditStore implements AuditStore {
248
286
  readonly name = "memory";
249
287
  private entries;
250
288
  private maxEntries;
@@ -259,4 +297,4 @@ declare class MemoryAuditStore implements AuditStore {
259
297
  clear(): void;
260
298
  }
261
299
  //#endregion
262
- export { type AuditAction, type AuditContext, type AuditEntry, type AuditLogger, type AuditPluginOptions, type AuditQueryOptions, type AuditStore, type AuditStoreOptions, MemoryAuditStore, type MemoryAuditStoreOptions, _default as auditPlugin, auditPlugin as auditPluginFn, createAuditEntry, repositoryAsAuditStore };
300
+ export { type AuditAction, type AuditContext, type AuditEntry, type AuditLogger, type AuditPluginOptions, type AuditQueryOptions, type AuditRequestLike, type AuditStore, type AuditStoreOptions, type MemoryAuditStoreOptions, type RepositoryAuditStoreOptions, type RequestAuditLogger, _default as auditPlugin };
@@ -1,6 +1,7 @@
1
- /*! @classytic/arc v2.41.1 | MIT | https://classytic.com */
2
- import { t as transactionContext } from "../transactionContext-CPbdlnVw.mjs";
3
- import { n as declareRuntimeCapability } from "../runtimeCapabilities-CkhS6jn6.mjs";
1
+ /*! @classytic/arc v2.44.0 | MIT | https://classytic.com */
2
+ import { g as getUserId, i as getClientId } from "../types-fvO6Ovzx.mjs";
3
+ import { t as transactionContext } from "../transactionContext-orvnGu4F.mjs";
4
+ import { n as declareRuntimeCapability } from "../runtimeCapabilities-DIMKMV2G.mjs";
4
5
  import { and, anyOf, eq, gte, lt, lte } from "@classytic/repo-core/filter";
5
6
  import fp from "fastify-plugin";
6
7
  //#region src/audit/repository-audit-adapter.ts
@@ -86,7 +87,7 @@ function createAuditEntry(resource, documentId, action, context, data) {
86
87
  resource,
87
88
  documentId,
88
89
  action,
89
- userId: extractUserId(context.user),
90
+ userId: context.actorId ?? extractUserId(context.user),
90
91
  organizationId: context.organizationId,
91
92
  before: data?.before,
92
93
  after: data?.after,
@@ -182,6 +183,7 @@ const auditPlugin = async (fastify, opts = {}) => {
182
183
  if (!enabled) {
183
184
  fastify.decorate("audit", createNoopLogger());
184
185
  fastify.decorateRequest("auditContext", void 0);
186
+ fastify.decorateRequest("auditMetadata", void 0);
185
187
  fastify.log?.debug?.("Audit plugin disabled");
186
188
  return;
187
189
  }
@@ -220,6 +222,16 @@ const auditPlugin = async (fastify, opts = {}) => {
220
222
  ...data
221
223
  } }));
222
224
  },
225
+ forRequest(request) {
226
+ const ctx = () => auditContextOf(request);
227
+ return {
228
+ create: (resource, documentId, data) => audit.create(resource, documentId, data, ctx()),
229
+ update: (resource, documentId, before, after) => audit.update(resource, documentId, before, after, ctx()),
230
+ delete: (resource, documentId, data) => audit.delete(resource, documentId, data, ctx()),
231
+ restore: (resource, documentId, data) => audit.restore(resource, documentId, data, ctx()),
232
+ custom: (resource, documentId, action, data) => audit.custom(resource, documentId, action, data, ctx())
233
+ };
234
+ },
223
235
  async query(options) {
224
236
  for (const store of stores) if (store.query) return store.query(options);
225
237
  return [];
@@ -231,27 +243,10 @@ const auditPlugin = async (fastify, opts = {}) => {
231
243
  }
232
244
  };
233
245
  fastify.decorate("audit", audit);
234
- fastify.decorateRequest("auditContext", void 0);
235
- fastify.addHook("onRequest", async (request) => {
236
- const user = request.user;
237
- request.context;
238
- const scope = request.scope;
239
- request.auditContext = {
240
- user,
241
- organizationId: scope?.kind === "member" ? scope.organizationId : scope?.kind === "elevated" ? scope.organizationId : void 0,
242
- requestId: request.id,
243
- ipAddress: request.ip,
244
- userAgent: request.headers["user-agent"],
245
- endpoint: void 0,
246
- duration: void 0
247
- };
248
- });
249
- fastify.addHook("onResponse", async (request, reply) => {
250
- if (request.auditContext) {
251
- request.auditContext.endpoint = `${request.method} ${request.routeOptions?.url ?? request.url}`;
252
- request.auditContext.duration = Math.round(reply.elapsedTime);
253
- }
254
- });
246
+ fastify.decorateRequest("auditContext", { getter() {
247
+ return auditContextOf(this);
248
+ } });
249
+ fastify.decorateRequest("auditMetadata", void 0);
255
250
  const retention = opts.retention;
256
251
  let retentionTimer = null;
257
252
  if (retention) {
@@ -307,8 +302,10 @@ const auditPlugin = async (fastify, opts = {}) => {
307
302
  } else if (excludeResources.has(ctx.resource)) return;
308
303
  const docId = autoAuditExtractId(ctx.result);
309
304
  const scope = ctx.context?._scope;
305
+ const scopedActor = scope ? getUserId(scope) ?? getClientId(scope) : void 0;
310
306
  const auditCtx = {
311
307
  user: ctx.user,
308
+ ...scopedActor ? { actorId: String(scopedActor) } : {},
312
309
  organizationId: scope ? autoAuditGetOrgId(scope) : void 0
313
310
  };
314
311
  try {
@@ -331,6 +328,24 @@ const auditPlugin = async (fastify, opts = {}) => {
331
328
  }
332
329
  fastify.log?.debug?.({ stores: stores.map((s) => s.name) }, "Audit plugin enabled");
333
330
  };
331
+ /**
332
+ * The audit context of a request, from its VALIDATED scope: the actor is the scope's user
333
+ * (or service client) and the organization is the scope's active organization.
334
+ */
335
+ function auditContextOf(request) {
336
+ const scope = request.metadata?._scope ?? request.scope;
337
+ const actorId = scope ? getUserId(scope) ?? getClientId(scope) : void 0;
338
+ const userAgent = request.headers?.["user-agent"];
339
+ return {
340
+ user: request.user,
341
+ ...actorId ? { actorId: String(actorId) } : {},
342
+ organizationId: scope ? autoAuditGetOrgId(scope) : void 0,
343
+ requestId: request.id,
344
+ ipAddress: request.ip,
345
+ userAgent: typeof userAgent === "string" ? userAgent : void 0,
346
+ endpoint: request.method && request.routeOptions?.url ? `${request.method} ${request.routeOptions.url}` : void 0
347
+ };
348
+ }
334
349
  /** Extract document ID from a result */
335
350
  function autoAuditExtractId(doc) {
336
351
  if (!doc || typeof doc !== "object") return "";
@@ -361,6 +376,13 @@ function createNoopLogger() {
361
376
  delete: noop,
362
377
  restore: noop,
363
378
  custom: noop,
379
+ forRequest: () => ({
380
+ create: noop,
381
+ update: noop,
382
+ delete: noop,
383
+ restore: noop,
384
+ custom: noop
385
+ }),
364
386
  query: async () => [],
365
387
  purge: async () => 0,
366
388
  _noop: true
@@ -372,4 +394,4 @@ var auditPlugin_default = fp(auditPlugin, {
372
394
  dependencies: ["arc-core"]
373
395
  });
374
396
  //#endregion
375
- export { MemoryAuditStore, auditPlugin_default as auditPlugin, auditPlugin as auditPluginFn, createAuditEntry, repositoryAsAuditStore };
397
+ export { MemoryAuditStore, auditContextOf, auditPlugin_default as auditPlugin, createAuditEntry, repositoryAsAuditStore };
@@ -9,13 +9,13 @@ import { FastifyInstance } from "fastify";
9
9
  * unknown events still flow through when matched, just with the BA-supplied
10
10
  * name verbatim. Use this list for autocomplete-friendly defaults.
11
11
  */
12
- type AuthEventName = "session.create" | "session.delete" | "session.update" | "user.create" | "user.update" | "user.delete" | "mfa.enroll" | "mfa.verify" | "mfa.failed" | "mfa.disable" | "password.reset.request" | "password.reset.complete" | "email.verify" | "org.create" | "org.delete" | "org.invite.create" | "org.invite.accept" | "org.invite.reject" | "org.member.add" | "org.member.remove" | "org.member.role.update" | "apikey.create" | "apikey.delete" | "apikey.failed" | "endpoint.before" | "endpoint.after" | "endpoint.error";
12
+ export type AuthEventName = "session.create" | "session.delete" | "session.update" | "user.create" | "user.update" | "user.delete" | "mfa.enroll" | "mfa.verify" | "mfa.failed" | "mfa.disable" | "password.reset.request" | "password.reset.complete" | "email.verify" | "org.create" | "org.delete" | "org.invite.create" | "org.invite.accept" | "org.invite.reject" | "org.member.add" | "org.member.remove" | "org.member.role.update" | "apikey.create" | "apikey.delete" | "apikey.failed" | "endpoint.before" | "endpoint.after" | "endpoint.error";
13
13
  /**
14
14
  * Resolved auth event ready for audit. The bridge constructs this from
15
15
  * BA's hook context, then forwards to `app.audit.custom('auth', subjectId,
16
16
  * event.name, event.payload)`.
17
17
  */
18
- interface AuthEvent {
18
+ export interface AuthEvent {
19
19
  /** Canonical name (see {@link AuthEventName}). */
20
20
  name: string;
21
21
  /**
@@ -34,7 +34,7 @@ interface AuthEvent {
34
34
  /**
35
35
  * Options for `wireBetterAuthAudit`.
36
36
  */
37
- interface WireBetterAuthAuditOptions {
37
+ export interface WireBetterAuthAuditOptions {
38
38
  /**
39
39
  * Glob patterns selecting which events to audit. `*` matches a single
40
40
  * segment (`session.*` matches `session.create` but not `session.create.foo`),
@@ -72,7 +72,7 @@ interface WireBetterAuthAuditOptions {
72
72
  * Result of `wireBetterAuthAudit` — duck-typed BA `hooks` + `databaseHooks`
73
73
  * config plus an `attach(app)` method to connect the live audit logger.
74
74
  */
75
- interface BetterAuthAuditBridge {
75
+ export interface BetterAuthAuditBridge {
76
76
  /**
77
77
  * Spread into `betterAuth({ hooks })`. BA's top-level `hooks.before` /
78
78
  * `hooks.after` slots take a single async function each (not an array).
@@ -157,7 +157,7 @@ interface BetterAuthAuditBridge {
157
157
  * Single BA hook entry — duck-typed to avoid pinning a BA major version.
158
158
  * The shape mirrors `better-auth`'s public `hooks.{before,after}` API.
159
159
  */
160
- interface BetterAuthHookEntry {
160
+ export interface BetterAuthHookEntry {
161
161
  matcher: (ctx: BetterAuthHookContext) => boolean;
162
162
  /**
163
163
  * Returns a (possibly empty) object so BA's `runBeforeHooks` /
@@ -169,7 +169,7 @@ interface BetterAuthHookEntry {
169
169
  handler: (ctx: BetterAuthHookContext) => Promise<Record<string, unknown>> | Record<string, unknown>;
170
170
  }
171
171
  /** Subset of BA's hook context the bridge actually reads. */
172
- interface BetterAuthHookContext {
172
+ export interface BetterAuthHookContext {
173
173
  path?: string;
174
174
  method?: string;
175
175
  body?: Record<string, unknown>;
@@ -198,6 +198,6 @@ interface SessionLike {
198
198
  userAgent?: string;
199
199
  impersonatedBy?: string;
200
200
  }
201
- declare function wireBetterAuthAudit(opts?: WireBetterAuthAuditOptions): BetterAuthAuditBridge;
201
+ export declare function wireBetterAuthAudit(opts?: WireBetterAuthAuditOptions): BetterAuthAuditBridge;
202
202
  //#endregion
203
- export { AuthEvent, AuthEventName, BetterAuthAuditBridge, BetterAuthHookContext, BetterAuthHookEntry, WireBetterAuthAuditOptions, wireBetterAuthAudit };
203
+ export {};
@@ -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/auth/audit.ts
3
3
  function compileGlobs(patterns) {
4
4
  if (patterns.length === 0) return () => false;
@@ -1,6 +1,7 @@
1
1
 
2
- import { l as PermissionCheck } from "../base-CxrV1LmG.mjs";
3
- import { Ft as AuthPluginOptions, Pt as AuthHelpers } from "../index-DHbyPo5j.mjs";
2
+ import { i as RequestScope } from "../types-Ddl9OjmE.mjs";
3
+ import { l as PermissionCheck } from "../base-CZyixEQR.mjs";
4
+ import { At as AuthHelpers, jt as AuthPluginOptions } from "../index-WH7xgvxS.mjs";
4
5
  import { t as ExternalOpenApiPaths } from "../externalPaths-CmGOxOtD.mjs";
5
6
  import { a as SessionManagerOptions, c as createSessionManager, i as SessionData, n as MemorySessionStoreOptions, o as SessionManagerResult, r as SessionCookieOptions, s as SessionStore, t as MemorySessionStore } from "../sessionManager-qimlKV0P.mjs";
6
7
  import { FastifyPluginAsync, FastifyReply as FastifyReply$1, FastifyRequest } from "fastify";
@@ -17,7 +18,6 @@ declare module "fastify" {
17
18
  auth: AuthHelpers;
18
19
  }
19
20
  }
20
- declare const authPlugin: FastifyPluginAsync<AuthPluginOptions>;
21
21
  declare const _default: FastifyPluginAsync<AuthPluginOptions>;
22
22
  //#endregion
23
23
  //#region src/auth/betterAuth.d.ts
@@ -50,6 +50,30 @@ interface BetterAuthAdapterOptions {
50
50
  * @default false
51
51
  */
52
52
  orgContext?: boolean;
53
+ /**
54
+ * Effective roles in the active organization — the seam for an organization HIERARCHY (roles held
55
+ * at a parent, e.g. a head office, apply in its child organizations). Called with the caller's own
56
+ * membership roles there (`null` when not a member); returns the roles the request carries, or
57
+ * `null` to leave it non-member. Requires `orgContext`.
58
+ */
59
+ resolveOrgRoles?: (input: {
60
+ userId: string;
61
+ organizationId: string;
62
+ memberRoles: readonly string[] | null;
63
+ }) => readonly string[] | null | Promise<readonly string[] | null>;
64
+ /**
65
+ * Where the caller's own membership roles come from. Default `'better-auth'`: arc asks Better
66
+ * Auth (`getActiveMember`, then `getActiveMemberRole`) and passes the answer to `resolveOrgRoles`
67
+ * as `memberRoles`.
68
+ *
69
+ * `'resolver'`: `resolveOrgRoles` reads membership itself, so arc skips Better Auth's lookup — one
70
+ * fewer membership read per authenticated request — and calls it with `memberRoles: null`. Choose
71
+ * it ONLY for a resolver that never reads `memberRoles`; one that extends them would lose them.
72
+ * Requires `resolveOrgRoles`.
73
+ *
74
+ * @default 'better-auth'
75
+ */
76
+ memberLookup?: "better-auth" | "resolver";
53
77
  /**
54
78
  * OpenAPI documentation for auth endpoints.
55
79
  * - `true` (default): auto-extract from auth.api if available
@@ -86,16 +110,20 @@ interface BetterAuthAdapterOptions {
86
110
  */
87
111
  exposeAuthErrors?: boolean;
88
112
  /**
89
- * Per-route rate limit for the Better Auth catch-all (`/api/auth/*`),
90
- * applied via `@fastify/rate-limit`'s route config (requires the
91
- * limiter to be registered, which createApp does by default).
113
+ * Credentials other than a session, by `Authorization` scheme (RFC 7235) — a paired device, an
114
+ * API key. A request presenting a registered scheme is authenticated by its verifier ONLY: the
115
+ * scope it returns (usually `{ kind: 'service', … }`) is installed; `null` is a 401. Gate such
116
+ * routes with `requireServiceScope(...)`. `Bearer` is the session's and cannot be registered.
92
117
  *
93
- * Credential endpoints deserve a tighter bucket than the global limit
94
- * (brute-force / credential-stuffing surface), BUT the same catch-all
95
- * also serves Better Auth's chatty `get-session` heartbeat — so arc
96
- * ships NO default here; a forced strict limit (the classic "5/min on
97
- * /login") would 429 every SPA that polls sessions. Pick a ceiling that
98
- * covers your session-poll cadence, or key the bucket smarter:
118
+ * @example
119
+ * credentials: { Device: (value) => verifyDevice(value) } // Authorization: Device <sn>:<token>
120
+ */
121
+ credentials?: Readonly<Record<string, CredentialVerifier>>;
122
+ /**
123
+ * Per-route `@fastify/rate-limit` config for the auth catch-all. No default:
124
+ * the same route serves the chatty `get-session` heartbeat, so a strict
125
+ * login-style limit would 429 every SPA that polls sessions. Pick a ceiling
126
+ * above your poll cadence, or exempt the heartbeat:
99
127
  *
100
128
  * @example
101
129
  * ```ts
@@ -173,7 +201,9 @@ declare module "fastify" {
173
201
  * fastify.get('/me', { preHandler: [authenticate] }, handler);
174
202
  * ```
175
203
  */
176
- declare function createBetterAuthAdapter(options: BetterAuthAdapterOptions): BetterAuthAdapterResult;
204
+ /** Verifies one credential scheme's value; the scope to install, or `null` when it is not valid. */
205
+ type CredentialVerifier = (credentials: string, request: FastifyRequest) => Promise<RequestScope | null>;
206
+ export declare function createBetterAuthAdapter(options: BetterAuthAdapterOptions): BetterAuthAdapterResult;
177
207
  //#endregion
178
208
  //#region src/auth/betterAuthOpenApi.d.ts
179
209
  interface BetterAuthOpenApiOptions {
@@ -211,7 +241,7 @@ interface BetterAuthOpenApiOptions {
211
241
  * endpoints, converts their Zod schemas to JSON Schema via `z.toJSONSchema()`,
212
242
  * and returns a complete `ExternalOpenApiPaths` object ready for Arc's spec builder.
213
243
  */
214
- declare function extractBetterAuthOpenApi(authApi: Record<string, unknown>, options?: BetterAuthOpenApiOptions): ExternalOpenApiPaths;
244
+ export declare function extractBetterAuthOpenApi(authApi: Record<string, unknown>, options?: BetterAuthOpenApiOptions): ExternalOpenApiPaths;
215
245
  //#endregion
216
246
  //#region src/auth/trustedOrigins.d.ts
217
247
  /**
@@ -288,6 +318,6 @@ interface MirrorTrustedOriginsOptions {
288
318
  * array.
289
319
  * - `[canonicalUrl]` when `corsOrigins` is `false` / `undefined`.
290
320
  */
291
- declare function mirrorTrustedOriginsFromCors(options: MirrorTrustedOriginsOptions): string[];
321
+ export declare function mirrorTrustedOriginsFromCors(options: MirrorTrustedOriginsOptions): string[];
292
322
  //#endregion
293
- export { type AuthPluginOptions, type BetterAuthAdapterOptions, type BetterAuthAdapterResult, type BetterAuthHandler, type BetterAuthOpenApiOptions, type CorsOriginsConfig, MemorySessionStore, type MemorySessionStoreOptions, type MirrorTrustedOriginsOptions, type SessionCookieOptions, type SessionData, type SessionManagerOptions, type SessionManagerResult, type SessionStore, _default as authPlugin, authPlugin as authPluginFn, createBetterAuthAdapter, createSessionManager, extractBetterAuthOpenApi, mirrorTrustedOriginsFromCors };
323
+ export { type AuthPluginOptions, type BetterAuthAdapterOptions, type BetterAuthAdapterResult, type BetterAuthHandler, type BetterAuthOpenApiOptions, type CorsOriginsConfig, type CredentialVerifier, MemorySessionStore, type MemorySessionStoreOptions, type MirrorTrustedOriginsOptions, type SessionCookieOptions, type SessionData, type SessionManagerOptions, type SessionManagerResult, type SessionStore, _default as authPlugin, createSessionManager };