@classytic/arc 2.30.0 → 2.31.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 (239) hide show
  1. package/LICENSE +1 -1
  2. package/README.md +7 -0
  3. package/TRADEMARK.md +22 -0
  4. package/dist/{BaseController-B5DwufbB.mjs → BaseController-De7GqHck.mjs} +16 -17
  5. package/dist/{EventTransport-QmY7A258.d.mts → EventTransport-2uVV34WU.d.mts} +1 -0
  6. package/dist/{EventTransport-C6YA8xY9.mjs → EventTransport-9mlTgqzE.mjs} +1 -0
  7. package/dist/{HookSystem-WkIougo6.mjs → HookSystem-BcmPyq5I.mjs} +2 -1
  8. package/dist/{QueryCache-DOMT_iq7.d.mts → QueryCache-DFA-QrU9.d.mts} +2 -1
  9. package/dist/{ResourceRegistry-y9Hnypus.mjs → ResourceRegistry-qq0mn2s9.mjs} +3 -2
  10. package/dist/{actionPermissions-CAphcmUG.mjs → actionPermissions-BE5NwaQk.mjs} +1 -0
  11. package/dist/audit/index.d.mts +3 -2
  12. package/dist/audit/index.mjs +2 -1
  13. package/dist/auth/audit.d.mts +1 -0
  14. package/dist/auth/audit.mjs +1 -0
  15. package/dist/auth/index.d.mts +5 -4
  16. package/dist/auth/index.mjs +6 -5
  17. package/dist/auth/redis-session.d.mts +2 -1
  18. package/dist/auth/redis-session.mjs +1 -0
  19. package/dist/{applyPermissionResult-Cf6zUCvb.mjs → authorizationDecision-ChvUQGG1.mjs} +19 -10
  20. package/dist/{buildHandler-BYS3Vkfz.mjs → buildHandler-CDrCJzmB.mjs} +4 -3
  21. package/dist/cache/index.d.mts +4 -3
  22. package/dist/cache/index.mjs +4 -3
  23. package/dist/{caching-_NJeDd1_.mjs → caching-BFHA6Scf.mjs} +2 -1
  24. package/dist/cleanup/index.d.mts +4 -3
  25. package/dist/cleanup/index.mjs +9 -13
  26. package/dist/cli/commands/describe.d.mts +2 -1
  27. package/dist/cli/commands/describe.mjs +3 -2
  28. package/dist/cli/commands/docs.d.mts +1 -0
  29. package/dist/cli/commands/docs.mjs +3 -2
  30. package/dist/cli/commands/doctor.d.mts +1 -0
  31. package/dist/cli/commands/doctor.mjs +1 -0
  32. package/dist/cli/commands/generate.d.mts +2 -1
  33. package/dist/cli/commands/generate.mjs +2 -1
  34. package/dist/cli/commands/init.d.mts +2 -1
  35. package/dist/cli/commands/init.mjs +2 -1
  36. package/dist/cli/commands/introspect.d.mts +1 -0
  37. package/dist/cli/commands/introspect.mjs +2 -1
  38. package/dist/cli/index.d.mts +3 -2
  39. package/dist/cli/index.mjs +3 -2
  40. package/dist/{constants-DNre0wRg.mjs → constants-D3Dv4LAV.mjs} +1 -0
  41. package/dist/context/index.d.mts +1 -0
  42. package/dist/context/index.mjs +3 -2
  43. package/dist/core/index.d.mts +3 -2
  44. package/dist/core/index.mjs +7 -6
  45. package/dist/{core-poalJLvp.mjs → core-BGpIxINN.mjs} +3 -2
  46. package/dist/{createActionRouter-BxuywVRB.mjs → createActionRouter-CDXEdasl.mjs} +8 -7
  47. package/dist/{createAggregationRouter-BiY-lGdY.mjs → createAggregationRouter-KrQDzx-k.mjs} +7 -6
  48. package/dist/{createApp-CXrKFciH.mjs → createApp-Df89BEni.mjs} +484 -209
  49. package/dist/{defineAggregation-ufWjUwml.mjs → defineAggregation-DbPPS8RI.mjs} +1 -0
  50. package/dist/{defineResource-CxWqDCAy.mjs → defineResource-Bv6EtN1x.mjs} +63 -38
  51. package/dist/discovery/index.d.mts +1 -0
  52. package/dist/discovery/index.mjs +1 -0
  53. package/dist/docs/index.d.mts +3 -2
  54. package/dist/docs/index.mjs +3 -2
  55. package/dist/{elevation-C9s5hFP_.d.mts → elevation-BXCfVcNG.d.mts} +1 -0
  56. package/dist/{elevation-CIz1GjGm.mjs → elevation-D2l1mh4g.mjs} +4 -3
  57. package/dist/encryption/index.d.mts +2 -1
  58. package/dist/encryption/index.mjs +3 -2
  59. package/dist/{errorHandler-Bq2OeEQL.mjs → errorHandler-C5NeD6bT.mjs} +3 -2
  60. package/dist/{errorHandler-C6yv4ZTW.d.mts → errorHandler-CpYUJmPA.d.mts} +1 -0
  61. package/dist/{errors-DHcHVAgk.d.mts → errors-BGdVwP25.d.mts} +1 -0
  62. package/dist/{errors-DkO8Nk6-.mjs → errors-Cj42AntO.mjs} +1 -0
  63. package/dist/{eventPlugin-D2NNR4py.mjs → eventPlugin-CkffEihm.mjs} +5 -4
  64. package/dist/{eventPlugin-Bg3cP2_u.d.mts → eventPlugin-GF5nnBIq.d.mts} +2 -1
  65. package/dist/events/index.d.mts +4 -3
  66. package/dist/events/index.mjs +16 -4
  67. package/dist/events/transports/redis-stream-entry.d.mts +2 -1
  68. package/dist/events/transports/redis-stream-entry.mjs +1 -0
  69. package/dist/events/transports/redis.d.mts +2 -1
  70. package/dist/events/transports/redis.mjs +1 -0
  71. package/dist/{externalPaths-BE6wFpYv.d.mts → externalPaths-CmGOxOtD.d.mts} +1 -0
  72. package/dist/factory/index.d.mts +9 -5
  73. package/dist/factory/index.mjs +7 -6
  74. package/dist/{fastify-augmentation-0WhPnUH7.d.mts → fastify-augmentation-zh0_AYHa.d.mts} +13 -4
  75. package/dist/{fields-CQ_7KQWw.mjs → fields-CB5Cuzk9.mjs} +1 -0
  76. package/dist/{fields-D3lhgzhD.d.mts → fields-I_EeRWND.d.mts} +1 -0
  77. package/dist/{generate-BnsL9GBm.d.mts → generate-DCGuEbqI.d.mts} +1 -0
  78. package/dist/{generate-D0wZupBg.mjs → generate-EcA6ui4i.mjs} +2 -1
  79. package/dist/{headers-BvlQSDMB.mjs → headers-3zvE0rM3.mjs} +2 -1
  80. package/dist/{health-CB_m4S32.mjs → health-DjZYjM7H.mjs} +1 -0
  81. package/dist/hooks/index.d.mts +2 -1
  82. package/dist/hooks/index.mjs +2 -1
  83. package/dist/idempotency/index.d.mts +3 -2
  84. package/dist/idempotency/index.mjs +4 -3
  85. package/dist/idempotency/redis.d.mts +2 -1
  86. package/dist/idempotency/redis.mjs +1 -0
  87. package/dist/{index-Cx7bro4R.d.mts → index-Bc2ox8_U.d.mts} +5 -4
  88. package/dist/{index-CG4yqMDB.d.mts → index-C-481Zf2.d.mts} +74 -18
  89. package/dist/{index-Ch16aAah.d.mts → index-CEJNZFtX.d.mts} +53 -15
  90. package/dist/index-CFThn1K9.d.mts +224 -0
  91. package/dist/{index-Q5gzEIWY.d.mts → index-CpzxqePp.d.mts} +107 -32
  92. package/dist/index.d.mts +9 -8
  93. package/dist/index.mjs +11 -10
  94. package/dist/{init-tlRSt667.mjs → init-BEiNZBsJ.mjs} +19 -23
  95. package/dist/{init-BPEu8cs-.d.mts → init-Cscs4lab.d.mts} +1 -0
  96. package/dist/integrations/event-gateway.d.mts +3 -2
  97. package/dist/integrations/event-gateway.mjs +2 -1
  98. package/dist/integrations/index.d.mts +4 -3
  99. package/dist/integrations/index.mjs +1 -0
  100. package/dist/integrations/jobs/index.d.mts +2 -1
  101. package/dist/integrations/jobs/index.mjs +108 -32
  102. package/dist/integrations/mcp/index.d.mts +6 -6
  103. package/dist/integrations/mcp/index.mjs +2 -1
  104. package/dist/integrations/mcp/testing.d.mts +3 -2
  105. package/dist/integrations/mcp/testing.mjs +2 -2
  106. package/dist/integrations/streamline.d.mts +1 -0
  107. package/dist/integrations/streamline.mjs +3 -2
  108. package/dist/integrations/webhooks.d.mts +1 -0
  109. package/dist/integrations/webhooks.mjs +1 -0
  110. package/dist/integrations/websocket-pushref-redis.d.mts +2 -1
  111. package/dist/integrations/websocket-pushref-redis.mjs +1 -0
  112. package/dist/integrations/websocket-redis.d.mts +2 -1
  113. package/dist/integrations/websocket-redis.mjs +1 -0
  114. package/dist/integrations/websocket.d.mts +3 -2
  115. package/dist/integrations/websocket.mjs +1 -0
  116. package/dist/{interface-BGZCzOGL.d.mts → interface-BOk3YnTz.d.mts} +1 -0
  117. package/dist/{interface-Uk8ONJv7.d.mts → interface-DEWNOMza.d.mts} +1 -0
  118. package/dist/{interface-n5hpKg1d.mjs → interface-DYzM0q-u.mjs} +1 -0
  119. package/dist/{jsonBody-BlzAoGCe.mjs → jsonBody-BYbLDi5D.mjs} +2 -1
  120. package/dist/{keys-Cd316u_y.mjs → keys-C0_pa1tY.mjs} +1 -0
  121. package/dist/{loadResources-DpU8pfjV.mjs → loadResources-yawi9tAE.mjs} +2 -1
  122. package/dist/{loadResourcesFromEntry-kvGT6EcO.mjs → loadResourcesFromEntry-BAjYZonx.mjs} +2 -1
  123. package/dist/logger/index.d.mts +1 -0
  124. package/dist/logger/index.mjs +1 -0
  125. package/dist/{mcpPlugin-nRzm5g2s.mjs → mcpPlugin-B_es0hiF.mjs} +22 -23
  126. package/dist/{memory-CmnzXE5Z.mjs → memory-DR6wpa9F.mjs} +2 -1
  127. package/dist/{metrics-fdxI5mn_.mjs → metrics-CUdw_wLB.mjs} +2 -1
  128. package/dist/middleware/index.d.mts +2 -1
  129. package/dist/middleware/index.mjs +3 -2
  130. package/dist/migrations/index.d.mts +1 -0
  131. package/dist/migrations/index.mjs +2 -1
  132. package/dist/module-0pFJA6YW.mjs +78 -0
  133. package/dist/{multipartBody-D0eBvkeq.mjs → multipartBody-wOgj_fnj.mjs} +1 -0
  134. package/dist/{openapi-B8Vkbxbn.mjs → openapi-B2L4js1v.mjs} +6 -5
  135. package/dist/permissions/index.d.mts +5 -4
  136. package/dist/permissions/index.mjs +6 -5
  137. package/dist/{permissions-Bfu_8y1U.mjs → permissions-L--QDAir.mjs} +214 -41
  138. package/dist/{pipe-BOLmGIxa.mjs → pipe-DRDNsI2T.mjs} +2 -1
  139. package/dist/pipeline/index.d.mts +2 -1
  140. package/dist/pipeline/index.mjs +2 -1
  141. package/dist/{pipeline-BJnpkY9i.mjs → pipeline-UPQFatit.mjs} +6 -5
  142. package/dist/plugins/index.d.mts +8 -7
  143. package/dist/plugins/index.mjs +16 -15
  144. package/dist/plugins/response-cache.d.mts +1 -0
  145. package/dist/plugins/response-cache.mjs +2 -1
  146. package/dist/plugins/tracing-entry.d.mts +2 -1
  147. package/dist/plugins/tracing-entry.mjs +2 -1
  148. package/dist/{pluralize-BhrgwZb9.mjs → pluralize-O0FZgESl.mjs} +1 -0
  149. package/dist/{preloadResources-DCfa2egz.d.mts → preloadResources-CyoJMSiO.d.mts} +117 -12
  150. package/dist/{preloadResources-DSY5HGpS.mjs → preloadResources-Dz6BjzM6.mjs} +1 -0
  151. package/dist/presets/filesUpload.d.mts +6 -5
  152. package/dist/presets/filesUpload.mjs +9 -11
  153. package/dist/presets/index.d.mts +3 -2
  154. package/dist/presets/index.mjs +2 -1
  155. package/dist/presets/multiTenant.d.mts +2 -1
  156. package/dist/presets/multiTenant.mjs +3 -2
  157. package/dist/presets/search.d.mts +3 -2
  158. package/dist/presets/search.mjs +2 -1
  159. package/dist/{presets-CJOEXLuk.mjs → presets-C4-g4_4P.mjs} +4 -3
  160. package/dist/{projection-UBDgfMwu.mjs → projection-BHw6xlOQ.mjs} +2 -1
  161. package/dist/{pushref-registry-BbcnpNTm.d.mts → pushref-registry-Dov9l4qE.d.mts} +1 -0
  162. package/dist/{queryCachePlugin-DSGuSVLW.mjs → queryCachePlugin-BZr8v46E.mjs} +5 -4
  163. package/dist/{queryCachePlugin-br4otiqF.d.mts → queryCachePlugin-BzqxconG.d.mts} +3 -2
  164. package/dist/{rateLimit-DrED-C4v.mjs → rateLimit-CMPY5Qms.mjs} +7 -3
  165. package/dist/{rateLimitKey-BoWRH3e7.mjs → rateLimitKey-Dn5JNmkj.mjs} +1 -0
  166. package/dist/{realtime-B9nmIY5V.mjs → realtime-C5NoxWFY.mjs} +20 -9
  167. package/dist/{redis-UDtLaNj6.d.mts → redis-CduhKsJi.d.mts} +2 -1
  168. package/dist/{redis-stream-uIaauNpZ.d.mts → redis-stream-qN6A-RFv.d.mts} +2 -1
  169. package/dist/{registerRoute-JkxIEBJq.mjs → registerRoute-CBN84fc7.mjs} +4 -3
  170. package/dist/registry/index.d.mts +3 -2
  171. package/dist/registry/index.mjs +3 -2
  172. package/dist/{registry-BYXUUg21.mjs → registry-4213U4zb.mjs} +2 -1
  173. package/dist/{renewingLease-3cuQH7h8.mjs → renewingLease-Dll8mGgc.mjs} +1 -0
  174. package/dist/{replyHelpers-67tRawQZ.mjs → replyHelpers-DIiZKGUt.mjs} +2 -1
  175. package/dist/{repositoryFilter-Dz8k3S4P.mjs → repositoryFilter-f9eAfq_l.mjs} +1 -0
  176. package/dist/{requestContext-MrlJwECf.mjs → requestContext-2TxXOJ7T.mjs} +1 -0
  177. package/dist/{rolldown-runtime-8H4AJuhK.mjs → rolldown-runtime-CUsjNX9R.mjs} +1 -0
  178. package/dist/{schedules-CjOd2MG9.mjs → schedules-DdtlICTf.mjs} +3 -2
  179. package/dist/{schemaConverter-DJvodiHj.mjs → schemaConverter-D--wdJM-.mjs} +70 -17
  180. package/dist/{schemaIR-Ca7ySNPx.mjs → schemaIR-Bm2enbnh.mjs} +2 -1
  181. package/dist/schemas/index.d.mts +1 -0
  182. package/dist/schemas/index.mjs +1 -0
  183. package/dist/scim/index.d.mts +1 -0
  184. package/dist/scim/index.mjs +2 -1
  185. package/dist/scope/index.d.mts +3 -2
  186. package/dist/scope/index.mjs +5 -4
  187. package/dist/{scope-DfaPntpo.mjs → scope-DHx5Fous.mjs} +4 -3
  188. package/dist/{sessionManager-BHOM3N_0.d.mts → sessionManager-qimlKV0P.d.mts} +1 -0
  189. package/dist/{simpleEqualityMatcher-Cg05HPsn.mjs → simpleEqualityMatcher-DjQJ7FKA.mjs} +1 -0
  190. package/dist/{sse-BQ1w0dfl.mjs → sse-CUTo9y0X.mjs} +5 -4
  191. package/dist/{sseStream-BQ7Qaynp.mjs → sseStream-B_5XcVVd.mjs} +2 -1
  192. package/dist/{storage-DLUZ04kK.d.mts → storage-Cx9Sz3Ou.d.mts} +1 -0
  193. package/dist/{store-helpers-DY6xq4sM.mjs → store-helpers-D1MP_gRG.mjs} +1 -0
  194. package/dist/{streaming-BQcW6_Kq.mjs → streaming-PSZwi8JV.mjs} +1 -0
  195. package/dist/{subscribe-helpers-DfzsivM6.mjs → subscribe-helpers-C0Tb2aUp.mjs} +2 -1
  196. package/dist/sync/changelog.d.mts +1 -0
  197. package/dist/sync/changelog.mjs +1 -0
  198. package/dist/testing/cleanupStoreContract.d.mts +2 -1
  199. package/dist/testing/cleanupStoreContract.mjs +1 -0
  200. package/dist/testing/index.d.mts +139 -12
  201. package/dist/testing/index.mjs +230 -16
  202. package/dist/testing/outboxStoreContract.d.mts +1 -0
  203. package/dist/testing/outboxStoreContract.mjs +1 -0
  204. package/dist/testing/storageContract.d.mts +2 -1
  205. package/dist/testing/storageContract.mjs +1 -0
  206. package/dist/{tracing-CsGKQJGg.d.mts → tracing-Dro5eH6j.d.mts} +1 -0
  207. package/dist/{transactionContext-PNdoyLLm.mjs → transactionContext-dAfBVUrk.mjs} +1 -0
  208. package/dist/{typeGuards-C9KXUly6.mjs → typeGuards-B0ryPo3I.mjs} +1 -0
  209. package/dist/types/index.d.mts +6 -5
  210. package/dist/types/index.mjs +1 -0
  211. package/dist/types/storage.d.mts +2 -1
  212. package/dist/types/storage.mjs +1 -0
  213. package/dist/{types-ChvvVNP8.d.mts → types-B7XeLEgK.d.mts} +34 -2
  214. package/dist/{types-Cc91nXuA.d.mts → types-ByHn9rEO.d.mts} +1 -0
  215. package/dist/{types-C_ZZRcD6.d.mts → types-C4QE8qnm.d.mts} +1 -0
  216. package/dist/{types-D_n8L6VY.d.mts → types-CBgvkp8O.d.mts} +51 -12
  217. package/dist/{types-DjC-KUCz.d.mts → types-ChuShpCP.d.mts} +1 -0
  218. package/dist/{types-Bmmn-fO0.d.mts → types-CwcUQ610.d.mts} +2 -6
  219. package/dist/{types-CSyCxl2V.mjs → types-Cys0ko9Z.mjs} +4 -3
  220. package/dist/usage/index.d.mts +3 -2
  221. package/dist/usage/index.mjs +0 -0
  222. package/dist/{userHelpers-Cg_y3fBz.mjs → userHelpers-ChYJ6CdK.mjs} +1 -0
  223. package/dist/utils/index.d.mts +16 -7
  224. package/dist/utils/index.mjs +10 -9
  225. package/dist/{utils-taUVD4p3.mjs → utils-vL8BrDE2.mjs} +3 -2
  226. package/dist/{validate-Bk44F2yT.mjs → validate-_KbifpHF.mjs} +2 -1
  227. package/dist/{versioning-gKWyryT3.mjs → versioning-CHDTor3P.mjs} +3 -2
  228. package/dist/{versioning-DXgY5BUm.d.mts → versioning-D_rhHfZm.d.mts} +5 -2
  229. package/dist/{websocket-_Pd0CgR0.d.mts → websocket-BOXVggMf.d.mts} +2 -1
  230. package/package.json +547 -539
  231. package/skills/arc/SKILL.md +22 -8
  232. package/skills/arc/references/api-reference.md +4 -0
  233. package/skills/arc/references/auth.md +3 -0
  234. package/skills/arc/references/integrations.md +33 -0
  235. package/skills/arc/references/mcp.md +6 -7
  236. package/skills/arc/references/production.md +24 -25
  237. package/skills/arc-code-review/references/anti-patterns.md +3 -3
  238. package/dist/index-CCzVR-N3.d.mts +0 -107
  239. package/dist/module-_-M7C5ES.mjs +0 -29
