@classytic/arc 2.30.0 → 2.32.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +1 -1
- package/README.md +7 -0
- package/TRADEMARK.md +22 -0
- package/dist/{BaseController-B5DwufbB.mjs → BaseController-Dk34dxhI.mjs} +21 -19
- package/dist/{EventTransport-QmY7A258.d.mts → EventTransport-2uVV34WU.d.mts} +1 -0
- package/dist/{EventTransport-C6YA8xY9.mjs → EventTransport-BV10D0oG.mjs} +1 -0
- package/dist/{HookSystem-WkIougo6.mjs → HookSystem-vimw5D7Z.mjs} +2 -1
- package/dist/{QueryCache-DOMT_iq7.d.mts → QueryCache-DFA-QrU9.d.mts} +2 -1
- package/dist/{ResourceRegistry-y9Hnypus.mjs → ResourceRegistry-BVr7PgA0.mjs} +18 -15
- package/dist/{actionPermissions-CAphcmUG.mjs → actionPermissions-Dti7ebyv.mjs} +1 -0
- package/dist/audit/index.d.mts +3 -2
- package/dist/audit/index.mjs +2 -1
- package/dist/auth/audit.d.mts +1 -0
- package/dist/auth/audit.mjs +1 -0
- package/dist/auth/index.d.mts +5 -4
- package/dist/auth/index.mjs +11 -9
- package/dist/auth/redis-session.d.mts +2 -1
- package/dist/auth/redis-session.mjs +1 -0
- package/dist/{applyPermissionResult-Cf6zUCvb.mjs → authorizationDecision-__T3EJDA.mjs} +19 -10
- package/dist/{buildHandler-BYS3Vkfz.mjs → buildHandler-Dn7rzUJo.mjs} +6 -4
- package/dist/cache/index.d.mts +4 -3
- package/dist/cache/index.mjs +4 -3
- package/dist/{caching-_NJeDd1_.mjs → caching-DFJqHyFs.mjs} +2 -1
- package/dist/cleanup/index.d.mts +39 -16
- package/dist/cleanup/index.mjs +50 -38
- package/dist/cli/commands/describe.d.mts +2 -1
- package/dist/cli/commands/describe.mjs +4 -5
- package/dist/cli/commands/docs.d.mts +1 -0
- package/dist/cli/commands/docs.mjs +3 -2
- package/dist/cli/commands/doctor.d.mts +1 -0
- package/dist/cli/commands/doctor.mjs +1 -0
- package/dist/cli/commands/generate.d.mts +2 -1
- package/dist/cli/commands/generate.mjs +2 -1
- package/dist/cli/commands/init.d.mts +2 -1
- package/dist/cli/commands/init.mjs +2 -1
- package/dist/cli/commands/introspect.d.mts +1 -0
- package/dist/cli/commands/introspect.mjs +2 -1
- package/dist/cli/index.d.mts +3 -2
- package/dist/cli/index.mjs +3 -2
- package/dist/{constants-DNre0wRg.mjs → constants-J99LGTJI.mjs} +1 -0
- package/dist/context/index.d.mts +1 -0
- package/dist/context/index.mjs +3 -2
- package/dist/core/index.d.mts +3 -2
- package/dist/core/index.mjs +7 -6
- package/dist/{core-poalJLvp.mjs → core-BwwmNSp7.mjs} +3 -2
- package/dist/{createActionRouter-BxuywVRB.mjs → createActionRouter-DkJZWo0m.mjs} +14 -10
- package/dist/{createAggregationRouter-BiY-lGdY.mjs → createAggregationRouter-B1exOVD8.mjs} +10 -7
- package/dist/{createApp-CXrKFciH.mjs → createApp-Bv0m-Q6_.mjs} +625 -229
- package/dist/{defineAggregation-ufWjUwml.mjs → defineAggregation-gX4No1gt.mjs} +1 -0
- package/dist/{defineResource-CxWqDCAy.mjs → defineResource-XFPPUu2c.mjs} +84 -50
- package/dist/discovery/index.d.mts +1 -0
- package/dist/discovery/index.mjs +1 -0
- package/dist/docs/index.d.mts +3 -2
- package/dist/docs/index.mjs +3 -2
- package/dist/{elevation-C9s5hFP_.d.mts → elevation-BXCfVcNG.d.mts} +1 -0
- package/dist/{elevation-CIz1GjGm.mjs → elevation-DNXXxgjl.mjs} +4 -3
- package/dist/encryption/index.d.mts +2 -1
- package/dist/encryption/index.mjs +5 -3
- package/dist/{errorHandler-Bq2OeEQL.mjs → errorHandler-B2AZUJUj.mjs} +3 -2
- package/dist/{errorHandler-C6yv4ZTW.d.mts → errorHandler-CpYUJmPA.d.mts} +1 -0
- package/dist/{errors-DHcHVAgk.d.mts → errors-BGdVwP25.d.mts} +1 -0
- package/dist/{errors-DkO8Nk6-.mjs → errors-Bh1KK5ps.mjs} +1 -0
- package/dist/{eventPlugin-D2NNR4py.mjs → eventPlugin-Dk_TyXeE.mjs} +8 -6
- package/dist/{eventPlugin-Bg3cP2_u.d.mts → eventPlugin-GF5nnBIq.d.mts} +2 -1
- package/dist/events/index.d.mts +112 -44
- package/dist/events/index.mjs +177 -141
- package/dist/events/transports/redis-stream-entry.d.mts +2 -1
- package/dist/events/transports/redis-stream-entry.mjs +14 -9
- package/dist/events/transports/redis.d.mts +2 -1
- package/dist/events/transports/redis.mjs +1 -0
- package/dist/{externalPaths-BE6wFpYv.d.mts → externalPaths-CmGOxOtD.d.mts} +1 -0
- package/dist/factory/index.d.mts +9 -5
- package/dist/factory/index.mjs +131 -7
- package/dist/fastify-augmentation-BppG5bMT.d.mts +179 -0
- package/dist/{fields-CQ_7KQWw.mjs → fields-DEi65pGj.mjs} +6 -10
- package/dist/{fields-D3lhgzhD.d.mts → fields-I_EeRWND.d.mts} +1 -0
- package/dist/{generate-D0wZupBg.mjs → generate-CRSG0jTv.mjs} +2 -1
- package/dist/{generate-BnsL9GBm.d.mts → generate-DCGuEbqI.d.mts} +1 -0
- package/dist/{headers-BvlQSDMB.mjs → headers-BXjQYbeC.mjs} +2 -1
- package/dist/{health-CB_m4S32.mjs → health-5XnTa2uo.mjs} +1 -0
- package/dist/hooks/index.d.mts +2 -1
- package/dist/hooks/index.mjs +2 -1
- package/dist/idempotency/index.d.mts +3 -2
- package/dist/idempotency/index.mjs +8 -5
- package/dist/idempotency/redis.d.mts +2 -1
- package/dist/idempotency/redis.mjs +1 -0
- package/dist/{index-CG4yqMDB.d.mts → index-C-481Zf2.d.mts} +74 -18
- package/dist/{index-Q5gzEIWY.d.mts → index-CpzxqePp.d.mts} +107 -32
- package/dist/{index-Cx7bro4R.d.mts → index-DXlHwvuE.d.mts} +5 -4
- package/dist/index-ZWhLTp_s.d.mts +290 -0
- package/dist/{index-Ch16aAah.d.mts → index-zq6VrjaB.d.mts} +122 -15
- package/dist/index.d.mts +9 -8
- package/dist/index.mjs +11 -10
- package/dist/{init-tlRSt667.mjs → init-BbfP8aky.mjs} +25 -27
- package/dist/{init-BPEu8cs-.d.mts → init-Cscs4lab.d.mts} +1 -0
- package/dist/integrations/event-gateway.d.mts +3 -2
- package/dist/integrations/event-gateway.mjs +2 -1
- package/dist/integrations/index.d.mts +4 -3
- package/dist/integrations/index.mjs +1 -0
- package/dist/integrations/jobs/index.d.mts +2 -1
- package/dist/integrations/jobs/index.mjs +109 -33
- package/dist/integrations/mcp/index.d.mts +6 -6
- package/dist/integrations/mcp/index.mjs +2 -1
- package/dist/integrations/mcp/testing.d.mts +3 -2
- package/dist/integrations/mcp/testing.mjs +2 -2
- package/dist/integrations/streamline.d.mts +1 -0
- package/dist/integrations/streamline.mjs +3 -2
- package/dist/integrations/webhooks.d.mts +1 -0
- package/dist/integrations/webhooks.mjs +1 -0
- package/dist/integrations/websocket-pushref-redis.d.mts +2 -1
- package/dist/integrations/websocket-pushref-redis.mjs +1 -0
- package/dist/integrations/websocket-redis.d.mts +2 -1
- package/dist/integrations/websocket-redis.mjs +1 -0
- package/dist/integrations/websocket.d.mts +3 -2
- package/dist/integrations/websocket.mjs +38 -34
- package/dist/{interface-BGZCzOGL.d.mts → interface-BOk3YnTz.d.mts} +1 -0
- package/dist/{interface-n5hpKg1d.mjs → interface-BP4IJCBO.mjs} +1 -0
- package/dist/{interface-Uk8ONJv7.d.mts → interface-DEWNOMza.d.mts} +1 -0
- package/dist/{jsonBody-BlzAoGCe.mjs → jsonBody-DDSR2HFl.mjs} +2 -1
- package/dist/{keys-Cd316u_y.mjs → keys-B-HBTGXl.mjs} +1 -0
- package/dist/{loadResources-DpU8pfjV.mjs → loadResources-DisEAmkm.mjs} +4 -2
- package/dist/{loadResourcesFromEntry-kvGT6EcO.mjs → loadResourcesFromEntry-D1MJp13f.mjs} +2 -1
- package/dist/logger/index.d.mts +1 -0
- package/dist/logger/index.mjs +1 -0
- package/dist/{mcpPlugin-nRzm5g2s.mjs → mcpPlugin-CpfVpKHE.mjs} +38 -38
- package/dist/{memory-CmnzXE5Z.mjs → memory-DsuWnj4T.mjs} +5 -4
- package/dist/{metrics-fdxI5mn_.mjs → metrics-DdQL8syd.mjs} +2 -1
- package/dist/middleware/index.d.mts +2 -1
- package/dist/middleware/index.mjs +3 -2
- package/dist/migrations/index.d.mts +1 -0
- package/dist/migrations/index.mjs +3 -2
- package/dist/module-Br81am6m.mjs +78 -0
- package/dist/{multipartBody-D0eBvkeq.mjs → multipartBody-BnkBLN7v.mjs} +3 -2
- package/dist/{openapi-B8Vkbxbn.mjs → openapi-DIRzvxC0.mjs} +6 -5
- package/dist/outbox-admin/index.d.mts +85 -0
- package/dist/outbox-admin/index.mjs +115 -0
- package/dist/permissions/index.d.mts +5 -4
- package/dist/permissions/index.mjs +6 -5
- package/dist/{permissions-Bfu_8y1U.mjs → permissions-Dl_m0NCe.mjs} +308 -42
- package/dist/{pipe-BOLmGIxa.mjs → pipe-ELHCP3zj.mjs} +3 -4
- package/dist/pipeline/index.d.mts +2 -1
- package/dist/pipeline/index.mjs +2 -1
- package/dist/{pipeline-BJnpkY9i.mjs → pipeline-CPA-jx0M.mjs} +17 -14
- package/dist/plugins/index.d.mts +8 -7
- package/dist/plugins/index.mjs +16 -15
- package/dist/plugins/response-cache.d.mts +1 -0
- package/dist/plugins/response-cache.mjs +2 -1
- package/dist/plugins/tracing-entry.d.mts +2 -1
- package/dist/plugins/tracing-entry.mjs +2 -1
- package/dist/{pluralize-BhrgwZb9.mjs → pluralize-C2ZkKbsn.mjs} +1 -0
- package/dist/{preloadResources-DSY5HGpS.mjs → preloadResources-CDs87fTw.mjs} +1 -0
- package/dist/{preloadResources-DCfa2egz.d.mts → preloadResources-T1hmlTEZ.d.mts} +117 -12
- package/dist/presets/filesUpload.d.mts +6 -5
- package/dist/presets/filesUpload.mjs +10 -12
- package/dist/presets/index.d.mts +3 -2
- package/dist/presets/index.mjs +2 -1
- package/dist/presets/multiTenant.d.mts +2 -1
- package/dist/presets/multiTenant.mjs +3 -2
- package/dist/presets/search.d.mts +3 -2
- package/dist/presets/search.mjs +2 -1
- package/dist/{presets-CJOEXLuk.mjs → presets-Dntd_pUO.mjs} +6 -4
- package/dist/{projection-UBDgfMwu.mjs → projection-CjHhM5W1.mjs} +2 -1
- package/dist/{pushref-registry-BbcnpNTm.d.mts → pushref-registry-Dov9l4qE.d.mts} +1 -0
- package/dist/{queryCachePlugin-br4otiqF.d.mts → queryCachePlugin-BzqxconG.d.mts} +3 -2
- package/dist/{queryCachePlugin-DSGuSVLW.mjs → queryCachePlugin-Dhps-Ldt.mjs} +6 -5
- package/dist/{rateLimit-DrED-C4v.mjs → rateLimit-C7mmAGs3.mjs} +7 -3
- package/dist/{rateLimitKey-BoWRH3e7.mjs → rateLimitKey-CccEFBfk.mjs} +1 -0
- package/dist/{realtime-B9nmIY5V.mjs → realtime-DVvVZZFz.mjs} +20 -9
- package/dist/{redis-UDtLaNj6.d.mts → redis-CduhKsJi.d.mts} +2 -1
- package/dist/{redis-stream-uIaauNpZ.d.mts → redis-stream-qN6A-RFv.d.mts} +2 -1
- package/dist/{registerRoute-JkxIEBJq.mjs → registerRoute-B_QrjYZS.mjs} +10 -7
- package/dist/registry/index.d.mts +3 -2
- package/dist/registry/index.mjs +3 -2
- package/dist/{registry-BYXUUg21.mjs → registry-DqUmzA9W.mjs} +2 -1
- package/dist/{renewingLease-3cuQH7h8.mjs → renewingLease-BpP9wwj7.mjs} +1 -0
- package/dist/{replyHelpers-67tRawQZ.mjs → replyHelpers-DS-P0b32.mjs} +2 -1
- package/dist/{repositoryFilter-Dz8k3S4P.mjs → repositoryFilter-DotlxG3L.mjs} +1 -0
- package/dist/{requestContext-MrlJwECf.mjs → requestContext-B6vQF-sy.mjs} +1 -0
- package/dist/{rolldown-runtime-8H4AJuhK.mjs → rolldown-runtime-BhflRrBs.mjs} +1 -0
- package/dist/{schedules-CjOd2MG9.mjs → schedules-C2Q3uv2d.mjs} +3 -2
- package/dist/{schemaConverter-DJvodiHj.mjs → schemaConverter-Crjvczi_.mjs} +70 -17
- package/dist/{schemaIR-Ca7ySNPx.mjs → schemaIR-CnyKK_1j.mjs} +2 -1
- package/dist/schemas/index.d.mts +1 -0
- package/dist/schemas/index.mjs +1 -0
- package/dist/scim/index.d.mts +1 -0
- package/dist/scim/index.mjs +2 -1
- package/dist/scope/index.d.mts +3 -2
- package/dist/scope/index.mjs +5 -4
- package/dist/{scope-DfaPntpo.mjs → scope-afz_Ieek.mjs} +4 -3
- package/dist/{sessionManager-BHOM3N_0.d.mts → sessionManager-qimlKV0P.d.mts} +1 -0
- package/dist/{simpleEqualityMatcher-Cg05HPsn.mjs → simpleEqualityMatcher-C4KiMWUa.mjs} +1 -0
- package/dist/{sse-BQ1w0dfl.mjs → sse-B9P4Y-UI.mjs} +7 -5
- package/dist/{sseStream-BQ7Qaynp.mjs → sseStream-B3zb_d1h.mjs} +2 -1
- package/dist/{storage-DLUZ04kK.d.mts → storage-Cx9Sz3Ou.d.mts} +1 -0
- package/dist/{store-helpers-DY6xq4sM.mjs → store-helpers-DmEMHlPQ.mjs} +1 -0
- package/dist/{streaming-BQcW6_Kq.mjs → streaming-DPZdQmDz.mjs} +1 -0
- package/dist/{subscribe-helpers-DfzsivM6.mjs → subscribe-helpers-sp5xTDVA.mjs} +2 -1
- package/dist/sync/changelog.d.mts +1 -0
- package/dist/sync/changelog.mjs +1 -0
- package/dist/testing/cleanupStoreContract.d.mts +2 -1
- package/dist/testing/cleanupStoreContract.mjs +10 -5
- package/dist/testing/index.d.mts +166 -12
- package/dist/testing/index.mjs +315 -43
- package/dist/testing/outboxStoreContract.d.mts +12 -2
- package/dist/testing/outboxStoreContract.mjs +110 -20
- package/dist/testing/storageContract.d.mts +2 -1
- package/dist/testing/storageContract.mjs +11 -5
- package/dist/{tracing-CsGKQJGg.d.mts → tracing-Dro5eH6j.d.mts} +1 -0
- package/dist/{transactionContext-PNdoyLLm.mjs → transactionContext-BvLKvOIL.mjs} +1 -0
- package/dist/{typeGuards-C9KXUly6.mjs → typeGuards-B_5-hKjJ.mjs} +1 -0
- package/dist/types/index.d.mts +6 -5
- package/dist/types/index.mjs +1 -0
- package/dist/types/storage.d.mts +2 -1
- package/dist/types/storage.mjs +1 -0
- package/dist/{types-ChvvVNP8.d.mts → types-B7XeLEgK.d.mts} +34 -2
- package/dist/{types-Cc91nXuA.d.mts → types-ByHn9rEO.d.mts} +1 -0
- package/dist/{types-C_ZZRcD6.d.mts → types-C4QE8qnm.d.mts} +1 -0
- package/dist/{types-D_n8L6VY.d.mts → types-CT5vu6M0.d.mts} +132 -17
- package/dist/{types-DjC-KUCz.d.mts → types-ChuShpCP.d.mts} +1 -0
- package/dist/{types-Bmmn-fO0.d.mts → types-CwcUQ610.d.mts} +2 -6
- package/dist/{types-CSyCxl2V.mjs → types-DXYX7dIR.mjs} +4 -3
- package/dist/usage/index.d.mts +3 -2
- package/dist/usage/index.mjs +0 -0
- package/dist/{userHelpers-Cg_y3fBz.mjs → userHelpers-BNsOXzOd.mjs} +1 -0
- package/dist/utils/index.d.mts +16 -7
- package/dist/utils/index.mjs +10 -9
- package/dist/{utils-taUVD4p3.mjs → utils-BtOha_Sp.mjs} +3 -2
- package/dist/{validate-Bk44F2yT.mjs → validate-PMCPR9El.mjs} +2 -1
- package/dist/{versioning-gKWyryT3.mjs → versioning-CKv2jz_U.mjs} +4 -3
- package/dist/{versioning-DXgY5BUm.d.mts → versioning-D_rhHfZm.d.mts} +5 -2
- package/dist/{websocket-_Pd0CgR0.d.mts → websocket-BOXVggMf.d.mts} +2 -1
- package/package.json +557 -539
- package/skills/arc/SKILL.md +22 -8
- package/skills/arc/references/api-reference.md +4 -0
- package/skills/arc/references/auth.md +3 -0
- package/skills/arc/references/integrations.md +33 -0
- package/skills/arc/references/mcp.md +6 -7
- package/skills/arc/references/production.md +24 -25
- package/skills/arc-code-review/references/anti-patterns.md +3 -3
- package/dist/fastify-augmentation-0WhPnUH7.d.mts +0 -76
- package/dist/index-CCzVR-N3.d.mts +0 -107
- package/dist/module-_-M7C5ES.mjs +0 -29
package/dist/events/index.mjs
CHANGED
|
@@ -1,8 +1,10 @@
|
|
|
1
|
-
|
|
2
|
-
import {
|
|
3
|
-
import { i as
|
|
4
|
-
import {
|
|
1
|
+
/*! @classytic/arc v2.32.0 | MIT | https://classytic.com */
|
|
2
|
+
import { t as MemoryEventTransport } from "../EventTransport-BV10D0oG.mjs";
|
|
3
|
+
import { a as createEventRegistry, i as wrapWithSchema, n as subscribeWithSchema, o as defineEvent, r as wrapWithBoundary, t as subscribeWithBoundary } from "../subscribe-helpers-sp5xTDVA.mjs";
|
|
4
|
+
import { i as withRetry, r as createDeadLetterPublisher, t as eventPlugin } from "../eventPlugin-Dk_TyXeE.mjs";
|
|
5
|
+
import { n as createSafeGetOne, t as createIsDuplicateKeyError } from "../store-helpers-DmEMHlPQ.mjs";
|
|
5
6
|
import { and, anyOf, eq, lte, ne, or } from "@classytic/repo-core/filter";
|
|
7
|
+
import { MemoryOutboxStore } from "@classytic/primitives/memory-outbox";
|
|
6
8
|
import { InvalidOutboxEventError, OutboxOwnershipError } from "@classytic/primitives/outbox";
|
|
7
9
|
import { update } from "@classytic/repo-core/update";
|
|
8
10
|
//#region src/events/eventTypes.ts
|
|
@@ -56,141 +58,6 @@ const CACHE_EVENTS = Object.freeze({
|
|
|
56
58
|
TAG_VERSION_BUMPED: "arc.cache.tag.bumped"
|
|
57
59
|
});
|
|
58
60
|
//#endregion
|
|
59
|
-
//#region src/events/memory-outbox.ts
|
|
60
|
-
/**
|
|
61
|
-
* In-memory outbox store — reference implementation for dev / testing.
|
|
62
|
-
*
|
|
63
|
-
* Supports the full `OutboxStore` capability set (claim/lease, fail/retry,
|
|
64
|
-
* dedupe, visibleAt). Production deployments should pass a `repository` to
|
|
65
|
-
* `EventOutbox` instead of using this; the memory store exists for unit
|
|
66
|
-
* tests and single-process dev where durable persistence is noise.
|
|
67
|
-
*/
|
|
68
|
-
const DEFAULT_LEASE_MS$2 = 3e4;
|
|
69
|
-
var MemoryOutboxStore = class {
|
|
70
|
-
entries = [];
|
|
71
|
-
seenDedupeKeys = /* @__PURE__ */ new Set();
|
|
72
|
-
/**
|
|
73
|
-
* Drop every entry (including delivered / failed / dead-lettered rows) and
|
|
74
|
-
* the dedupe-key memory.
|
|
75
|
-
*
|
|
76
|
-
* For tests only — it is the in-memory analogue of truncating the table, and
|
|
77
|
-
* exists so a suite can isolate cases against ONE store instance (the
|
|
78
|
-
* `reset` hook of `runOutboxStoreContract`). Not part of `OutboxStore`; a
|
|
79
|
-
* production store must never expose this.
|
|
80
|
-
*/
|
|
81
|
-
clear() {
|
|
82
|
-
this.entries.length = 0;
|
|
83
|
-
this.seenDedupeKeys.clear();
|
|
84
|
-
}
|
|
85
|
-
async save(event, options) {
|
|
86
|
-
if (!event?.type || typeof event.type !== "string") throw new InvalidOutboxEventError("event.type is required");
|
|
87
|
-
if (!event.meta?.id || typeof event.meta.id !== "string") throw new InvalidOutboxEventError("event.meta.id is required");
|
|
88
|
-
if (options?.dedupeKey) {
|
|
89
|
-
if (this.seenDedupeKeys.has(options.dedupeKey)) return;
|
|
90
|
-
this.seenDedupeKeys.add(options.dedupeKey);
|
|
91
|
-
}
|
|
92
|
-
this.entries.push({
|
|
93
|
-
event,
|
|
94
|
-
status: "pending",
|
|
95
|
-
attempts: 0,
|
|
96
|
-
visibleAt: options?.visibleAt?.getTime() ?? 0,
|
|
97
|
-
leaseOwner: null,
|
|
98
|
-
leaseExpiresAt: 0,
|
|
99
|
-
deliveredAt: null,
|
|
100
|
-
firstFailedAt: null,
|
|
101
|
-
lastFailedAt: null,
|
|
102
|
-
lastError: null,
|
|
103
|
-
dedupeKey: options?.dedupeKey
|
|
104
|
-
});
|
|
105
|
-
}
|
|
106
|
-
async getPending(limit) {
|
|
107
|
-
const now = Date.now();
|
|
108
|
-
return this.entries.filter((e) => e.status === "pending" && e.visibleAt <= now && (e.leaseOwner === null || e.leaseExpiresAt <= now)).slice(0, limit).map((e) => e.event);
|
|
109
|
-
}
|
|
110
|
-
async claimPending(options) {
|
|
111
|
-
const now = Date.now();
|
|
112
|
-
const limit = options?.limit ?? 100;
|
|
113
|
-
const leaseMs = options?.leaseMs ?? DEFAULT_LEASE_MS$2;
|
|
114
|
-
const consumerId = options?.consumerId ?? "anonymous";
|
|
115
|
-
const typeFilter = options?.types ? new Set(options.types) : null;
|
|
116
|
-
const claimed = [];
|
|
117
|
-
for (const entry of this.entries) {
|
|
118
|
-
if (claimed.length >= limit) break;
|
|
119
|
-
if (entry.status !== "pending") continue;
|
|
120
|
-
if (entry.visibleAt > now) continue;
|
|
121
|
-
if (entry.leaseOwner !== null && entry.leaseExpiresAt > now) continue;
|
|
122
|
-
if (typeFilter && !typeFilter.has(entry.event.type)) continue;
|
|
123
|
-
entry.leaseOwner = consumerId;
|
|
124
|
-
entry.leaseExpiresAt = now + leaseMs;
|
|
125
|
-
entry.attempts++;
|
|
126
|
-
claimed.push(entry.event);
|
|
127
|
-
}
|
|
128
|
-
return claimed;
|
|
129
|
-
}
|
|
130
|
-
async acknowledge(eventId, options) {
|
|
131
|
-
const entry = this.entries.find((e) => e.event.meta.id === eventId);
|
|
132
|
-
if (!entry) return;
|
|
133
|
-
if (entry.status === "delivered") return;
|
|
134
|
-
if (options?.consumerId && entry.leaseOwner && entry.leaseOwner !== options.consumerId) throw new OutboxOwnershipError(eventId, options.consumerId, entry.leaseOwner);
|
|
135
|
-
entry.status = "delivered";
|
|
136
|
-
entry.deliveredAt = Date.now();
|
|
137
|
-
entry.leaseOwner = null;
|
|
138
|
-
}
|
|
139
|
-
async fail(eventId, error, options) {
|
|
140
|
-
const entry = this.entries.find((e) => e.event.meta.id === eventId);
|
|
141
|
-
if (!entry) return;
|
|
142
|
-
if (options?.consumerId && entry.leaseOwner && entry.leaseOwner !== options.consumerId) throw new OutboxOwnershipError(eventId, options.consumerId, entry.leaseOwner);
|
|
143
|
-
const now = Date.now();
|
|
144
|
-
entry.lastError = error;
|
|
145
|
-
entry.leaseOwner = null;
|
|
146
|
-
entry.leaseExpiresAt = 0;
|
|
147
|
-
if (entry.firstFailedAt === null) entry.firstFailedAt = now;
|
|
148
|
-
entry.lastFailedAt = now;
|
|
149
|
-
if (options?.deadLetter) {
|
|
150
|
-
entry.status = "dead_letter";
|
|
151
|
-
return;
|
|
152
|
-
}
|
|
153
|
-
entry.status = "pending";
|
|
154
|
-
entry.visibleAt = options?.retryAt ? options.retryAt.getTime() : 0;
|
|
155
|
-
}
|
|
156
|
-
async getDeadLettered(limit) {
|
|
157
|
-
const out = [];
|
|
158
|
-
for (const entry of this.entries) {
|
|
159
|
-
if (out.length >= limit) break;
|
|
160
|
-
if (entry.status !== "dead_letter") continue;
|
|
161
|
-
out.push({
|
|
162
|
-
event: entry.event,
|
|
163
|
-
error: {
|
|
164
|
-
message: entry.lastError?.message ?? "unknown",
|
|
165
|
-
...entry.lastError?.code !== void 0 ? { code: entry.lastError.code } : {}
|
|
166
|
-
},
|
|
167
|
-
attempts: entry.attempts,
|
|
168
|
-
firstFailedAt: new Date(entry.firstFailedAt ?? entry.lastFailedAt ?? Date.now()),
|
|
169
|
-
lastFailedAt: new Date(entry.lastFailedAt ?? entry.firstFailedAt ?? Date.now())
|
|
170
|
-
});
|
|
171
|
-
}
|
|
172
|
-
return out;
|
|
173
|
-
}
|
|
174
|
-
async purge(olderThanMs) {
|
|
175
|
-
const cutoff = Date.now() - olderThanMs;
|
|
176
|
-
let purged = 0;
|
|
177
|
-
for (let i = this.entries.length - 1; i >= 0; i--) {
|
|
178
|
-
const entry = this.entries[i];
|
|
179
|
-
if (!entry) continue;
|
|
180
|
-
if (entry.status === "delivered" && entry.deliveredAt !== null && entry.deliveredAt < cutoff) {
|
|
181
|
-
if (entry.dedupeKey) this.seenDedupeKeys.delete(entry.dedupeKey);
|
|
182
|
-
this.entries.splice(i, 1);
|
|
183
|
-
purged++;
|
|
184
|
-
}
|
|
185
|
-
}
|
|
186
|
-
return purged;
|
|
187
|
-
}
|
|
188
|
-
/** Test helper: inspect entry by id */
|
|
189
|
-
_getEntry(eventId) {
|
|
190
|
-
return this.entries.find((e) => e.event.meta.id === eventId);
|
|
191
|
-
}
|
|
192
|
-
};
|
|
193
|
-
//#endregion
|
|
194
61
|
//#region src/events/outbox/backoff.ts
|
|
195
62
|
/**
|
|
196
63
|
* Compute a `retryAt` `Date` using exponential backoff with jitter.
|
|
@@ -330,6 +197,17 @@ function repositoryAsOutboxStore(repository, options) {
|
|
|
330
197
|
*/
|
|
331
198
|
const claimableFilter = (now) => and(eq("status", "pending"), lte(visibleAtField, now), or(eq("leaseOwner", null), lte("leaseExpiresAt", now)));
|
|
332
199
|
return {
|
|
200
|
+
/**
|
|
201
|
+
* Transactional: `save` forwards `options.session` to the backing
|
|
202
|
+
* repository's `create`, so the event row joins the caller's transaction on
|
|
203
|
+
* the SAME connection and commits or rolls back with the domain write.
|
|
204
|
+
*
|
|
205
|
+
* Declared explicitly because `OutboxWriteOptions.session` is best-effort by
|
|
206
|
+
* contract — a caller cannot infer atomicity from the presence of an outbox,
|
|
207
|
+
* only from this flag. See `OutboxStore.transactionalSave` in
|
|
208
|
+
* `@classytic/primitives/outbox`.
|
|
209
|
+
*/
|
|
210
|
+
transactionalSave: true,
|
|
333
211
|
async save(event, options) {
|
|
334
212
|
if (!event?.type || typeof event.type !== "string") throw new InvalidOutboxEventError("event.type is required");
|
|
335
213
|
if (!event.meta?.id || typeof event.meta.id !== "string") throw new InvalidOutboxEventError("event.meta.id is required");
|
|
@@ -475,13 +353,96 @@ function repositoryAsOutboxStore(repository, options) {
|
|
|
475
353
|
if (batch.length < DEFAULT_PURGE_BATCH) break;
|
|
476
354
|
}
|
|
477
355
|
return totalDeleted;
|
|
356
|
+
},
|
|
357
|
+
/**
|
|
358
|
+
* Re-drive one dead-lettered event — the other half of `fail({ deadLetter: true })`.
|
|
359
|
+
*
|
|
360
|
+
* Filtered on `status: 'dead_letter'` as well as the id, so this is a no-op on a row
|
|
361
|
+
* that is merely retrying. Requeuing a backing-off row would zero its attempt count
|
|
362
|
+
* and defeat the failure policy, and the id alone cannot distinguish the two.
|
|
363
|
+
*
|
|
364
|
+
* `attempts` resets because the operator's whole claim is that the CAUSE is fixed:
|
|
365
|
+
* carrying the old count forward would dead-letter the retry again after one more
|
|
366
|
+
* blip, which reads as "the fix did not work".
|
|
367
|
+
*/
|
|
368
|
+
async requeue(eventId) {
|
|
369
|
+
return await r.findOneAndUpdate(and(eq(idField, eventId), eq("status", "dead_letter")), update({ set: {
|
|
370
|
+
status: "pending",
|
|
371
|
+
attempts: 0,
|
|
372
|
+
leaseOwner: null,
|
|
373
|
+
leaseExpiresAt: null,
|
|
374
|
+
[visibleAtField]: /* @__PURE__ */ new Date(),
|
|
375
|
+
lastError: null
|
|
376
|
+
} }), { returnDocument: "after" }) != null;
|
|
377
|
+
},
|
|
378
|
+
/** Rows in `status` — the count behind an operator dashboard or a dead-letter alert. */
|
|
379
|
+
async countByStatus(status) {
|
|
380
|
+
const filters = eq("status", status);
|
|
381
|
+
const counter = repository.count;
|
|
382
|
+
if (typeof counter === "function") return counter.call(repository, filters);
|
|
383
|
+
/**
|
|
384
|
+
* Fallback for a kit without `count`. Two shapes, and the difference decides the
|
|
385
|
+
* query — getting it wrong here produces a number that LIES, which is worse than
|
|
386
|
+
* no number at all: the operator reads a healthy dashboard and stops looking.
|
|
387
|
+
*
|
|
388
|
+
* A paginated kit reports the match count in its envelope, so one row is enough
|
|
389
|
+
* to read it — deliberately not a hydrate-and-length, since a `delivered` count
|
|
390
|
+
* would drag the whole retention window into memory for one integer.
|
|
391
|
+
*
|
|
392
|
+
* A kit that returns a BARE ARRAY has no envelope to read, so the rows ARE the
|
|
393
|
+
* count and every match has to come back. `unwrapDocs` documents that shape as
|
|
394
|
+
* real, so it is not hypothetical. `select: idField` keeps it to one column.
|
|
395
|
+
*/
|
|
396
|
+
const probe = await r.getAll({
|
|
397
|
+
filters,
|
|
398
|
+
page: 1,
|
|
399
|
+
limit: 1,
|
|
400
|
+
select: idField
|
|
401
|
+
});
|
|
402
|
+
if (Array.isArray(probe)) {
|
|
403
|
+
const all = await r.getAll({
|
|
404
|
+
filters,
|
|
405
|
+
select: idField
|
|
406
|
+
});
|
|
407
|
+
return unwrapDocs(all).length;
|
|
408
|
+
}
|
|
409
|
+
const total = probe?.total ?? probe?.meta?.total;
|
|
410
|
+
if (typeof total === "number") return total;
|
|
411
|
+
/**
|
|
412
|
+
* An envelope in a shape neither branch recognises. Throwing beats returning 0:
|
|
413
|
+
* a zero is indistinguishable from a healthy queue, so it would silence exactly
|
|
414
|
+
* the alert this method exists to raise.
|
|
415
|
+
*/
|
|
416
|
+
throw new Error("repositoryAsOutboxStore.countByStatus: the repository's `getAll` returned an envelope with no `total` (and is not a bare array), so the count cannot be read. Implement `count(filter)` on the repository — every kit that has it answers this in one query.");
|
|
417
|
+
},
|
|
418
|
+
/**
|
|
419
|
+
* Age of the oldest PENDING row, or `null` when the queue is empty.
|
|
420
|
+
*
|
|
421
|
+
* The signal a pending count cannot give: a count of 40 looks the same whether the
|
|
422
|
+
* relay is draining briskly or has been wedged for an hour behind one poison row.
|
|
423
|
+
*
|
|
424
|
+
* Measured from `createdAt`, not the last attempt — the question is how long the
|
|
425
|
+
* event has gone undelivered, and restarting the clock on every retry would make a
|
|
426
|
+
* permanently-failing row look permanently fresh, which is the exact row an alert
|
|
427
|
+
* exists to surface.
|
|
428
|
+
*/
|
|
429
|
+
async oldestPendingAgeMs() {
|
|
430
|
+
const result = await r.getAll({
|
|
431
|
+
filters: eq("status", "pending"),
|
|
432
|
+
sort: { createdAt: 1 },
|
|
433
|
+
page: 1,
|
|
434
|
+
limit: 1
|
|
435
|
+
});
|
|
436
|
+
const [oldest] = unwrapDocs(result);
|
|
437
|
+
if (oldest?.createdAt == null) return null;
|
|
438
|
+
return Date.now() - new Date(oldest.createdAt).getTime();
|
|
478
439
|
}
|
|
479
440
|
};
|
|
480
441
|
}
|
|
481
442
|
//#endregion
|
|
482
443
|
//#region src/events/outbox/relay.ts
|
|
483
444
|
/** Default outbox retention — delivered events older than this are eligible for purge */
|
|
484
|
-
const DEFAULT_OUTBOX_RETENTION_MS =
|
|
445
|
+
const DEFAULT_OUTBOX_RETENTION_MS = 6048e5;
|
|
485
446
|
const DEFAULT_LEASE_MS = 3e4;
|
|
486
447
|
/**
|
|
487
448
|
* Hard cap on the in-process attempt-counter map. Entries are cleared on
|
|
@@ -794,4 +755,79 @@ function normalizeError(err) {
|
|
|
794
755
|
return { message: String(err) };
|
|
795
756
|
}
|
|
796
757
|
//#endregion
|
|
797
|
-
|
|
758
|
+
//#region src/events/outbox-module.ts
|
|
759
|
+
/** Default relay cadence. Frequent enough that a retry is not a user-visible delay. */
|
|
760
|
+
const DEFAULT_RELAY_EVERY_MS = 5e3;
|
|
761
|
+
/**
|
|
762
|
+
* Compose the outbox as a module.
|
|
763
|
+
*
|
|
764
|
+
* ```ts
|
|
765
|
+
* // host
|
|
766
|
+
* const model = createOutboxModel(connection, { visibleAtField: 'nextVisibleAt' });
|
|
767
|
+
* createApp({ modules: [
|
|
768
|
+
* createOutboxModule({
|
|
769
|
+
* store: repositoryAsOutboxStore(new Repository(model), { visibleAtField: 'nextVisibleAt' }),
|
|
770
|
+
* onError: (err, ctx) => app.log.error({ err, ...ctx }, 'outbox relay'),
|
|
771
|
+
* }),
|
|
772
|
+
* …
|
|
773
|
+
* ]});
|
|
774
|
+
*
|
|
775
|
+
* // consumer module
|
|
776
|
+
* defineModule({
|
|
777
|
+
* name: 'order',
|
|
778
|
+
* dependsOn: ['outbox'],
|
|
779
|
+
* bootstrap: (f) => createOrder({
|
|
780
|
+
* outbox: getModuleExports<OutboxModuleExports>(f, 'outbox').store,
|
|
781
|
+
* }),
|
|
782
|
+
* });
|
|
783
|
+
* ```
|
|
784
|
+
*
|
|
785
|
+
* `dependsOn` is doing real work there: it both orders the bootstraps and makes a missing outbox a
|
|
786
|
+
* boot error naming the module that needed it.
|
|
787
|
+
*/
|
|
788
|
+
function createOutboxModule(options) {
|
|
789
|
+
const name = options.name ?? "outbox";
|
|
790
|
+
const relayEveryMs = options.relayEveryMs ?? DEFAULT_RELAY_EVERY_MS;
|
|
791
|
+
/**
|
|
792
|
+
* Built at bootstrap and reused by the schedule.
|
|
793
|
+
*
|
|
794
|
+
* Not rebuilt per tick: `EventOutbox` carries the consumer id the lease is keyed on, and a fresh
|
|
795
|
+
* instance per tick would be a fresh claimant every few seconds — which defeats the lease it
|
|
796
|
+
* depends on for exactly-once publishing.
|
|
797
|
+
*/
|
|
798
|
+
let exports;
|
|
799
|
+
return {
|
|
800
|
+
name,
|
|
801
|
+
bootstrap: (fastify) => {
|
|
802
|
+
const store = typeof options.store === "function" ? options.store(fastify) : options.store;
|
|
803
|
+
const transport = options.transport === void 0 ? fastify.events : typeof options.transport === "function" ? options.transport(fastify) : options.transport;
|
|
804
|
+
exports = {
|
|
805
|
+
store,
|
|
806
|
+
relay: new EventOutbox({
|
|
807
|
+
store,
|
|
808
|
+
...transport !== void 0 ? { transport } : {},
|
|
809
|
+
...options.batchSize !== void 0 ? { batchSize: options.batchSize } : {},
|
|
810
|
+
...options.leaseMs !== void 0 ? { leaseMs: options.leaseMs } : {},
|
|
811
|
+
consumerId: options.consumerId ?? "arc-outbox-relay",
|
|
812
|
+
...options.onError !== void 0 ? { onError: options.onError } : {},
|
|
813
|
+
...options.failurePolicy !== void 0 ? { failurePolicy: options.failurePolicy } : {}
|
|
814
|
+
})
|
|
815
|
+
};
|
|
816
|
+
return exports;
|
|
817
|
+
},
|
|
818
|
+
/**
|
|
819
|
+
* The relay runs on the module's OWN schedule arm, so the host stops owning the tick — and
|
|
820
|
+
* a host that forgets to start one cannot exist.
|
|
821
|
+
*/
|
|
822
|
+
scheduledJobs: () => [{
|
|
823
|
+
name: `${name}.relay`,
|
|
824
|
+
every: relayEveryMs,
|
|
825
|
+
runOnStart: options.runOnStart ?? true,
|
|
826
|
+
handler: async () => {
|
|
827
|
+
await exports?.relay.relay();
|
|
828
|
+
}
|
|
829
|
+
}]
|
|
830
|
+
};
|
|
831
|
+
}
|
|
832
|
+
//#endregion
|
|
833
|
+
export { ARC_LIFECYCLE_EVENTS, CACHE_EVENTS, CRUD_EVENT_SUFFIXES, EventOutbox, MemoryEventTransport, MemoryOutboxStore, createDeadLetterPublisher, createEventRegistry, createOutboxModule, crudEventType, defineEvent, eventPlugin, exponentialBackoff, repositoryAsOutboxStore, subscribeWithBoundary, subscribeWithSchema, withRetry, wrapWithBoundary, wrapWithSchema };
|
|
@@ -1,2 +1,3 @@
|
|
|
1
|
-
|
|
1
|
+
|
|
2
|
+
import { n as RedisStreamTransport, r as RedisStreamTransportOptions, t as RedisStreamLike } from "../../redis-stream-qN6A-RFv.mjs";
|
|
2
3
|
export { type RedisStreamLike, RedisStreamTransport, type RedisStreamTransportOptions };
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
/*! @classytic/arc v2.32.0 | MIT | https://classytic.com */
|
|
1
2
|
import sjson from "secure-json-parse";
|
|
2
3
|
//#region src/events/transports/redis-stream.ts
|
|
3
4
|
/**
|
|
@@ -183,13 +184,15 @@ var RedisStreamTransport = class {
|
|
|
183
184
|
this.handlers.clear();
|
|
184
185
|
this.failureContext.clear();
|
|
185
186
|
if (this.pollPromise) {
|
|
186
|
-
if (await Promise.race([this.pollPromise.then(() => "drained"), this.sleep(this.closeTimeoutMs).then(() => "timeout")]) === "timeout")
|
|
187
|
-
if (
|
|
188
|
-
|
|
189
|
-
this.
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
187
|
+
if (await Promise.race([this.pollPromise.then(() => "drained"), this.sleep(this.closeTimeoutMs).then(() => "timeout")]) === "timeout") {
|
|
188
|
+
if (!this.externalLifecycle) {
|
|
189
|
+
if (typeof this.redis.disconnect === "function") this.redis.disconnect();
|
|
190
|
+
else await this.redis.quit().catch((err) => {
|
|
191
|
+
this.logger.error("[RedisStreamTransport] quit() during close raced:", err);
|
|
192
|
+
});
|
|
193
|
+
await this.pollPromise.catch(() => void 0);
|
|
194
|
+
} else this.pollPromise.catch(() => void 0);
|
|
195
|
+
}
|
|
193
196
|
this.pollPromise = null;
|
|
194
197
|
}
|
|
195
198
|
if (!this.externalLifecycle) await this.redis.quit().catch(() => void 0);
|
|
@@ -250,8 +253,10 @@ var RedisStreamTransport = class {
|
|
|
250
253
|
const overRetryIds = [];
|
|
251
254
|
for (const entry of pending) {
|
|
252
255
|
const [id, , idleTime, deliveryCount] = entry;
|
|
253
|
-
if (idleTime > this.claimTimeoutMs)
|
|
254
|
-
|
|
256
|
+
if (idleTime > this.claimTimeoutMs) {
|
|
257
|
+
if (deliveryCount >= this.maxRetries) overRetryIds.push(id);
|
|
258
|
+
else staleIds.push(id);
|
|
259
|
+
}
|
|
255
260
|
}
|
|
256
261
|
if (overRetryIds.length > 0) await this.moveToDlq(overRetryIds);
|
|
257
262
|
if (staleIds.length > 0) {
|
|
@@ -1,4 +1,5 @@
|
|
|
1
|
-
|
|
1
|
+
|
|
2
|
+
import { a as EventTransport, i as EventLogger, n as DomainEvent, r as EventHandler } from "../../EventTransport-2uVV34WU.mjs";
|
|
2
3
|
//#region src/events/transports/redis.d.ts
|
|
3
4
|
interface RedisLike {
|
|
4
5
|
publish(channel: string, message: string): Promise<number>;
|
package/dist/factory/index.d.mts
CHANGED
|
@@ -1,7 +1,8 @@
|
|
|
1
|
-
|
|
2
|
-
import { a as
|
|
3
|
-
import "../
|
|
4
|
-
import {
|
|
1
|
+
|
|
2
|
+
import { _ as HealthCheck, a as ModuleContribution, c as LoadResourcesOptions, d as loadResources, i as EventHandlerDefinition, l as ResourceLike, n as ArcModuleInput, o as ModuleDisposer, r as ArcModuleRegistry, s as ModuleSetupContext, t as ArcModule, u as ResourceModule } from "../types-CT5vu6M0.mjs";
|
|
3
|
+
import { _ as collectModuleScheduledJobs, a as lazyModuleExports, c as getModuleState, d as resolveModule, f as orderModules, g as collectModuleHealthChecks, h as extendModule, i as hasModuleExports, l as hasModule, m as ModuleExtension, n as getModuleExports, o as lazyRequiredModuleExports, p as subscribeModuleEventHandlers, r as getOptionalModuleExports, s as ModuleState, t as defineModule, u as resolveContribution, v as collectModuleWorkflows } from "../index-ZWhLTp_s.mjs";
|
|
4
|
+
import { c as ResolvedResourceDescriptor, l as describeResolvedModule, o as ResolvedModuleDescriptor, s as ResolvedModuleLifecycle } from "../fastify-augmentation-BppG5bMT.mjs";
|
|
5
|
+
import { a as RawBodyOptions, c as BetterAuthOption, d as JwtAuthOption, i as MultipartOptions, l as CustomAuthenticatorOption, n as preloadResourcesAsync, o as UnderPressureOptions, r as CreateAppOptions, s as AuthOption, t as preloadResources, u as CustomPluginAuthOption } from "../preloadResources-T1hmlTEZ.mjs";
|
|
5
6
|
import { FastifyInstance, FastifyServerOptions } from "fastify";
|
|
6
7
|
//#region src/factory/createApp.d.ts
|
|
7
8
|
/**
|
|
@@ -22,6 +23,9 @@ import { FastifyInstance, FastifyServerOptions } from "fastify";
|
|
|
22
23
|
* 10. afterResources() — post-registration wiring
|
|
23
24
|
* 11. onReady/onClose — lifecycle hooks
|
|
24
25
|
* ```
|
|
26
|
+
*
|
|
27
|
+
* **Boot is all-or-nothing.** If any phase throws, the partially-built
|
|
28
|
+
* instance is closed before the error propagates — see the wrapper below.
|
|
25
29
|
*/
|
|
26
30
|
declare function createApp(options: CreateAppOptions): Promise<FastifyInstance>;
|
|
27
31
|
/**
|
|
@@ -133,4 +137,4 @@ declare const testingPreset: Partial<CreateAppOptions>;
|
|
|
133
137
|
declare const edgePreset: Partial<CreateAppOptions>;
|
|
134
138
|
declare function getPreset(name: "production" | "development" | "testing" | "edge" | "worker"): Partial<CreateAppOptions>;
|
|
135
139
|
//#endregion
|
|
136
|
-
export { ArcFactory, type ArcModule, type ArcModuleInput, type ArcModuleRegistry, type ArcWorker, type AuthOption, type BetterAuthOption, type CreateAppOptions, type CreateWorkerOptions, type CustomAuthenticatorOption, type CustomPluginAuthOption, type EventHandlerDefinition, type FetchHandlerOptions, type JwtAuthOption, type LoadResourcesOptions, type ModuleContribution, type MultipartOptions, type RawBodyOptions, type ResourceLike, type ResourceModule, type UnderPressureOptions, type WorkerHealthOptions, collectModuleHealthChecks, collectModuleScheduledJobs, collectModuleWorkflows, createApp, createWorker, defineModule, developmentPreset, edgePreset, getModuleExports, getPreset, loadResources, orderModules, preloadResources, preloadResourcesAsync, productionPreset, resolveContribution, resolveModule, subscribeModuleEventHandlers, testingPreset, toFetchHandler };
|
|
140
|
+
export { ArcFactory, type ArcModule, type ArcModuleInput, type ArcModuleRegistry, type ArcWorker, type AuthOption, type BetterAuthOption, type CreateAppOptions, type CreateWorkerOptions, type CustomAuthenticatorOption, type CustomPluginAuthOption, type EventHandlerDefinition, type FetchHandlerOptions, type JwtAuthOption, type LoadResourcesOptions, type ModuleContribution, type ModuleDisposer, type ModuleExtension, type ModuleSetupContext, type ModuleState, type MultipartOptions, type RawBodyOptions, type ResolvedModuleDescriptor, type ResolvedModuleLifecycle, type ResolvedResourceDescriptor, type ResourceLike, type ResourceModule, type UnderPressureOptions, type WorkerHealthOptions, collectModuleHealthChecks, collectModuleScheduledJobs, collectModuleWorkflows, createApp, createWorker, defineModule, describeResolvedModule, developmentPreset, edgePreset, extendModule, getModuleExports, getModuleState, getOptionalModuleExports, getPreset, hasModule, hasModuleExports, lazyModuleExports, lazyRequiredModuleExports, loadResources, orderModules, preloadResources, preloadResourcesAsync, productionPreset, resolveContribution, resolveModule, subscribeModuleEventHandlers, testingPreset, toFetchHandler };
|
package/dist/factory/index.mjs
CHANGED
|
@@ -1,8 +1,131 @@
|
|
|
1
|
-
|
|
2
|
-
import {
|
|
3
|
-
import { n as
|
|
4
|
-
import { t as
|
|
5
|
-
import {
|
|
1
|
+
/*! @classytic/arc v2.32.0 | MIT | https://classytic.com */
|
|
2
|
+
import { r as mergeHealthChecks, t as healthPlugin } from "../health-5XnTa2uo.mjs";
|
|
3
|
+
import { _ as collectModuleWorkflows, a as edgePreset, c as testingPreset, d as hasModule, f as orderModules, g as collectModuleScheduledJobs, h as collectModuleHealthChecks, i as developmentPreset, l as workerPreset, m as describeResolvedModule, n as createApp, o as getPreset, p as subscribeModuleEventHandlers, s as productionPreset, t as ArcFactory, u as getModuleState, v as resolveContribution, y as resolveModule } from "../createApp-Bv0m-Q6_.mjs";
|
|
4
|
+
import { a as lazyModuleExports, i as hasModuleExports, n as getModuleExports, o as lazyRequiredModuleExports, r as getOptionalModuleExports, t as defineModule } from "../module-Br81am6m.mjs";
|
|
5
|
+
import { t as loadResources } from "../loadResources-DisEAmkm.mjs";
|
|
6
|
+
import { n as preloadResourcesAsync, t as preloadResources } from "../preloadResources-CDs87fTw.mjs";
|
|
7
|
+
//#region src/factory/module/extend.ts
|
|
8
|
+
/** Concatenate two contributions, resolving factories only when arc asks. */
|
|
9
|
+
function mergeContribution(own, extra) {
|
|
10
|
+
if (own === void 0) return extra;
|
|
11
|
+
if (extra === void 0) return own;
|
|
12
|
+
return async (fastify) => {
|
|
13
|
+
const a = typeof own === "function" ? await own(fastify) : own;
|
|
14
|
+
const b = typeof extra === "function" ? await extra(fastify) : extra;
|
|
15
|
+
return [...a, ...b];
|
|
16
|
+
};
|
|
17
|
+
}
|
|
18
|
+
/**
|
|
19
|
+
* Plain-array concat, for the arms that are NOT `ModuleContribution`.
|
|
20
|
+
*
|
|
21
|
+
* `healthChecks` and `errorMappers` are declared as arrays only — arc reads
|
|
22
|
+
* them directly and never calls them. Routing them through the factory merge
|
|
23
|
+
* above would hand arc a function where it expects a list, which does not fail
|
|
24
|
+
* loudly: the arm reads as non-empty and contributes nothing.
|
|
25
|
+
*/
|
|
26
|
+
function mergeArray(own, extra) {
|
|
27
|
+
if (own === void 0) return extra;
|
|
28
|
+
if (extra === void 0) return own;
|
|
29
|
+
return [...own, ...extra];
|
|
30
|
+
}
|
|
31
|
+
/** Order-preserving union — a duplicate edge or claim is not an error. */
|
|
32
|
+
function mergeNames(own, extra) {
|
|
33
|
+
if (own === void 0) return extra;
|
|
34
|
+
if (extra === void 0) return own;
|
|
35
|
+
return [.../* @__PURE__ */ new Set([...own, ...extra])];
|
|
36
|
+
}
|
|
37
|
+
function mergeOwns(own, extra) {
|
|
38
|
+
if (own === "provided" || extra === "provided") return "provided";
|
|
39
|
+
return mergeNames(own, extra);
|
|
40
|
+
}
|
|
41
|
+
/**
|
|
42
|
+
* Run both lifecycle hooks, module's first, composing whatever disposers they
|
|
43
|
+
* return. Teardown runs in REVERSE, matching arc's own module teardown order.
|
|
44
|
+
*/
|
|
45
|
+
function mergeHook(own, extra) {
|
|
46
|
+
if (own === void 0) return extra;
|
|
47
|
+
if (extra === void 0) return own;
|
|
48
|
+
return async (fastify, context) => {
|
|
49
|
+
const d1 = await own(fastify, context);
|
|
50
|
+
const d2 = await extra(fastify, context);
|
|
51
|
+
if (!d1 && !d2) return void 0;
|
|
52
|
+
const disposer = async () => {
|
|
53
|
+
if (d2) await d2();
|
|
54
|
+
if (d1) await d1();
|
|
55
|
+
};
|
|
56
|
+
return disposer;
|
|
57
|
+
};
|
|
58
|
+
}
|
|
59
|
+
/**
|
|
60
|
+
* Close the MODULE first, then the host — the opposite of the disposer order
|
|
61
|
+
* above, and deliberately so.
|
|
62
|
+
*
|
|
63
|
+
* A disposer pair is symmetric setup/teardown, so it unwinds LIFO. `onClose` is
|
|
64
|
+
* not: the resource whose lifetime encloses everything is usually the ENGINE,
|
|
65
|
+
* and in a bring-your-own-engine composition the HOST owns it and hands it to
|
|
66
|
+
* the module. Running the host's `onClose` first would destroy the engine while
|
|
67
|
+
* the module is still draining work against it.
|
|
68
|
+
*
|
|
69
|
+
* be-prod's planning module states exactly this order in prose and implements
|
|
70
|
+
* it by hand — "the module's own onClose first (drains its built-in inFlight),
|
|
71
|
+
* then our host-side fallback inFlight, then destroy the BYO engine". Encoding
|
|
72
|
+
* it here is what stops the next host from getting it backwards.
|
|
73
|
+
*/
|
|
74
|
+
function mergeOnClose(own, extra) {
|
|
75
|
+
if (own === void 0) return extra;
|
|
76
|
+
if (extra === void 0) return own;
|
|
77
|
+
return async (fastify) => {
|
|
78
|
+
await own(fastify);
|
|
79
|
+
await extra(fastify);
|
|
80
|
+
};
|
|
81
|
+
}
|
|
82
|
+
/**
|
|
83
|
+
* Merge a host's additions onto a module. Nothing the module declared is lost.
|
|
84
|
+
*
|
|
85
|
+
* ```ts
|
|
86
|
+
* return extendModule(createOrderModule({ … }), {
|
|
87
|
+
* eventHandlers: () => hostHandlers(), // appended after the module's
|
|
88
|
+
* dependsOn: ["outbox"], // unioned
|
|
89
|
+
* bootstrap: (inner) => async (f, c) => { // wraps, never replaces
|
|
90
|
+
* const exports = await inner?.(f, c);
|
|
91
|
+
* return decorate(exports);
|
|
92
|
+
* },
|
|
93
|
+
* });
|
|
94
|
+
* ```
|
|
95
|
+
*/
|
|
96
|
+
function extendModule(mod, extension) {
|
|
97
|
+
const out = { ...mod };
|
|
98
|
+
/**
|
|
99
|
+
* Assign only when the merge produced something.
|
|
100
|
+
*
|
|
101
|
+
* An absent arm must stay ABSENT rather than become `[]` or `undefined` —
|
|
102
|
+
* arc distinguishes "contributes nothing" from "contributes an empty list"
|
|
103
|
+
* when deciding whether a module participates in an arm at all.
|
|
104
|
+
*
|
|
105
|
+
* Each merge is therefore evaluated ONCE, into this call. Writing the
|
|
106
|
+
* condition and the value as two separate calls would leave twelve pairs of
|
|
107
|
+
* duplicated expressions, where editing one half and not the other changes
|
|
108
|
+
* nothing visible and produces a module whose arm silently disagrees with the
|
|
109
|
+
* check that decided to set it.
|
|
110
|
+
*/
|
|
111
|
+
const set = (key, value) => {
|
|
112
|
+
if (value !== void 0) out[key] = value;
|
|
113
|
+
};
|
|
114
|
+
set("dependsOn", mergeNames(mod.dependsOn, extension.dependsOn));
|
|
115
|
+
set("owns", mergeOwns(mod.owns, extension.owns));
|
|
116
|
+
set("resources", mergeContribution(mod.resources, extension.resources));
|
|
117
|
+
set("eventHandlers", mergeContribution(mod.eventHandlers, extension.eventHandlers));
|
|
118
|
+
set("scheduledJobs", mergeContribution(mod.scheduledJobs, extension.scheduledJobs));
|
|
119
|
+
set("workflows", mergeContribution(mod.workflows, extension.workflows));
|
|
120
|
+
set("healthChecks", mergeArray(mod.healthChecks, extension.healthChecks));
|
|
121
|
+
set("errorMappers", mergeArray(mod.errorMappers, extension.errorMappers));
|
|
122
|
+
set("plugins", mergeHook(mod.plugins, extension.plugins));
|
|
123
|
+
set("afterResources", mergeHook(mod.afterResources, extension.afterResources));
|
|
124
|
+
set("onClose", mergeOnClose(mod.onClose, extension.onClose));
|
|
125
|
+
if (extension.bootstrap) out.bootstrap = extension.bootstrap(mod.bootstrap);
|
|
126
|
+
return out;
|
|
127
|
+
}
|
|
128
|
+
//#endregion
|
|
6
129
|
//#region src/factory/createWorker.ts
|
|
7
130
|
async function createWorker(options, workerOptions = {}) {
|
|
8
131
|
const { auth: _auth, helmet: _helmet, cors: _cors, rateLimit: _rateLimit, multipart: _multipart, rawBody: _rawBody, sensible: _sensible, underPressure: _underPressure, replyHelpers: _replyHelpers, ...runtimeOptions } = options;
|
|
@@ -19,10 +142,11 @@ async function createWorker(options, workerOptions = {}) {
|
|
|
19
142
|
});
|
|
20
143
|
if (workerOptions.health) {
|
|
21
144
|
const { port, host = "0.0.0.0", checks = [] } = workerOptions.health;
|
|
145
|
+
const moduleChecks = app.arc?.healthChecks ?? [];
|
|
22
146
|
const mergedChecks = mergeHealthChecks([
|
|
23
147
|
{
|
|
24
148
|
owner: "the module graph",
|
|
25
|
-
checks:
|
|
149
|
+
checks: moduleChecks
|
|
26
150
|
},
|
|
27
151
|
{
|
|
28
152
|
owner: "shared arcPlugins.health",
|
|
@@ -120,4 +244,4 @@ function payloadTooLarge(limit) {
|
|
|
120
244
|
});
|
|
121
245
|
}
|
|
122
246
|
//#endregion
|
|
123
|
-
export { ArcFactory, collectModuleHealthChecks, collectModuleScheduledJobs, collectModuleWorkflows, createApp, createWorker, defineModule, developmentPreset, edgePreset, getModuleExports, getPreset, loadResources, orderModules, preloadResources, preloadResourcesAsync, productionPreset, resolveContribution, resolveModule, subscribeModuleEventHandlers, testingPreset, toFetchHandler };
|
|
247
|
+
export { ArcFactory, collectModuleHealthChecks, collectModuleScheduledJobs, collectModuleWorkflows, createApp, createWorker, defineModule, describeResolvedModule, developmentPreset, edgePreset, extendModule, getModuleExports, getModuleState, getOptionalModuleExports, getPreset, hasModule, hasModuleExports, lazyModuleExports, lazyRequiredModuleExports, loadResources, orderModules, preloadResources, preloadResourcesAsync, productionPreset, resolveContribution, resolveModule, subscribeModuleEventHandlers, testingPreset, toFetchHandler };
|