@classytic/arc 2.41.0 → 2.43.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 (230) hide show
  1. package/dist/{EventTransport-CbUKDzZZ.d.mts → EventTransport-CiicKz2Z.d.mts} +1 -1
  2. package/dist/{EventTransport-n7qO5Cz3.mjs → EventTransport-tzA7CAB4.mjs} +22 -17
  3. package/dist/{HookSystem-CxvZBo77.mjs → HookSystem-B6UaKRbF.mjs} +2 -2
  4. package/dist/{QueryCache-DFA-QrU9.d.mts → QueryCache--5uAuYPW.d.mts} +7 -3
  5. package/dist/{ResourceRegistry-Cu7uTJp9.mjs → ResourceRegistry-CuCARu2B.mjs} +44 -13
  6. package/dist/actionPermissions-BotX1YOa.mjs +776 -0
  7. package/dist/audit/index.d.mts +43 -5
  8. package/dist/audit/index.mjs +48 -26
  9. package/dist/auth/audit.mjs +1 -1
  10. package/dist/auth/index.d.mts +43 -13
  11. package/dist/auth/index.mjs +130 -155
  12. package/dist/auth/redis-session.mjs +1 -1
  13. package/dist/{authorizationDecision-2YY_Kaj1.mjs → authorizationDecision-DQYrkl5f.mjs} +7 -102
  14. package/dist/{base-CxrV1LmG.d.mts → base-CZyixEQR.d.mts} +27 -5
  15. package/dist/boundedMap-BULzfXYU.mjs +57 -0
  16. package/dist/{buildHandler-CUhjGHGZ.mjs → buildHandler-CRQ8EcX5.mjs} +4 -4
  17. package/dist/cache/index.d.mts +22 -34
  18. package/dist/cache/index.mjs +4 -4
  19. package/dist/{caching-DsWDjIxx.mjs → caching-Cc2M1DwO.mjs} +3 -3
  20. package/dist/claimed-B10RTfkM.mjs +8 -0
  21. package/dist/cleanup/index.d.mts +6 -10
  22. package/dist/cleanup/index.mjs +27 -73
  23. package/dist/cli/commands/describe.d.mts +4 -2
  24. package/dist/cli/commands/describe.mjs +7 -4
  25. package/dist/cli/commands/docs.mjs +3 -3
  26. package/dist/cli/commands/doctor.mjs +1 -1
  27. package/dist/cli/commands/generate.mjs +2 -2
  28. package/dist/cli/commands/init.mjs +2 -2
  29. package/dist/cli/commands/introspect.mjs +2 -2
  30. package/dist/cli/index.mjs +3 -3
  31. package/dist/{constants-BxLgNFF7.mjs → constants-Cu7Mx7_U.mjs} +4 -2
  32. package/dist/context/index.d.mts +2 -5
  33. package/dist/context/index.mjs +18 -38
  34. package/dist/core/index.d.mts +3 -3
  35. package/dist/core/index.mjs +8 -8
  36. package/dist/{core-iNGqYerO.mjs → core-Cb0LjccA.mjs} +5 -4
  37. package/dist/{createActionRouter-BBlbPs3k.mjs → createActionRouter-xjqonMRO.mjs} +87 -39
  38. package/dist/{createAggregationRouter-Dv5SuK2x.mjs → createAggregationRouter-zRYWw_5D.mjs} +11 -8
  39. package/dist/{preloadResources-Bbp4QJ8V.mjs → createApp-D-953ZAq.mjs} +299 -390
  40. package/dist/{createMcpServer-CbDWkZ1T.d.mts → createMcpServer-C9cxS8zt.d.mts} +1 -1
  41. package/dist/{defineAggregation-UyeurGkp.mjs → defineAggregation-Bm8RjMbq.mjs} +1 -1
  42. package/dist/{defineResource-DzGT-2LP.mjs → defineResource-DIC7JZGU.mjs} +118 -1024
  43. package/dist/discovery/index.mjs +1 -1
  44. package/dist/docs/index.d.mts +10 -4
  45. package/dist/docs/index.mjs +4 -4
  46. package/dist/{elevation-BXCfVcNG.d.mts → elevation-BW2J3tNj.d.mts} +1 -2
  47. package/dist/{elevation-BF-vhGxY.mjs → elevation-DG8SrLN-.mjs} +41 -12
  48. package/dist/encryption/index.mjs +3 -3
  49. package/dist/{errorHandler-CIMkCOWm.mjs → errorHandler-BnfDviL1.mjs} +26 -5
  50. package/dist/{errors-C_YhELhe.mjs → errors-CtrywMHP.mjs} +58 -23
  51. package/dist/{errors-BGdVwP25.d.mts → errors-JYc9vwqE.d.mts} +9 -0
  52. package/dist/{eventPlugin-B52abJ8W.d.mts → eventPlugin-BtmA4-3P.d.mts} +65 -9
  53. package/dist/{eventPlugin-CmXaPWLv.mjs → eventPlugin-Q6BhzHzs.mjs} +82 -21
  54. package/dist/{subscribe-helpers-AVYPgqTl.mjs → eventTypes-Cdhm6ZlV.mjs} +151 -17
  55. package/dist/events/index.d.mts +33 -8
  56. package/dist/events/index.mjs +49 -53
  57. package/dist/events/transports/redis-stream-entry.d.mts +1 -1
  58. package/dist/events/transports/redis-stream-entry.mjs +139 -157
  59. package/dist/events/transports/redis.d.mts +1 -1
  60. package/dist/events/transports/redis.mjs +2 -2
  61. package/dist/factory/index.d.mts +32 -17
  62. package/dist/factory/index.mjs +213 -32
  63. package/dist/{fastify-augmentation-Cq2nHVaW.d.mts → fastify-augmentation-DhUJ-mvo.d.mts} +11 -25
  64. package/dist/{fields-I_EeRWND.d.mts → fields-BBSpf8pS.d.mts} +1 -1
  65. package/dist/{fields-BTyPLHK0.mjs → fields-ToPYpMqS.mjs} +1 -1
  66. package/dist/filter-merge-IpGywvym.mjs +99 -0
  67. package/dist/{generate-D_YmiCNf.mjs → generate-DRTkLlIl.mjs} +2 -2
  68. package/dist/{headers-CNECDE6q.mjs → headers-BbkIF6vA.mjs} +2 -2
  69. package/dist/health-D-Ft6JM6.mjs +22 -0
  70. package/dist/{health-CxQzbJmr.mjs → health-DqbsG-_w.mjs} +115 -55
  71. package/dist/hooks/index.d.mts +2 -2
  72. package/dist/hooks/index.mjs +2 -2
  73. package/dist/idempotency/index.d.mts +31 -7
  74. package/dist/idempotency/index.mjs +155 -98
  75. package/dist/idempotency/redis.d.mts +1 -1
  76. package/dist/idempotency/redis.mjs +48 -11
  77. package/dist/{index-BHdZD8zK.d.mts → index-Bxriq3so.d.mts} +84 -21
  78. package/dist/{preloadResources-CjS2OfpU.d.mts → index-C8zVGYsk.d.mts} +37 -38
  79. package/dist/{index-D4u76eQW.d.mts → index-CRoPtISf.d.mts} +23 -73
  80. package/dist/{index-CbJiYrfv.d.mts → index-Chgz3d0i.d.mts} +1 -1
  81. package/dist/index-JdbZAIsm.d.mts +510 -0
  82. package/dist/{index-DHbyPo5j.d.mts → index-mqaqZNpe.d.mts} +253 -254
  83. package/dist/index.d.mts +8 -8
  84. package/dist/index.mjs +11 -11
  85. package/dist/{init-Y2MlSN9n.mjs → init-B_oUYDXI.mjs} +1 -1
  86. package/dist/integrations/event-gateway.d.mts +8 -3
  87. package/dist/integrations/event-gateway.mjs +4 -4
  88. package/dist/integrations/index.d.mts +3 -3
  89. package/dist/integrations/index.mjs +1 -1
  90. package/dist/integrations/jobs/index.d.mts +1 -1
  91. package/dist/integrations/jobs/index.mjs +18 -5
  92. package/dist/integrations/mcp/index.d.mts +72 -59
  93. package/dist/integrations/mcp/index.mjs +19 -4
  94. package/dist/integrations/mcp/testing.d.mts +2 -2
  95. package/dist/integrations/mcp/testing.mjs +2 -2
  96. package/dist/integrations/streamline.d.mts +78 -136
  97. package/dist/integrations/streamline.mjs +116 -54
  98. package/dist/integrations/webhooks.d.mts +60 -120
  99. package/dist/integrations/webhooks.mjs +36 -80
  100. package/dist/integrations/websocket-pushref-redis.d.mts +1 -1
  101. package/dist/integrations/websocket-pushref-redis.mjs +1 -1
  102. package/dist/integrations/websocket-redis.d.mts +1 -1
  103. package/dist/integrations/websocket-redis.mjs +1 -1
  104. package/dist/integrations/websocket.d.mts +3 -3
  105. package/dist/integrations/websocket.mjs +2 -2
  106. package/dist/{interface-DEWNOMza.d.mts → interface-C0395yUN.d.mts} +13 -1
  107. package/dist/{jsonBody-Cq9KYl2Z.mjs → jsonBody-B4YVGcql.mjs} +2 -2
  108. package/dist/{keys-Cmv4cBRa.mjs → keys-B2KtgMLJ.mjs} +7 -18
  109. package/dist/{loadResources-CblF7xt2.mjs → loadResources-Dn4lalw2.mjs} +3 -3
  110. package/dist/{loadResourcesFromEntry-99fIMC3p.mjs → loadResourcesFromEntry-3sLYbuse.mjs} +2 -2
  111. package/dist/logger/index.d.mts +1 -8
  112. package/dist/logger/index.mjs +3 -153
  113. package/dist/logger-DXjfIqjV.mjs +160 -0
  114. package/dist/{mcpPlugin-xF3Gw-HW.mjs → mcpPlugin-C08HalN5.mjs} +433 -170
  115. package/dist/{memory-CpDZ9NPw.mjs → memory-9Cp7611C.mjs} +36 -34
  116. package/dist/{metrics-BaKnq8ag.mjs → metrics-CWI11eRu.mjs} +39 -72
  117. package/dist/middleware/index.d.mts +2 -2
  118. package/dist/middleware/index.mjs +3 -3
  119. package/dist/migrations/index.mjs +2 -2
  120. package/dist/{module-BkQEesNQ.mjs → module-BSvOQC7E.mjs} +8 -8
  121. package/dist/multiTenant-cY0FkDiA.d.mts +104 -0
  122. package/dist/{multipartBody-C92aJ8am.mjs → multipartBody-CXM2a-Dy.mjs} +46 -7
  123. package/dist/{openapi-C3LFRzPq.mjs → openapi-DT_G4VhY.mjs} +48 -10
  124. package/dist/outbox-admin/index.d.mts +3 -3
  125. package/dist/outbox-admin/index.mjs +6 -18
  126. package/dist/{outbox-module-dsCyLxXH.mjs → outbox-module-Bjgzl6tg.mjs} +203 -33
  127. package/dist/{outbox-module-BHdUnuuK.d.mts → outbox-module-gZCHoE4H.d.mts} +79 -6
  128. package/dist/permissions/index.d.mts +5 -4
  129. package/dist/permissions/index.mjs +8 -6
  130. package/dist/{permissions-DiHXy8Ck.mjs → permissions-Cm0uYHvd.mjs} +149 -33
  131. package/dist/{pipe-CBVmUJ-j.mjs → pipe-BqeGT6UO.mjs} +2 -2
  132. package/dist/pipeline/index.d.mts +2 -2
  133. package/dist/pipeline/index.mjs +2 -2
  134. package/dist/plan-FVR6LP59.mjs +565 -0
  135. package/dist/{BaseController-DYJRZ0EJ.mjs → plugin-BccET1tL.mjs} +1256 -344
  136. package/dist/plugins/index.d.mts +14 -11
  137. package/dist/plugins/index.mjs +44 -22
  138. package/dist/plugins/response-cache.mjs +8 -14
  139. package/dist/plugins/tracing-entry.mjs +2 -2
  140. package/dist/{pluralize-2_U6QZQg.mjs → pluralize-CVIOcgMF.mjs} +1 -1
  141. package/dist/presets/filesUpload.d.mts +2 -2
  142. package/dist/presets/filesUpload.mjs +5 -5
  143. package/dist/presets/index.d.mts +26 -31
  144. package/dist/presets/index.mjs +4 -4
  145. package/dist/presets/multiTenant.d.mts +2 -107
  146. package/dist/presets/multiTenant.mjs +22 -162
  147. package/dist/presets/search.d.mts +2 -2
  148. package/dist/presets/search.mjs +2 -2
  149. package/dist/{presets-BdlugYTc.mjs → presets-Cpvj6fGO.mjs} +99 -107
  150. package/dist/{projection-Uvgp-EEj.mjs → projection-Ce4aQhQk.mjs} +2 -2
  151. package/dist/{pushref-registry-Dov9l4qE.d.mts → pushref-registry-UxCkoA4C.d.mts} +1 -1
  152. package/dist/{queryCachePlugin-BzqxconG.d.mts → queryCachePlugin-C2lZjTxk.d.mts} +1 -1
  153. package/dist/{queryCachePlugin-B5F09bYr.mjs → queryCachePlugin-CFWNH8aT.mjs} +12 -14
  154. package/dist/{rateLimitKey-CK-EU2L8.mjs → rateLimitKey-C4O-kPm0.mjs} +1 -1
  155. package/dist/{realtime-FnnD0n3Z.mjs → realtime-DmPE9S54.mjs} +25 -21
  156. package/dist/redis-stream-BMk-bpQS.d.mts +219 -0
  157. package/dist/{redis-Bzi1y3F9.d.mts → redis-vLvtcGy_.d.mts} +13 -24
  158. package/dist/redisShapes-CQksY67q.d.mts +55 -0
  159. package/dist/registry/index.d.mts +9 -4
  160. package/dist/registry/index.mjs +4 -4
  161. package/dist/{registry-D3y8VEJ1.mjs → registry-Bcz9G1bh.mjs} +10 -8
  162. package/dist/{renewingLease-B3VYh5aU.mjs → renewingLease-qODa7KJQ.mjs} +1 -1
  163. package/dist/{replyHelpers-DPHsdw-5.mjs → replyHelpers-CG_VtVgS.mjs} +2 -2
  164. package/dist/{requestContext-cSVKnYPk.mjs → requestContext-Dsu9P1bV.mjs} +1 -1
  165. package/dist/requirement-CbiLdQ1n.d.mts +22 -0
  166. package/dist/requirement-KDdoRcOP.mjs +35 -0
  167. package/dist/resourceRateLimit-Wf6d6FL9.d.mts +30 -0
  168. package/dist/resourceRateLimit-dSjVr1pk.mjs +180 -0
  169. package/dist/{rolldown-runtime-DjDuyglE.mjs → rolldown-runtime-D1w9Y1VJ.mjs} +1 -1
  170. package/dist/routeValidator-D36SW8qt.mjs +70 -0
  171. package/dist/{runtimeCapabilities-DSA_dH1_.mjs → runtimeCapabilities-0tccjRlj.mjs} +1 -1
  172. package/dist/{schedules-BktJb3Y3.mjs → schedules-C-OpmHBq.mjs} +46 -37
  173. package/dist/{schemaConverter-BLCSmNbr.mjs → schemaConverter-BORek3O-.mjs} +2 -2
  174. package/dist/{schemaIR-K16Bb5BX.mjs → schemaIR-C7Vbp71I.mjs} +2 -2
  175. package/dist/schemas/index.d.mts +10 -0
  176. package/dist/schemas/index.mjs +8 -3
  177. package/dist/scim/index.mjs +2 -2
  178. package/dist/scope/index.d.mts +2 -2
  179. package/dist/scope/index.mjs +6 -6
  180. package/dist/{scope-jSxuBTFM.mjs → scope-DQthe88b.mjs} +4 -4
  181. package/dist/shutdown-MqA81z_P.mjs +59 -0
  182. package/dist/{simpleEqualityMatcher-BiOnyflP.mjs → simpleEqualityMatcher-auW8QjXN.mjs} +1 -1
  183. package/dist/{sse-CH7Slxc1.mjs → sse-CYzQBVFe.mjs} +11 -15
  184. package/dist/{sseStream-CN-Lh1Tu.mjs → sseStream-B5_Afiqn.mjs} +2 -2
  185. package/dist/{store-helpers-9DX9-1j_.mjs → store-helpers-BOzk-CuX.mjs} +1 -1
  186. package/dist/{streaming-dvppnxsr.mjs → streaming-DjTQhZrN.mjs} +1 -1
  187. package/dist/subscriberIdentity-CW39uA8G.mjs +28 -0
  188. package/dist/tenancy--ed9eH_z.mjs +43 -0
  189. package/dist/tenantPreparation-BKon6Yox.mjs +67 -0
  190. package/dist/testing/cleanupStoreContract.mjs +1 -1
  191. package/dist/testing/index.d.mts +193 -175
  192. package/dist/testing/index.mjs +286 -212
  193. package/dist/testing/outboxStoreContract.mjs +137 -2
  194. package/dist/testing/requiredPorts.d.mts +17 -0
  195. package/dist/testing/requiredPorts.mjs +65 -0
  196. package/dist/testing/storageContract.mjs +1 -1
  197. package/dist/timing-qczZ0YC-.mjs +46 -0
  198. package/dist/{transactionContext-DCWMI6hV.mjs → transactionContext-DjFkrNG6.mjs} +1 -1
  199. package/dist/{typeGuards-D7tHFdH6.mjs → typeGuards-CySF12G-.mjs} +1 -1
  200. package/dist/types/index.d.mts +4 -4
  201. package/dist/types/index.mjs +1 -1
  202. package/dist/types/storage.mjs +1 -1
  203. package/dist/{types-CWQn9-IC.d.mts → types-B4Aq4Sdj.d.mts} +22 -9
  204. package/dist/{types-DfVDvkdP.d.mts → types-BJ-2XqVF.d.mts} +189 -204
  205. package/dist/{types-CDy3-zw1.mjs → types-Bk9NOgOC.mjs} +4 -4
  206. package/dist/usage/index.d.mts +2 -3
  207. package/dist/usage/index.mjs +5 -5
  208. package/dist/{userHelpers-DDMOZ_Pm.mjs → userHelpers-aklawRaK.mjs} +1 -1
  209. package/dist/utils/index.d.mts +84 -223
  210. package/dist/utils/index.mjs +13 -12
  211. package/dist/{utils-B-zj_b__.mjs → utils-ec1oTo1n.mjs} +315 -404
  212. package/dist/{validate-R0G_JrnK.mjs → validate-9yzHC1e2.mjs} +6 -14
  213. package/dist/{versioning-D6JIK8Tk.d.mts → versioning-Bsb51gGT.d.mts} +28 -13
  214. package/dist/{versioning-DuVWcu-F.mjs → versioning-fhhWHV74.mjs} +4 -4
  215. package/dist/{websocket-BOXVggMf.d.mts → websocket-Cs4awVyD.d.mts} +1 -1
  216. package/dist/{workScope-BhsWde4k.mjs → workScope-zpPyge4t.mjs} +2 -2
  217. package/package.json +578 -574
  218. package/skills/arc/SKILL.md +13 -4
  219. package/skills/arc/references/auth.md +8 -5
  220. package/skills/arc/references/integrations.md +1 -1
  221. package/skills/arc/references/mcp.md +8 -6
  222. package/skills/arc/references/multi-tenancy.md +19 -14
  223. package/skills/arc/references/production.md +27 -10
  224. package/dist/actionPermissions-DPwPWPOV.mjs +0 -350
  225. package/dist/index-CFfLKMH4.d.mts +0 -290
  226. package/dist/interface-BIpgzbZl.mjs +0 -17
  227. package/dist/redis-stream-BPKscsTB.d.mts +0 -260
  228. package/dist/registerRoute-DRsSHPSM.mjs +0 -364
  229. package/dist/sync/changelog.d.mts +0 -3
  230. package/dist/sync/changelog.mjs +0 -3
