@classytic/arc 2.31.0 → 2.32.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (151) hide show
  1. package/dist/{BaseController-De7GqHck.mjs → BaseController-Dk34dxhI.mjs} +16 -13
  2. package/dist/{EventTransport-9mlTgqzE.mjs → EventTransport-BV10D0oG.mjs} +1 -1
  3. package/dist/{HookSystem-BcmPyq5I.mjs → HookSystem-vimw5D7Z.mjs} +2 -2
  4. package/dist/{ResourceRegistry-qq0mn2s9.mjs → ResourceRegistry-BVr7PgA0.mjs} +17 -15
  5. package/dist/{actionPermissions-BE5NwaQk.mjs → actionPermissions-Dti7ebyv.mjs} +1 -1
  6. package/dist/audit/index.mjs +2 -2
  7. package/dist/auth/audit.mjs +1 -1
  8. package/dist/auth/index.mjs +11 -10
  9. package/dist/auth/redis-session.mjs +1 -1
  10. package/dist/{authorizationDecision-ChvUQGG1.mjs → authorizationDecision-__T3EJDA.mjs} +2 -2
  11. package/dist/{buildHandler-CDrCJzmB.mjs → buildHandler-Dn7rzUJo.mjs} +6 -5
  12. package/dist/cache/index.mjs +4 -4
  13. package/dist/{caching-BFHA6Scf.mjs → caching-DFJqHyFs.mjs} +2 -2
  14. package/dist/cleanup/index.d.mts +36 -14
  15. package/dist/cleanup/index.mjs +45 -29
  16. package/dist/cli/commands/describe.mjs +4 -6
  17. package/dist/cli/commands/docs.mjs +3 -3
  18. package/dist/cli/commands/doctor.mjs +1 -1
  19. package/dist/cli/commands/generate.mjs +2 -2
  20. package/dist/cli/commands/init.mjs +2 -2
  21. package/dist/cli/commands/introspect.mjs +2 -2
  22. package/dist/cli/index.mjs +3 -3
  23. package/dist/{constants-D3Dv4LAV.mjs → constants-J99LGTJI.mjs} +1 -1
  24. package/dist/context/index.mjs +3 -3
  25. package/dist/core/index.d.mts +1 -1
  26. package/dist/core/index.mjs +7 -7
  27. package/dist/{core-BGpIxINN.mjs → core-BwwmNSp7.mjs} +3 -3
  28. package/dist/{createActionRouter-CDXEdasl.mjs → createActionRouter-DkJZWo0m.mjs} +14 -11
  29. package/dist/{createAggregationRouter-KrQDzx-k.mjs → createAggregationRouter-B1exOVD8.mjs} +10 -8
  30. package/dist/{createApp-Df89BEni.mjs → createApp-Bv0m-Q6_.mjs} +182 -61
  31. package/dist/{defineAggregation-DbPPS8RI.mjs → defineAggregation-gX4No1gt.mjs} +1 -1
  32. package/dist/{defineResource-Bv6EtN1x.mjs → defineResource-XFPPUu2c.mjs} +41 -32
  33. package/dist/discovery/index.mjs +1 -1
  34. package/dist/docs/index.mjs +3 -3
  35. package/dist/{elevation-D2l1mh4g.mjs → elevation-DNXXxgjl.mjs} +4 -4
  36. package/dist/encryption/index.mjs +5 -4
  37. package/dist/{errorHandler-C5NeD6bT.mjs → errorHandler-B2AZUJUj.mjs} +3 -3
  38. package/dist/{errors-Cj42AntO.mjs → errors-Bh1KK5ps.mjs} +1 -1
  39. package/dist/{eventPlugin-CkffEihm.mjs → eventPlugin-Dk_TyXeE.mjs} +8 -7
  40. package/dist/events/index.d.mts +108 -41
  41. package/dist/events/index.mjs +166 -142
  42. package/dist/events/transports/redis-stream-entry.mjs +14 -10
  43. package/dist/events/transports/redis.mjs +1 -1
  44. package/dist/factory/index.d.mts +5 -5
  45. package/dist/factory/index.mjs +131 -8
  46. package/dist/fastify-augmentation-BppG5bMT.d.mts +179 -0
  47. package/dist/{fields-CB5Cuzk9.mjs → fields-DEi65pGj.mjs} +6 -11
  48. package/dist/{generate-EcA6ui4i.mjs → generate-CRSG0jTv.mjs} +2 -2
  49. package/dist/{headers-3zvE0rM3.mjs → headers-BXjQYbeC.mjs} +2 -2
  50. package/dist/{health-DjZYjM7H.mjs → health-5XnTa2uo.mjs} +1 -1
  51. package/dist/hooks/index.mjs +2 -2
  52. package/dist/idempotency/index.mjs +8 -6
  53. package/dist/idempotency/redis.mjs +1 -1
  54. package/dist/{index-Bc2ox8_U.d.mts → index-DXlHwvuE.d.mts} +1 -1
  55. package/dist/{index-CFThn1K9.d.mts → index-ZWhLTp_s.d.mts} +70 -4
  56. package/dist/{index-CEJNZFtX.d.mts → index-zq6VrjaB.d.mts} +70 -1
  57. package/dist/index.d.mts +3 -3
  58. package/dist/index.mjs +11 -11
  59. package/dist/{init-BEiNZBsJ.mjs → init-BbfP8aky.mjs} +7 -5
  60. package/dist/integrations/event-gateway.mjs +2 -2
  61. package/dist/integrations/index.mjs +1 -1
  62. package/dist/integrations/jobs/index.mjs +3 -3
  63. package/dist/integrations/mcp/index.mjs +2 -2
  64. package/dist/integrations/mcp/testing.mjs +2 -2
  65. package/dist/integrations/streamline.mjs +3 -3
  66. package/dist/integrations/webhooks.mjs +1 -1
  67. package/dist/integrations/websocket-pushref-redis.mjs +1 -1
  68. package/dist/integrations/websocket-redis.mjs +1 -1
  69. package/dist/integrations/websocket.mjs +38 -35
  70. package/dist/{interface-DYzM0q-u.mjs → interface-BP4IJCBO.mjs} +1 -1
  71. package/dist/{jsonBody-BYbLDi5D.mjs → jsonBody-DDSR2HFl.mjs} +2 -2
  72. package/dist/{keys-C0_pa1tY.mjs → keys-B-HBTGXl.mjs} +1 -1
  73. package/dist/{loadResources-yawi9tAE.mjs → loadResources-DisEAmkm.mjs} +4 -3
  74. package/dist/{loadResourcesFromEntry-BAjYZonx.mjs → loadResourcesFromEntry-D1MJp13f.mjs} +2 -2
  75. package/dist/logger/index.mjs +1 -1
  76. package/dist/{mcpPlugin-B_es0hiF.mjs → mcpPlugin-CpfVpKHE.mjs} +32 -31
  77. package/dist/{memory-DR6wpa9F.mjs → memory-DsuWnj4T.mjs} +5 -5
  78. package/dist/{metrics-CUdw_wLB.mjs → metrics-DdQL8syd.mjs} +2 -2
  79. package/dist/middleware/index.mjs +3 -3
  80. package/dist/migrations/index.mjs +3 -3
  81. package/dist/{module-0pFJA6YW.mjs → module-Br81am6m.mjs} +1 -1
  82. package/dist/{multipartBody-wOgj_fnj.mjs → multipartBody-BnkBLN7v.mjs} +3 -3
  83. package/dist/{openapi-B2L4js1v.mjs → openapi-DIRzvxC0.mjs} +6 -6
  84. package/dist/outbox-admin/index.d.mts +85 -0
  85. package/dist/outbox-admin/index.mjs +115 -0
  86. package/dist/permissions/index.d.mts +2 -2
  87. package/dist/permissions/index.mjs +6 -6
  88. package/dist/{permissions-L--QDAir.mjs → permissions-Dl_m0NCe.mjs} +101 -8
  89. package/dist/{pipe-DRDNsI2T.mjs → pipe-ELHCP3zj.mjs} +3 -5
  90. package/dist/pipeline/index.mjs +2 -2
  91. package/dist/{pipeline-UPQFatit.mjs → pipeline-CPA-jx0M.mjs} +17 -15
  92. package/dist/plugins/index.d.mts +2 -2
  93. package/dist/plugins/index.mjs +16 -16
  94. package/dist/plugins/response-cache.mjs +2 -2
  95. package/dist/plugins/tracing-entry.mjs +2 -2
  96. package/dist/{pluralize-O0FZgESl.mjs → pluralize-C2ZkKbsn.mjs} +1 -1
  97. package/dist/{preloadResources-Dz6BjzM6.mjs → preloadResources-CDs87fTw.mjs} +1 -1
  98. package/dist/{preloadResources-CyoJMSiO.d.mts → preloadResources-T1hmlTEZ.d.mts} +2 -2
  99. package/dist/presets/filesUpload.mjs +6 -6
  100. package/dist/presets/index.mjs +2 -2
  101. package/dist/presets/multiTenant.mjs +3 -3
  102. package/dist/presets/search.mjs +2 -2
  103. package/dist/{presets-C4-g4_4P.mjs → presets-Dntd_pUO.mjs} +6 -5
  104. package/dist/{projection-BHw6xlOQ.mjs → projection-CjHhM5W1.mjs} +2 -2
  105. package/dist/{queryCachePlugin-BZr8v46E.mjs → queryCachePlugin-Dhps-Ldt.mjs} +6 -6
  106. package/dist/{rateLimit-CMPY5Qms.mjs → rateLimit-C7mmAGs3.mjs} +2 -2
  107. package/dist/{rateLimitKey-Dn5JNmkj.mjs → rateLimitKey-CccEFBfk.mjs} +1 -1
  108. package/dist/{realtime-C5NoxWFY.mjs → realtime-DVvVZZFz.mjs} +10 -10
  109. package/dist/{registerRoute-CBN84fc7.mjs → registerRoute-B_QrjYZS.mjs} +10 -8
  110. package/dist/registry/index.d.mts +1 -1
  111. package/dist/registry/index.mjs +3 -3
  112. package/dist/{registry-4213U4zb.mjs → registry-DqUmzA9W.mjs} +2 -2
  113. package/dist/{renewingLease-Dll8mGgc.mjs → renewingLease-BpP9wwj7.mjs} +1 -1
  114. package/dist/{replyHelpers-DIiZKGUt.mjs → replyHelpers-DS-P0b32.mjs} +2 -2
  115. package/dist/{repositoryFilter-f9eAfq_l.mjs → repositoryFilter-DotlxG3L.mjs} +1 -1
  116. package/dist/{requestContext-2TxXOJ7T.mjs → requestContext-B6vQF-sy.mjs} +1 -1
  117. package/dist/{rolldown-runtime-CUsjNX9R.mjs → rolldown-runtime-BhflRrBs.mjs} +1 -1
  118. package/dist/{schedules-DdtlICTf.mjs → schedules-C2Q3uv2d.mjs} +3 -3
  119. package/dist/{schemaConverter-D--wdJM-.mjs → schemaConverter-Crjvczi_.mjs} +1 -1
  120. package/dist/{schemaIR-Bm2enbnh.mjs → schemaIR-CnyKK_1j.mjs} +2 -2
  121. package/dist/schemas/index.mjs +1 -1
  122. package/dist/scim/index.mjs +2 -2
  123. package/dist/scope/index.mjs +5 -5
  124. package/dist/{scope-DHx5Fous.mjs → scope-afz_Ieek.mjs} +4 -4
  125. package/dist/{simpleEqualityMatcher-DjQJ7FKA.mjs → simpleEqualityMatcher-C4KiMWUa.mjs} +1 -1
  126. package/dist/{sse-CUTo9y0X.mjs → sse-B9P4Y-UI.mjs} +7 -6
  127. package/dist/{sseStream-B_5XcVVd.mjs → sseStream-B3zb_d1h.mjs} +2 -2
  128. package/dist/{store-helpers-D1MP_gRG.mjs → store-helpers-DmEMHlPQ.mjs} +1 -1
  129. package/dist/{streaming-PSZwi8JV.mjs → streaming-DPZdQmDz.mjs} +1 -1
  130. package/dist/{subscribe-helpers-C0Tb2aUp.mjs → subscribe-helpers-sp5xTDVA.mjs} +2 -2
  131. package/dist/sync/changelog.mjs +1 -1
  132. package/dist/testing/cleanupStoreContract.mjs +10 -6
  133. package/dist/testing/index.d.mts +31 -4
  134. package/dist/testing/index.mjs +97 -39
  135. package/dist/testing/outboxStoreContract.d.mts +11 -2
  136. package/dist/testing/outboxStoreContract.mjs +110 -21
  137. package/dist/testing/storageContract.mjs +11 -6
  138. package/dist/{transactionContext-dAfBVUrk.mjs → transactionContext-BvLKvOIL.mjs} +1 -1
  139. package/dist/{typeGuards-B0ryPo3I.mjs → typeGuards-B_5-hKjJ.mjs} +1 -1
  140. package/dist/types/index.mjs +1 -1
  141. package/dist/types/storage.mjs +1 -1
  142. package/dist/{types-CBgvkp8O.d.mts → types-CT5vu6M0.d.mts} +87 -11
  143. package/dist/{types-Cys0ko9Z.mjs → types-DXYX7dIR.mjs} +4 -4
  144. package/dist/usage/index.mjs +0 -0
  145. package/dist/{userHelpers-ChYJ6CdK.mjs → userHelpers-BNsOXzOd.mjs} +1 -1
  146. package/dist/utils/index.mjs +10 -10
  147. package/dist/{utils-vL8BrDE2.mjs → utils-BtOha_Sp.mjs} +3 -3
  148. package/dist/{validate-_KbifpHF.mjs → validate-PMCPR9El.mjs} +2 -2
  149. package/dist/{versioning-CHDTor3P.mjs → versioning-CKv2jz_U.mjs} +4 -4
  150. package/package.json +23 -13
  151. package/dist/fastify-augmentation-zh0_AYHa.d.mts +0 -85
