@classytic/arc 2.23.0 → 2.25.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/{BaseController-BQ8KNvWC.mjs → BaseController--M8EMzt0.mjs} +90 -21
- package/dist/QueryCache-DOMT_iq7.d.mts +72 -0
- package/dist/actionPermissions-CAphcmUG.mjs +24 -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/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 +4 -4
- package/dist/{core-DQ6rDTyH.mjs → core-EUNu0u07.mjs} +296 -11
- package/dist/{createActionRouter-BuOv-m4Y.mjs → createActionRouter-B6GvAuAX.mjs} +3 -2
- package/dist/{createAggregationRouter-CgJv_BU_.mjs → createAggregationRouter-wzwFa_dW.mjs} +3 -3
- 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 +57 -314
- package/dist/events/index.mjs +163 -91
- 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 +4 -5
- package/dist/factory/index.mjs +28 -8
- package/dist/{fastify-augmentation-g_f9omSc.d.mts → fastify-augmentation-dPNBVeIj.d.mts} +25 -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-BBsvQt7U.d.mts → index-DPRH0Cme.d.mts} +36 -106
- package/dist/{module-B4u10iOJ.d.mts → index-KUoxjIq1.d.mts} +263 -23
- package/dist/{index-CC5ruDGh.d.mts → index-f4PTrk3B.d.mts} +37 -5
- package/dist/index.d.mts +6 -6
- package/dist/index.mjs +5 -5
- 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-Cb7olw0j.mjs} +11 -9
- 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/{multipartBody-CHEQSZ6G.mjs → multipartBody-D0eBvkeq.mjs} +17 -0
- package/dist/{openapi-jSDx-VDv.mjs → openapi-DrLtog_B.mjs} +3 -3
- package/dist/permissions/index.d.mts +2 -2
- package/dist/permissions/index.mjs +2 -2
- package/dist/{permissions-tbxFHxjZ.mjs → permissions-BrtiBKGs.mjs} +229 -229
- package/dist/pipeline/index.d.mts +1 -1
- package/dist/{actionPermissions-lszoJle1.mjs → pipeline-BN5G05rd.mjs} +3 -26
- 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-0oe-JSSS.d.mts} +50 -50
- package/dist/{createApp-CjOeWJ9_.mjs → preloadResources-DfyngIN9.mjs} +233 -70
- 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-Cuw_deA-.mjs} +2 -2
- package/dist/{redis-stream-iVR2l7aS.d.mts → redis-stream-uIaauNpZ.d.mts} +22 -0
- package/dist/registerRoute-v0Wb6Plt.mjs +108 -0
- package/dist/registry/index.d.mts +2 -2
- 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/index.d.mts +4 -3
- package/dist/testing/index.mjs +2 -2
- 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-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 +526 -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.
|
|
33
32
|
*/
|
|
34
|
-
|
|
35
|
-
return (
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
}))).sort((a, b) => (a.name ?? "").localeCompare(b.name ?? ""));
|
|
33
|
+
function collectModuleHealthChecks(modules) {
|
|
34
|
+
return mergeHealthChecks(modules.map((module) => ({
|
|
35
|
+
owner: `module "${module.name}"`,
|
|
36
|
+
checks: module.healthChecks
|
|
37
|
+
})));
|
|
40
38
|
}
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
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).
|
|
47
|
+
*/
|
|
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;
|
|
55
|
+
}
|
|
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,6 +213,8 @@ 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
|
}
|
|
216
|
+
//#endregion
|
|
217
|
+
//#region src/factory/module/index.ts
|
|
154
218
|
/**
|
|
155
219
|
* Identity helper for authoring a typed module — mirrors `defineResource`.
|
|
156
220
|
* Gives inference + a single obvious construction site; does no work.
|
|
@@ -200,7 +264,7 @@ const productionPreset = {
|
|
|
200
264
|
censor: "[REDACTED]"
|
|
201
265
|
}
|
|
202
266
|
},
|
|
203
|
-
trustProxy:
|
|
267
|
+
trustProxy: false,
|
|
204
268
|
helmet: { contentSecurityPolicy: { directives: {
|
|
205
269
|
defaultSrc: ["'self'"],
|
|
206
270
|
styleSrc: ["'self'", "'unsafe-inline'"],
|
|
@@ -237,6 +301,7 @@ const productionPreset = {
|
|
|
237
301
|
underPressure: {
|
|
238
302
|
exposeStatusRoute: true,
|
|
239
303
|
maxEventLoopDelay: 3e3,
|
|
304
|
+
maxEventLoopUtilization: .98,
|
|
240
305
|
maxHeapUsedBytes: 1024 * 1024 * 1024,
|
|
241
306
|
maxRssBytes: 1024 * 1024 * 1024
|
|
242
307
|
}
|
|
@@ -402,7 +467,7 @@ async function registerArcCore(fastify, config, trackPlugin) {
|
|
|
402
467
|
await fastify.register(arcCorePlugin, { emitEvents: config.arcPlugins?.emitEvents !== false });
|
|
403
468
|
trackPlugin("arc-core");
|
|
404
469
|
if (config.arcPlugins?.events !== false) {
|
|
405
|
-
const { default: eventPlugin } = await import("./eventPlugin-
|
|
470
|
+
const { default: eventPlugin } = await import("./eventPlugin-DrQ_FesK.mjs").then((n) => n.n);
|
|
406
471
|
const eventOpts = typeof config.arcPlugins?.events === "object" ? config.arcPlugins.events : {};
|
|
407
472
|
await fastify.register(eventPlugin, {
|
|
408
473
|
...eventOpts,
|
|
@@ -423,16 +488,27 @@ async function registerArcCore(fastify, config, trackPlugin) {
|
|
|
423
488
|
*
|
|
424
489
|
* @param modules - Plugin modules loaded by registerArcCore (avoids re-importing)
|
|
425
490
|
*/
|
|
426
|
-
async function registerArcPlugins(fastify, config, trackPlugin, modules) {
|
|
491
|
+
async function registerArcPlugins(fastify, config, trackPlugin, modules, moduleHealthChecks = []) {
|
|
427
492
|
const { requestIdPlugin, healthPlugin, gracefulShutdownPlugin } = modules;
|
|
428
493
|
if (config.arcPlugins?.requestId !== false) {
|
|
429
494
|
await fastify.register(requestIdPlugin);
|
|
430
495
|
trackPlugin("arc-request-id");
|
|
431
496
|
}
|
|
432
497
|
if (config.arcPlugins?.health !== false) {
|
|
433
|
-
const
|
|
498
|
+
const appHealth = typeof config.arcPlugins?.health === "object" && config.arcPlugins.health !== null ? config.arcPlugins.health : {};
|
|
499
|
+
const checks = mergeHealthChecks([{
|
|
500
|
+
owner: "the module graph",
|
|
501
|
+
checks: moduleHealthChecks
|
|
502
|
+
}, {
|
|
503
|
+
owner: "arcPlugins.health",
|
|
504
|
+
checks: appHealth.checks
|
|
505
|
+
}]);
|
|
506
|
+
const healthOpts = {
|
|
507
|
+
...appHealth,
|
|
508
|
+
checks
|
|
509
|
+
};
|
|
434
510
|
await fastify.register(healthPlugin, healthOpts);
|
|
435
|
-
trackPlugin("arc-health", healthOpts);
|
|
511
|
+
trackPlugin("arc-health", { checkCount: healthOpts.checks?.length ?? 0 });
|
|
436
512
|
}
|
|
437
513
|
if (config.arcPlugins?.gracefulShutdown !== false) {
|
|
438
514
|
await fastify.register(gracefulShutdownPlugin);
|
|
@@ -445,9 +521,9 @@ async function registerArcPlugins(fastify, config, trackPlugin, modules) {
|
|
|
445
521
|
trackPlugin("arc-caching", opts);
|
|
446
522
|
}
|
|
447
523
|
if (config.arcPlugins?.queryCache) {
|
|
448
|
-
const { queryCachePlugin } = await import("./queryCachePlugin-
|
|
524
|
+
const { queryCachePlugin } = await import("./queryCachePlugin-DSGuSVLW.mjs").then((n) => n.n);
|
|
449
525
|
const opts = config.arcPlugins.queryCache === true ? {} : config.arcPlugins.queryCache;
|
|
450
|
-
const store = config.stores?.queryCache ?? new (await (import("./memory-
|
|
526
|
+
const store = config.stores?.queryCache ?? new (await (import("./memory-CmnzXE5Z.mjs").then((n) => n.n))).MemoryCacheStore();
|
|
451
527
|
await fastify.register(queryCachePlugin, {
|
|
452
528
|
store,
|
|
453
529
|
...opts
|
|
@@ -456,14 +532,14 @@ async function registerArcPlugins(fastify, config, trackPlugin, modules) {
|
|
|
456
532
|
}
|
|
457
533
|
if (config.arcPlugins?.sse) if (config.arcPlugins?.events === false) fastify.log.warn("SSE plugin requires events plugin (arcPlugins.events). SSE disabled.");
|
|
458
534
|
else {
|
|
459
|
-
const { default: ssePlugin } = await import("./sse-
|
|
535
|
+
const { default: ssePlugin } = await import("./sse-BQ1w0dfl.mjs").then((n) => n.r);
|
|
460
536
|
const opts = config.arcPlugins.sse === true ? {} : config.arcPlugins.sse;
|
|
461
537
|
await fastify.register(ssePlugin, opts);
|
|
462
538
|
trackPlugin("arc-sse", opts);
|
|
463
539
|
}
|
|
464
540
|
if (config.arcPlugins?.realtime) if (config.arcPlugins?.events === false) fastify.log.warn("Realtime plugin requires events plugin (arcPlugins.events). Realtime disabled.");
|
|
465
541
|
else {
|
|
466
|
-
const { default: realtimePlugin } = await import("./realtime-
|
|
542
|
+
const { default: realtimePlugin } = await import("./realtime-Cuw_deA-.mjs").then((n) => n.r);
|
|
467
543
|
const opts = config.arcPlugins.realtime === true ? {} : config.arcPlugins.realtime;
|
|
468
544
|
await fastify.register(realtimePlugin, opts);
|
|
469
545
|
trackPlugin("arc-realtime", opts);
|
|
@@ -475,7 +551,7 @@ async function registerArcPlugins(fastify, config, trackPlugin, modules) {
|
|
|
475
551
|
trackPlugin("arc-metrics", opts);
|
|
476
552
|
}
|
|
477
553
|
if (config.arcPlugins?.versioning) {
|
|
478
|
-
const { default: versioningPlugin } = await import("./versioning-
|
|
554
|
+
const { default: versioningPlugin } = await import("./versioning-gKWyryT3.mjs").then((n) => n.r);
|
|
479
555
|
await fastify.register(versioningPlugin, config.arcPlugins.versioning);
|
|
480
556
|
trackPlugin("arc-versioning", config.arcPlugins.versioning);
|
|
481
557
|
}
|
|
@@ -544,7 +620,7 @@ async function registerAuth(fastify, config, trackPlugin) {
|
|
|
544
620
|
*/
|
|
545
621
|
async function registerElevation(fastify, config, trackPlugin) {
|
|
546
622
|
if (!config.elevation) return;
|
|
547
|
-
const { elevationPlugin } = await import("./elevation-
|
|
623
|
+
const { elevationPlugin } = await import("./elevation-CIz1GjGm.mjs").then((n) => n.r);
|
|
548
624
|
await fastify.register(elevationPlugin, config.elevation);
|
|
549
625
|
trackPlugin("arc-elevation", config.elevation);
|
|
550
626
|
fastify.log.debug("Elevation plugin enabled");
|
|
@@ -780,9 +856,34 @@ async function registerResources(fastify, config, preResolvedModules) {
|
|
|
780
856
|
throw new Error(`[arc] module "${m.name}" afterResources() threw: ${msg}`, { cause: err });
|
|
781
857
|
}
|
|
782
858
|
}
|
|
783
|
-
|
|
859
|
+
const scheduledJobs = await collectModuleScheduledJobs(fastify, modules);
|
|
860
|
+
if (scheduledJobs.length > 0) {
|
|
861
|
+
if (config.arcPlugins?.schedules === false) throw new Error("[arc] modules declare scheduledJobs but arcPlugins.schedules is false. Enable the scheduler or remove the declarations.");
|
|
862
|
+
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.");
|
|
863
|
+
const frozenJobs = Object.freeze(scheduledJobs.map((job) => Object.freeze({ ...job })));
|
|
864
|
+
const scheduleOptions = typeof config.arcPlugins?.schedules === "object" ? config.arcPlugins.schedules : {};
|
|
865
|
+
const { default: schedulesPlugin } = await import("./schedules-CjOd2MG9.mjs").then((n) => n.r);
|
|
866
|
+
await fastify.register(schedulesPlugin, {
|
|
867
|
+
...scheduleOptions,
|
|
868
|
+
schedules: frozenJobs
|
|
869
|
+
});
|
|
870
|
+
if (fastify.arc) {
|
|
871
|
+
fastify.arc.scheduledJobs = frozenJobs;
|
|
872
|
+
fastify.arc.plugins.set("arc-schedules", {
|
|
873
|
+
name: "arc-schedules",
|
|
874
|
+
options: { scheduleCount: frozenJobs.length },
|
|
875
|
+
registeredAt: (/* @__PURE__ */ new Date()).toISOString()
|
|
876
|
+
});
|
|
877
|
+
}
|
|
878
|
+
}
|
|
879
|
+
const eventUnsubscribes = await subscribeModuleEventHandlers(fastify, modules);
|
|
880
|
+
if (config.afterResources) try {
|
|
784
881
|
await config.afterResources(fastify);
|
|
785
882
|
fastify.log.debug("afterResources hook executed");
|
|
883
|
+
} catch (err) {
|
|
884
|
+
const rollbackErrors = await unsubscribeModuleEventHandlers(eventUnsubscribes);
|
|
885
|
+
for (const rollbackError of rollbackErrors) fastify.log.error({ err: rollbackError }, "[arc] module event-handler rollback after afterResources failure failed");
|
|
886
|
+
throw err;
|
|
786
887
|
}
|
|
787
888
|
if (config.onReady) {
|
|
788
889
|
const onReady = config.onReady;
|
|
@@ -792,7 +893,9 @@ async function registerResources(fastify, config, preResolvedModules) {
|
|
|
792
893
|
}
|
|
793
894
|
const closers = modules.filter((m) => m.onClose).reverse();
|
|
794
895
|
const appOnClose = config.onClose;
|
|
795
|
-
if (closers.length || appOnClose) fastify.addHook("onClose", async () => {
|
|
896
|
+
if (closers.length || appOnClose || eventUnsubscribes.length) fastify.addHook("onClose", async () => {
|
|
897
|
+
const unsubscribeErrors = await unsubscribeModuleEventHandlers(eventUnsubscribes);
|
|
898
|
+
for (const err of unsubscribeErrors) fastify.log.error({ err }, "[arc] module event-handler shutdown unsubscribe failed");
|
|
796
899
|
for (const m of closers) await m.onClose?.(fastify);
|
|
797
900
|
if (appOnClose) await appOnClose(fastify);
|
|
798
901
|
});
|
|
@@ -1018,7 +1121,21 @@ async function registerUtilityPlugins(fastify, config) {
|
|
|
1018
1121
|
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
1122
|
if (config.underPressure !== false) {
|
|
1020
1123
|
const underPressure = await loadPlugin("underPressure");
|
|
1021
|
-
|
|
1124
|
+
const hostConfig = config.underPressure;
|
|
1125
|
+
const effective = hostConfig ?? {
|
|
1126
|
+
exposeStatusRoute: true,
|
|
1127
|
+
maxEventLoopDelay: 3e3,
|
|
1128
|
+
maxEventLoopUtilization: .98
|
|
1129
|
+
};
|
|
1130
|
+
if (config.preset === "production" && hostConfig && ![
|
|
1131
|
+
"maxEventLoopDelay",
|
|
1132
|
+
"maxEventLoopUtilization",
|
|
1133
|
+
"maxHeapUsedBytes",
|
|
1134
|
+
"maxRssBytes",
|
|
1135
|
+
"pressureHandler",
|
|
1136
|
+
"healthCheck"
|
|
1137
|
+
].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.");
|
|
1138
|
+
await fastify.register(underPressure, effective);
|
|
1022
1139
|
fastify.log.debug("Health monitoring (under-pressure) enabled");
|
|
1023
1140
|
}
|
|
1024
1141
|
if (config.sensible !== false) {
|
|
@@ -1191,6 +1308,8 @@ function validateDistributedRuntime(options) {
|
|
|
1191
1308
|
hint: "a Redis-backed query-cache adapter"
|
|
1192
1309
|
});
|
|
1193
1310
|
}
|
|
1311
|
+
const schedules = options.arcPlugins?.schedules;
|
|
1312
|
+
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
1313
|
if (missing.length > 0) {
|
|
1195
1314
|
const lines = missing.map((m) => ` • ${m.key.padEnd(20)} → ${m.hint}`).join("\n");
|
|
1196
1315
|
throw new Error(`[Arc] runtime: 'distributed' requires durable (Redis/MongoDB) adapters, not memory.
|
|
@@ -1227,9 +1346,10 @@ async function createApp(options) {
|
|
|
1227
1346
|
...options.preset ? getPreset(options.preset) : {},
|
|
1228
1347
|
...options
|
|
1229
1348
|
};
|
|
1230
|
-
const
|
|
1349
|
+
const inheritedTrustProxyDefault = options.preset === "production" && options.trustProxy === void 0;
|
|
1231
1350
|
const resolvedModules = orderModules(await Promise.all((config.modules ?? []).map((m) => resolveModule(m))));
|
|
1232
1351
|
const moduleErrorMappers = resolvedModules.flatMap((m) => m.errorMappers ?? []);
|
|
1352
|
+
const moduleHealthChecks = collectModuleHealthChecks(resolvedModules);
|
|
1233
1353
|
const resolvedLogger = resolveLoggerConfig(config.logger);
|
|
1234
1354
|
const fastify = Fastify({
|
|
1235
1355
|
logger: resolvedLogger,
|
|
@@ -1263,13 +1383,7 @@ async function createApp(options) {
|
|
|
1263
1383
|
writer: createPinoWriter(fastify.log)
|
|
1264
1384
|
});
|
|
1265
1385
|
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
|
-
}
|
|
1386
|
+
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
1387
|
fastify.removeContentTypeParser("application/json");
|
|
1274
1388
|
fastify.addContentTypeParser("application/json", { parseAs: "string" }, (_req, body, done) => {
|
|
1275
1389
|
try {
|
|
@@ -1289,7 +1403,12 @@ async function createApp(options) {
|
|
|
1289
1403
|
registeredAt: (/* @__PURE__ */ new Date()).toISOString()
|
|
1290
1404
|
});
|
|
1291
1405
|
};
|
|
1292
|
-
|
|
1406
|
+
const arcModules = await registerArcCore(fastify, config, trackPlugin);
|
|
1407
|
+
if (fastify.arc) {
|
|
1408
|
+
fastify.arc.healthChecks = Object.freeze([...moduleHealthChecks]);
|
|
1409
|
+
fastify.arc.moduleDefinitions = Object.freeze([...resolvedModules]);
|
|
1410
|
+
}
|
|
1411
|
+
await registerArcPlugins(fastify, config, trackPlugin, arcModules, moduleHealthChecks);
|
|
1293
1412
|
decorateRequestScope(fastify);
|
|
1294
1413
|
await registerAuth(fastify, config, trackPlugin);
|
|
1295
1414
|
await registerElevation(fastify, config, trackPlugin);
|
|
@@ -1347,4 +1466,48 @@ const ArcFactory = {
|
|
|
1347
1466
|
}
|
|
1348
1467
|
};
|
|
1349
1468
|
//#endregion
|
|
1350
|
-
|
|
1469
|
+
//#region src/factory/preloadResources.ts
|
|
1470
|
+
/**
|
|
1471
|
+
* Normalize an eager `import.meta.glob` result into a `ResourceLike[]`.
|
|
1472
|
+
*
|
|
1473
|
+
* Accepts either:
|
|
1474
|
+
* - `{ import: 'default' }` form: values are the resource directly
|
|
1475
|
+
* - default form: values are the full module — picks first export with `toPlugin()`
|
|
1476
|
+
*
|
|
1477
|
+
* Throws if any module doesn't yield a valid `ResourceLike`.
|
|
1478
|
+
*/
|
|
1479
|
+
function preloadResources(globResult) {
|
|
1480
|
+
const resources = [];
|
|
1481
|
+
for (const [path, value] of Object.entries(globResult)) {
|
|
1482
|
+
const resource = pickResource(value);
|
|
1483
|
+
if (!resource) throw new Error(`preloadResources: ${path} does not export a valid resource.\n Expected: a default export OR a named export with toPlugin().`);
|
|
1484
|
+
resources.push(resource);
|
|
1485
|
+
}
|
|
1486
|
+
return resources.sort((a, b) => (a.name ?? "").localeCompare(b.name ?? ""));
|
|
1487
|
+
}
|
|
1488
|
+
/**
|
|
1489
|
+
* Normalize a lazy `import.meta.glob` result into a `Promise<ResourceLike[]>`.
|
|
1490
|
+
*
|
|
1491
|
+
* Use this when resources depend on prior bootstrap (e.g., engine init) and
|
|
1492
|
+
* cannot be evaluated at import time of the preload file.
|
|
1493
|
+
*/
|
|
1494
|
+
async function preloadResourcesAsync(globResult) {
|
|
1495
|
+
return (await Promise.all(Object.entries(globResult).map(async ([path, loader]) => {
|
|
1496
|
+
const resource = pickResource(await loader());
|
|
1497
|
+
if (!resource) throw new Error(`preloadResourcesAsync: ${path} does not export a valid resource.\n Expected: a default export OR a named export with toPlugin().`);
|
|
1498
|
+
return resource;
|
|
1499
|
+
}))).sort((a, b) => (a.name ?? "").localeCompare(b.name ?? ""));
|
|
1500
|
+
}
|
|
1501
|
+
function pickResource(value) {
|
|
1502
|
+
if (!value || typeof value !== "object") return void 0;
|
|
1503
|
+
if (typeof value.toPlugin === "function") return value;
|
|
1504
|
+
const mod = value;
|
|
1505
|
+
const candidates = [
|
|
1506
|
+
mod.default,
|
|
1507
|
+
mod.resource,
|
|
1508
|
+
...Object.values(mod)
|
|
1509
|
+
];
|
|
1510
|
+
for (const c of candidates) if (c && typeof c === "object" && typeof c.toPlugin === "function") return c;
|
|
1511
|
+
}
|
|
1512
|
+
//#endregion
|
|
1513
|
+
export { collectModuleScheduledJobs as _, createApp_exports as a, resolveModule as b, getPreset as c, workerPreset as d, defineModule as f, collectModuleHealthChecks as g, subscribeModuleEventHandlers as h, createApp as i, productionPreset as l, orderModules as m, preloadResourcesAsync as n, developmentPreset as o, getModuleExports as p, ArcFactory as r, edgePreset as s, preloadResources as t, testingPreset as u, collectModuleWorkflows as v, resolveContribution as y };
|
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
import { Y as PresetResult } from "../index-
|
|
2
|
-
import { i as RequestScope } from "../types-
|
|
3
|
-
import { t as PermissionCheck } from "../types-
|
|
1
|
+
import { Y as PresetResult } from "../index-DPRH0Cme.mjs";
|
|
2
|
+
import { i as RequestScope } from "../types-DjC-KUCz.mjs";
|
|
3
|
+
import { t as PermissionCheck } from "../types-Dmw-6Qeo.mjs";
|
|
4
4
|
import { a as StorageReadResult, i as StorageReadRange, n as StorageContext, o as StorageUploadInput, r as StorageFile, t as Storage } from "../storage-DLUZ04kK.mjs";
|
|
5
5
|
//#region src/presets/filesUpload.d.ts
|
|
6
6
|
interface FilesUploadPresetRoutes {
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { i as NotFoundError, u as ValidationError } from "../errors-DkO8Nk6-.mjs";
|
|
2
|
-
import { c as getOrgId, g as getUserId } from "../types-
|
|
3
|
-
import {
|
|
4
|
-
import { t as multipartBody } from "../multipartBody-
|
|
2
|
+
import { c as getOrgId, g as getUserId } from "../types-CSyCxl2V.mjs";
|
|
3
|
+
import { A as requireAuth, E as allowPublic } from "../permissions-BrtiBKGs.mjs";
|
|
4
|
+
import { t as multipartBody } from "../multipartBody-D0eBvkeq.mjs";
|
|
5
5
|
//#region src/presets/filesUpload.ts
|
|
6
6
|
const DEFAULT_FIELD_NAME = "file";
|
|
7
7
|
const DEFAULT_MAX_FILE_SIZE = 10 * 1024 * 1024;
|
package/dist/presets/index.d.mts
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { Rt as IControllerResponse, U as ResourceConfig, Y as PresetResult, d as PaginationResult, zt as IRequestContext } from "../index-DPRH0Cme.mjs";
|
|
2
|
+
import { c as AnyRecord } from "../types-Dmw-6Qeo.mjs";
|
|
2
3
|
import { FilesUploadPresetOptions, FilesUploadPresetPermissions, FilesUploadPresetRoutes, filesUploadPreset } from "./filesUpload.mjs";
|
|
3
4
|
import { MultiTenantOptions, TenantFieldSpec, multiTenantPreset } from "./multiTenant.mjs";
|
|
4
5
|
import { SearchHandler, SearchPresetOptions, SearchRouteConfig, searchPreset } from "./search.mjs";
|
package/dist/presets/index.mjs
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { multiTenantPreset } from "./multiTenant.mjs";
|
|
2
|
-
import { a as registerPreset, c as auditedPreset, d as ownedByUserPreset, i as getPreset, l as softDeletePreset, n as flexibleMultiTenantPreset, o as treePreset, r as getAvailablePresets, s as bulkPreset, t as applyPresets, u as slugLookupPreset } from "../presets-
|
|
2
|
+
import { a as registerPreset, c as auditedPreset, d as ownedByUserPreset, i as getPreset, l as softDeletePreset, n as flexibleMultiTenantPreset, o as treePreset, r as getAvailablePresets, s as bulkPreset, t as applyPresets, u as slugLookupPreset } from "../presets-DwFDVKP0.mjs";
|
|
3
3
|
import { filesUploadPreset } from "./filesUpload.mjs";
|
|
4
4
|
import { searchPreset } from "./search.mjs";
|
|
5
5
|
export { applyPresets, auditedPreset, bulkPreset, filesUploadPreset, flexibleMultiTenantPreset, getAvailablePresets, getPreset, multiTenantPreset, ownedByUserPreset, registerPreset, searchPreset, slugLookupPreset, softDeletePreset, treePreset };
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { X as CrudRouteKey, Y as PresetResult } from "../index-
|
|
1
|
+
import { X as CrudRouteKey, Y as PresetResult } from "../index-DPRH0Cme.mjs";
|
|
2
2
|
//#region src/presets/multiTenant.d.ts
|
|
3
3
|
/**
|
|
4
4
|
* One tenant dimension for multi-field filtering. Discriminated by source:
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import "../constants-DNre0wRg.mjs";
|
|
2
|
-
import { b as isElevated, c as getOrgId, d as getScopeContext, m as getTeamId, u as getRequestScope, v as hasOrgAccess } from "../types-
|
|
2
|
+
import { b as isElevated, c as getOrgId, d as getScopeContext, m as getTeamId, u as getRequestScope, v as hasOrgAccess } from "../types-CSyCxl2V.mjs";
|
|
3
3
|
//#region src/presets/multiTenant.ts
|
|
4
4
|
/**
|
|
5
5
|
* Resolve a single TenantFieldSpec against the current scope.
|
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import { At as RouteDefinition, Pt as ControllerHandler, Y as PresetResult, jt as RouteMcpConfig } from "../index-
|
|
2
|
-
import { t as PermissionCheck } from "../types-
|
|
1
|
+
import { At as RouteDefinition, Pt as ControllerHandler, Y as PresetResult, jt as RouteMcpConfig } from "../index-DPRH0Cme.mjs";
|
|
2
|
+
import { t as PermissionCheck } from "../types-Dmw-6Qeo.mjs";
|
|
3
3
|
//#region src/presets/search.d.ts
|
|
4
4
|
/**
|
|
5
5
|
* Search Preset — backend-agnostic search / vector / embed routes
|
package/dist/presets/search.mjs
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
import { b as isElevated, n as PUBLIC_SCOPE } from "./types-
|
|
1
|
+
import { b as isElevated, n as PUBLIC_SCOPE } from "./types-CSyCxl2V.mjs";
|
|
2
|
+
import { A as requireAuth, E as allowPublic, M as requireRoles } from "./permissions-BrtiBKGs.mjs";
|
|
2
3
|
import { multiTenantPreset } from "./presets/multiTenant.mjs";
|
|
3
|
-
import { C as allowPublic, D as requireAuth, k as requireRoles } from "./permissions-tbxFHxjZ.mjs";
|
|
4
4
|
//#region src/presets/ownedByUser.ts
|
|
5
5
|
/**
|
|
6
6
|
* Create ownership check middleware.
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { t as __exportAll } from "./rolldown-runtime-8H4AJuhK.mjs";
|
|
2
|
-
import { t as MemoryCacheStore } from "./memory-CLXA2VeJ.mjs";
|
|
3
2
|
import { t as hasEvents } from "./typeGuards-C9KXUly6.mjs";
|
|
4
3
|
import { i as versionKey, r as tagVersionKey } from "./keys-Cd316u_y.mjs";
|
|
4
|
+
import { t as MemoryCacheStore } from "./memory-CmnzXE5Z.mjs";
|
|
5
5
|
import fp from "fastify-plugin";
|
|
6
6
|
//#region src/cache/QueryCache.ts
|
|
7
7
|
var QueryCache = class {
|
|
@@ -33,7 +33,9 @@ var QueryCache = class {
|
|
|
33
33
|
};
|
|
34
34
|
}
|
|
35
35
|
async set(key, data, config) {
|
|
36
|
-
const
|
|
36
|
+
const baseStaleTimeSec = config.staleTime ?? 0;
|
|
37
|
+
const jitter = config.jitter ?? 0;
|
|
38
|
+
const staleTimeSec = jitter > 0 ? Math.max(0, baseStaleTimeSec * (1 + (Math.random() * 2 - 1) * jitter)) : baseStaleTimeSec;
|
|
37
39
|
const totalTtlSec = staleTimeSec + (config.gcTime ?? 60);
|
|
38
40
|
const now = Date.now();
|
|
39
41
|
const envelope = {
|
|
@@ -54,9 +56,7 @@ var QueryCache = class {
|
|
|
54
56
|
}
|
|
55
57
|
/** Bump resource version — orphans all cached queries for this resource */
|
|
56
58
|
async bumpResourceVersion(resource) {
|
|
57
|
-
|
|
58
|
-
const newVersion = Date.now();
|
|
59
|
-
await this.store.set(key, newVersion, 1440 * 60);
|
|
59
|
+
await this.bumpVersion(versionKey(resource));
|
|
60
60
|
}
|
|
61
61
|
/** Get current version for a tag */
|
|
62
62
|
async getTagVersion(tag) {
|
|
@@ -64,11 +64,42 @@ var QueryCache = class {
|
|
|
64
64
|
}
|
|
65
65
|
/** Bump tag version — orphans all cached queries tagged with this tag */
|
|
66
66
|
async bumpTagVersion(tag) {
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
67
|
+
await this.bumpVersion(tagVersionKey(tag));
|
|
68
|
+
}
|
|
69
|
+
/**
|
|
70
|
+
* Version-bump semantics:
|
|
71
|
+
*
|
|
72
|
+
* - `store.increment` present (Redis `INCR`) — atomic and strictly
|
|
73
|
+
* monotonic across replicas; concurrent bumps can never collide or
|
|
74
|
+
* go backwards.
|
|
75
|
+
* - fallback — read-modify-write with `max(now, current + 1)`: two
|
|
76
|
+
* bumps in the same millisecond and clock regressions still advance
|
|
77
|
+
* the version. Concurrent bumps from DIFFERENT replicas can lose one
|
|
78
|
+
* write, which is why distributed hosts should use an incrementing
|
|
79
|
+
* store (`runtime: 'distributed'` already requires a shared one).
|
|
80
|
+
*
|
|
81
|
+
* Version keys must outlive every derived cache entry — that invariant
|
|
82
|
+
* is what makes version invalidation sound (a version key expiring back
|
|
83
|
+
* to 0 would make entries written under the pre-reset version
|
|
84
|
+
* addressable again). The 1-year TTL applies with the repo-core
|
|
85
|
+
* TTL-ON-CREATE semantics, so a version key expires one year after its
|
|
86
|
+
* FIRST bump — safe, because a revived collision would need an entry to
|
|
87
|
+
* outlive that reset and entry TTLs are minutes-scale (staleTime +
|
|
88
|
+
* gcTime). The read-modify-write fallback uses plain `set`, which
|
|
89
|
+
* refreshes the TTL per bump — strictly longer-lived, equally safe.
|
|
90
|
+
*/
|
|
91
|
+
async bumpVersion(key) {
|
|
92
|
+
if (typeof this.store.increment === "function") {
|
|
93
|
+
await this.store.increment(key, 1, VERSION_TTL_SECONDS);
|
|
94
|
+
return;
|
|
95
|
+
}
|
|
96
|
+
const current = await this.store.get(key);
|
|
97
|
+
const next = Math.max(Date.now(), (current ?? 0) + 1);
|
|
98
|
+
await this.store.set(key, next, VERSION_TTL_SECONDS);
|
|
70
99
|
}
|
|
71
100
|
};
|
|
101
|
+
/** See `bumpVersion` — effectively non-expiring, refreshed on every bump. */
|
|
102
|
+
const VERSION_TTL_SECONDS = 365 * 24 * 60 * 60;
|
|
72
103
|
//#endregion
|
|
73
104
|
//#region src/cache/queryCachePlugin.ts
|
|
74
105
|
var queryCachePlugin_exports = /* @__PURE__ */ __exportAll({ queryCachePlugin: () => queryCachePlugin });
|
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import { r as CacheStore } from "./interface-
|
|
2
|
-
import { i as QueryCache } from "./QueryCache-
|
|
1
|
+
import { r as CacheStore } from "./interface-BGZCzOGL.mjs";
|
|
2
|
+
import { i as QueryCache } from "./QueryCache-DOMT_iq7.mjs";
|
|
3
3
|
import { FastifyPluginAsync } from "fastify";
|
|
4
4
|
//#region src/cache/queryCachePlugin.d.ts
|
|
5
5
|
interface QueryCachePluginOptions {
|