@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,8 +1,10 @@
1
- import { t as MemoryEventTransport } from "../EventTransport-C6YA8xY9.mjs";
2
- import { a as createEventRegistry, i as wrapWithSchema, n as subscribeWithSchema, o as defineEvent, r as wrapWithBoundary, t as subscribeWithBoundary } from "../subscribe-helpers-DfzsivM6.mjs";
3
- import { i as withRetry, r as createDeadLetterPublisher, t as eventPlugin } from "../eventPlugin-D2NNR4py.mjs";
4
- import { n as createSafeGetOne, t as createIsDuplicateKeyError } from "../store-helpers-DY6xq4sM.mjs";
1
+ /*! @classytic/arc v2.32.0 | MIT | https://classytic.com */
2
+ import { t as MemoryEventTransport } from "../EventTransport-BV10D0oG.mjs";
3
+ import { a as createEventRegistry, i as wrapWithSchema, n as subscribeWithSchema, o as defineEvent, r as wrapWithBoundary, t as subscribeWithBoundary } from "../subscribe-helpers-sp5xTDVA.mjs";
4
+ import { i as withRetry, r as createDeadLetterPublisher, t as eventPlugin } from "../eventPlugin-Dk_TyXeE.mjs";
5
+ import { n as createSafeGetOne, t as createIsDuplicateKeyError } from "../store-helpers-DmEMHlPQ.mjs";
5
6
  import { and, anyOf, eq, lte, ne, or } from "@classytic/repo-core/filter";
7
+ import { MemoryOutboxStore } from "@classytic/primitives/memory-outbox";
6
8
  import { InvalidOutboxEventError, OutboxOwnershipError } from "@classytic/primitives/outbox";
7
9
  import { update } from "@classytic/repo-core/update";
8
10
  //#region src/events/eventTypes.ts
@@ -56,141 +58,6 @@ const CACHE_EVENTS = Object.freeze({
56
58
  TAG_VERSION_BUMPED: "arc.cache.tag.bumped"
57
59
  });
58
60
  //#endregion