@@ -0,0 +1,28 @@
1
+ /*! @classytic/arc v2.43.0 | MIT | https://classytic.com */
2
+ //#region src/events/subscriberIdentity.ts
3
+ const SUBSCRIBER_ID = Symbol.for("@classytic/arc/subscriber-id");
4
+ /** Assign a deployment-stable identity for durable per-handler progress; never use an index. */
5
+ function withSubscriberId(id, handler) {
6
+ if (typeof id !== "string" || !id.trim()) throw new Error("[Arc Events] subscriber id must be non-empty");
7
+ const wrapped = async (event) => {
8
+ await handler(event);
9
+ };
10
+ Object.defineProperty(wrapped, SUBSCRIBER_ID, { value: id });
11
+ return wrapped;
12
+ }
13
+ function getSubscriberId(handler) {
14
+ const id = Reflect.get(handler, SUBSCRIBER_ID);
15
+ return typeof id === "string" ? id : void 0;
16
+ }
17
+ /** Preserve explicit IDs and the implicit function name through framework wrappers. */
18
+ function preserveSubscriberIdentity(original, wrapper) {
19
+ const id = getSubscriberId(original);
20
+ if (id !== void 0) Object.defineProperty(wrapper, SUBSCRIBER_ID, { value: id });
21
+ Object.defineProperty(wrapper, "name", {
22
+ value: original.name,
23
+ configurable: true
24
+ });
25
+ return wrapper;
26
+ }
27
+ //#endregion
28
+ export { preserveSubscriberIdentity as n, withSubscriberId as r, getSubscriberId as t };
@@ -0,0 +1,43 @@
1
+ /*! @classytic/arc v2.43.0 | MIT | https://classytic.com */
2
+ import { b as isElevated, c as getOrgId } from "./types-Bk9NOgOC.mjs";
3
+ //#region src/core/tenancy.ts
4
+ function eventAudienceOf(scope) {
5
+ const tenancy = resolveTenancy({ _scope: scope }, "organizationId");
6
+ return tenancy.mode === "scoped" ? {
7
+ orgId: tenancy.orgId,
8
+ platformView: false
9
+ } : { platformView: tenancy.reason === "platform" };
10
+ }
11
+ /**
12
+ * Whether an event tagged `eventOrgId` may reach `audience`. Untenanted events
13
+ * reach everyone; a tenant's event reaches that tenant and the platform view,
14
+ * and nobody else — including a caller with no org at all (fail-closed).
15
+ */
16
+ function isEventVisible(eventOrgId, audience) {
17
+ if (eventOrgId === void 0 || eventOrgId === null || eventOrgId === "") return true;
18
+ if (audience.platformView) return true;
19
+ return audience.orgId !== void 0 && String(eventOrgId) === audience.orgId;
20
+ }
21
+ function resolveTenancy(input, tenantField) {
22
+ if (!tenantField) return {
23
+ mode: "unscoped",
24
+ reason: "untenanted-resource"
25
+ };
26
+ if (input?._crossTenantRead === true) return {
27
+ mode: "unscoped",
28
+ reason: "cross-tenant"
29
+ };
30
+ const scope = input?._scope;
31
+ const orgId = scope ? getOrgId(scope) : void 0;
32
+ if (orgId) return {
33
+ mode: "scoped",
34
+ field: tenantField,
35
+ orgId
36
+ };
37
+ return {
38
+ mode: "unscoped",
39
+ reason: scope && isElevated(scope) ? "platform" : "no-org"
40
+ };
41
+ }
42
+ //#endregion
43
+ export { isEventVisible as n, resolveTenancy as r, eventAudienceOf as t };
@@ -0,0 +1,67 @@
1
+ /*! @classytic/arc v2.43.0 | MIT | https://classytic.com */
2
+ import { t as conjoinPolicyFilters } from "./filter-merge-IpGywvym.mjs";
3
+ import { d as createDomainError } from "./errors-CtrywMHP.mjs";
4
+ import { b as isElevated, c as getOrgId, d as getScopeContext, m as getTeamId, v as hasOrgAccess } from "./types-Bk9NOgOC.mjs";
5
+ //#region src/permissions/tenantPreparation.ts
6
+ /** Tenant preparation shared by HTTP middleware and direct transport invocation. */
7
+ const PREPARATION = Symbol.for("arc.tenantPreparation");
8
+ function tagTenantPreparation(middleware, preparation) {
9
+ Object.defineProperty(middleware, PREPARATION, { value: preparation });
10
+ return middleware;
11
+ }
12
+ function isTenantPreparation(middleware) {
13
+ return typeof middleware === "function" && middleware[PREPARATION] !== void 0;
14
+ }
15
+ /**
16
+ * The resource's tenant middlewares minus body INJECTION: an action payload is a command, not
17
+ * the document, so it is scoped (filters, `_tenantFields`) but never written into.
18
+ */
19
+ function withoutTenantInjection(middlewares) {
20
+ return (middlewares ?? []).filter((middleware) => {
21
+ if (typeof middleware !== "function") return true;
22
+ const preparation = Reflect.get(middleware, PREPARATION);
23
+ return !(typeof preparation === "object" && preparation !== null && "mode" in preparation && preparation.mode === "inject");
24
+ });
25
+ }
26
+ function prepareTenancy(scope, preparation) {
27
+ if (preparation.mode === "crossTenant") return { crossTenant: true };
28
+ const elevated = isElevated(scope);
29
+ if (preparation.mode === "inject" && elevated && !getOrgId(scope)) return {};
30
+ if (preparation.mode !== "inject" && !elevated && !hasOrgAccess(scope)) {
31
+ if (preparation.mode === "flexible") return {};
32
+ throw createDomainError(scope.kind === "public" ? "arc.unauthorized" : "arc.forbidden", scope.kind === "public" ? "Authentication required for multi-tenant resources" : "Organization context required for this operation", scope.kind === "public" ? 401 : 403);
33
+ }
34
+ const fields = {};
35
+ const missing = [];
36
+ for (const spec of preparation.specs) {
37
+ const value = "contextKey" in spec ? getScopeContext(scope, spec.contextKey) : spec.type === "org" ? getOrgId(scope) : getTeamId(scope);
38
+ if (value === void 0) missing.push(spec.field);
39
+ else fields[spec.field] = value;
40
+ }
41
+ if (missing.length && (preparation.mode === "inject" || !elevated)) throw createDomainError("arc.forbidden", `Tenant context incomplete — missing: ${missing.join(", ")}`, 403);
42
+ return {
43
+ fields,
44
+ ...preparation.mode === "inject" ? { inject: true } : { policy: fields }
45
+ };
46
+ }
47
+ function applyTenantPreparation(target, body, scope, preparation) {
48
+ const result = prepareTenancy(scope, preparation);
49
+ if (result.crossTenant) target._crossTenantRead = true;
50
+ if (result.policy && Object.keys(result.policy).length) target._policyFilters = conjoinPolicyFilters(target._policyFilters, result.policy);
51
+ if (result.fields && Object.keys(result.fields).length) target._tenantFields = {
52
+ ...target._tenantFields,
53
+ ...result.fields
54
+ };
55
+ if (result.inject && body && typeof body === "object") Object.assign(body, result.fields);
56
+ }
57
+ function prepareTenantContext(ctx, middlewares) {
58
+ const meta = ctx.metadata;
59
+ for (const middleware of middlewares ?? []) {
60
+ if (typeof middleware !== "function") continue;
61
+ const preparation = middleware[PREPARATION];
62
+ if (preparation) applyTenantPreparation(meta, ctx.body, meta._scope ?? { kind: "public" }, preparation);
63
+ if (meta._tenantFields) ctx._tenantFields = meta._tenantFields;
64
+ }
65
+ }
66
+ //#endregion
67
+ export { withoutTenantInjection as a, tagTenantPreparation as i, isTenantPreparation as n, prepareTenantContext as r, applyTenantPreparation as t };
@@ -1,4 +1,4 @@
1
- /*! @classytic/arc v2.41.0 | MIT | https://classytic.com */
1
+ /*! @classytic/arc v2.43.0 | MIT | https://classytic.com */
2
2
  import { afterAll, beforeAll, describe, expect, it } from "vitest";
