@classytic/arc 2.30.0 → 2.32.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 (242) 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-Dk34dxhI.mjs} +21 -19
  5. package/dist/{EventTransport-QmY7A258.d.mts → EventTransport-2uVV34WU.d.mts} +1 -0
  6. package/dist/{EventTransport-C6YA8xY9.mjs → EventTransport-BV10D0oG.mjs} +1 -0
  7. package/dist/{HookSystem-WkIougo6.mjs → HookSystem-vimw5D7Z.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-BVr7PgA0.mjs} +18 -15
  10. package/dist/{actionPermissions-CAphcmUG.mjs → actionPermissions-Dti7ebyv.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 +11 -9
  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-__T3EJDA.mjs} +19 -10
  20. package/dist/{buildHandler-BYS3Vkfz.mjs → buildHandler-Dn7rzUJo.mjs} +6 -4
  21. package/dist/cache/index.d.mts +4 -3
  22. package/dist/cache/index.mjs +4 -3
  23. package/dist/{caching-_NJeDd1_.mjs → caching-DFJqHyFs.mjs} +2 -1
  24. package/dist/cleanup/index.d.mts +39 -16
  25. package/dist/cleanup/index.mjs +50 -38
  26. package/dist/cli/commands/describe.d.mts +2 -1
  27. package/dist/cli/commands/describe.mjs +4 -5
  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-J99LGTJI.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-BwwmNSp7.mjs} +3 -2
  46. package/dist/{createActionRouter-BxuywVRB.mjs → createActionRouter-DkJZWo0m.mjs} +14 -10
  47. package/dist/{createAggregationRouter-BiY-lGdY.mjs → createAggregationRouter-B1exOVD8.mjs} +10 -7
  48. package/dist/{createApp-CXrKFciH.mjs → createApp-Bv0m-Q6_.mjs} +625 -229
  49. package/dist/{defineAggregation-ufWjUwml.mjs → defineAggregation-gX4No1gt.mjs} +1 -0
  50. package/dist/{defineResource-CxWqDCAy.mjs → defineResource-XFPPUu2c.mjs} +84 -50
  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-DNXXxgjl.mjs} +4 -3
  57. package/dist/encryption/index.d.mts +2 -1
  58. package/dist/encryption/index.mjs +5 -3
  59. package/dist/{errorHandler-Bq2OeEQL.mjs → errorHandler-B2AZUJUj.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-Bh1KK5ps.mjs} +1 -0
  63. package/dist/{eventPlugin-D2NNR4py.mjs → eventPlugin-Dk_TyXeE.mjs} +8 -6
  64. package/dist/{eventPlugin-Bg3cP2_u.d.mts → eventPlugin-GF5nnBIq.d.mts} +2 -1
  65. package/dist/events/index.d.mts +112 -44
  66. package/dist/events/index.mjs +177 -141
  67. package/dist/events/transports/redis-stream-entry.d.mts +2 -1
  68. package/dist/events/transports/redis-stream-entry.mjs +14 -9
  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 +131 -7
  74. package/dist/fastify-augmentation-BppG5bMT.d.mts +179 -0
  75. package/dist/{fields-CQ_7KQWw.mjs → fields-DEi65pGj.mjs} +6 -10
  76. package/dist/{fields-D3lhgzhD.d.mts → fields-I_EeRWND.d.mts} +1 -0
  77. package/dist/{generate-D0wZupBg.mjs → generate-CRSG0jTv.mjs} +2 -1
  78. package/dist/{generate-BnsL9GBm.d.mts → generate-DCGuEbqI.d.mts} +1 -0
  79. package/dist/{headers-BvlQSDMB.mjs → headers-BXjQYbeC.mjs} +2 -1
  80. package/dist/{health-CB_m4S32.mjs → health-5XnTa2uo.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 +8 -5
  85. package/dist/idempotency/redis.d.mts +2 -1
  86. package/dist/idempotency/redis.mjs +1 -0
  87. package/dist/{index-CG4yqMDB.d.mts → index-C-481Zf2.d.mts} +74 -18
  88. package/dist/{index-Q5gzEIWY.d.mts → index-CpzxqePp.d.mts} +107 -32
  89. package/dist/{index-Cx7bro4R.d.mts → index-DXlHwvuE.d.mts} +5 -4
  90. package/dist/index-ZWhLTp_s.d.mts +290 -0
  91. package/dist/{index-Ch16aAah.d.mts → index-zq6VrjaB.d.mts} +122 -15
  92. package/dist/index.d.mts +9 -8
  93. package/dist/index.mjs +11 -10
  94. package/dist/{init-tlRSt667.mjs → init-BbfP8aky.mjs} +25 -27
  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 +109 -33
  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 +38 -34
  116. package/dist/{interface-BGZCzOGL.d.mts → interface-BOk3YnTz.d.mts} +1 -0
  117. package/dist/{interface-n5hpKg1d.mjs → interface-BP4IJCBO.mjs} +1 -0
  118. package/dist/{interface-Uk8ONJv7.d.mts → interface-DEWNOMza.d.mts} +1 -0
  119. package/dist/{jsonBody-BlzAoGCe.mjs → jsonBody-DDSR2HFl.mjs} +2 -1
  120. package/dist/{keys-Cd316u_y.mjs → keys-B-HBTGXl.mjs} +1 -0
  121. package/dist/{loadResources-DpU8pfjV.mjs → loadResources-DisEAmkm.mjs} +4 -2
  122. package/dist/{loadResourcesFromEntry-kvGT6EcO.mjs → loadResourcesFromEntry-D1MJp13f.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-CpfVpKHE.mjs} +38 -38
  126. package/dist/{memory-CmnzXE5Z.mjs → memory-DsuWnj4T.mjs} +5 -4
  127. package/dist/{metrics-fdxI5mn_.mjs → metrics-DdQL8syd.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 +3 -2
  132. package/dist/module-Br81am6m.mjs +78 -0
  133. package/dist/{multipartBody-D0eBvkeq.mjs → multipartBody-BnkBLN7v.mjs} +3 -2
  134. package/dist/{openapi-B8Vkbxbn.mjs → openapi-DIRzvxC0.mjs} +6 -5
  135. package/dist/outbox-admin/index.d.mts +85 -0
  136. package/dist/outbox-admin/index.mjs +115 -0
  137. package/dist/permissions/index.d.mts +5 -4
  138. package/dist/permissions/index.mjs +6 -5
  139. package/dist/{permissions-Bfu_8y1U.mjs → permissions-Dl_m0NCe.mjs} +308 -42
  140. package/dist/{pipe-BOLmGIxa.mjs → pipe-ELHCP3zj.mjs} +3 -4
  141. package/dist/pipeline/index.d.mts +2 -1
  142. package/dist/pipeline/index.mjs +2 -1
  143. package/dist/{pipeline-BJnpkY9i.mjs → pipeline-CPA-jx0M.mjs} +17 -14
  144. package/dist/plugins/index.d.mts +8 -7
  145. package/dist/plugins/index.mjs +16 -15
  146. package/dist/plugins/response-cache.d.mts +1 -0
  147. package/dist/plugins/response-cache.mjs +2 -1
  148. package/dist/plugins/tracing-entry.d.mts +2 -1
  149. package/dist/plugins/tracing-entry.mjs +2 -1
  150. package/dist/{pluralize-BhrgwZb9.mjs → pluralize-C2ZkKbsn.mjs} +1 -0
  151. package/dist/{preloadResources-DSY5HGpS.mjs → preloadResources-CDs87fTw.mjs} +1 -0
  152. package/dist/{preloadResources-DCfa2egz.d.mts → preloadResources-T1hmlTEZ.d.mts} +117 -12
  153. package/dist/presets/filesUpload.d.mts +6 -5
  154. package/dist/presets/filesUpload.mjs +10 -12
  155. package/dist/presets/index.d.mts +3 -2
  156. package/dist/presets/index.mjs +2 -1
  157. package/dist/presets/multiTenant.d.mts +2 -1
  158. package/dist/presets/multiTenant.mjs +3 -2
  159. package/dist/presets/search.d.mts +3 -2
  160. package/dist/presets/search.mjs +2 -1
  161. package/dist/{presets-CJOEXLuk.mjs → presets-Dntd_pUO.mjs} +6 -4
  162. package/dist/{projection-UBDgfMwu.mjs → projection-CjHhM5W1.mjs} +2 -1
  163. package/dist/{pushref-registry-BbcnpNTm.d.mts → pushref-registry-Dov9l4qE.d.mts} +1 -0
  164. package/dist/{queryCachePlugin-br4otiqF.d.mts → queryCachePlugin-BzqxconG.d.mts} +3 -2
  165. package/dist/{queryCachePlugin-DSGuSVLW.mjs → queryCachePlugin-Dhps-Ldt.mjs} +6 -5
  166. package/dist/{rateLimit-DrED-C4v.mjs → rateLimit-C7mmAGs3.mjs} +7 -3
  167. package/dist/{rateLimitKey-BoWRH3e7.mjs → rateLimitKey-CccEFBfk.mjs} +1 -0
  168. package/dist/{realtime-B9nmIY5V.mjs → realtime-DVvVZZFz.mjs} +20 -9
  169. package/dist/{redis-UDtLaNj6.d.mts → redis-CduhKsJi.d.mts} +2 -1
  170. package/dist/{redis-stream-uIaauNpZ.d.mts → redis-stream-qN6A-RFv.d.mts} +2 -1
  171. package/dist/{registerRoute-JkxIEBJq.mjs → registerRoute-B_QrjYZS.mjs} +10 -7
  172. package/dist/registry/index.d.mts +3 -2
  173. package/dist/registry/index.mjs +3 -2
  174. package/dist/{registry-BYXUUg21.mjs → registry-DqUmzA9W.mjs} +2 -1
  175. package/dist/{renewingLease-3cuQH7h8.mjs → renewingLease-BpP9wwj7.mjs} +1 -0
  176. package/dist/{replyHelpers-67tRawQZ.mjs → replyHelpers-DS-P0b32.mjs} +2 -1
  177. package/dist/{repositoryFilter-Dz8k3S4P.mjs → repositoryFilter-DotlxG3L.mjs} +1 -0
  178. package/dist/{requestContext-MrlJwECf.mjs → requestContext-B6vQF-sy.mjs} +1 -0
  179. package/dist/{rolldown-runtime-8H4AJuhK.mjs → rolldown-runtime-BhflRrBs.mjs} +1 -0
  180. package/dist/{schedules-CjOd2MG9.mjs → schedules-C2Q3uv2d.mjs} +3 -2
  181. package/dist/{schemaConverter-DJvodiHj.mjs → schemaConverter-Crjvczi_.mjs} +70 -17
  182. package/dist/{schemaIR-Ca7ySNPx.mjs → schemaIR-CnyKK_1j.mjs} +2 -1
  183. package/dist/schemas/index.d.mts +1 -0
  184. package/dist/schemas/index.mjs +1 -0
  185. package/dist/scim/index.d.mts +1 -0
  186. package/dist/scim/index.mjs +2 -1
  187. package/dist/scope/index.d.mts +3 -2
  188. package/dist/scope/index.mjs +5 -4
  189. package/dist/{scope-DfaPntpo.mjs → scope-afz_Ieek.mjs} +4 -3
  190. package/dist/{sessionManager-BHOM3N_0.d.mts → sessionManager-qimlKV0P.d.mts} +1 -0
  191. package/dist/{simpleEqualityMatcher-Cg05HPsn.mjs → simpleEqualityMatcher-C4KiMWUa.mjs} +1 -0
  192. package/dist/{sse-BQ1w0dfl.mjs → sse-B9P4Y-UI.mjs} +7 -5
  193. package/dist/{sseStream-BQ7Qaynp.mjs → sseStream-B3zb_d1h.mjs} +2 -1
  194. package/dist/{storage-DLUZ04kK.d.mts → storage-Cx9Sz3Ou.d.mts} +1 -0
  195. package/dist/{store-helpers-DY6xq4sM.mjs → store-helpers-DmEMHlPQ.mjs} +1 -0
  196. package/dist/{streaming-BQcW6_Kq.mjs → streaming-DPZdQmDz.mjs} +1 -0
  197. package/dist/{subscribe-helpers-DfzsivM6.mjs → subscribe-helpers-sp5xTDVA.mjs} +2 -1
  198. package/dist/sync/changelog.d.mts +1 -0
  199. package/dist/sync/changelog.mjs +1 -0
  200. package/dist/testing/cleanupStoreContract.d.mts +2 -1
  201. package/dist/testing/cleanupStoreContract.mjs +10 -5
  202. package/dist/testing/index.d.mts +166 -12
  203. package/dist/testing/index.mjs +315 -43
  204. package/dist/testing/outboxStoreContract.d.mts +12 -2
  205. package/dist/testing/outboxStoreContract.mjs +110 -20
  206. package/dist/testing/storageContract.d.mts +2 -1
  207. package/dist/testing/storageContract.mjs +11 -5
  208. package/dist/{tracing-CsGKQJGg.d.mts → tracing-Dro5eH6j.d.mts} +1 -0
  209. package/dist/{transactionContext-PNdoyLLm.mjs → transactionContext-BvLKvOIL.mjs} +1 -0
  210. package/dist/{typeGuards-C9KXUly6.mjs → typeGuards-B_5-hKjJ.mjs} +1 -0
  211. package/dist/types/index.d.mts +6 -5
  212. package/dist/types/index.mjs +1 -0
  213. package/dist/types/storage.d.mts +2 -1
  214. package/dist/types/storage.mjs +1 -0
  215. package/dist/{types-ChvvVNP8.d.mts → types-B7XeLEgK.d.mts} +34 -2
  216. package/dist/{types-Cc91nXuA.d.mts → types-ByHn9rEO.d.mts} +1 -0
  217. package/dist/{types-C_ZZRcD6.d.mts → types-C4QE8qnm.d.mts} +1 -0
  218. package/dist/{types-D_n8L6VY.d.mts → types-CT5vu6M0.d.mts} +132 -17
  219. package/dist/{types-DjC-KUCz.d.mts → types-ChuShpCP.d.mts} +1 -0
  220. package/dist/{types-Bmmn-fO0.d.mts → types-CwcUQ610.d.mts} +2 -6
  221. package/dist/{types-CSyCxl2V.mjs → types-DXYX7dIR.mjs} +4 -3
  222. package/dist/usage/index.d.mts +3 -2
  223. package/dist/usage/index.mjs +0 -0
  224. package/dist/{userHelpers-Cg_y3fBz.mjs → userHelpers-BNsOXzOd.mjs} +1 -0
  225. package/dist/utils/index.d.mts +16 -7
  226. package/dist/utils/index.mjs +10 -9
  227. package/dist/{utils-taUVD4p3.mjs → utils-BtOha_Sp.mjs} +3 -2
  228. package/dist/{validate-Bk44F2yT.mjs → validate-PMCPR9El.mjs} +2 -1
  229. package/dist/{versioning-gKWyryT3.mjs → versioning-CKv2jz_U.mjs} +4 -3
  230. package/dist/{versioning-DXgY5BUm.d.mts → versioning-D_rhHfZm.d.mts} +5 -2
  231. package/dist/{websocket-_Pd0CgR0.d.mts → websocket-BOXVggMf.d.mts} +2 -1
  232. package/package.json +557 -539
  233. package/skills/arc/SKILL.md +22 -8
  234. package/skills/arc/references/api-reference.md +4 -0
  235. package/skills/arc/references/auth.md +3 -0
  236. package/skills/arc/references/integrations.md +33 -0
  237. package/skills/arc/references/mcp.md +6 -7
  238. package/skills/arc/references/production.md +24 -25
  239. package/skills/arc-code-review/references/anti-patterns.md +3 -3
  240. package/dist/fastify-augmentation-0WhPnUH7.d.mts +0 -76
  241. package/dist/index-CCzVR-N3.d.mts +0 -107
  242. package/dist/module-_-M7C5ES.mjs +0 -29
@@ -1,9 +1,10 @@
1
- import { t as __exportAll } from "./rolldown-runtime-8H4AJuhK.mjs";
1
+ /*! @classytic/arc v2.32.0 | MIT | https://classytic.com */
2
+ import { t as __exportAll } from "./rolldown-runtime-BhflRrBs.mjs";
2
3
  import { arcLog } from "./logger/index.mjs";
3
- import { n as getUserRoles } from "./userHelpers-Cg_y3fBz.mjs";
4
- import { C as isService, S as isOrgInScope, a as getDPoPJkt, b as isElevated, d as getScopeContext, f as getScopeContextMap, g as getUserId, m as getTeamId, n as PUBLIC_SCOPE, o as getMandate, p as getServiceScopes, u as getRequestScope, v as hasOrgAccess, x as isMember } from "./types-CSyCxl2V.mjs";
5
- import { a as normalizeToDecision, o as conjoinPolicyFilters } from "./applyPermissionResult-Cf6zUCvb.mjs";
6
- import { t as MemoryCacheStore } from "./memory-CmnzXE5Z.mjs";
4
+ import { a as conjoinPolicyFilters, i as normalizeToDecision } from "./authorizationDecision-__T3EJDA.mjs";
5
+ import { n as getUserRoles } from "./userHelpers-BNsOXzOd.mjs";
6
+ import { C as isService, S as isOrgInScope, a as getDPoPJkt, b as isElevated, d as getScopeContext, f as getScopeContextMap, g as getUserId, m as getTeamId, n as PUBLIC_SCOPE, o as getMandate, p as getServiceScopes, u as getRequestScope, v as hasOrgAccess, x as isMember } from "./types-DXYX7dIR.mjs";
7
+ import { t as MemoryCacheStore } from "./memory-DsuWnj4T.mjs";
7
8
  import { randomUUID } from "node:crypto";
8
9
  //#region src/permissions/context.ts
9
10
  /**
@@ -106,6 +107,7 @@ function requireAuth() {
106
107
  if (!ctx.user) return deny("Authentication required");
107
108
  return true;
108
109
  };
110
+ check._requiresAuth = true;
109
111
  return check;
110
112
  }
111
113
  function requireRoles(rolesOrFirst, optionsOrSecond, ...rest) {
@@ -139,6 +141,36 @@ function requireRoles(rolesOrFirst, optionsOrSecond, ...rest) {
139
141
  return check;
140
142
  }
141
143
  /**
144
+ * Require a PLATFORM role — global roles only. An organization role can never
145
+ * satisfy this gate.
146
+ *
147
+ * `requireRoles(["ops"])` also accepts an ORG role named `ops` (that is its
148
+ * documented default), and `requireOrgRole("manager")` grants ANY organization's
149
+ * manager. Both return a bare allow with no policy, so a surface that is global
150
+ * by construction — a queue dashboard, a cluster-wide operations endpoint —
151
+ * cannot distinguish them from a real operator gate by looking at the decision.
152
+ * That makes "platform-only" a property the check has to DECLARE, which is what
153
+ * `_platformOnly` records and what such a surface can then require.
154
+ *
155
+ * Elevated scope passes: platform admin is the identity this gate is for.
156
+ *
157
+ * @example
158
+ * permissions: { list: requirePlatformRole("platform-ops") }
159
+ */
160
+ function requirePlatformRole(...args) {
161
+ const roles = normalizeVariadicOrArray(args);
162
+ const check = (ctx) => {
163
+ if (isElevated(scopeOf(ctx))) return true;
164
+ if (!ctx.user) return deny("Authentication required");
165
+ const userRoles = getUserRoles(ctx.user);
166
+ if (roles.some((r) => userRoles.includes(r))) return true;
167
+ return deny(`Required platform roles: ${roles.join(", ")}`);
168
+ };
169
+ check._roles = roles;
170
+ check._platformOnly = true;
171
+ return check;
172
+ }
173
+ /**
142
174
  * Short-form alias of `requireRoles()`. Identical behavior — checks both
143
175
  * platform roles AND org roles. Prefer `requireRoles` for new code; this
144
176
  * exists for call sites that want a terser name.
@@ -234,6 +266,15 @@ function allOf(...checks) {
234
266
  if (rb?._roles?.length) check._roles = [...rb._roles];
235
267
  if (rb?._orgRoles?.length) check._orgRoles = [...rb._orgRoles];
236
268
  }
269
+ const scopeContext = {};
270
+ let scopeConflict = false;
271
+ for (const c of checks) for (const [dim, val] of Object.entries(c._scopeContext ?? {})) if (dim in scopeContext && scopeContext[dim] !== val) scopeConflict = true;
272
+ else scopeContext[dim] = val;
273
+ if (Object.keys(scopeContext).length > 0) check._scopeContext = scopeContext;
274
+ const hasMetaSurface = (c) => Boolean(c._isPublic || c._roles?.length || c._orgRoles?.length || c._scopeContext && Object.keys(c._scopeContext).length > 0);
275
+ const opaqueBranch = checks.some((c) => !hasMetaSurface(c) && !c._requiresAuth);
276
+ const childConditional = checks.some((c) => c._conditional);
277
+ if (opaqueBranch || scopeConflict || childConditional) check._conditional = true;
237
278
  return check;
238
279
  }
239
280
  /**
@@ -269,6 +310,7 @@ function anyOf(...checks) {
269
310
  }
270
311
  if (roles.size > 0) check._roles = [...roles];
271
312
  if (orgRoles.size > 0) check._orgRoles = [...orgRoles];
313
+ if (checks.some((c) => c._conditional)) check._conditional = true;
272
314
  }
273
315
  return check;
274
316
  }
@@ -536,7 +578,8 @@ function requireAgentScope(opts) {
536
578
  */
537
579
  function requireOrgMembership() {
538
580
  const check = (ctx) => {
539
- if (hasOrgAccess(scopeOf(ctx))) return true;
581
+ const scope = scopeOf(ctx);
582
+ if (hasOrgAccess(scope)) return true;
540
583
  if (!ctx.user) return deny("Authentication required");
541
584
  return deny("Organization membership required");
542
585
  };
@@ -818,20 +861,66 @@ function createDynamicPermissionMatrix(config) {
818
861
  }) : void 0;
819
862
  const cacheStore = config.cacheStore ?? internalStore;
820
863
  const trackedKeys = /* @__PURE__ */ new Set();
864
+ const orgKeyIndex = /* @__PURE__ */ new Map();
865
+ const maxTrackedKeys = config.cache?.maxEntries ?? 1e4;
866
+ /**
867
+ * Record a key and ENFORCE the cap in the same step.
868
+ *
869
+ * The cap has to live here rather than at the write site: a node that mostly
870
+ * READS a shared cache registers on every hit but may rarely set, so a
871
+ * cap applied only after a `set` never runs and the bookkeeping grows with
872
+ * every distinct org/user/role combination the node has ever seen — unbounded,
873
+ * and outliving the cache entries themselves, which the external store expires
874
+ * on its own schedule. Eviction is insertion-ordered (`Set` iteration order),
875
+ * so the oldest bookkeeping goes first; dropping a key only costs a missed
876
+ * local invalidation for an entry that will still expire by TTL.
877
+ */
878
+ const registerKey = (orgId, key) => {
879
+ trackedKeys.add(key);
880
+ const bucket = orgId ?? "no-org";
881
+ let keys = orgKeyIndex.get(bucket);
882
+ if (!keys) {
883
+ keys = /* @__PURE__ */ new Set();
884
+ orgKeyIndex.set(bucket, keys);
885
+ }
886
+ keys.add(key);
887
+ if (trackedKeys.size > maxTrackedKeys) {
888
+ const overflow = trackedKeys.size - maxTrackedKeys;
889
+ const iter = trackedKeys.values();
890
+ for (let i = 0; i < overflow; i++) {
891
+ const oldest = iter.next().value;
892
+ if (oldest) forgetKey(oldest);
893
+ }
894
+ }
895
+ };
896
+ const forgetKey = (key) => {
897
+ trackedKeys.delete(key);
898
+ for (const [bucket, keys] of orgKeyIndex) if (keys.delete(key) && keys.size === 0) orgKeyIndex.delete(bucket);
899
+ };
821
900
  const nodeId = randomUUID().slice(0, 8);
822
901
  const DEFAULT_EVENT_TYPE = "arc.permissions.invalidated";
823
902
  let eventBridge = null;
824
903
  async function localInvalidateByOrg(orgId) {
825
904
  if (!cacheStore) return;
826
905
  const prefix = `${orgId}::`;
827
- const toDelete = [];
828
- for (const key of trackedKeys) if (key.startsWith(prefix)) toDelete.push(key);
906
+ if (cacheStore.clear) try {
907
+ await cacheStore.clear(`${prefix}*`);
908
+ const keys = orgKeyIndex.get(orgId);
909
+ if (keys) for (const k of keys) trackedKeys.delete(k);
910
+ orgKeyIndex.delete(orgId);
911
+ return;
912
+ } catch (error) {
913
+ logger.warn(`[DynamicPermissionMatrix] invalidateByOrg clear('${prefix}*') failed — falling back to per-key delete: ${error instanceof Error ? error.message : String(error)}`);
914
+ }
915
+ const toDelete = new Set(orgKeyIndex.get(orgId) ?? []);
916
+ for (const key of trackedKeys) if (key.startsWith(prefix)) toDelete.add(key);
829
917
  for (const key of toDelete) try {
830
918
  await cacheStore.delete(key);
831
- trackedKeys.delete(key);
919
+ forgetKey(key);
832
920
  } catch (error) {
833
921
  logger.warn(`[DynamicPermissionMatrix] invalidateByOrg delete failed for '${key}': ${error instanceof Error ? error.message : String(error)}`);
834
922
  }
923
+ orgKeyIndex.delete(orgId);
835
924
  }
836
925
  function isActionAllowed(actions, action) {
837
926
  if (!actions || actions.length === 0) return false;
@@ -851,27 +940,22 @@ function createDynamicPermissionMatrix(config) {
851
940
  }
852
941
  async function resolveMatrix(ctx, orgId, orgRoles) {
853
942
  if (!cacheStore) return config.resolveRolePermissions(ctx);
854
- const cacheKey = config.cache?.key?.(ctx) ?? buildDefaultCacheKey(ctx, orgId, orgRoles);
943
+ const customKey = config.cache?.key?.(ctx);
944
+ const cacheKey = customKey ? `${orgId ?? "no-org"}::${(orgRoles ?? []).slice().sort().join(",")}::${customKey}` : buildDefaultCacheKey(ctx, orgId, orgRoles);
855
945
  if (!cacheKey) return config.resolveRolePermissions(ctx);
856
946
  try {
857
947
  const hit = await cacheStore.get(cacheKey);
858
- if (hit) return hit;
948
+ if (hit) {
949
+ registerKey(orgId, cacheKey);
950
+ return hit;
951
+ }
859
952
  } catch (error) {
860
953
  logger.warn(`[DynamicPermissionMatrix] Cache get failed for '${cacheKey}': ${error instanceof Error ? error.message : String(error)}`);
861
954
  }
862
955
  const value = await config.resolveRolePermissions(ctx);
863
956
  try {
864
957
  await cacheStore.set(cacheKey, value, cacheTtlSeconds);
865
- trackedKeys.add(cacheKey);
866
- const maxTracked = config.cache?.maxEntries ?? 1e4;
867
- if (trackedKeys.size > maxTracked) {
868
- const overflow = trackedKeys.size - maxTracked;
869
- const iter = trackedKeys.values();
870
- for (let i = 0; i < overflow; i++) {
871
- const oldest = iter.next().value;
872
- if (oldest) trackedKeys.delete(oldest);
873
- }
874
- }
958
+ registerKey(orgId, cacheKey);
875
959
  } catch (error) {
876
960
  logger.warn(`[DynamicPermissionMatrix] Cache set failed for '${cacheKey}': ${error instanceof Error ? error.message : String(error)}`);
877
961
  }
@@ -889,7 +973,8 @@ function createDynamicPermissionMatrix(config) {
889
973
  try {
890
974
  matrix = await resolveMatrix(ctx, scope.organizationId, orgRoles);
891
975
  } catch (error) {
892
- return deny(`Permission matrix resolution failed: ${error instanceof Error ? error.message : String(error)}`);
976
+ logger.warn(`[DynamicPermissionMatrix] matrix resolution failed for org '${scope.organizationId ?? "?"}': ${error instanceof Error ? error.message : String(error)}`);
977
+ return deny("Permission policy is temporarily unavailable");
893
978
  }
894
979
  for (const [resource, actions] of Object.entries(required)) for (const action of actions) if (!orgRoles.some((role) => roleAllows(matrix, role, resource, action))) return deny(`Missing permission: ${resource}:${action}`);
895
980
  return true;
@@ -925,6 +1010,7 @@ function createDynamicPermissionMatrix(config) {
925
1010
  if (cacheStore.clear) try {
926
1011
  await cacheStore.clear();
927
1012
  trackedKeys.clear();
1013
+ orgKeyIndex.clear();
928
1014
  return;
929
1015
  } catch (error) {
930
1016
  logger.warn(`[DynamicPermissionMatrix] cacheStore.clear failed: ${error instanceof Error ? error.message : String(error)}`);
@@ -935,6 +1021,7 @@ function createDynamicPermissionMatrix(config) {
935
1021
  logger.warn(`[DynamicPermissionMatrix] Cache delete failed for '${key}': ${error instanceof Error ? error.message : String(error)}`);
936
1022
  }
937
1023
  trackedKeys.clear();
1024
+ orgKeyIndex.clear();
938
1025
  },
939
1026
  async connectEvents(events, options) {
940
1027
  if (eventBridge) await this.disconnectEvents();
@@ -975,15 +1062,30 @@ function createDynamicPermissionMatrix(config) {
975
1062
  //#region src/permissions/explain.ts
976
1063
  /**
977
1064
  * Extract a check's static requirement from its `PermissionCheckMeta`. Public
978
- * wins; then the union of platform + org roles a role gate declares; otherwise
979
- * "authenticated" (auth-required / custom / request-time checks).
1065
+ * wins; then a scope-context gate (optionally with roles); then the union of
1066
+ * platform + org roles a role gate declares; otherwise "authenticated"
1067
+ * (auth-required / custom / request-time checks). An `allOf` that couldn't be
1068
+ * fully captured statically carries `conditional: true` (see `_conditional`).
980
1069
  */
981
1070
  function describePermission(check) {
982
1071
  if (check._isPublic) return { kind: "public" };
983
1072
  const roles = [...check._roles ?? [], ...check._orgRoles ?? []];
1073
+ const scope = check._scopeContext;
1074
+ const conditional = check._conditional ? true : void 0;
1075
+ if (scope && Object.keys(scope).length > 0) return roles.length > 0 ? {
1076
+ kind: "scoped",
1077
+ scope,
1078
+ roles,
1079
+ conditional
1080
+ } : {
1081
+ kind: "scoped",
1082
+ scope,
1083
+ conditional
1084
+ };
984
1085
  if (roles.length > 0) return {
985
1086
  kind: "roles",
986
- roles
1087
+ roles,
1088
+ conditional
987
1089
  };
988
1090
  return { kind: "authenticated" };
989
1091
  }
@@ -1008,23 +1110,63 @@ function explainAccess(check, principal) {
1008
1110
  reason: "requires authentication; decided per request",
1009
1111
  requirement
1010
1112
  };
1113
+ const held = /* @__PURE__ */ new Set([...principal?.roles ?? [], ...principal?.orgRoles ?? []]);
1114
+ if (requirement.kind === "scoped") {
1115
+ const scopeReason = describeScope(requirement.scope);
1116
+ const rolesReason = requirement.roles?.length ? `role(s) ${requirement.roles.join(", ")} + ` : "";
1117
+ if (requirement.roles?.length && principal && !requirement.roles.some((r) => held.has(r))) return {
1118
+ decision: "deny",
1119
+ reason: `requires ${rolesReason}${scopeReason}`,
1120
+ requirement
1121
+ };
1122
+ if (!principal?.scope) return {
1123
+ decision: "conditional",
1124
+ reason: `requires ${rolesReason}${scopeReason}`,
1125
+ requirement
1126
+ };
1127
+ if (!Object.entries(requirement.scope).every(([dim, val]) => val === void 0 ? principal.scope?.[dim] !== void 0 : principal.scope?.[dim] === val)) return {
1128
+ decision: "deny",
1129
+ reason: `requires ${rolesReason}${scopeReason}`,
1130
+ requirement
1131
+ };
1132
+ if (requirement.conditional) return {
1133
+ decision: "conditional",
1134
+ reason: `in scope ${scopeReason}; additional runtime conditions apply`,
1135
+ requirement
1136
+ };
1137
+ return {
1138
+ decision: "allow",
1139
+ reason: `granted in scope: ${scopeReason}`,
1140
+ requirement
1141
+ };
1142
+ }
1143
+ const rolesText = `requires one of: ${requirement.roles.join(", ")}`;
1011
1144
  if (!principal) return {
1012
1145
  decision: "conditional",
1013
- reason: `requires one of: ${requirement.roles.join(", ")}`,
1146
+ reason: rolesText,
1014
1147
  requirement
1015
1148
  };
1016
- const held = /* @__PURE__ */ new Set([...principal.roles ?? [], ...principal.orgRoles ?? []]);
1017
1149
  const matched = requirement.roles.filter((r) => held.has(r));
1018
- return matched.length > 0 ? {
1150
+ if (matched.length === 0) return {
1151
+ decision: "deny",
1152
+ reason: rolesText,
1153
+ requirement
1154
+ };
1155
+ if (requirement.conditional) return {
1156
+ decision: "conditional",
1157
+ reason: `has role(s) ${matched.join(", ")}; additional runtime conditions apply`,
1158
+ requirement
1159
+ };
1160
+ return {
1019
1161
  decision: "allow",
1020
1162
  reason: `granted via role(s): ${matched.join(", ")}`,
1021
1163
  requirement
1022
- } : {
1023
- decision: "deny",
1024
- reason: `requires one of: ${requirement.roles.join(", ")}`,
1025
- requirement
1026
1164
  };
1027
1165
  }
1166
+ /** Render a scope-context requirement as a readable clause: `branchRole=head_office`. */
1167
+ function describeScope(scope) {
1168
+ return Object.entries(scope).map(([dim, val]) => val === void 0 ? `${dim} (any)` : `${dim}=${val}`).join(", ");
1169
+ }
1028
1170
  /**
1029
1171
  * Introspect a whole permission map (`{ list, get, create, ... }` or any
1030
1172
  * action→check record) into `action → requirement`. The reusable core of a
@@ -1167,27 +1309,59 @@ function requireGrant(options) {
1167
1309
  */
1168
1310
  function createRoleHierarchy(map) {
1169
1311
  const cache = /* @__PURE__ */ new Map();
1170
- function resolveRole(role, visited) {
1171
- if (visited.has(role)) return [];
1172
- visited.add(role);
1312
+ /**
1313
+ * Resolve a role's transitive expansion.
1314
+ *
1315
+ * `stack` is the CURRENT recursion path (a DFS stack), NOT a shared visited
1316
+ * accumulator: a role is added on entry and REMOVED on exit, so sibling
1317
+ * branches never pollute each other. Without this, a diamond `A→B→D`, `A→C→D`
1318
+ * would let B's traversal leave `D` in a shared set, making C's `D` look like a
1319
+ * back-edge and caching `C` WITHOUT `D` — a nondeterministic under-grant.
1320
+ *
1321
+ * Returns `cyclic` = whether a back-edge into `stack` was cut anywhere in the
1322
+ * subtree. A cyclic subtree's result depends on the entry path, so it is NOT
1323
+ * cached (recomputed each call — correct, if unmemoized). Acyclic subtrees (the
1324
+ * norm) cache safely.
1325
+ */
1326
+ function resolveRole(role, stack) {
1327
+ if (stack.has(role)) return {
1328
+ roles: [],
1329
+ cyclic: true
1330
+ };
1173
1331
  const cached = cache.get(role);
1174
- if (cached) return cached;
1332
+ if (cached) return {
1333
+ roles: cached,
1334
+ cyclic: false
1335
+ };
1175
1336
  const children = map[role];
1176
1337
  if (!children || children.length === 0) {
1177
1338
  cache.set(role, [role]);
1178
- return [role];
1339
+ return {
1340
+ roles: [role],
1341
+ cyclic: false
1342
+ };
1179
1343
  }
1344
+ stack.add(role);
1180
1345
  const result = [role];
1181
- for (const child of children) result.push(...resolveRole(child, visited));
1346
+ let cyclic = false;
1347
+ for (const child of children) {
1348
+ const childResult = resolveRole(child, stack);
1349
+ result.push(...childResult.roles);
1350
+ if (childResult.cyclic) cyclic = true;
1351
+ }
1352
+ stack.delete(role);
1182
1353
  const deduped = [...new Set(result)];
1183
- cache.set(role, deduped);
1184
- return deduped;
1354
+ if (!cyclic) cache.set(role, deduped);
1355
+ return {
1356
+ roles: deduped,
1357
+ cyclic
1358
+ };
1185
1359
  }
1186
1360
  return {
1187
1361
  expand(roles) {
1188
1362
  if (roles.length === 0) return [];
1189
1363
  const all = /* @__PURE__ */ new Set();
1190
- for (const role of roles) for (const expanded of resolveRole(role, /* @__PURE__ */ new Set())) all.add(expanded);
1364
+ for (const role of roles) for (const expanded of resolveRole(role, /* @__PURE__ */ new Set()).roles) all.add(expanded);
1191
1365
  return [...all];
1192
1366
  },
1193
1367
  includes(userRoles, requiredRole) {
@@ -1372,4 +1546,96 @@ function readOnly(overrides) {
1372
1546
  }, overrides);
1373
1547
  }
1374
1548
  //#endregion
1375
- export { allow as A, when as B, requireScopeContext as C, requireDPoP as D, requireAgentScope as E, not as F, requireAuth as I, requireOwnership as L, anyOf as M, deny as N, requireMandate as O, denyAll as P, requireRoles as R, requireOrgRole as S, requireTeamMembership as T, scopeOf as V, explainAccess as _, permissionMatrix as a, requireOrgInScope as b, publicReadAdminWrite as c, GRANT_MODES as d, modeSatisfies as f, describePermissionMap as g, describePermission as h, ownerWithAdminBypass as i, allowPublic as j, allOf as k, readOnly as l, collectPublicSurface as m, authenticated as n, presets_exports as o, requireGrant as p, fullPublic as r, publicRead as s, adminOnly as t, createRoleHierarchy as u, createDynamicPermissionMatrix as v, requireServiceScope as w, requireOrgMembership as x, createOrgPermissions as y, roles as z };
1549
+ //#region src/permissions/matrix.ts
1550
+ /**
1551
+ * Permission MATRIX — what the caller may do, answered from the live registry.
1552
+ *
1553
+ * A permission-gated frontend has to decide which screens and buttons to render
1554
+ * before it issues a request. Without a server answer it keeps a hand-written
1555
+ * copy of the gates, and that copy drifts the moment a resource changes one —
1556
+ * silently, and in the dangerous direction: the UI offers an action the server
1557
+ * will refuse, or hides one the user is entitled to.
1558
+ *
1559
+ * This builds the answer from what arc ACTUALLY enforces. Every registered
1560
+ * resource contributes its CRUD gates, its state-transition `action:<verb>`
1561
+ * gates and its `agg:<name>` gates, introspected through `describePermission`.
1562
+ * A resource that exists therefore gates its own screen with no one adding it
1563
+ * anywhere — including resources unique to one deployment.
1564
+ *
1565
+ * ## Why it lives in arc
1566
+ *
1567
+ * Every input is arc's: `describePermission`, the resource registry, the shapes
1568
+ * of `actions` and `aggregations`. Nothing here knows what a domain is. Built
1569
+ * in a host, it is re-derived per deployment — and the known gap below is a
1570
+ * gap in arc's own introspection, which a host cannot close but arc can.
1571
+ *
1572
+ * ## Known gap
1573
+ *
1574
+ * `anyOf(a, b)` composes checks into a bare function with no aggregated meta,
1575
+ * so `anyOf(platformAdminOnly(), requireOrgRole(…))` introspects as
1576
+ * `authenticated` with no roles. Consumers must read `authenticated` as "the
1577
+ * server decides" rather than "any logged-in user", and fall back to an
1578
+ * explicit gate for those. Fixing it means teaching the combinators to
1579
+ * aggregate meta — an arc change, tracked here because this is where it shows.
1580
+ */
1581
+ /** Re-shape arc's typed requirement into the wire entry. */
1582
+ function introspectCheck(check) {
1583
+ const req = describePermission(check);
1584
+ if (req.kind === "public") return {
1585
+ type: "public",
1586
+ roles: []
1587
+ };
1588
+ if (req.kind === "roles") return {
1589
+ type: "roles",
1590
+ roles: [...req.roles]
1591
+ };
1592
+ if (req.kind === "scoped") return {
1593
+ type: "scoped",
1594
+ roles: req.roles ? [...req.roles] : [],
1595
+ scope: { ...req.scope }
1596
+ };
1597
+ return {
1598
+ type: "authenticated",
1599
+ roles: []
1600
+ };
1601
+ }
1602
+ /** Introspect a permission module, flattening nested groups to `a.b`. */
1603
+ function introspectModule(mod) {
1604
+ const result = {};
1605
+ for (const [action, check] of Object.entries(mod)) if (typeof check === "function") result[action] = introspectCheck(check);
1606
+ else if (typeof check === "object" && check !== null) for (const [nested, entry] of Object.entries(introspectModule(check))) result[`${action}.${nested}`] = entry;
1607
+ return result;
1608
+ }
1609
+ /** Every live resource's ACTUAL gates, keyed by arc resource name. */
1610
+ function introspectRegistry(registry) {
1611
+ const modules = {};
1612
+ for (const entry of registry.getAll()) {
1613
+ const map = {};
1614
+ for (const [action, check] of Object.entries(entry.permissions ?? {})) if (typeof check === "function") map[action] = introspectCheck(check);
1615
+ for (const a of entry.actions ?? []) if (typeof a.permissions === "function") map[`action:${a.name}`] = introspectCheck(a.permissions);
1616
+ for (const agg of entry.aggregations ?? []) if (typeof agg.permissions === "function") map[`agg:${agg.name}`] = introspectCheck(agg.permissions);
1617
+ if (Object.keys(map).length > 0) modules[entry.name] = map;
1618
+ }
1619
+ return modules;
1620
+ }
1621
+ function buildPermissionMatrix(options = {}) {
1622
+ const platform = [...options.platformRoles ?? []];
1623
+ const branch = [...options.orgRoles ?? []];
1624
+ const modules = {};
1625
+ for (const [name, mod] of Object.entries(options.curatedModules ?? {})) if (typeof mod === "object" && mod !== null) modules[name] = introspectModule(mod);
1626
+ for (const [name, mod] of Object.entries(options.fallbackModules ?? {})) {
1627
+ if (modules[name]) continue;
1628
+ if (typeof mod === "object" && mod !== null) modules[name] = introspectModule(mod);
1629
+ }
1630
+ if (options.registry) Object.assign(modules, introspectRegistry(options.registry));
1631
+ return {
1632
+ roles: [.../* @__PURE__ */ new Set([...platform, ...branch])],
1633
+ roleGroups: {
1634
+ platform,
1635
+ branch
1636
+ },
1637
+ modules
1638
+ };
1639
+ }
1640
+ //#endregion
1641
+ export { requireAgentScope as A, requireAuth as B, createOrgPermissions as C, requireScopeContext as D, requireOrgRole as E, allowPublic as F, when as G, requirePlatformRole as H, anyOf as I, scopeOf as K, deny as L, requireMandate as M, allOf as N, requireServiceScope as O, allow as P, denyAll as R, createDynamicPermissionMatrix as S, requireOrgMembership as T, requireRoles as U, requireOwnership as V, roles as W, requireGrant as _, adminOnly as a, describePermissionMap as b, ownerWithAdminBypass as c, publicRead as d, publicReadAdminWrite as f, modeSatisfies as g, GRANT_MODES as h, introspectRegistry as i, requireDPoP as j, requireTeamMembership as k, permissionMatrix as l, createRoleHierarchy as m, introspectCheck as n, authenticated as o, readOnly as p, introspectModule as r, fullPublic as s, buildPermissionMatrix as t, presets_exports as u, collectPublicSurface as v, requireOrgInScope as w, explainAccess as x, describePermission as y, not as z };
@@ -1,4 +1,5 @@
1
- import { r as ForbiddenError } from "./errors-DkO8Nk6-.mjs";
1
+ /*! @classytic/arc v2.32.0 | MIT | https://classytic.com */
2
+ import { r as ForbiddenError } from "./errors-Bh1KK5ps.mjs";
2
3
  //#region src/pipeline/pipe.ts
3
4
  /**
4
5
  * Compose pipeline steps into an ordered array.
@@ -39,9 +40,7 @@ async function executePipeline(steps, ctx, handler, operation) {
39
40
  case "transform":
40
41
  transforms.push(step);
41
42
  break;
42
- case "interceptor":
43
- interceptors.push(step);
44
- break;
43
+ case "interceptor": interceptors.push(step);
45
44
  }
46
45
  }
47
46
  for (const g of guards) if (!await g.handler(ctx)) throw new ForbiddenError(`Guard '${g.name}' denied access`);
@@ -1,4 +1,5 @@
1
- import { Ct as PipelineStep, Rt as IControllerResponse, St as PipelineContext, _t as Guard, bt as OperationFilter, vt as Interceptor, wt as Transform, xt as PipelineConfig, yt as NextFunction } from "../index-Q5gzEIWY.mjs";
1
+
2
+ import { Ct as PipelineStep, Rt as IControllerResponse, St as PipelineContext, _t as Guard, bt as OperationFilter, vt as Interceptor, wt as Transform, xt as PipelineConfig, yt as NextFunction } from "../index-CpzxqePp.mjs";
2
3
  //#region src/pipeline/guard.d.ts
3
4
  interface GuardOptions {
4
5
  operations?: OperationFilter;
@@ -1,4 +1,5 @@
1
- import { n as pipe, t as executePipeline } from "../pipe-BOLmGIxa.mjs";
1
+ /*! @classytic/arc v2.32.0 | MIT | https://classytic.com */
2
+ import { n as pipe, t as executePipeline } from "../pipe-ELHCP3zj.mjs";
2
3
  //#region src/pipeline/guard.ts
3
4
  /**
4
5
  * Create a named guard.
@@ -1,8 +1,9 @@
1
- import { n as getUserRoles } from "./userHelpers-Cg_y3fBz.mjs";
2
- import { b as isElevated, n as PUBLIC_SCOPE, x as isMember } from "./types-CSyCxl2V.mjs";
3
- import { i as resolveEffectiveRoles, t as applyFieldReadPermissions } from "./fields-CQ_7KQWw.mjs";
4
- import { t as executePipeline } from "./pipe-BOLmGIxa.mjs";
5
- import { t as buildRequestScopeProjection } from "./projection-UBDgfMwu.mjs";
1
+ /*! @classytic/arc v2.32.0 | MIT | https://classytic.com */
2
+ import { n as getUserRoles } from "./userHelpers-BNsOXzOd.mjs";
3
+ import { b as isElevated, n as PUBLIC_SCOPE, x as isMember } from "./types-DXYX7dIR.mjs";
4
+ import { i as resolveEffectiveRoles, t as applyFieldReadPermissions } from "./fields-DEi65pGj.mjs";
5
+ import { t as executePipeline } from "./pipe-ELHCP3zj.mjs";
6
+ import { t as buildRequestScopeProjection } from "./projection-CjHhM5W1.mjs";
6
7
  import { isPaginatedResult, toCanonicalList } from "@classytic/repo-core/pagination";
7
8
  //#region src/core/fastifyAdapter.ts
8
9
  /** Type guard for Mongoose-like documents with toObject() */
@@ -121,9 +122,7 @@ function computeFieldCapabilities(fieldPerms, effectiveRoles) {
121
122
  case "visibleTo":
122
123
  readable = perm.roles?.some((r) => effectiveRoles.includes(r)) ?? false;
123
124
  break;
124
- case "writableBy":
125
- writable = perm.roles?.some((r) => effectiveRoles.includes(r)) ?? false;
126
- break;
125
+ case "writableBy": writable = perm.roles?.some((r) => effectiveRoles.includes(r)) ?? false;
127
126
  }
128
127
  caps[field] = {
129
128
  readable,
@@ -171,8 +170,10 @@ function sendControllerResponse(reply, response, request) {
171
170
  /** Apply both field mask and field-level permissions to a data item */
172
171
  const applyPermissions = (data) => {
173
172
  let result = fieldMaskConfig ? applyFieldMask(data, fieldMaskConfig) : data;
174
- if (fieldPerms && result && typeof result === "object") if (Array.isArray(result)) result = result.map((item) => applyFieldReadPermissions(item, fieldPerms, effectiveRoles));
175
- else result = applyFieldReadPermissions(result, fieldPerms, effectiveRoles);
173
+ if (fieldPerms && result && typeof result === "object") {
174
+ if (Array.isArray(result)) result = result.map((item) => applyFieldReadPermissions(item, fieldPerms, effectiveRoles));
175
+ else result = applyFieldReadPermissions(result, fieldPerms, effectiveRoles);
176
+ }
176
177
  return result;
177
178
  };
178
179
  if (response.headers) for (const [key, value] of Object.entries(response.headers)) reply.header(key, value);
@@ -280,11 +281,12 @@ function resolvePipelineSteps(pipeline, operation) {
280
281
  */
281
282
  function buildPipelineHandler(controllerMethod, steps, operation, resourceName) {
282
283
  return async (req, reply) => {
283
- sendControllerResponse(reply, await executePipeline(steps, {
284
+ const pipeCtx = {
284
285
  ...createRequestContext(req),
285
286
  resource: resourceName,
286
287
  operation
287
- }, (ctx) => controllerMethod(ctx), operation), req);
288
+ };
289
+ sendControllerResponse(reply, await executePipeline(steps, pipeCtx, (ctx) => controllerMethod(ctx), operation), req);
288
290
  };
289
291
  }
290
292
  /**
@@ -309,11 +311,12 @@ function buildActionPipelineHandler(handler, steps, operation, resourceName) {
309
311
  data: await handler(id, data, req)
310
312
  });
311
313
  return async (id, data, req) => {
312
- return executePipeline(steps, {
314
+ const pipeCtx = {
313
315
  ...createRequestContext(req),
314
316
  resource: resourceName,
315
317
  operation
316
- }, async (_ctx) => ({
318
+ };
319
+ return executePipeline(steps, pipeCtx, async (_ctx) => ({
317
320
  status: 200,
318
321
  data: await handler(id, data, req)
319
322
  }), operation);
@@ -1,10 +1,11 @@
1
- import { At as RouteDefinition, J as PresetHook, Q as MiddlewareConfig, et as RouteSchemaOptions } from "../index-Q5gzEIWY.mjs";
2
- import { l as AnyRecord } from "../types-ChvvVNP8.mjs";
3
- import { i as errorHandlerPlugin, n as ErrorMapper, r as defaultIsDuplicateKeyError, t as ErrorHandlerOptions } from "../errorHandler-C6yv4ZTW.mjs";
4
- import { _ as _default$3, d as ScheduleStats, f as SchedulesPluginOptions, g as HealthOptions, h as HealthCheck, m as schedulesPlugin, p as _default$7, u as ScheduleDefinition, v as healthPlugin } from "../types-D_n8L6VY.mjs";
5
- import { a as arcCorePlugin, i as _default, n as ArcCorePluginOptions, r as PluginMeta, t as ArcCore } from "../fastify-augmentation-0WhPnUH7.mjs";
6
- import { _ as CachingRule, a as MetricsCollector, c as metricsPlugin, d as _default$5, f as realtimePlugin, g as CachingOptions, h as ssePlugin, i as MetricEntry, l as ChangeOperation, m as _default$8, n as _default$9, o as MetricsOptions, p as SSEOptions, r as versioningPlugin, s as _default$4, t as VersioningOptions, u as RealtimeOptions, v as _default$1, y as cachingPlugin } from "../versioning-DXgY5BUm.mjs";
7
- import { t as TracingOptions } from "../tracing-CsGKQJGg.mjs";
1
+
2
+ import { At as RouteDefinition, J as PresetHook, Q as MiddlewareConfig, et as RouteSchemaOptions } from "../index-CpzxqePp.mjs";
3
+ import { l as AnyRecord } from "../types-B7XeLEgK.mjs";
4
+ import { i as errorHandlerPlugin, n as ErrorMapper, r as defaultIsDuplicateKeyError, t as ErrorHandlerOptions } from "../errorHandler-CpYUJmPA.mjs";
5
+ import { _ as HealthCheck, b as healthPlugin, f as ScheduleDefinition, g as schedulesPlugin, h as _default$7, m as SchedulesPluginOptions, p as ScheduleStats, v as HealthOptions, y as _default$3 } from "../types-CT5vu6M0.mjs";
6
+ import { a as arcCorePlugin, i as _default, n as ArcCorePluginOptions, r as PluginMeta, t as ArcCore } from "../fastify-augmentation-BppG5bMT.mjs";
7
+ import { _ as CachingRule, a as MetricsCollector, c as metricsPlugin, d as _default$5, f as realtimePlugin, g as CachingOptions, h as ssePlugin, i as MetricEntry, l as ChangeOperation, m as _default$8, n as _default$9, o as MetricsOptions, p as SSEOptions, r as versioningPlugin, s as _default$4, t as VersioningOptions, u as RealtimeOptions, v as _default$1, y as cachingPlugin } from "../versioning-D_rhHfZm.mjs";
8
+ import { t as TracingOptions } from "../tracing-Dro5eH6j.mjs";
8
9
  import { PaginatedResult } from "@classytic/repo-core/pagination";
9
10
  import { FastifyInstance, FastifyPluginAsync } from "fastify";
10
11
  //#region src/plugins/createPlugin.d.ts
@@ -1,18 +1,19 @@
1
- import { p as MUTATION_OPERATIONS } from "../constants-DNre0wRg.mjs";
2
- import { c as getOrgId } from "../types-CSyCxl2V.mjs";
3
- import { n as requestContext } from "../requestContext-MrlJwECf.mjs";
4
- import { t as hasEvents } from "../typeGuards-C9KXUly6.mjs";
5
- import { t as HookSystem } from "../HookSystem-WkIougo6.mjs";
6
- import { t as ResourceRegistry } from "../ResourceRegistry-y9Hnypus.mjs";
7
- import { a as requestIdPlugin, i as createRequestIdGenerator, n as health_default, o as requestId_default, s as sanitizeRequestId, t as healthPlugin } from "../health-CB_m4S32.mjs";
8
- import { n as caching_default, t as cachingPlugin } from "../caching-_NJeDd1_.mjs";
9
- import { n as errorHandlerPlugin, t as defaultIsDuplicateKeyError } from "../errorHandler-Bq2OeEQL.mjs";
10
- import { n as metrics_default, t as metricsPlugin } from "../metrics-fdxI5mn_.mjs";
11
- import { n as realtime_default, t as realtimePlugin } from "../realtime-B9nmIY5V.mjs";
12
- import { t as replyHelpersPlugin } from "../replyHelpers-67tRawQZ.mjs";
13
- import { n as schedules_default, t as schedulesPlugin } from "../schedules-CjOd2MG9.mjs";
14
- import { n as sse_default, t as ssePlugin } from "../sse-BQ1w0dfl.mjs";
15
- import { n as versioning_default, t as versioningPlugin } from "../versioning-gKWyryT3.mjs";
1
+ /*! @classytic/arc v2.32.0 | MIT | https://classytic.com */
2
+ import { p as MUTATION_OPERATIONS } from "../constants-J99LGTJI.mjs";
3
+ import { c as getOrgId } from "../types-DXYX7dIR.mjs";
4
+ import { n as requestContext } from "../requestContext-B6vQF-sy.mjs";
5
+ import { t as hasEvents } from "../typeGuards-B_5-hKjJ.mjs";
6
+ import { t as HookSystem } from "../HookSystem-vimw5D7Z.mjs";
7
+ import { t as ResourceRegistry } from "../ResourceRegistry-BVr7PgA0.mjs";
8
+ import { a as requestIdPlugin, i as createRequestIdGenerator, n as health_default, o as requestId_default, s as sanitizeRequestId, t as healthPlugin } from "../health-5XnTa2uo.mjs";
9
+ import { n as caching_default, t as cachingPlugin } from "../caching-DFJqHyFs.mjs";
10
+ import { n as errorHandlerPlugin, t as defaultIsDuplicateKeyError } from "../errorHandler-B2AZUJUj.mjs";
11
+ import { n as metrics_default, t as metricsPlugin } from "../metrics-DdQL8syd.mjs";
12
+ import { n as realtime_default, t as realtimePlugin } from "../realtime-DVvVZZFz.mjs";
13
+ import { t as replyHelpersPlugin } from "../replyHelpers-DS-P0b32.mjs";
14
+ import { n as schedules_default, t as schedulesPlugin } from "../schedules-C2Q3uv2d.mjs";
15
+ import { n as sse_default, t as ssePlugin } from "../sse-B9P4Y-UI.mjs";
16
+ import { n as versioning_default, t as versioningPlugin } from "../versioning-CKv2jz_U.mjs";
16
17
  import fp from "fastify-plugin";
17
18
  //#region src/core/arcCorePlugin.ts
18
19
  const arcCorePlugin = async (fastify, opts = {}) => {