@classytic/arc 2.31.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 (151) hide show
  1. package/dist/{BaseController-De7GqHck.mjs → BaseController-Dk34dxhI.mjs} +16 -13
  2. package/dist/{EventTransport-9mlTgqzE.mjs → EventTransport-BV10D0oG.mjs} +1 -1
  3. package/dist/{HookSystem-BcmPyq5I.mjs → HookSystem-vimw5D7Z.mjs} +2 -2
  4. package/dist/{ResourceRegistry-qq0mn2s9.mjs → ResourceRegistry-BVr7PgA0.mjs} +17 -15
  5. package/dist/{actionPermissions-BE5NwaQk.mjs → actionPermissions-Dti7ebyv.mjs} +1 -1
  6. package/dist/audit/index.mjs +2 -2
  7. package/dist/auth/audit.mjs +1 -1
  8. package/dist/auth/index.mjs +11 -10
  9. package/dist/auth/redis-session.mjs +1 -1
  10. package/dist/{authorizationDecision-ChvUQGG1.mjs → authorizationDecision-__T3EJDA.mjs} +2 -2
  11. package/dist/{buildHandler-CDrCJzmB.mjs → buildHandler-Dn7rzUJo.mjs} +6 -5
  12. package/dist/cache/index.mjs +4 -4
  13. package/dist/{caching-BFHA6Scf.mjs → caching-DFJqHyFs.mjs} +2 -2
  14. package/dist/cleanup/index.d.mts +36 -14
  15. package/dist/cleanup/index.mjs +45 -29
  16. package/dist/cli/commands/describe.mjs +4 -6
  17. package/dist/cli/commands/docs.mjs +3 -3
  18. package/dist/cli/commands/doctor.mjs +1 -1
  19. package/dist/cli/commands/generate.mjs +2 -2
  20. package/dist/cli/commands/init.mjs +2 -2
  21. package/dist/cli/commands/introspect.mjs +2 -2
  22. package/dist/cli/index.mjs +3 -3
  23. package/dist/{constants-D3Dv4LAV.mjs → constants-J99LGTJI.mjs} +1 -1
  24. package/dist/context/index.mjs +3 -3
  25. package/dist/core/index.d.mts +1 -1
  26. package/dist/core/index.mjs +7 -7
  27. package/dist/{core-BGpIxINN.mjs → core-BwwmNSp7.mjs} +3 -3
  28. package/dist/{createActionRouter-CDXEdasl.mjs → createActionRouter-DkJZWo0m.mjs} +14 -11
  29. package/dist/{createAggregationRouter-KrQDzx-k.mjs → createAggregationRouter-B1exOVD8.mjs} +10 -8
  30. package/dist/{createApp-Df89BEni.mjs → createApp-Bv0m-Q6_.mjs} +182 -61
  31. package/dist/{defineAggregation-DbPPS8RI.mjs → defineAggregation-gX4No1gt.mjs} +1 -1
  32. package/dist/{defineResource-Bv6EtN1x.mjs → defineResource-XFPPUu2c.mjs} +41 -32
  33. package/dist/discovery/index.mjs +1 -1
  34. package/dist/docs/index.mjs +3 -3
  35. package/dist/{elevation-D2l1mh4g.mjs → elevation-DNXXxgjl.mjs} +4 -4
  36. package/dist/encryption/index.mjs +5 -4
  37. package/dist/{errorHandler-C5NeD6bT.mjs → errorHandler-B2AZUJUj.mjs} +3 -3
  38. package/dist/{errors-Cj42AntO.mjs → errors-Bh1KK5ps.mjs} +1 -1
  39. package/dist/{eventPlugin-CkffEihm.mjs → eventPlugin-Dk_TyXeE.mjs} +8 -7
  40. package/dist/events/index.d.mts +108 -41
  41. package/dist/events/index.mjs +166 -142
  42. package/dist/events/transports/redis-stream-entry.mjs +14 -10
  43. package/dist/events/transports/redis.mjs +1 -1
  44. package/dist/factory/index.d.mts +5 -5
  45. package/dist/factory/index.mjs +131 -8
  46. package/dist/fastify-augmentation-BppG5bMT.d.mts +179 -0
  47. package/dist/{fields-CB5Cuzk9.mjs → fields-DEi65pGj.mjs} +6 -11
  48. package/dist/{generate-EcA6ui4i.mjs → generate-CRSG0jTv.mjs} +2 -2
  49. package/dist/{headers-3zvE0rM3.mjs → headers-BXjQYbeC.mjs} +2 -2
  50. package/dist/{health-DjZYjM7H.mjs → health-5XnTa2uo.mjs} +1 -1
  51. package/dist/hooks/index.mjs +2 -2
  52. package/dist/idempotency/index.mjs +8 -6
  53. package/dist/idempotency/redis.mjs +1 -1
  54. package/dist/{index-Bc2ox8_U.d.mts → index-DXlHwvuE.d.mts} +1 -1
  55. package/dist/{index-CFThn1K9.d.mts → index-ZWhLTp_s.d.mts} +70 -4
  56. package/dist/{index-CEJNZFtX.d.mts → index-zq6VrjaB.d.mts} +70 -1
  57. package/dist/index.d.mts +3 -3
  58. package/dist/index.mjs +11 -11
  59. package/dist/{init-BEiNZBsJ.mjs → init-BbfP8aky.mjs} +7 -5
  60. package/dist/integrations/event-gateway.mjs +2 -2
  61. package/dist/integrations/index.mjs +1 -1
  62. package/dist/integrations/jobs/index.mjs +3 -3
  63. package/dist/integrations/mcp/index.mjs +2 -2
  64. package/dist/integrations/mcp/testing.mjs +2 -2
  65. package/dist/integrations/streamline.mjs +3 -3
  66. package/dist/integrations/webhooks.mjs +1 -1
  67. package/dist/integrations/websocket-pushref-redis.mjs +1 -1
  68. package/dist/integrations/websocket-redis.mjs +1 -1
  69. package/dist/integrations/websocket.mjs +38 -35
  70. package/dist/{interface-DYzM0q-u.mjs → interface-BP4IJCBO.mjs} +1 -1
  71. package/dist/{jsonBody-BYbLDi5D.mjs → jsonBody-DDSR2HFl.mjs} +2 -2
  72. package/dist/{keys-C0_pa1tY.mjs → keys-B-HBTGXl.mjs} +1 -1
  73. package/dist/{loadResources-yawi9tAE.mjs → loadResources-DisEAmkm.mjs} +4 -3
  74. package/dist/{loadResourcesFromEntry-BAjYZonx.mjs → loadResourcesFromEntry-D1MJp13f.mjs} +2 -2
  75. package/dist/logger/index.mjs +1 -1
  76. package/dist/{mcpPlugin-B_es0hiF.mjs → mcpPlugin-CpfVpKHE.mjs} +32 -31
  77. package/dist/{memory-DR6wpa9F.mjs → memory-DsuWnj4T.mjs} +5 -5
  78. package/dist/{metrics-CUdw_wLB.mjs → metrics-DdQL8syd.mjs} +2 -2
  79. package/dist/middleware/index.mjs +3 -3
  80. package/dist/migrations/index.mjs +3 -3
  81. package/dist/{module-0pFJA6YW.mjs → module-Br81am6m.mjs} +1 -1
  82. package/dist/{multipartBody-wOgj_fnj.mjs → multipartBody-BnkBLN7v.mjs} +3 -3
  83. package/dist/{openapi-B2L4js1v.mjs → openapi-DIRzvxC0.mjs} +6 -6
  84. package/dist/outbox-admin/index.d.mts +85 -0
  85. package/dist/outbox-admin/index.mjs +115 -0
  86. package/dist/permissions/index.d.mts +2 -2
  87. package/dist/permissions/index.mjs +6 -6
  88. package/dist/{permissions-L--QDAir.mjs → permissions-Dl_m0NCe.mjs} +101 -8
  89. package/dist/{pipe-DRDNsI2T.mjs → pipe-ELHCP3zj.mjs} +3 -5
  90. package/dist/pipeline/index.mjs +2 -2
  91. package/dist/{pipeline-UPQFatit.mjs → pipeline-CPA-jx0M.mjs} +17 -15
  92. package/dist/plugins/index.d.mts +2 -2
  93. package/dist/plugins/index.mjs +16 -16
  94. package/dist/plugins/response-cache.mjs +2 -2
  95. package/dist/plugins/tracing-entry.mjs +2 -2
  96. package/dist/{pluralize-O0FZgESl.mjs → pluralize-C2ZkKbsn.mjs} +1 -1
  97. package/dist/{preloadResources-Dz6BjzM6.mjs → preloadResources-CDs87fTw.mjs} +1 -1
  98. package/dist/{preloadResources-CyoJMSiO.d.mts → preloadResources-T1hmlTEZ.d.mts} +2 -2
  99. package/dist/presets/filesUpload.mjs +6 -6
  100. package/dist/presets/index.mjs +2 -2
  101. package/dist/presets/multiTenant.mjs +3 -3
  102. package/dist/presets/search.mjs +2 -2
  103. package/dist/{presets-C4-g4_4P.mjs → presets-Dntd_pUO.mjs} +6 -5
  104. package/dist/{projection-BHw6xlOQ.mjs → projection-CjHhM5W1.mjs} +2 -2
  105. package/dist/{queryCachePlugin-BZr8v46E.mjs → queryCachePlugin-Dhps-Ldt.mjs} +6 -6
  106. package/dist/{rateLimit-CMPY5Qms.mjs → rateLimit-C7mmAGs3.mjs} +2 -2
  107. package/dist/{rateLimitKey-Dn5JNmkj.mjs → rateLimitKey-CccEFBfk.mjs} +1 -1
  108. package/dist/{realtime-C5NoxWFY.mjs → realtime-DVvVZZFz.mjs} +10 -10
  109. package/dist/{registerRoute-CBN84fc7.mjs → registerRoute-B_QrjYZS.mjs} +10 -8
  110. package/dist/registry/index.d.mts +1 -1
  111. package/dist/registry/index.mjs +3 -3
  112. package/dist/{registry-4213U4zb.mjs → registry-DqUmzA9W.mjs} +2 -2
  113. package/dist/{renewingLease-Dll8mGgc.mjs → renewingLease-BpP9wwj7.mjs} +1 -1
  114. package/dist/{replyHelpers-DIiZKGUt.mjs → replyHelpers-DS-P0b32.mjs} +2 -2
  115. package/dist/{repositoryFilter-f9eAfq_l.mjs → repositoryFilter-DotlxG3L.mjs} +1 -1
  116. package/dist/{requestContext-2TxXOJ7T.mjs → requestContext-B6vQF-sy.mjs} +1 -1
  117. package/dist/{rolldown-runtime-CUsjNX9R.mjs → rolldown-runtime-BhflRrBs.mjs} +1 -1
  118. package/dist/{schedules-DdtlICTf.mjs → schedules-C2Q3uv2d.mjs} +3 -3
  119. package/dist/{schemaConverter-D--wdJM-.mjs → schemaConverter-Crjvczi_.mjs} +1 -1
  120. package/dist/{schemaIR-Bm2enbnh.mjs → schemaIR-CnyKK_1j.mjs} +2 -2
  121. package/dist/schemas/index.mjs +1 -1
  122. package/dist/scim/index.mjs +2 -2
  123. package/dist/scope/index.mjs +5 -5
  124. package/dist/{scope-DHx5Fous.mjs → scope-afz_Ieek.mjs} +4 -4
  125. package/dist/{simpleEqualityMatcher-DjQJ7FKA.mjs → simpleEqualityMatcher-C4KiMWUa.mjs} +1 -1
  126. package/dist/{sse-CUTo9y0X.mjs → sse-B9P4Y-UI.mjs} +7 -6
  127. package/dist/{sseStream-B_5XcVVd.mjs → sseStream-B3zb_d1h.mjs} +2 -2
  128. package/dist/{store-helpers-D1MP_gRG.mjs → store-helpers-DmEMHlPQ.mjs} +1 -1
  129. package/dist/{streaming-PSZwi8JV.mjs → streaming-DPZdQmDz.mjs} +1 -1
  130. package/dist/{subscribe-helpers-C0Tb2aUp.mjs → subscribe-helpers-sp5xTDVA.mjs} +2 -2
  131. package/dist/sync/changelog.mjs +1 -1
  132. package/dist/testing/cleanupStoreContract.mjs +10 -6
  133. package/dist/testing/index.d.mts +31 -4
  134. package/dist/testing/index.mjs +97 -39
  135. package/dist/testing/outboxStoreContract.d.mts +11 -2
  136. package/dist/testing/outboxStoreContract.mjs +110 -21
  137. package/dist/testing/storageContract.mjs +11 -6
  138. package/dist/{transactionContext-dAfBVUrk.mjs → transactionContext-BvLKvOIL.mjs} +1 -1
  139. package/dist/{typeGuards-B0ryPo3I.mjs → typeGuards-B_5-hKjJ.mjs} +1 -1
  140. package/dist/types/index.mjs +1 -1
  141. package/dist/types/storage.mjs +1 -1
  142. package/dist/{types-CBgvkp8O.d.mts → types-CT5vu6M0.d.mts} +87 -11
  143. package/dist/{types-Cys0ko9Z.mjs → types-DXYX7dIR.mjs} +4 -4
  144. package/dist/usage/index.mjs +0 -0
  145. package/dist/{userHelpers-ChYJ6CdK.mjs → userHelpers-BNsOXzOd.mjs} +1 -1
  146. package/dist/utils/index.mjs +10 -10
  147. package/dist/{utils-vL8BrDE2.mjs → utils-BtOha_Sp.mjs} +3 -3
  148. package/dist/{validate-_KbifpHF.mjs → validate-PMCPR9El.mjs} +2 -2
  149. package/dist/{versioning-CHDTor3P.mjs → versioning-CKv2jz_U.mjs} +4 -4
  150. package/package.json +23 -13
  151. package/dist/fastify-augmentation-zh0_AYHa.d.mts +0 -85