@@ -1,10 +1,13 @@
1
- import { t as CRUD_OPERATIONS } from "../constants-DNre0wRg.mjs";
2
- import { t as BaseController } from "../BaseController-B5DwufbB.mjs";
3
- import { t as defineResource } from "../defineResource-CxWqDCAy.mjs";
4
- import { t as defineAggregation } from "../defineAggregation-ufWjUwml.mjs";
5
- import { n as createApp } from "../createApp-CXrKFciH.mjs";
6
- import { n as getModuleExports } from "../module-_-M7C5ES.mjs";
7
- import { n as preloadResourcesAsync, t as preloadResources } from "../preloadResources-DSY5HGpS.mjs";
1
+ /*! @classytic/arc v2.31.0 | MIT | https://classytic.com */
2
+ import { t as CRUD_OPERATIONS } from "../constants-D3Dv4LAV.mjs";
3
+ import { g as getUserId } from "../types-Cys0ko9Z.mjs";
4
+ import { t as BaseController } from "../BaseController-De7GqHck.mjs";
5
+ import { t as defineResource } from "../defineResource-Bv6EtN1x.mjs";
6
+ import { t as defineAggregation } from "../defineAggregation-DbPPS8RI.mjs";
7
+ import { S as requireOrgRole } from "../permissions-L--QDAir.mjs";
8
+ import { n as createApp } from "../createApp-Df89BEni.mjs";
9
+ import { n as getModuleExports } from "../module-0pFJA6YW.mjs";
10
+ import { n as preloadResourcesAsync, t as preloadResources } from "../preloadResources-Dz6BjzM6.mjs";
8
11
  import { runStorageContract } from "./storageContract.mjs";
