@classytic/arc 2.11.3 → 2.13.1

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 (185) hide show
  1. package/README.md +27 -18
  2. package/dist/{BaseController-swXruJ2_.mjs → BaseController-DX_T-bDB.mjs} +388 -423
  3. package/dist/EventTransport-CT_52aWU.d.mts +34 -0
  4. package/dist/EventTransport-DLWoUMHy.mjs +103 -0
  5. package/dist/{QueryCache-DOBNHBE0.d.mts → QueryCache-D41bfdBB.d.mts} +1 -1
  6. package/dist/{ResourceRegistry-DkAeAuTX.mjs → ResourceRegistry-CTERg_2x.mjs} +139 -66
  7. package/dist/audit/index.d.mts +2 -2
  8. package/dist/audit/index.mjs +1 -1
  9. package/dist/auth/audit.d.mts +199 -0
  10. package/dist/auth/audit.mjs +288 -0
  11. package/dist/auth/index.d.mts +5 -5
  12. package/dist/auth/index.mjs +117 -191
  13. package/dist/auth/redis-session.d.mts +1 -1
  14. package/dist/{betterAuthOpenApi-DwxtK3uG.mjs → betterAuthOpenApi--M_i87dQ.mjs} +1 -1
  15. package/dist/buildHandler-olo-gt94.mjs +610 -0
  16. package/dist/cache/index.d.mts +3 -3
  17. package/dist/cache/index.mjs +3 -3
  18. package/dist/cli/commands/describe.d.mts +89 -13
  19. package/dist/cli/commands/describe.mjs +56 -2
  20. package/dist/cli/commands/docs.mjs +2 -2
  21. package/dist/cli/commands/generate.mjs +147 -48
  22. package/dist/cli/commands/init.d.mts +13 -0
  23. package/dist/cli/commands/init.mjs +237 -112
  24. package/dist/cli/commands/introspect.mjs +8 -1
  25. package/dist/context/index.mjs +1 -1
  26. package/dist/core/index.d.mts +3 -3
  27. package/dist/core/index.mjs +5 -5
  28. package/dist/core-D72ia0EH.mjs +1399 -0
  29. package/dist/{createActionRouter-u3ql2EDo.mjs → createActionRouter-CEvzKcy8.mjs} +7 -20
  30. package/dist/createAggregationRouter-CyecOxnO.mjs +114 -0
  31. package/dist/{createApp-BFxtdKy6.mjs → createApp-XX2-N0Yd.mjs} +31 -27
  32. package/dist/defineEvent-D5h7EvAx.mjs +188 -0
  33. package/dist/docs/index.d.mts +2 -2
  34. package/dist/docs/index.mjs +2 -2
  35. package/dist/{elevation-DOFoxoDs.mjs → elevation-DgoeTyfX.mjs} +1 -1
  36. package/dist/errorHandler-Bk-AGhkU.mjs +174 -0
  37. package/dist/errorHandler-DFr45ZG4.d.mts +45 -0
  38. package/dist/errors-j4aJm1Wg.mjs +184 -0
  39. package/dist/{eventPlugin-KrFIQ097.mjs → eventPlugin-CaKTYkYM.mjs} +35 -137
  40. package/dist/{eventPlugin-CUNjYYRY.d.mts → eventPlugin-qXpqTebY.d.mts} +57 -7
  41. package/dist/events/index.d.mts +164 -5
  42. package/dist/events/index.mjs +133 -209
  43. package/dist/events/transports/redis-stream-entry.d.mts +1 -1
  44. package/dist/events/transports/redis-stream-entry.mjs +204 -31
  45. package/dist/events/transports/redis.d.mts +1 -1
  46. package/dist/factory/index.d.mts +2 -2
  47. package/dist/factory/index.mjs +2 -2
  48. package/dist/{fields-C8Y0XLAu.d.mts → fields-COhcH3fk.d.mts} +23 -2
  49. package/dist/hooks/index.d.mts +1 -1
  50. package/dist/hooks/index.mjs +1 -1
  51. package/dist/idempotency/index.d.mts +3 -3
  52. package/dist/idempotency/index.mjs +1 -20
  53. package/dist/idempotency/redis.d.mts +1 -1
  54. package/dist/idempotency/redis.mjs +1 -1
  55. package/dist/{index-BYCqHCVu.d.mts → index-BTqLEvhu.d.mts} +164 -4
  56. package/dist/{index-6u4_Gg6G.d.mts → index-BtW7qYwa.d.mts} +661 -281
  57. package/dist/{index-BdXnTPRj.d.mts → index-Ds61mrJE.d.mts} +50 -4
  58. package/dist/{index-DdQ3O9Pg.d.mts → index-Dz5IKsrE.d.mts} +360 -219
  59. package/dist/index.d.mts +6 -7
  60. package/dist/index.mjs +9 -10
  61. package/dist/integrations/event-gateway.d.mts +2 -2
  62. package/dist/integrations/event-gateway.mjs +1 -1
  63. package/dist/integrations/index.d.mts +2 -2
  64. package/dist/integrations/mcp/index.d.mts +2 -2
  65. package/dist/integrations/mcp/index.mjs +1 -1
  66. package/dist/integrations/mcp/testing.d.mts +1 -1
  67. package/dist/integrations/mcp/testing.mjs +1 -1
  68. package/dist/integrations/streamline.d.mts +60 -11
  69. package/dist/integrations/streamline.mjs +75 -85
  70. package/dist/integrations/websocket-redis.d.mts +1 -1
  71. package/dist/integrations/websocket.d.mts +1 -1
  72. package/dist/integrations/websocket.mjs +2 -8
  73. package/dist/middleware/index.d.mts +1 -1
  74. package/dist/middleware/index.mjs +2 -2
  75. package/dist/migrations/index.d.mts +23 -3
  76. package/dist/migrations/index.mjs +0 -7
  77. package/dist/{multipartBody-CvTR1Un6.mjs → multipartBody-BOvVSVCD.mjs} +11 -8
  78. package/dist/{openapi-BGUn7Ki1.mjs → openapi-CiOMVW1p.mjs} +143 -13
  79. package/dist/org/index.d.mts +2 -2
  80. package/dist/org/index.mjs +1 -1
  81. package/dist/permissions/index.d.mts +3 -3
  82. package/dist/permissions/index.mjs +3 -3
  83. package/dist/{permissions-gd_aUWrR.mjs → permissions-ohQyv50e.mjs} +404 -176
  84. package/dist/{pipe-DVoIheVC.mjs → pipe-Zr0KXjQe.mjs} +1 -1
  85. package/dist/pipeline/index.d.mts +1 -1
  86. package/dist/pipeline/index.mjs +1 -1
  87. package/dist/plugins/index.d.mts +18 -33
  88. package/dist/plugins/index.mjs +33 -13
  89. package/dist/plugins/response-cache.mjs +1 -1
  90. package/dist/plugins/tracing-entry.d.mts +1 -1
  91. package/dist/plugins/tracing-entry.mjs +1 -1
  92. package/dist/presets/filesUpload.d.mts +5 -5
  93. package/dist/presets/filesUpload.mjs +6 -9
  94. package/dist/presets/index.d.mts +1 -1
  95. package/dist/presets/index.mjs +1 -1
  96. package/dist/presets/multiTenant.d.mts +1 -1
  97. package/dist/presets/multiTenant.mjs +2 -2
  98. package/dist/presets/search.d.mts +2 -2
  99. package/dist/presets/search.mjs +6 -8
  100. package/dist/{presets-Z7P5w4gF.mjs → presets-BbkjdPeH.mjs} +6 -28
  101. package/dist/{queryCachePlugin-BUXBSm4F.d.mts → queryCachePlugin-CqMdLI2-.d.mts} +2 -2
  102. package/dist/{queryCachePlugin-Bq6bO6vc.mjs → queryCachePlugin-m1XsgAIJ.mjs} +3 -3
  103. package/dist/{redis-Cm1gnRDf.d.mts → redis-DiMkdHEl.d.mts} +1 -1
  104. package/dist/redis-stream-D6HzR1Z_.d.mts +232 -0
  105. package/dist/registry/index.d.mts +1 -1
  106. package/dist/registry/index.mjs +2 -2
  107. package/dist/{replyHelpers-ByllIXXV.mjs → replyHelpers-CK-FNO8E.mjs} +3 -21
  108. package/dist/{resourceToTools-ByZpgjeH.mjs → resourceToTools-C5coh64w.mjs} +224 -71
  109. package/dist/{routerShared-BqLRb5l7.mjs → routerShared-D6_fEGHh.mjs} +40 -36
  110. package/dist/{schemaIR-BlG9bY7v.mjs → schemaIR-7Vl611Qs.mjs} +1 -1
  111. package/dist/schemas/index.d.mts +100 -30
  112. package/dist/schemas/index.mjs +86 -29
  113. package/dist/scim/index.d.mts +264 -0
  114. package/dist/scim/index.mjs +963 -0
  115. package/dist/scope/index.d.mts +3 -3
  116. package/dist/scope/index.mjs +4 -4
  117. package/dist/{sse-V7aXc3bW.mjs → sse-Bz-5ZeTt.mjs} +1 -1
  118. package/dist/{store-helpers-BhrzxvyQ.mjs → store-helpers-BkIN9-vu.mjs} +1 -1
  119. package/dist/testing/index.d.mts +2 -8
  120. package/dist/testing/index.mjs +16 -24
  121. package/dist/testing/storageContract.d.mts +1 -1
  122. package/dist/types/index.d.mts +4 -4
  123. package/dist/types/storage.d.mts +1 -1
  124. package/dist/{types-BH7dEGvU.d.mts → types-BvqwCCSx.d.mts} +77 -29
  125. package/dist/{types-tgR4Pt8F.d.mts → types-CTYvcwHe.d.mts} +195 -1
  126. package/dist/{types-AOD8fxIw.mjs → types-C_s5moIu.mjs} +117 -1
  127. package/dist/{types-9beEMe25.d.mts → types-DQHFc8PM.d.mts} +1 -1
  128. package/dist/utils/index.d.mts +2 -2
  129. package/dist/utils/index.mjs +5 -5
  130. package/dist/{utils-CcYTj09l.mjs → utils-_h9B3c57.mjs} +1269 -1334
  131. package/dist/{versioning-M9lNLhO8.d.mts → versioning-DTTvc80y.d.mts} +1 -1
  132. package/package.json +24 -34
  133. package/skills/arc/SKILL.md +521 -785
  134. package/skills/arc/references/agent-auth.md +238 -0
  135. package/skills/arc/references/api-reference.md +187 -0
  136. package/skills/arc/references/auth.md +354 -7
  137. package/skills/arc/references/enterprise-auth.md +94 -0
  138. package/skills/arc/references/events.md +8 -6
  139. package/skills/arc/references/mcp.md +2 -2
  140. package/skills/arc/references/multi-tenancy.md +11 -2
  141. package/skills/arc/references/production.md +10 -9
  142. package/skills/arc/references/scim.md +247 -0
  143. package/skills/arc/references/testing.md +1 -1
  144. package/skills/arc-code-review/SKILL.md +141 -0
  145. package/skills/arc-code-review/references/anti-patterns.md +911 -0
  146. package/skills/arc-code-review/references/arc-cheatsheet.md +380 -0
  147. package/skills/arc-code-review/references/migration-recipes.md +700 -0
  148. package/skills/arc-code-review/references/mongokit-migration.md +386 -0
  149. package/skills/arc-code-review/references/scaffolding.md +230 -0
  150. package/skills/arc-code-review/references/severity.md +127 -0
  151. package/dist/EventTransport-CfVEGaEl.d.mts +0 -293
  152. package/dist/adapters/index.d.mts +0 -3
  153. package/dist/adapters/index.mjs +0 -2
  154. package/dist/adapters-D0tT2Tyo.mjs +0 -949
  155. package/dist/auth/mongoose.d.mts +0 -191
  156. package/dist/auth/mongoose.mjs +0 -73
  157. package/dist/core-DnUsRpuX.mjs +0 -1049
  158. package/dist/errorHandler-BQm8ZxTK.mjs +0 -173
  159. package/dist/errorHandler-Co3lnVmJ.d.mts +0 -114
  160. package/dist/errors-D5c-5BJL.mjs +0 -232
  161. package/dist/index-BbMrcvGp.d.mts +0 -362
  162. package/dist/redis-stream-CM8TXTix.d.mts +0 -110
  163. /package/dist/{HookSystem-CGsMd6oK.mjs → HookSystem-Iiebom92.mjs} +0 -0
  164. /package/dist/{actionPermissions-sUUKDhtP.mjs → actionPermissions-CyUkQu6O.mjs} +0 -0
  165. /package/dist/{caching-CheW3m-S.mjs → caching-SM8gghN6.mjs} +0 -0
  166. /package/dist/{constants-BhY1OHoH.mjs → constants-Cxde4rpC.mjs} +0 -0
  167. /package/dist/{elevation-s5ykdNHr.d.mts → elevation-BXOWoGCF.d.mts} +0 -0
  168. /package/dist/{externalPaths-Bapitwvd.d.mts → externalPaths-BD5nw6St.d.mts} +0 -0
  169. /package/dist/{interface-CkkWm5uR.d.mts → interface-DfLGcus7.d.mts} +0 -0
  170. /package/dist/{interface-Da0r7Lna.d.mts → interface-beEtJyWM.d.mts} +0 -0
  171. /package/dist/{keys-CARyUjiR.mjs → keys-CGcCbNyu.mjs} +0 -0
  172. /package/dist/{loadResources-CPpkyKfM.mjs → loadResources-DBMQg_Aj.mjs} +0 -0
  173. /package/dist/{memory-DikHSvWa.mjs → memory-UBydS5ku.mjs} +0 -0
  174. /package/dist/{metrics-Csh4nsvv.mjs → metrics-Qnvwc-LQ.mjs} +0 -0
  175. /package/dist/{pluralize-BneOJkpi.mjs → pluralize-DQgqgifU.mjs} +0 -0
  176. /package/dist/{registry-D63ee7fl.mjs → registry-I-ogLgL9.mjs} +0 -0
  177. /package/dist/{requestContext-C5XeK3VA.mjs → requestContext-SSaaTgW8.mjs} +0 -0
  178. /package/dist/{schemaConverter-B0oKLuqI.mjs → schemaConverter-De34B1ZG.mjs} +0 -0
  179. /package/dist/{sessionManager-D-oNWHz3.d.mts → sessionManager-C4Le_UB3.d.mts} +0 -0
  180. /package/dist/{storage-BwGQXUpd.d.mts → storage-Dfzt4VTl.d.mts} +0 -0
  181. /package/dist/{tracing-DokiEsuz.d.mts → tracing-QJVprktp.d.mts} +0 -0
  182. /package/dist/{typeGuards-CcFZXgU7.mjs → typeGuards-BzkXkvVv.mjs} +0 -0
  183. /package/dist/{types-DV9WDfeg.mjs → types-D57iXYb8.mjs} +0 -0
  184. /package/dist/{versioning-CGPjkqAg.mjs → versioning-BUrT5aP4.mjs} +0 -0
  185. /package/dist/{websocket-CyJ1VIFI.d.mts → websocket-ChC2rqe1.d.mts} +0 -0