@@ -1,9 +1,10 @@
1
- /*! @classytic/arc v2.31.0 | MIT | https://classytic.com */
2
- import { t as MemoryEventTransport } from "../EventTransport-9mlTgqzE.mjs";
3
- import { a as createEventRegistry, i as wrapWithSchema, n as subscribeWithSchema, o as defineEvent, r as wrapWithBoundary, t as subscribeWithBoundary } from "../subscribe-helpers-C0Tb2aUp.mjs";
4
- import { i as withRetry, r as createDeadLetterPublisher, t as eventPlugin } from "../eventPlugin-CkffEihm.mjs";
5
- import { n as createSafeGetOne, t as createIsDuplicateKeyError } from "../store-helpers-D1MP_gRG.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";
6
6
  import { and, anyOf, eq, lte, ne, or } from "@classytic/repo-core/filter";
7
+ import { MemoryOutboxStore } from "@classytic/primitives/memory-outbox";
7
8
  import { InvalidOutboxEventError, OutboxOwnershipError } from "@classytic/primitives/outbox";
8
9
  import { update } from "@classytic/repo-core/update";
9
10
  //#region src/events/eventTypes.ts
@@ -57,141 +58,6 @@ const CACHE_EVENTS = Object.freeze({
57
58
  TAG_VERSION_BUMPED: "arc.cache.tag.bumped"
58
59
  });
