@classytic/arc 2.18.2 → 2.18.4

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 (153) hide show
  1. package/dist/{BaseController-DPwe89qs.mjs → BaseController-B-uXzf6I.mjs} +403 -216
  2. package/dist/{QueryCache-8YmAQ-zW.d.mts → QueryCache-Dsyv2mLS.d.mts} +1 -1
  3. package/dist/audit/index.d.mts +1 -1
  4. package/dist/auth/index.d.mts +4 -4
  5. package/dist/auth/index.mjs +4 -4
  6. package/dist/auth/redis-session.d.mts +1 -1
  7. package/dist/{betterAuthOpenApi-ClWxaceA.mjs → betterAuthOpenApi-DAAhMZff.mjs} +1 -1
  8. package/dist/{buildHandler-C_SkBNF3.mjs → buildHandler-Cvrc2p7r.mjs} +1 -1
  9. package/dist/cache/index.d.mts +3 -3
  10. package/dist/cache/index.mjs +3 -3
  11. package/dist/cli/commands/describe.d.mts +1 -1
  12. package/dist/cli/commands/describe.mjs +1 -1
  13. package/dist/cli/commands/docs.mjs +2 -2
  14. package/dist/cli/commands/generate.d.mts +1 -1
  15. package/dist/cli/commands/generate.mjs +1 -1
  16. package/dist/cli/commands/init.d.mts +1 -1
  17. package/dist/cli/commands/init.mjs +1 -1
  18. package/dist/cli/commands/introspect.mjs +1 -1
  19. package/dist/cli/index.d.mts +2 -2
  20. package/dist/cli/index.mjs +2 -2
  21. package/dist/core/index.d.mts +2 -2
  22. package/dist/core/index.mjs +3 -3
  23. package/dist/{core-B41eZxyM.mjs → core-C88uIwd3.mjs} +161 -103
  24. package/dist/{createActionRouter-CRaIwB5S.mjs → createActionRouter-DGCCp-kP.mjs} +4 -2
  25. package/dist/{createAggregationRouter-n3O6WdLO.mjs → createAggregationRouter-Ca77b8iK.mjs} +3 -3
  26. package/dist/{createApp-DZvrb587.mjs → createApp-J1iPXY9p.mjs} +11 -11
  27. package/dist/{defineEvent-NPKUJRt8.mjs → defineEvent-DC981g-o.mjs} +1 -1
  28. package/dist/docs/index.d.mts +2 -2
  29. package/dist/docs/index.mjs +2 -2
  30. package/dist/{elevation-6mfTLBC-.mjs → elevation-BzhyVWfn.mjs} +1 -1
  31. package/dist/{eventPlugin-Dxp9JlKK.mjs → eventPlugin-Bw4bUDEO.mjs} +2 -2
  32. package/dist/{eventPlugin-DjEx_J3j.d.mts → eventPlugin-C1W1CRbf.d.mts} +1 -1
  33. package/dist/events/index.d.mts +8 -5
  34. package/dist/events/index.mjs +26 -7
  35. package/dist/events/transports/redis-stream-entry.d.mts +1 -1
  36. package/dist/events/transports/redis-stream-entry.mjs +52 -5
  37. package/dist/events/transports/redis.d.mts +1 -1
  38. package/dist/factory/index.d.mts +1 -1
  39. package/dist/factory/index.mjs +3 -3
  40. package/dist/{fastify-augmentation-CL48B6gW.d.mts → fastify-augmentation-BdCl_eNo.d.mts} +2 -2
  41. package/dist/{fields-DqZLp5d6.d.mts → fields-CKLrhyOb.d.mts} +1 -1
  42. package/dist/{generate-CcM24hoq.mjs → generate-BaxDdmJ8.mjs} +1 -1
  43. package/dist/hooks/index.d.mts +1 -1
  44. package/dist/hooks/index.mjs +1 -1
  45. package/dist/idempotency/index.d.mts +2 -2
  46. package/dist/idempotency/index.mjs +1 -1
  47. package/dist/idempotency/redis.d.mts +1 -1
  48. package/dist/{index-CNiryUMN.d.mts → index-Cvo1u9n6.d.mts} +3 -3
  49. package/dist/{index-C000faom.d.mts → index-QYma8mdC.d.mts} +215 -241
  50. package/dist/{index-3L-adFJO.d.mts → index-vcWUGkwf.d.mts} +3 -3
  51. package/dist/index.d.mts +5 -5
  52. package/dist/index.mjs +5 -5
  53. package/dist/{init-DQ_rY1J7.mjs → init-DSsdRfk9.mjs} +3 -3
  54. package/dist/integrations/event-gateway.d.mts +2 -2
  55. package/dist/integrations/event-gateway.mjs +1 -1
  56. package/dist/integrations/index.d.mts +2 -2
  57. package/dist/integrations/jobs.mjs +66 -1
  58. package/dist/integrations/mcp/index.d.mts +4 -4
  59. package/dist/integrations/mcp/index.mjs +1 -1
  60. package/dist/integrations/mcp/testing.d.mts +1 -1
  61. package/dist/integrations/mcp/testing.mjs +1 -1
  62. package/dist/integrations/streamline.d.mts +16 -1
  63. package/dist/integrations/streamline.mjs +33 -2
  64. package/dist/integrations/websocket-pushref-redis.d.mts +1 -1
  65. package/dist/integrations/websocket-redis.d.mts +1 -1
  66. package/dist/integrations/websocket.d.mts +2 -2
  67. package/dist/{loadResourcesFromEntry-DCjYMgfC.mjs → loadResourcesFromEntry-XHNeUWpu.mjs} +1 -1
  68. package/dist/{mcpPlugin-BrepZ0cV.mjs → mcpPlugin-CzKdt3B8.mjs} +9 -9
  69. package/dist/middleware/index.d.mts +1 -1
  70. package/dist/middleware/index.mjs +1 -1
  71. package/dist/{openapi-CLZx9bwS.mjs → openapi-ButnRyOM.mjs} +5 -5
  72. package/dist/permissions/index.d.mts +2 -2
  73. package/dist/permissions/index.mjs +2 -2
  74. package/dist/{permissions-B1Qy55fg.mjs → permissions-3uzwkbQt.mjs} +99 -99
  75. package/dist/pipeline/index.d.mts +1 -1
  76. package/dist/pipeline/index.mjs +1 -1
  77. package/dist/pipeline-D4sqiokR.mjs +336 -0
  78. package/dist/plugins/index.d.mts +5 -5
  79. package/dist/plugins/index.mjs +9 -9
  80. package/dist/plugins/response-cache.mjs +1 -1
  81. package/dist/plugins/tracing-entry.d.mts +1 -1
  82. package/dist/plugins/tracing-entry.mjs +1 -1
  83. package/dist/presets/filesUpload.d.mts +4 -4
  84. package/dist/presets/filesUpload.mjs +2 -2
  85. package/dist/presets/index.d.mts +1 -1
  86. package/dist/presets/index.mjs +1 -1
  87. package/dist/presets/multiTenant.d.mts +1 -1
  88. package/dist/presets/search.d.mts +2 -2
  89. package/dist/presets/search.mjs +1 -1
  90. package/dist/{presets-BYbPobbF.mjs → presets-CF79RHEo.mjs} +1 -1
  91. package/dist/{queryCachePlugin-Do4P_WBC.d.mts → queryCachePlugin-Bd2tYgh5.d.mts} +2 -2
  92. package/dist/{queryCachePlugin-B4XMSSe7.mjs → queryCachePlugin-CnAeFqju.mjs} +3 -3
  93. package/dist/rateLimit-yKvxWAky.mjs +207 -0
  94. package/dist/{redis-cpqVb85w.d.mts → redis-3hqH_Qk_.d.mts} +1 -1
  95. package/dist/{redis-stream-BuiAS3aE.d.mts → redis-stream-BtEUtjxZ.d.mts} +5 -4
  96. package/dist/registerRoute-BEYn0ylk.mjs +108 -0
  97. package/dist/registry/index.d.mts +1 -1
  98. package/dist/registry/index.mjs +2 -2
  99. package/dist/{schemaIR-Bk6Tns82.mjs → schemaIR-CE6USIRi.mjs} +1 -1
  100. package/dist/scope/index.d.mts +2 -2
  101. package/dist/scope/index.mjs +2 -2
  102. package/dist/{sse-jh5OqFjR.mjs → sse-BrY1W6U8.mjs} +11 -2
  103. package/dist/testing/index.d.mts +2 -2
  104. package/dist/testing/index.mjs +12 -2
  105. package/dist/testing/storageContract.d.mts +1 -1
  106. package/dist/types/index.d.mts +4 -4
  107. package/dist/types/storage.d.mts +1 -1
  108. package/dist/{types-DH-7WO64.d.mts → types-BWBcb88c.d.mts} +1 -1
  109. package/dist/{types-DEEWwQFR.d.mts → types-WVlAVp8T.d.mts} +10 -10
  110. package/dist/utils/index.d.mts +3 -3
  111. package/dist/utils/index.mjs +3 -3
  112. package/dist/{utils-BMQK8mRl.mjs → utils-xAQeDyU_.mjs} +1 -1
  113. package/dist/{validate-DFIP2VrM.mjs → validate-0__MuUsK.mjs} +201 -190
  114. package/dist/{versioning-5H4930tj.d.mts → versioning-BSEDGXKC.d.mts} +1 -1
  115. package/dist/{websocket-BZMUP7Gr.d.mts → websocket-BFBT5rH-.d.mts} +1 -1
  116. package/package.json +5 -5
  117. package/skills/arc/references/api-reference.md +1 -1
  118. package/dist/routerShared-Dl0A77Ba.mjs +0 -724
  119. /package/dist/{EventTransport-ClEDfiMS.mjs → EventTransport-7LPXLEUX.mjs} +0 -0
  120. /package/dist/{EventTransport-B1GFiYUy.d.mts → EventTransport-CaaXM5Y1.d.mts} +0 -0
  121. /package/dist/{HookSystem-Cmf7-Etp.mjs → HookSystem-BfCpWbwM.mjs} +0 -0
  122. /package/dist/{ResourceRegistry-CzSzVK1m.mjs → ResourceRegistry-CWmOggxS.mjs} +0 -0
  123. /package/dist/{actionPermissions-Bjmvn7Eb.mjs → actionPermissions-CX9QIMTd.mjs} +0 -0
  124. /package/dist/{caching-8MfrrUYU.mjs → caching-C0DdKiYB.mjs} +0 -0
  125. /package/dist/{elevation-DvwtjDwu.d.mts → elevation-DW90z5bT.d.mts} +0 -0
  126. /package/dist/{errorHandler-wQejMBT3.mjs → errorHandler-Bua97SEt.mjs} +0 -0
  127. /package/dist/{errorHandler-DuqTqeqn.d.mts → errorHandler-_Lp2k9M3.d.mts} +0 -0
  128. /package/dist/{errors-4ZloYFvm.d.mts → errors-DZOemeGa.d.mts} +0 -0
  129. /package/dist/{externalPaths-ClykOQqa.d.mts → externalPaths-DLle0R07.d.mts} +0 -0
  130. /package/dist/{generate-cRnsafMS.d.mts → generate-Dmk8cfvX.d.mts} +0 -0
  131. /package/dist/{init-D-vrTZ1G.d.mts → init-BS4Daqnv.d.mts} +0 -0
  132. /package/dist/{interface-DoE6UoC2.d.mts → interface-CsrGR8LF.d.mts} +0 -0
  133. /package/dist/{interface-BVkZsvEe.d.mts → interface-DL7LlWS5.d.mts} +0 -0
  134. /package/dist/{keys-DopsCuyQ.mjs → keys-B3yyBEyH.mjs} +0 -0
  135. /package/dist/{loadResources-fol6JuvW.mjs → loadResources-CwXt9MCh.mjs} +0 -0
  136. /package/dist/{memory-QOLe11D5.mjs → memory-TdTHCS9-.mjs} +0 -0
  137. /package/dist/{metrics-B-PU4-Yu.mjs → metrics-Da2EUPi9.mjs} +0 -0
  138. /package/dist/{multipartBody-VjVWmZl8.mjs → multipartBody-709YcXi1.mjs} +0 -0
  139. /package/dist/{pipe-DiCyvyPN.mjs → pipe-9Tl-OAus.mjs} +0 -0
  140. /package/dist/{pluralize-B9M8xvy-.mjs → pluralize-BxdUl2P4.mjs} +0 -0
  141. /package/dist/{preloadResources-BOXzPhKU.mjs → preloadResources-B-j-iqo-.mjs} +0 -0
  142. /package/dist/{pushref-registry-Bq_-EVsC.d.mts → pushref-registry-Dqclzjy7.d.mts} +0 -0
  143. /package/dist/{registry-BrlHkrRS.mjs → registry-Br15xdGM.mjs} +0 -0
  144. /package/dist/{replyHelpers-boS5Rx26.mjs → replyHelpers-DXHVxrrj.mjs} +0 -0
  145. /package/dist/{schemaConverter-De34B1ZG.mjs → schemaConverter-DatAaVNj.mjs} +0 -0
  146. /package/dist/{sessionManager-DlmaHUAl.d.mts → sessionManager-CyHpYXoB.d.mts} +0 -0
  147. /package/dist/{storage-BFmamN5m.d.mts → storage-C5q8fuEt.d.mts} +0 -0
  148. /package/dist/{store-helpers-5JgaxieH.mjs → store-helpers-DjSYI2uP.mjs} +0 -0
  149. /package/dist/{tracing-BYQ3hsbM.d.mts → tracing-Bnb1-YVd.d.mts} +0 -0
  150. /package/dist/{typeGuards-BzkXkvVv.mjs → typeGuards-CcFZXgU7.mjs} +0 -0
  151. /package/dist/{types-CfapW2t8.d.mts → types-BJwJN2Pg.d.mts} +0 -0
  152. /package/dist/{types-DV9WDfeg.mjs → types-D57iXYb8.mjs} +0 -0
  153. /package/dist/{versioning-CuIwUQZ2.mjs → versioning-C0K8KQLx.mjs} +0 -0