@@ -1,4 +1,4 @@
1
- import { i as EventLogger, n as DomainEvent, o as EventTransport, r as EventHandler } from "./EventTransport-CfVEGaEl.mjs";
1
+ import { a as EventTransport, i as EventLogger, n as DomainEvent, r as EventHandler } from "./EventTransport-CT_52aWU.mjs";
2
2
  import { FastifyPluginAsync } from "fastify";
3
3
 
4
4
  //#region src/events/defineEvent.d.ts
@@ -73,8 +73,15 @@ interface EventRegistry {
73
73
  description?: string;
74
74
  schema?: EventSchema;
75
75
  }>;
76
- /** Validate a payload against a registered event's schema */
77
- validate(name: string, payload: unknown): ValidationResult;
76
+ /**
77
+ * Validate a payload against a registered event's schema.
78
+ *
79
+ * @param version - Optional schema version. When set, validation runs
80
+ * against that exact version (use during migrations: producer A still
81
+ * on v1 must validate against v1's schema even if v2 is registered).
82
+ * When omitted, validates against the latest registered version.
83
+ */
84
+ validate(name: string, payload: unknown, version?: number): ValidationResult;
78
85
  }
79
86
  /**
80
87
  * Define a typed event with optional schema validation.
@@ -155,8 +162,13 @@ interface RetryOptions {
155
162
  *
156
163
  * On failure, retries with exponential backoff (with jitter).
157
164
  * After all retries exhausted, calls `onDead` callback if provided.
165
+ *
166
+ * Generic in the payload type `T` so composing with `wrapWithSchema<T>` /
167
+ * `subscribeWithSchema<T>` doesn't force a cast at the boundary — the inner
168
+ * `handler: EventHandler<T>` flows through to the returned wrapper. Defaults
169
+ * to `unknown` for raw `subscribe(pattern, withRetry(...))` call sites.
158
170
  */