59
- //#region src/events/memory-outbox.ts
60
- /**
61
- * In-memory outbox store — reference implementation for dev / testing.
62
- *
63
- * Supports the full `OutboxStore` capability set (claim/lease, fail/retry,
64
- * dedupe, visibleAt). Production deployments should pass a `repository` to
65
- * `EventOutbox` instead of using this; the memory store exists for unit
66
- * tests and single-process dev where durable persistence is noise.
67
- */
68
- const DEFAULT_LEASE_MS$2 = 3e4;
69
- var MemoryOutboxStore = class {
70
- entries = [];
71
- seenDedupeKeys = /* @__PURE__ */ new Set();
72
- /**
73
- * Drop every entry (including delivered / failed / dead-lettered rows) and
74
- * the dedupe-key memory.
75
- *
76
- * For tests only — it is the in-memory analogue of truncating the table, and
77
- * exists so a suite can isolate cases against ONE store instance (the
78
- * `reset` hook of `runOutboxStoreContract`). Not part of `OutboxStore`; a
79
- * production store must never expose this.
80
- */
81
- clear() {
82
- this.entries.length = 0;
83
- this.seenDedupeKeys.clear();
84
- }
85
- async save(event, options) {
86
- if (!event?.type || typeof event.type !== "string") throw new InvalidOutboxEventError("event.type is required");
87
- if (!event.meta?.id || typeof event.meta.id !== "string") throw new InvalidOutboxEventError("event.meta.id is required");
88
- if (options?.dedupeKey) {
89
- if (this.seenDedupeKeys.has(options.dedupeKey)) return;
90
- this.seenDedupeKeys.add(options.dedupeKey);
91
- }
92
- this.entries.push({
93
- event,
94
- status: "pending",
95
- attempts: 0,
96
- visibleAt: options?.visibleAt?.getTime() ?? 0,
97
- leaseOwner: null,
98
- leaseExpiresAt: 0,
99
- deliveredAt: null,
100
- firstFailedAt: null,
101
- lastFailedAt: null,
102
- lastError: null,
103
- dedupeKey: options?.dedupeKey
104
- });
105
- }
106
- async getPending(limit) {
107
- const now = Date.now();
108
- return this.entries.filter((e) => e.status === "pending" && e.visibleAt <= now && (e.leaseOwner === null || e.leaseExpiresAt <= now)).slice(0, limit).map((e) => e.event);
109
- }
110
- async claimPending(options) {
111
- const now = Date.now();
112
- const limit = options?.limit ?? 100;
113
- const leaseMs = options?.leaseMs ?? DEFAULT_LEASE_MS$2;
114
- const consumerId = options?.consumerId ?? "anonymous";
115
- const typeFilter = options?.types ? new Set(options.types) : null;
116
- const claimed = [];
117
- for (const entry of this.entries) {
118
- if (claimed.length >= limit) break;
119
- if (entry.status !== "pending") continue;
120
- if (entry.visibleAt > now) continue;
121
- if (entry.leaseOwner !== null && entry.leaseExpiresAt > now) continue;
122
- if (typeFilter && !typeFilter.has(entry.event.type)) continue;
123
- entry.leaseOwner = consumerId;
124
- entry.leaseExpiresAt = now + leaseMs;
125
- entry.attempts++;
126
- claimed.push(entry.event);
127
- }
128
- return claimed;
129
- }
130
- async acknowledge(eventId, options) {
131
- const entry = this.entries.find((e) => e.event.meta.id === eventId);
132
- if (!entry) return;
133
- if (entry.status === "delivered") return;
134
- if (options?.consumerId && entry.leaseOwner && entry.leaseOwner !== options.consumerId) throw new OutboxOwnershipError(eventId, options.consumerId, entry.leaseOwner);
135
- entry.status = "delivered";
136
- entry.deliveredAt = Date.now();
137
- entry.leaseOwner = null;
138
- }
139
- async fail(eventId, error, options) {
140
- const entry = this.entries.find((e) => e.event.meta.id === eventId);
141
- if (!entry) return;
142
- if (options?.consumerId && entry.leaseOwner && entry.leaseOwner !== options.consumerId) throw new OutboxOwnershipError(eventId, options.consumerId, entry.leaseOwner);
143
- const now = Date.now();
144
- entry.lastError = error;
145
- entry.leaseOwner = null;
146
- entry.leaseExpiresAt = 0;
147
- if (entry.firstFailedAt === null) entry.firstFailedAt = now;
148
- entry.lastFailedAt = now;
149
- if (options?.deadLetter) {
150
- entry.status = "dead_letter";
151
- return;
152
- }
153
- entry.status = "pending";
154
- entry.visibleAt = options?.retryAt ? options.retryAt.getTime() : 0;
155
- }
156
- async getDeadLettered(limit) {
157
- const out = [];
158
- for (const entry of this.entries) {
159
- if (out.length >= limit) break;
160
- if (entry.status !== "dead_letter") continue;
161
- out.push({
162
- event: entry.event,
163
- error: {
164
- message: entry.lastError?.message ?? "unknown",
165
- ...entry.lastError?.code !== void 0 ? { code: entry.lastError.code } : {}
166
- },
167
- attempts: entry.attempts,
168
- firstFailedAt: new Date(entry.firstFailedAt ?? entry.lastFailedAt ?? Date.now()),
169
- lastFailedAt: new Date(entry.lastFailedAt ?? entry.firstFailedAt ?? Date.now())
170
- });
171
- }
172
- return out;
173
- }
174
- async purge(olderThanMs) {
175
- const cutoff = Date.now() - olderThanMs;
176
- let purged = 0;
177
- for (let i = this.entries.length - 1; i >= 0; i--) {
178
- const entry = this.entries[i];
179
- if (!entry) continue;
180
- if (entry.status === "delivered" && entry.deliveredAt !== null && entry.deliveredAt < cutoff) {
181
- if (entry.dedupeKey) this.seenDedupeKeys.delete(entry.dedupeKey);
182
- this.entries.splice(i, 1);
183
- purged++;
184
- }
185
- }
186
- return purged;
187
- }
188
- /** Test helper: inspect entry by id */
189
- _getEntry(eventId) {
190
- return this.entries.find((e) => e.event.meta.id === eventId);
191
- }
192
- };
193
- //#endregion
194
61
  //#region src/events/outbox/backoff.ts
195
62
  /**
196
63
  * Compute a `retryAt` `Date` using exponential backoff with jitter.
@@ -330,6 +197,17 @@ function repositoryAsOutboxStore(repository, options) {
330
197
  */
331
198
  const claimableFilter = (now) => and(eq("status", "pending"), lte(visibleAtField, now), or(eq("leaseOwner", null), lte("leaseExpiresAt", now)));