59
60
  //#endregion
60
- //#region src/events/memory-outbox.ts
61
- /**
62
- * In-memory outbox store — reference implementation for dev / testing.
63
- *
64
- * Supports the full `OutboxStore` capability set (claim/lease, fail/retry,
65
- * dedupe, visibleAt). Production deployments should pass a `repository` to
66
- * `EventOutbox` instead of using this; the memory store exists for unit
67
- * tests and single-process dev where durable persistence is noise.
68
- */
69
- const DEFAULT_LEASE_MS$2 = 3e4;
70
- var MemoryOutboxStore = class {
71
- entries = [];
72
- seenDedupeKeys = /* @__PURE__ */ new Set();
73
- /**
74
- * Drop every entry (including delivered / failed / dead-lettered rows) and
75
- * the dedupe-key memory.
76
- *
77
- * For tests only — it is the in-memory analogue of truncating the table, and
78
- * exists so a suite can isolate cases against ONE store instance (the
79
- * `reset` hook of `runOutboxStoreContract`). Not part of `OutboxStore`; a
80
- * production store must never expose this.
81
- */
82
- clear() {
83
- this.entries.length = 0;
84
- this.seenDedupeKeys.clear();
85
- }
86
- async save(event, options) {
87
- if (!event?.type || typeof event.type !== "string") throw new InvalidOutboxEventError("event.type is required");
88
- if (!event.meta?.id || typeof event.meta.id !== "string") throw new InvalidOutboxEventError("event.meta.id is required");
89
- if (options?.dedupeKey) {
90
- if (this.seenDedupeKeys.has(options.dedupeKey)) return;
91
- this.seenDedupeKeys.add(options.dedupeKey);
92
- }
93
- this.entries.push({
94
- event,
95
- status: "pending",
96
- attempts: 0,
97
- visibleAt: options?.visibleAt?.getTime() ?? 0,
98
- leaseOwner: null,
99
- leaseExpiresAt: 0,
100
- deliveredAt: null,
101
- firstFailedAt: null,
102
- lastFailedAt: null,
103
- lastError: null,
104
- dedupeKey: options?.dedupeKey
105
- });
106
- }
107
- async getPending(limit) {
108
- const now = Date.now();
109
- return this.entries.filter((e) => e.status === "pending" && e.visibleAt <= now && (e.leaseOwner === null || e.leaseExpiresAt <= now)).slice(0, limit).map((e) => e.event);
110
- }
111
- async claimPending(options) {
112
- const now = Date.now();
113
- const limit = options?.limit ?? 100;
114
- const leaseMs = options?.leaseMs ?? DEFAULT_LEASE_MS$2;
115
- const consumerId = options?.consumerId ?? "anonymous";
116
- const typeFilter = options?.types ? new Set(options.types) : null;
117
- const claimed = [];
118
- for (const entry of this.entries) {
119
- if (claimed.length >= limit) break;
120
- if (entry.status !== "pending") continue;
121
- if (entry.visibleAt > now) continue;
122
- if (entry.leaseOwner !== null && entry.leaseExpiresAt > now) continue;
123
- if (typeFilter && !typeFilter.has(entry.event.type)) continue;
124
- entry.leaseOwner = consumerId;
125
- entry.leaseExpiresAt = now + leaseMs;
126
- entry.attempts++;
127
- claimed.push(entry.event);
128
- }
129
- return claimed;
130
- }
131
- async acknowledge(eventId, options) {
132
- const entry = this.entries.find((e) => e.event.meta.id === eventId);
133
- if (!entry) return;
134
- if (entry.status === "delivered") return;
135
- if (options?.consumerId && entry.leaseOwner && entry.leaseOwner !== options.consumerId) throw new OutboxOwnershipError(eventId, options.consumerId, entry.leaseOwner);
136
- entry.status = "delivered";
137
- entry.deliveredAt = Date.now();
138
- entry.leaseOwner = null;
139
- }
140
- async fail(eventId, error, options) {
141
- const entry = this.entries.find((e) => e.event.meta.id === eventId);
142
- if (!entry) return;
143
- if (options?.consumerId && entry.leaseOwner && entry.leaseOwner !== options.consumerId) throw new OutboxOwnershipError(eventId, options.consumerId, entry.leaseOwner);
144
- const now = Date.now();
145
- entry.lastError = error;
146
- entry.leaseOwner = null;
147
- entry.leaseExpiresAt = 0;
148
- if (entry.firstFailedAt === null) entry.firstFailedAt = now;
149
- entry.lastFailedAt = now;
150
- if (options?.deadLetter) {
151
- entry.status = "dead_letter";
152
- return;
153
- }
154
- entry.status = "pending";
155
- entry.visibleAt = options?.retryAt ? options.retryAt.getTime() : 0;
156
- }
157
- async getDeadLettered(limit) {
158
- const out = [];
159
- for (const entry of this.entries) {
160
- if (out.length >= limit) break;
161
- if (entry.status !== "dead_letter") continue;
162
- out.push({
163
- event: entry.event,
164
- error: {
165
- message: entry.lastError?.message ?? "unknown",
166
- ...entry.lastError?.code !== void 0 ? { code: entry.lastError.code } : {}
167
- },
168
- attempts: entry.attempts,
169
- firstFailedAt: new Date(entry.firstFailedAt ?? entry.lastFailedAt ?? Date.now()),
170
- lastFailedAt: new Date(entry.lastFailedAt ?? entry.firstFailedAt ?? Date.now())
171
- });
172
- }
173
- return out;
174
- }
175
- async purge(olderThanMs) {
176
- const cutoff = Date.now() - olderThanMs;
177
- let purged = 0;
178
- for (let i = this.entries.length - 1; i >= 0; i--) {
179
- const entry = this.entries[i];
180
- if (!entry) continue;
181
- if (entry.status === "delivered" && entry.deliveredAt !== null && entry.deliveredAt < cutoff) {
182
- if (entry.dedupeKey) this.seenDedupeKeys.delete(entry.dedupeKey);
183
- this.entries.splice(i, 1);
184
- purged++;
185
- }
186
- }
187
- return purged;
188
- }
189
- /** Test helper: inspect entry by id */
190
- _getEntry(eventId) {
191
- return this.entries.find((e) => e.event.meta.id === eventId);
192
- }
193
- };
194
- //#endregion
195
61
  //#region src/events/outbox/backoff.ts