@@ -1,5 +1,5 @@
1
- import { p as RegistryEntry } from "../index-C000faom.mjs";
2
- import { t as ExternalOpenApiPaths } from "../externalPaths-ClykOQqa.mjs";
1
+ import { p as RegistryEntry } from "../index-QYma8mdC.mjs";
2
+ import { t as ExternalOpenApiPaths } from "../externalPaths-DLle0R07.mjs";
3
3
  import { FastifyPluginAsync } from "fastify";
4
4
 
5
5
  //#region src/docs/openapi/types.d.ts
@@ -1,5 +1,5 @@
1
- import { t as getUserRoles } from "../types-DV9WDfeg.mjs";
2
- import { n as openApiPlugin, r as openapi_default, t as buildOpenApiSpec } from "../openapi-CLZx9bwS.mjs";
1
+ import { t as getUserRoles } from "../types-D57iXYb8.mjs";
2
+ import { n as openApiPlugin, r as openapi_default, t as buildOpenApiSpec } from "../openapi-ButnRyOM.mjs";
3
3
  import fp from "fastify-plugin";
4
4
  //#region src/docs/scalar.ts
5
5
  const scalarPlugin = async (fastify, opts = {}) => {
@@ -1,6 +1,6 @@
1
1
  import { t as __exportAll } from "./chunk-BpYLSNr0.mjs";
2
2
  import { arcLog } from "./logger/index.mjs";
3
- import { t as getUserRoles } from "./types-DV9WDfeg.mjs";
3
+ import { t as getUserRoles } from "./types-D57iXYb8.mjs";
4
4
  import fp from "fastify-plugin";
5
5
  //#region src/scope/elevation.ts
6
6
  var elevation_exports = /* @__PURE__ */ __exportAll({
@@ -2,7 +2,7 @@ import { t as __exportAll } from "./chunk-BpYLSNr0.mjs";
2
2
  import { arcLog } from "./logger/index.mjs";
3
3
  import { d as createDomainError } from "./errors-j4aJm1Wg.mjs";
4
4
  import { n as requestContext } from "./requestContext-DDLS3NVU.mjs";
5
- import { n as createEvent, t as MemoryEventTransport } from "./EventTransport-ClEDfiMS.mjs";
5
+ import { n as createEvent, t as MemoryEventTransport } from "./EventTransport-7LPXLEUX.mjs";
6
6
  import fp from "fastify-plugin";
7
7
  //#region src/events/retry.ts
8
8
  /**
@@ -115,7 +115,7 @@ const eventPlugin = async (fastify, opts = {}) => {
115
115
  const validateMode = rawValidateMode ?? (registry ? "warn" : "off");
116
116
  const warnOnDuplicate = rawWarnOnDuplicate ?? process.env.NODE_ENV !== "production";
117
117
  const DUP_WINDOW_MS = 5e3;
118
- const recentPublishes = warnOnDuplicate ? /* @__PURE__ */ new Map() : /* @__PURE__ */ new Map();
118
+ const recentPublishes = /* @__PURE__ */ new Map();
119
119
  const evictExpiredPublishes = (now) => {
120
120
  if (recentPublishes.size === 0) return;
121
121
  for (const [key, timestamp] of recentPublishes) {
@@ -1,4 +1,4 @@
1
- import { a as EventTransport, i as EventLogger, n as DomainEvent, r as EventHandler } from "./EventTransport-B1GFiYUy.mjs";
1
+ import { a as EventTransport, i as EventLogger, n as DomainEvent, r as EventHandler } from "./EventTransport-CaaXM5Y1.mjs";
2
2
  import { FastifyPluginAsync } from "fastify";
3
3
 
4
4
  //#region src/events/defineEvent.d.ts
@@ -1,7 +1,7 @@
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-B1GFiYUy.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-DjEx_J3j.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-CaaXM5Y1.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-C1W1CRbf.mjs";
3
3
  import { RedisEventTransportOptions, RedisLike } from "./transports/redis.mjs";
4
- import { r as RedisStreamTransportOptions, t as RedisStreamLike } from "../redis-stream-BuiAS3aE.mjs";
4
+ import { r as RedisStreamTransportOptions, t as RedisStreamLike } from "../redis-stream-BtEUtjxZ.mjs";
5
5
  import { RepositoryLike } from "@classytic/repo-core/adapter";
6
6
 
7
7
  //#region src/events/eventTypes.d.ts
@@ -511,8 +511,11 @@ declare class EventOutbox {
511
511
  /**
512
512
  * In-process attempt counter per event id. Accurate within this relay
513
513
  * process; resets on restart. Populated as failures occur and cleared on
514
- * successful ack or dead-letter transition. For durable authoritative
515
- * counts, apps can query the store directly inside {@link OutboxFailurePolicy}.
514
+ * successful ack or dead-letter transition; additionally hard-capped at
515
+ * {@link MAX_TRACKED_ATTEMPTS} (oldest-write evicted first) so events
516
+ * resolved by another relay worker can't accumulate forever. For durable
517
+ * authoritative counts, apps can query the store directly inside
518
+ * {@link OutboxFailurePolicy}.
516
519
  */
517
520
  private readonly _attempts;
518
521
  constructor(opts: EventOutboxOptions);
@@ -1,7 +1,7 @@
1
- import { t as MemoryEventTransport } from "../EventTransport-ClEDfiMS.mjs";
2
- import { n as defineEvent, t as createEventRegistry } from "../defineEvent-NPKUJRt8.mjs";
3
- import { i as withRetry, r as createDeadLetterPublisher, t as eventPlugin } from "../eventPlugin-Dxp9JlKK.mjs";
4
- import { n as createSafeGetOne, t as createIsDuplicateKeyError } from "../store-helpers-5JgaxieH.mjs";
1
+ import { t as MemoryEventTransport } from "../EventTransport-7LPXLEUX.mjs";
2
+ import { n as defineEvent, t as createEventRegistry } from "../defineEvent-DC981g-o.mjs";
3
+ import { i as withRetry, r as createDeadLetterPublisher, t as eventPlugin } from "../eventPlugin-Bw4bUDEO.mjs";
4
+ import { n as createSafeGetOne, t as createIsDuplicateKeyError } from "../store-helpers-DjSYI2uP.mjs";
5
5
  import { and, anyOf, eq, lte, ne, or } from "@classytic/repo-core/filter";
6
6
  import { update } from "@classytic/repo-core/update";
7
7
  //#region src/events/eventTypes.ts
@@ -352,6 +352,17 @@ function repositoryAsOutboxStore(repository) {
352
352
  const DEFAULT_OUTBOX_RETENTION_MS = 10080 * 60 * 1e3;
353
353
  const DEFAULT_LEASE_MS = 3e4;
354
354
  /**
355
+ * Hard cap on the in-process attempt-counter map. Entries are cleared on
356
+ * local ack and on dead-letter, but an event that fails HERE and is later
357
+ * acked by a DIFFERENT relay worker (lease expiry → reclaim) never drains
358
+ * locally — without a cap the map grows for the life of the process in
359
+ * multi-worker deployments. Eviction is oldest-write-first; evicting an
360
+ * entry only resets its non-authoritative in-process count (the documented
361
+ * contract already says counts reset on restart — durable counts live in
362
+ * the store).
363
+ */
364
+ const MAX_TRACKED_ATTEMPTS = 1e4;
365
+ /**
355
366
  * Thrown by a store when `acknowledge` / `fail` is called by a consumer that
356
367
  * does not own the event's current lease.
357
368
  *
@@ -393,8 +404,11 @@ var EventOutbox = class {
393
404
  /**
394
405
  * In-process attempt counter per event id. Accurate within this relay
395
406
  * process; resets on restart. Populated as failures occur and cleared on
396
- * successful ack or dead-letter transition. For durable authoritative
397
- * counts, apps can query the store directly inside {@link OutboxFailurePolicy}.
407
+ * successful ack or dead-letter transition; additionally hard-capped at
408
+ * {@link MAX_TRACKED_ATTEMPTS} (oldest-write evicted first) so events
409
+ * resolved by another relay worker can't accumulate forever. For durable
410
+ * authoritative counts, apps can query the store directly inside
411
+ * {@link OutboxFailurePolicy}.
398
412
  */
399
413
  _attempts = /* @__PURE__ */ new Map();
400
414
  constructor(opts) {
@@ -578,7 +592,12 @@ var EventOutbox = class {
578
592
  continue;
579
593
  }
580
594
  const attempts = (this._attempts.get(event.meta.id) ?? 0) + 1;
595
+ this._attempts.delete(event.meta.id);
581
596
  this._attempts.set(event.meta.id, attempts);
597
+ if (this._attempts.size > MAX_TRACKED_ATTEMPTS) {
598
+ const oldest = this._attempts.keys().next().value;
599
+ if (oldest !== void 0) this._attempts.delete(oldest);
600
+ }
582
601
  let failOpts = { consumerId: this._consumerId };
583
602
  if (this._failurePolicy) try {
584
603
  const decision = await this._failurePolicy({
@@ -738,7 +757,7 @@ function wrapWithSchema(definition, handler, options = {}) {
738
757
  if (validate && definition.schema) result = validate(definition.schema, event.payload);
739
758
  else if (registry) result = registry.validate(definition.name, event.payload, eventVersion);
740
759
  else if (definition.schema) {
741
- const { createEventRegistry } = await import("../defineEvent-NPKUJRt8.mjs").then((n) => n.r);
760
+ const { createEventRegistry } = await import("../defineEvent-DC981g-o.mjs").then((n) => n.r);
742
761
  const adhoc = createEventRegistry();
743
762
  adhoc.register(definition);
744
763
  result = adhoc.validate(definition.name, event.payload);
@@ -1,2 +1,2 @@
1
- import { n as RedisStreamTransport, r as RedisStreamTransportOptions, t as RedisStreamLike } from "../../redis-stream-BuiAS3aE.mjs";
1
+ import { n as RedisStreamTransport, r as RedisStreamTransportOptions, t as RedisStreamLike } from "../../redis-stream-BtEUtjxZ.mjs";
2
2
  export { type RedisStreamLike, RedisStreamTransport, type RedisStreamTransportOptions };
@@ -32,6 +32,16 @@ import sjson from "secure-json-parse";
32
32
  * await app.register(eventPlugin, { transport });
33
33
  * ```
34
34
  */
35
+ /**
36
+ * Hard cap on the failure-context map. Entries are deleted on local ack and
37
+ * on DLQ write, but a message that fails HERE and is later acked or
38
+ * dead-lettered by a DIFFERENT consumer in the group never gets cleared
39
+ * locally — without a cap the map grows for the life of the process in
40
+ * multi-consumer deployments. Eviction is oldest-first (Map insertion
41
+ * order); an evicted entry only degrades the eventual DLQ envelope to the
42
+ * existing cross-consumer fallback message, never loses the event itself.
43
+ */
44
+ const MAX_FAILURE_CONTEXT_ENTRIES = 1024;
35
45
  var RedisStreamTransport = class {
36
46
  name = "redis-stream";
37
47
  redis;
@@ -67,9 +77,10 @@ var RedisStreamTransport = class {
67
77
  * Last-seen failure context per message id, populated when an in-process
68
78
  * handler throws in {@link processEntry}. Consumed (and cleared) by
69
79
  * {@link moveToDlq} so the dead-letter envelope carries the actual error
70
- * message instead of opaque "reclaimed without context". Bounded by
71
- * `maxRetries × consumer-throughput` — entries are deleted on ack and
72
- * on DLQ write, so the map naturally drains.
80
+ * message instead of opaque "reclaimed without context". Entries are
81
+ * deleted on ack and on DLQ write; messages resolved by ANOTHER consumer
82
+ * never drain locally, so the map is additionally hard-capped at
83
+ * {@link MAX_FAILURE_CONTEXT_ENTRIES} with oldest-first eviction.
73
84
  */
74
85
  failureContext = /* @__PURE__ */ new Map();
75
86
  /** One-shot guard so the "client lacks xrange" warning fires once per process. */
@@ -168,6 +179,7 @@ var RedisStreamTransport = class {
168
179
  this.running = false;
169
180
  this.generation++;
170
181
  this.handlers.clear();
182
+ this.failureContext.clear();
171
183
  if (this.pollPromise) {
172
184
  if (await Promise.race([this.pollPromise.then(() => "drained"), this.sleep(this.closeTimeoutMs).then(() => "timeout")]) === "timeout") if (!this.externalLifecycle) {
173
185
  if (typeof this.redis.disconnect === "function") this.redis.disconnect();
@@ -190,13 +202,16 @@ var RedisStreamTransport = class {
190
202
  this.groupCreated = true;
191
203
  }
192
204
  async pollLoop(myGen) {
205
+ let pollErrorAttempts = 0;
193
206
  while (this.running && this.generation === myGen) try {
194
207
  await this.claimPending();
195
208
  await this.readNewMessages();
209
+ pollErrorAttempts = 0;
196
210
  } catch (err) {
197
211
  if (this.running && this.generation === myGen) {
212
+ pollErrorAttempts++;
198
213
  this.logger.error("[RedisStreamTransport] Poll error:", err);
199
- await this.sleep(1e3);
214
+ await this.sleep(fullJitterBackoffMs(pollErrorAttempts));
200
215
  }
201
216
  }
202
217
  }
@@ -207,7 +222,7 @@ var RedisStreamTransport = class {
207
222
  }
208
223
  async claimPending() {
209
224
  try {
210
- const pending = await this.redis.xpending(this.stream, this.group, "-", "+", 10);
225
+ const pending = await this.redis.xpending(this.stream, this.group, "-", "+", Math.max(this.batchSize, 10));
211
226
  if (!pending || pending.length === 0) return;
212
227
  const staleIds = [];
213
228
  const overRetryIds = [];
@@ -251,6 +266,7 @@ var RedisStreamTransport = class {
251
266
  }
252
267
  const now = /* @__PURE__ */ new Date();
253
268
  const prior = this.failureContext.get(messageId);
269
+ this.failureContext.delete(messageId);
254
270
  this.failureContext.set(messageId, {
255
271
  error: lastError ? toErrorRecord(lastError) : { message: "handler returned without acking — no error captured" },
256
272
  firstFailedAt: prior?.firstFailedAt ?? now,
@@ -258,6 +274,10 @@ var RedisStreamTransport = class {
258
274
  attempts: (prior?.attempts ?? 0) + 1,
259
275
  handlerName: lastHandlerName ?? prior?.handlerName
260
276
  });
277
+ if (this.failureContext.size > MAX_FAILURE_CONTEXT_ENTRIES) {
278
+ const oldest = this.failureContext.keys().next().value;
279
+ if (oldest !== void 0) this.failureContext.delete(oldest);
280
+ }
261
281
  }
262
282
  getMatchingHandlers(eventType) {
263
283
  const matched = [];
@@ -347,6 +367,33 @@ var RedisStreamTransport = class {
347
367
  return new Promise((resolve) => setTimeout(resolve, ms));
348
368
  }
349
369
  };
370
+ /** Poll-error backoff tuning — see {@link fullJitterBackoffMs}. */
371
+ const POLL_ERROR_BACKOFF_BASE_MS = 1e3;
372
+ const POLL_ERROR_BACKOFF_MAX_MS = 3e4;
373
+ const POLL_ERROR_BACKOFF_FLOOR_MS = 100;
374
+ /**
375
+ * Exponential backoff with full jitter for poll-error retries.
376
+ *
377
+ * The delay is drawn uniformly from the capped exponential window
378
+ * `[floor, min(max, base * 2^(attempt-1))]` — the AWS "full jitter"
379
+ * strategy. Full jitter decorrelates a fleet of consumers that all observed
380
+ * the same Redis blip; the small floor keeps a near-zero draw from
381
+ * hot-spinning the loop while Redis is hard-down.
382
+ *
383
+ * Sibling of `exponentialBackoff()` in `src/events/outbox.ts`, which is a
384
+ * PUBLIC store-author helper with additive jitter and `Date` (retryAt)
385
+ * semantics. Deliberately not shared: the two have different jitter models
386
+ * and audiences, and coupling them would hang this internal tuning off the
387
+ * public API surface.
388
+ *
389
+ * @param attempt 1-indexed consecutive poll-error count (reset on success)
390
+ * @param random injectable for deterministic tests; defaults to `Math.random`
391
+ */
392
+ function fullJitterBackoffMs(attempt, random = Math.random) {
393
+ const exp = POLL_ERROR_BACKOFF_BASE_MS * 2 ** (Math.max(1, Math.floor(attempt)) - 1);
394
+ const capped = Math.min(POLL_ERROR_BACKOFF_MAX_MS, exp);
395
+ return POLL_ERROR_BACKOFF_FLOOR_MS + random() * (capped - POLL_ERROR_BACKOFF_FLOOR_MS);
396
+ }
350
397
  /**
351
398
  * Convert a thrown value into the `DeadLetteredEvent.error` shape — message
352
399
  * always present, optional `code` (string only) and `stack`. Centralised so
@@ -1,4 +1,4 @@
1
- import { a as EventTransport, i as EventLogger, n as DomainEvent, r as EventHandler } from "../../EventTransport-B1GFiYUy.mjs";
1
+ import { a as EventTransport, i as EventLogger, n as DomainEvent, r as EventHandler } from "../../EventTransport-CaaXM5Y1.mjs";
2
2
 
3
3
  //#region src/events/transports/redis.d.ts
4
4
  interface RedisLike {
@@ -1,4 +1,4 @@
1
- import { a as CustomPluginAuthOption, c as RawBodyOptions, d as preloadResourcesAsync, f as LoadResourcesOptions, h as loadResources, i as CustomAuthenticatorOption, l as UnderPressureOptions, m as ResourceModule, n as BetterAuthOption, o as JwtAuthOption, p as ResourceLike, r as CreateAppOptions, s as MultipartOptions, t as AuthOption, u as preloadResources } from "../types-DEEWwQFR.mjs";
1
+ import { a as CustomPluginAuthOption, c as RawBodyOptions, d as preloadResourcesAsync, f as LoadResourcesOptions, h as loadResources, i as CustomAuthenticatorOption, l as UnderPressureOptions, m as ResourceModule, n as BetterAuthOption, o as JwtAuthOption, p as ResourceLike, r as CreateAppOptions, s as MultipartOptions, t as AuthOption, u as preloadResources } from "../types-WVlAVp8T.mjs";
2
2
  import { FastifyInstance, FastifyServerOptions } from "fastify";
3
3
 
4
4
  //#region src/factory/createApp.d.ts
@@ -1,6 +1,6 @@
1
- import { n as preloadResourcesAsync, t as preloadResources } from "../preloadResources-BOXzPhKU.mjs";
2
- import { a as edgePreset, c as testingPreset, i as developmentPreset, n as createApp, o as getPreset, s as productionPreset, t as ArcFactory } from "../createApp-DZvrb587.mjs";
3
- import { t as loadResources } from "../loadResources-fol6JuvW.mjs";
1
+ import { n as preloadResourcesAsync, t as preloadResources } from "../preloadResources-B-j-iqo-.mjs";
2
+ import { a as edgePreset, c as testingPreset, i as developmentPreset, n as createApp, o as getPreset, s as productionPreset, t as ArcFactory } from "../createApp-J1iPXY9p.mjs";
3
+ import { t as loadResources } from "../loadResources-CwXt9MCh.mjs";
4
4
  //#region src/factory/edge.ts
5
5
  /**
6
6
  * Convert a Fastify app into a Web Standards fetch handler.
@@ -1,5 +1,5 @@
1
- import { An as HookSystem, z as ResourceRegistry } from "./index-C000faom.mjs";
2
- import { t as ExternalOpenApiPaths } from "./externalPaths-ClykOQqa.mjs";
1
+ import { yn as HookSystem, z as ResourceRegistry } from "./index-QYma8mdC.mjs";
2
+ import { t as ExternalOpenApiPaths } from "./externalPaths-DLle0R07.mjs";
3
3
  import { FastifyPluginAsync } from "fastify";
4
4
 
5
5
  //#region src/core/arcCorePlugin.d.ts
@@ -1,4 +1,4 @@
1
- import { i as RequestScope } from "./types-CfapW2t8.mjs";
1
+ import { i as RequestScope } from "./types-BJwJN2Pg.mjs";
2
2
  import { FastifyRequest } from "fastify";
3
3
 
4
4
  //#region src/permissions/types.d.ts
@@ -1,4 +1,4 @@
1
- import { t as pluralize } from "./pluralize-B9M8xvy-.mjs";
1
+ import { t as pluralize } from "./pluralize-BxdUl2P4.mjs";
2
2
  import { join } from "node:path";
3
3
  import { existsSync, mkdirSync, readFileSync, writeFileSync } from "node:fs";
4
4
  //#region src/cli/commands/generate/name-helpers.ts
@@ -1,2 +1,2 @@
1
- import { An as HookSystem, Dn as HookOperation, En as HookHandler, Fn as beforeCreate, In as beforeDelete, Ln as beforeUpdate, Mn as afterCreate, Nn as afterDelete, On as HookPhase, Pn as afterUpdate, Rn as createHookSystem, Tn as HookContext, jn as HookSystemOptions, kn as HookRegistration, wn as DefineHookOptions, zn as defineHook } from "../index-C000faom.mjs";
1
+ import { Cn as afterUpdate, Dn as createHookSystem, En as beforeUpdate, On as defineHook, Sn as afterDelete, Tn as beforeDelete, _n as HookPhase, bn as HookSystemOptions, gn as HookOperation, hn as HookHandler, mn as HookContext, pn as DefineHookOptions, vn as HookRegistration, wn as beforeCreate, xn as afterCreate, yn as HookSystem } from "../index-QYma8mdC.mjs";
2
2
  export { type DefineHookOptions, type HookContext, type HookHandler, type HookOperation, type HookPhase, type HookRegistration, HookSystem, type HookSystemOptions, afterCreate, afterDelete, afterUpdate, beforeCreate, beforeDelete, beforeUpdate, createHookSystem, defineHook };
@@ -1,2 +1,2 @@
1
- import { a as beforeCreate, c as createHookSystem, i as afterUpdate, l as defineHook, n as afterCreate, o as beforeDelete, r as afterDelete, s as beforeUpdate, t as HookSystem } from "../HookSystem-Cmf7-Etp.mjs";
1
+ import { a as beforeCreate, c as createHookSystem, i as afterUpdate, l as defineHook, n as afterCreate, o as beforeDelete, r as afterDelete, s as beforeUpdate, t as HookSystem } from "../HookSystem-BfCpWbwM.mjs";
2
2
  export { HookSystem, afterCreate, afterDelete, afterUpdate, beforeCreate, beforeDelete, beforeUpdate, createHookSystem, defineHook };
@@ -1,5 +1,5 @@
1
- import { i as createIdempotencyResult, n as IdempotencyResult, r as IdempotencyStore, t as IdempotencyLock } from "../interface-DoE6UoC2.mjs";
2
- import { i as RedisIdempotencyStoreOptions, n as RedisClient } from "../redis-cpqVb85w.mjs";
1
+ import { i as createIdempotencyResult, n as IdempotencyResult, r as IdempotencyStore, t as IdempotencyLock } from "../interface-CsrGR8LF.mjs";
2
+ import { i as RedisIdempotencyStoreOptions, n as RedisClient } from "../redis-3hqH_Qk_.mjs";
3
3
  import { FastifyPluginAsync } from "fastify";
4
4
  import { RepositoryLike } from "@classytic/repo-core/adapter";
5
5
 
@@ -1,4 +1,4 @@
1
- import { n as createSafeGetOne, t as createIsDuplicateKeyError } from "../store-helpers-5JgaxieH.mjs";
1
+ import { n as createSafeGetOne, t as createIsDuplicateKeyError } from "../store-helpers-DjSYI2uP.mjs";
2
2
  import { createHash } from "node:crypto";
3
3
  import fp from "fastify-plugin";
4
4
  import { and, eq, exists, gt, lt, or, startsWith } from "@classytic/repo-core/filter";
@@ -1,2 +1,2 @@
1
- import { a as UpstashRedisLike, i as RedisIdempotencyStoreOptions, n as RedisClient, o as ioredisAsIdempotencyClient, r as RedisIdempotencyStore, s as upstashAsIdempotencyClient, t as IoredisLike } from "../redis-cpqVb85w.mjs";
1
+ import { a as UpstashRedisLike, i as RedisIdempotencyStoreOptions, n as RedisClient, o as ioredisAsIdempotencyClient, r as RedisIdempotencyStore, s as upstashAsIdempotencyClient, t as IoredisLike } from "../redis-3hqH_Qk_.mjs";
2
2
  export { type IoredisLike, type RedisClient, RedisIdempotencyStore, type RedisIdempotencyStoreOptions, type UpstashRedisLike, ioredisAsIdempotencyClient, upstashAsIdempotencyClient };
@@ -1,6 +1,6 @@
1
- import { C as RequestContext, F as FastifyWithDecorators, H as ActionDefinition, K as ArcFieldRule, Kt as AnyRecord, L as RequestWithExtras, T as CrudRouterOptions, Tt as AggregationConfig, V as ResourceDefinition, bt as IRequestContext, ft as RouteMcpConfig, mt as RouteSchemaOptions, q as CrudController, st as ResourceConfig, vt as IController, yt as IControllerResponse } from "./index-C000faom.mjs";
2
- import { i as RequestScope } from "./types-CfapW2t8.mjs";
3
- import { c as PermissionCheck } from "./fields-DqZLp5d6.mjs";
1
+ import { C as RequestContext, F as FastifyWithDecorators, H as ActionDefinition, K as ArcFieldRule, Kt as AnyRecord, L as RequestWithExtras, T as CrudRouterOptions, Tt as AggregationConfig, V as ResourceDefinition, bt as IRequestContext, ft as RouteMcpConfig, mt as RouteSchemaOptions, q as CrudController, st as ResourceConfig, vt as IController, yt as IControllerResponse } from "./index-QYma8mdC.mjs";
2
+ import { i as RequestScope } from "./types-BJwJN2Pg.mjs";
3
+ import { c as PermissionCheck } from "./fields-CKLrhyOb.mjs";
4
4
  import { FastifyReply, FastifyRequest, RouteHandlerMethod } from "fastify";
5
5
  import { z } from "zod";
6
6
  import { AggDateBucket, AggMeasure, AggTopN } from "@classytic/repo-core/repository";