332
199
  return {
200
+ /**
201
+ * Transactional: `save` forwards `options.session` to the backing
202
+ * repository's `create`, so the event row joins the caller's transaction on
203
+ * the SAME connection and commits or rolls back with the domain write.
204
+ *
205
+ * Declared explicitly because `OutboxWriteOptions.session` is best-effort by
206
+ * contract — a caller cannot infer atomicity from the presence of an outbox,
207
+ * only from this flag. See `OutboxStore.transactionalSave` in
208
+ * `@classytic/primitives/outbox`.
209
+ */
210
+ transactionalSave: true,
333
211
  async save(event, options) {
334
212
  if (!event?.type || typeof event.type !== "string") throw new InvalidOutboxEventError("event.type is required");
335
213
  if (!event.meta?.id || typeof event.meta.id !== "string") throw new InvalidOutboxEventError("event.meta.id is required");
@@ -475,13 +353,96 @@ function repositoryAsOutboxStore(repository, options) {
475
353
  if (batch.length < DEFAULT_PURGE_BATCH) break;
476
354
  }
477
355
  return totalDeleted;
356
+ },
357
+ /**
358
+ * Re-drive one dead-lettered event — the other half of `fail({ deadLetter: true })`.
359
+ *
360
+ * Filtered on `status: 'dead_letter'` as well as the id, so this is a no-op on a row
361
+ * that is merely retrying. Requeuing a backing-off row would zero its attempt count
362
+ * and defeat the failure policy, and the id alone cannot distinguish the two.
363
+ *
364
+ * `attempts` resets because the operator's whole claim is that the CAUSE is fixed:
365
+ * carrying the old count forward would dead-letter the retry again after one more
366
+ * blip, which reads as "the fix did not work".
367
+ */
368
+ async requeue(eventId) {
369
+ return await r.findOneAndUpdate(and(eq(idField, eventId), eq("status", "dead_letter")), update({ set: {
370
+ status: "pending",
371
+ attempts: 0,
372
+ leaseOwner: null,
373
+ leaseExpiresAt: null,
374
+ [visibleAtField]: /* @__PURE__ */ new Date(),
375
+ lastError: null
376
+ } }), { returnDocument: "after" }) != null;
377
+ },
378
+ /** Rows in `status` — the count behind an operator dashboard or a dead-letter alert. */
379
+ async countByStatus(status) {
380
+ const filters = eq("status", status);
381
+ const counter = repository.count;
382
+ if (typeof counter === "function") return counter.call(repository, filters);
383
+ /**
384
+ * Fallback for a kit without `count`. Two shapes, and the difference decides the
385
+ * query — getting it wrong here produces a number that LIES, which is worse than
386
+ * no number at all: the operator reads a healthy dashboard and stops looking.
387
+ *
388
+ * A paginated kit reports the match count in its envelope, so one row is enough
389
+ * to read it — deliberately not a hydrate-and-length, since a `delivered` count
390
+ * would drag the whole retention window into memory for one integer.
391
+ *
392
+ * A kit that returns a BARE ARRAY has no envelope to read, so the rows ARE the
393
+ * count and every match has to come back. `unwrapDocs` documents that shape as
394
+ * real, so it is not hypothetical. `select: idField` keeps it to one column.
395
+ */
396
+ const probe = await r.getAll({
397
+ filters,
398
+ page: 1,
399
+ limit: 1,
400
+ select: idField
401
+ });
402
+ if (Array.isArray(probe)) {
403
+ const all = await r.getAll({
404
+ filters,
405
+ select: idField
406
+ });
407
+ return unwrapDocs(all).length;
408
+ }
409
+ const total = probe?.total ?? probe?.meta?.total;
410
+ if (typeof total === "number") return total;
411
+ /**
412
+ * An envelope in a shape neither branch recognises. Throwing beats returning 0:
413
+ * a zero is indistinguishable from a healthy queue, so it would silence exactly
414
+ * the alert this method exists to raise.
415
+ */
416
+ throw new Error("repositoryAsOutboxStore.countByStatus: the repository's `getAll` returned an envelope with no `total` (and is not a bare array), so the count cannot be read. Implement `count(filter)` on the repository — every kit that has it answers this in one query.");
417
+ },
418
+ /**
419
+ * Age of the oldest PENDING row, or `null` when the queue is empty.
420
+ *
421
+ * The signal a pending count cannot give: a count of 40 looks the same whether the
422
+ * relay is draining briskly or has been wedged for an hour behind one poison row.
423
+ *
424
+ * Measured from `createdAt`, not the last attempt — the question is how long the
425
+ * event has gone undelivered, and restarting the clock on every retry would make a
426
+ * permanently-failing row look permanently fresh, which is the exact row an alert
427
+ * exists to surface.
428
+ */
429
+ async oldestPendingAgeMs() {
430
+ const result = await r.getAll({
431
+ filters: eq("status", "pending"),
432
+ sort: { createdAt: 1 },
433
+ page: 1,
434
+ limit: 1
435
+ });
436
+ const [oldest] = unwrapDocs(result);
437
+ if (oldest?.createdAt == null) return null;
438
+ return Date.now() - new Date(oldest.createdAt).getTime();
478
439
  }
479
440
  };
480
441
  }
481
442
  //#endregion
482
443
  //#region src/events/outbox/relay.ts
483
444
  /** Default outbox retention — delivered events older than this are eligible for purge */
484
- const DEFAULT_OUTBOX_RETENTION_MS = 10080 * 60 * 1e3;
445
+ const DEFAULT_OUTBOX_RETENTION_MS = 6048e5;
485
446
  const DEFAULT_LEASE_MS = 3e4;