3
3
  //#region src/testing/cleanupStoreContract.ts
4
4
  /**
@@ -1,10 +1,10 @@
1
1
 
2
2
  import { i as RequestScope } from "../types-Ddl9OjmE.mjs";
3
- import { l as PermissionCheck, t as AnyRecord } from "../base-CxrV1LmG.mjs";
4
- import { V as ResourceDefinition } from "../index-DHbyPo5j.mjs";
5
- import { n as ArcModuleInput, o as ModuleDisposer, s as ModuleSetupContext } from "../types-DfVDvkdP.mjs";
6
- import "../index-CFfLKMH4.mjs";
7
- import { n as preloadResourcesAsync, r as CreateAppOptions, t as preloadResources } from "../preloadResources-CjS2OfpU.mjs";
3
+ import { l as PermissionCheck, t as AnyRecord } from "../base-CZyixEQR.mjs";
4
+ import { F as ResourceDefinition } from "../index-mqaqZNpe.mjs";
5
+ import { h as ModuleSetupContext, m as ModuleDisposer, o as ArcPorts, p as PortName, r as ArcModuleInput, u as ModuleName } from "../types-BJ-2XqVF.mjs";
6
+ import "../index-JdbZAIsm.mjs";
7
+ import { n as CreateAppOptions } from "../index-C8zVGYsk.mjs";
8
8
  import { StorageContractSetup, StorageContractSetupResult, runStorageContract } from "./storageContract.mjs";
9
9
  import { StandardRepo } from "@classytic/repo-core/repository";
10
10
  import { FastifyInstance, FastifyServerOptions } from "fastify";
@@ -74,6 +74,180 @@ interface ArcAssertion {
74
74
  }
75
75
  declare function expectArc(response: ArcResponseLike): ArcAssertion;
76
76
  //#endregion
77
+ //#region src/testing/testActor.d.ts
78
+ /** The header the testing harness uses to carry a request's actor. */
79
+ declare const TEST_ACTOR_HEADER = "x-test-actor";
80
+ /**
81
+ * What a test wants to be. An `organizationId` produces a `member` scope (the
82
+ * org-scoped case); without one the actor is `authenticated`.
83
+ */
84
+ interface TestActor {
85
+ /**
86
+ * Defaults to a synthetic id derived from the roles. Pass one explicitly when
87
+ * a test distinguishes two actors — ownership and audit assertions do, and
88
+ * two actors sharing roles would otherwise share an id.
89
+ */
90
+ userId?: string;
91
+ /** Global roles — what `requireRoles` reads. */
92
+ roles?: string[];
93
+ /** Tenant. Present ⇒ a `member` scope. */
94
+ organizationId?: string;
95
+ /** Org-level roles from membership records. Defaults to `roles`. */
96
+ orgRoles?: string[];
97
+ teamId?: string;
98
+ /** App-defined dimensions (branch, project, region) read via `getScopeContext`. */
99
+ context?: Record<string, string>;
100
+ /**
101
+ * Elevated (platform-admin) scope. The value is the `elevatedBy` audit string
102
+ * arc requires — an elevated scope must record who granted it. `true` is
103
+ * shorthand for `'test'`.
104
+ *
105
+ * An elevated scope carries no roles: elevation is a different kind of actor,
106
+ * not a role. A test needing a role gate AND elevation wants `member`.
107
+ */
108
+ elevated?: string | boolean;
109
+ }
110
+ /**
111
+ * Build the headers that make an injected request act as `actor`.
112
+ *
113
+ * A string is shorthand for a single role. `null` means "stay public", so a test
114
+ * can prove an endpoint is reachable anonymously.
115
+ *
116
+ * @example
117
+ * ```ts
118
+ * t.app.inject({ method: 'GET', url: '/pos/shifts/current',
119
+ * headers: testActorHeaders('manager', ORG) });
120
+ * ```
121
+ */
122
+ declare function testActorHeaders(actor: TestActor | string | null, organizationId?: string): Record<string, string>;
123
+ /**
124
+ * Turn a spec into a `RequestScope`.
125
+ *
126
+ * `orgRoles` defaults to `roles`: a test that says "manager" means it at both
127
+ * levels, and making the caller repeat itself only produces tests that pass a
128
+ * global gate and mysteriously fail an org one.
129
+ */
130
+ declare function scopeFromTestActor(spec: TestActor): RequestScope;
131
+ /**
132
+ * Build the `auth` option that makes the test actor arc's authenticator.
133
+ *
134
+ * It must be an authenticator, not a hook. Fastify encapsulates plugins and
135
+ * modules register DURING `createApp`, so a hook added to the root instance
136
+ * afterwards never runs for their routes — every request still resolves as
137
+ * public and every role gate still answers 401, which reads exactly like a
138
+ * broken authorization rule in the code under test. Registering as
139
+ * `auth: { type: 'authenticator' }` puts the scope where arc's own auth would,
140
+ * at the phase arc expects, so tenant filtering and permission checks behave
141
+ * identically to production.
142
+ *
143
+ * `defaultActor` applies to requests carrying no header. A malformed header
144
+ * REJECTS rather than falling back to public — a test that meant to be an admin
145
+ * and got quietly demoted is the most expensive way to fail.
146
+ */
147
+ declare function testActorAuth(defaultActor?: TestActor): {
148
+ type: "authenticator";
149
+ authenticate: (request: unknown) => Promise<void>;
150
+ };
151
+ //#endregion
152
+ //#region src/testing/bootModuleApp.d.ts
153
+ /** Handed to a modules factory — the in-memory DB is live at call time. */
154
+ /**
155
+ * A provisioned test database — what the `database` seam yields. Arc
156
+ * core never names a driver: kits ship factories (`mongoMemoryDatabase`
157
+ * is the built-in default; a sqlitekit/pgkit testkit ships its own) and
158
+ * the connection flows through as an opaque generic.
159
+ */
160
+ interface TestDatabase<TConn = unknown> {
161
+ /** Connection string, when the backend has one (informational). */
162
+ uri: string;
163
+ /** Driver connection handle passed to the modules factory. */
164
+ connection: TConn;
165
+ /** Stop/disconnect everything this factory started. MUST be idempotent-safe to call once. */
166
+ teardown(): Promise<void>;
167
+ }
168
+ /** The DB-provisioning seam — `bootModuleApp({ database })`. */
169
+ type TestDatabaseFactory<TConn = unknown> = (opts: {
170
+ replset?: boolean;
171
+ }) => Promise<TestDatabase<TConn>>;
172
+ interface TestkitContext<TConn = unknown> {
173
+ /** The provisioned database's connection string. */
174
+ uri: string;
175
+ /**
176
+ * Driver connection handle (the default factory yields mongoose's
177
+ * DEFAULT connection). Generic so DB-typed wrappers assert the driver
178
+ * type ONCE: `bootModuleApp<Connection>(...)`. Arc core stays DB-agnostic.
179
+ */
180
+ connection: TConn;
181
+ }
182
+ type BootModulesInput<TConn = unknown> = ArcModuleInput | readonly ArcModuleInput[] | ((ctx: TestkitContext<TConn>) => ArcModuleInput | readonly ArcModuleInput[] | Promise<ArcModuleInput | readonly ArcModuleInput[]>);
183
+ interface BootModuleAppOptions<TConn = unknown> {
184
+ /**
185
+ * DB provisioner. Default: `mongoMemoryDatabase` (in-memory MongoDB —
186
+ * lazy devDep imports, nothing Mongo touches arc's runtime). Kits for
187
+ * other stores inject their own factory here; the rest of the harness
188
+ * (module resolution, app boot, teardown ordering) is DB-agnostic.
189
+ */
190
+ database?: TestDatabaseFactory<TConn>;
191
+ /** Single-node replica set (transactions). Default: standalone. Passed to the database factory. */
192
+ replset?: boolean;
193
+ /**
194
+ * Extra `createApp` options merged over the testing defaults
195
+ * (`preset: 'testing'`, `auth: false`, `sensible: false` — utility
196
+ * plugins stay off so the testkit is dependency-light; re-enable per
197
+ * boot when a module genuinely exercises one).
198
+ */
199
+ app?: Partial<CreateAppOptions>;
200
+ /**
201
+ * Default actor for every injected request that carries no `x-test-actor` header.
202
+ *
203
+ * The harness boots with `auth: false`, so without this every request is
204
+ * `PUBLIC_SCOPE` and role-gated routes answer 401 — correct, but it left tests with
205
+ * no supported way to be somebody. See `testActor.ts` for why the seam lives in the
206
+ * testing entrypoint and can never be reached from production.
207
+ */
208
+ actor?: TestActor;
209
+ /** Host ports for the booted modules — typed and checked exactly as `createApp({ ports })`. */
210
+ ports?: ArcPorts;
211
+ }
212
+ interface ModuleTestApp<TConn = unknown> {
213
+ app: FastifyInstance;
214
+ /** The provisioned database's connection string. */
215
+ uri: string;
216
+ /** The mongoose `Connection` handed to the modules factory. */
217
+ connection: TConn;
218
+ /**
219
+ * Typed module-export accessor — `t.exports<CatalogEngine>('catalog')`
220
+ * instead of reaching through `(t.app as ...).arc.modules.catalog`.
221
+ * Delegates to `getModuleExports` (throws with the registered-module
222
+ * list when the name is unknown — same contract hosts get).
223
+ */
224
+ exports<TExports = unknown>(name: string): TExports;
225
+ /** Close app → disconnect mongoose → stop the in-memory server. Idempotent. */
226
+ close(): Promise<void>;
227
+ }
228
+ /**
229
+ * The built-in DB factory: in-memory MongoDB via lazy devDep imports.
230
+ * Everything Mongo-specific about the harness lives HERE — the harness
231
+ * itself is driver-blind and any kit can replace this via `database`.
232
+ */
233
+ declare function mongoMemoryDatabase(opts: {
234
+ replset?: boolean;
235
+ }): Promise<TestDatabase<unknown>>;
236
+ declare function bootModuleApp<TConn = unknown>(modules: BootModulesInput<TConn>, options?: BootModuleAppOptions<TConn>): Promise<ModuleTestApp<TConn>>;
237
+ //#endregion
238
+ //#region src/testing/assertModuleBootsAlone.d.ts
239
+ interface ModuleBootsAloneOptions<TConn = unknown> extends BootModuleAppOptions<TConn> {
240
+ /** Runs against the booted closure before it closes — drive the module's cross-module ports. */
241
+ probe?: (t: ModuleTestApp<TConn>) => void | Promise<void>;
242
+ }
243
+ interface ModuleBootsAloneResult {
244
+ /** The closure's modules (bridges excluded), in input order. */
245
+ readonly closure: readonly string[];
246
+ }
247
+ declare function assertModuleBootsAlone<TConn = unknown>(modules: BootModulesInput<TConn>,
248
+ /** The module — or several roots booted together, when a deployment rule pairs them. */
249
+ name: ModuleName | readonly ModuleName[], options?: ModuleBootsAloneOptions<TConn>): Promise<ModuleBootsAloneResult>;
250
+ //#endregion
77
251
  //#region src/testing/authorizationConformance.d.ts