196
62
  /**
197
63
  * Compute a `retryAt` `Date` using exponential backoff with jitter.
@@ -487,13 +353,96 @@ function repositoryAsOutboxStore(repository, options) {
487
353
  if (batch.length < DEFAULT_PURGE_BATCH) break;
488
354
  }
489
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();
490
439
  }
491
440
  };
492
441
  }
493
442
  //#endregion
494
443
  //#region src/events/outbox/relay.ts
495
444
  /** Default outbox retention — delivered events older than this are eligible for purge */
496
- const DEFAULT_OUTBOX_RETENTION_MS = 10080 * 60 * 1e3;
445
+ const DEFAULT_OUTBOX_RETENTION_MS = 6048e5;
497
446
  const DEFAULT_LEASE_MS = 3e4;
498
447
  /**
499
448
  * Hard cap on the in-process attempt-counter map. Entries are cleared on
@@ -806,4 +755,79 @@ function normalizeError(err) {
806
755
  return { message: String(err) };
807
756
  }
808
757
  //#endregion
809
- 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,4 +1,4 @@
1
- /*! @classytic/arc v2.31.0 | MIT | https://classytic.com */
1
+ /*! @classytic/arc v2.32.0 | MIT | https://classytic.com */
2
2
  import sjson from "secure-json-parse";