486
447
  /**
487
448
  * Hard cap on the in-process attempt-counter map. Entries are cleared on
@@ -794,4 +755,79 @@ function normalizeError(err) {
794
755
  return { message: String(err) };
795
756
  }
796
757
  //#endregion
797
- export { ARC_LIFECYCLE_EVENTS, CACHE_EVENTS, CRUD_EVENT_SUFFIXES, EventOutbox, MemoryEventTransport, MemoryOutboxStore, createDeadLetterPublisher, createEventRegistry, crudEventType, defineEvent, eventPlugin, exponentialBackoff, repositoryAsOutboxStore, subscribeWithBoundary, subscribeWithSchema, withRetry, wrapWithBoundary, wrapWithSchema };
758
+ //#region src/events/outbox-module.ts
759
+ /** Default relay cadence. Frequent enough that a retry is not a user-visible delay. */
760
+ const DEFAULT_RELAY_EVERY_MS = 5e3;
761
+ /**
762
+ * Compose the outbox as a module.
763
+ *
764
+ * ```ts
765
+ * // host
766
+ * const model = createOutboxModel(connection, { visibleAtField: 'nextVisibleAt' });
767
+ * createApp({ modules: [
768
+ * createOutboxModule({
769
+ * store: repositoryAsOutboxStore(new Repository(model), { visibleAtField: 'nextVisibleAt' }),
770
+ * onError: (err, ctx) => app.log.error({ err, ...ctx }, 'outbox relay'),
771
+ * }),
772
+ * …
773
+ * ]});
774
+ *
775
+ * // consumer module
776
+ * defineModule({
777
+ * name: 'order',
778
+ * dependsOn: ['outbox'],
779
+ * bootstrap: (f) => createOrder({
780
+ * outbox: getModuleExports<OutboxModuleExports>(f, 'outbox').store,
781
+ * }),
782
+ * });
783
+ * ```
784
+ *
785
+ * `dependsOn` is doing real work there: it both orders the bootstraps and makes a missing outbox a
786
+ * boot error naming the module that needed it.
787
+ */
788
+ function createOutboxModule(options) {
789
+ const name = options.name ?? "outbox";
790
+ const relayEveryMs = options.relayEveryMs ?? DEFAULT_RELAY_EVERY_MS;
791
+ /**
792
+ * Built at bootstrap and reused by the schedule.
793
+ *
794
+ * Not rebuilt per tick: `EventOutbox` carries the consumer id the lease is keyed on, and a fresh
795
+ * instance per tick would be a fresh claimant every few seconds — which defeats the lease it
796
+ * depends on for exactly-once publishing.
797
+ */
798
+ let exports;
799
+ return {
800
+ name,
801
+ bootstrap: (fastify) => {
802
+ const store = typeof options.store === "function" ? options.store(fastify) : options.store;
803
+ const transport = options.transport === void 0 ? fastify.events : typeof options.transport === "function" ? options.transport(fastify) : options.transport;
804
+ exports = {
805
+ store,
806
+ relay: new EventOutbox({
807
+ store,
808
+ ...transport !== void 0 ? { transport } : {},
809
+ ...options.batchSize !== void 0 ? { batchSize: options.batchSize } : {},
810
+ ...options.leaseMs !== void 0 ? { leaseMs: options.leaseMs } : {},
811
+ consumerId: options.consumerId ?? "arc-outbox-relay",
812
+ ...options.onError !== void 0 ? { onError: options.onError } : {},
813
+ ...options.failurePolicy !== void 0 ? { failurePolicy: options.failurePolicy } : {}
814
+ })
815
+ };
816
+ return exports;
817
+ },
818
+ /**
819
+ * The relay runs on the module's OWN schedule arm, so the host stops owning the tick — and
820
+ * a host that forgets to start one cannot exist.
821
+ */
822
+ scheduledJobs: () => [{
823
+ name: `${name}.relay`,
824
+ every: relayEveryMs,
825
+ runOnStart: options.runOnStart ?? true,
826
+ handler: async () => {
827
+ await exports?.relay.relay();
828
+ }
829
+ }]
830
+ };
831
+ }
832
+ //#endregion
833
+ export { ARC_LIFECYCLE_EVENTS, CACHE_EVENTS, CRUD_EVENT_SUFFIXES, EventOutbox, MemoryEventTransport, MemoryOutboxStore, createDeadLetterPublisher, createEventRegistry, createOutboxModule, crudEventType, defineEvent, eventPlugin, exponentialBackoff, repositoryAsOutboxStore, subscribeWithBoundary, subscribeWithSchema, withRetry, wrapWithBoundary, wrapWithSchema };
@@ -1,2 +1,3 @@
1
- import { n as RedisStreamTransport, r as RedisStreamTransportOptions, t as RedisStreamLike } from "../../redis-stream-uIaauNpZ.mjs";
1
+
2
+ import { n as RedisStreamTransport, r as RedisStreamTransportOptions, t as RedisStreamLike } from "../../redis-stream-qN6A-RFv.mjs";
2
3
  export { type RedisStreamLike, RedisStreamTransport, type RedisStreamTransportOptions };