9
12
  import Fastify from "fastify";
10
13
  import { afterAll, describe, expect, it, vi } from "vitest";
@@ -304,18 +307,30 @@ function createSpy(_name = "spy") {
304
307
  return spy;
305
308
  }
306
309
  /**
307
- * Wait for a condition to be true
310
+ * Wait until `condition` holds — the standard way to assert on asynchronous
311
+ * effects in arc's suites.
308
312
  *
309
- * Useful for async testing
313
+ * **Prefer this over `await sleep(n)` + assert.** A fixed delay encodes a guess
314
+ * about scheduling: too short and it flakes the moment the pool is busy, too
315
+ * long and every run pays for the worst case. Polling returns as soon as the
316
+ * effect lands, so the fast path stays fast and a loaded machine simply waits —
317
+ * and the failure mode becomes "this never happened", which is the thing worth
318
+ * asserting.
319
+ *
320
+ * Supply `label`: without it a timeout reports only its own duration, which is
321
+ * the least useful sentence a flaky test can print.
322
+ *
323
+ * @example
324
+ * await waitFor(() => runs >= 3, { label: "3 scheduler ticks" });
310
325
  */
311
326
  async function waitFor(condition, options = {}) {
312
- const { timeout = 5e3, interval = 100 } = options;
327
+ const { timeout = 5e3, interval = 25, label } = options;
313
328
  const startTime = Date.now();
314
329
  while (Date.now() - startTime < timeout) {
315
330
  if (await condition()) return;
316
331
  await new Promise((resolve) => setTimeout(resolve, interval));
317
332
  }
318
- throw new Error(`Timeout waiting for condition after ${timeout}ms`);
333
+ throw new Error(label ? `Timed out after ${timeout}ms waiting for: ${label}` : `Timed out after ${timeout}ms waiting for a condition (pass "label" to name it)`);
319
334
  }