3
3
  //#region src/events/transports/redis-stream.ts
4
4
  /**
@@ -184,13 +184,15 @@ var RedisStreamTransport = class {
184
184
  this.handlers.clear();
185
185
  this.failureContext.clear();
186
186
  if (this.pollPromise) {
187
- if (await Promise.race([this.pollPromise.then(() => "drained"), this.sleep(this.closeTimeoutMs).then(() => "timeout")]) === "timeout") if (!this.externalLifecycle) {
188
- if (typeof this.redis.disconnect === "function") this.redis.disconnect();
189
- else await this.redis.quit().catch((err) => {
190
- this.logger.error("[RedisStreamTransport] quit() during close raced:", err);
191
- });
192
- await this.pollPromise.catch(() => void 0);
193
- } 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
+ }
194
196
  this.pollPromise = null;
195
197
  }
196
198
  if (!this.externalLifecycle) await this.redis.quit().catch(() => void 0);
@@ -251,8 +253,10 @@ var RedisStreamTransport = class {
251
253
  const overRetryIds = [];
252
254
  for (const entry of pending) {
253
255
  const [id, , idleTime, deliveryCount] = entry;
254
- if (idleTime > this.claimTimeoutMs) if (deliveryCount >= this.maxRetries) overRetryIds.push(id);
255
- else staleIds.push(id);
256
+ if (idleTime > this.claimTimeoutMs) {
257
+ if (deliveryCount >= this.maxRetries) overRetryIds.push(id);
258
+ else staleIds.push(id);
259
+ }
256
260
  }
257
261
  if (overRetryIds.length > 0) await this.moveToDlq(overRetryIds);
258
262
  if (staleIds.length > 0) {
@@ -1,4 +1,4 @@
1
- /*! @classytic/arc v2.31.0 | MIT | https://classytic.com */
1
+ /*! @classytic/arc v2.32.0 | MIT | https://classytic.com */
2
2
  import sjson from "secure-json-parse";