@@ -1,3 +1,4 @@
1
+ /*! @classytic/arc v2.32.0 | MIT | https://classytic.com */
1
2
  import sjson from "secure-json-parse";
2
3
  //#region src/events/transports/redis-stream.ts
3
4
  /**
@@ -183,13 +184,15 @@ var RedisStreamTransport = class {
183
184
  this.handlers.clear();
184
185
  this.failureContext.clear();
185
186
  if (this.pollPromise) {
186
- if (await Promise.race([this.pollPromise.then(() => "drained"), this.sleep(this.closeTimeoutMs).then(() => "timeout")]) === "timeout") if (!this.externalLifecycle) {
187
- if (typeof this.redis.disconnect === "function") this.redis.disconnect();
188
- else await this.redis.quit().catch((err) => {
189
- this.logger.error("[RedisStreamTransport] quit() during close raced:", err);
190
- });
191
- await this.pollPromise.catch(() => void 0);
192
- } else this.pollPromise.catch(() => void 0);
187
+ if (await Promise.race([this.pollPromise.then(() => "drained"), this.sleep(this.closeTimeoutMs).then(() => "timeout")]) === "timeout") {
188
+ if (!this.externalLifecycle) {
189
+ if (typeof this.redis.disconnect === "function") this.redis.disconnect();
190
+ else await this.redis.quit().catch((err) => {
191
+ this.logger.error("[RedisStreamTransport] quit() during close raced:", err);
192
+ });
193
+ await this.pollPromise.catch(() => void 0);
194
+ } else this.pollPromise.catch(() => void 0);
195
+ }
193
196
  this.pollPromise = null;
194
197
  }
195
198
  if (!this.externalLifecycle) await this.redis.quit().catch(() => void 0);
@@ -250,8 +253,10 @@ var RedisStreamTransport = class {
250
253
  const overRetryIds = [];
251
254
  for (const entry of pending) {
252
255
  const [id, , idleTime, deliveryCount] = entry;
253
- if (idleTime > this.claimTimeoutMs) if (deliveryCount >= this.maxRetries) overRetryIds.push(id);
254
- else staleIds.push(id);
256
+ if (idleTime > this.claimTimeoutMs) {
257
+ if (deliveryCount >= this.maxRetries) overRetryIds.push(id);
258
+ else staleIds.push(id);
259
+ }
255
260
  }
256
261
  if (overRetryIds.length > 0) await this.moveToDlq(overRetryIds);
257
262
  if (staleIds.length > 0) {
@@ -1,4 +1,5 @@
1
- import { a as EventTransport, i as EventLogger, n as DomainEvent, r as EventHandler } from "../../EventTransport-QmY7A258.mjs";
1
+
2
+ import { a as EventTransport, i as EventLogger, n as DomainEvent, r as EventHandler } from "../../EventTransport-2uVV34WU.mjs";
2
3
  //#region src/events/transports/redis.d.ts
3
4
  interface RedisLike {
4
5
  publish(channel: string, message: string): Promise<number>;
@@ -1,3 +1,4 @@
1
+ /*! @classytic/arc v2.32.0 | MIT | https://classytic.com */
1
2
  import sjson from "secure-json-parse";
2
3
  //#region src/events/transports/redis.ts
