@classytic/arc 2.3.0 → 2.4.2
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 +187 -18
- package/bin/arc.js +11 -3
- package/dist/BaseController-CkM5dUh_.mjs +1031 -0
- package/dist/{EventTransport-BkUDYZEb.d.mts → EventTransport-wc5hSLik.d.mts} +1 -1
- package/dist/{HookSystem-BsGV-j2l.mjs → HookSystem-COkyWztM.mjs} +2 -3
- package/dist/{ResourceRegistry-7Ic20ZMw.mjs → ResourceRegistry-DeCIFlix.mjs} +8 -5
- package/dist/adapters/index.d.mts +3 -5
- package/dist/adapters/index.mjs +2 -3
- package/dist/{prisma-DJbMt3yf.mjs → adapters-DTC4Ug66.mjs} +45 -12
- package/dist/audit/index.d.mts +4 -7
- package/dist/audit/index.mjs +2 -29
- package/dist/audit/mongodb.d.mts +1 -4
- package/dist/audit/mongodb.mjs +2 -3
- package/dist/auth/index.d.mts +7 -9
- package/dist/auth/index.mjs +65 -63
- package/dist/auth/redis-session.d.mts +1 -1
- package/dist/auth/redis-session.mjs +1 -2
- package/dist/{betterAuthOpenApi-DjWDddNc.mjs → betterAuthOpenApi-lz0IRbXJ.mjs} +4 -6
- package/dist/cache/index.d.mts +23 -23
- package/dist/cache/index.mjs +4 -6
- package/dist/{caching-GSDJcA6-.mjs → caching-BSXB-Xr7.mjs} +2 -24
- package/dist/chunk-BpYLSNr0.mjs +14 -0
- package/dist/circuitBreaker-BOBOpN2w.mjs +284 -0
- package/dist/circuitBreaker-JP2GdJ4b.d.mts +206 -0
- package/dist/cli/commands/describe.mjs +24 -7
- package/dist/cli/commands/docs.mjs +6 -7
- package/dist/cli/commands/doctor.d.mts +10 -0
- package/dist/cli/commands/doctor.mjs +156 -0
- package/dist/cli/commands/generate.mjs +66 -17
- package/dist/cli/commands/init.mjs +315 -45
- package/dist/cli/commands/introspect.mjs +2 -4
- package/dist/cli/index.d.mts +1 -10
- package/dist/cli/index.mjs +4 -153
- package/dist/{constants-DdXFXQtN.mjs → constants-Cxde4rpC.mjs} +1 -2
- package/dist/core/index.d.mts +3 -5
- package/dist/core/index.mjs +5 -4
- package/dist/core-C1XCMtqM.mjs +185 -0
- package/dist/{createApp-CgKOPhA4.mjs → createApp-ByWNRsZj.mjs} +64 -35
- package/dist/{defineResource-DWbpJYtm.mjs → defineResource-D9aY5Cy6.mjs} +108 -1157
- package/dist/discovery/index.mjs +37 -5
- package/dist/docs/index.d.mts +6 -9
- package/dist/docs/index.mjs +3 -21
- package/dist/dynamic/index.d.mts +93 -0
- package/dist/dynamic/index.mjs +122 -0
- package/dist/{elevation-DSTbVvYj.mjs → elevation-BEdACOLB.mjs} +5 -36
- package/dist/{elevation-DGo5shaX.d.mts → elevation-Ca_yveIO.d.mts} +41 -7
- package/dist/{errorHandler-C3GY3_ow.mjs → errorHandler--zp54tGc.mjs} +3 -5
- package/dist/errorHandler-Do4vVQ1f.d.mts +139 -0
- package/dist/{errors-DBANPbGr.mjs → errors-rxhfP7Hf.mjs} +1 -2
- package/dist/{eventPlugin-BEOvaDqo.mjs → eventPlugin-Ba00swHF.mjs} +25 -27
- package/dist/{eventPlugin-H6wDDjGO.d.mts → eventPlugin-iGrSEmwJ.d.mts} +105 -5
- package/dist/events/index.d.mts +72 -7
- package/dist/events/index.mjs +216 -4
- package/dist/events/transports/redis-stream-entry.d.mts +1 -1
- package/dist/events/transports/redis-stream-entry.mjs +19 -7
- package/dist/events/transports/redis.d.mts +1 -1
- package/dist/events/transports/redis.mjs +3 -4
- package/dist/factory/index.d.mts +23 -9
- package/dist/factory/index.mjs +48 -3
- package/dist/{fields-Bi_AVKSo.d.mts → fields-DFwdaWCq.d.mts} +1 -1
- package/dist/{fields-CTd_CrKr.mjs → fields-ipsbIRPK.mjs} +1 -2
- package/dist/hooks/index.d.mts +1 -3
- package/dist/hooks/index.mjs +2 -3
- package/dist/idempotency/index.d.mts +5 -5
- package/dist/idempotency/index.mjs +3 -7
- package/dist/idempotency/mongodb.d.mts +1 -1
- package/dist/idempotency/mongodb.mjs +4 -5
- package/dist/idempotency/redis.d.mts +1 -1
- package/dist/idempotency/redis.mjs +2 -5
- package/dist/{fastifyAdapter-6b_eRDBw.d.mts → index-BL8CaQih.d.mts} +56 -57
- package/dist/index-Diqcm14c.d.mts +369 -0
- package/dist/{prisma-Dy5S5F5i.d.mts → index-yhxyjqNb.d.mts} +4 -5
- package/dist/index.d.mts +100 -105
- package/dist/index.mjs +85 -58
- package/dist/integrations/event-gateway.d.mts +1 -1
- package/dist/integrations/event-gateway.mjs +8 -4
- package/dist/integrations/index.d.mts +4 -2
- package/dist/integrations/index.mjs +1 -1
- package/dist/integrations/jobs.d.mts +2 -2
- package/dist/integrations/jobs.mjs +63 -14
- package/dist/integrations/mcp/index.d.mts +219 -0
- package/dist/integrations/mcp/index.mjs +572 -0
- package/dist/integrations/mcp/testing.d.mts +53 -0
- package/dist/integrations/mcp/testing.mjs +104 -0
- package/dist/integrations/streamline.mjs +39 -19
- package/dist/integrations/webhooks.d.mts +56 -0
- package/dist/integrations/webhooks.mjs +139 -0
- package/dist/integrations/websocket-redis.d.mts +46 -0
- package/dist/integrations/websocket-redis.mjs +50 -0
- package/dist/integrations/websocket.d.mts +68 -2
- package/dist/integrations/websocket.mjs +96 -13
- package/dist/{interface-CSNjltAc.d.mts → interface-B4awm1RJ.d.mts} +2 -2
- package/dist/interface-DGmPxakH.d.mts +2213 -0
- package/dist/{keys-DhqDRxv3.mjs → keys-qcD-TVJl.mjs} +3 -4
- package/dist/{logger-ByrvQWZO.mjs → logger-Dz3j1ItV.mjs} +2 -4
- package/dist/{memory-B2v7KrCB.mjs → memory-Cb_7iy9e.mjs} +2 -4
- package/dist/metrics-Csh4nsvv.mjs +224 -0
- package/dist/migrations/index.d.mts +113 -44
- package/dist/migrations/index.mjs +84 -102
- package/dist/{mongodb-DNKEExbf.mjs → mongodb-BuQ7fNTg.mjs} +1 -4
- package/dist/{mongodb-ClykrfGo.d.mts → mongodb-CUpYfxfD.d.mts} +2 -3
- package/dist/{mongodb-Dg8O_gvd.d.mts → mongodb-bga9AbkD.d.mts} +2 -2
- package/dist/{openapi-9nB_kiuR.mjs → openapi-CBmZ6EQN.mjs} +4 -21
- package/dist/org/index.d.mts +12 -14
- package/dist/org/index.mjs +92 -119
- package/dist/org/types.d.mts +2 -2
- package/dist/org/types.mjs +1 -1
- package/dist/permissions/index.d.mts +4 -278
- package/dist/permissions/index.mjs +4 -579
- package/dist/permissions-CA5zg0yK.mjs +751 -0
- package/dist/plugins/index.d.mts +104 -107
- package/dist/plugins/index.mjs +203 -313
- package/dist/plugins/response-cache.mjs +4 -69
- package/dist/plugins/tracing-entry.d.mts +1 -1
- package/dist/plugins/tracing-entry.mjs +24 -11
- package/dist/{pluralize-CM-jZg7p.mjs → pluralize-CcT6qF0a.mjs} +12 -13
- package/dist/policies/index.d.mts +2 -2
- package/dist/policies/index.mjs +80 -83
- package/dist/presets/index.d.mts +26 -19
- package/dist/presets/index.mjs +2 -142
- package/dist/presets/multiTenant.d.mts +1 -4
- package/dist/presets/multiTenant.mjs +4 -6
- package/dist/presets-C9QXJV1u.mjs +422 -0
- package/dist/{queryCachePlugin-B6R0d4av.mjs → queryCachePlugin-ClosZdNS.mjs} +6 -27
- package/dist/{queryCachePlugin-Q6SYuHZ6.d.mts → queryCachePlugin-DcmETvcB.d.mts} +3 -3
- package/dist/queryParser-CgCtsjti.mjs +352 -0
- package/dist/{redis-UwjEp8Ea.d.mts → redis-CQ5YxMC5.d.mts} +2 -2
- package/dist/{redis-stream-CBg0upHI.d.mts → redis-stream-BW9UKLZM.d.mts} +9 -2
- package/dist/registry/index.d.mts +1 -4
- package/dist/registry/index.mjs +3 -4
- package/dist/{introspectionPlugin-B3JkrjwU.mjs → registry-I-ogLgL9.mjs} +1 -8
- package/dist/{requestContext-xi6OKBL-.mjs → requestContext-DYtmNpm5.mjs} +1 -3
- package/dist/resourceToTools-PMFE8HIv.mjs +533 -0
- package/dist/rpc/index.d.mts +90 -0
- package/dist/rpc/index.mjs +248 -0
- package/dist/{schemaConverter-Dtg0Kt9T.mjs → schemaConverter-DjzHpFam.mjs} +1 -2
- package/dist/schemas/index.d.mts +30 -30
- package/dist/schemas/index.mjs +2 -4
- package/dist/scope/index.d.mts +13 -2
- package/dist/scope/index.mjs +18 -5
- package/dist/{sessionManager-D_iEHjQl.d.mts → sessionManager-wbkYj2HL.d.mts} +2 -2
- package/dist/{sse-DkqQ1uxb.mjs → sse-BkViJPlT.mjs} +4 -25
- package/dist/testing/index.d.mts +551 -567
- package/dist/testing/index.mjs +1744 -1799
- package/dist/{tracing-8CEbhF0w.d.mts → tracing-bz_U4EM1.d.mts} +6 -1
- package/dist/{typeGuards-DwxA1t_L.mjs → typeGuards-Cj5Rgvlg.mjs} +1 -2
- package/dist/types/index.d.mts +4 -946
- package/dist/types/index.mjs +2 -4
- package/dist/types-BJmgxNbF.d.mts +275 -0
- package/dist/{types-RLkFVgaw.d.mts → types-BNUccdcf.d.mts} +2 -2
- package/dist/{types-Beqn1Un7.mjs → types-C6TQjtdi.mjs} +30 -2
- package/dist/{types-tKwaViYB.d.mts → types-Dt0-AI6E.d.mts} +68 -27
- package/dist/{types-DelU6kln.mjs → types-ZUu_h0jp.mjs} +1 -2
- package/dist/utils/index.d.mts +254 -351
- package/dist/utils/index.mjs +7 -6
- package/dist/utils-Dc0WhlIl.mjs +594 -0
- package/dist/versioning-BzfeHmhj.mjs +37 -0
- package/package.json +44 -10
- package/skills/arc/SKILL.md +518 -0
- package/skills/arc/references/auth.md +250 -0
- package/skills/arc/references/events.md +272 -0
- package/skills/arc/references/integrations.md +385 -0
- package/skills/arc/references/mcp.md +431 -0
- package/skills/arc/references/production.md +610 -0
- package/skills/arc/references/testing.md +183 -0
- package/dist/audited-CGdLiSlE.mjs +0 -140
- package/dist/chunk-C7Uep-_p.mjs +0 -20
- package/dist/circuitBreaker-CSS2VvL6.mjs +0 -1109
- package/dist/errorHandler-CW3OOeYq.d.mts +0 -72
- package/dist/interface-BtdYtQUA.d.mts +0 -1114
- package/dist/presets-BTeYbw7h.d.mts +0 -57
- package/dist/presets-CeFtfDR8.mjs +0 -119
- /package/dist/{errors-DAWRdiYP.d.mts → errors-CPpvPHT0.d.mts} +0 -0
- /package/dist/{externalPaths-SyPF2tgK.d.mts → externalPaths-DpO-s7r8.d.mts} +0 -0
- /package/dist/{interface-DTbsvIWe.d.mts → interface-D_BWALyZ.d.mts} +0 -0
package/dist/plugins/index.mjs
CHANGED
|
@@ -1,75 +1,191 @@
|
|
|
1
|
-
import { p as MUTATION_OPERATIONS } from "../constants-
|
|
2
|
-
import { r as getOrgId } from "../types-
|
|
3
|
-
import { t as
|
|
4
|
-
import { t as hasEvents } from "../typeGuards-
|
|
5
|
-
import { t as
|
|
6
|
-
import { t as ResourceRegistry } from "../ResourceRegistry-
|
|
7
|
-
import { t as
|
|
8
|
-
import {
|
|
9
|
-
import { n as
|
|
1
|
+
import { p as MUTATION_OPERATIONS } from "../constants-Cxde4rpC.mjs";
|
|
2
|
+
import { r as getOrgId } from "../types-C6TQjtdi.mjs";
|
|
3
|
+
import { t as requestContext } from "../requestContext-DYtmNpm5.mjs";
|
|
4
|
+
import { t as hasEvents } from "../typeGuards-Cj5Rgvlg.mjs";
|
|
5
|
+
import { t as HookSystem } from "../HookSystem-COkyWztM.mjs";
|
|
6
|
+
import { t as ResourceRegistry } from "../ResourceRegistry-DeCIFlix.mjs";
|
|
7
|
+
import { n as caching_default, t as cachingPlugin } from "../caching-BSXB-Xr7.mjs";
|
|
8
|
+
import { t as errorHandlerPlugin } from "../errorHandler--zp54tGc.mjs";
|
|
9
|
+
import { n as metrics_default, t as metricsPlugin } from "../metrics-Csh4nsvv.mjs";
|
|
10
|
+
import { n as sse_default, t as ssePlugin } from "../sse-BkViJPlT.mjs";
|
|
11
|
+
import { n as versioning_default, t as versioningPlugin } from "../versioning-BzfeHmhj.mjs";
|
|
12
|
+
import { randomUUID } from "node:crypto";
|
|
10
13
|
import fp from "fastify-plugin";
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
+
//#region src/core/arcCorePlugin.ts
|
|
15
|
+
const arcCorePlugin = async (fastify, opts = {}) => {
|
|
16
|
+
const { emitEvents = true, hookSystem, registry } = opts;
|
|
17
|
+
const actualHookSystem = hookSystem ?? new HookSystem();
|
|
18
|
+
const actualRegistry = registry ?? new ResourceRegistry();
|
|
19
|
+
fastify.decorate("arc", {
|
|
20
|
+
hooks: actualHookSystem,
|
|
21
|
+
registry: actualRegistry,
|
|
22
|
+
emitEvents,
|
|
23
|
+
externalOpenApiPaths: [],
|
|
24
|
+
plugins: /* @__PURE__ */ new Map()
|
|
25
|
+
});
|
|
26
|
+
fastify.addHook("onRequest", (request, _reply, done) => {
|
|
27
|
+
const store = {
|
|
28
|
+
requestId: request.id,
|
|
29
|
+
startTime: performance.now()
|
|
30
|
+
};
|
|
31
|
+
requestContext.storage.run(store, done);
|
|
32
|
+
});
|
|
33
|
+
fastify.addHook("preHandler", (request, _reply, done) => {
|
|
34
|
+
const store = requestContext.get();
|
|
35
|
+
if (store) {
|
|
36
|
+
store.user = request.user ?? null;
|
|
37
|
+
store.organizationId = request.scope?.kind === "member" ? request.scope.organizationId : request.scope?.kind === "elevated" ? request.scope.organizationId : void 0;
|
|
38
|
+
}
|
|
39
|
+
done();
|
|
40
|
+
});
|
|
41
|
+
if (emitEvents) {
|
|
42
|
+
const eventOperations = MUTATION_OPERATIONS;
|
|
43
|
+
for (const operation of eventOperations) actualHookSystem.after("*", operation, async (ctx) => {
|
|
44
|
+
if (!hasEvents(fastify)) return;
|
|
45
|
+
const store = requestContext.get();
|
|
46
|
+
const eventType = `${ctx.resource}.${operation}d`;
|
|
47
|
+
const userId = ctx.user?.id ?? ctx.user?._id;
|
|
48
|
+
const organizationId = ctx.context?._scope ? getOrgId(ctx.context._scope) : void 0;
|
|
49
|
+
const payload = {
|
|
50
|
+
resource: ctx.resource,
|
|
51
|
+
operation: ctx.operation,
|
|
52
|
+
data: ctx.result,
|
|
53
|
+
userId,
|
|
54
|
+
organizationId,
|
|
55
|
+
timestamp: (/* @__PURE__ */ new Date()).toISOString()
|
|
56
|
+
};
|
|
57
|
+
try {
|
|
58
|
+
await fastify.events.publish(eventType, payload, {
|
|
59
|
+
correlationId: store?.requestId,
|
|
60
|
+
resource: ctx.resource,
|
|
61
|
+
resourceId: extractId(ctx.result),
|
|
62
|
+
userId: userId ? String(userId) : void 0,
|
|
63
|
+
organizationId
|
|
64
|
+
});
|
|
65
|
+
} catch (error) {
|
|
66
|
+
fastify.log?.warn?.({
|
|
67
|
+
eventType,
|
|
68
|
+
error
|
|
69
|
+
}, "Failed to emit event");
|
|
70
|
+
}
|
|
71
|
+
});
|
|
72
|
+
}
|
|
73
|
+
fastify.addHook("onReady", async () => {
|
|
74
|
+
if (!hasEvents(fastify)) return;
|
|
75
|
+
try {
|
|
76
|
+
await fastify.events.publish("arc.ready", {
|
|
77
|
+
resources: actualRegistry.getAll().length,
|
|
78
|
+
hooks: actualHookSystem.getAll().length,
|
|
79
|
+
timestamp: (/* @__PURE__ */ new Date()).toISOString()
|
|
80
|
+
});
|
|
81
|
+
} catch {}
|
|
82
|
+
});
|
|
83
|
+
fastify.addHook("onClose", async () => {
|
|
84
|
+
actualHookSystem.clear();
|
|
85
|
+
actualRegistry._clear();
|
|
86
|
+
});
|
|
87
|
+
fastify.log?.debug?.("Arc core plugin enabled (instance-scoped hooks & registry)");
|
|
88
|
+
};
|
|
89
|
+
/** Extract document ID from a result (handles Mongoose docs and plain objects) */
|
|
90
|
+
function extractId(doc) {
|
|
91
|
+
if (!doc || typeof doc !== "object") return void 0;
|
|
92
|
+
const d = doc;
|
|
93
|
+
const rawId = d._id ?? d.id;
|
|
94
|
+
return rawId ? String(rawId) : void 0;
|
|
95
|
+
}
|
|
96
|
+
var arcCorePlugin_default = fp(arcCorePlugin, {
|
|
97
|
+
name: "arc-core",
|
|
98
|
+
fastify: "5.x"
|
|
99
|
+
});
|
|
100
|
+
//#endregion
|
|
101
|
+
//#region src/plugins/createPlugin.ts
|
|
14
102
|
/**
|
|
15
|
-
*
|
|
16
|
-
*
|
|
17
|
-
* Propagates request IDs for distributed tracing.
|
|
18
|
-
* - Accepts incoming x-request-id header
|
|
19
|
-
* - Generates UUID if not provided
|
|
20
|
-
* - Attaches to request.id and response header
|
|
21
|
-
*
|
|
22
|
-
* @example
|
|
23
|
-
* import { requestIdPlugin } from '@classytic/arc';
|
|
24
|
-
*
|
|
25
|
-
* await fastify.register(requestIdPlugin);
|
|
103
|
+
* Create a structured plugin with forRoot (global) and forResource (per-resource) support.
|
|
26
104
|
*
|
|
27
|
-
*
|
|
28
|
-
*
|
|
29
|
-
*
|
|
30
|
-
* });
|
|
105
|
+
* @param name - Plugin name (used for Fastify registration and debugging)
|
|
106
|
+
* @param definition - Plugin setup functions
|
|
107
|
+
* @returns ArcPlugin with forRoot() and forResource() methods
|
|
31
108
|
*/
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
109
|
+
function createPlugin(name, definition) {
|
|
110
|
+
return {
|
|
111
|
+
name,
|
|
112
|
+
forRoot(opts) {
|
|
113
|
+
const plugin = async (fastify, pluginOpts) => {
|
|
114
|
+
const mergedOpts = {
|
|
115
|
+
...opts,
|
|
116
|
+
...pluginOpts
|
|
117
|
+
};
|
|
118
|
+
if (definition.forRoot) await definition.forRoot(fastify, mergedOpts);
|
|
119
|
+
};
|
|
120
|
+
return fp(plugin, {
|
|
121
|
+
name: `arc-plugin-${name}`,
|
|
122
|
+
fastify: "5.x"
|
|
123
|
+
});
|
|
124
|
+
},
|
|
125
|
+
forResource(opts) {
|
|
126
|
+
if (!definition.forResource) return {};
|
|
127
|
+
return definition.forResource({}, opts ?? {});
|
|
128
|
+
}
|
|
129
|
+
};
|
|
130
|
+
}
|
|
131
|
+
//#endregion
|
|
132
|
+
//#region src/plugins/gracefulShutdown.ts
|
|
133
|
+
const gracefulShutdownPlugin = async (fastify, opts = {}) => {
|
|
134
|
+
const { timeout = 3e4, onShutdown, signals = ["SIGTERM", "SIGINT"], logEvents = true, onForceExit = () => process.exit(1) } = opts;
|
|
135
|
+
let isShuttingDown = false;
|
|
136
|
+
const signalHandlers = /* @__PURE__ */ new Map();
|
|
137
|
+
const shutdown = async (signal) => {
|
|
138
|
+
if (isShuttingDown) {
|
|
139
|
+
if (logEvents) fastify.log?.warn?.({ signal }, "Shutdown already in progress, ignoring signal");
|
|
140
|
+
return;
|
|
141
|
+
}
|
|
142
|
+
isShuttingDown = true;
|
|
143
|
+
if (logEvents) fastify.log?.info?.({
|
|
144
|
+
signal,
|
|
145
|
+
timeout
|
|
146
|
+
}, "Shutdown signal received, starting graceful shutdown");
|
|
147
|
+
const forceExitTimer = setTimeout(() => {
|
|
148
|
+
if (logEvents) fastify.log?.error?.("Graceful shutdown timeout exceeded, forcing exit");
|
|
149
|
+
onForceExit("timeout");
|
|
150
|
+
}, timeout);
|
|
151
|
+
forceExitTimer.unref();
|
|
152
|
+
try {
|
|
153
|
+
if (logEvents) fastify.log?.info?.("Closing server to new connections");
|
|
154
|
+
await fastify.close();
|
|
155
|
+
if (onShutdown) {
|
|
156
|
+
if (logEvents) fastify.log?.info?.("Running custom shutdown handler");
|
|
157
|
+
await onShutdown();
|
|
158
|
+
}
|
|
159
|
+
if (logEvents) fastify.log?.info?.("Graceful shutdown complete");
|
|
160
|
+
clearTimeout(forceExitTimer);
|
|
161
|
+
} catch (err) {
|
|
162
|
+
if (logEvents) fastify.log?.error?.({ error: err.message }, "Error during shutdown");
|
|
163
|
+
clearTimeout(forceExitTimer);
|
|
164
|
+
onForceExit("error");
|
|
165
|
+
}
|
|
166
|
+
};
|
|
167
|
+
for (const signal of signals) {
|
|
168
|
+
const handler = () => {
|
|
169
|
+
shutdown(signal);
|
|
170
|
+
};
|
|
171
|
+
signalHandlers.set(signal, handler);
|
|
172
|
+
process.on(signal, handler);
|
|
173
|
+
}
|
|
174
|
+
fastify.addHook("onClose", async () => {
|
|
175
|
+
for (const [signal, handler] of signalHandlers) process.removeListener(signal, handler);
|
|
176
|
+
signalHandlers.clear();
|
|
41
177
|
});
|
|
42
|
-
|
|
43
|
-
|
|
178
|
+
fastify.decorate("shutdown", async () => {
|
|
179
|
+
await shutdown("MANUAL");
|
|
44
180
|
});
|
|
45
|
-
fastify.log?.debug?.("
|
|
181
|
+
if (logEvents) fastify.log?.debug?.({ signals }, "Graceful shutdown plugin registered");
|
|
46
182
|
};
|
|
47
|
-
var
|
|
48
|
-
name: "arc-
|
|
183
|
+
var gracefulShutdown_default = fp(gracefulShutdownPlugin, {
|
|
184
|
+
name: "arc-graceful-shutdown",
|
|
49
185
|
fastify: "5.x"
|
|
50
186
|
});
|
|
51
|
-
|
|
52
187
|
//#endregion
|
|
53
188
|
//#region src/plugins/health.ts
|
|
54
|
-
/**
|
|
55
|
-
* Health Check Plugin
|
|
56
|
-
*
|
|
57
|
-
* Kubernetes-ready health endpoints:
|
|
58
|
-
* - /health/live - Liveness probe (is the process alive?)
|
|
59
|
-
* - /health/ready - Readiness probe (can we serve traffic?)
|
|
60
|
-
* - /health/metrics - Prometheus metrics (optional)
|
|
61
|
-
*
|
|
62
|
-
* @example
|
|
63
|
-
* import { healthPlugin } from '@classytic/arc';
|
|
64
|
-
*
|
|
65
|
-
* await fastify.register(healthPlugin, {
|
|
66
|
-
* prefix: '/_health',
|
|
67
|
-
* checks: [
|
|
68
|
-
* { name: 'mongodb', check: async () => mongoose.connection.readyState === 1 },
|
|
69
|
-
* { name: 'redis', check: async () => redis.ping() === 'PONG' },
|
|
70
|
-
* ],
|
|
71
|
-
* });
|
|
72
|
-
*/
|
|
73
189
|
function createHttpMetrics() {
|
|
74
190
|
return {
|
|
75
191
|
requestsTotal: {},
|
|
@@ -253,270 +369,44 @@ var health_default = fp(healthPlugin, {
|
|
|
253
369
|
name: "arc-health",
|
|
254
370
|
fastify: "5.x"
|
|
255
371
|
});
|
|
256
|
-
|
|
257
372
|
//#endregion
|
|
258
|
-
//#region src/plugins/
|
|
259
|
-
/**
|
|
260
|
-
* Graceful Shutdown Plugin
|
|
261
|
-
*
|
|
262
|
-
* Handles SIGTERM and SIGINT signals for clean shutdown:
|
|
263
|
-
* - Stops accepting new connections
|
|
264
|
-
* - Waits for in-flight requests to complete
|
|
265
|
-
* - Closes database connections
|
|
266
|
-
* - Exits cleanly
|
|
267
|
-
*
|
|
268
|
-
* Essential for Kubernetes deployments.
|
|
269
|
-
*
|
|
270
|
-
* @example
|
|
271
|
-
* import { gracefulShutdownPlugin } from '@classytic/arc';
|
|
272
|
-
*
|
|
273
|
-
* // Production
|
|
274
|
-
* await fastify.register(gracefulShutdownPlugin, {
|
|
275
|
-
* timeout: 30000, // 30 seconds max
|
|
276
|
-
* onShutdown: async () => {
|
|
277
|
-
* await mongoose.disconnect();
|
|
278
|
-
* await redis.quit();
|
|
279
|
-
* },
|
|
280
|
-
* });
|
|
281
|
-
*
|
|
282
|
-
* // Tests — prevent process.exit from killing the runner
|
|
283
|
-
* await fastify.register(gracefulShutdownPlugin, {
|
|
284
|
-
* onForceExit: () => {},
|
|
285
|
-
* });
|
|
286
|
-
*/
|
|
287
|
-
const gracefulShutdownPlugin = async (fastify, opts = {}) => {
|
|
288
|
-
const { timeout = 3e4, onShutdown, signals = ["SIGTERM", "SIGINT"], logEvents = true, onForceExit = () => process.exit(1) } = opts;
|
|
289
|
-
let isShuttingDown = false;
|
|
290
|
-
const signalHandlers = /* @__PURE__ */ new Map();
|
|
291
|
-
const shutdown = async (signal) => {
|
|
292
|
-
if (isShuttingDown) {
|
|
293
|
-
if (logEvents) fastify.log?.warn?.({ signal }, "Shutdown already in progress, ignoring signal");
|
|
294
|
-
return;
|
|
295
|
-
}
|
|
296
|
-
isShuttingDown = true;
|
|
297
|
-
if (logEvents) fastify.log?.info?.({
|
|
298
|
-
signal,
|
|
299
|
-
timeout
|
|
300
|
-
}, "Shutdown signal received, starting graceful shutdown");
|
|
301
|
-
const forceExitTimer = setTimeout(() => {
|
|
302
|
-
if (logEvents) fastify.log?.error?.("Graceful shutdown timeout exceeded, forcing exit");
|
|
303
|
-
onForceExit("timeout");
|
|
304
|
-
}, timeout);
|
|
305
|
-
forceExitTimer.unref();
|
|
306
|
-
try {
|
|
307
|
-
if (logEvents) fastify.log?.info?.("Closing server to new connections");
|
|
308
|
-
await fastify.close();
|
|
309
|
-
if (onShutdown) {
|
|
310
|
-
if (logEvents) fastify.log?.info?.("Running custom shutdown handler");
|
|
311
|
-
await onShutdown();
|
|
312
|
-
}
|
|
313
|
-
if (logEvents) fastify.log?.info?.("Graceful shutdown complete");
|
|
314
|
-
clearTimeout(forceExitTimer);
|
|
315
|
-
} catch (err) {
|
|
316
|
-
if (logEvents) fastify.log?.error?.({ error: err.message }, "Error during shutdown");
|
|
317
|
-
clearTimeout(forceExitTimer);
|
|
318
|
-
onForceExit("error");
|
|
319
|
-
}
|
|
320
|
-
};
|
|
321
|
-
for (const signal of signals) {
|
|
322
|
-
const handler = () => {
|
|
323
|
-
shutdown(signal);
|
|
324
|
-
};
|
|
325
|
-
signalHandlers.set(signal, handler);
|
|
326
|
-
process.on(signal, handler);
|
|
327
|
-
}
|
|
328
|
-
fastify.addHook("onClose", async () => {
|
|
329
|
-
for (const [signal, handler] of signalHandlers) process.removeListener(signal, handler);
|
|
330
|
-
signalHandlers.clear();
|
|
331
|
-
});
|
|
332
|
-
fastify.decorate("shutdown", async () => {
|
|
333
|
-
await shutdown("MANUAL");
|
|
334
|
-
});
|
|
335
|
-
if (logEvents) fastify.log?.debug?.({ signals }, "Graceful shutdown plugin registered");
|
|
336
|
-
};
|
|
337
|
-
var gracefulShutdown_default = fp(gracefulShutdownPlugin, {
|
|
338
|
-
name: "arc-graceful-shutdown",
|
|
339
|
-
fastify: "5.x"
|
|
340
|
-
});
|
|
341
|
-
|
|
342
|
-
//#endregion
|
|
343
|
-
//#region src/plugins/createPlugin.ts
|
|
344
|
-
/**
|
|
345
|
-
* createPlugin() — forRoot/forFeature Pattern
|
|
346
|
-
*
|
|
347
|
-
* Standard pattern for plugins that need both global setup and per-resource configuration.
|
|
348
|
-
* Inspired by NestJS forRoot/forFeature but simpler — plain functions, no decorators.
|
|
349
|
-
*
|
|
350
|
-
* @example
|
|
351
|
-
* ```typescript
|
|
352
|
-
* // Define a plugin with global + per-resource config
|
|
353
|
-
* const analytics = createPlugin('analytics', {
|
|
354
|
-
* forRoot: async (fastify, opts) => {
|
|
355
|
-
* // Global setup: connect to analytics service, add decorators
|
|
356
|
-
* const client = new AnalyticsClient(opts.apiKey);
|
|
357
|
-
* fastify.decorate('analytics', client);
|
|
358
|
-
* },
|
|
359
|
-
* forResource: (resourceConfig, opts) => {
|
|
360
|
-
* // Per-resource: return hooks, middleware, or routes
|
|
361
|
-
* return {
|
|
362
|
-
* hooks: [{
|
|
363
|
-
* operation: 'create', phase: 'after', priority: 100,
|
|
364
|
-
* handler: (ctx) => client.track('created', ctx.result),
|
|
365
|
-
* }],
|
|
366
|
-
* };
|
|
367
|
-
* },
|
|
368
|
-
* });
|
|
369
|
-
*
|
|
370
|
-
* // Usage — register globally once
|
|
371
|
-
* await app.register(analytics.forRoot({ apiKey: 'xxx' }));
|
|
372
|
-
*
|
|
373
|
-
* // Then apply per-resource
|
|
374
|
-
* const productResource = defineResource({
|
|
375
|
-
* name: 'product',
|
|
376
|
-
* adapter: productAdapter,
|
|
377
|
-
* ...analytics.forResource({ trackEvents: true }),
|
|
378
|
-
* });
|
|
379
|
-
* ```
|
|
380
|
-
*/
|
|
381
|
-
/**
|
|
382
|
-
* Create a structured plugin with forRoot (global) and forResource (per-resource) support.
|
|
383
|
-
*
|
|
384
|
-
* @param name - Plugin name (used for Fastify registration and debugging)
|
|
385
|
-
* @param definition - Plugin setup functions
|
|
386
|
-
* @returns ArcPlugin with forRoot() and forResource() methods
|
|
387
|
-
*/
|
|
388
|
-
function createPlugin(name, definition) {
|
|
389
|
-
return {
|
|
390
|
-
name,
|
|
391
|
-
forRoot(opts) {
|
|
392
|
-
const plugin = async (fastify, pluginOpts) => {
|
|
393
|
-
const mergedOpts = {
|
|
394
|
-
...opts,
|
|
395
|
-
...pluginOpts
|
|
396
|
-
};
|
|
397
|
-
if (definition.forRoot) await definition.forRoot(fastify, mergedOpts);
|
|
398
|
-
};
|
|
399
|
-
return fp(plugin, {
|
|
400
|
-
name: `arc-plugin-${name}`,
|
|
401
|
-
fastify: "5.x"
|
|
402
|
-
});
|
|
403
|
-
},
|
|
404
|
-
forResource(opts) {
|
|
405
|
-
if (!definition.forResource) return {};
|
|
406
|
-
return definition.forResource({}, opts ?? {});
|
|
407
|
-
}
|
|
408
|
-
};
|
|
409
|
-
}
|
|
410
|
-
|
|
411
|
-
//#endregion
|
|
412
|
-
//#region src/core/arcCorePlugin.ts
|
|
373
|
+
//#region src/plugins/requestId.ts
|
|
413
374
|
/**
|
|
414
|
-
*
|
|
415
|
-
*
|
|
416
|
-
* Sets up instance-scoped Arc systems:
|
|
417
|
-
* - HookSystem: Lifecycle hooks per app instance
|
|
418
|
-
* - ResourceRegistry: Resource tracking per app instance
|
|
419
|
-
* - Event integration: Wires CRUD operations to fastify.events
|
|
375
|
+
* Request ID Plugin
|
|
420
376
|
*
|
|
421
|
-
*
|
|
422
|
-
*
|
|
377
|
+
* Propagates request IDs for distributed tracing.
|
|
378
|
+
* - Accepts incoming x-request-id header
|
|
379
|
+
* - Generates UUID if not provided
|
|
380
|
+
* - Attaches to request.id and response header
|
|
423
381
|
*
|
|
424
382
|
* @example
|
|
425
|
-
* import {
|
|
383
|
+
* import { requestIdPlugin } from '@classytic/arc';
|
|
426
384
|
*
|
|
427
|
-
*
|
|
428
|
-
* await app.register(arcCorePlugin);
|
|
385
|
+
* await fastify.register(requestIdPlugin);
|
|
429
386
|
*
|
|
430
|
-
* //
|
|
431
|
-
*
|
|
432
|
-
*
|
|
387
|
+
* // In handlers, access via request.id
|
|
388
|
+
* fastify.get('/', async (request) => {
|
|
389
|
+
* console.log(request.id); // UUID
|
|
433
390
|
* });
|
|
434
391
|
*/
|
|
435
|
-
const
|
|
436
|
-
const {
|
|
437
|
-
|
|
438
|
-
|
|
439
|
-
|
|
440
|
-
|
|
441
|
-
|
|
442
|
-
|
|
443
|
-
|
|
444
|
-
plugins: /* @__PURE__ */ new Map()
|
|
445
|
-
});
|
|
446
|
-
fastify.addHook("onRequest", (request, _reply, done) => {
|
|
447
|
-
const store = {
|
|
448
|
-
requestId: request.id,
|
|
449
|
-
startTime: performance.now()
|
|
450
|
-
};
|
|
451
|
-
requestContext.storage.run(store, done);
|
|
452
|
-
});
|
|
453
|
-
fastify.addHook("preHandler", (request, _reply, done) => {
|
|
454
|
-
const store = requestContext.get();
|
|
455
|
-
if (store) {
|
|
456
|
-
store.user = request.user ?? null;
|
|
457
|
-
store.organizationId = request.scope?.kind === "member" ? request.scope.organizationId : request.scope?.kind === "elevated" ? request.scope.organizationId : void 0;
|
|
458
|
-
}
|
|
459
|
-
done();
|
|
460
|
-
});
|
|
461
|
-
if (emitEvents) {
|
|
462
|
-
const eventOperations = MUTATION_OPERATIONS;
|
|
463
|
-
for (const operation of eventOperations) actualHookSystem.after("*", operation, async (ctx) => {
|
|
464
|
-
if (!hasEvents(fastify)) return;
|
|
465
|
-
const store = requestContext.get();
|
|
466
|
-
const eventType = `${ctx.resource}.${operation}d`;
|
|
467
|
-
const userId = ctx.user?.id ?? ctx.user?._id;
|
|
468
|
-
const organizationId = ctx.context?._scope ? getOrgId(ctx.context._scope) : void 0;
|
|
469
|
-
const payload = {
|
|
470
|
-
resource: ctx.resource,
|
|
471
|
-
operation: ctx.operation,
|
|
472
|
-
data: ctx.result,
|
|
473
|
-
userId,
|
|
474
|
-
organizationId,
|
|
475
|
-
timestamp: (/* @__PURE__ */ new Date()).toISOString()
|
|
476
|
-
};
|
|
477
|
-
try {
|
|
478
|
-
await fastify.events.publish(eventType, payload, {
|
|
479
|
-
correlationId: store?.requestId,
|
|
480
|
-
resource: ctx.resource,
|
|
481
|
-
resourceId: extractId(ctx.result),
|
|
482
|
-
userId: userId ? String(userId) : void 0,
|
|
483
|
-
organizationId
|
|
484
|
-
});
|
|
485
|
-
} catch (error) {
|
|
486
|
-
fastify.log?.warn?.({
|
|
487
|
-
eventType,
|
|
488
|
-
error
|
|
489
|
-
}, "Failed to emit event");
|
|
490
|
-
}
|
|
491
|
-
});
|
|
492
|
-
}
|
|
493
|
-
fastify.addHook("onReady", async () => {
|
|
494
|
-
if (!hasEvents(fastify)) return;
|
|
495
|
-
try {
|
|
496
|
-
await fastify.events.publish("arc.ready", {
|
|
497
|
-
resources: actualRegistry.getAll().length,
|
|
498
|
-
hooks: actualHookSystem.getAll().length,
|
|
499
|
-
timestamp: (/* @__PURE__ */ new Date()).toISOString()
|
|
500
|
-
});
|
|
501
|
-
} catch {}
|
|
392
|
+
const requestIdPlugin = async (fastify, opts = {}) => {
|
|
393
|
+
const { header = "x-request-id", generator = randomUUID, setResponseHeader = true } = opts;
|
|
394
|
+
if (!fastify.hasRequestDecorator("requestId")) fastify.decorateRequest("requestId", "");
|
|
395
|
+
fastify.addHook("onRequest", async (request) => {
|
|
396
|
+
const incomingId = request.headers[header];
|
|
397
|
+
const sanitized = typeof incomingId === "string" ? incomingId.trim() : "";
|
|
398
|
+
const requestId = sanitized.length > 0 && sanitized.length <= 128 && /^[\w.:-]+$/.test(sanitized) ? sanitized : generator();
|
|
399
|
+
request.id = requestId;
|
|
400
|
+
request.requestId = requestId;
|
|
502
401
|
});
|
|
503
|
-
fastify.addHook("
|
|
504
|
-
|
|
505
|
-
actualRegistry._clear();
|
|
402
|
+
if (setResponseHeader) fastify.addHook("onSend", async (request, reply) => {
|
|
403
|
+
reply.header(header, request.requestId);
|
|
506
404
|
});
|
|
507
|
-
fastify.log?.debug?.("
|
|
405
|
+
fastify.log?.debug?.("Request ID plugin registered");
|
|
508
406
|
};
|
|
509
|
-
|
|
510
|
-
|
|
511
|
-
if (!doc || typeof doc !== "object") return void 0;
|
|
512
|
-
const d = doc;
|
|
513
|
-
const rawId = d._id ?? d.id;
|
|
514
|
-
return rawId ? String(rawId) : void 0;
|
|
515
|
-
}
|
|
516
|
-
var arcCorePlugin_default = fp(arcCorePlugin, {
|
|
517
|
-
name: "arc-core",
|
|
407
|
+
var requestId_default = fp(requestIdPlugin, {
|
|
408
|
+
name: "arc-request-id",
|
|
518
409
|
fastify: "5.x"
|
|
519
410
|
});
|
|
520
|
-
|
|
521
411
|
//#endregion
|
|
522
|
-
export { arcCorePlugin_default as arcCorePlugin, arcCorePlugin as arcCorePluginFn, caching_default as cachingPlugin, cachingPlugin as cachingPluginFn, createPlugin, errorHandlerPlugin, errorHandlerPlugin as errorHandlerPluginFn, gracefulShutdown_default as gracefulShutdownPlugin, gracefulShutdownPlugin as gracefulShutdownPluginFn, health_default as healthPlugin, healthPlugin as healthPluginFn, requestId_default as requestIdPlugin, requestIdPlugin as requestIdPluginFn, sse_default as ssePlugin, ssePlugin as ssePluginFn };
|
|
412
|
+
export { arcCorePlugin_default as arcCorePlugin, arcCorePlugin as arcCorePluginFn, caching_default as cachingPlugin, cachingPlugin as cachingPluginFn, createPlugin, errorHandlerPlugin, errorHandlerPlugin as errorHandlerPluginFn, gracefulShutdown_default as gracefulShutdownPlugin, gracefulShutdownPlugin as gracefulShutdownPluginFn, health_default as healthPlugin, healthPlugin as healthPluginFn, metrics_default as metricsPlugin, metricsPlugin as metricsPluginFn, requestId_default as requestIdPlugin, requestIdPlugin as requestIdPluginFn, sse_default as ssePlugin, ssePlugin as ssePluginFn, versioning_default as versioningPlugin, versioningPlugin as versioningPluginFn };
|
|
@@ -1,71 +1,7 @@
|
|
|
1
|
-
import { t as hasEvents } from "../typeGuards-
|
|
1
|
+
import { t as hasEvents } from "../typeGuards-Cj5Rgvlg.mjs";
|
|
2
2
|
import fp from "fastify-plugin";
|
|
3
|
-
|
|
4
3
|
//#region src/plugins/response-cache.ts
|
|
5
4
|
/**
|
|
6
|
-
* Response Cache Plugin for Arc
|
|
7
|
-
*
|
|
8
|
-
* In-memory LRU/TTL response cache that sits in front of your database.
|
|
9
|
-
* Caches serialized responses for GET requests, dramatically reducing DB load
|
|
10
|
-
* for frequently accessed resources.
|
|
11
|
-
*
|
|
12
|
-
* Features:
|
|
13
|
-
* - LRU eviction with configurable max entries
|
|
14
|
-
* - Per-route TTL configuration
|
|
15
|
-
* - Automatic invalidation on mutations (POST/PUT/PATCH/DELETE)
|
|
16
|
-
* - Manual invalidation via `fastify.responseCache.invalidate()`
|
|
17
|
-
* - Cache stats endpoint for monitoring
|
|
18
|
-
* - Zero external deps — pure in-memory, serverless-safe
|
|
19
|
-
*
|
|
20
|
-
* NOTE: This cache is per-instance (in-memory). In multi-instance deployments,
|
|
21
|
-
* each instance maintains its own cache. For cross-instance invalidation,
|
|
22
|
-
* wire `fastify.responseCache.invalidate()` to your event bus manually.
|
|
23
|
-
*
|
|
24
|
-
* ## Auth Safety
|
|
25
|
-
*
|
|
26
|
-
* The cache check runs as a **route-level middleware** (`responseCache.middleware`)
|
|
27
|
-
* that must be wired AFTER authentication in the preHandler chain. Arc's
|
|
28
|
-
* `createCrudRouter` does this automatically. For custom routes, wire it
|
|
29
|
-
* manually:
|
|
30
|
-
*
|
|
31
|
-
* ```typescript
|
|
32
|
-
* fastify.get('/data', {
|
|
33
|
-
* preHandler: [fastify.authenticate, fastify.responseCache.middleware],
|
|
34
|
-
* }, handler);
|
|
35
|
-
* ```
|
|
36
|
-
*
|
|
37
|
-
* This ensures cached responses are never served before auth validates the
|
|
38
|
-
* caller's identity. The default cache key includes `userId` and `orgId`
|
|
39
|
-
* to prevent cross-caller data leaks.
|
|
40
|
-
*
|
|
41
|
-
* This is a SEPARATE subpath import — only loaded when explicitly used:
|
|
42
|
-
* import { responseCachePlugin } from '@classytic/arc/plugins/response-cache';
|
|
43
|
-
*
|
|
44
|
-
* @example
|
|
45
|
-
* ```typescript
|
|
46
|
-
* import { responseCachePlugin } from '@classytic/arc/plugins/response-cache';
|
|
47
|
-
*
|
|
48
|
-
* await fastify.register(responseCachePlugin, {
|
|
49
|
-
* maxEntries: 1000,
|
|
50
|
-
* defaultTTL: 30, // 30 seconds
|
|
51
|
-
* rules: [
|
|
52
|
-
* { match: '/api/products', ttl: 120 }, // 2 min for products
|
|
53
|
-
* { match: '/api/categories', ttl: 300 }, // 5 min for categories
|
|
54
|
-
* { match: '/api/users', ttl: 0 }, // never cache users
|
|
55
|
-
* ],
|
|
56
|
-
* invalidateOn: ['POST', 'PUT', 'PATCH', 'DELETE'],
|
|
57
|
-
* });
|
|
58
|
-
*
|
|
59
|
-
* // Manual invalidation
|
|
60
|
-
* fastify.responseCache.invalidate('/api/products');
|
|
61
|
-
* fastify.responseCache.invalidateAll();
|
|
62
|
-
*
|
|
63
|
-
* // Stats
|
|
64
|
-
* const stats = fastify.responseCache.stats();
|
|
65
|
-
* // { entries: 42, hits: 1250, misses: 180, hitRate: 0.87, evictions: 5 }
|
|
66
|
-
* ```
|
|
67
|
-
*/
|
|
68
|
-
/**
|
|
69
5
|
* Simple LRU cache using Map iteration order.
|
|
70
6
|
* Map in JS preserves insertion order — we re-insert on access to make it LRU.
|
|
71
7
|
*/
|
|
@@ -191,7 +127,7 @@ const responseCachePluginImpl = async (fastify, opts = {}) => {
|
|
|
191
127
|
const segments = path.split("/").filter(Boolean);
|
|
192
128
|
const lastSegment = segments[segments.length - 1];
|
|
193
129
|
if (segments.length >= 2 && lastSegment != null && /^[0-9a-f]{8,}$|^\d+$/.test(lastSegment)) {
|
|
194
|
-
const resourceRoot =
|
|
130
|
+
const resourceRoot = `/${segments.slice(0, -1).join("/")}`;
|
|
195
131
|
cache.invalidatePrefix(resourceRoot);
|
|
196
132
|
cache.invalidatePrefix(path);
|
|
197
133
|
} else cache.invalidatePrefix(path);
|
|
@@ -227,7 +163,7 @@ const responseCachePluginImpl = async (fastify, opts = {}) => {
|
|
|
227
163
|
const contentType = reply.getHeader("content-type");
|
|
228
164
|
if (contentType) headers["content-type"] = String(contentType);
|
|
229
165
|
const etag = reply.getHeader("etag");
|
|
230
|
-
if (etag) headers
|
|
166
|
+
if (etag) headers.etag = String(etag);
|
|
231
167
|
cache.set(key, {
|
|
232
168
|
body,
|
|
233
169
|
statusCode,
|
|
@@ -278,6 +214,5 @@ const responseCachePlugin = fp(responseCachePluginImpl, {
|
|
|
278
214
|
name: "arc-response-cache",
|
|
279
215
|
fastify: "5.x"
|
|
280
216
|
});
|
|
281
|
-
|
|
282
217
|
//#endregion
|
|
283
|
-
export { responseCachePlugin as default, responseCachePlugin };
|
|
218
|
+
export { responseCachePlugin as default, responseCachePlugin };
|
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
import { a as traced, i as isTracingAvailable, n as _default, r as createSpan, t as TracingOptions } from "../tracing-
|
|
1
|
+
import { a as traced, i as isTracingAvailable, n as _default, r as createSpan, t as TracingOptions } from "../tracing-bz_U4EM1.mjs";
|
|
2
2
|
export { type TracingOptions, createSpan, isTracingAvailable, traced, _default as tracingPlugin };
|