@@ -1,4 +1,4 @@
1
- /*! @classytic/arc v2.31.0 | MIT | https://classytic.com */
1
+ /*! @classytic/arc v2.32.0 | MIT | https://classytic.com */
2
2
  import "node:module";
3
3
  //#region \0rolldown/runtime.js
4
4
  var __defProp = Object.defineProperty;
@@ -1,6 +1,6 @@
1
- /*! @classytic/arc v2.31.0 | MIT | https://classytic.com */
2
- import { t as __exportAll } from "./rolldown-runtime-CUsjNX9R.mjs";
3
- import { t as startRenewingLease } from "./renewingLease-Dll8mGgc.mjs";
1
+ /*! @classytic/arc v2.32.0 | MIT | https://classytic.com */
2
+ import { t as __exportAll } from "./rolldown-runtime-BhflRrBs.mjs";
3
+ import { t as startRenewingLease } from "./renewingLease-BpP9wwj7.mjs";
4
4
  import fp from "fastify-plugin";
5
5
  //#region src/plugins/schedules.ts
6
6
  var schedules_exports = /* @__PURE__ */ __exportAll({
@@ -1,4 +1,4 @@
1
- /*! @classytic/arc v2.31.0 | MIT | https://classytic.com */
1
+ /*! @classytic/arc v2.32.0 | MIT | https://classytic.com */
2
2
  import { arcLog } from "./logger/index.mjs";
3
3
  //#region src/utils/schemaConverter.ts
4
4
  /**
@@ -1,5 +1,5 @@
1
- /*! @classytic/arc v2.31.0 | MIT | https://classytic.com */
2
- import { o as toJsonSchema } from "./schemaConverter-D--wdJM-.mjs";
1
+ /*! @classytic/arc v2.32.0 | MIT | https://classytic.com */
2
+ import { o as toJsonSchema } from "./schemaConverter-Crjvczi_.mjs";
3
3
  //#region src/core/schemaIR.ts
4
4
  /**
5
5
  * Schema IR — one canonical representation, two adapters.
@@ -1,4 +1,4 @@
1
- /*! @classytic/arc v2.31.0 | MIT | https://classytic.com */
1
+ /*! @classytic/arc v2.32.0 | MIT | https://classytic.com */
2
2
  import { errorContractSchema, errorDetailSchema } from "@classytic/repo-core/errors";
3
3
  import { Type, Type as Type$1 } from "typebox";
4
4
  //#region src/schemas/index.ts
@@ -1,5 +1,5 @@
1
- /*! @classytic/arc v2.31.0 | MIT | https://classytic.com */
2
- import { t as parseJsonBody } from "../jsonBody-BYbLDi5D.mjs";
1
+ /*! @classytic/arc v2.32.0 | MIT | https://classytic.com */
2
+ import { t as parseJsonBody } from "../jsonBody-DDSR2HFl.mjs";
3
3
  import fp from "fastify-plugin";
4
4
  //#region src/scim/errors.ts
5
5
  var ScimError = class extends Error {
@@ -1,6 +1,6 @@
1
- /*! @classytic/arc v2.31.0 | MIT | https://classytic.com */
2
- import { C as isService, D as requireUserId, E as requireTeamId, O as scopeFirstCtx, S as isOrgInScope, T as requireOrgId, _ as getUserRoles, a as getDPoPJkt, b as isElevated, c as getOrgId, d as getScopeContext, f as getScopeContextMap, g as getUserId, h as getTenantFromRequest, i as getClientId, l as getOrgRoles, m as getTeamId, n as PUBLIC_SCOPE, o as getMandate, p as getServiceScopes, r as getAncestorOrgIds, s as getOrgContext, t as AUTHENTICATED_SCOPE, u as getRequestScope, v as hasOrgAccess, w as requireClientId, x as isMember, y as isAuthenticated } from "../types-Cys0ko9Z.mjs";
3
- import { n as elevation_default, t as elevationPlugin } from "../elevation-D2l1mh4g.mjs";
4
- import { t as createTenantKeyGenerator } from "../rateLimitKey-Dn5JNmkj.mjs";
5
- import { t as resolveOrgFromHeader } from "../scope-DHx5Fous.mjs";
1
+ /*! @classytic/arc v2.32.0 | MIT | https://classytic.com */
2
+ import { C as isService, D as requireUserId, E as requireTeamId, O as scopeFirstCtx, S as isOrgInScope, T as requireOrgId, _ as getUserRoles, a as getDPoPJkt, b as isElevated, c as getOrgId, d as getScopeContext, f as getScopeContextMap, g as getUserId, h as getTenantFromRequest, i as getClientId, l as getOrgRoles, m as getTeamId, n as PUBLIC_SCOPE, o as getMandate, p as getServiceScopes, r as getAncestorOrgIds, s as getOrgContext, t as AUTHENTICATED_SCOPE, u as getRequestScope, v as hasOrgAccess, w as requireClientId, x as isMember, y as isAuthenticated } from "../types-DXYX7dIR.mjs";
3
+ import { n as elevation_default, t as elevationPlugin } from "../elevation-DNXXxgjl.mjs";
4
+ import { t as createTenantKeyGenerator } from "../rateLimitKey-CccEFBfk.mjs";
5
+ import { t as resolveOrgFromHeader } from "../scope-afz_Ieek.mjs";
6
6
  export { AUTHENTICATED_SCOPE, PUBLIC_SCOPE, createTenantKeyGenerator, elevation_default as elevationPlugin, elevationPlugin as elevationPluginFn, getAncestorOrgIds, getClientId, getDPoPJkt, getMandate, getOrgContext, getOrgId, getOrgRoles, getRequestScope, getScopeContext, getScopeContextMap, getServiceScopes, getTeamId, getTenantFromRequest, getUserId, getUserRoles, hasOrgAccess, isAuthenticated, isElevated, isMember, isOrgInScope, isService, requireClientId, requireOrgId, requireTeamId, requireUserId, resolveOrgFromHeader, scopeFirstCtx };
@@ -1,7 +1,7 @@
1
- /*! @classytic/arc v2.31.0 | MIT | https://classytic.com */
2
- import { a as OrgAccessDeniedError, l as UnauthorizedError } from "./errors-Cj42AntO.mjs";
3
- import { n as requireSingleHeaderValue } from "./headers-3zvE0rM3.mjs";
4
- import { n as getUserRoles } from "./userHelpers-ChYJ6CdK.mjs";
1
+ /*! @classytic/arc v2.32.0 | MIT | https://classytic.com */
2
+ import { a as OrgAccessDeniedError, l as UnauthorizedError } from "./errors-Bh1KK5ps.mjs";
3
+ import { n as requireSingleHeaderValue } from "./headers-BXjQYbeC.mjs";
4
+ import { n as getUserRoles } from "./userHelpers-BNsOXzOd.mjs";
5
5
  //#region src/scope/resolveOrgFromHeader.ts
6
6
  /**
7
7
  * Create a preHandler hook that resolves org scope from a header.
@@ -1,4 +1,4 @@
1
- /*! @classytic/arc v2.31.0 | MIT | https://classytic.com */
1
+ /*! @classytic/arc v2.32.0 | MIT | https://classytic.com */
2
2
  //#region src/utils/simpleEqualityMatcher.ts
3
3
  /**
4
4
  * `simpleEqualityMatcher` — a minimal, dialect-agnostic flat-key equality
@@ -1,9 +1,9 @@
1
- /*! @classytic/arc v2.31.0 | MIT | https://classytic.com */
2
- import { t as __exportAll } from "./rolldown-runtime-CUsjNX9R.mjs";
1
+ /*! @classytic/arc v2.32.0 | MIT | https://classytic.com */
2
+ import { t as __exportAll } from "./rolldown-runtime-BhflRrBs.mjs";
3
3
  import { arcLog } from "./logger/index.mjs";
4
- import { c as getOrgId, n as PUBLIC_SCOPE } from "./types-Cys0ko9Z.mjs";
5
- import { a as promoteStreamTokenToHeader } from "./streaming-PSZwi8JV.mjs";
6
- import { t as openSseStream } from "./sseStream-B_5XcVVd.mjs";
4
+ import { c as getOrgId, n as PUBLIC_SCOPE } from "./types-DXYX7dIR.mjs";
5
+ import { a as promoteStreamTokenToHeader } from "./streaming-DPZdQmDz.mjs";
6
+ import { t as openSseStream } from "./sseStream-B3zb_d1h.mjs";
7
7
  import fp from "fastify-plugin";
8
8
  //#region src/plugins/sse.ts
9
9
  var sse_exports = /* @__PURE__ */ __exportAll({
@@ -33,7 +33,8 @@ const ssePlugin = async (fastify, opts = {}) => {
33
33
  handler: async (request, reply) => {
34
34
  const stream = openSseStream(request, reply, { heartbeatMs: heartbeat });
35
35
  stream.onCleanup(() => activeConnections.delete(stream.close));
36
- const requestOrgId = getOrgId(request.scope ?? PUBLIC_SCOPE);
36
+ const scope = request.scope ?? PUBLIC_SCOPE;
37
+ const requestOrgId = getOrgId(scope);
37
38
  const dropOrgEvents = orgScoped && !requestOrgId;
38
39
  for (const pattern of patterns) {
39
40
  const unsub = await fastify.events.subscribe(pattern, async (event) => {
@@ -1,5 +1,5 @@
1
- /*! @classytic/arc v2.31.0 | MIT | https://classytic.com */
2
- import { n as forwardedStreamHeaders } from "./streaming-PSZwi8JV.mjs";
1
+ /*! @classytic/arc v2.32.0 | MIT | https://classytic.com */
2
+ import { n as forwardedStreamHeaders } from "./streaming-DPZdQmDz.mjs";
3
3
  //#region src/utils/sseStream.ts
4
4
  /**
5
5
  * Take over the reply socket and open an SSE stream. Call ONLY after every
@@ -1,4 +1,4 @@
1
- /*! @classytic/arc v2.31.0 | MIT | https://classytic.com */
1
+ /*! @classytic/arc v2.32.0 | MIT | https://classytic.com */
2
2
  //#region src/utils/store-helpers.ts
3
3
  /**
4
4
  * Classify an error thrown by `getOne` / `getById` / `update` as a
@@ -1,4 +1,4 @@
1
- /*! @classytic/arc v2.31.0 | MIT | https://classytic.com */
1
+ /*! @classytic/arc v2.32.0 | MIT | https://classytic.com */
2
2
  import { Readable } from "node:stream";
3
3
  //#region src/utils/streaming.ts
4
4
  /**
@@ -1,5 +1,5 @@
1
- /*! @classytic/arc v2.31.0 | MIT | https://classytic.com */
2
- import { n as createEvent } from "./EventTransport-9mlTgqzE.mjs";
1
+ /*! @classytic/arc v2.32.0 | MIT | https://classytic.com */
2
+ import { n as createEvent } from "./EventTransport-BV10D0oG.mjs";
3
3
  //#region src/events/defineEvent.ts
4
4
  /**
5
5
  * defineEvent — Typed Event Definitions with Optional Schema Validation
@@ -1,3 +1,3 @@
1
- /*! @classytic/arc v2.31.0 | MIT | https://classytic.com */
1
+ /*! @classytic/arc v2.32.0 | MIT | https://classytic.com */
2
2
  import { CursorExpiredError, MemoryChangeLogStore } from "@classytic/repo-core/sync";
3
3
  export { CursorExpiredError, MemoryChangeLogStore };
@@ -1,4 +1,4 @@
1
- /*! @classytic/arc v2.31.0 | MIT | https://classytic.com */
1
+ /*! @classytic/arc v2.32.0 | MIT | https://classytic.com */
2
2
  import { afterAll, beforeAll, describe, expect, it } from "vitest";
3
3
  //#region src/testing/cleanupStoreContract.ts
4
4
  /**
@@ -152,10 +152,11 @@ function runCleanupRunStoreContract(name, setup) {
152
152
  expiresAt: future()
153
153
  });
154
154
  await store.compareAndTransition(run.id, ["queued"], "running");
155
- expect(await store.claim(run.id, {
155
+ const stolen = await store.claim(run.id, {
156
156
  token: uid("thief"),
157
157
  expiresAt: future()
158
- })).toBeNull();
158
+ });
159
+ expect(stolen).toBeNull();
159
160
  });
160
161
  it("recovers a running run whose lease EXPIRED (crash recovery)", async () => {
161
162
  const run = makeRun(uid("crashed"), { concurrencyKey: void 0 });
@@ -234,9 +235,11 @@ function runCleanupRunStoreContract(name, setup) {
234
235
  token: live,
235
236
  expiresAt: future()
236
237
  });
237
- expect(await store.compareAndTransition(run.id, ["running"], "completed", { completedAt: /* @__PURE__ */ new Date() }, "a-stale-token")).toBeNull();
238
+ const clobbered = await store.compareAndTransition(run.id, ["running"], "completed", { completedAt: /* @__PURE__ */ new Date() }, "a-stale-token");
239
+ expect(clobbered).toBeNull();
238
240
  expect((await store.get(run.id))?.status).toBe("running");
239
- expect((await store.compareAndTransition(run.id, ["running"], "completed", void 0, live))?.status).toBe("completed");
241
+ const ok = await store.compareAndTransition(run.id, ["running"], "completed", void 0, live);
242
+ expect(ok?.status).toBe("completed");
240
243
  });
241
244
  });
242
245
  describe("requestCancel — durable cancellation flag", () => {
@@ -353,7 +356,8 @@ function runCleanupRunStoreContract(name, setup) {
353
356
  const run = makeRun(uid("rearm-cancelled"), { concurrencyKey: void 0 });
354
357
  await store.createIfPermitted(run);
355
358
  await store.compareAndTransition(run.id, ["queued"], "cancelled");
356
- expect((await store.reArmIfPermitted(run.id, { queuedAt: /* @__PURE__ */ new Date() }))?.status).toBe("queued");
359
+ const requeued = await store.reArmIfPermitted(run.id, { queuedAt: /* @__PURE__ */ new Date() });
360
+ expect(requeued?.status).toBe("queued");
357
361
  });
358
362
  it("refuses a run that is not in a terminal status", async () => {
359
363
  const run = makeRun(uid("rearm-live"), { concurrencyKey: void 0 });
@@ -2,9 +2,9 @@
2
2
  import { V as ResourceDefinition } from "../index-CpzxqePp.mjs";
3
3
  import { i as RequestScope } from "../types-ChuShpCP.mjs";
4
4
  import { l as AnyRecord, r as PermissionCheck } from "../types-B7XeLEgK.mjs";
5
- import { n as ArcModuleInput } from "../types-CBgvkp8O.mjs";
6
- import "../index-CFThn1K9.mjs";
7
- import { n as preloadResourcesAsync, r as CreateAppOptions, t as preloadResources } from "../preloadResources-CyoJMSiO.mjs";
5
+ import { n as ArcModuleInput, o as ModuleDisposer, s as ModuleSetupContext } from "../types-CT5vu6M0.mjs";
6
+ import "../index-ZWhLTp_s.mjs";
7
+ import { n as preloadResourcesAsync, r as CreateAppOptions, t as preloadResources } from "../preloadResources-T1hmlTEZ.mjs";
8
8
  import { StorageContractSetup, StorageContractSetupResult, runStorageContract } from "./storageContract.mjs";
9
9
  import { FastifyInstance, FastifyServerOptions } from "fastify";
10
10
  import { Mock } from "vitest";
@@ -91,6 +91,11 @@ interface AuthorizationConformance {
91
91
  * `buildIdFilter` output (route id AND policy AND tenant), so a policy that
92
92
  * tried to redirect/widen the target would show here — the surface the
93
93
  * filter-overwrite class of bug lives on.
94
+ *
95
+ * `filter` is `undefined` when the permission carries NO policy: `fetchDetailed`
96
+ * only builds a compound filter when there is something to compound, and takes a
97
+ * plain by-id read otherwise. `status` is meaningful on both paths — assert the
98
+ * filter only when the permission under test emits a policy.
94
99
  */
95
100
  get: SurfaceObservation;
96
101
  /** `GET /confs/aggregations/tally` — the aggregation surface. */
@@ -675,6 +680,28 @@ declare function createTestTimer(): {
675
680
  reset: () => void;
676
681
  };
677
682
  //#endregion
683
+ //#region src/testing/moduleSetup.d.ts
684
+ interface TestModuleSetup {
685
+ /** Pass this as the second argument to `bootstrap` / `plugins` / `afterResources`. */
686
+ readonly context: ModuleSetupContext;
687
+ /**
688
+ * Disposers the module registered, in REGISTRATION order (`dispose()` runs
689
+ * them backwards). Assert on `.length` to prove a phase registered teardown
690
+ * at all — the check a no-op `defer` can never fail.
691
+ */
692
+ readonly deferred: readonly ModuleDisposer[];
693
+ /**
694
+ * Run every collected disposer LIFO, then clear them.
695
+ *
696
+ * Best-effort like arc's teardown: one throwing disposer never blocks the
697
+ * ones behind it. Any errors are collected and thrown together at the end, so
698
+ * a test still fails loudly instead of a rejection vanishing into a floating
699
+ * promise.
700
+ */
701
+ dispose(): Promise<void>;
702
+ }
703
+ declare function createTestModuleSetup(): TestModuleSetup;
704
+ //#endregion
678
705
  //#region src/testing/scopeRoleGate.d.ts
679
706
  /**
680
707
  * Grants iff the caller is an org member holding one of `allowed` as an ORG role.
@@ -777,4 +804,4 @@ declare function createTestApp(options?: CreateTestAppOptions): Promise<TestAppC
777
804
  */
778
805
  declare function createMinimalTestApp(options?: FastifyServerOptions): FastifyInstance;
779
806
  //#endregion
780
- 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 TestkitContext, bootModuleApp, createBetterAuthProvider, createBetterAuthTestHelpers, createCustomAuthProvider, createDataFactory, createHttpTestHarness, createJwtAuthProvider, createMinimalTestApp, createMockController, createMockReply, createMockRepository, createMockRequest, createMockUser, createSpy, createTestApp, createTestFixtures, createTestTimer, expectArc, mongoMemoryDatabase, preloadResources, preloadResourcesAsync, runAuthorizationConformance, runStorageContract, safeParseBody, scopeFromTestActor, scopeRoleGate, setupBetterAuthTestApp, testActorAuth, testActorHeaders, waitFor };
807
+ 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 };
@@ -1,13 +1,13 @@
1
- /*! @classytic/arc v2.31.0 | MIT | https://classytic.com */
2
- import { t as CRUD_OPERATIONS } from "../constants-D3Dv4LAV.mjs";
3
- import { g as getUserId } from "../types-Cys0ko9Z.mjs";
4
- import { t as BaseController } from "../BaseController-De7GqHck.mjs";
5
- import { t as defineResource } from "../defineResource-Bv6EtN1x.mjs";
6
- import { t as defineAggregation } from "../defineAggregation-DbPPS8RI.mjs";
7
- import { S as requireOrgRole } from "../permissions-L--QDAir.mjs";
8
- import { n as createApp } from "../createApp-Df89BEni.mjs";
9
- import { n as getModuleExports } from "../module-0pFJA6YW.mjs";
10
- import { n as preloadResourcesAsync, t as preloadResources } from "../preloadResources-Dz6BjzM6.mjs";
1
+ /*! @classytic/arc v2.32.0 | MIT | https://classytic.com */
2
+ import { t as CRUD_OPERATIONS } from "../constants-J99LGTJI.mjs";
3
+ import { g as getUserId } from "../types-DXYX7dIR.mjs";
4
+ import { t as BaseController } from "../BaseController-Dk34dxhI.mjs";
5
+ import { t as defineResource } from "../defineResource-XFPPUu2c.mjs";
6
+ import { t as defineAggregation } from "../defineAggregation-gX4No1gt.mjs";
7
+ import { E as requireOrgRole } from "../permissions-Dl_m0NCe.mjs";
8
+ import { n as createApp } from "../createApp-Bv0m-Q6_.mjs";
9
+ import { n as getModuleExports } from "../module-Br81am6m.mjs";
10
+ import { n as preloadResourcesAsync, t as preloadResources } from "../preloadResources-CDs87fTw.mjs";
11
11
  import { runStorageContract } from "./storageContract.mjs";
12
12
  import Fastify from "fastify";
13
13
  import { afterAll, describe, expect, it, vi } from "vitest";
@@ -64,7 +64,8 @@ function expectArc(response) {
64
64
  failed(status) {
65
65
  if (status !== void 0) expect(response.statusCode).toBe(status);
66
66
  else expect(response.statusCode).toBeGreaterThanOrEqual(400);
67
- expect(getBody().code, "expected ErrorContract.code on failed response").toBeDefined();
67
+ const body = getBody();
68
+ expect(body.code, "expected ErrorContract.code on failed response").toBeDefined();
68
69
  return assertion;
69
70
  },
70
71
  unauthorized() {
@@ -84,7 +85,8 @@ function expectArc(response) {
84
85
  },
85
86
  hasData() {
86
87
  const body = getBody();
87
- expect(Object.keys(body).length > 0, "expected response body to be defined").toBe(true);
88
+ const hasContent = Object.keys(body).length > 0;
89
+ expect(hasContent, "expected response body to be defined").toBe(true);
88
90
  return assertion;
89
91
  },
90
92
  hasStatus(status) {
@@ -92,11 +94,13 @@ function expectArc(response) {
92
94
  return assertion;
93
95
  },
94
96
  hidesField(field) {
95
- expect(getBody(), `expected field '${field}' to be hidden from response`).not.toHaveProperty(field);
97
+ const body = getBody();
98
+ expect(body, `expected field '${field}' to be hidden from response`).not.toHaveProperty(field);
96
99
  return assertion;
97
100
  },
98
101
  showsField(field) {
99
- expect(getBody(), `expected field '${field}' on response`).toHaveProperty(field);
102
+ const body = getBody();
103
+ expect(body, `expected field '${field}' on response`).toHaveProperty(field);
100
104
  return assertion;
101
105
  },
102
106
  paginated(expected) {
@@ -398,6 +402,21 @@ async function runAuthorizationConformance(opts) {
398
402
  getFilter = filter;
399
403
  return rows[0] ?? null;
400
404
  }),
405
+ /**
406
+ * The single-record read has TWO paths, and the harness must be able to observe both.
407
+ *
408
+ * `fetchDetailed` only builds a compound filter and calls `getOne` when there is something to
409
+ * compound — a row policy, a tenant field, or a non-default id field. A permission that simply
410
+ * ALLOWS (no policy) leaves `{ _id }` alone and takes the plain `getById` path instead. The base
411
+ * mock stubs that as `null`, so before this stub the `get` surface reported **404 on every
412
+ * policy-free allow** while `list` and `aggregation` reported 200.
413
+ *
414
+ * That reads as partial enforcement and it is not: it is the harness failing to answer. A host
415
+ * asserting `get.status === 200` for its own allow-gate — the obvious falsification case to pair
416
+ * with a deny-parity suite — was told its gate broke the single-record surface. Found from
417
+ * be-prod, whose conformance suite does exactly that.
418
+ */
419
+ getById: (async () => rows[0] ?? null),
401
420
  aggregate: (async (req = {}) => {
402
421
  aggFilter = req.filter;
403
422
  return { rows: [{ tally: rows.length }] };
@@ -1166,12 +1185,13 @@ var HttpTestHarness = class {
1166
1185
  it(`${verb} /:id with non-existent ID should return 404`, async () => {
1167
1186
  const { app, fixtures } = this.getOptions();
1168
1187
  const payload = fixtures.update ?? fixtures.valid;
1169
- expect((await app.inject({
1188
+ const res = await app.inject({
1170
1189
  method: verb,
1171
1190
  url: `${this.getBaseUrl()}/000000000000000000000000`,
1172
1191
  headers: this.adminHeaders(),
1173
1192
  payload
1174
- })).statusCode).toBe(404);
1193
+ });
1194
+ expect(res.statusCode).toBe(404);
1175
1195
  });
1176
1196
  }
1177
1197
  if (enabledRoutes.has("delete")) {
@@ -1188,24 +1208,29 @@ var HttpTestHarness = class {
1188
1208
  deleteId = JSON.parse(createRes.body)._id;
1189
1209
  }
1190
1210
  if (!deleteId) return;
1191
- expect((await app.inject({
1211
+ const res = await app.inject({
1192
1212
  method: "DELETE",
1193
1213
  url: `${this.getBaseUrl()}/${deleteId}`,
1194
1214
  headers: this.adminHeaders()
1195
- })).statusCode).toBe(200);
1196
- if (enabledRoutes.has("get")) expect((await app.inject({
1197
- method: "GET",
1198
- url: `${this.getBaseUrl()}/${deleteId}`,
1199
- headers: this.adminHeaders()
1200
- })).statusCode).toBe(404);
1215
+ });
1216
+ expect(res.statusCode).toBe(200);
1217
+ if (enabledRoutes.has("get")) {
1218
+ const getRes = await app.inject({
1219
+ method: "GET",
1220
+ url: `${this.getBaseUrl()}/${deleteId}`,
1221
+ headers: this.adminHeaders()
1222
+ });
1223
+ expect(getRes.statusCode).toBe(404);
1224
+ }
1201
1225
  });
1202
1226
  it("DELETE /:id with non-existent ID should return 404", async () => {
1203
1227
  const { app } = this.getOptions();
1204
- expect((await app.inject({
1228
+ const res = await app.inject({
1205
1229
  method: "DELETE",
1206
1230
  url: `${this.getBaseUrl()}/000000000000000000000000`,
1207
1231
  headers: this.adminHeaders()
1208
- })).statusCode).toBe(404);
1232
+ });
1233
+ expect(res.statusCode).toBe(404);
1209
1234
  });
1210
1235
  }
1211
1236
  });
@@ -1224,49 +1249,55 @@ var HttpTestHarness = class {
1224
1249
  describe(`${resource.displayName} HTTP Permissions`, () => {
1225
1250
  if (enabledRoutes.has("list") && protectedOps.list) it("GET list without auth should return 401", async () => {
1226
1251
  const { app } = this.getOptions();
1227
- expect((await app.inject({
1252
+ const res = await app.inject({
1228
1253
  method: "GET",
1229
1254
  url: this.getBaseUrl()
1230
- })).statusCode).toBe(401);
1255
+ });
1256
+ expect(res.statusCode).toBe(401);
1231
1257
  });
1232
1258
  if (enabledRoutes.has("get") && protectedOps.get) it("GET /:id without auth should return 401", async () => {
1233
1259
  const { app } = this.getOptions();
1234
- expect((await app.inject({
1260
+ const res = await app.inject({
1235
1261
  method: "GET",
1236
1262
  url: `${this.getBaseUrl()}/000000000000000000000000`
1237
- })).statusCode).toBe(401);
1263
+ });
1264
+ expect(res.statusCode).toBe(401);
1238
1265
  });
1239
1266
  if (enabledRoutes.has("create") && protectedOps.create) it("POST without auth should return 401", async () => {
1240
1267
  const { app, fixtures } = this.getOptions();
1241
- expect((await app.inject({
1268
+ const res = await app.inject({
1242
1269
  method: "POST",
1243
1270
  url: this.getBaseUrl(),
1244
1271
  payload: fixtures.valid
1245
- })).statusCode).toBe(401);
1272
+ });
1273
+ expect(res.statusCode).toBe(401);
1246
1274
  });
1247
1275
  if (enabledRoutes.has("update") && protectedOps.update) for (const verb of updateMethods) it(`${verb} without auth should return 401`, async () => {
1248
1276
  const { app, fixtures } = this.getOptions();
1249
1277
  const payload = fixtures.update ?? fixtures.valid;
1250
- expect((await app.inject({
1278
+ const res = await app.inject({
1251
1279
  method: verb,
1252
1280
  url: `${this.getBaseUrl()}/000000000000000000000000`,
1253
1281
  payload
1254
- })).statusCode).toBe(401);
1282
+ });
1283
+ expect(res.statusCode).toBe(401);
1255
1284
  });
1256
1285
  if (enabledRoutes.has("delete") && protectedOps.delete) it("DELETE without auth should return 401", async () => {
1257
1286
  const { app } = this.getOptions();
1258
- expect((await app.inject({
1287
+ const res = await app.inject({
1259
1288
  method: "DELETE",
1260
1289
  url: `${this.getBaseUrl()}/000000000000000000000000`
1261
- })).statusCode).toBe(401);
1290
+ });
1291
+ expect(res.statusCode).toBe(401);
1262
1292
  });
1263
1293
  if (enabledRoutes.has("list")) it("admin should access list endpoint", async () => {
1264
1294
  const { app } = this.getOptions();
1265
- expect((await app.inject({
1295
+ const res = await app.inject({
1266
1296
  method: "GET",
1267
1297
  url: this.getBaseUrl(),
1268
1298
  headers: this.adminHeaders()
1269
- })).statusCode).toBeLessThan(400);
1299
+ });
1300
+ expect(res.statusCode).toBeLessThan(400);
1270
1301
  });
1271
1302
  if (enabledRoutes.has("create")) it("admin should access create endpoint", async () => {
1272
1303
  const { app, fixtures } = this.getOptions();
@@ -1313,6 +1344,33 @@ function createHttpTestHarness(resource, optionsOrGetter) {
1313
1344
  return new HttpTestHarness(resource, optionsOrGetter);
1314
1345
  }
1315
1346
  //#endregion
1347
+ //#region src/testing/moduleSetup.ts
1348
+ function createTestModuleSetup() {
1349
+ const deferred = [];
1350
+ return {
1351
+ context: { defer(disposer) {
1352
+ if (typeof disposer !== "function") throw new TypeError(`[arc/testing] defer() expected a function, received ${typeof disposer}.`);
1353
+ deferred.push(disposer);
1354
+ } },
1355
+ deferred,
1356
+ async dispose() {
1357
+ const errors = [];
1358
+ for (let i = deferred.length - 1; i >= 0; i--) {
1359
+ const dispose = deferred[i];
1360
+ if (!dispose) continue;
1361
+ try {
1362
+ await dispose();
1363
+ } catch (err) {
1364
+ errors.push(err);
1365
+ }
1366
+ }
1367
+ deferred.length = 0;
1368
+ if (errors.length === 1) throw errors[0];
1369
+ if (errors.length > 1) throw new AggregateError(errors, `[arc/testing] ${errors.length} disposers threw`);
1370
+ }
1371
+ };
1372
+ }
1373
+ //#endregion
1316
1374
  //#region src/testing/scopeRoleGate.ts
1317
1375
  /**
1318
1376
  * `scopeRoleGate` — the ONE role gate for package tests.
@@ -1435,7 +1493,7 @@ function pickDefaultAuth(authMode, callerAuth) {
1435
1493
  };
1436
1494
  }
1437
1495
  async function createTestApp(options = {}) {
1438
- const { createApp } = await import("../createApp-Df89BEni.mjs").then((n) => n.r);
1496
+ const { createApp } = await import("../createApp-Bv0m-Q6_.mjs").then((n) => n.r);
1439
1497
  const { resources = [], db = "in-memory", connectMongoose = false, authMode = "jwt", defaultOrgId, plugins, auth: callerAuth, ...appOptions } = options;
1440
1498
  let dbHandle;
1441
1499
  let dbUri;
@@ -1505,4 +1563,4 @@ function createMinimalTestApp(options = {}) {
1505
1563
  });
1506
1564
  }
1507
1565
  //#endregion
1508
- export { HttpTestHarness, TEST_ACTOR_HEADER, bootModuleApp, createBetterAuthProvider, createBetterAuthTestHelpers, createCustomAuthProvider, createDataFactory, createHttpTestHarness, createJwtAuthProvider, createMinimalTestApp, createMockController, createMockReply, createMockRepository, createMockRequest, createMockUser, createSpy, createTestApp, createTestFixtures, createTestTimer, expectArc, mongoMemoryDatabase, preloadResources, preloadResourcesAsync, runAuthorizationConformance, runStorageContract, safeParseBody, scopeFromTestActor, scopeRoleGate, setupBetterAuthTestApp, testActorAuth, testActorHeaders, waitFor };
1566
+ export { HttpTestHarness, TEST_ACTOR_HEADER, 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 };
@@ -5,8 +5,17 @@ interface OutboxStoreContractSetupResult {
5
5
  store: OutboxStore;
6
6
  /** Called once after the suite. */
7
7
  teardown?: () => Promise<void>;
8
- /** Empty the store between tests. Strongly recommended for isolation. */
9
- reset?: () => Promise<void>;
8
+ /**
9
+ * Empty the store. REQUIRED, and run before every test.
10
+ *
11
+ * Not a recommendation: several assertions are absolute rather than relative —
12
+ * "exactly zero pending", "exactly two", "`oldestPendingAgeMs()` is null on an
13
+ * empty queue". Without a reset those read whatever the previous test left, so the
14
+ * suite passes on ORDER rather than on behaviour, and inserting one unrelated case
15
+ * turns a conforming store red. A store that cannot empty itself cannot be
16
+ * conformance-tested.
17
+ */
18
+ reset: () => Promise<void>;
10
19
  }
11
20
  type OutboxStoreContractSetup = () => Promise<OutboxStoreContractSetupResult>;
12
21
  declare function runOutboxStoreContract(name: string, setup: OutboxStoreContractSetup): void;