3
4
  /**
@@ -1,3 +1,4 @@
1
+
1
2
  //#region src/docs/externalPaths.d.ts
2
3
  /**
3
4
  * External OpenAPI Path Provider
@@ -1,7 +1,8 @@
1
- import { a as ModuleContribution, c as ResourceModule, h as HealthCheck, i as EventHandlerDefinition, l as loadResources, n as ArcModuleInput, o as LoadResourcesOptions, r as ArcModuleRegistry, s as ResourceLike, t as ArcModule } from "../types-D_n8L6VY.mjs";
2
- import { a as orderModules, c as collectModuleScheduledJobs, i as resolveModule, l as collectModuleWorkflows, n as getModuleExports, o as subscribeModuleEventHandlers, r as resolveContribution, s as collectModuleHealthChecks, t as defineModule } from "../index-CCzVR-N3.mjs";
3
- import "../fastify-augmentation-0WhPnUH7.mjs";
4
- import { a as RawBodyOptions, c as BetterAuthOption, d as JwtAuthOption, i as MultipartOptions, l as CustomAuthenticatorOption, n as preloadResourcesAsync, o as UnderPressureOptions, r as CreateAppOptions, s as AuthOption, t as preloadResources, u as CustomPluginAuthOption } from "../preloadResources-DCfa2egz.mjs";
1
+
2
+ import { _ as HealthCheck, a as ModuleContribution, c as LoadResourcesOptions, d as loadResources, i as EventHandlerDefinition, l as ResourceLike, n as ArcModuleInput, o as ModuleDisposer, r as ArcModuleRegistry, s as ModuleSetupContext, t as ArcModule, u as ResourceModule } from "../types-CT5vu6M0.mjs";
3
+ import { _ as collectModuleScheduledJobs, a as lazyModuleExports, c as getModuleState, d as resolveModule, f as orderModules, g as collectModuleHealthChecks, h as extendModule, i as hasModuleExports, l as hasModule, m as ModuleExtension, n as getModuleExports, o as lazyRequiredModuleExports, p as subscribeModuleEventHandlers, r as getOptionalModuleExports, s as ModuleState, t as defineModule, u as resolveContribution, v as collectModuleWorkflows } from "../index-ZWhLTp_s.mjs";
4
+ import { c as ResolvedResourceDescriptor, l as describeResolvedModule, o as ResolvedModuleDescriptor, s as ResolvedModuleLifecycle } from "../fastify-augmentation-BppG5bMT.mjs";
5
+ import { a as RawBodyOptions, c as BetterAuthOption, d as JwtAuthOption, i as MultipartOptions, l as CustomAuthenticatorOption, n as preloadResourcesAsync, o as UnderPressureOptions, r as CreateAppOptions, s as AuthOption, t as preloadResources, u as CustomPluginAuthOption } from "../preloadResources-T1hmlTEZ.mjs";
5
6
  import { FastifyInstance, FastifyServerOptions } from "fastify";
6
7
  //#region src/factory/createApp.d.ts
7
8
  /**
@@ -22,6 +23,9 @@ import { FastifyInstance, FastifyServerOptions } from "fastify";
22
23
  * 10. afterResources() — post-registration wiring
23
24
  * 11. onReady/onClose — lifecycle hooks
24
25
  * ```
26
+ *
27
+ * **Boot is all-or-nothing.** If any phase throws, the partially-built
28
+ * instance is closed before the error propagates — see the wrapper below.
25
29
  */
26
30
  declare function createApp(options: CreateAppOptions): Promise<FastifyInstance>;