320
335
  /**
321
336
  * Create a test timer that can be controlled
@@ -363,6 +378,7 @@ function createTestTimer() {
363
378
  async function runAuthorizationConformance(opts) {
364
379
  const rows = opts.rows ?? [{ _id: "1" }];
365
380
  let listFilter;
381
+ let getFilter;
366
382
  let aggFilter;
367
383
  const repo = createMockRepository({
368
384
  getAll: (async (params = {}) => {
@@ -378,6 +394,10 @@ async function runAuthorizationConformance(opts) {
378
394
  hasPrev: false
379
395
  };
380
396
  }),
397
+ getOne: (async (filter) => {
398
+ getFilter = filter;
399
+ return rows[0] ?? null;
400
+ }),
381
401
  aggregate: (async (req = {}) => {
382
402
  aggFilter = req.filter;
383
403
  return { rows: [{ tally: rows.length }] };
@@ -419,6 +439,10 @@ async function runAuthorizationConformance(opts) {
419
439
  method: "GET",
420
440
  url: "/confs"
421
441
  });
442
+ const get = await app.inject({
443
+ method: "GET",
444
+ url: "/confs/1"
445
+ });
422
446
  const aggregation = await app.inject({
423
447
  method: "GET",
424
448
  url: "/confs/aggregations/tally"
@@ -428,6 +452,10 @@ async function runAuthorizationConformance(opts) {
428
452
  status: list.statusCode,
429
453
  filter: listFilter
430
454
  },
455
+ get: {
456
+ status: get.statusCode,
457
+ filter: getFilter
458
+ },
431
459
  aggregation: {
432
460
  status: aggregation.statusCode,
433
461
  filter: aggFilter
@@ -775,6 +803,127 @@ async function setupBetterAuthTestApp(input) {
775
803
  };
776
804
  }
777
805
  //#endregion
806
+ //#region src/testing/testActor.ts
807
+ /**
808
+ * Test-only actor installation — how a package's HTTP tests say "run this
809
+ * request as a manager in org X".
810
+ *
811
+ * Identity in arc comes from `request.scope` and only from `request.scope`;
812
+ * headers like `x-role` are not identity. That rule left tests with no seam —
813
+ * `bootModuleApp` boots with `auth: false`, so every injected request arrived as
814
+ * `PUBLIC_SCOPE` and any role-gated route answered 401 with no supported way to
815
+ * be somebody.
816
+ *
817
+ * The `x-test-actor` header is a TRANSPORT for the test's intent, not a trusted
818
+ * claim. The authenticator that reads it is installed by `bootModuleApp` alone,
819
+ * behind the `@classytic/arc/testing` entrypoint; `createApp` never registers
820
+ * it, so no production build turns a forged header into identity. It is
821
+ * per-request rather than boot-only because the interesting authorization tests
822
+ * are the ones where a cashier is refused what a manager is allowed.
823
+ *
824
+ * Gate the routes under test with arc's own primitives — `requireRoles`,
825
+ * `requireOrgMembership`, `allOf(...)`. A test-specific gate would be a second
826
+ * implementation of authorization whose semantics could drift from the one
827
+ * actually shipping.
828
+ */
829
+ /** The header the testing harness uses to carry a request's actor. */
830
+ const TEST_ACTOR_HEADER = "x-test-actor";
831
+ /**
832
+ * Build the headers that make an injected request act as `actor`.
833
+ *
834
+ * A string is shorthand for a single role. `null` means "stay public", so a test
835
+ * can prove an endpoint is reachable anonymously.
836
+ *
837
+ * @example
838
+ * ```ts
839
+ * t.app.inject({ method: 'GET', url: '/pos/shifts/current',
840
+ * headers: testActorHeaders('manager', ORG) });
841
+ * ```
842
+ */
843
+ function testActorHeaders(actor, organizationId) {
844
+ if (actor === null) return {};
845
+ const org = organizationId !== void 0 ? { organizationId } : {};
846
+ const spec = typeof actor === "string" ? {
847
+ roles: [actor],
848
+ ...org
849
+ } : {
850
+ ...actor,
851
+ ...org
852
+ };
853
+ return { [TEST_ACTOR_HEADER]: JSON.stringify(spec) };
854
+ }
855
+ /**
856
+ * Turn a spec into a `RequestScope`.
857
+ *
858
+ * `orgRoles` defaults to `roles`: a test that says "manager" means it at both
859
+ * levels, and making the caller repeat itself only produces tests that pass a
860
+ * global gate and mysteriously fail an org one.
861
+ */
862
+ function scopeFromTestActor(spec) {
863
+ const roles = spec.roles ?? [];
864
+ const userId = spec.userId ?? `test-${roles.join("-") || "anon"}`;
865
+ if (spec.elevated !== void 0 && spec.elevated !== false) return {
866
+ kind: "elevated",
867
+ userId,
868
+ elevatedBy: spec.elevated === true ? "test" : spec.elevated,
869
+ ...spec.organizationId !== void 0 ? { organizationId: spec.organizationId } : {},
870
+ ...spec.context !== void 0 ? { context: Object.freeze({ ...spec.context }) } : {}
871
+ };
872
+ if (spec.organizationId !== void 0) return {
873
+ kind: "member",
874
+ userId,
875
+ userRoles: roles,
876
+ organizationId: spec.organizationId,
877
+ orgRoles: spec.orgRoles ?? roles,
878
+ ...spec.teamId !== void 0 ? { teamId: spec.teamId } : {},
879
+ ...spec.context !== void 0 ? { context: Object.freeze({ ...spec.context }) } : {}
880
+ };
881
+ return {
882
+ kind: "authenticated",
883
+ userId,
884
+ userRoles: roles
885
+ };
886
+ }
887
+ /**
888
+ * Build the `auth` option that makes the test actor arc's authenticator.
889
+ *
890
+ * It must be an authenticator, not a hook. Fastify encapsulates plugins and
891
+ * modules register DURING `createApp`, so a hook added to the root instance
892
+ * afterwards never runs for their routes — every request still resolves as
893
+ * public and every role gate still answers 401, which reads exactly like a
894
+ * broken authorization rule in the code under test. Registering as
895
+ * `auth: { type: 'authenticator' }` puts the scope where arc's own auth would,
896
+ * at the phase arc expects, so tenant filtering and permission checks behave
897
+ * identically to production.
898
+ *
899
+ * `defaultActor` applies to requests carrying no header. A malformed header
900
+ * REJECTS rather than falling back to public — a test that meant to be an admin
901
+ * and got quietly demoted is the most expensive way to fail.
902
+ */
903
+ function testActorAuth(defaultActor) {
904
+ const authenticate = async (request) => {
905
+ const req = request;
906
+ const raw = req.headers[TEST_ACTOR_HEADER];
907
+ let spec = defaultActor;
908
+ if (typeof raw === "string") try {
909
+ spec = JSON.parse(raw);
910
+ } catch {
911
+ throw new Error(`[arc-testing] malformed ${TEST_ACTOR_HEADER} header: ${raw}. Build it with testActorHeaders() rather than by hand.`);
912
+ }
913
+ if (spec === void 0) return;
914
+ req.scope = scopeFromTestActor(spec);
915
+ req.user = {
916
+ id: getUserId(req.scope) ?? "test-anon",
917
+ ...spec.roles !== void 0 ? { role: spec.roles } : {},
918
+ ...spec.organizationId !== void 0 ? { organizationId: spec.organizationId } : {}
919
+ };
920
+ };
921
+ return {
922
+ type: "authenticator",
923
+ authenticate
924
+ };
925
+ }
926
+ //#endregion
778
927
  //#region src/testing/bootModuleApp.ts
