@classytic/arc 1.1.0 → 2.1.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 +247 -794
- package/bin/arc.js +91 -52
- package/dist/EventTransport-BD2U0BTc.d.mts +100 -0
- package/dist/EventTransport-BD2U0BTc.d.mts.map +1 -0
- package/dist/HookSystem-BsGV-j2l.mjs +405 -0
- package/dist/HookSystem-BsGV-j2l.mjs.map +1 -0
- package/dist/ResourceRegistry-DsN4KJjV.mjs +250 -0
- package/dist/ResourceRegistry-DsN4KJjV.mjs.map +1 -0
- package/dist/adapters/index.d.mts +5 -0
- package/dist/adapters/index.mjs +3 -0
- package/dist/audit/index.d.mts +82 -0
- package/dist/audit/index.d.mts.map +1 -0
- package/dist/audit/index.mjs +276 -0
- package/dist/audit/index.mjs.map +1 -0
- package/dist/audit/mongodb.d.mts +5 -0
- package/dist/audit/mongodb.mjs +3 -0
- package/dist/audited-C3T5DTUx.mjs +141 -0
- package/dist/audited-C3T5DTUx.mjs.map +1 -0
- package/dist/auth/index.d.mts +189 -0
- package/dist/auth/index.d.mts.map +1 -0
- package/dist/auth/index.mjs +1102 -0
- package/dist/auth/index.mjs.map +1 -0
- package/dist/auth/redis-session.d.mts +44 -0
- package/dist/auth/redis-session.d.mts.map +1 -0
- package/dist/auth/redis-session.mjs +76 -0
- package/dist/auth/redis-session.mjs.map +1 -0
- package/dist/betterAuthOpenApi-BrHKeSAx.mjs +250 -0
- package/dist/betterAuthOpenApi-BrHKeSAx.mjs.map +1 -0
- package/dist/cache/index.d.mts +146 -0
- package/dist/cache/index.d.mts.map +1 -0
- package/dist/cache/index.mjs +92 -0
- package/dist/cache/index.mjs.map +1 -0
- package/dist/caching-Bl28lYsR.mjs +94 -0
- package/dist/caching-Bl28lYsR.mjs.map +1 -0
- package/dist/chunk-C7Uep-_p.mjs +20 -0
- package/dist/circuitBreaker-DeY4FCjs.mjs +1097 -0
- package/dist/circuitBreaker-DeY4FCjs.mjs.map +1 -0
- package/dist/cli/commands/describe.d.mts +19 -0
- package/dist/cli/commands/describe.d.mts.map +1 -0
- package/dist/cli/commands/describe.mjs +239 -0
- package/dist/cli/commands/describe.mjs.map +1 -0
- package/dist/cli/commands/docs.d.mts +14 -0
- package/dist/cli/commands/docs.d.mts.map +1 -0
- package/dist/cli/commands/docs.mjs +53 -0
- package/dist/cli/commands/docs.mjs.map +1 -0
- package/dist/cli/commands/{generate.d.ts → generate.d.mts} +3 -1
- package/dist/cli/commands/generate.d.mts.map +1 -0
- package/dist/cli/commands/generate.mjs +358 -0
- package/dist/cli/commands/generate.mjs.map +1 -0
- package/dist/cli/commands/{init.d.ts → init.d.mts} +12 -8
- package/dist/cli/commands/init.d.mts.map +1 -0
- package/dist/cli/commands/{init.js → init.mjs} +807 -616
- package/dist/cli/commands/init.mjs.map +1 -0
- package/dist/cli/commands/introspect.d.mts +11 -0
- package/dist/cli/commands/introspect.d.mts.map +1 -0
- package/dist/cli/commands/introspect.mjs +76 -0
- package/dist/cli/commands/introspect.mjs.map +1 -0
- package/dist/cli/index.d.mts +17 -0
- package/dist/cli/index.d.mts.map +1 -0
- package/dist/cli/index.mjs +157 -0
- package/dist/cli/index.mjs.map +1 -0
- package/dist/constants-DdXFXQtN.mjs +85 -0
- package/dist/constants-DdXFXQtN.mjs.map +1 -0
- package/dist/core/index.d.mts +5 -0
- package/dist/core/index.mjs +4 -0
- package/dist/createApp-CUgNqegw.mjs +560 -0
- package/dist/createApp-CUgNqegw.mjs.map +1 -0
- package/dist/defineResource-k0_BDn8v.mjs +2197 -0
- package/dist/defineResource-k0_BDn8v.mjs.map +1 -0
- package/dist/discovery/index.d.mts +47 -0
- package/dist/discovery/index.d.mts.map +1 -0
- package/dist/discovery/index.mjs +110 -0
- package/dist/discovery/index.mjs.map +1 -0
- package/dist/docs/index.d.mts +163 -0
- package/dist/docs/index.d.mts.map +1 -0
- package/dist/docs/index.mjs +73 -0
- package/dist/docs/index.mjs.map +1 -0
- package/dist/elevation-BRy3yFWT.mjs +113 -0
- package/dist/elevation-BRy3yFWT.mjs.map +1 -0
- package/dist/elevation-B_2dRLVP.d.mts +88 -0
- package/dist/elevation-B_2dRLVP.d.mts.map +1 -0
- package/dist/errorHandler-BbcgBmIH.d.mts +73 -0
- package/dist/errorHandler-BbcgBmIH.d.mts.map +1 -0
- package/dist/errorHandler-C1okiriz.mjs +109 -0
- package/dist/errorHandler-C1okiriz.mjs.map +1 -0
- package/dist/errors-B9bZok84.mjs +212 -0
- package/dist/errors-B9bZok84.mjs.map +1 -0
- package/dist/errors-ChKiFz62.d.mts +125 -0
- package/dist/errors-ChKiFz62.d.mts.map +1 -0
- package/dist/eventPlugin-CTrLH3mt.d.mts +125 -0
- package/dist/eventPlugin-CTrLH3mt.d.mts.map +1 -0
- package/dist/eventPlugin-DGR_B2on.mjs +230 -0
- package/dist/eventPlugin-DGR_B2on.mjs.map +1 -0
- package/dist/events/index.d.mts +54 -0
- package/dist/events/index.d.mts.map +1 -0
- package/dist/events/index.mjs +52 -0
- package/dist/events/index.mjs.map +1 -0
- package/dist/events/transports/redis-stream-entry.d.mts +2 -0
- package/dist/events/transports/redis-stream-entry.mjs +178 -0
- package/dist/events/transports/redis-stream-entry.mjs.map +1 -0
- package/dist/events/transports/redis.d.mts +77 -0
- package/dist/events/transports/redis.d.mts.map +1 -0
- package/dist/events/transports/redis.mjs +125 -0
- package/dist/events/transports/redis.mjs.map +1 -0
- package/dist/externalPaths-DlINfKbP.d.mts +51 -0
- package/dist/externalPaths-DlINfKbP.d.mts.map +1 -0
- package/dist/factory/index.d.mts +64 -0
- package/dist/factory/index.d.mts.map +1 -0
- package/dist/factory/index.mjs +3 -0
- package/dist/fastifyAdapter-BkrGrlFi.d.mts +217 -0
- package/dist/fastifyAdapter-BkrGrlFi.d.mts.map +1 -0
- package/dist/fields-DyaDVX4J.d.mts +110 -0
- package/dist/fields-DyaDVX4J.d.mts.map +1 -0
- package/dist/fields-iagOozy0.mjs +115 -0
- package/dist/fields-iagOozy0.mjs.map +1 -0
- package/dist/hooks/index.d.mts +4 -0
- package/dist/hooks/index.mjs +3 -0
- package/dist/idempotency/index.d.mts +97 -0
- package/dist/idempotency/index.d.mts.map +1 -0
- package/dist/idempotency/index.mjs +320 -0
- package/dist/idempotency/index.mjs.map +1 -0
- package/dist/idempotency/mongodb.d.mts +2 -0
- package/dist/idempotency/mongodb.mjs +115 -0
- package/dist/idempotency/mongodb.mjs.map +1 -0
- package/dist/idempotency/redis.d.mts +2 -0
- package/dist/idempotency/redis.mjs +104 -0
- package/dist/idempotency/redis.mjs.map +1 -0
- package/dist/index.d.mts +261 -0
- package/dist/index.d.mts.map +1 -0
- package/dist/index.mjs +105 -0
- package/dist/index.mjs.map +1 -0
- package/dist/integrations/event-gateway.d.mts +47 -0
- package/dist/integrations/event-gateway.d.mts.map +1 -0
- package/dist/integrations/event-gateway.mjs +44 -0
- package/dist/integrations/event-gateway.mjs.map +1 -0
- package/dist/integrations/index.d.mts +5 -0
- package/dist/integrations/index.mjs +1 -0
- package/dist/integrations/jobs.d.mts +104 -0
- package/dist/integrations/jobs.d.mts.map +1 -0
- package/dist/integrations/jobs.mjs +124 -0
- package/dist/integrations/jobs.mjs.map +1 -0
- package/dist/integrations/streamline.d.mts +61 -0
- package/dist/integrations/streamline.d.mts.map +1 -0
- package/dist/integrations/streamline.mjs +126 -0
- package/dist/integrations/streamline.mjs.map +1 -0
- package/dist/integrations/websocket.d.mts +83 -0
- package/dist/integrations/websocket.d.mts.map +1 -0
- package/dist/integrations/websocket.mjs +289 -0
- package/dist/integrations/websocket.mjs.map +1 -0
- package/dist/interface-B01JvPVc.d.mts +78 -0
- package/dist/interface-B01JvPVc.d.mts.map +1 -0
- package/dist/interface-CZe8IkMf.d.mts +55 -0
- package/dist/interface-CZe8IkMf.d.mts.map +1 -0
- package/dist/interface-Ch8HU9uM.d.mts +1098 -0
- package/dist/interface-Ch8HU9uM.d.mts.map +1 -0
- package/dist/introspectionPlugin-rFdO8ZUa.mjs +54 -0
- package/dist/introspectionPlugin-rFdO8ZUa.mjs.map +1 -0
- package/dist/keys-BqNejWup.mjs +43 -0
- package/dist/keys-BqNejWup.mjs.map +1 -0
- package/dist/logger-Df2O2WsW.mjs +79 -0
- package/dist/logger-Df2O2WsW.mjs.map +1 -0
- package/dist/memory-cQgelFOj.mjs +144 -0
- package/dist/memory-cQgelFOj.mjs.map +1 -0
- package/dist/migrations/index.d.mts +157 -0
- package/dist/migrations/index.d.mts.map +1 -0
- package/dist/migrations/index.mjs +261 -0
- package/dist/migrations/index.mjs.map +1 -0
- package/dist/mongodb-BfJVlUJH.mjs +94 -0
- package/dist/mongodb-BfJVlUJH.mjs.map +1 -0
- package/dist/mongodb-CGzRbfAK.d.mts +119 -0
- package/dist/mongodb-CGzRbfAK.d.mts.map +1 -0
- package/dist/mongodb-JN-9JA7K.d.mts +72 -0
- package/dist/mongodb-JN-9JA7K.d.mts.map +1 -0
- package/dist/openapi-G3Cw7XuM.mjs +524 -0
- package/dist/openapi-G3Cw7XuM.mjs.map +1 -0
- package/dist/org/index.d.mts +69 -0
- package/dist/org/index.d.mts.map +1 -0
- package/dist/org/index.mjs +514 -0
- package/dist/org/index.mjs.map +1 -0
- package/dist/org/types.d.mts +83 -0
- package/dist/org/types.d.mts.map +1 -0
- package/dist/org/types.mjs +1 -0
- package/dist/permissions/index.d.mts +279 -0
- package/dist/permissions/index.d.mts.map +1 -0
- package/dist/permissions/index.mjs +579 -0
- package/dist/permissions/index.mjs.map +1 -0
- package/dist/plugins/index.d.mts +173 -0
- package/dist/plugins/index.d.mts.map +1 -0
- package/dist/plugins/index.mjs +523 -0
- package/dist/plugins/index.mjs.map +1 -0
- package/dist/plugins/response-cache.d.mts +88 -0
- package/dist/plugins/response-cache.d.mts.map +1 -0
- package/dist/plugins/response-cache.mjs +284 -0
- package/dist/plugins/response-cache.mjs.map +1 -0
- package/dist/plugins/tracing-entry.d.mts +2 -0
- package/dist/plugins/tracing-entry.mjs +186 -0
- package/dist/plugins/tracing-entry.mjs.map +1 -0
- package/dist/pluralize-CEweyOEm.mjs +87 -0
- package/dist/pluralize-CEweyOEm.mjs.map +1 -0
- package/dist/policies/{index.d.ts → index.d.mts} +204 -169
- package/dist/policies/index.d.mts.map +1 -0
- package/dist/policies/index.mjs +322 -0
- package/dist/policies/index.mjs.map +1 -0
- package/dist/presets/{index.d.ts → index.d.mts} +63 -131
- package/dist/presets/index.d.mts.map +1 -0
- package/dist/presets/index.mjs +144 -0
- package/dist/presets/index.mjs.map +1 -0
- package/dist/presets/multiTenant.d.mts +25 -0
- package/dist/presets/multiTenant.d.mts.map +1 -0
- package/dist/presets/multiTenant.mjs +114 -0
- package/dist/presets/multiTenant.mjs.map +1 -0
- package/dist/presets-BITljm96.mjs +120 -0
- package/dist/presets-BITljm96.mjs.map +1 -0
- package/dist/presets-DzSMwlKj.d.mts +58 -0
- package/dist/presets-DzSMwlKj.d.mts.map +1 -0
- package/dist/prisma-DJbMt3yf.mjs +628 -0
- package/dist/prisma-DJbMt3yf.mjs.map +1 -0
- package/dist/prisma-Dg9GoVdj.d.mts +275 -0
- package/dist/prisma-Dg9GoVdj.d.mts.map +1 -0
- package/dist/queryCachePlugin-7THaI5mt.d.mts +72 -0
- package/dist/queryCachePlugin-7THaI5mt.d.mts.map +1 -0
- package/dist/queryCachePlugin-DMBnp2Q0.mjs +139 -0
- package/dist/queryCachePlugin-DMBnp2Q0.mjs.map +1 -0
- package/dist/redis-D-JAeLtm.d.mts +50 -0
- package/dist/redis-D-JAeLtm.d.mts.map +1 -0
- package/dist/redis-stream-Bdh_vUU8.d.mts +104 -0
- package/dist/redis-stream-Bdh_vUU8.d.mts.map +1 -0
- package/dist/registry/index.d.mts +12 -0
- package/dist/registry/index.d.mts.map +1 -0
- package/dist/registry/index.mjs +4 -0
- package/dist/requestContext-QQD6ROJc.mjs +56 -0
- package/dist/requestContext-QQD6ROJc.mjs.map +1 -0
- package/dist/schemaConverter-BwrmWroW.mjs +99 -0
- package/dist/schemaConverter-BwrmWroW.mjs.map +1 -0
- package/dist/schemas/index.d.mts +64 -0
- package/dist/schemas/index.d.mts.map +1 -0
- package/dist/schemas/index.mjs +83 -0
- package/dist/schemas/index.mjs.map +1 -0
- package/dist/scope/index.d.mts +22 -0
- package/dist/scope/index.d.mts.map +1 -0
- package/dist/scope/index.mjs +66 -0
- package/dist/scope/index.mjs.map +1 -0
- package/dist/sessionManager-jPKLbHE0.d.mts +187 -0
- package/dist/sessionManager-jPKLbHE0.d.mts.map +1 -0
- package/dist/sse-B3c3_yZp.mjs +124 -0
- package/dist/sse-B3c3_yZp.mjs.map +1 -0
- package/dist/testing/index.d.mts +908 -0
- package/dist/testing/index.d.mts.map +1 -0
- package/dist/testing/index.mjs +1977 -0
- package/dist/testing/index.mjs.map +1 -0
- package/dist/tracing-Cc7vVQPp.d.mts +71 -0
- package/dist/tracing-Cc7vVQPp.d.mts.map +1 -0
- package/dist/typeGuards-DhMNLuvU.mjs +10 -0
- package/dist/typeGuards-DhMNLuvU.mjs.map +1 -0
- package/dist/types/index.d.mts +947 -0
- package/dist/types/index.d.mts.map +1 -0
- package/dist/types/index.mjs +15 -0
- package/dist/types/index.mjs.map +1 -0
- package/dist/types-Beqn1Un7.mjs +39 -0
- package/dist/types-Beqn1Un7.mjs.map +1 -0
- package/dist/types-CIgB7UUl.d.mts +446 -0
- package/dist/types-CIgB7UUl.d.mts.map +1 -0
- package/dist/types-aYB4V7uN.d.mts +87 -0
- package/dist/types-aYB4V7uN.d.mts.map +1 -0
- package/dist/utils/index.d.mts +748 -0
- package/dist/utils/index.d.mts.map +1 -0
- package/dist/utils/index.mjs +6 -0
- package/package.json +194 -68
- package/dist/BaseController-DVAiHxEQ.d.ts +0 -233
- package/dist/adapters/index.d.ts +0 -237
- package/dist/adapters/index.js +0 -668
- package/dist/arcCorePlugin-CsShQdyP.d.ts +0 -273
- package/dist/audit/index.d.ts +0 -195
- package/dist/audit/index.js +0 -319
- package/dist/auth/index.d.ts +0 -47
- package/dist/auth/index.js +0 -174
- package/dist/cli/commands/docs.d.ts +0 -11
- package/dist/cli/commands/docs.js +0 -474
- package/dist/cli/commands/generate.js +0 -334
- package/dist/cli/commands/introspect.d.ts +0 -8
- package/dist/cli/commands/introspect.js +0 -338
- package/dist/cli/index.d.ts +0 -4
- package/dist/cli/index.js +0 -3269
- package/dist/core/index.d.ts +0 -220
- package/dist/core/index.js +0 -2786
- package/dist/createApp-Ce9wl8W9.d.ts +0 -77
- package/dist/docs/index.d.ts +0 -166
- package/dist/docs/index.js +0 -658
- package/dist/errors-8WIxGS_6.d.ts +0 -122
- package/dist/events/index.d.ts +0 -117
- package/dist/events/index.js +0 -89
- package/dist/factory/index.d.ts +0 -38
- package/dist/factory/index.js +0 -1652
- package/dist/hooks/index.d.ts +0 -4
- package/dist/hooks/index.js +0 -199
- package/dist/idempotency/index.d.ts +0 -323
- package/dist/idempotency/index.js +0 -500
- package/dist/index-B4t03KQ0.d.ts +0 -1366
- package/dist/index.d.ts +0 -135
- package/dist/index.js +0 -4756
- package/dist/migrations/index.d.ts +0 -185
- package/dist/migrations/index.js +0 -274
- package/dist/org/index.d.ts +0 -129
- package/dist/org/index.js +0 -220
- package/dist/permissions/index.d.ts +0 -144
- package/dist/permissions/index.js +0 -103
- package/dist/plugins/index.d.ts +0 -46
- package/dist/plugins/index.js +0 -1069
- package/dist/policies/index.js +0 -196
- package/dist/presets/index.js +0 -384
- package/dist/presets/multiTenant.d.ts +0 -39
- package/dist/presets/multiTenant.js +0 -112
- package/dist/registry/index.d.ts +0 -16
- package/dist/registry/index.js +0 -253
- package/dist/testing/index.d.ts +0 -618
- package/dist/testing/index.js +0 -48020
- package/dist/types/index.d.ts +0 -4
- package/dist/types/index.js +0 -8
- package/dist/types-B99TBmFV.d.ts +0 -76
- package/dist/types-BvckRbs2.d.ts +0 -143
- package/dist/utils/index.d.ts +0 -679
- package/dist/utils/index.js +0 -931
|
@@ -0,0 +1,173 @@
|
|
|
1
|
+
import "../elevation-B_2dRLVP.mjs";
|
|
2
|
+
import { K as HookSystem, w as ResourceRegistry } from "../interface-Ch8HU9uM.mjs";
|
|
3
|
+
import "../types-aYB4V7uN.mjs";
|
|
4
|
+
import { AdditionalRoute, AnyRecord, MiddlewareConfig, PresetHook, RouteSchemaOptions } from "../types/index.mjs";
|
|
5
|
+
import { t as ExternalOpenApiPaths } from "../externalPaths-DlINfKbP.mjs";
|
|
6
|
+
import { a as ssePlugin, c as _default$1, i as _default$5, l as cachingPlugin, n as errorHandlerPlugin, o as CachingOptions, r as SSEOptions, s as CachingRule, t as ErrorHandlerOptions } from "../errorHandler-BbcgBmIH.mjs";
|
|
7
|
+
import { t as TracingOptions } from "../tracing-Cc7vVQPp.mjs";
|
|
8
|
+
import { FastifyInstance, FastifyPluginAsync } from "fastify";
|
|
9
|
+
|
|
10
|
+
//#region src/plugins/requestId.d.ts
|
|
11
|
+
interface RequestIdOptions {
|
|
12
|
+
/** Header name to read/write request ID (default: 'x-request-id') */
|
|
13
|
+
header?: string;
|
|
14
|
+
/** Custom ID generator (default: crypto.randomUUID) */
|
|
15
|
+
generator?: () => string;
|
|
16
|
+
/** Whether to set response header (default: true) */
|
|
17
|
+
setResponseHeader?: boolean;
|
|
18
|
+
}
|
|
19
|
+
declare module 'fastify' {
|
|
20
|
+
interface FastifyRequest {
|
|
21
|
+
/** Unique request identifier for tracing */
|
|
22
|
+
requestId: string;
|
|
23
|
+
}
|
|
24
|
+
}
|
|
25
|
+
declare const requestIdPlugin: FastifyPluginAsync<RequestIdOptions>;
|
|
26
|
+
declare const _default$4: FastifyPluginAsync<RequestIdOptions>;
|
|
27
|
+
//#endregion
|
|
28
|
+
//#region src/plugins/health.d.ts
|
|
29
|
+
declare module 'fastify' {
|
|
30
|
+
interface FastifyRequest {
|
|
31
|
+
_startTime?: number;
|
|
32
|
+
}
|
|
33
|
+
}
|
|
34
|
+
interface HealthCheck {
|
|
35
|
+
/** Name of the dependency */
|
|
36
|
+
name: string;
|
|
37
|
+
/** Function that returns true if healthy, false otherwise */
|
|
38
|
+
check: () => Promise<boolean> | boolean;
|
|
39
|
+
/** Optional timeout in ms (default: 5000) */
|
|
40
|
+
timeout?: number;
|
|
41
|
+
/** Whether this check is critical for readiness (default: true) */
|
|
42
|
+
critical?: boolean;
|
|
43
|
+
}
|
|
44
|
+
interface HealthOptions {
|
|
45
|
+
/** Route prefix (default: '/_health') */
|
|
46
|
+
prefix?: string;
|
|
47
|
+
/** Health check dependencies */
|
|
48
|
+
checks?: HealthCheck[];
|
|
49
|
+
/** Enable metrics endpoint (default: false) */
|
|
50
|
+
metrics?: boolean;
|
|
51
|
+
/** Custom metrics collector function */
|
|
52
|
+
metricsCollector?: () => Promise<string> | string;
|
|
53
|
+
/** Version info to include in responses */
|
|
54
|
+
version?: string;
|
|
55
|
+
/** Collect HTTP request metrics (default: true if metrics enabled) */
|
|
56
|
+
collectHttpMetrics?: boolean;
|
|
57
|
+
}
|
|
58
|
+
declare const healthPlugin: FastifyPluginAsync<HealthOptions>;
|
|
59
|
+
declare const _default$3: FastifyPluginAsync<HealthOptions>;
|
|
60
|
+
//#endregion
|
|
61
|
+
//#region src/plugins/gracefulShutdown.d.ts
|
|
62
|
+
interface GracefulShutdownOptions {
|
|
63
|
+
/** Maximum time to wait for graceful shutdown in ms (default: 30000) */
|
|
64
|
+
timeout?: number;
|
|
65
|
+
/** Custom cleanup function called before exit */
|
|
66
|
+
onShutdown?: () => Promise<void> | void;
|
|
67
|
+
/** Signals to handle (default: ['SIGTERM', 'SIGINT']) */
|
|
68
|
+
signals?: NodeJS.Signals[];
|
|
69
|
+
/** Whether to log shutdown events (default: true) */
|
|
70
|
+
logEvents?: boolean;
|
|
71
|
+
/**
|
|
72
|
+
* Called when shutdown times out or encounters an error.
|
|
73
|
+
* Defaults to `process.exit(1)` — appropriate for production but dangerous in:
|
|
74
|
+
* - **Tests**: kills the test runner. Pass `() => {}` or `() => { throw … }`.
|
|
75
|
+
* - **Shared runtimes** (e.g., serverless): may kill unrelated handlers.
|
|
76
|
+
*
|
|
77
|
+
* @param reason - `'timeout'` if shutdown exceeded `timeout` ms,
|
|
78
|
+
* `'error'` if `onShutdown` or `fastify.close()` threw.
|
|
79
|
+
*/
|
|
80
|
+
onForceExit?: (reason: 'timeout' | 'error') => void;
|
|
81
|
+
}
|
|
82
|
+
declare const gracefulShutdownPlugin: FastifyPluginAsync<GracefulShutdownOptions>;
|
|
83
|
+
declare module 'fastify' {
|
|
84
|
+
interface FastifyInstance {
|
|
85
|
+
/** Trigger graceful shutdown manually */
|
|
86
|
+
shutdown: () => Promise<void>;
|
|
87
|
+
}
|
|
88
|
+
}
|
|
89
|
+
declare const _default$2: FastifyPluginAsync<GracefulShutdownOptions>;
|
|
90
|
+
//#endregion
|
|
91
|
+
//#region src/plugins/createPlugin.d.ts
|
|
92
|
+
interface PluginResourceResult {
|
|
93
|
+
/** Additional routes to add to the resource */
|
|
94
|
+
additionalRoutes?: AdditionalRoute[];
|
|
95
|
+
/** Middlewares per operation */
|
|
96
|
+
middlewares?: MiddlewareConfig;
|
|
97
|
+
/** Hooks to register */
|
|
98
|
+
hooks?: PresetHook[];
|
|
99
|
+
/** Schema options to merge */
|
|
100
|
+
schemaOptions?: RouteSchemaOptions;
|
|
101
|
+
}
|
|
102
|
+
interface CreatePluginDefinition<TRootOpts extends AnyRecord = AnyRecord, TResourceOpts extends AnyRecord = AnyRecord> {
|
|
103
|
+
/**
|
|
104
|
+
* Global setup function. Called once when the plugin is registered on the Fastify instance.
|
|
105
|
+
* Use this for database connections, decorators, shared state, etc.
|
|
106
|
+
*/
|
|
107
|
+
forRoot?: (fastify: FastifyInstance, opts: TRootOpts) => void | Promise<void>;
|
|
108
|
+
/**
|
|
109
|
+
* Per-resource configuration function. Called for each resource that uses this plugin.
|
|
110
|
+
* Returns hooks, routes, middlewares, etc. to merge into the resource config.
|
|
111
|
+
*/
|
|
112
|
+
forResource?: (resourceConfig: AnyRecord, opts: TResourceOpts) => PluginResourceResult;
|
|
113
|
+
}
|
|
114
|
+
interface ArcPlugin<TRootOpts extends AnyRecord = AnyRecord, TResourceOpts extends AnyRecord = AnyRecord> {
|
|
115
|
+
/** Plugin name */
|
|
116
|
+
readonly name: string;
|
|
117
|
+
/**
|
|
118
|
+
* Register the plugin globally on a Fastify instance.
|
|
119
|
+
* Returns a Fastify plugin that can be passed to `app.register()`.
|
|
120
|
+
*/
|
|
121
|
+
forRoot(opts?: TRootOpts): FastifyPluginAsync<TRootOpts>;
|
|
122
|
+
/**
|
|
123
|
+
* Apply per-resource configuration.
|
|
124
|
+
* Returns a partial resource config to spread into `defineResource()`.
|
|
125
|
+
*/
|
|
126
|
+
forResource(opts?: TResourceOpts): PluginResourceResult;
|
|
127
|
+
}
|
|
128
|
+
/**
|
|
129
|
+
* Create a structured plugin with forRoot (global) and forResource (per-resource) support.
|
|
130
|
+
*
|
|
131
|
+
* @param name - Plugin name (used for Fastify registration and debugging)
|
|
132
|
+
* @param definition - Plugin setup functions
|
|
133
|
+
* @returns ArcPlugin with forRoot() and forResource() methods
|
|
134
|
+
*/
|
|
135
|
+
declare function createPlugin<TRootOpts extends AnyRecord = AnyRecord, TResourceOpts extends AnyRecord = AnyRecord>(name: string, definition: CreatePluginDefinition<TRootOpts, TResourceOpts>): ArcPlugin<TRootOpts, TResourceOpts>;
|
|
136
|
+
//#endregion
|
|
137
|
+
//#region src/core/arcCorePlugin.d.ts
|
|
138
|
+
interface ArcCorePluginOptions {
|
|
139
|
+
/** Enable event emission for CRUD operations (requires eventPlugin) */
|
|
140
|
+
emitEvents?: boolean;
|
|
141
|
+
/** Hook system instance (for testing/custom setup) */
|
|
142
|
+
hookSystem?: HookSystem;
|
|
143
|
+
/** Resource registry instance (for testing/custom setup) */
|
|
144
|
+
registry?: ResourceRegistry;
|
|
145
|
+
}
|
|
146
|
+
interface PluginMeta {
|
|
147
|
+
name: string;
|
|
148
|
+
version?: string;
|
|
149
|
+
options?: Record<string, unknown>;
|
|
150
|
+
registeredAt: string;
|
|
151
|
+
}
|
|
152
|
+
interface ArcCore {
|
|
153
|
+
/** Instance-scoped hook system */
|
|
154
|
+
hooks: HookSystem;
|
|
155
|
+
/** Instance-scoped resource registry */
|
|
156
|
+
registry: ResourceRegistry;
|
|
157
|
+
/** Whether event emission is enabled */
|
|
158
|
+
emitEvents: boolean;
|
|
159
|
+
/** External OpenAPI paths contributed by auth adapters or third-party integrations */
|
|
160
|
+
externalOpenApiPaths: ExternalOpenApiPaths[];
|
|
161
|
+
/** Registered plugins for introspection */
|
|
162
|
+
plugins: Map<string, PluginMeta>;
|
|
163
|
+
}
|
|
164
|
+
declare module 'fastify' {
|
|
165
|
+
interface FastifyInstance {
|
|
166
|
+
arc: ArcCore;
|
|
167
|
+
}
|
|
168
|
+
}
|
|
169
|
+
declare const arcCorePlugin: FastifyPluginAsync<ArcCorePluginOptions>;
|
|
170
|
+
declare const _default: FastifyPluginAsync<ArcCorePluginOptions>;
|
|
171
|
+
//#endregion
|
|
172
|
+
export { type ArcCore, type ArcCorePluginOptions, type ArcPlugin, type CachingOptions, type CachingRule, type CreatePluginDefinition, type ErrorHandlerOptions, type GracefulShutdownOptions, type HealthCheck, type HealthOptions, type PluginMeta, type PluginResourceResult, type RequestIdOptions, type SSEOptions, type TracingOptions, _default as arcCorePlugin, arcCorePlugin as arcCorePluginFn, _default$1 as cachingPlugin, cachingPlugin as cachingPluginFn, createPlugin, errorHandlerPlugin, errorHandlerPlugin as errorHandlerPluginFn, _default$2 as gracefulShutdownPlugin, gracefulShutdownPlugin as gracefulShutdownPluginFn, _default$3 as healthPlugin, healthPlugin as healthPluginFn, _default$4 as requestIdPlugin, requestIdPlugin as requestIdPluginFn, _default$5 as ssePlugin, ssePlugin as ssePluginFn };
|
|
173
|
+
//# sourceMappingURL=index.d.mts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.mts","names":[],"sources":["../../src/plugins/requestId.ts","../../src/plugins/health.ts","../../src/plugins/gracefulShutdown.ts","../../src/plugins/createPlugin.ts","../../src/core/arcCorePlugin.ts"],"mappings":";;;;;;;;;;UAuBiB,gBAAA;EAOhB;EALC,MAAA;EAQU;EANV,SAAA;EAQW;EANX,iBAAA;AAAA;AAAA;EAAA,UAIU,cAAA;IAM8C;IAJtD,SAAA;EAAA;AAAA;AAAA,cAIE,eAAA,EAAiB,kBAAA,CAAmB,gBAAA;AAAA,cAAgB,UAAA;;;;YCd9C,cAAA;IACR,UAAA;EAAA;AAAA;AAAA,UAIa,WAAA;EDiDhB;EC/CC,IAAA;EDOqB;ECLrB,KAAA,QAAa,OAAA;EDK2C;ECHxD,OAAA;;EAEA,QAAA;AAAA;AAAA,UAGe,aAAA;;EAEf,MAAA;EAtBiE;EAwBjE,MAAA,GAAS,WAAA;;EAET,OAAA;EArBY;EAuBZ,gBAAA,SAAyB,OAAA;EAnBV;EAqBf,OAAA;;EAEA,kBAAA;AAAA;AAAA,cA4BI,YAAA,EAAc,kBAAA,CAAmB,aAAA;AAAA,cAAa,UAAA;;;UCjDnC,uBAAA;EFOyC;EELxD,OAAA;;EAEA,UAAA,SAAmB,OAAA;;EAEnB,OAAA,GAAU,MAAA,CAAO,OAAA;;EAEjB,SAAA;EDnBiE;;;;;;AASnE;;;ECoBE,WAAA,IAAe,MAAA;AAAA;AAAA,cAGX,sBAAA,EAAwB,kBAAA,CAAmB,uBAAA;AAAA;EAAA,UAoGrC,eAAA;IDnHV;ICqHE,QAAA,QAAgB,OAAA;EAAA;AAAA;AAAA,cAAO,UAAA;;;UC7GV,oBAAA;;EAEf,gBAAA,GAAmB,eAAA;EFtBP;EEwBZ,WAAA,GAAc,gBAAA;EFpBC;EEsBf,KAAA,GAAQ,UAAA;;EAER,aAAA,GAAgB,kBAAA;AAAA;AAAA,UAGD,sBAAA,mBACG,SAAA,GAAY,SAAA,wBACR,SAAA,GAAY,SAAA;EFzBrB;;;;EE+Bb,OAAA,IAAW,OAAA,EAAS,eAAA,EAAiB,IAAA,EAAM,SAAA,YAAqB,OAAA;EFxBjD;;;;EE8Bf,WAAA,IAAe,cAAA,EAAgB,SAAA,EAAW,IAAA,EAAM,aAAA,KAAkB,oBAAA;AAAA;AAAA,UAGnD,SAAA,mBACG,SAAA,GAAY,SAAA,wBACR,SAAA,GAAY,SAAA;EF7BlC;EAAA,SEgCS,IAAA;EF9BgB;;;;EEoCzB,OAAA,CAAQ,IAAA,GAAO,SAAA,GAAY,kBAAA,CAAmB,SAAA;EFJ1C;;;;EEUJ,WAAA,CAAY,IAAA,GAAO,aAAA,GAAgB,oBAAA;AAAA;;;;;;;;iBAcrB,YAAA,mBACI,SAAA,GAAY,SAAA,wBACR,SAAA,GAAY,SAAA,CAAA,CAElC,IAAA,UACA,UAAA,EAAY,sBAAA,CAAuB,SAAA,EAAW,aAAA,IAC7C,SAAA,CAAU,SAAA,EAAW,aAAA;;;UC5EP,oBAAA;;EAEf,UAAA;EJEwD;EIAxD,UAAA,GAAa,UAAA;;EAEb,QAAA,GAAW,gBAAA;AAAA;AAAA,UAGI,UAAA;EACf,IAAA;EACA,OAAA;EACA,OAAA,GAAU,MAAA;EACV,YAAA;AAAA;AAAA,UAGe,OAAA;EH1BL;EG4BV,KAAA,EAAO,UAAA;EH3BK;EG6BZ,QAAA,EAAU,gBAAA;EHzBgB;EG2B1B,UAAA;EHvBoB;EGyBpB,oBAAA,EAAsB,oBAAA;EHzBtB;EG2BA,OAAA,EAAS,GAAA,SAAY,UAAA;AAAA;AAAA;EAAA,UAIX,eAAA;IACR,GAAA,EAAK,OAAA;EAAA;AAAA;AAAA,cAIH,aAAA,EAAe,kBAAA,CAAmB,oBAAA;AAAA,cAAoB,QAAA"}
|
|
@@ -0,0 +1,523 @@
|
|
|
1
|
+
import { p as MUTATION_OPERATIONS } from "../constants-DdXFXQtN.mjs";
|
|
2
|
+
import { r as getOrgId } from "../types-Beqn1Un7.mjs";
|
|
3
|
+
import { t as HookSystem } from "../HookSystem-BsGV-j2l.mjs";
|
|
4
|
+
import { t as hasEvents } from "../typeGuards-DhMNLuvU.mjs";
|
|
5
|
+
import { t as requestContext } from "../requestContext-QQD6ROJc.mjs";
|
|
6
|
+
import { t as ResourceRegistry } from "../ResourceRegistry-DsN4KJjV.mjs";
|
|
7
|
+
import { t as errorHandlerPlugin } from "../errorHandler-C1okiriz.mjs";
|
|
8
|
+
import { n as caching_default, t as cachingPlugin } from "../caching-Bl28lYsR.mjs";
|
|
9
|
+
import { n as sse_default, t as ssePlugin } from "../sse-B3c3_yZp.mjs";
|
|
10
|
+
import fp from "fastify-plugin";
|
|
11
|
+
import { randomUUID } from "crypto";
|
|
12
|
+
|
|
13
|
+
//#region src/plugins/requestId.ts
|
|
14
|
+
/**
|
|
15
|
+
* Request ID Plugin
|
|
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);
|
|
26
|
+
*
|
|
27
|
+
* // In handlers, access via request.id
|
|
28
|
+
* fastify.get('/', async (request) => {
|
|
29
|
+
* console.log(request.id); // UUID
|
|
30
|
+
* });
|
|
31
|
+
*/
|
|
32
|
+
const requestIdPlugin = async (fastify, opts = {}) => {
|
|
33
|
+
const { header = "x-request-id", generator = randomUUID, setResponseHeader = true } = opts;
|
|
34
|
+
if (!fastify.hasRequestDecorator("requestId")) fastify.decorateRequest("requestId", "");
|
|
35
|
+
fastify.addHook("onRequest", async (request) => {
|
|
36
|
+
const incomingId = request.headers[header];
|
|
37
|
+
const sanitized = typeof incomingId === "string" ? incomingId.trim() : "";
|
|
38
|
+
const requestId = sanitized.length > 0 && sanitized.length <= 128 && /^[\w.:-]+$/.test(sanitized) ? sanitized : generator();
|
|
39
|
+
request.id = requestId;
|
|
40
|
+
request.requestId = requestId;
|
|
41
|
+
});
|
|
42
|
+
if (setResponseHeader) fastify.addHook("onSend", async (request, reply) => {
|
|
43
|
+
reply.header(header, request.requestId);
|
|
44
|
+
});
|
|
45
|
+
fastify.log?.debug?.("Request ID plugin registered");
|
|
46
|
+
};
|
|
47
|
+
var requestId_default = fp(requestIdPlugin, {
|
|
48
|
+
name: "arc-request-id",
|
|
49
|
+
fastify: "5.x"
|
|
50
|
+
});
|
|
51
|
+
|
|
52
|
+
//#endregion
|
|
53
|
+
//#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
|
+
function createHttpMetrics() {
|
|
74
|
+
return {
|
|
75
|
+
requestsTotal: {},
|
|
76
|
+
requestDurations: [],
|
|
77
|
+
_ringIndex: 0,
|
|
78
|
+
startTime: Date.now()
|
|
79
|
+
};
|
|
80
|
+
}
|
|
81
|
+
const healthPlugin = async (fastify, opts = {}) => {
|
|
82
|
+
const { prefix = "/_health", checks = [], metrics = false, metricsCollector, version, collectHttpMetrics = metrics } = opts;
|
|
83
|
+
const httpMetrics = createHttpMetrics();
|
|
84
|
+
fastify.get(`${prefix}/live`, { schema: {
|
|
85
|
+
tags: ["Health"],
|
|
86
|
+
summary: "Liveness probe",
|
|
87
|
+
description: "Returns 200 if the process is alive",
|
|
88
|
+
response: { 200: {
|
|
89
|
+
type: "object",
|
|
90
|
+
properties: {
|
|
91
|
+
status: {
|
|
92
|
+
type: "string",
|
|
93
|
+
enum: ["ok"]
|
|
94
|
+
},
|
|
95
|
+
timestamp: { type: "string" },
|
|
96
|
+
version: { type: "string" }
|
|
97
|
+
}
|
|
98
|
+
} }
|
|
99
|
+
} }, async () => {
|
|
100
|
+
return {
|
|
101
|
+
status: "ok",
|
|
102
|
+
timestamp: (/* @__PURE__ */ new Date()).toISOString(),
|
|
103
|
+
...version ? { version } : {}
|
|
104
|
+
};
|
|
105
|
+
});
|
|
106
|
+
fastify.get(`${prefix}/ready`, { schema: {
|
|
107
|
+
tags: ["Health"],
|
|
108
|
+
summary: "Readiness probe",
|
|
109
|
+
description: "Returns 200 if all dependencies are healthy",
|
|
110
|
+
response: {
|
|
111
|
+
200: {
|
|
112
|
+
type: "object",
|
|
113
|
+
properties: {
|
|
114
|
+
status: {
|
|
115
|
+
type: "string",
|
|
116
|
+
enum: ["ready", "not_ready"]
|
|
117
|
+
},
|
|
118
|
+
timestamp: { type: "string" },
|
|
119
|
+
checks: {
|
|
120
|
+
type: "array",
|
|
121
|
+
items: {
|
|
122
|
+
type: "object",
|
|
123
|
+
properties: {
|
|
124
|
+
name: { type: "string" },
|
|
125
|
+
healthy: { type: "boolean" },
|
|
126
|
+
duration: { type: "number" },
|
|
127
|
+
error: { type: "string" }
|
|
128
|
+
}
|
|
129
|
+
}
|
|
130
|
+
}
|
|
131
|
+
}
|
|
132
|
+
},
|
|
133
|
+
503: {
|
|
134
|
+
type: "object",
|
|
135
|
+
properties: {
|
|
136
|
+
status: {
|
|
137
|
+
type: "string",
|
|
138
|
+
enum: ["not_ready"]
|
|
139
|
+
},
|
|
140
|
+
timestamp: { type: "string" },
|
|
141
|
+
checks: { type: "array" }
|
|
142
|
+
}
|
|
143
|
+
}
|
|
144
|
+
}
|
|
145
|
+
} }, async (_, reply) => {
|
|
146
|
+
const results = await runChecks(checks);
|
|
147
|
+
const criticalFailed = results.some((r) => !r.healthy && (checks.find((c) => c.name === r.name)?.critical ?? true));
|
|
148
|
+
const response = {
|
|
149
|
+
status: criticalFailed ? "not_ready" : "ready",
|
|
150
|
+
timestamp: (/* @__PURE__ */ new Date()).toISOString(),
|
|
151
|
+
checks: results
|
|
152
|
+
};
|
|
153
|
+
if (criticalFailed) reply.code(503);
|
|
154
|
+
return response;
|
|
155
|
+
});
|
|
156
|
+
if (metrics) fastify.get(`${prefix}/metrics`, async (_, reply) => {
|
|
157
|
+
reply.type("text/plain; charset=utf-8");
|
|
158
|
+
if (metricsCollector) return await metricsCollector();
|
|
159
|
+
const uptime = process.uptime();
|
|
160
|
+
const memory = process.memoryUsage();
|
|
161
|
+
const cpu = process.cpuUsage();
|
|
162
|
+
const lines = [
|
|
163
|
+
"# HELP process_uptime_seconds Process uptime in seconds",
|
|
164
|
+
"# TYPE process_uptime_seconds gauge",
|
|
165
|
+
`process_uptime_seconds ${uptime.toFixed(2)}`,
|
|
166
|
+
"",
|
|
167
|
+
"# HELP process_memory_heap_bytes Heap memory usage in bytes",
|
|
168
|
+
"# TYPE process_memory_heap_bytes gauge",
|
|
169
|
+
`process_memory_heap_bytes{type="used"} ${memory.heapUsed}`,
|
|
170
|
+
`process_memory_heap_bytes{type="total"} ${memory.heapTotal}`,
|
|
171
|
+
"",
|
|
172
|
+
"# HELP process_memory_rss_bytes RSS memory in bytes",
|
|
173
|
+
"# TYPE process_memory_rss_bytes gauge",
|
|
174
|
+
`process_memory_rss_bytes ${memory.rss}`,
|
|
175
|
+
"",
|
|
176
|
+
"# HELP process_memory_external_bytes External memory in bytes",
|
|
177
|
+
"# TYPE process_memory_external_bytes gauge",
|
|
178
|
+
`process_memory_external_bytes ${memory.external}`,
|
|
179
|
+
"",
|
|
180
|
+
"# HELP process_cpu_user_microseconds User CPU time in microseconds",
|
|
181
|
+
"# TYPE process_cpu_user_microseconds counter",
|
|
182
|
+
`process_cpu_user_microseconds ${cpu.user}`,
|
|
183
|
+
"",
|
|
184
|
+
"# HELP process_cpu_system_microseconds System CPU time in microseconds",
|
|
185
|
+
"# TYPE process_cpu_system_microseconds counter",
|
|
186
|
+
`process_cpu_system_microseconds ${cpu.system}`,
|
|
187
|
+
""
|
|
188
|
+
];
|
|
189
|
+
if (collectHttpMetrics && Object.keys(httpMetrics.requestsTotal).length > 0) {
|
|
190
|
+
lines.push("# HELP http_requests_total Total HTTP requests by status code", "# TYPE http_requests_total counter");
|
|
191
|
+
for (const [status, count] of Object.entries(httpMetrics.requestsTotal)) lines.push(`http_requests_total{status="${status}"} ${count}`);
|
|
192
|
+
lines.push("");
|
|
193
|
+
if (httpMetrics.requestDurations.length > 0) {
|
|
194
|
+
const sorted = [...httpMetrics.requestDurations].sort((a, b) => a - b);
|
|
195
|
+
const p50 = sorted[Math.floor(sorted.length * .5)] || 0;
|
|
196
|
+
const p95 = sorted[Math.floor(sorted.length * .95)] || 0;
|
|
197
|
+
const p99 = sorted[Math.floor(sorted.length * .99)] || 0;
|
|
198
|
+
const sum = sorted.reduce((a, b) => a + b, 0);
|
|
199
|
+
lines.push("# HELP http_request_duration_milliseconds HTTP request duration", "# TYPE http_request_duration_milliseconds summary", `http_request_duration_milliseconds{quantile="0.5"} ${p50.toFixed(2)}`, `http_request_duration_milliseconds{quantile="0.95"} ${p95.toFixed(2)}`, `http_request_duration_milliseconds{quantile="0.99"} ${p99.toFixed(2)}`, `http_request_duration_milliseconds_sum ${sum.toFixed(2)}`, `http_request_duration_milliseconds_count ${sorted.length}`, "");
|
|
200
|
+
}
|
|
201
|
+
}
|
|
202
|
+
return lines.join("\n");
|
|
203
|
+
});
|
|
204
|
+
if (collectHttpMetrics) {
|
|
205
|
+
fastify.addHook("onRequest", async (request) => {
|
|
206
|
+
request._startTime = Date.now();
|
|
207
|
+
});
|
|
208
|
+
fastify.addHook("onResponse", async (request, reply) => {
|
|
209
|
+
const duration = Date.now() - (request._startTime ?? Date.now());
|
|
210
|
+
const statusBucket = `${Math.floor(reply.statusCode / 100)}xx`;
|
|
211
|
+
httpMetrics.requestsTotal[statusBucket] = (httpMetrics.requestsTotal[statusBucket] || 0) + 1;
|
|
212
|
+
if (httpMetrics.requestDurations.length < 1e4) httpMetrics.requestDurations.push(duration);
|
|
213
|
+
else httpMetrics.requestDurations[httpMetrics._ringIndex % 1e4] = duration;
|
|
214
|
+
httpMetrics._ringIndex = httpMetrics._ringIndex + 1;
|
|
215
|
+
});
|
|
216
|
+
}
|
|
217
|
+
fastify.log?.debug?.(`Health plugin registered at ${prefix}`);
|
|
218
|
+
};
|
|
219
|
+
/**
|
|
220
|
+
* Run all health checks with timeout
|
|
221
|
+
*/
|
|
222
|
+
async function runChecks(checks) {
|
|
223
|
+
const results = [];
|
|
224
|
+
for (const check of checks) {
|
|
225
|
+
const start = Date.now();
|
|
226
|
+
const timeout = check.timeout ?? 5e3;
|
|
227
|
+
let timer;
|
|
228
|
+
try {
|
|
229
|
+
const checkPromise = Promise.resolve(check.check());
|
|
230
|
+
const timeoutPromise = new Promise((_, reject) => {
|
|
231
|
+
timer = setTimeout(() => reject(/* @__PURE__ */ new Error("Health check timeout")), timeout);
|
|
232
|
+
});
|
|
233
|
+
const healthy = await Promise.race([checkPromise, timeoutPromise]);
|
|
234
|
+
results.push({
|
|
235
|
+
name: check.name,
|
|
236
|
+
healthy: Boolean(healthy),
|
|
237
|
+
duration: Date.now() - start
|
|
238
|
+
});
|
|
239
|
+
} catch (err) {
|
|
240
|
+
results.push({
|
|
241
|
+
name: check.name,
|
|
242
|
+
healthy: false,
|
|
243
|
+
duration: Date.now() - start,
|
|
244
|
+
error: err.message
|
|
245
|
+
});
|
|
246
|
+
} finally {
|
|
247
|
+
if (timer) clearTimeout(timer);
|
|
248
|
+
}
|
|
249
|
+
}
|
|
250
|
+
return results;
|
|
251
|
+
}
|
|
252
|
+
var health_default = fp(healthPlugin, {
|
|
253
|
+
name: "arc-health",
|
|
254
|
+
fastify: "5.x"
|
|
255
|
+
});
|
|
256
|
+
|
|
257
|
+
//#endregion
|
|
258
|
+
//#region src/plugins/gracefulShutdown.ts
|
|
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
|
|
413
|
+
/**
|
|
414
|
+
* Arc Core Plugin
|
|
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
|
|
420
|
+
*
|
|
421
|
+
* This solves the global singleton leak problem where multiple
|
|
422
|
+
* app instances (e.g., in tests) would share state.
|
|
423
|
+
*
|
|
424
|
+
* @example
|
|
425
|
+
* import { arcCorePlugin } from '@classytic/arc';
|
|
426
|
+
*
|
|
427
|
+
* const app = Fastify();
|
|
428
|
+
* await app.register(arcCorePlugin);
|
|
429
|
+
*
|
|
430
|
+
* // Now use instance-scoped hooks
|
|
431
|
+
* app.arc.hooks.before('product', 'create', async (ctx) => {
|
|
432
|
+
* ctx.data.slug = slugify(ctx.data.name);
|
|
433
|
+
* });
|
|
434
|
+
*/
|
|
435
|
+
const arcCorePlugin = async (fastify, opts = {}) => {
|
|
436
|
+
const { emitEvents = true, hookSystem, registry } = opts;
|
|
437
|
+
const actualHookSystem = hookSystem ?? new HookSystem();
|
|
438
|
+
const actualRegistry = registry ?? new ResourceRegistry();
|
|
439
|
+
fastify.decorate("arc", {
|
|
440
|
+
hooks: actualHookSystem,
|
|
441
|
+
registry: actualRegistry,
|
|
442
|
+
emitEvents,
|
|
443
|
+
externalOpenApiPaths: [],
|
|
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 {}
|
|
502
|
+
});
|
|
503
|
+
fastify.addHook("onClose", async () => {
|
|
504
|
+
actualHookSystem.clear();
|
|
505
|
+
actualRegistry._clear();
|
|
506
|
+
});
|
|
507
|
+
fastify.log?.debug?.("Arc core plugin enabled (instance-scoped hooks & registry)");
|
|
508
|
+
};
|
|
509
|
+
/** Extract document ID from a result (handles Mongoose docs and plain objects) */
|
|
510
|
+
function extractId(doc) {
|
|
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",
|
|
518
|
+
fastify: "5.x"
|
|
519
|
+
});
|
|
520
|
+
|
|
521
|
+
//#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 };
|
|
523
|
+
//# sourceMappingURL=index.mjs.map
|