27
31
  /**
@@ -133,4 +137,4 @@ declare const testingPreset: Partial<CreateAppOptions>;
133
137
  declare const edgePreset: Partial<CreateAppOptions>;
134
138
  declare function getPreset(name: "production" | "development" | "testing" | "edge" | "worker"): Partial<CreateAppOptions>;
135
139
  //#endregion
136
- export { ArcFactory, type ArcModule, type ArcModuleInput, type ArcModuleRegistry, type ArcWorker, type AuthOption, type BetterAuthOption, type CreateAppOptions, type CreateWorkerOptions, type CustomAuthenticatorOption, type CustomPluginAuthOption, type EventHandlerDefinition, type FetchHandlerOptions, type JwtAuthOption, type LoadResourcesOptions, type ModuleContribution, type MultipartOptions, type RawBodyOptions, type ResourceLike, type ResourceModule, type UnderPressureOptions, type WorkerHealthOptions, collectModuleHealthChecks, collectModuleScheduledJobs, collectModuleWorkflows, createApp, createWorker, defineModule, developmentPreset, edgePreset, getModuleExports, getPreset, loadResources, orderModules, preloadResources, preloadResourcesAsync, productionPreset, resolveContribution, resolveModule, subscribeModuleEventHandlers, testingPreset, toFetchHandler };
140
+ export { ArcFactory, type ArcModule, type ArcModuleInput, type ArcModuleRegistry, type ArcWorker, type AuthOption, type BetterAuthOption, type CreateAppOptions, type CreateWorkerOptions, type CustomAuthenticatorOption, type CustomPluginAuthOption, type EventHandlerDefinition, type FetchHandlerOptions, type JwtAuthOption, type LoadResourcesOptions, type ModuleContribution, type ModuleDisposer, type ModuleExtension, type ModuleSetupContext, type ModuleState, type MultipartOptions, type RawBodyOptions, type ResolvedModuleDescriptor, type ResolvedModuleLifecycle, type ResolvedResourceDescriptor, type ResourceLike, type ResourceModule, type UnderPressureOptions, type WorkerHealthOptions, collectModuleHealthChecks, collectModuleScheduledJobs, collectModuleWorkflows, createApp, createWorker, defineModule, describeResolvedModule, developmentPreset, edgePreset, extendModule, getModuleExports, getModuleState, getOptionalModuleExports, getPreset, hasModule, hasModuleExports, lazyModuleExports, lazyRequiredModuleExports, loadResources, orderModules, preloadResources, preloadResourcesAsync, productionPreset, resolveContribution, resolveModule, subscribeModuleEventHandlers, testingPreset, toFetchHandler };
@@ -1,8 +1,131 @@
1
- import { r as mergeHealthChecks, t as healthPlugin } from "../health-CB_m4S32.mjs";
2
- import { a as edgePreset, c as testingPreset, d as subscribeModuleEventHandlers, f as collectModuleHealthChecks, g as resolveModule, h as resolveContribution, i as developmentPreset, l as workerPreset, m as collectModuleWorkflows, n as createApp, o as getPreset, p as collectModuleScheduledJobs, s as productionPreset, t as ArcFactory, u as orderModules } from "../createApp-CXrKFciH.mjs";
3
- import { n as getModuleExports, t as defineModule } from "../module-_-M7C5ES.mjs";
4
- import { t as loadResources } from "../loadResources-DpU8pfjV.mjs";
5
- import { n as preloadResourcesAsync, t as preloadResources } from "../preloadResources-DSY5HGpS.mjs";
1
+ /*! @classytic/arc v2.32.0 | MIT | https://classytic.com */
2
+ import { r as mergeHealthChecks, t as healthPlugin } from "../health-5XnTa2uo.mjs";
3
+ import { _ as collectModuleWorkflows, a as edgePreset, c as testingPreset, d as hasModule, f as orderModules, g as collectModuleScheduledJobs, h as collectModuleHealthChecks, i as developmentPreset, l as workerPreset, m as describeResolvedModule, n as createApp, o as getPreset, p as subscribeModuleEventHandlers, s as productionPreset, t as ArcFactory, u as getModuleState, v as resolveContribution, y as resolveModule } from "../createApp-Bv0m-Q6_.mjs";
4
+ import { a as lazyModuleExports, i as hasModuleExports, n as getModuleExports, o as lazyRequiredModuleExports, r as getOptionalModuleExports, t as defineModule } from "../module-Br81am6m.mjs";
5
+ import { t as loadResources } from "../loadResources-DisEAmkm.mjs";
6
+ import { n as preloadResourcesAsync, t as preloadResources } from "../preloadResources-CDs87fTw.mjs";
7
+ //#region src/factory/module/extend.ts
8
+ /** Concatenate two contributions, resolving factories only when arc asks. */
9
+ function mergeContribution(own, extra) {
10
+ if (own === void 0) return extra;
11
+ if (extra === void 0) return own;
12
+ return async (fastify) => {
13
+ const a = typeof own === "function" ? await own(fastify) : own;
14
+ const b = typeof extra === "function" ? await extra(fastify) : extra;
15
+ return [...a, ...b];
16
+ };
17
+ }
18
+ /**
19
+ * Plain-array concat, for the arms that are NOT `ModuleContribution`.
20
+ *
21
+ * `healthChecks` and `errorMappers` are declared as arrays only — arc reads
22
+ * them directly and never calls them. Routing them through the factory merge
23
+ * above would hand arc a function where it expects a list, which does not fail
24
+ * loudly: the arm reads as non-empty and contributes nothing.
25
+ */
26
+ function mergeArray(own, extra) {
27
+ if (own === void 0) return extra;
28
+ if (extra === void 0) return own;
29
+ return [...own, ...extra];
30
+ }
31
+ /** Order-preserving union — a duplicate edge or claim is not an error. */
32
+ function mergeNames(own, extra) {
33
+ if (own === void 0) return extra;
34
+ if (extra === void 0) return own;
35
+ return [.../* @__PURE__ */ new Set([...own, ...extra])];
36
+ }
37
+ function mergeOwns(own, extra) {
38
+ if (own === "provided" || extra === "provided") return "provided";
39
+ return mergeNames(own, extra);
40
+ }
41
+ /**
42
+ * Run both lifecycle hooks, module's first, composing whatever disposers they
43
+ * return. Teardown runs in REVERSE, matching arc's own module teardown order.
44
+ */
45
+ function mergeHook(own, extra) {
46
+ if (own === void 0) return extra;
47
+ if (extra === void 0) return own;
48
+ return async (fastify, context) => {
49
+ const d1 = await own(fastify, context);
50
+ const d2 = await extra(fastify, context);
51
+ if (!d1 && !d2) return void 0;
52
+ const disposer = async () => {
53
+ if (d2) await d2();
54
+ if (d1) await d1();
55
+ };
56
+ return disposer;
57
+ };
58
+ }
59
+ /**
60
+ * Close the MODULE first, then the host — the opposite of the disposer order
61
+ * above, and deliberately so.
62
+ *
63
+ * A disposer pair is symmetric setup/teardown, so it unwinds LIFO. `onClose` is
64
+ * not: the resource whose lifetime encloses everything is usually the ENGINE,
65
+ * and in a bring-your-own-engine composition the HOST owns it and hands it to
66
+ * the module. Running the host's `onClose` first would destroy the engine while
67
+ * the module is still draining work against it.
68
+ *
69
+ * be-prod's planning module states exactly this order in prose and implements
70
+ * it by hand — "the module's own onClose first (drains its built-in inFlight),
71
+ * then our host-side fallback inFlight, then destroy the BYO engine". Encoding
72
+ * it here is what stops the next host from getting it backwards.
73
+ */
74
+ function mergeOnClose(own, extra) {
75
+ if (own === void 0) return extra;
76
+ if (extra === void 0) return own;
77
+ return async (fastify) => {
78
+ await own(fastify);
79
+ await extra(fastify);
80
+ };
81
+ }
82
+ /**
83
+ * Merge a host's additions onto a module. Nothing the module declared is lost.
84
+ *
85
+ * ```ts
86
+ * return extendModule(createOrderModule({ … }), {
87
+ * eventHandlers: () => hostHandlers(), // appended after the module's
88
+ * dependsOn: ["outbox"], // unioned
89
+ * bootstrap: (inner) => async (f, c) => { // wraps, never replaces
90
+ * const exports = await inner?.(f, c);
91
+ * return decorate(exports);
92
+ * },
93
+ * });
94
+ * ```
95
+ */
96
+ function extendModule(mod, extension) {
97
+ const out = { ...mod };
98
+ /**
99
+ * Assign only when the merge produced something.
100
+ *
101
+ * An absent arm must stay ABSENT rather than become `[]` or `undefined` —
102
+ * arc distinguishes "contributes nothing" from "contributes an empty list"
103
+ * when deciding whether a module participates in an arm at all.
104
+ *
105
+ * Each merge is therefore evaluated ONCE, into this call. Writing the
106
+ * condition and the value as two separate calls would leave twelve pairs of
107
+ * duplicated expressions, where editing one half and not the other changes
108
+ * nothing visible and produces a module whose arm silently disagrees with the
109
+ * check that decided to set it.
110
+ */
111
+ const set = (key, value) => {
112
+ if (value !== void 0) out[key] = value;
113
+ };
114
+ set("dependsOn", mergeNames(mod.dependsOn, extension.dependsOn));
115
+ set("owns", mergeOwns(mod.owns, extension.owns));
116
+ set("resources", mergeContribution(mod.resources, extension.resources));
117
+ set("eventHandlers", mergeContribution(mod.eventHandlers, extension.eventHandlers));
118
+ set("scheduledJobs", mergeContribution(mod.scheduledJobs, extension.scheduledJobs));
119
+ set("workflows", mergeContribution(mod.workflows, extension.workflows));
120
+ set("healthChecks", mergeArray(mod.healthChecks, extension.healthChecks));
121
+ set("errorMappers", mergeArray(mod.errorMappers, extension.errorMappers));
122
+ set("plugins", mergeHook(mod.plugins, extension.plugins));
123
+ set("afterResources", mergeHook(mod.afterResources, extension.afterResources));
124
+ set("onClose", mergeOnClose(mod.onClose, extension.onClose));
125
+ if (extension.bootstrap) out.bootstrap = extension.bootstrap(mod.bootstrap);
126
+ return out;
127
+ }
128
+ //#endregion
6
129
  //#region src/factory/createWorker.ts