159
- declare function withRetry(handler: EventHandler, options?: RetryOptions): EventHandler;
171
+ declare function withRetry<T = unknown>(handler: EventHandler<T>, options?: RetryOptions): EventHandler<T>;
160
172
  /**
161
173
  * Create a dead letter publisher that sends failed events to a `$deadLetter` channel.
162
174
  *
@@ -203,9 +215,24 @@ interface EventPluginOptions {
203
215
  */
204
216
  failOpen?: boolean;
205
217
  /**
206
- * Write-Ahead Log (WAL) configuration for at-least-once delivery guarantees.
207
- * If provided, events will be saved to the WAL *before* passing to the transport.
208
- * After a successful publish, they are acknowledged.
218
+ * Low-level write-ahead hook called BEFORE the transport publish, with an
219
+ * optional acknowledge() called AFTER a successful publish.
220
+ *
221
+ * **Important**: this is NOT at-least-once delivery on its own. If
222
+ * `transport.publish()` throws after `wal.save()`, the saved row stays
223
+ * but arc does NOT relay it on next boot — there is no replay loop here.
224
+ * For at-least-once you must EITHER:
225
+ *
226
+ * 1. Run a relay loop yourself (read unacknowledged WAL rows on boot,
227
+ * republish, ack on success), or
228
+ * 2. Use `EventOutbox` ([./outbox.ts]) — `outbox.relay()` is the
229
+ * production-grade at-least-once primitive with claim/lease,
230
+ * retry/DLQ, multi-worker safety, and `repository`-backed durable
231
+ * storage. New code should prefer `EventOutbox` over `wal`.
232
+ *
233
+ * The `wal` slot is kept for hosts that want to integrate with custom
234
+ * write-ahead infrastructure (Kafka producer transactions, S3 batch
235
+ * archives, debug audit logs) without arc's outbox claim/lease semantics.
209
236
  */