779
928
  /**
780
929
  * The built-in DB factory: in-memory MongoDB via lazy devDep imports.
@@ -807,7 +956,6 @@ async function bootModuleApp(modules, options = {}) {
807
956
  const db = await (options.database ?? mongoMemoryDatabase)({ replset: options.replset ?? false });
808
957
  const ctx = {
809
958
  uri: db.uri,
810
- mongoUri: db.uri,
811
959
  connection: db.connection
812
960
  };
813
961
  const resolved = typeof modules === "function" ? await modules(ctx) : modules;
@@ -816,7 +964,17 @@ async function bootModuleApp(modules, options = {}) {
816
964
  try {
817
965
  app = await createApp({
818
966
  preset: "testing",
819
- auth: false,
967
+ /**
968
+ * The test actor is arc's AUTHENTICATOR rather than a bolted-on hook, so the scope
969
+ * lands at the phase the rest of the pipeline expects — tenant filtering and
970
+ * permission checks then behave exactly as they do in production.
971
+ *
972
+ * This replaces the old `auth: false`. The authenticator is inert for a request
973
+ * carrying no `x-test-actor` header and no configured default, so a suite that
974
+ * never mentions an actor behaves as it did before. `options.app` still wins, so a
975
+ * suite exercising real auth can override it.
976
+ */
977
+ auth: testActorAuth(options.actor),
820
978
  sensible: false,
821
979
  ...options.app,
822
980
  modules: list