78
252
  /** One surface's observed enforcement: the HTTP status + the filter arc passed the repo. */
79
253
  interface SurfaceObservation {
@@ -341,181 +515,22 @@ declare function createBetterAuthTestHelpers(options?: BetterAuthTestHelpersOpti
341
515
  */
342
516
  declare function setupBetterAuthTestApp(input: SetupBetterAuthTestAppInput): Promise<SetupBetterAuthTestAppResult>;
343
517
  //#endregion
344
- //#region src/testing/testActor.d.ts
345
- /** The header the testing harness uses to carry a request's actor. */
346
- declare const TEST_ACTOR_HEADER = "x-test-actor";
347
- /**
348
- * What a test wants to be. An `organizationId` produces a `member` scope (the
349
- * org-scoped case); without one the actor is `authenticated`.
350
- */
351
- interface TestActor {
352
- /**
353
- * Defaults to a synthetic id derived from the roles. Pass one explicitly when
354
- * a test distinguishes two actors — ownership and audit assertions do, and
355
- * two actors sharing roles would otherwise share an id.
356
- */
357
- userId?: string;
358
- /** Global roles — what `requireRoles` reads. */
359
- roles?: string[];
360
- /** Tenant. Present ⇒ a `member` scope. */
361
- organizationId?: string;
362
- /** Org-level roles from membership records. Defaults to `roles`. */
363
- orgRoles?: string[];
364
- teamId?: string;
365
- /** App-defined dimensions (branch, project, region) read via `getScopeContext`. */
366
- context?: Record<string, string>;
367
- /**
368
- * Elevated (platform-admin) scope. The value is the `elevatedBy` audit string
369
- * arc requires — an elevated scope must record who granted it. `true` is
370
- * shorthand for `'test'`.
371
- *
372
- * An elevated scope carries no roles: elevation is a different kind of actor,
373
- * not a role. A test needing a role gate AND elevation wants `member`.
374
- */
375
- elevated?: string | boolean;
376
- }
377
- /**
378
- * Build the headers that make an injected request act as `actor`.
379
- *
380
- * A string is shorthand for a single role. `null` means "stay public", so a test
381
- * can prove an endpoint is reachable anonymously.
382
- *
383
- * @example
384
- * ```ts
385
- * t.app.inject({ method: 'GET', url: '/pos/shifts/current',
386
- * headers: testActorHeaders('manager', ORG) });
387
- * ```
388
- */
389
- declare function testActorHeaders(actor: TestActor | string | null, organizationId?: string): Record<string, string>;
390
- /**
391
- * Turn a spec into a `RequestScope`.
392
- *
393
- * `orgRoles` defaults to `roles`: a test that says "manager" means it at both
394
- * levels, and making the caller repeat itself only produces tests that pass a
395
- * global gate and mysteriously fail an org one.
396
- */
397
- declare function scopeFromTestActor(spec: TestActor): RequestScope;
398
- /**
399
- * Build the `auth` option that makes the test actor arc's authenticator.
400
- *
401
- * It must be an authenticator, not a hook. Fastify encapsulates plugins and
402
- * modules register DURING `createApp`, so a hook added to the root instance
403
- * afterwards never runs for their routes — every request still resolves as
404
- * public and every role gate still answers 401, which reads exactly like a
405
- * broken authorization rule in the code under test. Registering as
406
- * `auth: { type: 'authenticator' }` puts the scope where arc's own auth would,
407
- * at the phase arc expects, so tenant filtering and permission checks behave
408
- * identically to production.
409
- *
410
- * `defaultActor` applies to requests carrying no header. A malformed header
411
- * REJECTS rather than falling back to public — a test that meant to be an admin
412
- * and got quietly demoted is the most expensive way to fail.
413
- */
414
- declare function testActorAuth(defaultActor?: TestActor): {
415
- type: "authenticator";
416
- authenticate: (request: unknown) => Promise<void>;
417
- };
418
- //#endregion
419
- //#region src/testing/bootModuleApp.d.ts
420
- /** Handed to a modules factory — the in-memory DB is live at call time. */
421
- /**
422
- * A provisioned test database — what the `database` seam yields. Arc
423
- * core never names a driver: kits ship factories (`mongoMemoryDatabase`
424
- * is the built-in default; a sqlitekit/pgkit testkit ships its own) and
425
- * the connection flows through as an opaque generic.
426
- */
427
- interface TestDatabase<TConn = unknown> {
428
- /** Connection string, when the backend has one (informational). */
429
- uri: string;
430
- /** Driver connection handle passed to the modules factory. */
431
- connection: TConn;
432
- /** Stop/disconnect everything this factory started. MUST be idempotent-safe to call once. */
433
- teardown(): Promise<void>;
434
- }
435
- /** The DB-provisioning seam — `bootModuleApp({ database })`. */
436
- type TestDatabaseFactory<TConn = unknown> = (opts: {
437
- replset?: boolean;
438
- }) => Promise<TestDatabase<TConn>>;
439
- interface TestkitContext<TConn = unknown> {
440
- /** The provisioned database's connection string. */
441
- uri: string;
442
- /**
443
- * Driver connection handle (the default factory yields mongoose's
444
- * DEFAULT connection). Generic so DB-typed wrappers assert the driver
445
- * type ONCE: `bootModuleApp<Connection>(...)`. Arc core stays DB-agnostic.
446
- */
447
- connection: TConn;
448
- }
449
- type BootModulesInput<TConn = unknown> = ArcModuleInput | readonly ArcModuleInput[] | ((ctx: TestkitContext<TConn>) => ArcModuleInput | readonly ArcModuleInput[] | Promise<ArcModuleInput | readonly ArcModuleInput[]>);
450
- interface BootModuleAppOptions<TConn = unknown> {
451
- /**
452
- * DB provisioner. Default: `mongoMemoryDatabase` (in-memory MongoDB —
453
- * lazy devDep imports, nothing Mongo touches arc's runtime). Kits for
454
- * other stores inject their own factory here; the rest of the harness
455
- * (module resolution, app boot, teardown ordering) is DB-agnostic.
456
- */
457
- database?: TestDatabaseFactory<TConn>;
458
- /** Single-node replica set (transactions). Default: standalone. Passed to the database factory. */
459
- replset?: boolean;
460
- /**
461
- * Extra `createApp` options merged over the testing defaults
462
- * (`preset: 'testing'`, `auth: false`, `sensible: false` — utility
463
- * plugins stay off so the testkit is dependency-light; re-enable per
464
- * boot when a module genuinely exercises one).
465
- */
466
- app?: Partial<CreateAppOptions>;
467
- /**
468
- * Default actor for every injected request that carries no `x-test-actor` header.
469
- *
470
- * The harness boots with `auth: false`, so without this every request is
471
- * `PUBLIC_SCOPE` and role-gated routes answer 401 — correct, but it left tests with
472
- * no supported way to be somebody. See `testActor.ts` for why the seam lives in the
473
- * testing entrypoint and can never be reached from production.
474
- */
475
- actor?: TestActor;
476
- }
477
- interface ModuleTestApp<TConn = unknown> {
478
- app: FastifyInstance;
479
- /** The provisioned database's connection string. */
480
- uri: string;
481
- /** The mongoose `Connection` handed to the modules factory. */
482
- connection: TConn;
483
- /**
484
- * Typed module-export accessor — `t.exports<CatalogEngine>('catalog')`
485
- * instead of reaching through `(t.app as ...).arc.modules.catalog`.
486
- * Delegates to `getModuleExports` (throws with the registered-module
487
- * list when the name is unknown — same contract hosts get).
488
- */
489
- exports<TExports = unknown>(name: string): TExports;
490
- /** Close app → disconnect mongoose → stop the in-memory server. Idempotent. */
491
- close(): Promise<void>;
492
- }
493
- /**
494
- * The built-in DB factory: in-memory MongoDB via lazy devDep imports.
495
- * Everything Mongo-specific about the harness lives HERE — the harness
496
- * itself is driver-blind and any kit can replace this via `database`.
497
- */
498
- declare function mongoMemoryDatabase(opts: {
499
- replset?: boolean;
500
- }): Promise<TestDatabase<unknown>>;
501
- declare function bootModuleApp<TConn = unknown>(modules: BootModulesInput<TConn>, options?: BootModuleAppOptions<TConn>): Promise<ModuleTestApp<TConn>>;
502
- //#endregion
503
518
  //#region src/testing/fixtures.d.ts
504
- type FixtureFactory<T extends AnyRecord = AnyRecord> = (data: Partial<T>) => Promise<T>;
519
+ type FixtureFactory<T extends object = AnyRecord> = (data: Partial<T>) => Promise<T>;
505
520
  /** Delete hook invoked by `clear()` for records that were created through a factory. */
506
- type FixtureDestroyer<T extends AnyRecord = AnyRecord> = (record: T) => Promise<void>;
507
- interface FixtureRegistration<T extends AnyRecord = AnyRecord> {
521
+ type FixtureDestroyer<T extends object = AnyRecord> = (record: T) => Promise<void>;
522
+ interface FixtureRegistration<T extends object = AnyRecord> {
508
523
  create: FixtureFactory<T>;
509
524
  /** Optional cleanup. Defaults to a no-op; adapters that support deletion should provide one. */
510
525
  destroy?: FixtureDestroyer<T>;
511
526
  }
512
527
  interface TestFixtures {
513
528
  /** Register a named factory. Later calls replace the earlier registration. */
514
- register<T extends AnyRecord = AnyRecord>(name: string, factoryOrRegistration: FixtureFactory<T> | FixtureRegistration<T>): void;
529
+ register<T extends object = AnyRecord>(name: string, factoryOrRegistration: FixtureFactory<T> | FixtureRegistration<T>): void;
515
530
  /** Create one record through the named factory. Tracked for cleanup. */
516
- create<T extends AnyRecord = AnyRecord>(name: string, data?: Partial<T>): Promise<T>;
531
+ create<T extends object = AnyRecord>(name: string, data?: Partial<T>): Promise<T>;
517
532
  /** Create many records with a shared template. Tracked for cleanup. */
518
- createMany<T extends AnyRecord = AnyRecord>(name: string, count: number, template?: Partial<T>): Promise<T[]>;
533
+ createMany<T extends object = AnyRecord>(name: string, count: number, template?: Partial<T>): Promise<T[]>;
519
534
  /**
520
535
  * Run every registered `destroy` hook over the records this instance
521
536
  * created, then forget them. Safe to call multiple times (idempotent).
@@ -524,7 +539,7 @@ interface TestFixtures {
524
539
  */
525
540
  clear(): Promise<void>;
526
541
  /** All records ever created by a given factory name (read-only snapshot). */
527
- all<T extends AnyRecord = AnyRecord>(name: string): readonly T[];
542
+ all<T extends object = AnyRecord>(name: string): readonly T[];
528
543
  /** Registered factory names. */
529
544
  readonly names: readonly string[];
530
545
  }
@@ -591,7 +606,7 @@ interface MockRepository<T> extends StandardRepo<T> {
591
606
  *
592
607
  * await mockRepo.getById('1'); // Returns mocked product
593
608
  */
594
- declare function createMockRepository<T extends AnyRecord = AnyRecord>(overrides?: Partial<MockRepository<T>>): MockRepository<T>;
609
+ declare function createMockRepository<T extends object = AnyRecord>(overrides?: Partial<MockRepository<T>>): MockRepository<T>;
595
610
  /**
596
611
  * Create a mock user for authentication testing
597
612
  */
@@ -695,7 +710,7 @@ declare function createTestTimer(): {
695
710
  //#region src/testing/moduleSetup.d.ts
696
711
  interface TestModuleSetup {
697
712
  /** Pass this as the second argument to `bootstrap` / `plugins` / `afterResources`. */
698
- readonly context: ModuleSetupContext;
713
+ readonly context: ModuleSetupContext<PortName, PortName>;
699
714
  /**
700
715
  * Disposers the module registered, in REGISTRATION order (`dispose()` runs
701
716
  * them backwards). Assert on `.length` to prove a phase registered teardown
@@ -712,7 +727,10 @@ interface TestModuleSetup {
712
727
  */
713
728
  dispose(): Promise<void>;
714
729
  }
715
- declare function createTestModuleSetup(): TestModuleSetup;
730
+ declare function createTestModuleSetup(options?: {
731
+ /** The `context.ports` the phases see — supply what the module declares. */
732
+ readonly ports?: ArcPorts;
733
+ }): TestModuleSetup;
716
734
  //#endregion
717
735
  //#region src/testing/scopeRoleGate.d.ts
718
736
  /**
@@ -816,4 +834,4 @@ declare function createTestApp(options?: CreateTestAppOptions): Promise<TestAppC
816
834
  */
817
835
  declare function createMinimalTestApp(options?: FastifyServerOptions): FastifyInstance;
818
836
  //#endregion
819
- export { type ArcAssertion, type ArcResponseLike, type AuthMode, type AuthResponse, type AuthorizationConformance, type BetterAuthTestHelpers, type BetterAuthTestHelpersOptions, type BetterAuthTestUser, type BootModuleAppOptions, type BootModulesInput, type CreateOrgInput, type CreateTestAppOptions, type DbMode, type FixtureDestroyer, type FixtureFactory, type FixtureRegistration, HttpTestHarness, type HttpTestHarnessOptions, type MockRepository, type ModuleTestApp, type OrgResponse, type RoleConfig, type SetupBetterAuthTestAppInput, type SetupBetterAuthTestAppResult, type SignInInput, type SignUpInput, type StorageContractSetup, type StorageContractSetupResult, type SurfaceObservation, TEST_ACTOR_HEADER, type TestActor, type TestAppContext, type TestAuthProvider, type TestAuthSession, type TestDatabase, type TestDatabaseFactory, type TestFixtures, type TestModuleSetup, type TestkitContext, bootModuleApp, createBetterAuthProvider, createBetterAuthTestHelpers, createCustomAuthProvider, createDataFactory, createHttpTestHarness, createJwtAuthProvider, createMinimalTestApp, createMockController, createMockReply, createMockRepository, createMockRequest, createMockUser, createSpy, createTestApp, createTestFixtures, createTestModuleSetup, createTestTimer, expectArc, mongoMemoryDatabase, preloadResources, preloadResourcesAsync, runAuthorizationConformance, runStorageContract, safeParseBody, scopeFromTestActor, scopeRoleGate, setupBetterAuthTestApp, testActorAuth, testActorHeaders, waitFor };
837
+ export { type ArcAssertion, type ArcResponseLike, type AuthMode, type AuthResponse, type AuthorizationConformance, type BetterAuthTestHelpers, type BetterAuthTestHelpersOptions, type BetterAuthTestUser, type BootModuleAppOptions, type BootModulesInput, type CreateOrgInput, type CreateTestAppOptions, type DbMode, type FixtureDestroyer, type FixtureFactory, type FixtureRegistration, HttpTestHarness, type HttpTestHarnessOptions, type MockRepository, type ModuleBootsAloneOptions, type ModuleBootsAloneResult, type ModuleTestApp, type OrgResponse, type RoleConfig, type SetupBetterAuthTestAppInput, type SetupBetterAuthTestAppResult, type SignInInput, type SignUpInput, type StorageContractSetup, type StorageContractSetupResult, type SurfaceObservation, TEST_ACTOR_HEADER, type TestActor, type TestAppContext, type TestAuthProvider, type TestAuthSession, type TestDatabase, type TestDatabaseFactory, type TestFixtures, type TestModuleSetup, type TestkitContext, assertModuleBootsAlone, bootModuleApp, createBetterAuthProvider, createBetterAuthTestHelpers, createCustomAuthProvider, createDataFactory, createHttpTestHarness, createJwtAuthProvider, createMinimalTestApp, createMockController, createMockReply, createMockRepository, createMockRequest, createMockUser, createSpy, createTestApp, createTestFixtures, createTestModuleSetup, createTestTimer, expectArc, mongoMemoryDatabase, runAuthorizationConformance, runStorageContract, safeParseBody, scopeFromTestActor, scopeRoleGate, setupBetterAuthTestApp, testActorAuth, testActorHeaders, waitFor };