@classytic/arc 2.23.0 → 2.28.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/README.md +2 -0
- package/dist/{BaseController-BQ8KNvWC.mjs → BaseController-B65zbz9m.mjs} +94 -23
- package/dist/QueryCache-DOMT_iq7.d.mts +72 -0
- package/dist/actionPermissions-CAphcmUG.mjs +24 -0
- package/dist/{applyPermissionResult-Tw4iXtlf.mjs → applyPermissionResult-BnyVE9eD.mjs} +10 -0
- package/dist/audit/index.d.mts +32 -3
- package/dist/audit/index.mjs +7 -4
- package/dist/auth/index.d.mts +2 -2
- package/dist/auth/index.mjs +263 -259
- package/dist/{buildHandler-D_2ULWxZ.mjs → buildHandler-Dtwd8fdz.mjs} +1 -1
- package/dist/cache/index.d.mts +41 -3
- package/dist/cache/index.mjs +33 -4
- package/dist/cleanup/index.d.mts +313 -0
- package/dist/cleanup/index.mjs +1267 -0
- package/dist/cli/commands/describe.d.mts +1 -1
- package/dist/cli/commands/docs.mjs +1 -1
- package/dist/cli/commands/generate.mjs +1 -1
- package/dist/cli/commands/init.mjs +1 -1
- package/dist/cli/index.mjs +2 -2
- package/dist/context/index.mjs +2 -64
- package/dist/core/index.d.mts +3 -3
- package/dist/core/index.mjs +5 -4
- package/dist/core-BGa8TXJR.mjs +221 -0
- package/dist/{createActionRouter-BuOv-m4Y.mjs → createActionRouter-C-N7z2EL.mjs} +3 -2
- package/dist/{createAggregationRouter-CgJv_BU_.mjs → createAggregationRouter-wzwFa_dW.mjs} +3 -3
- package/dist/{createApp-CjOeWJ9_.mjs → createApp-Du5G54TM.mjs} +187 -96
- package/dist/{core-DQ6rDTyH.mjs → defineResource-2zvVVpxg.mjs} +297 -230
- package/dist/docs/index.d.mts +1 -1
- package/dist/docs/index.mjs +2 -2
- package/dist/{elevation-COpYKt2h.mjs → elevation-CIz1GjGm.mjs} +4 -3
- package/dist/{eventPlugin-A6EbthXR.d.mts → eventPlugin-Bg3cP2_u.d.mts} +38 -3
- package/dist/{eventPlugin-DJG2D7sy.mjs → eventPlugin-DrQ_FesK.mjs} +22 -4
- package/dist/events/index.d.mts +83 -314
- package/dist/events/index.mjs +183 -95
- package/dist/events/transports/redis-stream-entry.d.mts +1 -1
- package/dist/events/transports/redis-stream-entry.mjs +24 -2
- package/dist/factory/index.d.mts +5 -5
- package/dist/factory/index.mjs +30 -8
- package/dist/{fastify-augmentation-g_f9omSc.d.mts → fastify-augmentation-OzYGIiU1.d.mts} +26 -2
- package/dist/{generate-usRSdQmP.mjs → generate-D0wZupBg.mjs} +1 -1
- package/dist/headers-BvlQSDMB.mjs +45 -0
- package/dist/health-CB_m4S32.mjs +377 -0
- package/dist/hooks/index.d.mts +1 -1
- package/dist/idempotency/index.mjs +27 -3
- package/dist/{index-wMoWBLkn.d.mts → index-BMnbzvtO.d.mts} +3 -3
- package/dist/{integrations/jobs.d.mts → index-CG4yqMDB.d.mts} +82 -2
- package/dist/index-CZY4EGga.d.mts +107 -0
- package/dist/{index-CC5ruDGh.d.mts → index-CjVg-Epg.d.mts} +37 -5
- package/dist/{index-BBsvQt7U.d.mts → index-DPRH0Cme.d.mts} +36 -106
- package/dist/index.d.mts +6 -6
- package/dist/index.mjs +7 -6
- package/dist/{init-CjO4p_q1.mjs → init-tlRSt667.mjs} +206 -208
- package/dist/integrations/event-gateway.d.mts +1 -1
- package/dist/integrations/event-gateway.mjs +1 -1
- package/dist/integrations/index.d.mts +3 -3
- package/dist/integrations/jobs/index.d.mts +2 -0
- package/dist/integrations/{jobs.mjs → jobs/index.mjs} +110 -67
- package/dist/integrations/mcp/index.d.mts +4 -4
- package/dist/integrations/mcp/index.mjs +2 -2
- package/dist/integrations/mcp/testing.d.mts +1 -1
- package/dist/integrations/mcp/testing.mjs +1 -1
- package/dist/integrations/webhooks.d.mts +37 -30
- package/dist/integrations/webhooks.mjs +51 -62
- package/dist/integrations/websocket-redis.d.mts +1 -1
- package/dist/integrations/websocket.d.mts +1 -1
- package/dist/integrations/websocket.mjs +41 -14
- package/dist/{interface-C1_WLTMK.d.mts → interface-BGZCzOGL.d.mts} +24 -31
- package/dist/{mcpPlugin-BtzIL_kM.mjs → mcpPlugin-Cs6nRGCt.mjs} +12 -10
- package/dist/{memory-CLXA2VeJ.mjs → memory-CmnzXE5Z.mjs} +29 -0
- package/dist/middleware/index.d.mts +17 -1
- package/dist/middleware/index.mjs +1 -1
- package/dist/migrations/index.d.mts +94 -44
- package/dist/migrations/index.mjs +196 -44
- package/dist/module-_-M7C5ES.mjs +29 -0
- package/dist/{multipartBody-CHEQSZ6G.mjs → multipartBody-D0eBvkeq.mjs} +17 -0
- package/dist/{openapi-jSDx-VDv.mjs → openapi-BLV4OMXb.mjs} +3 -3
- package/dist/permissions/index.d.mts +2 -2
- package/dist/permissions/index.mjs +3 -3
- package/dist/{permissions-tbxFHxjZ.mjs → permissions-BrtiBKGs.mjs} +229 -229
- package/dist/pipeline/index.d.mts +1 -1
- package/dist/{actionPermissions-lszoJle1.mjs → pipeline-BGRnV-To.mjs} +4 -27
- package/dist/plugins/index.d.mts +6 -77
- package/dist/plugins/index.mjs +11 -95
- package/dist/plugins/response-cache.mjs +1 -0
- package/dist/plugins/tracing-entry.mjs +1 -1
- package/dist/{index-Ba10fdKp.d.mts → preloadResources-CM4uUaFv.d.mts} +51 -50
- package/dist/preloadResources-DSY5HGpS.mjs +45 -0
- package/dist/presets/filesUpload.d.mts +3 -3
- package/dist/presets/filesUpload.mjs +3 -3
- package/dist/presets/index.d.mts +2 -1
- package/dist/presets/index.mjs +1 -1
- package/dist/presets/multiTenant.d.mts +1 -1
- package/dist/presets/multiTenant.mjs +1 -1
- package/dist/presets/search.d.mts +2 -2
- package/dist/presets/search.mjs +1 -1
- package/dist/{presets-BOe4Ugkl.mjs → presets-DwFDVKP0.mjs} +2 -2
- package/dist/{queryCachePlugin-DzqLN904.mjs → queryCachePlugin-DSGuSVLW.mjs} +39 -8
- package/dist/{queryCachePlugin-CfYWq-Ak.d.mts → queryCachePlugin-br4otiqF.d.mts} +2 -2
- package/dist/{registerRoute-CWO6mdYe.mjs → rateLimit-DrED-C4v.mjs} +2 -109
- package/dist/{realtime-I8OrfnMR.mjs → realtime-DEuDu9io.mjs} +3 -3
- package/dist/{redis-stream-iVR2l7aS.d.mts → redis-stream-uIaauNpZ.d.mts} +22 -0
- package/dist/registerRoute-DTp18Ah_.mjs +108 -0
- package/dist/registry/index.d.mts +2 -2
- package/dist/registry/index.mjs +1 -1
- package/dist/{registry-vcOCtPij.mjs → registry-9km_w1aP.mjs} +15 -0
- package/dist/renewingLease-3cuQH7h8.mjs +44 -0
- package/dist/{requestContext-BZKOr13H.mjs → requestContext-MrlJwECf.mjs} +2 -2
- package/dist/schedules-CjOd2MG9.mjs +113 -0
- package/dist/schemas/index.d.mts +67 -67
- package/dist/schemas/index.mjs +2 -3
- package/dist/scim/index.mjs +1 -2
- package/dist/scope/index.d.mts +2 -2
- package/dist/scope/index.mjs +4 -4
- package/dist/{scope-LONQ3mfZ.mjs → scope-DfaPntpo.mjs} +4 -5
- package/dist/{sse-0u4P4-OI.mjs → sse-BQ1w0dfl.mjs} +1 -1
- package/dist/testing/cleanupStoreContract.d.mts +10 -0
- package/dist/testing/cleanupStoreContract.mjs +375 -0
- package/dist/testing/index.d.mts +5 -3
- package/dist/testing/index.mjs +4 -2
- package/dist/testing/outboxStoreContract.d.mts +13 -0
- package/dist/testing/outboxStoreContract.mjs +247 -0
- package/dist/transactionContext-PNdoyLLm.mjs +64 -0
- package/dist/types/index.d.mts +4 -4
- package/dist/{types-bHblZ9YN.mjs → types-CSyCxl2V.mjs} +38 -7
- package/dist/types-C_ZZRcD6.d.mts +450 -0
- package/dist/{module-B4u10iOJ.d.mts → types-Cu6vVJmh.d.mts} +196 -60
- package/dist/{types-DH0GupI7.d.mts → types-DjC-KUCz.d.mts} +118 -20
- package/dist/{types-Csxga7d3.d.mts → types-Dmw-6Qeo.d.mts} +96 -4
- package/dist/{types-5OuqeTDX.d.mts → types-QFGc3Cjv.d.mts} +1 -1
- package/dist/usage/index.d.mts +3 -52
- package/dist/usage/index.mjs +0 -0
- package/dist/userHelpers-Cg_y3fBz.mjs +29 -0
- package/dist/utils/index.d.mts +58 -85
- package/dist/utils/index.mjs +4 -2
- package/dist/{utils-CZyeRXTh.mjs → utils-taUVD4p3.mjs} +47 -337
- package/dist/{validate-C2mGgSXg.mjs → validate-Bk44F2yT.mjs} +1 -1
- package/dist/{versioning-D6PvCwpq.d.mts → versioning-DXgY5BUm.d.mts} +11 -29
- package/dist/{versioning-Dk-71iIz.mjs → versioning-gKWyryT3.mjs} +8 -1
- package/dist/{websocket-uuWqM6UY.d.mts → websocket-_Pd0CgR0.d.mts} +15 -1
- package/package.json +541 -513
- package/skills/arc/references/api-reference.md +3 -1
- package/skills/arc/references/integrations.md +20 -11
- package/dist/QueryCache-Z1B38koh.d.mts +0 -40
- package/dist/betterAuthOpenApi-CV1lxpRX.mjs +0 -2
- package/dist/health-ScyjIVo5.mjs +0 -202
- package/dist/requestId-5BhTVzBv.mjs +0 -113
- package/dist/types-XTApa8b6.mjs +0 -24
|
@@ -1,66 +1,128 @@
|
|
|
1
1
|
import { t as __exportAll } from "./rolldown-runtime-8H4AJuhK.mjs";
|
|
2
2
|
import { arcLog, configureArcLogger, createPinoWriter } from "./logger/index.mjs";
|
|
3
|
-
import { n as PUBLIC_SCOPE } from "./types-
|
|
3
|
+
import { n as PUBLIC_SCOPE } from "./types-CSyCxl2V.mjs";
|
|
4
4
|
import { t as createTenantKeyGenerator } from "./rateLimitKey-BoWRH3e7.mjs";
|
|
5
|
-
import {
|
|
5
|
+
import { i as createRequestIdGenerator, r as mergeHealthChecks } from "./health-CB_m4S32.mjs";
|
|
6
6
|
import { t as parseJsonBody } from "./jsonBody-BlzAoGCe.mjs";
|
|
7
7
|
import Fastify from "fastify";
|
|
8
8
|
import qs from "qs";
|
|
9
|
-
//#region src/
|
|
9
|
+
//#region src/factory/module/resolve.ts
|
|
10
10
|
/**
|
|
11
|
-
*
|
|
12
|
-
*
|
|
13
|
-
*
|
|
14
|
-
* - `{ import: 'default' }` form: values are the resource directly
|
|
15
|
-
* - default form: values are the full module — picks first export with `toPlugin()`
|
|
16
|
-
*
|
|
17
|
-
* Throws if any module doesn't yield a valid `ResourceLike`.
|
|
11
|
+
* Resolve any module-input form to a concrete `ArcModule`. A thunk is invoked
|
|
12
|
+
* (this is where a dynamic `import()` fires); a promise (or plain module) is
|
|
13
|
+
* awaited. Called once per module at the start of the bootstrap phase.
|
|
18
14
|
*/
|
|
19
|
-
function
|
|
20
|
-
const
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
if (!resource) throw new Error(`preloadResources: ${path} does not export a valid resource.\n Expected: a default export OR a named export with toPlugin().`);
|
|
24
|
-
resources.push(resource);
|
|
25
|
-
}
|
|
26
|
-
return resources.sort((a, b) => (a.name ?? "").localeCompare(b.name ?? ""));
|
|
15
|
+
async function resolveModule(input) {
|
|
16
|
+
const resolved = typeof input === "function" ? await input() : await input;
|
|
17
|
+
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.");
|
|
18
|
+
return resolved;
|
|
27
19
|
}
|
|
20
|
+
/** Resolve a `ModuleContribution` to a concrete array (array passthrough or factory call). */
|
|
21
|
+
async function resolveContribution(contribution, fastify) {
|
|
22
|
+
if (!contribution) return [];
|
|
23
|
+
return typeof contribution === "function" ? await contribution(fastify) : contribution;
|
|
24
|
+
}
|
|
25
|
+
//#endregion
|
|
26
|
+
//#region src/factory/module/contributions.ts
|
|
28
27
|
/**
|
|
29
|
-
*
|
|
30
|
-
*
|
|
31
|
-
*
|
|
32
|
-
*
|
|
28
|
+
* Collect every module's `healthChecks` in the caller's order. Fails at boot
|
|
29
|
+
* on a duplicate check `name` across modules, naming BOTH owning modules.
|
|
30
|
+
* The host's app-level checks are appended AFTER this (modules-first,
|
|
31
|
+
* host-last) by the caller.
|
|
32
|
+
*/
|
|
33
|
+
function collectModuleHealthChecks(modules) {
|
|
34
|
+
return mergeHealthChecks(modules.map((module) => ({
|
|
35
|
+
owner: `module "${module.name}"`,
|
|
36
|
+
checks: module.healthChecks
|
|
37
|
+
})));
|
|
38
|
+
}
|
|
39
|
+
/**
|
|
40
|
+
* Collect every module's `workflows`. Resolves factory contributions (so a
|
|
41
|
+
* factory can close over a container the caller has already decorated on
|
|
42
|
+
* `fastify`) and flattens to a single opaque array. Arc core deliberately
|
|
43
|
+
* does NOT validate or interpret the values — the streamline integration
|
|
44
|
+
* owns name/shape checks and registration. Called by the integration at init
|
|
45
|
+
* time, not by arc's boot pipeline (workflows depend on the integration's
|
|
46
|
+
* container).
|
|
33
47
|
*/
|
|
34
|
-
async function
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
}
|
|
48
|
+
async function collectModuleWorkflows(fastify, modules) {
|
|
49
|
+
const out = [];
|
|
50
|
+
for (const m of modules) {
|
|
51
|
+
const wfs = await resolveContribution(m.workflows, fastify);
|
|
52
|
+
out.push(...wfs);
|
|
53
|
+
}
|
|
54
|
+
return out;
|
|
40
55
|
}
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
];
|
|
50
|
-
for (const
|
|
56
|
+
/**
|
|
57
|
+
* Collect every module's `scheduledJobs`. Resolves factory contributions
|
|
58
|
+
* (after bootstraps) and fails at boot on a duplicate job `name` across
|
|
59
|
+
* modules. The caller passes the returned definitions to the canonical
|
|
60
|
+
* schedules plugin; factories must therefore be resolved only once.
|
|
61
|
+
*/
|
|
62
|
+
async function collectModuleScheduledJobs(fastify, modules) {
|
|
63
|
+
const owner = /* @__PURE__ */ new Map();
|
|
64
|
+
const out = [];
|
|
65
|
+
for (const m of modules) {
|
|
66
|
+
const jobs = await resolveContribution(m.scheduledJobs, fastify);
|
|
67
|
+
for (const job of jobs) {
|
|
68
|
+
const prior = owner.get(job.name);
|
|
69
|
+
if (prior !== void 0) throw new Error(`[arc] duplicate scheduled-job name "${job.name}" — declared by module "${prior}" and module "${m.name}". Scheduled-job names must be unique across the module graph.`);
|
|
70
|
+
owner.set(job.name, m.name);
|
|
71
|
+
out.push(job);
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
|
+
return out;
|
|
51
75
|
}
|
|
52
76
|
//#endregion
|
|
53
|
-
//#region src/factory/module.ts
|
|
77
|
+
//#region src/factory/module/lifecycle.ts
|
|
54
78
|
/**
|
|
55
|
-
*
|
|
56
|
-
*
|
|
57
|
-
*
|
|
79
|
+
* Subscribe every module's `eventHandlers` in dependency order (pass the
|
|
80
|
+
* `orderModules`-sorted list). Returns the ordered list of unsubscribe
|
|
81
|
+
* functions so the caller can tear them down at shutdown BEFORE module
|
|
82
|
+
* `onClose` (while module deps are still alive). Fails at boot on:
|
|
83
|
+
* - a duplicate NAMED handler across modules (attributing both owners);
|
|
84
|
+
* - a module declaring handlers while the event subsystem is unavailable.
|
|
85
|
+
* Resolves factory contributions (after bootstraps) so a handler can close over
|
|
86
|
+
* booted engines rather than a global getter.
|
|
58
87
|
*/
|
|
59
|
-
async function
|
|
60
|
-
const
|
|
61
|
-
|
|
62
|
-
|
|
88
|
+
async function subscribeModuleEventHandlers(fastify, modules) {
|
|
89
|
+
const unsubscribes = [];
|
|
90
|
+
const owner = /* @__PURE__ */ new Map();
|
|
91
|
+
try {
|
|
92
|
+
for (const m of modules) {
|
|
93
|
+
const handlers = await resolveContribution(m.eventHandlers, fastify);
|
|
94
|
+
if (handlers.length === 0) continue;
|
|
95
|
+
const bus = fastify.events;
|
|
96
|
+
if (!bus) throw new Error(`[arc] module "${m.name}" declares eventHandlers but the event subsystem is unavailable (fastify.events). Enable arcPlugins.events.`);
|
|
97
|
+
for (const def of handlers) {
|
|
98
|
+
if (def.name !== void 0) {
|
|
99
|
+
const prior = owner.get(def.name);
|
|
100
|
+
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.`);
|
|
101
|
+
owner.set(def.name, m.name);
|
|
102
|
+
}
|
|
103
|
+
const patterns = Array.isArray(def.event) ? def.event : [def.event];
|
|
104
|
+
for (const pattern of patterns) unsubscribes.push(await bus.subscribe(pattern, def.handler));
|
|
105
|
+
}
|
|
106
|
+
}
|
|
107
|
+
return unsubscribes;
|
|
108
|
+
} catch (err) {
|
|
109
|
+
const rollbackErrors = await unsubscribeModuleEventHandlers(unsubscribes);
|
|
110
|
+
for (const rollbackError of rollbackErrors) fastify.log.error({ err: rollbackError }, "[arc] module event-handler activation rollback failed");
|
|
111
|
+
throw err;
|
|
112
|
+
}
|
|
63
113
|
}
|
|
114
|
+
/** Best-effort reverse-order teardown; every unsubscribe is attempted. */
|
|
115
|
+
async function unsubscribeModuleEventHandlers(unsubscribes) {
|
|
116
|
+
const errors = [];
|
|
117
|
+
for (let index = unsubscribes.length - 1; index >= 0; index--) try {
|
|
118
|
+
await unsubscribes[index]?.();
|
|
119
|
+
} catch (err) {
|
|
120
|
+
errors.push(err);
|
|
121
|
+
}
|
|
122
|
+
return errors;
|
|
123
|
+
}
|
|
124
|
+
//#endregion
|
|
125
|
+
//#region src/factory/module/order.ts
|
|
64
126
|
/**
|
|
65
127
|
* Order modules for composition by their `dependsOn` edges — a STABLE
|
|
66
128
|
* topological sort. Called once at the start of the bootstrap phase; every
|
|
@@ -151,32 +213,6 @@ function describeModuleCycle(remaining, byName) {
|
|
|
151
213
|
}
|
|
152
214
|
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.`;
|
|
153
215
|
}
|
|
154
|
-
/**
|
|
155
|
-
* Identity helper for authoring a typed module — mirrors `defineResource`.
|
|
156
|
-
* Gives inference + a single obvious construction site; does no work.
|
|
157
|
-
*
|
|
158
|
-
* `TExports` is inferred from the `bootstrap` return value, so a module
|
|
159
|
-
* author gets a typed public export for free:
|
|
160
|
-
*
|
|
161
|
-
* ```ts
|
|
162
|
-
* export const accountingModule = (deps: Deps) =>
|
|
163
|
-
* defineModule({
|
|
164
|
-
* name: "accounting",
|
|
165
|
-
* bootstrap: async () => createAccountingEngine(deps), // TExports inferred
|
|
166
|
-
* });
|
|
167
|
-
* ```
|
|
168
|
-
*/
|
|
169
|
-
function defineModule(module) {
|
|
170
|
-
return module;
|
|
171
|
-
}
|
|
172
|
-
function getModuleExports(fastify, name) {
|
|
173
|
-
const modules = fastify.arc?.modules;
|
|
174
|
-
if (!modules || !Object.hasOwn(modules, name)) {
|
|
175
|
-
const available = modules ? Object.keys(modules) : [];
|
|
176
|
-
throw new Error(`[arc] no public export recorded for module "${name}".\nA module's export is its bootstrap() return value; it is readable only AFTER that module's bootstrap ran (dependsOn / list order = init order).\nModules with recorded exports: ${available.length ? available.join(", ") : "(none)"}.`);
|
|
177
|
-
}
|
|
178
|
-
return modules[name];
|
|
179
|
-
}
|
|
180
216
|
//#endregion
|
|
181
217
|
//#region src/factory/presets.ts
|
|
182
218
|
/**
|
|
@@ -200,7 +236,7 @@ const productionPreset = {
|
|
|
200
236
|
censor: "[REDACTED]"
|
|
201
237
|
}
|
|
202
238
|
},
|
|
203
|
-
trustProxy:
|
|
239
|
+
trustProxy: false,
|
|
204
240
|
helmet: { contentSecurityPolicy: { directives: {
|
|
205
241
|
defaultSrc: ["'self'"],
|
|
206
242
|
styleSrc: ["'self'", "'unsafe-inline'"],
|
|
@@ -237,6 +273,7 @@ const productionPreset = {
|
|
|
237
273
|
underPressure: {
|
|
238
274
|
exposeStatusRoute: true,
|
|
239
275
|
maxEventLoopDelay: 3e3,
|
|
276
|
+
maxEventLoopUtilization: .98,
|
|
240
277
|
maxHeapUsedBytes: 1024 * 1024 * 1024,
|
|
241
278
|
maxRssBytes: 1024 * 1024 * 1024
|
|
242
279
|
}
|
|
@@ -402,7 +439,7 @@ async function registerArcCore(fastify, config, trackPlugin) {
|
|
|
402
439
|
await fastify.register(arcCorePlugin, { emitEvents: config.arcPlugins?.emitEvents !== false });
|
|
403
440
|
trackPlugin("arc-core");
|
|
404
441
|
if (config.arcPlugins?.events !== false) {
|
|
405
|
-
const { default: eventPlugin } = await import("./eventPlugin-
|
|
442
|
+
const { default: eventPlugin } = await import("./eventPlugin-DrQ_FesK.mjs").then((n) => n.n);
|
|
406
443
|
const eventOpts = typeof config.arcPlugins?.events === "object" ? config.arcPlugins.events : {};
|
|
407
444
|
await fastify.register(eventPlugin, {
|
|
408
445
|
...eventOpts,
|
|
@@ -423,16 +460,27 @@ async function registerArcCore(fastify, config, trackPlugin) {
|
|
|
423
460
|
*
|
|
424
461
|
* @param modules - Plugin modules loaded by registerArcCore (avoids re-importing)
|
|
425
462
|
*/
|
|
426
|
-
async function registerArcPlugins(fastify, config, trackPlugin, modules) {
|
|
463
|
+
async function registerArcPlugins(fastify, config, trackPlugin, modules, moduleHealthChecks = []) {
|
|
427
464
|
const { requestIdPlugin, healthPlugin, gracefulShutdownPlugin } = modules;
|
|
428
465
|
if (config.arcPlugins?.requestId !== false) {
|
|
429
466
|
await fastify.register(requestIdPlugin);
|
|
430
467
|
trackPlugin("arc-request-id");
|
|
431
468
|
}
|
|
432
469
|
if (config.arcPlugins?.health !== false) {
|
|
433
|
-
const
|
|
470
|
+
const appHealth = typeof config.arcPlugins?.health === "object" && config.arcPlugins.health !== null ? config.arcPlugins.health : {};
|
|
471
|
+
const checks = mergeHealthChecks([{
|
|
472
|
+
owner: "the module graph",
|
|
473
|
+
checks: moduleHealthChecks
|
|
474
|
+
}, {
|
|
475
|
+
owner: "arcPlugins.health",
|
|
476
|
+
checks: appHealth.checks
|
|
477
|
+
}]);
|
|
478
|
+
const healthOpts = {
|
|
479
|
+
...appHealth,
|
|
480
|
+
checks
|
|
481
|
+
};
|
|
434
482
|
await fastify.register(healthPlugin, healthOpts);
|
|
435
|
-
trackPlugin("arc-health", healthOpts);
|
|
483
|
+
trackPlugin("arc-health", { checkCount: healthOpts.checks?.length ?? 0 });
|
|
436
484
|
}
|
|
437
485
|
if (config.arcPlugins?.gracefulShutdown !== false) {
|
|
438
486
|
await fastify.register(gracefulShutdownPlugin);
|
|
@@ -445,9 +493,9 @@ async function registerArcPlugins(fastify, config, trackPlugin, modules) {
|
|
|
445
493
|
trackPlugin("arc-caching", opts);
|
|
446
494
|
}
|
|
447
495
|
if (config.arcPlugins?.queryCache) {
|
|
448
|
-
const { queryCachePlugin } = await import("./queryCachePlugin-
|
|
496
|
+
const { queryCachePlugin } = await import("./queryCachePlugin-DSGuSVLW.mjs").then((n) => n.n);
|
|
449
497
|
const opts = config.arcPlugins.queryCache === true ? {} : config.arcPlugins.queryCache;
|
|
450
|
-
const store = config.stores?.queryCache ?? new (await (import("./memory-
|
|
498
|
+
const store = config.stores?.queryCache ?? new (await (import("./memory-CmnzXE5Z.mjs").then((n) => n.n))).MemoryCacheStore();
|
|
451
499
|
await fastify.register(queryCachePlugin, {
|
|
452
500
|
store,
|
|
453
501
|
...opts
|
|
@@ -456,14 +504,14 @@ async function registerArcPlugins(fastify, config, trackPlugin, modules) {
|
|
|
456
504
|
}
|
|
457
505
|
if (config.arcPlugins?.sse) if (config.arcPlugins?.events === false) fastify.log.warn("SSE plugin requires events plugin (arcPlugins.events). SSE disabled.");
|
|
458
506
|
else {
|
|
459
|
-
const { default: ssePlugin } = await import("./sse-
|
|
507
|
+
const { default: ssePlugin } = await import("./sse-BQ1w0dfl.mjs").then((n) => n.r);
|
|
460
508
|
const opts = config.arcPlugins.sse === true ? {} : config.arcPlugins.sse;
|
|
461
509
|
await fastify.register(ssePlugin, opts);
|
|
462
510
|
trackPlugin("arc-sse", opts);
|
|
463
511
|
}
|
|
464
512
|
if (config.arcPlugins?.realtime) if (config.arcPlugins?.events === false) fastify.log.warn("Realtime plugin requires events plugin (arcPlugins.events). Realtime disabled.");
|
|
465
513
|
else {
|
|
466
|
-
const { default: realtimePlugin } = await import("./realtime-
|
|
514
|
+
const { default: realtimePlugin } = await import("./realtime-DEuDu9io.mjs").then((n) => n.r);
|
|
467
515
|
const opts = config.arcPlugins.realtime === true ? {} : config.arcPlugins.realtime;
|
|
468
516
|
await fastify.register(realtimePlugin, opts);
|
|
469
517
|
trackPlugin("arc-realtime", opts);
|
|
@@ -475,7 +523,7 @@ async function registerArcPlugins(fastify, config, trackPlugin, modules) {
|
|
|
475
523
|
trackPlugin("arc-metrics", opts);
|
|
476
524
|
}
|
|
477
525
|
if (config.arcPlugins?.versioning) {
|
|
478
|
-
const { default: versioningPlugin } = await import("./versioning-
|
|
526
|
+
const { default: versioningPlugin } = await import("./versioning-gKWyryT3.mjs").then((n) => n.r);
|
|
479
527
|
await fastify.register(versioningPlugin, config.arcPlugins.versioning);
|
|
480
528
|
trackPlugin("arc-versioning", config.arcPlugins.versioning);
|
|
481
529
|
}
|
|
@@ -544,7 +592,7 @@ async function registerAuth(fastify, config, trackPlugin) {
|
|
|
544
592
|
*/
|
|
545
593
|
async function registerElevation(fastify, config, trackPlugin) {
|
|
546
594
|
if (!config.elevation) return;
|
|
547
|
-
const { elevationPlugin } = await import("./elevation-
|
|
595
|
+
const { elevationPlugin } = await import("./elevation-CIz1GjGm.mjs").then((n) => n.r);
|
|
548
596
|
await fastify.register(elevationPlugin, config.elevation);
|
|
549
597
|
trackPlugin("arc-elevation", config.elevation);
|
|
550
598
|
fastify.log.debug("Elevation plugin enabled");
|
|
@@ -780,9 +828,34 @@ async function registerResources(fastify, config, preResolvedModules) {
|
|
|
780
828
|
throw new Error(`[arc] module "${m.name}" afterResources() threw: ${msg}`, { cause: err });
|
|
781
829
|
}
|
|
782
830
|
}
|
|
783
|
-
|
|
831
|
+
const scheduledJobs = await collectModuleScheduledJobs(fastify, modules);
|
|
832
|
+
if (scheduledJobs.length > 0) {
|
|
833
|
+
if (config.arcPlugins?.schedules === false) throw new Error("[arc] modules declare scheduledJobs but arcPlugins.schedules is false. Enable the scheduler or remove the declarations.");
|
|
834
|
+
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.");
|
|
835
|
+
const frozenJobs = Object.freeze(scheduledJobs.map((job) => Object.freeze({ ...job })));
|
|
836
|
+
const scheduleOptions = typeof config.arcPlugins?.schedules === "object" ? config.arcPlugins.schedules : {};
|
|
837
|
+
const { default: schedulesPlugin } = await import("./schedules-CjOd2MG9.mjs").then((n) => n.r);
|
|
838
|
+
await fastify.register(schedulesPlugin, {
|
|
839
|
+
...scheduleOptions,
|
|
840
|
+
schedules: frozenJobs
|
|
841
|
+
});
|
|
842
|
+
if (fastify.arc) {
|
|
843
|
+
fastify.arc.scheduledJobs = frozenJobs;
|
|
844
|
+
fastify.arc.plugins.set("arc-schedules", {
|
|
845
|
+
name: "arc-schedules",
|
|
846
|
+
options: { scheduleCount: frozenJobs.length },
|
|
847
|
+
registeredAt: (/* @__PURE__ */ new Date()).toISOString()
|
|
848
|
+
});
|
|
849
|
+
}
|
|
850
|
+
}
|
|
851
|
+
const eventUnsubscribes = await subscribeModuleEventHandlers(fastify, modules);
|
|
852
|
+
if (config.afterResources) try {
|
|
784
853
|
await config.afterResources(fastify);
|
|
785
854
|
fastify.log.debug("afterResources hook executed");
|
|
855
|
+
} catch (err) {
|
|
856
|
+
const rollbackErrors = await unsubscribeModuleEventHandlers(eventUnsubscribes);
|
|
857
|
+
for (const rollbackError of rollbackErrors) fastify.log.error({ err: rollbackError }, "[arc] module event-handler rollback after afterResources failure failed");
|
|
858
|
+
throw err;
|
|
786
859
|
}
|
|
787
860
|
if (config.onReady) {
|
|
788
861
|
const onReady = config.onReady;
|
|
@@ -792,7 +865,9 @@ async function registerResources(fastify, config, preResolvedModules) {
|
|
|
792
865
|
}
|
|
793
866
|
const closers = modules.filter((m) => m.onClose).reverse();
|
|
794
867
|
const appOnClose = config.onClose;
|
|
795
|
-
if (closers.length || appOnClose) fastify.addHook("onClose", async () => {
|
|
868
|
+
if (closers.length || appOnClose || eventUnsubscribes.length) fastify.addHook("onClose", async () => {
|
|
869
|
+
const unsubscribeErrors = await unsubscribeModuleEventHandlers(eventUnsubscribes);
|
|
870
|
+
for (const err of unsubscribeErrors) fastify.log.error({ err }, "[arc] module event-handler shutdown unsubscribe failed");
|
|
796
871
|
for (const m of closers) await m.onClose?.(fastify);
|
|
797
872
|
if (appOnClose) await appOnClose(fastify);
|
|
798
873
|
});
|
|
@@ -1018,7 +1093,21 @@ async function registerUtilityPlugins(fastify, config) {
|
|
|
1018
1093
|
if (config.preset === "production") fastify.log.warn("Response compression is not enabled (Fastify 5 stream issues). Use a reverse proxy (Nginx, Caddy, Cloudflare) for gzip/brotli in production.");
|
|
1019
1094
|
if (config.underPressure !== false) {
|
|
1020
1095
|
const underPressure = await loadPlugin("underPressure");
|
|
1021
|
-
|
|
1096
|
+
const hostConfig = config.underPressure;
|
|
1097
|
+
const effective = hostConfig ?? {
|
|
1098
|
+
exposeStatusRoute: true,
|
|
1099
|
+
maxEventLoopDelay: 3e3,
|
|
1100
|
+
maxEventLoopUtilization: .98
|
|
1101
|
+
};
|
|
1102
|
+
if (config.preset === "production" && hostConfig && ![
|
|
1103
|
+
"maxEventLoopDelay",
|
|
1104
|
+
"maxEventLoopUtilization",
|
|
1105
|
+
"maxHeapUsedBytes",
|
|
1106
|
+
"maxRssBytes",
|
|
1107
|
+
"pressureHandler",
|
|
1108
|
+
"healthCheck"
|
|
1109
|
+
].some((key) => key in hostConfig)) fastify.log.warn("[arc] underPressure is configured without any pressure threshold (maxEventLoopDelay / maxEventLoopUtilization / maxHeapUsedBytes / maxRssBytes) — it will monitor but never shed load. Set at least an event-loop threshold, and size heap/RSS limits from your container memory limit.");
|
|
1110
|
+
await fastify.register(underPressure, effective);
|
|
1022
1111
|
fastify.log.debug("Health monitoring (under-pressure) enabled");
|
|
1023
1112
|
}
|
|
1024
1113
|
if (config.sensible !== false) {
|
|
@@ -1191,6 +1280,8 @@ function validateDistributedRuntime(options) {
|
|
|
1191
1280
|
hint: "a Redis-backed query-cache adapter"
|
|
1192
1281
|
});
|
|
1193
1282
|
}
|
|
1283
|
+
const schedules = options.arcPlugins?.schedules;
|
|
1284
|
+
if (schedules && typeof schedules === "object" && schedules.enabled !== false && !schedules.lock) deferredWarnings.push("runtime: 'distributed' — schedules configured without a `lock` adapter. EVERY replica will fire every schedule tick. Pass an ecosystem lock (e.g. createMongoLockAdapter) under arcPlugins.schedules.lock for single-firer leases.");
|
|
1194
1285
|
if (missing.length > 0) {
|
|
1195
1286
|
const lines = missing.map((m) => ` • ${m.key.padEnd(20)} → ${m.hint}`).join("\n");
|
|
1196
1287
|
throw new Error(`[Arc] runtime: 'distributed' requires durable (Redis/MongoDB) adapters, not memory.
|
|
@@ -1227,9 +1318,10 @@ async function createApp(options) {
|
|
|
1227
1318
|
...options.preset ? getPreset(options.preset) : {},
|
|
1228
1319
|
...options
|
|
1229
1320
|
};
|
|
1230
|
-
const
|
|
1321
|
+
const inheritedTrustProxyDefault = options.preset === "production" && options.trustProxy === void 0;
|
|
1231
1322
|
const resolvedModules = orderModules(await Promise.all((config.modules ?? []).map((m) => resolveModule(m))));
|
|
1232
1323
|
const moduleErrorMappers = resolvedModules.flatMap((m) => m.errorMappers ?? []);
|
|
1324
|
+
const moduleHealthChecks = collectModuleHealthChecks(resolvedModules);
|
|
1233
1325
|
const resolvedLogger = resolveLoggerConfig(config.logger);
|
|
1234
1326
|
const fastify = Fastify({
|
|
1235
1327
|
logger: resolvedLogger,
|
|
@@ -1263,13 +1355,7 @@ async function createApp(options) {
|
|
|
1263
1355
|
writer: createPinoWriter(fastify.log)
|
|
1264
1356
|
});
|
|
1265
1357
|
for (const warning of deferredWarnings) fastify.log.warn(warning);
|
|
1266
|
-
if (
|
|
1267
|
-
if (config.typeProvider === "typebox") try {
|
|
1268
|
-
const { TypeBoxValidatorCompiler } = await import("@fastify/type-provider-typebox");
|
|
1269
|
-
fastify.setValidatorCompiler(TypeBoxValidatorCompiler);
|
|
1270
|
-
} catch {
|
|
1271
|
-
fastify.log.warn("typeProvider: \"typebox\" requested but @fastify/type-provider-typebox is not installed.");
|
|
1272
|
-
}
|
|
1358
|
+
if (inheritedTrustProxyDefault) fastify.log.warn("[arc] preset: 'production' now defaults to trustProxy: false (fail-closed; was true before 2.24). 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: a hop count (trustProxy: 1), a CIDR allow-list ('10.0.0.0/8'), a resolver function, or false if clients truly connect directly.");
|
|
1273
1359
|
fastify.removeContentTypeParser("application/json");
|
|
1274
1360
|
fastify.addContentTypeParser("application/json", { parseAs: "string" }, (_req, body, done) => {
|
|
1275
1361
|
try {
|
|
@@ -1289,7 +1375,12 @@ async function createApp(options) {
|
|
|
1289
1375
|
registeredAt: (/* @__PURE__ */ new Date()).toISOString()
|
|
1290
1376
|
});
|
|
1291
1377
|
};
|
|
1292
|
-
|
|
1378
|
+
const arcModules = await registerArcCore(fastify, config, trackPlugin);
|
|
1379
|
+
if (fastify.arc) {
|
|
1380
|
+
fastify.arc.healthChecks = Object.freeze([...moduleHealthChecks]);
|
|
1381
|
+
fastify.arc.moduleDefinitions = Object.freeze([...resolvedModules]);
|
|
1382
|
+
}
|
|
1383
|
+
await registerArcPlugins(fastify, config, trackPlugin, arcModules, moduleHealthChecks);
|
|
1293
1384
|
decorateRequestScope(fastify);
|
|
1294
1385
|
await registerAuth(fastify, config, trackPlugin);
|
|
1295
1386
|
await registerElevation(fastify, config, trackPlugin);
|
|
@@ -1347,4 +1438,4 @@ const ArcFactory = {
|
|
|
1347
1438
|
}
|
|
1348
1439
|
};
|
|
1349
1440
|
//#endregion
|
|
1350
|
-
export { edgePreset as a, testingPreset as c,
|
|
1441
|
+
export { edgePreset as a, testingPreset as c, subscribeModuleEventHandlers as d, collectModuleHealthChecks as f, resolveModule as g, resolveContribution as h, developmentPreset as i, workerPreset as l, collectModuleWorkflows as m, createApp as n, getPreset as o, collectModuleScheduledJobs as p, createApp_exports as r, productionPreset as s, ArcFactory as t, orderModules as u };
|