@@ -830,7 +988,6 @@ async function bootModuleApp(modules, options = {}) {
830
988
  return {
831
989
  app,
832
990
  uri: db.uri,
833
- mongoUri: db.uri,
834
991
  connection: db.connection,
835
992
  exports: (name) => getModuleExports(app, name),
836
993
  close: async () => {
@@ -1156,6 +1313,63 @@ function createHttpTestHarness(resource, optionsOrGetter) {
1156
1313
  return new HttpTestHarness(resource, optionsOrGetter);
1157
1314
  }
1158
1315
  //#endregion
1316
+ //#region src/testing/scopeRoleGate.ts
1317
+ /**
1318
+ * `scopeRoleGate` — the ONE role gate for package tests.
1319
+ *
1320
+ * ## Why this is in the framework
1321
+ *
1322
+ * Nine `@classytic/arc-*` packages had their own copy of this helper, and all nine were
1323
+ * wrong in the same two ways:
1324
+ *
1325
+ * 1. They read the role from an `x-role` HEADER and INSTALLED the request scope from
1326
+ * inside the check — so identity came from the caller's own claim, and it arrived too
1327
+ * late for arc's tenant-filtering pipeline to see it.
1328
+ * 2. They returned the pre-2.30 `{ granted: boolean }` shape, which is neither a boolean
1329
+ * nor an `AuthorizationDecision`. It passed through `normalizeToDecision` untouched,
1330
+ * arrived with `effect: undefined`, and was read as DENY — the gate said yes and arc
1331
+ * refused anyway. Nothing type-errored, because the gate is cast at the call site.
1332
+ *
1333
+ * Two of the nine had already been fixed by hand, each with a docstring describing this
1334
+ * exact discovery. The knowledge reached neither of the other seven. That is the whole
1335
+ * case for a shared seam: a copy does not fail, it diverges.
1336
+ *
1337
+ * A test role gate is generic — "does the verified scope hold one of these roles" has no
1338
+ * domain in it — so it belongs here, beside the contract it depends on.
1339
+ *
1340
+ * Identity itself comes from `testActorHeaders()` + `bootModuleApp`'s authenticator
1341
+ * (see `testActor.ts`), which puts the scope where production puts it. This stays a pure
1342
+ * predicate over what arc verified.
1343
+ *
1344
+ * Kept in its own module rather than beside `testActor.ts` so the two can be edited
1345
+ * independently.
1346
+ */
1347
+ /**
1348
+ * Grants iff the caller is an org member holding one of `allowed` as an ORG role.
1349
+ *
1350
+ * This is `requireOrgRole` under a name package tests reach for — deliberately a
1351
+ * delegation, not a reimplementation. It previously read `scope.userRoles` while
1352
+ * requiring an organization, so it disagreed with production for any actor whose
1353
+ * global and org roles differ: given
1354
+ * `{ roles: ["employee"], orgRoles: ["manager"], organizationId: "org-1" }`,
1355
+ * `requireOrgRole("manager")` allowed and this denied. The tests never caught it
1356
+ * because `scopeFromTestActor` defaults `orgRoles` to `roles`, making the two
1357
+ * dimensions identical for the common actor.
1358
+ *
1359
+ * Delegating also inherits the semantics a hand-rolled gate kept missing:
1360
+ * elevated scopes pass, and a service (API-key) identity is refused with a
1361
+ * message naming `requireServiceScope` instead of silently failing a role test.
1362
+ *
1363
+ * **Pair it with an org-bearing actor.** `testActorHeaders('manager', ORG)`
1364
+ * yields a `member` scope and passes; `testActorHeaders('manager')` yields an
1365
+ * `authenticated` scope with no tenant, which is refused — arc's generated CRUD
1366
+ * filters by tenant, so a gate that waves a tenant-less request through is how a
1367
+ * green test reads another org's rows.
1368
+ */
1369
+ function scopeRoleGate(...allowed) {
1370
+ return requireOrgRole(...allowed);
1371
+ }
1372
+ //#endregion
1159
1373
  //#region src/testing/testApp.ts
1160
1374
  /**
1161
1375
  * createTestApp — test app factory for arc
@@ -1221,7 +1435,7 @@ function pickDefaultAuth(authMode, callerAuth) {
1221
1435
  };
1222
1436
  }
1223
1437
  async function createTestApp(options = {}) {
1224
- const { createApp } = await import("../createApp-CXrKFciH.mjs").then((n) => n.r);
1438
+ const { createApp } = await import("../createApp-Df89BEni.mjs").then((n) => n.r);
1225
1439
  const { resources = [], db = "in-memory", connectMongoose = false, authMode = "jwt", defaultOrgId, plugins, auth: callerAuth, ...appOptions } = options;
1226
1440
  let dbHandle;
1227
1441
  let dbUri;
@@ -1291,4 +1505,4 @@ function createMinimalTestApp(options = {}) {
1291
1505
  });
1292
1506
  }
1293
1507
  //#endregion
1294
- export { HttpTestHarness, bootModuleApp, createBetterAuthProvider, createBetterAuthTestHelpers, createCustomAuthProvider, createDataFactory, createHttpTestHarness, createJwtAuthProvider, createMinimalTestApp, createMockController, createMockReply, createMockRepository, createMockRequest, createMockUser, createSpy, createTestApp, createTestFixtures, createTestTimer, expectArc, mongoMemoryDatabase, preloadResources, preloadResourcesAsync, runAuthorizationConformance, runStorageContract, safeParseBody, setupBetterAuthTestApp, waitFor };
1508
+ export { HttpTestHarness, TEST_ACTOR_HEADER, bootModuleApp, createBetterAuthProvider, createBetterAuthTestHelpers, createCustomAuthProvider, createDataFactory, createHttpTestHarness, createJwtAuthProvider, createMinimalTestApp, createMockController, createMockReply, createMockRepository, createMockRequest, createMockUser, createSpy, createTestApp, createTestFixtures, createTestTimer, expectArc, mongoMemoryDatabase, preloadResources, preloadResourcesAsync, runAuthorizationConformance, runStorageContract, safeParseBody, scopeFromTestActor, scopeRoleGate, setupBetterAuthTestApp, testActorAuth, testActorHeaders, waitFor };
@@ -1,3 +1,4 @@
1
+
1
2
  import { OutboxStore } from "@classytic/primitives/outbox";
2
3
  //#region src/testing/outboxStoreContract.d.ts
3
4
  interface OutboxStoreContractSetupResult {
@@ -1,3 +1,4 @@
1
+ /*! @classytic/arc v2.31.0 | MIT | https://classytic.com */
1
2
  import { OutboxOwnershipError } from "@classytic/primitives/outbox";
2
3
  import { afterAll, beforeAll, describe, expect, it } from "vitest";
3
4
  //#region src/testing/outboxStoreContract.ts
@@ -1,4 +1,5 @@
1
- import { t as Storage } from "../storage-DLUZ04kK.mjs";
1
+
2
+ import { t as Storage } from "../storage-Cx9Sz3Ou.mjs";
2
3
  //#region src/testing/storageContract.d.ts
3
4
  interface StorageContractSetupResult {
4
5
  storage: Storage;
@@ -1,3 +1,4 @@
1
+ /*! @classytic/arc v2.31.0 | MIT | https://classytic.com */
1
2
  import { afterAll, beforeAll, describe, expect, it } from "vitest";
2
3
  //#region src/testing/storageContract.ts
3
4
  /**
@@ -1,3 +1,4 @@
1
+
1
2
  import { FastifyInstance, FastifyRequest } from "fastify";
2
3
  import { Span, Tracer } from "@opentelemetry/api";
3
4
  //#region src/plugins/tracing.d.ts
@@ -1,3 +1,4 @@
1
+ /*! @classytic/arc v2.31.0 | MIT | https://classytic.com */
1
2
  import { AsyncLocalStorage } from "node:async_hooks";
2
3
  //#region src/context/transactionContext.ts
3
4
  /**
@@ -1,3 +1,4 @@
1
+ /*! @classytic/arc v2.31.0 | MIT | https://classytic.com */
1
2
  //#region src/utils/typeGuards.ts
2
3
  /** Check if fastify has the events plugin registered */
3
4
  function hasEvents(instance) {
@@ -1,5 +1,6 @@
1
- import { $ as OpenApiSchemas, A as RequestIdOptions, At as RouteDefinition, Bt as RouteHandler, C as RequestContext, D as HealthCheck, Dt as ActionEntry, E as GracefulShutdownOptions, Et as ActionDefinition, F as FastifyWithDecorators, Ft as ControllerLike, G as OnTenantDeleteConfig, Gt as AuthenticatorContext, H as CrudController, Ht as AuthHelpers, I as MiddlewareHandler, It as FastifyHandler, J as PresetHook, K as ResolvedTenantPurge, Kt as JwtContext, L as RequestWithExtras, Lt as IController, M as EventsDecorator, Mt as RouteMethod, N as FastifyRequestExtras, Nt as RateLimitConfig, O as HealthOptions, Ot as ActionHandlerFn, P as FastifyWithAuth, Pt as ControllerHandler, Q as MiddlewareConfig, Rt as IControllerResponse, S as QueryParserInterface, T as CrudRouterOptions, Tt as ResourceCacheConfig, U as ResourceConfig, Ut as AuthPluginOptions, W as ResourcePermissions, Wt as Authenticator, X as CrudRouteKey, Y as PresetResult, Z as CrudSchemas, _ as ControllerQueryOptions, a as InferAdapterDoc, at as ResourceExtensions, b as ParsedQuery, c as TypedController, d as PaginationResult, et as RouteSchemaOptions, f as IntrospectionData, g as ArcInternalMetadata, h as ResourceMetadata, i as ValidationResult, it as ResourceHooks, j as ArcDecorator, jt as RouteMcpConfig, k as IntrospectionPluginOptions, kn as BaseControllerOptions, kt as ActionsMap, l as TypedRepository, m as RegistryStats, n as ConfigError, nt as FieldRule, o as InferDocType, ot as EventDefinition, p as RegistryEntry, q as PresetFunction, qt as TokenPair, r as ValidateOptions, rt as ResourceHookContext, s as InferResourceDoc, t as RouteHandlerMethod, tt as ArcFieldRule, u as TypedResourceConfig, v as LookupOption, w as ServiceContext, x as PopulateOption, y as OwnershipCheck, zt as IRequestContext } from "../index-Q5gzEIWY.mjs";
2
- import { i as RequestScope } from "../types-DjC-KUCz.mjs";
3
- import { a as UserBase, d as JWTPayload, f as ObjectId, i as PermissionContext, l as AnyRecord, m as UserOrganization, n as DataPolicy, p as UserLike, r as PermissionCheck, t as AuthorizationDecision, u as ArcRequest } from "../types-ChvvVNP8.mjs";
4
- import { n as ElevationOptions, t as ElevationEvent } from "../elevation-C9s5hFP_.mjs";
5
- export type { ActionDefinition, ActionEntry, ActionHandlerFn, ActionsMap, AnyRecord, ArcDecorator, ArcFieldRule, ArcInternalMetadata, ArcRequest, AuthHelpers, AuthPluginOptions, Authenticator, AuthenticatorContext, AuthorizationDecision, BaseControllerOptions, ConfigError, ControllerHandler, ControllerLike, ControllerQueryOptions, CrudController, CrudRouteKey, CrudRouterOptions, CrudSchemas, DataPolicy, ElevationEvent, ElevationOptions, EventDefinition, EventsDecorator, FastifyHandler, FastifyRequestExtras, FastifyWithAuth, FastifyWithDecorators, FieldRule, GracefulShutdownOptions, HealthCheck, HealthOptions, IController, IControllerResponse, IRequestContext, InferAdapterDoc, InferDocType, InferResourceDoc, IntrospectionData, IntrospectionPluginOptions, JWTPayload, JwtContext, LookupOption, MiddlewareConfig, MiddlewareHandler, ObjectId, OnTenantDeleteConfig, OpenApiSchemas, OwnershipCheck, PaginationResult, ParsedQuery, PermissionCheck, PermissionContext, PopulateOption, PresetFunction, PresetHook, PresetResult, QueryParserInterface, RateLimitConfig, RegistryEntry, RegistryStats, RequestContext, RequestIdOptions, RequestScope, RequestWithExtras, ResolvedTenantPurge, ResourceCacheConfig, ResourceConfig, ResourceExtensions, ResourceHookContext, ResourceHooks, ResourceMetadata, ResourcePermissions, RouteDefinition, RouteHandler, RouteHandlerMethod, RouteMcpConfig, RouteMethod, RouteSchemaOptions, ServiceContext, TokenPair, TypedController, TypedRepository, TypedResourceConfig, UserBase, UserLike, UserOrganization, ValidateOptions, ValidationResult };
1
+
2
+ import { $ as OpenApiSchemas, A as RequestIdOptions, An as BaseControllerOptions, At as RouteDefinition, Bt as RawRouteHandler, C as RequestContext, D as HealthCheck, Dt as ActionEntry, E as GracefulShutdownOptions, Et as ActionDefinition, F as FastifyWithDecorators, Ft as ControllerLike, G as OnTenantDeleteConfig, Gt as Authenticator, H as CrudController, I as MiddlewareHandler, It as FastifyHandler, J as PresetHook, Jt as TokenPair, K as ResolvedTenantPurge, Kt as AuthenticatorContext, L as RequestWithExtras, Lt as IController, M as EventsDecorator, Mt as RouteMethod, N as FastifyRequestExtras, Nt as RateLimitConfig, O as HealthOptions, Ot as ActionHandlerFn, P as FastifyWithAuth, Pt as ControllerHandler, Q as MiddlewareConfig, Rt as IControllerResponse, S as QueryParserInterface, T as CrudRouterOptions, Tt as ResourceCacheConfig, U as ResourceConfig, Ut as AuthHelpers, Vt as RouteHandler, W as ResourcePermissions, Wt as AuthPluginOptions, X as CrudRouteKey, Y as PresetResult, Z as CrudSchemas, _ as ControllerQueryOptions, a as InferAdapterDoc, at as ResourceExtensions, b as ParsedQuery, c as TypedController, d as PaginationResult, et as RouteSchemaOptions, f as IntrospectionData, g as ArcInternalMetadata, h as ResourceMetadata, i as ValidationResult, it as ResourceHooks, j as ArcDecorator, jt as RouteMcpConfig, k as IntrospectionPluginOptions, kt as ActionsMap, l as TypedRepository, m as RegistryStats, n as ConfigError, nt as FieldRule, o as InferDocType, ot as EventDefinition, p as RegistryEntry, q as PresetFunction, qt as JwtContext, r as ValidateOptions, rt as ResourceHookContext, s as InferResourceDoc, t as RouteHandlerMethod, tt as ArcFieldRule, u as TypedResourceConfig, v as LookupOption, w as ServiceContext, x as PopulateOption, y as OwnershipCheck, zt as IRequestContext } from "../index-CpzxqePp.mjs";
3
+ import { i as RequestScope } from "../types-ChuShpCP.mjs";
4
+ import { a as UserBase, d as JWTPayload, f as ObjectId, i as PermissionContext, l as AnyRecord, m as UserOrganization, n as DataPolicy, p as UserLike, r as PermissionCheck, t as AuthorizationDecision, u as ArcRequest } from "../types-B7XeLEgK.mjs";
5
+ import { n as ElevationOptions, t as ElevationEvent } from "../elevation-BXCfVcNG.mjs";
6
+ export type { ActionDefinition, ActionEntry, ActionHandlerFn, ActionsMap, AnyRecord, ArcDecorator, ArcFieldRule, ArcInternalMetadata, ArcRequest, AuthHelpers, AuthPluginOptions, Authenticator, AuthenticatorContext, AuthorizationDecision, BaseControllerOptions, ConfigError, ControllerHandler, ControllerLike, ControllerQueryOptions, CrudController, CrudRouteKey, CrudRouterOptions, CrudSchemas, DataPolicy, ElevationEvent, ElevationOptions, EventDefinition, EventsDecorator, FastifyHandler, FastifyRequestExtras, FastifyWithAuth, FastifyWithDecorators, FieldRule, GracefulShutdownOptions, HealthCheck, HealthOptions, IController, IControllerResponse, IRequestContext, InferAdapterDoc, InferDocType, InferResourceDoc, IntrospectionData, IntrospectionPluginOptions, JWTPayload, JwtContext, LookupOption, MiddlewareConfig, MiddlewareHandler, ObjectId, OnTenantDeleteConfig, OpenApiSchemas, OwnershipCheck, PaginationResult, ParsedQuery, PermissionCheck, PermissionContext, PopulateOption, PresetFunction, PresetHook, PresetResult, QueryParserInterface, RateLimitConfig, RawRouteHandler, RegistryEntry, RegistryStats, RequestContext, RequestIdOptions, RequestScope, RequestWithExtras, ResolvedTenantPurge, ResourceCacheConfig, ResourceConfig, ResourceExtensions, ResourceHookContext, ResourceHooks, ResourceMetadata, ResourcePermissions, RouteDefinition, RouteHandler, RouteHandlerMethod, RouteMcpConfig, RouteMethod, RouteSchemaOptions, ServiceContext, TokenPair, TypedController, TypedRepository, TypedResourceConfig, UserBase, UserLike, UserOrganization, ValidateOptions, ValidationResult };
@@ -1 +1,2 @@
1
+ /*! @classytic/arc v2.31.0 | MIT | https://classytic.com */
1
2
  export {};
@@ -1,2 +1,3 @@
1
- import { a as StorageReadResult, i as StorageReadRange, n as StorageContext, o as StorageUploadInput, r as StorageFile, t as Storage } from "../storage-DLUZ04kK.mjs";
1
+
2
+ import { a as StorageReadResult, i as StorageReadRange, n as StorageContext, o as StorageUploadInput, r as StorageFile, t as Storage } from "../storage-Cx9Sz3Ou.mjs";
2
3
  export { Storage, StorageContext, StorageFile, StorageReadRange, StorageReadResult, StorageUploadInput };
@@ -1 +1,2 @@
1
+ /*! @classytic/arc v2.31.0 | MIT | https://classytic.com */
1
2
  export {};
@@ -1,4 +1,5 @@
1
- import { i as RequestScope } from "./types-DjC-KUCz.mjs";
1
+
2
+ import { i as RequestScope } from "./types-ChuShpCP.mjs";
2
3
  import { FastifyRequest } from "fastify";
3
4
  //#region src/types/base.d.ts
4
5
  declare module "fastify" {
@@ -53,7 +54,7 @@ interface JWTPayload {
53
54
  [key: string]: unknown;
54
55
  }
55
56
  /**
56
- * Typed Fastify request with Arc decorations. Use in `raw: true` handlers
57
+ * Typed Fastify request with Arc decorations. Use in `rawHandler` routes
57
58
  * instead of `(req as any).user`.
58
59
  *
59
60
  * @example
@@ -241,8 +242,27 @@ type PermissionCheck<TDoc = Record<string, unknown>> = ((context: PermissionCont
241
242
  interface PermissionCheckMeta {
242
243
  /** Set by allowPublic() — marks the endpoint as publicly accessible */
243
244
  _isPublic?: boolean;
245
+ /**
246
+ * Set by requireAuth() — a pure identity gate (must be signed in) that adds NO
247
+ * row-level or environmental condition. Marked so `allOf` does not mistake it
248
+ * for an opaque runtime branch and needlessly taint the composite `conditional`.
249
+ */
250
+ _requiresAuth?: boolean;
244
251
  /** Set by requireRoles() — the roles required for access */
245
252
  _roles?: readonly string[];
253
+ /**
254
+ * Set by `requirePlatformRole()` — this gate consults PLATFORM roles only and
255
+ * can never be satisfied by an organization role.
256
+ *
257
+ * It exists because that property is otherwise unprovable from outside a
258
+ * check. `requireRoles(["ops"])` accepts an ORG role named `ops` by default,
259
+ * and `requireOrgRole("manager")` grants any org's manager — both return a
260
+ * bare allow with no policy, so a surface that must be platform-wide cannot
261
+ * tell them apart from a genuine operator gate by inspecting the decision.
262
+ * A global surface (see `jobsPlugin`'s `managementRoutes`) requires this
263
+ * marker at boot rather than trusting the caller's intent.
264
+ */
265
+ _platformOnly?: boolean;
246
266
  /** Set by requireOrgMembership() — org-level permission type */
247
267
  _orgPermission?: string;
248
268
  /** Set by requireOrgRole() — the org roles required for access */
@@ -290,6 +310,18 @@ interface PermissionCheckMeta {
290
310
  scopes?: readonly string[];
291
311
  dpop: boolean;
292
312
  };
313
+ /**
314
+ * Set by `allOf(...)` when its surfaced role/scope meta is NECESSARY but NOT
315
+ * SUFFICIENT — the conjunction also contains an opaque runtime branch
316
+ * (ownership / custom / dynamic / flow-mode / quota …) or a contradictory
317
+ * constraint that flat metadata can't capture. `describePermission` propagates
318
+ * it as `conditional: true`, so `explainAccess` reports a role/scope MATCH as
319
+ * `conditional` (server decides) instead of a definitive — and unsound —
320
+ * `allow`. A role/scope MISMATCH is still a definitive `deny` (a necessary
321
+ * condition failed). Without this flag a composed gate could tell a UI "you can"
322
+ * when the runtime check will refuse.
323
+ */
324
+ _conditional?: boolean;
293
325
  }
294
326
  //#endregion
295
327
  export { UserBase as a, normalizeRoles as c, JWTPayload as d, ObjectId as f, PermissionContext as i, AnyRecord as l, UserOrganization as m, DataPolicy as n, getUserId as o, UserLike as p, PermissionCheck as r, getUserRoles as s, AuthorizationDecision as t, ArcRequest as u };
@@ -1,3 +1,4 @@
1
+
1
2
  import { FastifyRequest } from "fastify";
2
3
  //#region src/encryption/types.d.ts
3
4
  /** JWE key-management algorithm — the protected-header `alg`. */
@@ -1,3 +1,4 @@
1
+
1
2
  import { PurgeActor, PurgeEvidence, PurgeResourceResult, PurgeStrategyKind as PurgeStrategyKind$1, PurgeVerificationSummary } from "@classytic/primitives/retention";
2
3
  //#region src/cleanup/types.d.ts
3
4
  /** Ambient context every recipe method receives. Host-injected — never global. */
@@ -1,5 +1,6 @@
1
- import { n as DomainEvent, r as EventHandler } from "./EventTransport-QmY7A258.mjs";
2
- import { n as ErrorMapper } from "./errorHandler-C6yv4ZTW.mjs";
1
+
2
+ import { n as DomainEvent, r as EventHandler } from "./EventTransport-2uVV34WU.mjs";
3
+ import { n as ErrorMapper } from "./errorHandler-CpYUJmPA.mjs";
3
4
  import { FastifyInstance, FastifyPluginAsync } from "fastify";
4
5
  import { LockAdapter } from "@classytic/repo-core/lock";
5
6
  //#region src/plugins/health.d.ts
@@ -409,6 +410,13 @@ interface ArcModule<TExports = unknown> {
409
410
  * *importing* a dependent until its dependency is ready. Use a thunk to gate
410
411
  * whether a module is imported at all (region/tier packs); use `dependsOn`
411
412
  * to order modules that are already selected.
413
+ *
414
+ * **Convention: every HARD sibling dependency belongs here** — including
415
+ * ones read lazily. `lazyRequiredModuleExports` defers the export READ to
416
+ * first use (and eagerly validates the module is composed), but only
417
+ * `dependsOn` orders composition so the sibling's engine exists before
418
+ * yours initializes. Omit the edge only for genuinely optional
419
+ * integrations (`lazyModuleExports` / `getOptionalModuleExports`).
412
420
  */
413
421
  readonly dependsOn?: readonly string[];
414
422
  /**
@@ -476,15 +484,21 @@ interface ArcModule<TExports = unknown> {
476
484
  * Semantics:
477
485
  * - Purely a supersede-the-app-fork switch — it filters the *app* resource
478
486
  * list, never the module's own `resources` (those always register).
479
- * - Static + declarative: read at compose time, so it works regardless of
480
- * whether `resources` is an array or a post-bootstrap factory (no early
481
- * factory resolution, no boot-order coupling).
482
- * - Idempotent + tolerant: an `owns` entry with no matching app resource is
483
- * a silent no-op — a module may pre-declare a name before any fork exists.
487
+ * - Idempotent + tolerant on the APP side: an `owns` entry with no matching
488
+ * app resource is a silent no-op — a module may pre-declare a name before
489
+ * any fork exists.
484
490
  * - Collected across ALL modules; the app-side supersession is their union.
485
491
  *
486
- * Contract: if you `owns` a name, PROVIDE it (in this module's `resources`).
487
- * Dropping the app fork without supplying a replacement removes the route.
492
+ * **Contract (ENFORCED at boot): if you `owns` a name, PROVIDE it** — in
493
+ * THIS module's own `resources`. Arc resolves module resources first and
494
+ * fails boot when a claimed name is not among them, naming the module, the
495
+ * unmet names, and what it did supply. Without that check, a typo or a
496
+ * resource deleted without updating `owns` booted "successfully" with the
497
+ * app's route silently removed and nothing serving it.
498
+ *
499
+ * A sibling module supplying the name does NOT satisfy the claim — ownership
500
+ * is local to the module that declares it, which is the colocation this arm
501
+ * exists for.
488
502
  */
489
503
  readonly owns?: readonly string[];
490
504
  /**
@@ -569,9 +583,34 @@ interface ArcModule<TExports = unknown> {
569
583
  */
570
584
  readonly scheduledJobs?: ModuleContribution<ScheduleDefinition>;
571
585
  /**
572
- * Teardown — registered as a Fastify `onClose` hook. Destroy engines, stop
573
- * timers, flush the module's outbox. Modules close in REVERSE list order
574
- * (last composed, first closed), mirroring init.
586
+ * Teardown — destroy engines, stop timers, flush the module's outbox.
587
+ * Modules close in REVERSE composition order (last composed, first closed),
588
+ * mirroring init, BEFORE the app-level `onClose` and before infra plugins'
589
+ * own close hooks (so teardown still sees a live DB/Redis).
590
+ *
591
+ * Boot is transactional: if any phase fails mid-boot — including THIS
592
+ * module's own `plugins`/`bootstrap` — arc immediately closes every module
593
+ * that entered an init phase, this closer included, in reverse order, then
594
+ * rethrows the original boot error. A closer runs AT MOST ONCE across the
595
+ * rollback and shutdown paths, and one throwing closer never blocks the
596
+ * rest (best-effort; failures are logged).
597
+ *
598
+ * Because rollback fires after a PARTIAL init — your `bootstrap` opened a
599
+ * client and threw on the next line, or your `plugins` ran but `bootstrap`
600
+ * never did — write closers defensively. Guard everything init may not have
601
+ * reached: `client?.close()`, not `client.close()`.
602
+ *
603
+ * ```ts
604
+ * let client: Client | undefined;
605
+ * defineModule({
606
+ * name: "billing",
607
+ * bootstrap: async () => {
608
+ * client = await openClient(); // allocated…
609
+ * await migrate(client); // …and this may throw
610
+ * },
611
+ * onClose: async () => { await client?.close(); }, // still runs
612
+ * });
613
+ * ```
575
614
  */
576
615
  onClose?: (fastify: FastifyInstance) => void | Promise<void>;
577
616
  }
@@ -1,3 +1,4 @@
1
+
1
2
  //#region src/utils/headers.d.ts
2
3
  /**
3
4
  * Canonical header typing + accessor.
@@ -1,4 +1,5 @@
1
- import { V as ResourceDefinition } from "./index-Q5gzEIWY.mjs";
1
+
2
+ import { V as ResourceDefinition } from "./index-CpzxqePp.mjs";
2
3
  import { z } from "zod";
3
4
  //#region src/integrations/mcp/types.d.ts
4
5
  /** Behavioral hints for MCP clients (tool annotations per MCP spec) */
@@ -306,11 +307,6 @@ interface McpPluginOptions {
306
307
  * ```
307
308
  */
308
309
  expose?: string[];
309
- /**
310
- * @deprecated Use `expose` — same default-deny semantics with a clearer
311
- * verb. Kept for backwards compatibility with pre-2.15.5 hosts.
312
- */
313
- include?: string[];
314
310
  /** Tool name prefix: 'crm' → 'crm_list_products' */
315
311
  toolNamePrefix?: string;
316
312
  /** Per-resource overrides (operations, descriptions, hideFields, names, toolNamePrefix) */
@@ -1,6 +1,7 @@
1
- import { l as UnauthorizedError, o as OrgRequiredError } from "./errors-DkO8Nk6-.mjs";
2
- import { n as requireSingleHeaderValue } from "./headers-BvlQSDMB.mjs";
3
- import { t as getUserId$1 } from "./userHelpers-Cg_y3fBz.mjs";
1
+ /*! @classytic/arc v2.31.0 | MIT | https://classytic.com */
2
+ import { l as UnauthorizedError, o as OrgRequiredError } from "./errors-Cj42AntO.mjs";
3
+ import { n as requireSingleHeaderValue } from "./headers-3zvE0rM3.mjs";
4
+ import { t as getUserId$1 } from "./userHelpers-ChYJ6CdK.mjs";
4
5
  //#region src/scope/types.ts
5
6
  /**
6
7
  * Request Scope — The One Standard
@@ -1,5 +1,6 @@
1
- import { i as RequestScope } from "../types-DjC-KUCz.mjs";
2
- import { i as PermissionContext, r as PermissionCheck } from "../types-ChvvVNP8.mjs";
1
+
2
+ import { i as RequestScope } from "../types-ChuShpCP.mjs";
3
+ import { i as PermissionContext, r as PermissionCheck } from "../types-B7XeLEgK.mjs";
3
4
  import { UsageBucket, UsageStore, usagePeriod } from "@classytic/repo-core/usage";
4
5
  import { FastifyPluginAsync, FastifyRequest } from "fastify";
5
6
  //#region src/usage/requireQuota.d.ts
Binary file
@@ -1,3 +1,4 @@
1
+ /*! @classytic/arc v2.31.0 | MIT | https://classytic.com */
1
2
  //#region src/utils/userHelpers.ts
2
3
  /**
3
4
  * Extract a user ID from a user object. Accepts `id` or `_id` — returns