@classytic/arc 2.41.1 → 2.44.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/dist/{EventTransport-CbUKDzZZ.d.mts → EventTransport-CiicKz2Z.d.mts} +1 -1
- package/dist/{EventTransport-DSqD_Xsg.mjs → EventTransport-DZWDsaPa.mjs} +22 -17
- package/dist/{HookSystem-CMF94SEA.mjs → HookSystem-yjEgn7Cm.mjs} +2 -2
- package/dist/{QueryCache-DFA-QrU9.d.mts → QueryCache--5uAuYPW.d.mts} +7 -3
- package/dist/{ResourceRegistry-MGpXoxHZ.mjs → ResourceRegistry-DJxHUpE4.mjs} +44 -13
- package/dist/acquire-BozJ-9yt.mjs +35 -0
- package/dist/actionPermissions-5N04zBL4.mjs +772 -0
- package/dist/audit/index.d.mts +46 -8
- package/dist/audit/index.mjs +48 -26
- package/dist/auth/audit.d.mts +8 -8
- package/dist/auth/audit.mjs +1 -1
- package/dist/auth/index.d.mts +46 -16
- package/dist/auth/index.mjs +134 -156
- package/dist/auth/redis-session.d.mts +4 -5
- package/dist/auth/redis-session.mjs +1 -1
- package/dist/{authorizationDecision-DawKGoXb.mjs → authorizationDecision-wX3SA9w_.mjs} +7 -102
- package/dist/{base-CxrV1LmG.d.mts → base-CZyixEQR.d.mts} +27 -5
- package/dist/boundedMap-DWiKW7lP.mjs +57 -0
- package/dist/{buildHandler-CvxKOc0m.mjs → buildHandler-Bswu_vNb.mjs} +4 -4
- package/dist/cache/index.d.mts +31 -43
- package/dist/cache/index.mjs +4 -4
- package/dist/{caching-DbCBxlgw.mjs → caching-Drp68DIS.mjs} +3 -3
- package/dist/claimed-DYvQoZLv.mjs +8 -0
- package/dist/cleanup/index.d.mts +18 -22
- package/dist/cleanup/index.mjs +27 -73
- package/dist/cli/commands/describe.d.mts +7 -5
- package/dist/cli/commands/describe.mjs +7 -4
- package/dist/cli/commands/docs.d.mts +2 -3
- package/dist/cli/commands/docs.mjs +3 -3
- package/dist/cli/commands/doctor.d.mts +2 -3
- package/dist/cli/commands/doctor.mjs +1 -1
- package/dist/cli/commands/generate.mjs +2 -3
- package/dist/cli/commands/init.mjs +2 -3
- package/dist/cli/commands/introspect.d.mts +2 -3
- package/dist/cli/commands/introspect.mjs +2 -2
- package/dist/cli/index.mjs +3 -3
- package/dist/{constants-D5Ubn1fe.mjs → constants-B2QHb6u6.mjs} +4 -2
- package/dist/context/index.d.mts +11 -14
- package/dist/context/index.mjs +18 -38
- package/dist/core/index.d.mts +3 -3
- package/dist/core/index.mjs +8 -8
- package/dist/{core-5lazhva9.mjs → core-COcqo0Cp.mjs} +15 -5
- package/dist/{createActionRouter-DNqoaK81.mjs → createActionRouter-zG13rpPJ.mjs} +97 -39
- package/dist/{createAggregationRouter-BcbQJf10.mjs → createAggregationRouter-CWRz96Vr.mjs} +11 -8
- package/dist/{preloadResources-f3boenVZ.mjs → createApp-BGLYtErt.mjs} +305 -394
- package/dist/{createMcpServer-CbDWkZ1T.d.mts → createMcpServer-DSztofa9.d.mts} +1 -1
- package/dist/{defineAggregation-D_eJjm5A.mjs → defineAggregation--3xguLkk.mjs} +1 -1
- package/dist/{defineResource-DxkMTjRn.mjs → defineResource-gUp9ug66.mjs} +118 -1024
- package/dist/discovery/index.d.mts +6 -7
- package/dist/discovery/index.mjs +1 -1
- package/dist/docs/index.d.mts +11 -5
- package/dist/docs/index.mjs +4 -4
- package/dist/{elevation-CkvG0LG2.mjs → elevation-BG4gnMsJ.mjs} +41 -12
- package/dist/{elevation-BXCfVcNG.d.mts → elevation-BW2J3tNj.d.mts} +1 -2
- package/dist/encryption/index.d.mts +5 -5
- package/dist/encryption/index.mjs +3 -3
- package/dist/{errorHandler-D1GHKGlV.mjs → errorHandler-COHw3N1b.mjs} +26 -5
- package/dist/{errors-BH7YU_9O.mjs → errors-CW4anJBy.mjs} +58 -23
- package/dist/{errors-BGdVwP25.d.mts → errors-JYc9vwqE.d.mts} +9 -0
- package/dist/{eventPlugin-B52abJ8W.d.mts → eventPlugin-BtmA4-3P.d.mts} +65 -9
- package/dist/{eventPlugin-zZY-unhr.mjs → eventPlugin-BweHAVws.mjs} +82 -21
- package/dist/{subscribe-helpers-DatDkcuG.mjs → eventTypes-N4-omQFR.mjs} +151 -17
- package/dist/events/index.d.mts +43 -18
- package/dist/events/index.mjs +49 -53
- package/dist/events/transports/redis-stream-entry.d.mts +1 -1
- package/dist/events/transports/redis-stream-entry.mjs +139 -157
- package/dist/events/transports/redis.d.mts +5 -6
- package/dist/events/transports/redis.mjs +2 -2
- package/dist/factory/index.d.mts +42 -27
- package/dist/factory/index.mjs +213 -32
- package/dist/{fastify-augmentation-Cq2nHVaW.d.mts → fastify-augmentation-DRlCIWE9.d.mts} +11 -25
- package/dist/{fields-BHrkoF5y.mjs → fields-A5crhsJb.mjs} +1 -1
- package/dist/{fields-I_EeRWND.d.mts → fields-BBSpf8pS.d.mts} +1 -1
- package/dist/filter-merge-DGM-GQ1J.mjs +99 -0
- package/dist/{generate-BLRf2m3y.mjs → generate-BXKo2Gc8.mjs} +2 -2
- package/dist/{headers-BIXWKrzQ.mjs → headers-DKZQKL-Z.mjs} +2 -2
- package/dist/health-BYAwA6YS.mjs +22 -0
- package/dist/{health-ZRHeDHXH.mjs → health-CrqUp3sz.mjs} +115 -55
- package/dist/hooks/index.d.mts +2 -2
- package/dist/hooks/index.mjs +2 -3
- package/dist/idempotency/index.d.mts +36 -10
- package/dist/idempotency/index.mjs +230 -111
- package/dist/idempotency/redis.d.mts +1 -1
- package/dist/idempotency/redis.mjs +77 -12
- package/dist/{index-D4u76eQW.d.mts → index-Beg3aNnK.d.mts} +23 -101
- package/dist/{preloadResources-CjS2OfpU.d.mts → index-BkV55WlV.d.mts} +37 -38
- package/dist/{index-BHdZD8zK.d.mts → index-Bxriq3so.d.mts} +84 -21
- package/dist/index-CUJvoyw3.d.mts +510 -0
- package/dist/{index-CbJiYrfv.d.mts → index-Chgz3d0i.d.mts} +1 -1
- package/dist/{index-DHbyPo5j.d.mts → index-WH7xgvxS.d.mts} +304 -254
- package/dist/index.d.mts +9 -9
- package/dist/index.mjs +11 -11
- package/dist/{init-CpLkjU18.mjs → init-BPzK5Lnr.mjs} +1 -1
- package/dist/integrations/event-gateway.d.mts +11 -7
- package/dist/integrations/event-gateway.mjs +4 -4
- package/dist/integrations/index.d.mts +3 -3
- package/dist/integrations/index.mjs +1 -1
- package/dist/integrations/jobs/index.d.mts +1 -1
- package/dist/integrations/jobs/index.mjs +18 -5
- package/dist/integrations/mcp/index.d.mts +102 -89
- package/dist/integrations/mcp/index.mjs +19 -4
- package/dist/integrations/mcp/testing.d.mts +8 -9
- package/dist/integrations/mcp/testing.mjs +2 -2
- package/dist/integrations/streamline.d.mts +85 -144
- package/dist/integrations/streamline.mjs +131 -55
- package/dist/integrations/webhooks.d.mts +83 -143
- package/dist/integrations/webhooks.mjs +36 -80
- package/dist/integrations/websocket-pushref-redis.d.mts +5 -6
- package/dist/integrations/websocket-pushref-redis.mjs +1 -1
- package/dist/integrations/websocket-redis.d.mts +5 -6
- package/dist/integrations/websocket-redis.mjs +1 -1
- package/dist/integrations/websocket.d.mts +3 -3
- package/dist/integrations/websocket.mjs +2 -2
- package/dist/{interface-DEWNOMza.d.mts → interface-JJzwH4Ro.d.mts} +34 -2
- package/dist/{jsonBody-ELR9lfeU.mjs → jsonBody-YIq-vGLv.mjs} +2 -2
- package/dist/{keys-cpZCtcIE.mjs → keys-CpoiZ8hU.mjs} +7 -18
- package/dist/{loadResources-BPfWiI5s.mjs → loadResources-6jO52-RR.mjs} +3 -3
- package/dist/{loadResourcesFromEntry-ixtfL3J2.mjs → loadResourcesFromEntry-MP8xEppu.mjs} +2 -2
- package/dist/logger/index.d.mts +9 -17
- package/dist/logger/index.mjs +2 -153
- package/dist/logger-D4V1zGLI.mjs +160 -0
- package/dist/{mcpPlugin-BX5PujI0.mjs → mcpPlugin-srPc7ItN.mjs} +446 -170
- package/dist/{memory-Iotz0cCx.mjs → memory-CTa5pOCW.mjs} +36 -34
- package/dist/{metrics-NMsmpRmY.mjs → metrics-DsZrvnpB.mjs} +39 -72
- package/dist/middleware/index.d.mts +5 -5
- package/dist/middleware/index.mjs +3 -3
- package/dist/migrations/index.d.mts +13 -13
- package/dist/migrations/index.mjs +2 -2
- package/dist/{module-DpLJG2_N.mjs → module--L0iBIZz.mjs} +8 -8
- package/dist/mongodb-memory-server-CHhYSq5y.mjs +35539 -0
- package/dist/mongoose-CYJCLyi-.mjs +67451 -0
- package/dist/multiTenant-CAtYlW6H.d.mts +104 -0
- package/dist/{multipartBody-DtRWM6_U.mjs → multipartBody-Cc7eZ9cq.mjs} +46 -7
- package/dist/node-D8UZv_8c.mjs +41236 -0
- package/dist/{openapi-CmFf3yq_.mjs → openapi-CMegfsm8.mjs} +48 -10
- package/dist/outbox-admin/index.d.mts +5 -5
- package/dist/outbox-admin/index.mjs +6 -18
- package/dist/{outbox-module-D2z5WkDY.mjs → outbox-module-Ryk270jd.mjs} +203 -33
- package/dist/{outbox-module-BHdUnuuK.d.mts → outbox-module-gZCHoE4H.d.mts} +79 -6
- package/dist/permissions/index.d.mts +5 -4
- package/dist/permissions/index.mjs +8 -6
- package/dist/{permissions-D8TBH44E.mjs → permissions-C5ijIz5Q.mjs} +149 -33
- package/dist/{pipe-FMWNheSO.mjs → pipe-D81zMtWs.mjs} +2 -2
- package/dist/pipeline/index.d.mts +7 -7
- package/dist/pipeline/index.mjs +2 -2
- package/dist/plan-CJGMKoui.mjs +565 -0
- package/dist/{BaseController-CYEwjyjN.mjs → plugin-DALd9dcF.mjs} +1269 -346
- package/dist/plugins/index.d.mts +17 -14
- package/dist/plugins/index.mjs +44 -22
- package/dist/plugins/response-cache.d.mts +5 -6
- package/dist/plugins/response-cache.mjs +8 -14
- package/dist/plugins/tracing-entry.mjs +2 -2
- package/dist/{pluralize-CXWZIzTv.mjs → pluralize-COUNYo3d.mjs} +1 -1
- package/dist/presets/filesUpload.d.mts +8 -8
- package/dist/presets/filesUpload.mjs +5 -5
- package/dist/presets/index.d.mts +35 -40
- package/dist/presets/index.mjs +4 -4
- package/dist/presets/multiTenant.d.mts +2 -107
- package/dist/presets/multiTenant.mjs +22 -162
- package/dist/presets/search.d.mts +9 -10
- package/dist/presets/search.mjs +2 -2
- package/dist/{presets-ZJSPgmT2.mjs → presets-CONf2Qem.mjs} +99 -107
- package/dist/{projection-BzIgzoKZ.mjs → projection-wFoLqdss.mjs} +2 -2
- package/dist/{pushref-registry-Dov9l4qE.d.mts → pushref-registry-UxCkoA4C.d.mts} +1 -1
- package/dist/{queryCachePlugin-DZHPOm9p.mjs → queryCachePlugin-BATECpyh.mjs} +12 -14
- package/dist/{queryCachePlugin-BzqxconG.d.mts → queryCachePlugin-C2lZjTxk.d.mts} +1 -1
- package/dist/{rateLimitKey-BvOvcBDi.mjs → rateLimitKey-DVEZ_UpV.mjs} +1 -1
- package/dist/{realtime-8tIQWZz4.mjs → realtime-xyubMKSB.mjs} +25 -21
- package/dist/{redis-Bzi1y3F9.d.mts → redis-BDgv9Glw.d.mts} +17 -24
- package/dist/redis-stream-BMk-bpQS.d.mts +219 -0
- package/dist/redisShapes-CQksY67q.d.mts +55 -0
- package/dist/registry/index.d.mts +15 -10
- package/dist/registry/index.mjs +4 -4
- package/dist/{registry-DNkgGQcK.mjs → registry-Bdzg-hdm.mjs} +13 -10
- package/dist/{renewingLease-yF8BdW4b.mjs → renewingLease-BFJxGPuh.mjs} +1 -1
- package/dist/{replyHelpers-CNdvMfM4.mjs → replyHelpers-Bnc6lJyy.mjs} +2 -2
- package/dist/{requestContext-cDkAUr-P.mjs → requestContext-C10UeFe5.mjs} +1 -1
- package/dist/requirement-Bu50QALH.mjs +35 -0
- package/dist/requirement-CbiLdQ1n.d.mts +22 -0
- package/dist/resourceRateLimit-Wf6d6FL9.d.mts +30 -0
- package/dist/resourceRateLimit-urWPJxrt.mjs +180 -0
- package/dist/rolldown-runtime-MYrLNGnq.mjs +45 -0
- package/dist/routeValidator-DHtpzrzk.mjs +70 -0
- package/dist/{runtimeCapabilities-CkhS6jn6.mjs → runtimeCapabilities-DIMKMV2G.mjs} +1 -1
- package/dist/{schedules-j6uXXAeJ.mjs → schedules-CKTs5ys8.mjs} +46 -37
- package/dist/{schemaConverter-BVA7cFRG.mjs → schemaConverter-BVjkQ5DD.mjs} +2 -2
- package/dist/{schemaIR-CTvPDoPB.mjs → schemaIR-BA0Hb21Y.mjs} +2 -2
- package/dist/schemas/index.d.mts +20 -10
- package/dist/schemas/index.mjs +8 -3
- package/dist/scim/index.d.mts +13 -13
- package/dist/scim/index.mjs +2 -2
- package/dist/scope/index.d.mts +4 -4
- package/dist/scope/index.mjs +6 -6
- package/dist/{scope-BUi1xV1I.mjs → scope-DINaj7Ax.mjs} +4 -4
- package/dist/shutdown-LpLMH8T1.mjs +59 -0
- package/dist/{simpleEqualityMatcher-C9bzq2gO.mjs → simpleEqualityMatcher-DbwIM1f1.mjs} +1 -1
- package/dist/{sse-BeqVKQKu.mjs → sse-BzJFPX_1.mjs} +11 -15
- package/dist/{sseStream-Ppzo-WUH.mjs → sseStream-DQChzGTs.mjs} +2 -2
- package/dist/{store-helpers-dW71XVWL.mjs → store-helpers-Coj9RhOZ.mjs} +1 -1
- package/dist/{streaming-IcRFjWQU.mjs → streaming-JzB5CAga.mjs} +1 -1
- package/dist/subscriberIdentity-DeyppIif.mjs +28 -0
- package/dist/tenancy-DxV8OuX2.mjs +43 -0
- package/dist/tenantPreparation-CBvvAcAj.mjs +67 -0
- package/dist/testing/cleanupStoreContract.d.mts +4 -5
- package/dist/testing/cleanupStoreContract.mjs +1 -1
- package/dist/testing/index.d.mts +215 -197
- package/dist/testing/index.mjs +289 -214
- package/dist/testing/outboxStoreContract.d.mts +4 -5
- package/dist/testing/outboxStoreContract.mjs +137 -2
- package/dist/testing/requiredPorts.d.mts +16 -0
- package/dist/testing/requiredPorts.mjs +65 -0
- package/dist/testing/storageContract.d.mts +4 -5
- package/dist/testing/storageContract.mjs +1 -1
- package/dist/timing-CntavTlR.mjs +46 -0
- package/dist/{transactionContext-CPbdlnVw.mjs → transactionContext-orvnGu4F.mjs} +1 -1
- package/dist/{typeGuards-BLjPgVBm.mjs → typeGuards-kEaP_WmO.mjs} +1 -1
- package/dist/types/index.d.mts +4 -4
- package/dist/types/index.mjs +1 -1
- package/dist/types/storage.mjs +1 -1
- package/dist/{types-DfVDvkdP.d.mts → types-BJ-2XqVF.d.mts} +189 -204
- package/dist/{types-CWQn9-IC.d.mts → types-D3Ixsh00.d.mts} +22 -9
- package/dist/{types-CgenB1gv.mjs → types-fvO6Ovzx.mjs} +4 -4
- package/dist/usage/index.d.mts +5 -6
- package/dist/usage/index.mjs +5 -5
- package/dist/{userHelpers-ChRvkuSZ.mjs → userHelpers-C76zD5pa.mjs} +1 -1
- package/dist/utils/index.d.mts +125 -264
- package/dist/utils/index.mjs +13 -12
- package/dist/{utils-CxcctrAh.mjs → utils-BpkVZ8rB.mjs} +315 -404
- package/dist/{validate-DrYWnOzI.mjs → validate-CWp-lsrm.mjs} +6 -14
- package/dist/{versioning-D6JIK8Tk.d.mts → versioning-Bsb51gGT.d.mts} +28 -13
- package/dist/{versioning-DKkT4hNJ.mjs → versioning-CHhL9P3o.mjs} +4 -4
- package/dist/{websocket-BOXVggMf.d.mts → websocket-Cs4awVyD.d.mts} +1 -1
- package/dist/{workScope-CxKqoIFn.mjs → workScope-n9a7d_lZ.mjs} +2 -2
- package/package.json +57 -59
- package/skills/arc/SKILL.md +13 -4
- package/skills/arc/references/auth.md +8 -5
- package/skills/arc/references/integrations.md +1 -1
- package/skills/arc/references/mcp.md +8 -6
- package/skills/arc/references/multi-tenancy.md +19 -14
- package/skills/arc/references/production.md +27 -10
- package/dist/actionPermissions-BoPGQ5-4.mjs +0 -350
- package/dist/index-CFfLKMH4.d.mts +0 -290
- package/dist/interface-YzIbF7Ii.mjs +0 -17
- package/dist/redis-stream-BPKscsTB.d.mts +0 -260
- package/dist/registerRoute-Bsnk6NiZ.mjs +0 -364
- package/dist/rolldown-runtime-eysyzFAF.mjs +0 -15
- package/dist/sync/changelog.d.mts +0 -3
- package/dist/sync/changelog.mjs +0 -3
|
@@ -1,15 +1,22 @@
|
|
|
1
|
-
/*! @classytic/arc v2.
|
|
2
|
-
import {
|
|
3
|
-
import {
|
|
4
|
-
import { n as PUBLIC_SCOPE } from "./types-
|
|
5
|
-
import { r as initRuntimeCapabilityRegistry, t as auditRuntimeCapabilities } from "./runtimeCapabilities-
|
|
6
|
-
import {
|
|
7
|
-
import {
|
|
8
|
-
import {
|
|
9
|
-
import { r as
|
|
10
|
-
import {
|
|
1
|
+
/*! @classytic/arc v2.44.0 | MIT | https://classytic.com */
|
|
2
|
+
import { r as __exportAll } from "./rolldown-runtime-MYrLNGnq.mjs";
|
|
3
|
+
import { a as enterArcLoggerScope, i as runWithArcLogger, n as configureArcLogger, r as createPinoWriter, t as arcLog } from "./logger-D4V1zGLI.mjs";
|
|
4
|
+
import { n as PUBLIC_SCOPE } from "./types-fvO6Ovzx.mjs";
|
|
5
|
+
import { r as initRuntimeCapabilityRegistry, t as auditRuntimeCapabilities } from "./runtimeCapabilities-DIMKMV2G.mjs";
|
|
6
|
+
import { a as runShutdown, t as initShutdownRegistry } from "./shutdown-LpLMH8T1.mjs";
|
|
7
|
+
import { n as isScopeAwareKeyGenerator, t as createTenantKeyGenerator } from "./rateLimitKey-DVEZ_UpV.mjs";
|
|
8
|
+
import { i as resolveResourceRateLimiter, n as markRateLimitingDisabled } from "./resourceRateLimit-urWPJxrt.mjs";
|
|
9
|
+
import { r as markArcRootValidator, t as arcAjvCustomOptions } from "./routeValidator-DHtpzrzk.mjs";
|
|
10
|
+
import { c as createRequestIdGenerator, r as mergeHealthChecks } from "./health-CrqUp3sz.mjs";
|
|
11
|
+
import { t as parseJsonBody } from "./jsonBody-YIq-vGLv.mjs";
|
|
12
|
+
import { a as portsForModule, b as verifyEventDurability, f as resolveModuleArm, g as crudEventsOf, h as buildModuleGraph, m as analyzeModuleGraph, n as composeModules, x as verifyModuleGraph } from "./plan-CJGMKoui.mjs";
|
|
13
|
+
import { c as wrapWithAttribution, f as defineEvent, l as wrapWithBoundary, r as CRUD_EVENT_SCHEMA } from "./eventTypes-N4-omQFR.mjs";
|
|
14
|
+
import { r as withSubscriberId } from "./subscriberIdentity-DeyppIif.mjs";
|
|
15
|
+
import { r as getOptionalModuleExports } from "./module--L0iBIZz.mjs";
|
|
16
|
+
import { isProductionEnv, readEnv } from "@classytic/primitives/environment";
|
|
11
17
|
import Fastify from "fastify";
|
|
12
18
|
import qs from "qs";
|
|
19
|
+
import { createEventRouter } from "@classytic/primitives/events";
|
|
13
20
|
//#region src/factory/security/pluginLoader.ts
|
|
14
21
|
const PLUGIN_REGISTRY = {
|
|
15
22
|
cors: {
|
|
@@ -205,47 +212,6 @@ async function registerAssetRoots(fastify, roots) {
|
|
|
205
212
|
}
|
|
206
213
|
}
|
|
207
214
|
//#endregion
|
|
208
|
-
//#region src/factory/module/resolve.ts
|
|
209
|
-
/**
|
|
210
|
-
* Resolve any module-input form to a concrete `ArcModule`. A thunk is invoked
|
|
211
|
-
* (this is where a dynamic `import()` fires); a promise (or plain module) is
|
|
212
|
-
* awaited. Called once per module at the start of the bootstrap phase.
|
|
213
|
-
*/
|
|
214
|
-
async function resolveModule(input) {
|
|
215
|
-
const resolved = typeof input === "function" ? await input() : await input;
|
|
216
|
-
if (!resolved || typeof resolved !== "object" || typeof resolved.name !== "string") throw new Error("[arc] a `modules` entry resolved to something that is not an ArcModule (expected an object with a string `name`). Check dynamic-import thunks return `createXModule(deps)` (or the module object), not the namespace.");
|
|
217
|
-
if (resolved.name.trim().length === 0) throw new Error("[arc] a `modules` entry has an empty (or whitespace-only) `name`. A module's name is its graph key, its `arc.modules` export key, its `dependsOn` target, and its label in every boot diagnostic — give it a stable, non-empty identifier.");
|
|
218
|
-
return resolved;
|
|
219
|
-
}
|
|
220
|
-
/** Resolve a `ModuleContribution` to a concrete array (array passthrough or factory call). */
|
|
221
|
-
async function resolveContribution(contribution, fastify) {
|
|
222
|
-
if (!contribution) return [];
|
|
223
|
-
return typeof contribution === "function" ? await contribution(fastify) : contribution;
|
|
224
|
-
}
|
|
225
|
-
/**
|
|
226
|
-
* `resolveContribution` with module attribution — the form every arm collector
|
|
227
|
-
* should use.
|
|
228
|
-
*
|
|
229
|
-
* A contribution factory closes over booted engines, so it throws for exactly
|
|
230
|
-
* the reasons a bootstrap does ("connection unavailable", "engine not ready").
|
|
231
|
-
* Bare, that message names neither the module nor the arm, which in a graph of
|
|
232
|
-
* twenty modules turns a one-line fix into a bisect. Every other module
|
|
233
|
-
* boundary (`plugins`, `bootstrap`, `resources`, `afterResources`) already
|
|
234
|
-
* wraps with the owning module's name; this keeps the arms consistent with
|
|
235
|
-
* them. The original error is preserved via `{ cause }`.
|
|
236
|
-
*/
|
|
237
|
-
async function resolveModuleArm(module, arm, contribution, fastify) {
|
|
238
|
-
let resolved;
|
|
239
|
-
try {
|
|
240
|
-
resolved = await resolveContribution(contribution, fastify);
|
|
241
|
-
} catch (err) {
|
|
242
|
-
const msg = err instanceof Error ? err.message : String(err);
|
|
243
|
-
throw new Error(`[arc] module "${module.name}" ${arm} factory threw: ${msg}`, { cause: err });
|
|
244
|
-
}
|
|
245
|
-
if (!Array.isArray(resolved)) throw new TypeError(`[arc] module "${module.name}" ${arm} factory must return an array — received ${resolved === null ? "null" : typeof resolved}.`);
|
|
246
|
-
return resolved;
|
|
247
|
-
}
|
|
248
|
-
//#endregion
|
|
249
215
|
//#region src/factory/module/contributions.ts
|
|
250
216
|
/**
|
|
251
217
|
* Collect every module's `healthChecks` in the caller's order. Fails at boot
|
|
@@ -321,7 +287,15 @@ function describeResolvedModule(input) {
|
|
|
321
287
|
}
|
|
322
288
|
//#endregion
|
|
323
289
|
//#region src/factory/module/disposers.ts
|
|
324
|
-
|
|
290
|
+
const NO_PORTS = Object.freeze({});
|
|
291
|
+
/**
|
|
292
|
+
* The one typing boundary for ports: `assertModuleRequirements` has already proven every
|
|
293
|
+
* declared required port present, so the module's subset is read as its declared shape.
|
|
294
|
+
*/
|
|
295
|
+
function asModulePorts(ports) {
|
|
296
|
+
return ports;
|
|
297
|
+
}
|
|
298
|
+
function createDisposerRegistry(fastify, portsOf = () => NO_PORTS) {
|
|
325
299
|
const stacks = /* @__PURE__ */ new Map();
|
|
326
300
|
const drained = /* @__PURE__ */ new Set();
|
|
327
301
|
const contexts = /* @__PURE__ */ new Map();
|
|
@@ -329,25 +303,28 @@ function createDisposerRegistry(fastify) {
|
|
|
329
303
|
contextFor(moduleName) {
|
|
330
304
|
const existing = contexts.get(moduleName);
|
|
331
305
|
if (existing) return existing;
|
|
332
|
-
const ctx = {
|
|
333
|
-
|
|
334
|
-
|
|
335
|
-
(
|
|
336
|
-
|
|
337
|
-
|
|
338
|
-
|
|
339
|
-
|
|
340
|
-
|
|
341
|
-
|
|
342
|
-
|
|
343
|
-
|
|
344
|
-
|
|
345
|
-
|
|
306
|
+
const ctx = {
|
|
307
|
+
ports: asModulePorts(portsOf(moduleName)),
|
|
308
|
+
defer(disposer) {
|
|
309
|
+
if (typeof disposer !== "function") throw new TypeError(`[arc] module "${moduleName}" called defer() with a ${typeof disposer} — expected a function.`);
|
|
310
|
+
if (drained.has(moduleName)) {
|
|
311
|
+
(async () => {
|
|
312
|
+
try {
|
|
313
|
+
await disposer();
|
|
314
|
+
} catch (err) {
|
|
315
|
+
fastify.log?.error({
|
|
316
|
+
err,
|
|
317
|
+
module: moduleName
|
|
318
|
+
}, `[arc] module "${moduleName}" deferred a disposer after teardown had already run; it was invoked immediately and threw.`);
|
|
319
|
+
}
|
|
320
|
+
})();
|
|
321
|
+
return;
|
|
322
|
+
}
|
|
323
|
+
const stack = stacks.get(moduleName);
|
|
324
|
+
if (stack) stack.push(disposer);
|
|
325
|
+
else stacks.set(moduleName, [disposer]);
|
|
346
326
|
}
|
|
347
|
-
|
|
348
|
-
if (stack) stack.push(disposer);
|
|
349
|
-
else stacks.set(moduleName, [disposer]);
|
|
350
|
-
} };
|
|
327
|
+
};
|
|
351
328
|
contexts.set(moduleName, ctx);
|
|
352
329
|
return ctx;
|
|
353
330
|
},
|
|
@@ -372,7 +349,7 @@ function createDisposerRegistry(fastify) {
|
|
|
372
349
|
* Resolves factory contributions (after bootstraps) so a handler can close over
|
|
373
350
|
* booted engines rather than a global getter.
|
|
374
351
|
*/
|
|
375
|
-
async function subscribeModuleEventHandlers(fastify, modules, countsByModule) {
|
|
352
|
+
async function subscribeModuleEventHandlers(fastify, modules, countsByModule, consumedByModule, handlersByModule) {
|
|
376
353
|
const unsubscribes = [];
|
|
377
354
|
const owner = /* @__PURE__ */ new Map();
|
|
378
355
|
try {
|
|
@@ -382,14 +359,24 @@ async function subscribeModuleEventHandlers(fastify, modules, countsByModule) {
|
|
|
382
359
|
if (handlers.length === 0) continue;
|
|
383
360
|
const bus = fastify.events;
|
|
384
361
|
if (!bus) throw new Error(`[arc] module "${m.name}" declares eventHandlers but the event subsystem is unavailable (fastify.events). Enable arcPlugins.events.`);
|
|
385
|
-
for (const def of handlers) {
|
|
362
|
+
for (const [index, def] of handlers.entries()) {
|
|
386
363
|
if (def.name !== void 0) {
|
|
387
364
|
const prior = owner.get(def.name);
|
|
388
365
|
if (prior !== void 0) throw new Error(`[arc] duplicate event-handler name "${def.name}" — declared by module "${prior}" and module "${m.name}". Named event handlers must be unique across the module graph; prefix names with the owning module.`);
|
|
389
366
|
owner.set(def.name, m.name);
|
|
390
367
|
}
|
|
391
|
-
const handler = applyBoundary(def, m.name, fastify);
|
|
392
368
|
const patterns = Array.isArray(def.event) ? def.event : [def.event];
|
|
369
|
+
if (def.durability === "retry" && def.boundary) throw new Error(`[arc] event handler "${def.name ?? patterns.join(",")}" in module "${m.name}" declares durability 'retry' and a boundary — a boundary swallows, so it is 'boundary'.`);
|
|
370
|
+
const label = handlerLabel(def, m.name, patterns, index);
|
|
371
|
+
const identity = def.name ?? [...patterns].sort().join(",");
|
|
372
|
+
if (!def.name && handlers.some((other) => other !== def && !other.name && (Array.isArray(other.event) ? [...other.event] : [other.event]).sort().join(",") === identity)) throw new Error(`[arc] module "${m.name}" has ambiguous unnamed event handlers for "${identity}" — give each handler a stable name.`);
|
|
373
|
+
const handler = withSubscriberId(`${m.name}:${identity}`, applyBoundary(def, m.name, label, fastify));
|
|
374
|
+
handlersByModule?.set(m.name, [...handlersByModule.get(m.name) ?? [], {
|
|
375
|
+
label,
|
|
376
|
+
patterns,
|
|
377
|
+
...def.durability !== void 0 ? { durability: def.durability } : {}
|
|
378
|
+
}]);
|
|
379
|
+
consumedByModule?.set(m.name, [...consumedByModule.get(m.name) ?? [], ...patterns]);
|
|
393
380
|
for (const pattern of patterns) unsubscribes.push(await bus.subscribe(pattern, handler));
|
|
394
381
|
}
|
|
395
382
|
}
|
|
@@ -401,18 +388,24 @@ async function subscribeModuleEventHandlers(fastify, modules, countsByModule) {
|
|
|
401
388
|
}
|
|
402
389
|
}
|
|
403
390
|
/**
|
|
404
|
-
*
|
|
405
|
-
*
|
|
406
|
-
*
|
|
407
|
-
|
|
408
|
-
|
|
409
|
-
|
|
410
|
-
|
|
411
|
-
|
|
391
|
+
* The name a subscriber's errors and logs carry: its declared `name`, else
|
|
392
|
+
* `<module>:<patterns>#<index in the module's handlers>` — unique, so two unnamed
|
|
393
|
+
* subscribers of one event stay distinguishable in a dead-lettered row.
|
|
394
|
+
*/
|
|
395
|
+
function handlerLabel(def, moduleName, patterns, index) {
|
|
396
|
+
return def.name ?? `${moduleName}:${patterns.join(",")}#${index}`;
|
|
397
|
+
}
|
|
398
|
+
/**
|
|
399
|
+
* Without a boundary, attribute the handler's errors to `label` and let them reach the transport —
|
|
400
|
+
* that is what makes the durable path work (unacked → redelivered → DLQ), and the label is what
|
|
401
|
+
* names the subscriber in the outbox's `lastError`. With the opt-in boundary, failures are logged
|
|
402
|
+
* under the handler's name (`<module>.<pattern>` when unnamed) and swallowed, so the original error
|
|
403
|
+
* reaches the boundary's `onError` untouched.
|
|
412
404
|
*/
|
|
413
|
-
function applyBoundary(def, moduleName, fastify) {
|
|
414
|
-
|
|
415
|
-
|
|
405
|
+
function applyBoundary(def, moduleName, label, fastify) {
|
|
406
|
+
const boundary = def.boundary ?? def.durability === "boundary";
|
|
407
|
+
if (!boundary) return wrapWithAttribution(def.handler, label);
|
|
408
|
+
const onError = typeof boundary === "object" ? boundary.onError : void 0;
|
|
416
409
|
return wrapWithBoundary(def.handler, {
|
|
417
410
|
logger: fastify.log,
|
|
418
411
|
name: def.name ?? `${moduleName}.${String(def.event)}`,
|
|
@@ -430,98 +423,6 @@ async function unsubscribeModuleEventHandlers(unsubscribes) {
|
|
|
430
423
|
return errors;
|
|
431
424
|
}
|
|
432
425
|
//#endregion
|
|
433
|
-
//#region src/factory/module/order.ts
|
|
434
|
-
/**
|
|
435
|
-
* Order modules for composition by their `dependsOn` edges — a STABLE
|
|
436
|
-
* topological sort. Called once at the start of the bootstrap phase; every
|
|
437
|
-
* subsequent phase (bootstrap, resources, afterResources, and reverse-order
|
|
438
|
-
* onClose) iterates the returned list, so a module's declared dependencies are
|
|
439
|
-
* always composed before it.
|
|
440
|
-
*
|
|
441
|
-
* "Stable" = modules with no edge between them keep their original list order,
|
|
442
|
-
* so declaring `dependsOn` on one module never silently reorders an unrelated
|
|
443
|
-
* one. Backward compatible: a `modules` array with NO `dependsOn` anywhere is
|
|
444
|
-
* returned unchanged.
|
|
445
|
-
*
|
|
446
|
-
* Fail-fast — throws (never reorders past a broken contract) on:
|
|
447
|
-
* - duplicate module names (the name is the graph key)
|
|
448
|
-
* - a `dependsOn` name not present in the composed set
|
|
449
|
-
* - a self-reference (`dependsOn` includes the module's own name)
|
|
450
|
-
* - a dependency cycle (reports the concrete `a → b → … → a` path)
|
|
451
|
-
*/
|
|
452
|
-
function orderModules(modules) {
|
|
453
|
-
const byName = /* @__PURE__ */ new Map();
|
|
454
|
-
for (const m of modules) {
|
|
455
|
-
if (byName.has(m.name)) throw new Error(`[arc] Duplicate module name "${m.name}" — composed twice; check your modules array.`);
|
|
456
|
-
byName.set(m.name, m);
|
|
457
|
-
}
|
|
458
|
-
for (const m of modules) for (const dep of m.dependsOn ?? []) {
|
|
459
|
-
if (dep === m.name) throw new Error(`[arc] module "${m.name}" dependsOn itself — remove the self-reference.`);
|
|
460
|
-
if (!byName.has(dep)) throw new Error(`[arc] module "${m.name}" dependsOn "${dep}", which is not composed. Add the "${dep}" module to createApp({ modules }) (before this one is fine — arc orders them), or drop the dependency. Composed modules: ${[...byName.keys()].join(", ") || "(none)"}.`);
|
|
461
|
-
}
|
|
462
|
-
if (modules.every((m) => !m.dependsOn || m.dependsOn.length === 0)) return [...modules];
|
|
463
|
-
const originalIndex = /* @__PURE__ */ new Map();
|
|
464
|
-
let nextIndex = 0;
|
|
465
|
-
for (const m of modules) originalIndex.set(m.name, nextIndex++);
|
|
466
|
-
const pendingDeps = new Map(modules.map((m) => [m.name, (m.dependsOn ?? []).length]));
|
|
467
|
-
const dependents = /* @__PURE__ */ new Map();
|
|
468
|
-
for (const m of modules) for (const dep of m.dependsOn ?? []) {
|
|
469
|
-
const list = dependents.get(dep);
|
|
470
|
-
if (list) list.push(m.name);
|
|
471
|
-
else dependents.set(dep, [m.name]);
|
|
472
|
-
}
|
|
473
|
-
const ordered = [];
|
|
474
|
-
const remaining = new Set(modules.map((m) => m.name));
|
|
475
|
-
while (remaining.size > 0) {
|
|
476
|
-
let pick;
|
|
477
|
-
let pickIndex = Number.POSITIVE_INFINITY;
|
|
478
|
-
for (const name of remaining) {
|
|
479
|
-
if (pendingDeps.get(name) !== 0) continue;
|
|
480
|
-
const index = originalIndex.get(name) ?? Number.POSITIVE_INFINITY;
|
|
481
|
-
if (index < pickIndex) {
|
|
482
|
-
pick = name;
|
|
483
|
-
pickIndex = index;
|
|
484
|
-
}
|
|
485
|
-
}
|
|
486
|
-
if (pick === void 0) throw new Error(describeModuleCycle(remaining, byName));
|
|
487
|
-
const picked = byName.get(pick);
|
|
488
|
-
if (!picked) throw new Error(`[arc] internal: ordered module "${pick}" is missing from the index`);
|
|
489
|
-
ordered.push(picked);
|
|
490
|
-
remaining.delete(pick);
|
|
491
|
-
for (const dependent of dependents.get(pick) ?? []) pendingDeps.set(dependent, (pendingDeps.get(dependent) ?? 1) - 1);
|
|
492
|
-
}
|
|
493
|
-
return ordered;
|
|
494
|
-
}
|
|
495
|
-
/** Walk the still-unordered subgraph for one concrete cycle path. */
|
|
496
|
-
function describeModuleCycle(remaining, byName) {
|
|
497
|
-
const stack = [];
|
|
498
|
-
const onStack = /* @__PURE__ */ new Set();
|
|
499
|
-
const done = /* @__PURE__ */ new Set();
|
|
500
|
-
let cycle = null;
|
|
501
|
-
const walk = (name) => {
|
|
502
|
-
if (cycle) return;
|
|
503
|
-
stack.push(name);
|
|
504
|
-
onStack.add(name);
|
|
505
|
-
for (const dep of byName.get(name)?.dependsOn ?? []) {
|
|
506
|
-
if (!remaining.has(dep)) continue;
|
|
507
|
-
if (onStack.has(dep)) {
|
|
508
|
-
cycle = [...stack.slice(stack.indexOf(dep)), dep];
|
|
509
|
-
return;
|
|
510
|
-
}
|
|
511
|
-
if (!done.has(dep)) walk(dep);
|
|
512
|
-
if (cycle) return;
|
|
513
|
-
}
|
|
514
|
-
onStack.delete(name);
|
|
515
|
-
stack.pop();
|
|
516
|
-
done.add(name);
|
|
517
|
-
};
|
|
518
|
-
for (const name of remaining) {
|
|
519
|
-
if (!done.has(name)) walk(name);
|
|
520
|
-
if (cycle) break;
|
|
521
|
-
}
|
|
522
|
-
return `[arc] module dependency cycle: ${cycle ? cycle.join(" → ") : [...remaining].join(", ")}. Modules cannot dependsOn each other circularly — break it with a shared module both point at, or wire the softer direction through an event/port instead of a hard dependsOn.`;
|
|
523
|
-
}
|
|
524
|
-
//#endregion
|
|
525
426
|
//#region src/factory/module/state.ts
|
|
526
427
|
/**
|
|
527
428
|
* The honest read of the state map. `fastify.arc` is optional (see
|
|
@@ -578,6 +479,28 @@ function getModuleState(fastify, name) {
|
|
|
578
479
|
const states = readStates(fastify);
|
|
579
480
|
return states !== void 0 && Object.hasOwn(states, name) ? states[name] : void 0;
|
|
580
481
|
}
|
|
482
|
+
/**
|
|
483
|
+
* The `ResourceDefinition`s module `name` mounted, in its declared order — for building that
|
|
484
|
+
* module's MCP tools or docs after boot. Throws when `name` is not a composed module (a typo would
|
|
485
|
+
* otherwise read as "no resources"), and when called before `registerResources` has published the
|
|
486
|
+
* descriptors. A resource declaring `skipRegistry` is absent, as from every introspection surface.
|
|
487
|
+
*/
|
|
488
|
+
function getModuleResources(fastify, name) {
|
|
489
|
+
const descriptors = fastify.arc?.moduleDescriptors;
|
|
490
|
+
const registry = fastify.arc?.registry;
|
|
491
|
+
if (!descriptors || !registry) throw new Error(`[arc] getModuleResources("${name}"): no composed module graph on this instance yet — call it after boot (afterResources, onReady, or after app.ready()).`);
|
|
492
|
+
const descriptor = descriptors.find((d) => d.name === name);
|
|
493
|
+
if (!descriptor) {
|
|
494
|
+
const known = descriptors.map((d) => d.name).join(", ") || "(none)";
|
|
495
|
+
throw new Error(`[arc] getModuleResources: "${name}" is not a composed module. Composed: ${known}.`);
|
|
496
|
+
}
|
|
497
|
+
const out = [];
|
|
498
|
+
for (const resource of descriptor.resources) {
|
|
499
|
+
const definition = resource.name === void 0 ? void 0 : registry.getDefinition(resource.name);
|
|
500
|
+
if (definition) out.push(definition);
|
|
501
|
+
}
|
|
502
|
+
return out;
|
|
503
|
+
}
|
|
581
504
|
//#endregion
|
|
582
505
|
//#region src/factory/module/teardown.ts
|
|
583
506
|
function createModuleTeardown(fastify, modules, disposers) {
|
|
@@ -851,18 +774,13 @@ async function registerArcCore(fastify, config, trackPlugin) {
|
|
|
851
774
|
await fastify.register(arcCorePlugin, { emitEvents: config.arcPlugins?.emitEvents !== false });
|
|
852
775
|
trackPlugin("arc-core");
|
|
853
776
|
if (config.arcPlugins?.events !== false) {
|
|
854
|
-
const { default: eventPlugin } = await import("./eventPlugin-
|
|
777
|
+
const { default: eventPlugin } = await import("./eventPlugin-BweHAVws.mjs").then((n) => n.r);
|
|
855
778
|
const eventOpts = typeof config.arcPlugins?.events === "object" ? config.arcPlugins.events : {};
|
|
856
779
|
await fastify.register(eventPlugin, {
|
|
857
780
|
/**
|
|
858
|
-
*
|
|
859
|
-
*
|
|
860
|
-
*
|
|
861
|
-
* once, no second knob to keep in sync. The comparison is against the
|
|
862
|
-
* literal, not the resolved default: `runtime` UNSET also means memory
|
|
863
|
-
* stores, but an unstated topology must keep the forgotten-default
|
|
864
|
-
* warn. `eventOpts` spreads after, so a direct
|
|
865
|
-
* `arcPlugins.events.singleProcess` still wins either way.
|
|
781
|
+
* Only an EXPLICIT `runtime: 'memory'` declares single-instance — an
|
|
782
|
+
* UNSET runtime must keep the forgotten-default warn. `eventOpts`
|
|
783
|
+
* spreads after, so `arcPlugins.events.singleProcess` still wins.
|
|
866
784
|
*/
|
|
867
785
|
...config.runtime === "memory" ? { singleProcess: true } : {},
|
|
868
786
|
...eventOpts,
|
|
@@ -911,15 +829,15 @@ async function registerArcPlugins(fastify, config, trackPlugin, modules, moduleH
|
|
|
911
829
|
trackPlugin("arc-graceful-shutdown", shutdownOpts);
|
|
912
830
|
}
|
|
913
831
|
if (config.arcPlugins?.caching) {
|
|
914
|
-
const { default: cachingPlugin } = await import("./caching-
|
|
832
|
+
const { default: cachingPlugin } = await import("./caching-Drp68DIS.mjs").then((n) => n.n);
|
|
915
833
|
const opts = config.arcPlugins.caching === true ? {} : config.arcPlugins.caching;
|
|
916
834
|
await fastify.register(cachingPlugin, opts);
|
|
917
835
|
trackPlugin("arc-caching", opts);
|
|
918
836
|
}
|
|
919
837
|
if (config.arcPlugins?.queryCache) {
|
|
920
|
-
const { queryCachePlugin } = await import("./queryCachePlugin-
|
|
838
|
+
const { queryCachePlugin } = await import("./queryCachePlugin-BATECpyh.mjs").then((n) => n.n);
|
|
921
839
|
const opts = config.arcPlugins.queryCache === true ? {} : config.arcPlugins.queryCache;
|
|
922
|
-
const store = config.stores?.queryCache ?? new (await (import("./memory-
|
|
840
|
+
const store = config.stores?.queryCache ?? new (await (import("./memory-CTa5pOCW.mjs").then((n) => n.n))).MemoryCacheStore();
|
|
923
841
|
await fastify.register(queryCachePlugin, {
|
|
924
842
|
store,
|
|
925
843
|
...opts
|
|
@@ -929,7 +847,7 @@ async function registerArcPlugins(fastify, config, trackPlugin, modules, moduleH
|
|
|
929
847
|
if (config.arcPlugins?.sse) {
|
|
930
848
|
if (config.arcPlugins?.events === false) fastify.log.warn("SSE plugin requires events plugin (arcPlugins.events). SSE disabled.");
|
|
931
849
|
else {
|
|
932
|
-
const { default: ssePlugin } = await import("./sse-
|
|
850
|
+
const { default: ssePlugin } = await import("./sse-BzJFPX_1.mjs").then((n) => n.n);
|
|
933
851
|
const opts = config.arcPlugins.sse === true ? {} : config.arcPlugins.sse;
|
|
934
852
|
await fastify.register(ssePlugin, opts);
|
|
935
853
|
trackPlugin("arc-sse", opts);
|
|
@@ -938,20 +856,20 @@ async function registerArcPlugins(fastify, config, trackPlugin, modules, moduleH
|
|
|
938
856
|
if (config.arcPlugins?.realtime) {
|
|
939
857
|
if (config.arcPlugins?.events === false) fastify.log.warn("Realtime plugin requires events plugin (arcPlugins.events). Realtime disabled.");
|
|
940
858
|
else {
|
|
941
|
-
const { default: realtimePlugin } = await import("./realtime-
|
|
859
|
+
const { default: realtimePlugin } = await import("./realtime-xyubMKSB.mjs").then((n) => n.n);
|
|
942
860
|
const opts = config.arcPlugins.realtime === true ? {} : config.arcPlugins.realtime;
|
|
943
861
|
await fastify.register(realtimePlugin, opts);
|
|
944
862
|
trackPlugin("arc-realtime", opts);
|
|
945
863
|
}
|
|
946
864
|
}
|
|
947
865
|
if (config.arcPlugins?.metrics) {
|
|
948
|
-
const { default: metricsPlugin } = await import("./metrics-
|
|
866
|
+
const { default: metricsPlugin } = await import("./metrics-DsZrvnpB.mjs").then((n) => n.n);
|
|
949
867
|
const opts = config.arcPlugins.metrics === true ? {} : config.arcPlugins.metrics;
|
|
950
868
|
await fastify.register(metricsPlugin, opts);
|
|
951
869
|
trackPlugin("arc-metrics", opts);
|
|
952
870
|
}
|
|
953
871
|
if (config.arcPlugins?.versioning) {
|
|
954
|
-
const { default: versioningPlugin } = await import("./versioning-
|
|
872
|
+
const { default: versioningPlugin } = await import("./versioning-CHhL9P3o.mjs").then((n) => n.n);
|
|
955
873
|
await fastify.register(versioningPlugin, config.arcPlugins.versioning);
|
|
956
874
|
trackPlugin("arc-versioning", config.arcPlugins.versioning);
|
|
957
875
|
}
|
|
@@ -1028,7 +946,7 @@ async function registerAuth(fastify, config, trackPlugin) {
|
|
|
1028
946
|
*/
|
|
1029
947
|
async function registerElevation(fastify, config, trackPlugin) {
|
|
1030
948
|
if (!config.elevation) return;
|
|
1031
|
-
const { elevationPlugin } = await import("./elevation-
|
|
949
|
+
const { elevationPlugin } = await import("./elevation-BG4gnMsJ.mjs").then((n) => n.n);
|
|
1032
950
|
await fastify.register(elevationPlugin, config.elevation);
|
|
1033
951
|
trackPlugin("arc-elevation", config.elevation);
|
|
1034
952
|
fastify.log.debug("Elevation plugin enabled");
|
|
@@ -1043,7 +961,7 @@ async function registerElevation(fastify, config, trackPlugin) {
|
|
|
1043
961
|
*/
|
|
1044
962
|
async function registerErrorHandler(fastify, config, trackPlugin, moduleErrorMappers) {
|
|
1045
963
|
if (config.errorHandler === false) return;
|
|
1046
|
-
const { errorHandlerPlugin } = await import("./errorHandler-
|
|
964
|
+
const { errorHandlerPlugin } = await import("./errorHandler-COHw3N1b.mjs").then((n) => n.r);
|
|
1047
965
|
/**
|
|
1048
966
|
* DISCLOSURE DEFAULTS MERGE — they used to be REPLACED, and that was two bugs.
|
|
1049
967
|
*
|
|
@@ -1128,6 +1046,7 @@ function createOptionalAuthenticate(authenticate) {
|
|
|
1128
1046
|
}
|
|
1129
1047
|
//#endregion
|
|
1130
1048
|
//#region src/factory/registerResources.ts
|
|
1049
|
+
/** Resource registration and the module/app lifecycle for createApp. */
|
|
1131
1050
|
function isResourcesFactory(value) {
|
|
1132
1051
|
return typeof value === "function";
|
|
1133
1052
|
}
|
|
@@ -1150,8 +1069,8 @@ async function registerOne(parent, resource, mountRoutes) {
|
|
|
1150
1069
|
* infra. Every phase below iterates that order; each module phase runs BEFORE
|
|
1151
1070
|
* its app-level counterpart:
|
|
1152
1071
|
*
|
|
1153
|
-
* 1.
|
|
1154
|
-
* deps, cycles
|
|
1072
|
+
* 1. composeModules — validate the graph (dup names, missing/self
|
|
1073
|
+
* deps, cycles, unmet `requires`) BEFORE side effects
|
|
1155
1074
|
* 2. plugins() — app infra (DB, data, webhooks)
|
|
1156
1075
|
* 3. module.plugins — module infra, dependsOn order, before bootstraps
|
|
1157
1076
|
* 4a. module.bootstrap — engine init (return value → arc.modules[name])
|
|
@@ -1170,33 +1089,27 @@ async function registerOne(parent, resource, mountRoutes) {
|
|
|
1170
1089
|
* THEN app onClose, in ONE hook so module
|
|
1171
1090
|
* teardown runs before shared infra closes.
|
|
1172
1091
|
*
|
|
1173
|
-
* Boot is TRANSACTIONAL for module-held resources:
|
|
1174
|
-
*
|
|
1175
|
-
*
|
|
1176
|
-
*
|
|
1177
|
-
* ORIGINAL boot error. Without this, a failure after module A's bootstrap but
|
|
1178
|
-
* before the §7b hook registration leaked A's clients/timers/engines with no
|
|
1179
|
-
* teardown path.
|
|
1180
|
-
*
|
|
1181
|
-
* "Entered", not "completed": a module is eligible for teardown from the
|
|
1182
|
-
* moment it enters `plugins()`/`bootstrap()`, so the callback that allocates a
|
|
1183
|
-
* client and THEN throws — the commonest partial-init shape — gets its closer
|
|
1184
|
-
* called too.
|
|
1092
|
+
* Boot is TRANSACTIONAL for module-held resources: on any failure in phases
|
|
1093
|
+
* 2–6, arc unsubscribes live module event handlers, closes every module that
|
|
1094
|
+
* ENTERED an init phase (see module/teardown.ts), and rethrows the ORIGINAL
|
|
1095
|
+
* boot error.
|
|
1185
1096
|
*/
|
|
1186
|
-
async function registerResources(fastify, config, preResolvedModules) {
|
|
1097
|
+
async function registerResources(fastify, config, preResolvedModules, skippedBridges = []) {
|
|
1098
|
+
if (config.strictResources === void 0) config = {
|
|
1099
|
+
...config,
|
|
1100
|
+
strictResources: true
|
|
1101
|
+
};
|
|
1187
1102
|
if (config.preset === "production") {
|
|
1188
|
-
if (config.strictResources === void 0) config = {
|
|
1189
|
-
...config,
|
|
1190
|
-
strictResources: true
|
|
1191
|
-
};
|
|
1192
1103
|
if (config.strictResourceDir === void 0) config = {
|
|
1193
1104
|
...config,
|
|
1194
1105
|
strictResourceDir: true
|
|
1195
1106
|
};
|
|
1196
1107
|
}
|
|
1197
|
-
|
|
1108
|
+
if (config.strictResources === false) fastify.arc?.registry?.tolerateDuplicateNames();
|
|
1109
|
+
const modules = preResolvedModules ?? (await composeModules(config.modules ?? [], config.ports)).modules;
|
|
1198
1110
|
initModuleStates(fastify, modules.map((m) => m.name));
|
|
1199
|
-
const
|
|
1111
|
+
const portsByModule = new Map(modules.map((m) => [m.name, portsForModule(m, config.ports)]));
|
|
1112
|
+
const disposers = createDisposerRegistry(fastify, (name) => portsByModule.get(name) ?? {});
|
|
1200
1113
|
const teardown = createModuleTeardown(fastify, modules, disposers);
|
|
1201
1114
|
let eventUnsubscribes = [];
|
|
1202
1115
|
try {
|
|
@@ -1267,7 +1180,7 @@ async function registerResources(fastify, config, preResolvedModules) {
|
|
|
1267
1180
|
let discoveryPath;
|
|
1268
1181
|
let discoveryYieldedZero = false;
|
|
1269
1182
|
if (resolvedResources === void 0 && config.resourceDir) {
|
|
1270
|
-
const { loadResources } = await import("./loadResources-
|
|
1183
|
+
const { loadResources } = await import("./loadResources-6jO52-RR.mjs").then((n) => n.n);
|
|
1271
1184
|
const { resolve, dirname } = await import("node:path");
|
|
1272
1185
|
const { fileURLToPath } = await import("node:url");
|
|
1273
1186
|
const rawDir = config.resourceDir;
|
|
@@ -1276,7 +1189,7 @@ async function registerResources(fastify, config, preResolvedModules) {
|
|
|
1276
1189
|
discoveryPath = dir;
|
|
1277
1190
|
const discovered = await loadResources(dir, { logger: fastify.log });
|
|
1278
1191
|
if (discovered.length === 0) {
|
|
1279
|
-
if (config.strictResourceDir) throw new Error(`[arc] loadResources: resourceDir "${rawDir}" resolved to "${dir}" but yielded 0 resources. Check the path, file naming (*.resource.{ts,js,mts,mjs}), and runtime layout (src/ vs dist/)
|
|
1192
|
+
if (config.strictResourceDir) throw new Error(`[arc] loadResources: resourceDir "${rawDir}" resolved to "${dir}" but yielded 0 resources. Check the path, file naming (*.resource.{ts,js,mts,mjs}), and runtime layout (src/ vs dist/).`);
|
|
1280
1193
|
discoveryYieldedZero = true;
|
|
1281
1194
|
}
|
|
1282
1195
|
resolvedResources = discovered;
|
|
@@ -1336,9 +1249,9 @@ Resources supplied by "${m.name}": ${provided}.`);
|
|
|
1336
1249
|
const seen = /* @__PURE__ */ new Set();
|
|
1337
1250
|
for (const resource of resolvedResources) if (resource.name) {
|
|
1338
1251
|
if (seen.has(resource.name)) {
|
|
1339
|
-
const msg = `Duplicate resource name "${resource.name}" detected. This will cause route conflicts. Check your resources array and loadResources() output. Common cause: stale compiled files in dist/ alongside src
|
|
1252
|
+
const msg = `Duplicate resource name "${resource.name}" detected. This will cause route conflicts. Check your resources array and loadResources() output. Common cause: stale compiled files in dist/ alongside src/.`;
|
|
1340
1253
|
if (config.strictResources) throw new Error(msg);
|
|
1341
|
-
fastify.log.warn(msg);
|
|
1254
|
+
fastify.log.warn(`${msg} Boot continues because strictResources is false.`);
|
|
1342
1255
|
}
|
|
1343
1256
|
seen.add(resource.name);
|
|
1344
1257
|
}
|
|
@@ -1348,10 +1261,12 @@ Resources supplied by "${m.name}": ${provided}.`);
|
|
|
1348
1261
|
else prefixed.push(resource);
|
|
1349
1262
|
for (const resource of root) await registerOne(fastify, resource, config.mountRoutes);
|
|
1350
1263
|
if (prefixed.length) {
|
|
1351
|
-
if (config.resourcePrefix)
|
|
1352
|
-
|
|
1353
|
-
|
|
1354
|
-
|
|
1264
|
+
if (config.resourcePrefix) {
|
|
1265
|
+
const registerPrefixed = async (scoped) => {
|
|
1266
|
+
for (const resource of prefixed) await registerOne(scoped, resource, config.mountRoutes);
|
|
1267
|
+
};
|
|
1268
|
+
await fastify.register(registerPrefixed, { prefix: config.resourcePrefix });
|
|
1269
|
+
} else for (const resource of prefixed) await registerOne(fastify, resource, config.mountRoutes);
|
|
1355
1270
|
}
|
|
1356
1271
|
const names = resolvedResources.map((r) => r.name ?? "?").join(", ");
|
|
1357
1272
|
const prefix = config.resourcePrefix ? ` (prefix: ${config.resourcePrefix})` : "";
|
|
@@ -1375,11 +1290,8 @@ Resources supplied by "${m.name}": ${provided}.`);
|
|
|
1375
1290
|
const scheduleCounts = /* @__PURE__ */ new Map();
|
|
1376
1291
|
const collectedJobs = await collectModuleScheduledJobs(fastify, modules, scheduleCounts);
|
|
1377
1292
|
/**
|
|
1378
|
-
* ROLE gate
|
|
1379
|
-
*
|
|
1380
|
-
* that a different deployment of the same codebase executes. 'relay' runs
|
|
1381
|
-
* only kind:'relay' arms (the outbox relays); 'api'/'worker' run none;
|
|
1382
|
-
* 'scheduler'/'all' run everything.
|
|
1293
|
+
* ROLE gate: arms this role does not run are skipped and logged, never
|
|
1294
|
+
* fatal — another deployment of the same codebase runs them.
|
|
1383
1295
|
*/
|
|
1384
1296
|
const role = config.role ?? "all";
|
|
1385
1297
|
const scheduledJobs = role === "scheduler" || role === "all" ? collectedJobs : role === "relay" ? collectedJobs.filter((j) => j.kind === "relay") : [];
|
|
@@ -1393,7 +1305,7 @@ Resources supplied by "${m.name}": ${provided}.`);
|
|
|
1393
1305
|
if (fastify.hasDecorator("getScheduleStats")) throw new Error("[arc] modules declare scheduledJobs but schedulesPlugin was already registered manually. Configure their runner through arcPlugins.schedules so Arc can compose one schedule table.");
|
|
1394
1306
|
const frozenJobs = Object.freeze(scheduledJobs.map((job) => Object.freeze({ ...job })));
|
|
1395
1307
|
const scheduleOptions = typeof config.arcPlugins?.schedules === "object" ? config.arcPlugins.schedules : {};
|
|
1396
|
-
const { default: schedulesPlugin } = await import("./schedules-
|
|
1308
|
+
const { default: schedulesPlugin } = await import("./schedules-CKTs5ys8.mjs").then((n) => n.n);
|
|
1397
1309
|
await fastify.register(schedulesPlugin, {
|
|
1398
1310
|
...scheduleOptions,
|
|
1399
1311
|
schedules: frozenJobs
|
|
@@ -1408,7 +1320,64 @@ Resources supplied by "${m.name}": ${provided}.`);
|
|
|
1408
1320
|
}
|
|
1409
1321
|
}
|
|
1410
1322
|
const subscriptionCounts = /* @__PURE__ */ new Map();
|
|
1411
|
-
|
|
1323
|
+
const consumedByModule = /* @__PURE__ */ new Map();
|
|
1324
|
+
const handlersByModule = /* @__PURE__ */ new Map();
|
|
1325
|
+
eventUnsubscribes = await subscribeModuleEventHandlers(fastify, modules, subscriptionCounts, consumedByModule, handlersByModule);
|
|
1326
|
+
const registry = fastify.arc?.registry;
|
|
1327
|
+
const emitsCrud = config.arcPlugins?.emitEvents !== false && registry !== void 0;
|
|
1328
|
+
const derived = /* @__PURE__ */ new Map();
|
|
1329
|
+
const moduleOwned = /* @__PURE__ */ new Set();
|
|
1330
|
+
if (emitsCrud) for (const [moduleName, names] of moduleResourceNames) {
|
|
1331
|
+
const publishes = [];
|
|
1332
|
+
for (const name of names) {
|
|
1333
|
+
moduleOwned.add(name);
|
|
1334
|
+
const entry = registry.get(name);
|
|
1335
|
+
if (entry) publishes.push(...crudEventsOf(entry));
|
|
1336
|
+
}
|
|
1337
|
+
derived.set(moduleName, {
|
|
1338
|
+
publishes,
|
|
1339
|
+
namespaces: [...names]
|
|
1340
|
+
});
|
|
1341
|
+
}
|
|
1342
|
+
const appCrudEvents = emitsCrud ? registry.getAll().filter((e) => !moduleOwned.has(e.name)).flatMap((e) => crudEventsOf(e)) : [];
|
|
1343
|
+
const eventRegistry = fastify.events?.registry;
|
|
1344
|
+
if (emitsCrud && eventRegistry) for (const entry of registry.getAll()) for (const name of crudEventsOf(entry)) {
|
|
1345
|
+
if (eventRegistry.get(name)) continue;
|
|
1346
|
+
eventRegistry.register(defineEvent({
|
|
1347
|
+
name,
|
|
1348
|
+
description: `arc CRUD event — a ${entry.name} was ${name.slice(entry.name.length + 1)}.`,
|
|
1349
|
+
schema: CRUD_EVENT_SCHEMA
|
|
1350
|
+
}));
|
|
1351
|
+
}
|
|
1352
|
+
const moduleGraph = buildModuleGraph(modules, consumedByModule, skippedBridges, [
|
|
1353
|
+
...config.moduleGraph?.externalEvents ?? [],
|
|
1354
|
+
...ARC_OWNED_EVENTS,
|
|
1355
|
+
...appCrudEvents
|
|
1356
|
+
], derived);
|
|
1357
|
+
for (const bridge of moduleGraph.skippedBridges) fastify.log.info({
|
|
1358
|
+
bridge: bridge.name,
|
|
1359
|
+
missing: bridge.missing
|
|
1360
|
+
}, "[arc] bridge module inactive — a module it joins is not composed");
|
|
1361
|
+
const graphMode = config.moduleGraph?.events ?? (config.preset === "production" ? "warn" : "off");
|
|
1362
|
+
if (graphMode !== "off") {
|
|
1363
|
+
const analysis = analyzeModuleGraph(moduleGraph);
|
|
1364
|
+
const issues = verifyModuleGraph(moduleGraph, analysis);
|
|
1365
|
+
if (issues.length > 0 && graphMode === "strict") throw new Error(`[arc] module graph has ${issues.length} issue(s):\n${issues.map((i) => ` - ${i.message}`).join("\n")}`);
|
|
1366
|
+
for (const issue of issues) fastify.log.warn({ issue }, `[arc] module graph: ${issue.message}`);
|
|
1367
|
+
const outboxModules = new Set(modules.filter((m) => isTransactionalOutbox(getOptionalModuleExports(fastify, m.name))).map((m) => m.name));
|
|
1368
|
+
const durabilityIssues = verifyEventDurability(modules, {
|
|
1369
|
+
outboxModules,
|
|
1370
|
+
handlers: handlersByModule
|
|
1371
|
+
});
|
|
1372
|
+
if (durabilityIssues.length && graphMode === "strict") throw new Error(`[arc] module graph durability has ${durabilityIssues.length} issue(s):\n${durabilityIssues.map((i) => ` - ${i.message}`).join("\n")}`);
|
|
1373
|
+
for (const issue of durabilityIssues) fastify.log.warn({ issue }, `[arc] module graph (${issue.code}): ${issue.message}`);
|
|
1374
|
+
}
|
|
1375
|
+
if (fastify.arc) fastify.arc.moduleGraph = moduleGraph;
|
|
1376
|
+
const events = fastify.events;
|
|
1377
|
+
if (graphMode !== "off" && typeof events?.observe === "function" && fastify.arc) {
|
|
1378
|
+
const undeclared = watchUndeclaredEvents(fastify, moduleGraph, graphMode);
|
|
1379
|
+
fastify.arc.undeclaredEvents = undeclared;
|
|
1380
|
+
}
|
|
1412
1381
|
if (fastify.arc) fastify.arc.moduleDescriptors = Object.freeze(modules.map((m) => describeResolvedModule({
|
|
1413
1382
|
name: m.name,
|
|
1414
1383
|
dependsOn: m.dependsOn,
|
|
@@ -1427,6 +1396,8 @@ Resources supplied by "${m.name}": ${provided}.`);
|
|
|
1427
1396
|
fastify.log.debug("afterResources hook executed");
|
|
1428
1397
|
}
|
|
1429
1398
|
} catch (err) {
|
|
1399
|
+
const shutdownErrors = await runShutdown(fastify);
|
|
1400
|
+
for (const shutdownError of shutdownErrors) fastify.log.error({ err: shutdownError }, "[arc] runtime rollback failed");
|
|
1430
1401
|
const unsubscribeErrors = await unsubscribeModuleEventHandlers(eventUnsubscribes);
|
|
1431
1402
|
for (const unsubscribeError of unsubscribeErrors) fastify.log.error({ err: unsubscribeError }, "[arc] module event-handler rollback after boot failure failed");
|
|
1432
1403
|
const closeErrors = await teardown.rollback();
|
|
@@ -1441,30 +1412,53 @@ Resources supplied by "${m.name}": ${provided}.`);
|
|
|
1441
1412
|
}
|
|
1442
1413
|
const appOnClose = config.onClose;
|
|
1443
1414
|
if (teardown.hasComposedModules() || appOnClose || eventUnsubscribes.length) fastify.addHook("onClose", async () => {
|
|
1415
|
+
const shutdownErrors = await runShutdown(fastify);
|
|
1444
1416
|
const unsubscribeErrors = await unsubscribeModuleEventHandlers(eventUnsubscribes);
|
|
1445
1417
|
for (const err of unsubscribeErrors) fastify.log.error({ err }, "[arc] module event-handler shutdown unsubscribe failed");
|
|
1446
1418
|
const closeErrors = await teardown.closeAll();
|
|
1447
1419
|
for (const err of closeErrors) fastify.log.error({ err }, "[arc] module onClose failed during shutdown");
|
|
1448
1420
|
if (appOnClose) await appOnClose(fastify);
|
|
1449
1421
|
if (closeErrors.length > 0) throw closeErrors[0];
|
|
1422
|
+
if (shutdownErrors.length > 0) throw shutdownErrors[0];
|
|
1450
1423
|
});
|
|
1451
1424
|
}
|
|
1452
1425
|
/**
|
|
1453
|
-
* `plugins()` is CALLED by arc, never
|
|
1454
|
-
*
|
|
1455
|
-
*
|
|
1456
|
-
* invokes their plugin at shutdown with no arguments.
|
|
1457
|
-
*
|
|
1458
|
-
* The `fastify-plugin` case is unambiguous (its marker symbols are only ever
|
|
1459
|
-
* set by `fp()`), so it throws. A bare `(fastify, opts)` function is a strong
|
|
1460
|
-
* but not certain signal — arc's disposer contract calls it with ZERO
|
|
1461
|
-
* arguments, so a 2-parameter disposer is meaningless either way — and warns.
|
|
1426
|
+
* `plugins()` is CALLED by arc, never `register()`ed — a returned Fastify
|
|
1427
|
+
* plugin would be silently treated as a disposer. `fp()` markers are
|
|
1428
|
+
* unambiguous, so that throws; a bare 2+-arg function only warns.
|
|
1462
1429
|
*/
|
|
1463
1430
|
function assertNotAReturnedPlugin(returned, moduleName, fastify) {
|
|
1464
1431
|
const fn = returned;
|
|
1465
1432
|
if (fn[Symbol.for("skip-override")] !== void 0 || fn[Symbol.for("plugin-meta")] !== void 0 || fn[Symbol.for("fastify.display-name")] !== void 0) throw new Error(`[arc] module "${moduleName}" returned a fastify-plugin from plugins(). That slot is a SETUP function arc calls directly — it is never passed to fastify.register(), so the returned value is read as a disposer and your plugin would be invoked at shutdown instead of registered at boot. Register it yourself: \`plugins: async (fastify) => { await fastify.register(myPlugin) }\`.`);
|
|
1466
1433
|
if (returned.length >= 2) fastify.log?.warn?.(`[arc] module "${moduleName}" returned a ${returned.length}-argument function from plugins(). Arc calls disposers with NO arguments, so this looks like a Fastify plugin that was meant to be registered (\`await fastify.register(fn)\`) rather than a teardown callback. If it really is a disposer, declare it with no parameters.`);
|
|
1467
1434
|
}
|
|
1435
|
+
/** Namespaces arc itself publishes — lifecycle (`arc.*`) and its streamline integration (`workflow.*`). */
|
|
1436
|
+
const ARC_OWNED_EVENTS = ["arc.*", "workflow.*"];
|
|
1437
|
+
/**
|
|
1438
|
+
* Record every published event type the graph has no owner for. Logs each name once; never
|
|
1439
|
+
* touches delivery.
|
|
1440
|
+
*/
|
|
1441
|
+
function watchUndeclaredEvents(fastify, graph, mode) {
|
|
1442
|
+
const owned = new Set(graph.modules.flatMap((m) => m.publishes));
|
|
1443
|
+
const external = createEventRouter();
|
|
1444
|
+
for (const pattern of graph.externalEvents) external.add(pattern, true);
|
|
1445
|
+
const undeclared = /* @__PURE__ */ new Set();
|
|
1446
|
+
const stop = fastify.events.observe((event) => {
|
|
1447
|
+
const type = event.type;
|
|
1448
|
+
if (owned.has(type) || undeclared.has(type) || type.startsWith("$")) return;
|
|
1449
|
+
if (external.route(type).length > 0) return;
|
|
1450
|
+
undeclared.add(type);
|
|
1451
|
+
fastify.log[mode === "strict" ? "error" : "warn"]({ event: type }, `[arc] module graph: "${type}" was published but no module declares it — add it to its owner's \`publishes\` (or \`moduleGraph.externalEvents\`).`);
|
|
1452
|
+
});
|
|
1453
|
+
fastify.addHook("onClose", async () => stop());
|
|
1454
|
+
return undeclared;
|
|
1455
|
+
}
|
|
1456
|
+
/** A module export that is an outbox whose `save` enlists the caller's session. */
|
|
1457
|
+
function isTransactionalOutbox(exports) {
|
|
1458
|
+
if (typeof exports !== "object" || exports === null || !("store" in exports)) return false;
|
|
1459
|
+
const store = exports.store;
|
|
1460
|
+
return typeof store === "object" && store !== null && "transactionalSave" in store && store.transactionalSave === true;
|
|
1461
|
+
}
|
|
1468
1462
|
//#endregion
|
|
1469
1463
|
//#region src/factory/security/cors.ts
|
|
1470
1464
|
/**
|
|
@@ -1650,12 +1644,17 @@ async function registerSecurityPlugins(fastify, config) {
|
|
|
1650
1644
|
});
|
|
1651
1645
|
await fastify.register(rateLimit, rateLimitOpts);
|
|
1652
1646
|
const rateLimitRecord = typeof rateLimitOpts === "object" && rateLimitOpts !== null ? rateLimitOpts : void 0;
|
|
1653
|
-
|
|
1647
|
+
const hasStore = rateLimitRecord !== void 0 && (rateLimitRecord.store != null || rateLimitRecord.redis != null);
|
|
1648
|
+
resolveResourceRateLimiter(fastify, hasStore ? "shared" : "memory");
|
|
1649
|
+
if (!hasStore) {
|
|
1654
1650
|
if (config.runtime === "distributed") throw new Error("[Arc] runtime: 'distributed' with rate limiting requires a shared store.\nProvide rateLimit: { redis: ioredisClient } (or { store: CustomStore }) or disable rate limiting: rateLimit: false");
|
|
1655
1651
|
else if (config.preset === "production") fastify.log.warn("Rate limiting is using in-memory store. In multi-instance deployments, each instance tracks limits independently. Configure a Redis store for distributed rate limiting.");
|
|
1656
1652
|
}
|
|
1657
1653
|
fastify.log.debug("Rate limiting enabled");
|
|
1658
|
-
} else
|
|
1654
|
+
} else {
|
|
1655
|
+
fastify.log.warn("Rate limiting disabled");
|
|
1656
|
+
markRateLimitingDisabled(fastify);
|
|
1657
|
+
}
|
|
1659
1658
|
}
|
|
1660
1659
|
/**
|
|
1661
1660
|
* Register performance and utility plugins (Under Pressure, Sensible, Multipart, Raw Body).
|
|
@@ -1721,40 +1720,18 @@ async function registerUtilityPlugins(fastify, config) {
|
|
|
1721
1720
|
//#endregion
|
|
1722
1721
|
//#region src/factory/createApp.ts
|
|
1723
1722
|
/**
|
|
1724
|
-
* ArcFactory - Production-ready Fastify application factory
|
|
1725
|
-
*
|
|
1726
|
-
* Enforces security best practices by making plugins opt-out instead of opt-in.
|
|
1727
|
-
* A developer must explicitly disable security features rather than forget to enable them.
|
|
1723
|
+
* ArcFactory - Production-ready Fastify application factory.
|
|
1728
1724
|
*
|
|
1729
|
-
*
|
|
1730
|
-
*
|
|
1731
|
-
* connection pooling, and full control over your data layer.
|
|
1725
|
+
* Security plugins are opt-out, not opt-in. Arc is database-agnostic: connect
|
|
1726
|
+
* your database separately and pass adapters to `defineResource`.
|
|
1732
1727
|
*
|
|
1733
1728
|
* @example
|
|
1734
|
-
* // 1. Connect your database(s) separately
|
|
1735
|
-
* import mongoose from 'mongoose';
|
|
1736
|
-
* await mongoose.connect(process.env.MONGO_URI);
|
|
1737
|
-
*
|
|
1738
|
-
* // 2. Create Arc app with resources
|
|
1739
1729
|
* const app = await createApp({
|
|
1740
1730
|
* preset: 'production',
|
|
1731
|
+
* runtime: 'memory',
|
|
1741
1732
|
* auth: { type: 'jwt', jwt: { secret: process.env.JWT_SECRET } },
|
|
1742
|
-
* cors: { origin: ['https://example.com'] },
|
|
1743
1733
|
* resources: [productResource, orderResource],
|
|
1744
1734
|
* });
|
|
1745
|
-
*
|
|
1746
|
-
* @example
|
|
1747
|
-
* // Multiple databases example
|
|
1748
|
-
* const primaryDb = await mongoose.connect(process.env.PRIMARY_DB);
|
|
1749
|
-
* const analyticsDb = mongoose.createConnection(process.env.ANALYTICS_DB);
|
|
1750
|
-
*
|
|
1751
|
-
* const orderResource = defineResource({
|
|
1752
|
-
* adapter: createMongooseAdapter({ model: OrderModel, repository: orderRepo }),
|
|
1753
|
-
* });
|
|
1754
|
-
*
|
|
1755
|
-
* const analyticsResource = defineResource({
|
|
1756
|
-
* adapter: createMongooseAdapter({ model: AnalyticsModel, repository: analyticsRepo }),
|
|
1757
|
-
* });
|
|
1758
1735
|
*/
|
|
1759
1736
|
var createApp_exports = /* @__PURE__ */ __exportAll({
|
|
1760
1737
|
ArcFactory: () => ArcFactory,
|
|
@@ -1763,10 +1740,8 @@ var createApp_exports = /* @__PURE__ */ __exportAll({
|
|
|
1763
1740
|
resolveLoggerConfig: () => resolveLoggerConfig
|
|
1764
1741
|
});
|
|
1765
1742
|
/**
|
|
1766
|
-
* The fail-loud switches
|
|
1767
|
-
*
|
|
1768
|
-
* Every one is a SPECIFIC instruction defeated by a GENERAL default, which is
|
|
1769
|
-
* why they are reported together rather than one at a time.
|
|
1743
|
+
* The fail-loud switches — ON by default — and what each one costs when a deployment turns it OFF.
|
|
1744
|
+
* Reported together at production boot, so an opt-out is never silent.
|
|
1770
1745
|
*/
|
|
1771
1746
|
const STRICT_ENV_FLAGS = [
|
|
1772
1747
|
["ARC_STRICT_PERMISSIONS", "An ungated write route warns instead of failing boot."],
|
|
@@ -1775,18 +1750,10 @@ const STRICT_ENV_FLAGS = [
|
|
|
1775
1750
|
];
|
|
1776
1751
|
const MEMORY_STORE_NAMES = /* @__PURE__ */ new Set(["memory", "memory-cache"]);
|
|
1777
1752
|
/**
|
|
1778
|
-
* Default redact paths
|
|
1779
|
-
*
|
|
1780
|
-
*
|
|
1781
|
-
*
|
|
1782
|
-
* outgoing logs may carry either), API-key headers, and any nested
|
|
1783
|
-
* `password` / `token` / `secret` / `apiKey` fields anywhere in the log
|
|
1784
|
-
* tree.
|
|
1785
|
-
*
|
|
1786
|
-
* Hosts that need NO redaction (test fixtures, security audits) opt out
|
|
1787
|
-
* with `logger: { redact: [] }`. Hosts that need MORE redaction supply
|
|
1788
|
-
* their own `redact` and arc steps aside — this default never overrides
|
|
1789
|
-
* an explicit setting.
|
|
1753
|
+
* Default pino redact paths (auth/cookie/API-key headers, nested
|
|
1754
|
+
* password/token/secret fields), applied only when the host supplies no
|
|
1755
|
+
* `logger.redact`. Opt out with `logger: { redact: [] }`; an explicit
|
|
1756
|
+
* `redact` always wins.
|
|
1790
1757
|
*/
|
|
1791
1758
|
const DEFAULT_LOGGER_REDACT_PATHS = [
|
|
1792
1759
|
"req.headers.authorization",
|
|
@@ -1805,16 +1772,8 @@ const DEFAULT_LOGGER_REDACT_PATHS = [
|
|
|
1805
1772
|
"*.apiKey"
|
|
1806
1773
|
];
|
|
1807
1774
|
/**
|
|
1808
|
-
* Resolve the host's `logger` option, layering
|
|
1809
|
-
*
|
|
1810
|
-
* for its `logger` server option.
|
|
1811
|
-
*
|
|
1812
|
-
* Three branches:
|
|
1813
|
-
* - `logger === false` → pass through (no logger).
|
|
1814
|
-
* - `logger === undefined` → enable with default redact paths.
|
|
1815
|
-
* - `logger === true` → enable with default redact paths.
|
|
1816
|
-
* - `logger` is an object → respect explicit `redact` (host wins);
|
|
1817
|
-
* otherwise inject defaults.
|
|
1775
|
+
* Resolve the host's `logger` option for Fastify, layering the default redact
|
|
1776
|
+
* paths unless it is `false` or an object with its own `redact`.
|
|
1818
1777
|
*/
|
|
1819
1778
|
function resolveLoggerConfig(logger) {
|
|
1820
1779
|
if (logger === false) return false;
|
|
@@ -1900,28 +1859,31 @@ Missing:\n${lines}\n\nFix: pass durable adapters under \`stores\`, or use runtim
|
|
|
1900
1859
|
*/
|
|
1901
1860
|
async function createApp(options) {
|
|
1902
1861
|
/**
|
|
1903
|
-
*
|
|
1904
|
-
*
|
|
1905
|
-
|
|
1906
|
-
|
|
1862
|
+
* TOPOLOGY is declared, never guessed: a second replica on the in-memory runtime silently
|
|
1863
|
+
* loses cross-instance events and caches.
|
|
1864
|
+
*/
|
|
1865
|
+
if (options.preset === "production" && options.runtime === void 0) throw new Error("createApp: preset 'production' requires an explicit `runtime` — 'memory' for a single instance, 'distributed' before running a second replica (it requires shared stores and is audited at boot).");
|
|
1866
|
+
/**
|
|
1867
|
+
* A relay/scheduler process declaring HTTP resources is a wiring error —
|
|
1868
|
+
* mounting routes anyway would hide the misdeployment behind a working endpoint.
|
|
1907
1869
|
*/
|
|
1908
1870
|
const declaredRole = options.role ?? "all";
|
|
1909
1871
|
if ((declaredRole === "relay" || declaredRole === "scheduler") && (options.resources !== void 0 || options.resourceDir !== void 0)) throw new Error(`createApp: role '${declaredRole}' declares HTTP resources — a ${declaredRole} process must not serve routes. Remove \`resources\`/\`resourceDir\`, or change the role.`);
|
|
1910
1872
|
/**
|
|
1911
|
-
* This app's own `arcLog` config, entered for the WHOLE boot.
|
|
1912
|
-
*
|
|
1913
|
-
*
|
|
1914
|
-
*
|
|
1915
|
-
* post-construction boot warning to the console instead of this app's
|
|
1916
|
-
* logger. Scoping it per app is what stops two apps in one process from
|
|
1917
|
-
* overwriting each other's writer — see `src/logger/index.ts`.
|
|
1873
|
+
* This app's own `arcLog` config, entered for the WHOLE boot. Held by
|
|
1874
|
+
* reference and mutated in `buildApp` once `fastify.log` exists — a copy
|
|
1875
|
+
* would send post-construction boot warnings to the console. Per-app so two
|
|
1876
|
+
* apps in one process keep separate writers.
|
|
1918
1877
|
*/
|
|
1919
1878
|
const loggerScope = {};
|
|
1920
1879
|
if (options.debug !== void 0 && options.debug !== false) loggerScope.debug = options.debug;
|
|
1921
1880
|
let partial;
|
|
1881
|
+
let disposeBoot;
|
|
1922
1882
|
try {
|
|
1923
1883
|
return await runWithArcLogger(loggerScope, () => buildApp(options, loggerScope, (instance) => {
|
|
1924
1884
|
partial = instance;
|
|
1885
|
+
}, (dispose) => {
|
|
1886
|
+
disposeBoot = dispose;
|
|
1925
1887
|
}));
|
|
1926
1888
|
} catch (err) {
|
|
1927
1889
|
if (partial) try {
|
|
@@ -1929,38 +1891,42 @@ async function createApp(options) {
|
|
|
1929
1891
|
} catch (closeErr) {
|
|
1930
1892
|
partial.log?.error?.({ err: closeErr }, "[arc] cleanup of a partially-booted app failed; original boot error follows");
|
|
1931
1893
|
}
|
|
1894
|
+
if (disposeBoot) try {
|
|
1895
|
+
await disposeBoot();
|
|
1896
|
+
} catch (disposeErr) {
|
|
1897
|
+
arcLog("createApp").error("beforeBoot disposer failed during failed-boot cleanup; original boot error follows", disposeErr);
|
|
1898
|
+
}
|
|
1932
1899
|
throw err;
|
|
1933
1900
|
}
|
|
1934
1901
|
}
|
|
1935
|
-
async function buildApp(options, loggerScope, onInstance) {
|
|
1902
|
+
async function buildApp(options, loggerScope, onInstance, onBootDisposer) {
|
|
1936
1903
|
validateAuthOptions(options);
|
|
1937
1904
|
const deferredWarnings = validateDistributedRuntime(options);
|
|
1938
|
-
|
|
1905
|
+
let disposeBoot;
|
|
1906
|
+
if (options.beforeBoot) {
|
|
1907
|
+
const dispose = await options.beforeBoot();
|
|
1908
|
+
if (typeof dispose === "function") {
|
|
1909
|
+
let once;
|
|
1910
|
+
disposeBoot = () => {
|
|
1911
|
+
once ??= Promise.resolve().then(dispose);
|
|
1912
|
+
return once;
|
|
1913
|
+
};
|
|
1914
|
+
onBootDisposer(disposeBoot);
|
|
1915
|
+
}
|
|
1916
|
+
}
|
|
1939
1917
|
const config = {
|
|
1940
1918
|
...options.preset ? getPreset(options.preset) : {},
|
|
1941
|
-
...options
|
|
1919
|
+
...options,
|
|
1920
|
+
...options.role === "relay" || options.role === "scheduler" ? { mountRoutes: false } : {}
|
|
1942
1921
|
};
|
|
1943
1922
|
const inheritedTrustProxyDefault = options.preset === "production" && options.trustProxy === void 0;
|
|
1944
1923
|
/**
|
|
1945
|
-
* A NUMERIC `trustProxy`
|
|
1946
|
-
*
|
|
1947
|
-
*
|
|
1948
|
-
* `lib/request.js` is explicit: "Hop-count-only trust cannot validate the
|
|
1949
|
-
* immediate peer. Fail closed so direct clients cannot spoof X-Forwarded-*
|
|
1950
|
-
* values by supplying enough hops." So `trustProxy: 1` now trusts NOTHING.
|
|
1951
|
-
*
|
|
1952
|
-
* That is the right call upstream and the wrong thing to inherit silently:
|
|
1953
|
-
* `request.ip` becomes the proxy's address, which is the exact failure the
|
|
1954
|
-
* production-preset warning below describes — rate-limit keys collapse onto
|
|
1955
|
-
* one bucket and audit rows record the wrong client. The host asked for
|
|
1956
|
-
* proxy trust and got none, with no error.
|
|
1957
|
-
*
|
|
1958
|
-
* Warn rather than refuse: the app still serves, and a hard boot failure on
|
|
1959
|
-
* a transitive `fastify` bump would be a harsher surprise than the one being
|
|
1960
|
-
* reported. The replacement is named so the fix is a copy-paste.
|
|
1924
|
+
* A NUMERIC `trustProxy` trusts NOTHING on Fastify >= 5.12.1 (hop-count
|
|
1925
|
+
* trust fails closed upstream), so the host silently gets the proxy's IP.
|
|
1926
|
+
* Warn rather than refuse — a boot failure on a transitive bump is harsher.
|
|
1961
1927
|
*/
|
|
1962
1928
|
const numericTrustProxy = typeof config.trustProxy === "number";
|
|
1963
|
-
const resolvedModules =
|
|
1929
|
+
const { modules: resolvedModules, skipped: skippedBridges } = await composeModules(config.modules ?? [], config.ports);
|
|
1964
1930
|
const moduleErrorMappers = resolvedModules.flatMap((m) => m.errorMappers ?? []);
|
|
1965
1931
|
const moduleHealthChecks = collectModuleHealthChecks(resolvedModules);
|
|
1966
1932
|
const resolvedLogger = resolveLoggerConfig(config.logger);
|
|
@@ -1983,16 +1949,13 @@ async function buildApp(options, loggerScope, onInstance) {
|
|
|
1983
1949
|
}),
|
|
1984
1950
|
maxParamLength: config.maxParamLength ?? 400
|
|
1985
1951
|
},
|
|
1986
|
-
ajv: { customOptions:
|
|
1987
|
-
coerceTypes: true,
|
|
1988
|
-
useDefaults: true,
|
|
1989
|
-
removeAdditional: false,
|
|
1990
|
-
strictTypes: false,
|
|
1991
|
-
keywords: ["example", ...config.ajv?.keywords ?? []]
|
|
1992
|
-
} }
|
|
1952
|
+
ajv: { customOptions: arcAjvCustomOptions(config.ajv?.keywords) }
|
|
1993
1953
|
});
|
|
1954
|
+
markArcRootValidator(fastify);
|
|
1994
1955
|
onInstance(fastify);
|
|
1956
|
+
if (disposeBoot) fastify.addHook("onClose", disposeBoot);
|
|
1995
1957
|
initRuntimeCapabilityRegistry(fastify);
|
|
1958
|
+
initShutdownRegistry(fastify);
|
|
1996
1959
|
if (resolvedLogger !== false) {
|
|
1997
1960
|
const writer = createPinoWriter(fastify.log);
|
|
1998
1961
|
loggerScope.writer = writer;
|
|
@@ -2006,22 +1969,15 @@ async function buildApp(options, loggerScope, onInstance) {
|
|
|
2006
1969
|
});
|
|
2007
1970
|
for (const warning of deferredWarnings) fastify.log.warn(warning);
|
|
2008
1971
|
if (numericTrustProxy) fastify.log.warn(`[arc] trustProxy: ${String(config.trustProxy)} (a hop COUNT) trusts nothing on Fastify >= 5.12.1 — hop-count trust cannot validate the immediate peer, so it fails closed upstream to stop clients spoofing X-Forwarded-* with padded hops. \`request.ip\` is therefore the PROXY's address: rate-limit keys collapse onto one bucket and audit rows record the wrong client. Name the proxies instead — trustProxy: 'loopback', '10.0.0.0/8', a comma-separated list, or \`true\` if every upstream hop is genuinely trusted.`);
|
|
2009
|
-
if (inheritedTrustProxyDefault) fastify.log.warn("[arc] preset: 'production'
|
|
1972
|
+
if (inheritedTrustProxyDefault) fastify.log.warn("[arc] preset: 'production' defaults to trustProxy: false (fail-closed). If this app runs behind a load balancer or reverse proxy, request.ip is currently the PROXY's address — rate-limit keys collapse onto one bucket and audit logs record the wrong client. Set trustProxy explicitly: the proxies' addresses ('loopback', '10.0.0.0/8', a comma-separated list), a resolver function, or false if clients truly connect directly. A hop count trusts nothing on Fastify >= 5.12.1.");
|
|
2010
1973
|
/**
|
|
2011
|
-
*
|
|
2012
|
-
*
|
|
2013
|
-
* All three are read where the object is CONSTRUCTED — `defineResource` at
|
|
2014
|
-
* define time, `QueryParser` and `BodySanitizer` in their constructors — and a
|
|
2015
|
-
* host imports its resources before it calls `createApp`. So setting
|
|
2016
|
-
* `process.env` here would be too late for everything already built: wired to
|
|
2017
|
-
* read, doing nothing. Reporting cannot fail that way.
|
|
2018
|
-
*
|
|
2019
|
-
* Set them in the process entrypoint, BEFORE the first resource import, with
|
|
2020
|
-
* `??=` so an operator's explicit `false` still wins.
|
|
1974
|
+
* Production names every fail-loud switch turned OFF. They are read before `createApp`, so this
|
|
1975
|
+
* reports rather than sets.
|
|
2021
1976
|
*/
|
|
2022
1977
|
if (config.preset === "production") {
|
|
2023
|
-
const
|
|
2024
|
-
|
|
1978
|
+
const env = readEnv(process.env);
|
|
1979
|
+
const off = STRICT_ENV_FLAGS.filter(([name]) => !env.bool(name, true));
|
|
1980
|
+
if (off.length > 0) fastify.log.warn(`[arc] preset: 'production' with fail-loud turned OFF: ${off.map(([n]) => n).join(", ")}. ${off.map(([, what]) => what).join(" ")} They default ON; unset them to restore enforcement. Per-resource options still override.`);
|
|
2025
1981
|
}
|
|
2026
1982
|
fastify.removeContentTypeParser("application/json");
|
|
2027
1983
|
fastify.addContentTypeParser("application/json", { parseAs: "string" }, (_req, body, done) => {
|
|
@@ -2053,15 +2009,14 @@ async function buildApp(options, loggerScope, onInstance) {
|
|
|
2053
2009
|
await registerAuth(fastify, config, trackPlugin);
|
|
2054
2010
|
await registerElevation(fastify, config, trackPlugin);
|
|
2055
2011
|
await registerErrorHandler(fastify, config, trackPlugin, moduleErrorMappers);
|
|
2056
|
-
await registerResources(fastify, config, resolvedModules);
|
|
2012
|
+
await registerResources(fastify, config, resolvedModules, skippedBridges);
|
|
2057
2013
|
if (config.replyHelpers) {
|
|
2058
|
-
const { replyHelpersPlugin } = await import("./replyHelpers-
|
|
2014
|
+
const { replyHelpersPlugin } = await import("./replyHelpers-Bnc6lJyy.mjs").then((n) => n.n);
|
|
2059
2015
|
await fastify.register(replyHelpersPlugin);
|
|
2060
2016
|
}
|
|
2061
2017
|
if (config.serializeBigInt) {
|
|
2062
|
-
const mode = config.serializeBigInt
|
|
2063
|
-
if (
|
|
2064
|
-
else if (mode === "number") arcLog("createApp").warn("serializeBigInt: 'number' loses precision above Number.MAX_SAFE_INTEGER (9007199254740991). Use 'string' instead unless you've audited that no bigint payload field can exceed the safe range.");
|
|
2018
|
+
const mode = config.serializeBigInt;
|
|
2019
|
+
if (mode === "number") arcLog("createApp").warn("serializeBigInt: 'number' loses precision above Number.MAX_SAFE_INTEGER (9007199254740991). Use 'string' instead unless you've audited that no bigint payload field can exceed the safe range.");
|
|
2065
2020
|
fastify.addHook("preSerialization", async (_request, _reply, payload) => {
|
|
2066
2021
|
if (payload === null || payload === void 0) return payload;
|
|
2067
2022
|
try {
|
|
@@ -2107,48 +2062,4 @@ const ArcFactory = {
|
|
|
2107
2062
|
}
|
|
2108
2063
|
};
|
|
2109
2064
|
//#endregion
|
|
2110
|
-
|
|
2111
|
-
/**
|
|
2112
|
-
* Normalize an eager `import.meta.glob` result into a `ResourceLike[]`.
|
|
2113
|
-
*
|
|
2114
|
-
* Accepts either:
|
|
2115
|
-
* - `{ import: 'default' }` form: values are the resource directly
|
|
2116
|
-
* - default form: values are the full module — picks first export with `toPlugin()`
|
|
2117
|
-
*
|
|
2118
|
-
* Throws if any module doesn't yield a valid `ResourceLike`.
|
|
2119
|
-
*/
|
|
2120
|
-
function preloadResources(globResult) {
|
|
2121
|
-
const resources = [];
|
|
2122
|
-
for (const [path, value] of Object.entries(globResult)) {
|
|
2123
|
-
const resource = pickResource(value);
|
|
2124
|
-
if (!resource) throw new Error(`preloadResources: ${path} does not export a valid resource.\n Expected: a default export OR a named export with toPlugin().`);
|
|
2125
|
-
resources.push(resource);
|
|
2126
|
-
}
|
|
2127
|
-
return resources.sort((a, b) => (a.name ?? "").localeCompare(b.name ?? ""));
|
|
2128
|
-
}
|
|
2129
|
-
/**
|
|
2130
|
-
* Normalize a lazy `import.meta.glob` result into a `Promise<ResourceLike[]>`.
|
|
2131
|
-
*
|
|
2132
|
-
* Use this when resources depend on prior bootstrap (e.g., engine init) and
|
|
2133
|
-
* cannot be evaluated at import time of the preload file.
|
|
2134
|
-
*/
|
|
2135
|
-
async function preloadResourcesAsync(globResult) {
|
|
2136
|
-
return (await Promise.all(Object.entries(globResult).map(async ([path, loader]) => {
|
|
2137
|
-
const resource = pickResource(await loader());
|
|
2138
|
-
if (!resource) throw new Error(`preloadResourcesAsync: ${path} does not export a valid resource.\n Expected: a default export OR a named export with toPlugin().`);
|
|
2139
|
-
return resource;
|
|
2140
|
-
}))).sort((a, b) => (a.name ?? "").localeCompare(b.name ?? ""));
|
|
2141
|
-
}
|
|
2142
|
-
function pickResource(value) {
|
|
2143
|
-
if (!value || typeof value !== "object") return void 0;
|
|
2144
|
-
if (typeof value.toPlugin === "function") return value;
|
|
2145
|
-
const mod = value;
|
|
2146
|
-
const candidates = [
|
|
2147
|
-
mod.default,
|
|
2148
|
-
mod.resource,
|
|
2149
|
-
...Object.values(mod)
|
|
2150
|
-
];
|
|
2151
|
-
for (const c of candidates) if (c && typeof c === "object" && typeof c.toPlugin === "function") return c;
|
|
2152
|
-
}
|
|
2153
|
-
//#endregion
|
|
2154
|
-
export { resolveModule as S, describeResolvedModule as _, createApp as a, collectModuleWorkflows as b, edgePreset as c, testingPreset as d, workerPreset as f, subscribeModuleEventHandlers as g, orderModules as h, DEFAULT_LOGGER_REDACT_PATHS as i, getPreset as l, hasModule as m, preloadResourcesAsync as n, createApp_exports as o, getModuleState as p, ArcFactory as r, developmentPreset as s, preloadResources as t, productionPreset as u, collectModuleHealthChecks as v, resolveContribution as x, collectModuleScheduledJobs as y };
|
|
2065
|
+
export { developmentPreset as a, productionPreset as c, getModuleResources as d, getModuleState as f, collectModuleWorkflows as g, asModulePorts as h, createApp_exports as i, testingPreset as l, subscribeModuleEventHandlers as m, DEFAULT_LOGGER_REDACT_PATHS as n, edgePreset as o, hasModule as p, createApp as r, getPreset as s, ArcFactory as t, workerPreset as u };
|