3
3
  //#region src/events/transports/redis.ts
4
4
  /**
@@ -1,8 +1,8 @@
1
1
 
2
- 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-CBgvkp8O.mjs";
3
- import { a as lazyModuleExports, c as getModuleState, d as resolveModule, f as orderModules, g as collectModuleWorkflows, h as collectModuleScheduledJobs, i as hasModuleExports, l as hasModule, m as collectModuleHealthChecks, n as getModuleExports, o as lazyRequiredModuleExports, p as subscribeModuleEventHandlers, r as getOptionalModuleExports, s as ModuleState, t as defineModule, u as resolveContribution } from "../index-CFThn1K9.mjs";
4
- import "../fastify-augmentation-zh0_AYHa.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-CyoJMSiO.mjs";
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";
6
6
  import { FastifyInstance, FastifyServerOptions } from "fastify";
7
7
  //#region src/factory/createApp.d.ts
8
8
  /**
@@ -137,4 +137,4 @@ declare const testingPreset: Partial<CreateAppOptions>;
137
137
  declare const edgePreset: Partial<CreateAppOptions>;
138
138
  declare function getPreset(name: "production" | "development" | "testing" | "edge" | "worker"): Partial<CreateAppOptions>;
139
139
  //#endregion
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 ModuleState, type MultipartOptions, type RawBodyOptions, type ResourceLike, type ResourceModule, type UnderPressureOptions, type WorkerHealthOptions, collectModuleHealthChecks, collectModuleScheduledJobs, collectModuleWorkflows, createApp, createWorker, defineModule, developmentPreset, edgePreset, getModuleExports, getModuleState, getOptionalModuleExports, getPreset, hasModule, hasModuleExports, lazyModuleExports, lazyRequiredModuleExports, 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,9 +1,131 @@
1
- /*! @classytic/arc v2.31.0 | MIT | https://classytic.com */
2
- import { r as mergeHealthChecks, t as healthPlugin } from "../health-DjZYjM7H.mjs";
3
- import { _ as resolveContribution, a as edgePreset, c as testingPreset, d as hasModule, f as orderModules, g as collectModuleWorkflows, h as collectModuleScheduledJobs, i as developmentPreset, l as workerPreset, m as collectModuleHealthChecks, n as createApp, o as getPreset, p as subscribeModuleEventHandlers, s as productionPreset, t as ArcFactory, u as getModuleState, v as resolveModule } from "../createApp-Df89BEni.mjs";
4
- import { a as lazyModuleExports, i as hasModuleExports, n as getModuleExports, o as lazyRequiredModuleExports, r as getOptionalModuleExports, t as defineModule } from "../module-0pFJA6YW.mjs";
5
- import { t as loadResources } from "../loadResources-yawi9tAE.mjs";
6
- import { n as preloadResourcesAsync, t as preloadResources } from "../preloadResources-Dz6BjzM6.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
7
129
  //#region src/factory/createWorker.ts