210
237
  wal?: {
211
238
  save: (event: DomainEvent) => Promise<void>;
@@ -248,6 +275,29 @@ interface EventPluginOptions {
248
275
  * - `'off'`: skip validation entirely (registry is only for introspection)
249
276
  */
250
277
  validateMode?: "warn" | "reject" | "off";
278
+ /**
279
+ * Dev-mode duplicate-publish detector (v2.12).
280
+ *
281
+ * When enabled, arc keeps a 5-second LRU on `(eventType, correlationId)`
282
+ * and emits an `arcLog("events").warn(...)` the second time a request
283
+ * publishes the same event with the same correlation id within the
284
+ * window. Catches the dual-publish trap where a domain service holds
285
+ * BOTH a publisher AND a notification helper that internally publishes
286
+ * to the same bus — every subscriber fires twice for one logical event.
287
+ *
288
+ * Defaults:
289
+ * - `undefined` → enabled when `process.env.NODE_ENV !== 'production'`.
290
+ * - `true` → always enabled (catches duplicates in prod too — overhead
291
+ * is one Map lookup per publish).
292
+ * - `false` → always disabled.
293
+ *
294
+ * When a duplicate is detected, arc logs once and **still publishes** —
295
+ * the detector is observability, not enforcement. Pair with the outbox
296
+ * for at-most-once delivery.
297
+ *
298
+ * Documented in `wiki/gotchas.md` (#20).
299
+ */
300
+ warnOnDuplicate?: boolean;
251
301
  }
252
302
  declare module "fastify" {
253
303
  interface FastifyInstance {
@@ -1,8 +1,8 @@
1
- import { jn as RepositoryLike } from "../index-6u4_Gg6G.mjs";
2
- import { a as EventMeta, c as MemoryEventTransportOptions, d as createEvent, i as EventLogger, l as PublishManyResult, n as DomainEvent, o as EventTransport, r as EventHandler, s as MemoryEventTransport, t as DeadLetteredEvent, u as createChildEvent } from "../EventTransport-CfVEGaEl.mjs";
3
- import { a as withRetry, c as EventDefinitionOutput, d as EventSchema, f as ValidationResult, i as createDeadLetterPublisher, l as EventRegistry, m as defineEvent, n as eventPlugin, o as CustomValidator, p as createEventRegistry, r as RetryOptions, s as EventDefinitionInput, t as EventPluginOptions, u as EventRegistryOptions } from "../eventPlugin-CUNjYYRY.mjs";
1
+ import { a as EventTransport, i as EventLogger, n as DomainEvent, o as MemoryEventTransport, r as EventHandler, s as MemoryEventTransportOptions, t as DeadLetteredEvent } from "../EventTransport-CT_52aWU.mjs";
2
+ import { a as withRetry, c as EventDefinitionOutput, d as EventSchema, f as ValidationResult, i as createDeadLetterPublisher, l as EventRegistry, m as defineEvent, n as eventPlugin, o as CustomValidator, p as createEventRegistry, r as RetryOptions, s as EventDefinitionInput, t as EventPluginOptions, u as EventRegistryOptions } from "../eventPlugin-qXpqTebY.mjs";
4
3
  import { RedisEventTransportOptions, RedisLike } from "./transports/redis.mjs";
5
- import { r as RedisStreamTransportOptions, t as RedisStreamLike } from "../redis-stream-CM8TXTix.mjs";
4
+ import { r as RedisStreamTransportOptions, t as RedisStreamLike } from "../redis-stream-D6HzR1Z_.mjs";
5
+ import { RepositoryLike } from "@classytic/repo-core/adapter";
6
6
 
7
7
  //#region src/events/eventTypes.d.ts
8
8
  /**
@@ -600,4 +600,163 @@ declare function exponentialBackoff(options: ExponentialBackoffOptions): Date;
600
600
  //#region src/events/repository-outbox-adapter.d.ts
601
601
  declare function repositoryAsOutboxStore(repository: RepositoryLike): OutboxStore;
602
602
  //#endregion
603
- export { ARC_LIFECYCLE_EVENTS, type ArcLifecycleEvent, CACHE_EVENTS, CRUD_EVENT_SUFFIXES, type CacheEvent, type CrudEventSuffix, type CustomValidator, type DeadLetteredEvent, type DomainEvent, type EventDefinitionInput, type EventDefinitionOutput, type EventHandler, type EventLogger, type EventMeta, EventOutbox, type EventOutboxOptions, type EventPluginOptions, type EventRegistry, type EventRegistryOptions, type EventSchema, type EventTransport, type ExponentialBackoffOptions, InvalidOutboxEventError, MemoryEventTransport, type MemoryEventTransportOptions, MemoryOutboxStore, type OutboxAcknowledgeOptions, type OutboxClaimOptions, type OutboxErrorInfo, type OutboxFailOptions, type OutboxFailureContext, type OutboxFailureDecision, type OutboxFailurePolicy, OutboxOwnershipError, type OutboxRelayErrorHandler, type OutboxRelayErrorKind, type OutboxStore, type OutboxWriteOptions, type PublishManyResult, type RedisEventTransportOptions, type RedisLike, type RedisStreamLike, type RedisStreamTransportOptions, type RelayResult, type RetryOptions, type ValidationResult, createChildEvent, createDeadLetterPublisher, createEvent, createEventRegistry, crudEventType, defineEvent, eventPlugin, exponentialBackoff, repositoryAsOutboxStore, withRetry };
603
+ //#region src/events/subscribe-helpers.d.ts
604
+ /**
605
+ * Extract the payload type from an `EventDefinitionOutput<T>`.
606
+ *
607
+ * `defineEvent<T>` already threads `T` through `.create(payload: T, ...)`, but
608
+ * there's no exposed way to recover `T` for use in handler signatures, factory
609
+ * helpers, or test fixtures. `PayloadOf<typeof OrderPaid>` closes the loop
610
+ * without forcing every host to define their own copy.
611
+ *
612
+ * @example
613
+ * ```ts
614
+ * const OrderPaid = defineEvent<{ orderId: string; total: number }>({
615
+ * name: 'order.paid',
616
+ * schema: { type: 'object', required: ['orderId', 'total'] },
617
+ * });
618
+ * type OrderPaidPayload = PayloadOf<typeof OrderPaid>;
619
+ * // ^? { orderId: string; total: number }
620
+ * ```
621
+ */
622
+ type PayloadOf<D> = D extends EventDefinitionOutput<infer T> ? T : never;
623
+ interface WrapWithSchemaOptions<_T> {
624
+ /**
625
+ * Custom validator. Overrides the built-in lookup. Use this to plug AJV /
626
+ * Zod / TypeBox in. Same shape as `EventRegistryOptions.validate`.
627
+ *
628
+ * Resolution order:
629
+ * 1. `validate` (this option)
630
+ * 2. `registry.validate(definition.name, payload)` — uses whatever
631
+ * validator the registry was configured with
632
+ * 3. Built-in minimal validator (top-level `required` + property types)
633
+ */
634
+ validate?: CustomValidator;
635
+ /**
636
+ * Optional registry — when set and `validate` is omitted, validation routes
637
+ * through `registry.validate(definition.name, payload)`. Lets the subscriber
638
+ * use the same configured validator (AJV, custom) the publish side uses
639
+ * via `eventPlugin({ registry })`.
640
+ */
641
+ registry?: EventRegistry;
642
+ /**
643
+ * Called when payload validation fails. Default behaviour: log a warning
644
+ * with the event's id/type/errors and skip the handler (the event is NOT
645
+ * acknowledged as a failure, since the handler never ran — matches `withRetry`'s
646
+ * `onDead` semantics for terminal failures, but at the validation boundary).
647
+ *
648
+ * Receives the raw event (untyped — the payload is by definition not the
649
+ * declared shape) plus the validation errors array.
650
+ */
651
+ onInvalid?: (event: DomainEvent<unknown>, errors: string[]) => void | Promise<void>;
652
+ /**
653
+ * Logger for invalid-payload warnings. Pass `fastify.log` to integrate
654
+ * with the application logger. Default: `console`.
655
+ */
656
+ logger?: EventLogger;
657
+ /**
658
+ * Optional name for log output (otherwise the definition name is used).
659
+ */
660
+ name?: string;
661
+ }
662
+ /**
663
+ * Pure handler wrapper — returns a new `EventHandler` that validates
664
+ * `event.payload` against the definition's schema before invoking the handler.
665
+ *
666
+ * The returned handler's input is `DomainEvent<unknown>` (since the transport
667
+ * delivers untyped events) but the inner `handler` receives `DomainEvent<T>`.
668
+ * No cast at the call site.
669
+ *
670
+ * @example
671
+ * ```ts
672
+ * await fastify.events.subscribe(
673
+ * OrderPaid.name,
674
+ * wrapWithSchema(OrderPaid, async (event) => {
675
+ * // event.payload is typed via the registered schema — no cast.
676
+ * await postSalesEntry(event.payload.orderId, event.payload.total);
677
+ * }),
678
+ * );
679
+ * ```
680
+ */
681
+ declare function wrapWithSchema<T>(definition: EventDefinitionOutput<T>, handler: EventHandler<T>, options?: WrapWithSchemaOptions<T>): EventHandler<unknown>;
682
+ /**
683
+ * Convenience: validate + subscribe in one call. Equivalent to
684
+ * `fastify.events.subscribe(definition.name, wrapWithSchema(definition, handler, options))`.
685
+ *
686
+ * Returns the unsubscribe function from the underlying transport.
687
+ *
688
+ * @example
689
+ * ```ts
690
+ * await subscribeWithSchema(fastify, OrderPaid, async (event) => {
691
+ * await postSalesEntry(event.payload.orderId, event.payload.total);
692
+ * });
693
+ *
694
+ * // Compose with withRetry — schema validation runs FIRST, then retry on
695
+ * // handler failure. Invalid payloads skip without burning retry attempts.
696
+ * await subscribeWithSchema(
697
+ * fastify,
698
+ * OrderPaid,
699
+ * withRetry(handler, { maxRetries: 3 }),
700
+ * );
701
+ * ```
702
+ */
703
+ declare function subscribeWithSchema<T>(fastify: FastifyEventBus, definition: EventDefinitionOutput<T>, handler: EventHandler<T>, options?: WrapWithSchemaOptions<T>): Promise<() => void>;
704
+ interface WrapWithBoundaryOptions {
705
+ /**
706
+ * Called when the handler throws. Default behaviour: log the error with
707
+ * `{ err, event: event.type, eventId: event.meta.id }` and swallow.
708
+ *
709
+ * Use this to push metrics (`statsd.increment('handler.error', { type })`)
710
+ * or alert on specific event types.
711
+ */
712
+ onError?: (err: Error, event: DomainEvent) => void | Promise<void>;
713
+ /**
714
+ * Logger for handler errors. Pass `fastify.log` to integrate with the
715
+ * application logger. Default: `console`.
716
+ */
717
+ logger?: EventLogger;
718
+ /**
719
+ * Optional name for log output (otherwise the handler's `.name` is used).
720
+ */
721
+ name?: string;
722
+ }
723
+ /**
724
+ * Pure handler wrapper — returns a new `EventHandler` that catches handler
725
+ * exceptions and routes them to `onError` (or logs and swallows). For
726
+ * projection / cache-invalidation / fire-and-forget handlers where retry
727
+ * would just delay the next-event resync, and where one bad event must NOT
728
+ * stop processing of subsequent events.
729
+ *
730
+ * Lighter than `withRetry`: no exponential backoff, no DLQ. Composes with
731
+ * `withRetry` if you want both ("retry, then log if exhausted, never throw").
732
+ *
733
+ * @example
734
+ * ```ts
735
+ * await fastify.events.subscribe(
736
+ * 'product:variants.changed',
737
+ * wrapWithBoundary(async (event) => {
738
+ * cache.invalidate(event.payload.productId);
739
+ * }),
740
+ * );
741
+ * ```
742
+ */
743
+ declare function wrapWithBoundary(handler: EventHandler, options?: WrapWithBoundaryOptions): EventHandler;
744
+ /**
745
+ * Convenience: subscribe + error-boundary in one call. Equivalent to
746
+ * `fastify.events.subscribe(pattern, wrapWithBoundary(handler, options))`.
747
+ *
748
+ * Returns the unsubscribe function from the underlying transport.
749
+ */
750
+ declare function subscribeWithBoundary(fastify: FastifyEventBus, pattern: string, handler: EventHandler, options?: WrapWithBoundaryOptions): Promise<() => void>;
751
+ /**
752
+ * Structural type — accepts anything with the `events.subscribe` method,
753
+ * including `FastifyInstance` (declaration-merged in `eventPlugin.ts`) and
754
+ * test doubles. Avoids importing the full Fastify type in this module.
755
+ */
756
+ interface FastifyEventBus {
757
+ events: {
758
+ subscribe: (pattern: string, handler: EventHandler) => Promise<() => void>;
759
+ };
760
+ }
761
+ //#endregion
762
+ export { ARC_LIFECYCLE_EVENTS, type ArcLifecycleEvent, CACHE_EVENTS, CRUD_EVENT_SUFFIXES, type CacheEvent, type CrudEventSuffix, type CustomValidator, type EventDefinitionInput, type EventDefinitionOutput, EventOutbox, type EventOutboxOptions, type EventPluginOptions, type EventRegistry, type EventRegistryOptions, type EventSchema, type ExponentialBackoffOptions, InvalidOutboxEventError, MemoryEventTransport, type MemoryEventTransportOptions, MemoryOutboxStore, type OutboxAcknowledgeOptions, type OutboxClaimOptions, type OutboxErrorInfo, type OutboxFailOptions, type OutboxFailureContext, type OutboxFailureDecision, type OutboxFailurePolicy, OutboxOwnershipError, type OutboxRelayErrorHandler, type OutboxRelayErrorKind, type OutboxStore, type OutboxWriteOptions, type PayloadOf, type RedisEventTransportOptions, type RedisLike, type RedisStreamLike, type RedisStreamTransportOptions, type RelayResult, type RetryOptions, type ValidationResult, type WrapWithBoundaryOptions, type WrapWithSchemaOptions, createDeadLetterPublisher, createEventRegistry, crudEventType, defineEvent, eventPlugin, exponentialBackoff, repositoryAsOutboxStore, subscribeWithBoundary, subscribeWithSchema, withRetry, wrapWithBoundary, wrapWithSchema };
@@ -1,184 +1,9 @@
1
- import { a as MemoryEventTransport, i as withRetry, o as createChildEvent, r as createDeadLetterPublisher, s as createEvent, t as eventPlugin } from "../eventPlugin-KrFIQ097.mjs";
2
- import { n as createSafeGetOne, t as createIsDuplicateKeyError } from "../store-helpers-BhrzxvyQ.mjs";
1
+ import { t as MemoryEventTransport } from "../EventTransport-DLWoUMHy.mjs";
2
+ import { n as defineEvent, t as createEventRegistry } from "../defineEvent-D5h7EvAx.mjs";
3
+ import { i as withRetry, r as createDeadLetterPublisher, t as eventPlugin } from "../eventPlugin-CaKTYkYM.mjs";
4
+ import { n as createSafeGetOne, t as createIsDuplicateKeyError } from "../store-helpers-BkIN9-vu.mjs";
3
5
  import { and, anyOf, eq, lte, ne, or } from "@classytic/repo-core/filter";
4
6
  import { update } from "@classytic/repo-core/update";
5
- //#region src/events/defineEvent.ts
6
- /**
7
- * defineEvent — Typed Event Definitions with Optional Schema Validation
8
- *
9
- * Provides:
10
- * 1. defineEvent() — declare an event with name, schema, version, description
11
- * 2. EventRegistry — catalog of all known events + payload validation
12
- * 3. .create() helper — build DomainEvent with auto-generated metadata
13
- *
14
- * The built-in validator checks: object type, required fields, and top-level
15
- * property types. It does NOT recurse into nested objects, validate arrays,
16
- * enums, patterns, formats, or $ref. This is intentional — it's a lightweight
17
- * guard, not a full JSON Schema engine.
18
- *
19
- * For full validation, pass a custom `validate` function to `createEventRegistry()`:
20
- *
21
- * @example
22
- * ```typescript
23
- * import Ajv from 'ajv';
24
- * const ajv = new Ajv();
25
- *
26
- * const registry = createEventRegistry({
27
- * validate: (schema, payload) => {
28
- * const valid = ajv.validate(schema, payload);
29
- * return valid
30
- * ? { valid: true }
31
- * : { valid: false, errors: ajv.errorsText().split(', ') };
32
- * },
33
- * });
34
- * ```
35
- *
36
- * @example
37
- * ```typescript
38
- * import { defineEvent, createEventRegistry } from '@classytic/arc/events';
39
- *
40
- * const OrderCreated = defineEvent({
41
- * name: 'order.created',
42
- * version: 1,
43
- * schema: {
44
- * type: 'object',
45
- * properties: {
46
- * orderId: { type: 'string' },
47
- * total: { type: 'number' },
48
- * },
49
- * required: ['orderId', 'total'],
50
- * },
51
- * });
52
- *
53
- * // Type-safe event creation
54
- * const event = OrderCreated.create({ orderId: 'o-1', total: 100 });
55
- * await fastify.events.publish(event.type, event.payload, event.meta);
56
- *
57
- * // Registry for introspection + validation
58
- * const registry = createEventRegistry();
59
- * registry.register(OrderCreated);
60
- * const result = registry.validate('order.created', payload);
61
- * ```
62
- */
63
- /**
64
- * Define a typed event with optional schema validation.
65
- *
66
- * @example
67
- * const OrderCreated = defineEvent({
68
- * name: 'order.created',
69
- * schema: { type: 'object', properties: { orderId: { type: 'string' } }, required: ['orderId'] },
70
- * });
71
- *
72
- * const event = OrderCreated.create({ orderId: '123' });
73
- */
74
- function defineEvent(input) {
75
- const { name, schema, version = 1, description } = input;
76
- return {
77
- name,
78
- schema,
79
- version,
80
- description,
81
- create(payload, meta) {
82
- return createEvent(name, payload, meta);
83
- }
84
- };
85
- }
86
- /**
87
- * Create an event registry for cataloging and validating events.
88
- *
89
- * The registry is opt-in — unregistered events pass validation.
90
- * This allows gradual adoption without breaking existing code.
91
- *
92
- * @param options.validate - Custom validator replacing the built-in minimal validator.
93
- * The built-in validator only checks top-level object structure (type, required, property types).
94
- * For nested objects, arrays, enums, patterns, or $ref, provide AJV or similar.
95
- */
96
- function createEventRegistry(options) {
97
- const customValidator = options?.validate;
98
- const definitions = /* @__PURE__ */ new Map();
99
- function registryKey(name, version) {
100
- return `${name}:v${version}`;
101
- }
102
- return {
103
- register(definition) {
104
- const key = registryKey(definition.name, definition.version);
105
- if (definitions.has(key)) throw new Error(`Event '${definition.name}' v${definition.version} is already registered. Use a different version number for schema evolution.`);
106
- definitions.set(key, definition);
107
- },
108
- get(name, version) {
109
- if (version !== void 0) return definitions.get(registryKey(name, version));
110
- let latest;
111
- let latestVersion = -1;
112
- for (const def of definitions.values()) if (def.name === name && def.version > latestVersion) {
113
- latest = def;
114
- latestVersion = def.version;
115
- }
116
- return latest;
117
- },
118
- catalog() {
119
- return Array.from(definitions.values()).map((def) => ({
120
- name: def.name,
121
- version: def.version,
122
- description: def.description,
123
- schema: def.schema
124
- }));
125
- },
126
- validate(name, payload) {
127
- let latest;
128
- let latestVersion = -1;
129
- for (const def of definitions.values()) if (def.name === name && def.version > latestVersion) {
130
- latest = def;
131
- latestVersion = def.version;
132
- }
133
- if (!latest) return { valid: true };
134
- if (!latest.schema) return { valid: true };
135
- if (customValidator) return customValidator(latest.schema, payload);
136
- return validatePayload(payload, latest.schema);
137
- }
138
- };
139
- }
140
- /**
141
- * Built-in minimal validator — lightweight guard, NOT a full JSON Schema engine.
142
- *
143
- * Checks:
144
- * - payload is an object (not null, not array)
145
- * - required fields are present
146
- * - top-level property types match (string, number, boolean, array, object)
147
- *
148
- * Does NOT check:
149
- * - nested object properties
150
- * - array item types
151
- * - enum, pattern, format, minLength, minimum, $ref
152
- *
153
- * For full validation, pass a custom `validate` function to `createEventRegistry()`.
154
- */
155
- function validatePayload(payload, schema) {
156
- const errors = [];
157
- if (schema.type === "object") {
158
- if (payload === null || payload === void 0 || typeof payload !== "object" || Array.isArray(payload)) return {
159
- valid: false,
160
- errors: ["Payload must be an object"]
161
- };
162
- const record = payload;
163
- if (schema.required) {
164
- for (const field of schema.required) if (!(field in record) || record[field] === void 0) errors.push(`Missing required field: '${field}'`);
165
- }
166
- if (schema.properties) {
167
- for (const [key, propSchema] of Object.entries(schema.properties)) if (key in record && record[key] !== void 0 && record[key] !== null) {
168
- const expectedType = propSchema.type;
169
- if (expectedType) {
170
- const actualType = Array.isArray(record[key]) ? "array" : typeof record[key];
171
- if (expectedType !== actualType) errors.push(`Field '${key}': expected ${expectedType}, got ${actualType}`);
172
- }
173
- }
174
- }
175
- }
176
- return errors.length === 0 ? { valid: true } : {
177
- valid: false,
178
- errors
179
- };
180
- }
181
- //#endregion
182
7
  //#region src/events/eventTypes.ts
183
8
  /**
184
9
  * Event Type Constants and Helpers
@@ -345,30 +170,6 @@ var MemoryOutboxStore = class {
345
170
  };
346
171
  //#endregion
347
172
  //#region src/events/repository-outbox-adapter.ts
348
- /**
349
- * RepositoryLike → OutboxStore adapter.
350
- *
351
- * Maps the `OutboxStore` vocabulary (save / claimPending / acknowledge /
352
- * fail / getDeadLettered / purge) onto arc's own `RepositoryLike` primitives
353
- * (create / getOne / findAll / deleteMany / findOneAndUpdate). `EventOutbox`
354
- * wraps a passed repository with this helper when you use the
355
- * `{ repository }` option; the function is also re-exported from
356
- * `@classytic/arc/events` so consumers can build and decorate the store
357
- * manually (metrics, tracing, multi-transport fan-out).
358
- *
359
- * Portability: filters compose via `@classytic/repo-core/filter` and
360
- * updates via `@classytic/repo-core/update`. The primary-key column name
361
- * is read from `repository.idField` — mongokit defaults to `_id`,
362
- * sqlitekit / pgkit / prismakit to the schema's declared PK. The adapter
363
- * therefore runs on any kit that implements `StandardRepo.findOneAndUpdate`
364
- * + `getOne` + `getAll` + `deleteMany` + `create`.
365
- *
366
- * `fail()` uses a lease-gated read-then-write pair to preserve
367
- * `firstFailedAt` across retries without relying on Mongo's aggregation-
368
- * pipeline `$ifNull`. Leases guarantee single-writer during the failure
369
- * window (`claimPending` filters out non-owned rows), so the two calls are
370
- * safe under concurrent relayers.
371
- */
372
173
  const DEFAULT_LEASE_MS$1 = 3e4;
373
174
  const DEFAULT_CLAIM_LIMIT = 100;
374
175
  const DEFAULT_PURGE_BATCH = 500;
@@ -383,12 +184,12 @@ function repositoryAsOutboxStore(repository) {
383
184
  const r = repository;
384
185
  const idField = repository.idField ?? "_id";
385
186
  /**
386
- * Unwrap mongokit's pagination envelope ({ docs, total, ... }) — some
187
+ * Unwrap mongokit's pagination envelope ({ data, total, ... }) — some
387
188
  * kits may return a bare array when pagination is disabled. Handle both.
388
189
  */
389
190
  const unwrapDocs = (result) => {
390
191
  if (Array.isArray(result)) return result;
391
- return result?.docs ?? [];
192
+ return result?.data ?? [];
392
193
  };
393
194
  const isDuplicateKeyError = createIsDuplicateKeyError(repository);
394
195
  const safeGetOne = createSafeGetOne(repository);
@@ -707,7 +508,7 @@ var EventOutbox = class {
707
508
  const valid = [];
708
509
  let malformed = 0;
709
510
  for (const event of pending) {
710
- if (!event || !event.type || !event.meta?.id) {
511
+ if (!event?.type || !event.meta?.id) {
711
512
  this._reportError("malformed_event", new InvalidOutboxEventError("store returned event missing type or meta.id — batch aborted"), event);
712
513
  malformed++;
713
514
  break;
@@ -726,7 +527,7 @@ var EventOutbox = class {
726
527
  const canFail = typeof this._store.fail === "function";
727
528
  let publishOutcomes;
728
529
  if (canPublishMany && valid.length > 0) try {
729
- const result = await this._transport.publishMany(valid);
530
+ const result = await this._transport.publishMany?.(valid);
730
531
  publishOutcomes = new Map(result);
731
532
  } catch (batchErr) {
732
533
  publishOutcomes = /* @__PURE__ */ new Map();
@@ -771,7 +572,7 @@ var EventOutbox = class {
771
572
  this._reportError("fail_failed", policyErr, event);
772
573
  }
773
574
  try {
774
- await this._store.fail(event.meta.id, normalizeError(publishErr), failOpts);
575
+ await this._store.fail?.(event.meta.id, normalizeError(publishErr), failOpts);
775
576
  if (failOpts.deadLetter) {
776
577
  counts.deadLettered++;
777
578
  this._attempts.delete(event.meta.id);
@@ -886,4 +687,127 @@ function exponentialBackoff(options) {
886
687
  return new Date(now + jittered);
887
688
  }
888
689
  //#endregion
889
- export { ARC_LIFECYCLE_EVENTS, CACHE_EVENTS, CRUD_EVENT_SUFFIXES, EventOutbox, InvalidOutboxEventError, MemoryEventTransport, MemoryOutboxStore, OutboxOwnershipError, createChildEvent, createDeadLetterPublisher, createEvent, createEventRegistry, crudEventType, defineEvent, eventPlugin, exponentialBackoff, repositoryAsOutboxStore, withRetry };
690
+ //#region src/events/subscribe-helpers.ts
691
+ /**
692
+ * Pure handler wrapper — returns a new `EventHandler` that validates
693
+ * `event.payload` against the definition's schema before invoking the handler.
694
+ *
695
+ * The returned handler's input is `DomainEvent<unknown>` (since the transport
696
+ * delivers untyped events) but the inner `handler` receives `DomainEvent<T>`.
697
+ * No cast at the call site.
698
+ *
699
+ * @example
700
+ * ```ts
701
+ * await fastify.events.subscribe(
702
+ * OrderPaid.name,
703
+ * wrapWithSchema(OrderPaid, async (event) => {
704
+ * // event.payload is typed via the registered schema — no cast.
705
+ * await postSalesEntry(event.payload.orderId, event.payload.total);
706
+ * }),
707
+ * );
708
+ * ```
709
+ */
710
+ function wrapWithSchema(definition, handler, options = {}) {
711
+ const { validate, registry, onInvalid, logger = console, name } = options;
712
+ const label = name ?? definition.name;
713
+ return async (event) => {
714
+ const eventVersion = typeof event.meta?.schemaVersion === "number" ? event.meta.schemaVersion : definition.version;
715
+ let result;
716
+ if (validate && definition.schema) result = validate(definition.schema, event.payload);
717
+ else if (registry) result = registry.validate(definition.name, event.payload, eventVersion);
718
+ else if (definition.schema) {
719
+ const { createEventRegistry } = await import("../defineEvent-D5h7EvAx.mjs").then((n) => n.r);
720
+ const adhoc = createEventRegistry();
721
+ adhoc.register(definition);
722
+ result = adhoc.validate(definition.name, event.payload);
723
+ } else result = { valid: true };
724
+ if (!result.valid) {
725
+ const errors = result.errors ?? ["payload failed validation"];
726
+ if (onInvalid) try {
727
+ await onInvalid(event, errors);
728
+ } catch (cbErr) {
729
+ logger.error(`[Arc Events] '${label}' onInvalid callback threw:`, cbErr);
730
+ }
731
+ else logger.warn(`[Arc Events] '${label}' skipped event ${event.meta?.id ?? "<no-id>"} — payload failed validation: ${errors.join("; ")}`);
732
+ return;
733
+ }
734
+ await handler(event);
735
+ };
736
+ }
737
+ /**
738
+ * Convenience: validate + subscribe in one call. Equivalent to
739
+ * `fastify.events.subscribe(definition.name, wrapWithSchema(definition, handler, options))`.
740
+ *
741
+ * Returns the unsubscribe function from the underlying transport.
742
+ *
743
+ * @example
744
+ * ```ts
745
+ * await subscribeWithSchema(fastify, OrderPaid, async (event) => {
746
+ * await postSalesEntry(event.payload.orderId, event.payload.total);
747
+ * });
748
+ *
749
+ * // Compose with withRetry — schema validation runs FIRST, then retry on
750
+ * // handler failure. Invalid payloads skip without burning retry attempts.
751
+ * await subscribeWithSchema(
752
+ * fastify,
753
+ * OrderPaid,
754
+ * withRetry(handler, { maxRetries: 3 }),
755
+ * );
756
+ * ```
757
+ */
758
+ async function subscribeWithSchema(fastify, definition, handler, options) {
759
+ return fastify.events.subscribe(definition.name, wrapWithSchema(definition, handler, options));
760
+ }
761
+ /**
762
+ * Pure handler wrapper — returns a new `EventHandler` that catches handler
763
+ * exceptions and routes them to `onError` (or logs and swallows). For
764
+ * projection / cache-invalidation / fire-and-forget handlers where retry
765
+ * would just delay the next-event resync, and where one bad event must NOT
766
+ * stop processing of subsequent events.
767
+ *
768
+ * Lighter than `withRetry`: no exponential backoff, no DLQ. Composes with
769
+ * `withRetry` if you want both ("retry, then log if exhausted, never throw").
770
+ *
771
+ * @example
772
+ * ```ts
773
+ * await fastify.events.subscribe(
774
+ * 'product:variants.changed',
775
+ * wrapWithBoundary(async (event) => {
776
+ * cache.invalidate(event.payload.productId);
777
+ * }),
778
+ * );
779
+ * ```
780
+ */
781
+ function wrapWithBoundary(handler, options = {}) {
782
+ const { onError, logger = console, name } = options;
783
+ const label = name ?? handler.name ?? "anonymous";
784
+ return async (event) => {
785
+ try {
786
+ await handler(event);
787
+ } catch (err) {
788
+ const error = err instanceof Error ? err : new Error(String(err));
789
+ if (onError) try {
790
+ await onError(error, event);
791
+ } catch (cbErr) {
792
+ logger.error(`[Arc Events] '${label}' onError callback threw:`, cbErr);
793
+ }
794
+ else logger.error(`[Arc Events] '${label}' threw on ${event.type} — swallowed (boundary): ${error.message}`, {
795
+ err: error,
796
+ event: event.type,
797
+ eventId: event.meta?.id,
798
+ handler: label
799
+ });
800
+ }
801
+ };
802
+ }
803
+ /**
804
+ * Convenience: subscribe + error-boundary in one call. Equivalent to
805
+ * `fastify.events.subscribe(pattern, wrapWithBoundary(handler, options))`.
806
+ *
807
+ * Returns the unsubscribe function from the underlying transport.
808
+ */
809
+ async function subscribeWithBoundary(fastify, pattern, handler, options) {
810
+ return fastify.events.subscribe(pattern, wrapWithBoundary(handler, options));
811
+ }
812
+ //#endregion
813
+ export { ARC_LIFECYCLE_EVENTS, CACHE_EVENTS, CRUD_EVENT_SUFFIXES, EventOutbox, InvalidOutboxEventError, MemoryEventTransport, MemoryOutboxStore, OutboxOwnershipError, createDeadLetterPublisher, createEventRegistry, crudEventType, defineEvent, eventPlugin, exponentialBackoff, repositoryAsOutboxStore, subscribeWithBoundary, subscribeWithSchema, withRetry, wrapWithBoundary, wrapWithSchema };
@@ -1,2 +1,2 @@
1
- import { n as RedisStreamTransport, r as RedisStreamTransportOptions, t as RedisStreamLike } from "../../redis-stream-CM8TXTix.mjs";
1
+ import { n as RedisStreamTransport, r as RedisStreamTransportOptions, t as RedisStreamLike } from "../../redis-stream-D6HzR1Z_.mjs";
2
2
  export { type RedisStreamLike, RedisStreamTransport, type RedisStreamTransportOptions };