7
130
  async function createWorker(options, workerOptions = {}) {
8
131
  const { auth: _auth, helmet: _helmet, cors: _cors, rateLimit: _rateLimit, multipart: _multipart, rawBody: _rawBody, sensible: _sensible, underPressure: _underPressure, replyHelpers: _replyHelpers, ...runtimeOptions } = options;
@@ -19,10 +142,11 @@ async function createWorker(options, workerOptions = {}) {
19
142
  });
20
143
  if (workerOptions.health) {
21
144
  const { port, host = "0.0.0.0", checks = [] } = workerOptions.health;
145
+ const moduleChecks = app.arc?.healthChecks ?? [];
22
146
  const mergedChecks = mergeHealthChecks([
23
147
  {
24
148
  owner: "the module graph",
25
- checks: app.arc?.healthChecks ?? []
149
+ checks: moduleChecks
26
150
  },
27
151
  {
28
152
  owner: "shared arcPlugins.health",
@@ -120,4 +244,4 @@ function payloadTooLarge(limit) {
120
244
  });
121
245
  }
122
246
  //#endregion
123
- export { ArcFactory, collectModuleHealthChecks, collectModuleScheduledJobs, collectModuleWorkflows, createApp, createWorker, defineModule, developmentPreset, edgePreset, getModuleExports, getPreset, loadResources, orderModules, preloadResources, preloadResourcesAsync, productionPreset, resolveContribution, resolveModule, subscribeModuleEventHandlers, testingPreset, toFetchHandler };
247
+ export { ArcFactory, collectModuleHealthChecks, collectModuleScheduledJobs, collectModuleWorkflows, createApp, createWorker, defineModule, describeResolvedModule, developmentPreset, edgePreset, extendModule, getModuleExports, getModuleState, getOptionalModuleExports, getPreset, hasModule, hasModuleExports, lazyModuleExports, lazyRequiredModuleExports, loadResources, orderModules, preloadResources, preloadResourcesAsync, productionPreset, resolveContribution, resolveModule, subscribeModuleEventHandlers, testingPreset, toFetchHandler };