8
130
  async function createWorker(options, workerOptions = {}) {
9
131
  const { auth: _auth, helmet: _helmet, cors: _cors, rateLimit: _rateLimit, multipart: _multipart, rawBody: _rawBody, sensible: _sensible, underPressure: _underPressure, replyHelpers: _replyHelpers, ...runtimeOptions } = options;
@@ -20,10 +142,11 @@ async function createWorker(options, workerOptions = {}) {
20
142
  });
21
143
  if (workerOptions.health) {
22
144
  const { port, host = "0.0.0.0", checks = [] } = workerOptions.health;
145
+ const moduleChecks = app.arc?.healthChecks ?? [];
23
146
  const mergedChecks = mergeHealthChecks([
24
147
  {
25
148
  owner: "the module graph",
26
- checks: app.arc?.healthChecks ?? []
149
+ checks: moduleChecks
27
150
  },
28
151
  {
29
152
  owner: "shared arcPlugins.health",
@@ -121,4 +244,4 @@ function payloadTooLarge(limit) {
121
244
  });
122
245
  }
123
246
  //#endregion
124
- export { ArcFactory, collectModuleHealthChecks, collectModuleScheduledJobs, collectModuleWorkflows, createApp, createWorker, defineModule, developmentPreset, edgePreset, getModuleExports, getModuleState, getOptionalModuleExports, getPreset, hasModule, hasModuleExports, lazyModuleExports, lazyRequiredModuleExports, 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 };