@classytic/arc 2.9.1 → 2.10.8
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 +20 -91
- package/dist/{BaseController-Vu2yc56T.mjs → BaseController-DVNKvoX4.mjs} +154 -170
- package/dist/{ResourceRegistry-Dq3_zBQP.mjs → ResourceRegistry-CcN2LVrc.mjs} +1 -1
- package/dist/actionPermissions-TUVR3uiZ.mjs +22 -0
- package/dist/adapters/index.d.mts +3 -3
- package/dist/adapters/index.mjs +2 -2
- package/dist/{adapters-BBqAVvPK.mjs → adapters-BXY4i-hw.mjs} +210 -41
- package/dist/audit/index.d.mts +38 -3
- package/dist/audit/index.mjs +54 -22
- package/dist/auth/index.d.mts +2 -2
- package/dist/auth/index.mjs +3 -3
- package/dist/cache/index.d.mts +17 -15
- package/dist/cache/index.mjs +16 -15
- package/dist/{caching-CjybdRwx.mjs → caching-3h93rkJM.mjs} +8 -3
- package/dist/cli/commands/describe.mjs +1 -1
- package/dist/cli/commands/docs.mjs +2 -2
- package/dist/cli/commands/init.mjs +1 -1
- package/dist/cli/commands/introspect.mjs +1 -1
- package/dist/context/index.d.mts +58 -0
- package/dist/context/index.mjs +2 -0
- package/dist/core/index.d.mts +2 -2
- package/dist/core/index.mjs +3 -4
- package/dist/{defineResource-C__jkwvs.mjs → core-3MWJosCH.mjs} +174 -94
- package/dist/{createActionRouter-DH1YFL9m.mjs → createActionRouter-C8UUB3Px.mjs} +1 -1
- package/dist/{createApp-CBJUJKGP.mjs → createApp-BwnEAO2h.mjs} +53 -19
- package/dist/docs/index.d.mts +1 -1
- package/dist/docs/index.mjs +2 -2
- package/dist/{elevation-DxQ6ACbt.mjs → elevation-Dci0AYLT.mjs} +2 -2
- package/dist/errorHandler-2ii4RIYr.d.mts +114 -0
- package/dist/{errorHandler-CZDW4EXS.mjs → errorHandler-CSxe7KIM.mjs} +1 -1
- package/dist/{eventPlugin-Dl7MoVWH.mjs → eventPlugin-ByU4Cv0e.mjs} +1 -1
- package/dist/{eventPlugin-BxvaCIZF.d.mts → eventPlugin-D1ThQ1Pp.d.mts} +1 -1
- package/dist/events/index.d.mts +8 -5
- package/dist/events/index.mjs +87 -52
- package/dist/events/transports/redis-stream-entry.d.mts +1 -1
- package/dist/events/transports/redis.d.mts +1 -1
- package/dist/factory/index.d.mts +1 -1
- package/dist/factory/index.mjs +1 -1
- package/dist/{types-DZi1aYhm.d.mts → fields-C8Y0XLAu.d.mts} +122 -2
- package/dist/hooks/index.d.mts +1 -1
- package/dist/idempotency/index.d.mts +5 -2
- package/dist/idempotency/index.mjs +46 -37
- package/dist/{interface-YrWsmKqE.d.mts → index-BGbpGVyM.d.mts} +2107 -2756
- package/dist/{index-CtGKT0lf.d.mts → index-BziRPS4H.d.mts} +81 -7
- package/dist/{index-C-xjcA6F.d.mts → index-C_Noptz-.d.mts} +284 -409
- package/dist/{index-Cibkchnx.d.mts → index-EqQN6p0W.d.mts} +3 -3
- package/dist/index.d.mts +6 -219
- package/dist/index.mjs +10 -131
- package/dist/integrations/event-gateway.d.mts +1 -1
- package/dist/integrations/event-gateway.mjs +1 -1
- package/dist/integrations/index.d.mts +1 -1
- package/dist/integrations/mcp/index.d.mts +2 -2
- package/dist/integrations/mcp/index.mjs +1 -1
- package/dist/integrations/mcp/testing.d.mts +1 -1
- package/dist/integrations/mcp/testing.mjs +1 -1
- package/dist/interface-yhyb_pLY.d.mts +77 -0
- package/dist/logger/index.d.mts +81 -0
- package/dist/{logger-CDjpjySd.mjs → logger/index.mjs} +1 -6
- package/dist/{memory-BFAYkf8H.mjs → memory-DqI-449b.mjs} +23 -8
- package/dist/middleware/index.d.mts +109 -0
- package/dist/middleware/index.mjs +70 -0
- package/dist/multipartBody-CUQGVlM_.mjs +123 -0
- package/dist/{openapi-CXuTG1M9.mjs → openapi-DpNpqBmo.mjs} +9 -7
- package/dist/org/index.d.mts +2 -2
- package/dist/permissions/index.d.mts +3 -4
- package/dist/permissions/index.mjs +5 -5
- package/dist/{permissions-oNZawnkR.mjs → permissions-wkqRwicB.mjs} +315 -397
- package/dist/pipe-CGJxqDGx.mjs +62 -0
- package/dist/pipeline/index.d.mts +62 -0
- package/dist/pipeline/index.mjs +53 -0
- package/dist/plugins/index.d.mts +23 -3
- package/dist/plugins/index.mjs +9 -11
- package/dist/plugins/response-cache.mjs +1 -1
- package/dist/plugins/tracing-entry.mjs +1 -1
- package/dist/presets/filesUpload.d.mts +3 -3
- package/dist/presets/filesUpload.mjs +255 -1
- package/dist/presets/index.d.mts +1 -1
- package/dist/presets/index.mjs +2 -2
- package/dist/presets/multiTenant.d.mts +1 -1
- package/dist/presets/multiTenant.mjs +43 -9
- package/dist/presets/search.d.mts +91 -4
- package/dist/presets/search.mjs +1 -1
- package/dist/{presets-hM4WhNWY.mjs → presets-CrwOvuXI.mjs} +1 -1
- package/dist/{queryCachePlugin-DbUVroUG.mjs → queryCachePlugin-ChLNZvFT.mjs} +9 -9
- package/dist/{queryCachePlugin-CnTZZTC5.d.mts → queryCachePlugin-Dumka73q.d.mts} +1 -1
- package/dist/{queryParser-Cs-6SHQK.mjs → queryParser-NR__Qiju.mjs} +69 -2
- package/dist/{redis-stream-Bz-4q96t.d.mts → redis-stream-bkO88VHx.d.mts} +1 -1
- package/dist/registry/index.d.mts +1 -1
- package/dist/registry/index.mjs +1 -1
- package/dist/{requestContext-DYtmNpm5.mjs → requestContext-C38GskNt.mjs} +1 -1
- package/dist/{resourceToTools-C3cWymnW.mjs → resourceToTools-BhF3JV5p.mjs} +8 -3
- package/dist/scope/index.d.mts +2 -2
- package/dist/scope/index.mjs +2 -2
- package/dist/{sse-CJpt7LGI.mjs → sse-D8UeDwis.mjs} +1 -1
- package/dist/{store-helpers-DFiZl5TL.mjs → store-helpers-DYYUQbQN.mjs} +4 -0
- package/dist/testing/index.d.mts +6 -5
- package/dist/testing/index.mjs +17 -10
- package/dist/types/index.d.mts +5 -5
- package/dist/types/index.mjs +1 -31
- package/dist/types-CDnTEpga.mjs +27 -0
- package/dist/{types-CoSzA-s-.d.mts → types-CVKBssX5.d.mts} +1 -1
- package/dist/{types-CunEX4UX.d.mts → types-CVdgPXBW.d.mts} +20 -7
- package/dist/utils/index.d.mts +277 -3
- package/dist/utils/index.mjs +4 -5
- package/dist/{utils-B7FuRr9w.mjs → utils-LMwVidKy.mjs} +303 -2
- package/dist/{versioning-Cm8qoFDg.mjs → versioning-B6mimogM.mjs} +3 -5
- package/dist/versioning-CeUXHfjw.d.mts +117 -0
- package/package.json +31 -18
- package/skills/arc/SKILL.md +8 -12
- package/skills/arc/references/production.md +0 -41
- package/dist/circuitBreaker-CvXkjfrW.d.mts +0 -206
- package/dist/circuitBreaker-l18oRgL5.mjs +0 -284
- package/dist/core-DNncu0xF.mjs +0 -34
- package/dist/dynamic/index.d.mts +0 -93
- package/dist/dynamic/index.mjs +0 -122
- package/dist/errorHandler-DixGcttC.d.mts +0 -218
- package/dist/fields-BC7zcmI9.d.mts +0 -121
- package/dist/filesUpload-q8oHt--L.mjs +0 -377
- package/dist/interface-DplgQO2e.d.mts +0 -54
- package/dist/policies/index.d.mts +0 -425
- package/dist/policies/index.mjs +0 -318
- package/dist/rpc/index.d.mts +0 -90
- package/dist/rpc/index.mjs +0 -248
- /package/dist/{EventTransport-CqZ8FyM_.d.mts → EventTransport-CfVEGaEl.d.mts} +0 -0
- /package/dist/{applyPermissionResult-bqGpo9ML.mjs → applyPermissionResult-QhV1Pa-g.mjs} +0 -0
- /package/dist/{constants-Cxde4rpC.mjs → constants-BhY1OHoH.mjs} +0 -0
- /package/dist/{elevation-B6S5csVA.d.mts → elevation-s5ykdNHr.d.mts} +0 -0
- /package/dist/{errors-CqWnSqM-.mjs → errors-BqdUDja_.mjs} +0 -0
- /package/dist/{fields-CU6FlaDV.mjs → fields-CTMWOUDt.mjs} +0 -0
- /package/dist/{keys-qcD-TVJl.mjs → keys-nWQGUTu1.mjs} +0 -0
- /package/dist/{types-ZUu_h0jp.mjs → types-D57iXYb8.mjs} +0 -0
- /package/dist/{types-BD85MlEK.d.mts → types-tgR4Pt8F.d.mts} +0 -0
|
@@ -10,7 +10,7 @@ const versioningPlugin = async (fastify, opts) => {
|
|
|
10
10
|
const { type, defaultVersion = "1", headerName = "accept-version", responseHeader = "x-api-version", deprecated = [], sunset } = opts;
|
|
11
11
|
const deprecatedSet = new Set(deprecated);
|
|
12
12
|
fastify.decorateRequest("apiVersion", defaultVersion);
|
|
13
|
-
fastify.addHook("onRequest", async (request) => {
|
|
13
|
+
fastify.addHook("onRequest", async (request, reply) => {
|
|
14
14
|
let version = defaultVersion;
|
|
15
15
|
if (type === "header") {
|
|
16
16
|
const headerValue = request.headers[headerName];
|
|
@@ -20,10 +20,8 @@ const versioningPlugin = async (fastify, opts) => {
|
|
|
20
20
|
if (match) version = match[1] ?? defaultVersion;
|
|
21
21
|
}
|
|
22
22
|
request.apiVersion = version;
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
reply.header(responseHeader, request.apiVersion);
|
|
26
|
-
if (deprecatedSet.has(request.apiVersion)) {
|
|
23
|
+
reply.header(responseHeader, version);
|
|
24
|
+
if (deprecatedSet.has(version)) {
|
|
27
25
|
reply.header("deprecation", "true");
|
|
28
26
|
reply.header("sunset", sunset ?? new Date(Date.now() + 2160 * 60 * 60 * 1e3).toISOString());
|
|
29
27
|
}
|
|
@@ -0,0 +1,117 @@
|
|
|
1
|
+
import { n as DomainEvent } from "./EventTransport-CfVEGaEl.mjs";
|
|
2
|
+
import { FastifyPluginAsync, FastifyRequest } from "fastify";
|
|
3
|
+
|
|
4
|
+
//#region src/plugins/caching.d.ts
|
|
5
|
+
interface CachingRule {
|
|
6
|
+
/** Path prefix to match (e.g., '/api/products') */
|
|
7
|
+
match: string;
|
|
8
|
+
/** Cache-Control max-age in seconds */
|
|
9
|
+
maxAge: number;
|
|
10
|
+
/** Cache-Control: private vs public (default: public) */
|
|
11
|
+
private?: boolean;
|
|
12
|
+
/** stale-while-revalidate directive in seconds */
|
|
13
|
+
staleWhileRevalidate?: number;
|
|
14
|
+
}
|
|
15
|
+
interface CachingOptions {
|
|
16
|
+
/** Default max-age in seconds for Cache-Control (default: 0 = no-cache) */
|
|
17
|
+
maxAge?: number;
|
|
18
|
+
/** Enable ETag generation (default: true) */
|
|
19
|
+
etag?: boolean;
|
|
20
|
+
/** Enable conditional requests — 304 Not Modified (default: true) */
|
|
21
|
+
conditional?: boolean;
|
|
22
|
+
/** HTTP methods to cache (default: ['GET', 'HEAD']) */
|
|
23
|
+
methods?: string[];
|
|
24
|
+
/** Paths to exclude from caching (prefix match) */
|
|
25
|
+
exclude?: string[];
|
|
26
|
+
/** Custom cache rules per path prefix */
|
|
27
|
+
rules?: CachingRule[];
|
|
28
|
+
}
|
|
29
|
+
declare const cachingPlugin: FastifyPluginAsync<CachingOptions>;
|
|
30
|
+
declare const _default$3: FastifyPluginAsync<CachingOptions>;
|
|
31
|
+
//#endregion
|
|
32
|
+
//#region src/plugins/sse.d.ts
|
|
33
|
+
interface SSEOptions {
|
|
34
|
+
/** SSE endpoint path (default: '/events/stream') */
|
|
35
|
+
path?: string;
|
|
36
|
+
/** Require authentication (default: true) */
|
|
37
|
+
requireAuth?: boolean;
|
|
38
|
+
/** Event patterns to stream (default: ['*'] = all) */
|
|
39
|
+
patterns?: string[];
|
|
40
|
+
/** Heartbeat interval in ms (default: 30000) */
|
|
41
|
+
heartbeat?: number;
|
|
42
|
+
/** Filter events by organizationId from request.scope (default: false) */
|
|
43
|
+
orgScoped?: boolean;
|
|
44
|
+
/** Custom event filter function */
|
|
45
|
+
filter?: (event: DomainEvent<unknown>, request: FastifyRequest) => boolean;
|
|
46
|
+
}
|
|
47
|
+
declare const ssePlugin: FastifyPluginAsync<SSEOptions>;
|
|
48
|
+
declare const _default$2: FastifyPluginAsync<SSEOptions>;
|
|
49
|
+
//#endregion
|
|
50
|
+
//#region src/plugins/metrics.d.ts
|
|
51
|
+
interface MetricsOptions {
|
|
52
|
+
/** Endpoint path (default: '/_metrics') */
|
|
53
|
+
path?: string;
|
|
54
|
+
/** Prefix for all metric names (default: 'arc') */
|
|
55
|
+
prefix?: string;
|
|
56
|
+
/** Called after metrics are collected (for OTLP push, etc.) */
|
|
57
|
+
onCollect?: (metrics: MetricEntry[]) => void;
|
|
58
|
+
}
|
|
59
|
+
interface MetricEntry {
|
|
60
|
+
name: string;
|
|
61
|
+
type: "counter" | "histogram" | "gauge";
|
|
62
|
+
help: string;
|
|
63
|
+
values: Array<{
|
|
64
|
+
labels: Record<string, string>;
|
|
65
|
+
value: number;
|
|
66
|
+
}>;
|
|
67
|
+
}
|
|
68
|
+
interface MetricsCollector {
|
|
69
|
+
/** Get all metrics as structured data */
|
|
70
|
+
collect(): MetricEntry[];
|
|
71
|
+
/** Reset all metrics */
|
|
72
|
+
reset(): void;
|
|
73
|
+
/** Record a CRUD operation */
|
|
74
|
+
recordOperation(resource: string, operation: string, status: number, durationMs: number): void;
|
|
75
|
+
/** Record a cache hit */
|
|
76
|
+
recordCacheHit(resource: string): void;
|
|
77
|
+
/** Record a cache miss */
|
|
78
|
+
recordCacheMiss(resource: string): void;
|
|
79
|
+
/** Record an event publish */
|
|
80
|
+
recordEventPublish(eventType: string): void;
|
|
81
|
+
/** Record an event consume */
|
|
82
|
+
recordEventConsume(eventType: string): void;
|
|
83
|
+
/** Record a circuit breaker state change */
|
|
84
|
+
recordCircuitBreakerState(service: string, state: string): void;
|
|
85
|
+
}
|
|
86
|
+
declare module "fastify" {
|
|
87
|
+
interface FastifyInstance {
|
|
88
|
+
metrics: MetricsCollector;
|
|
89
|
+
}
|
|
90
|
+
}
|
|
91
|
+
declare const metricsPlugin: FastifyPluginAsync<MetricsOptions>;
|
|
92
|
+
declare const _default$1: FastifyPluginAsync<MetricsOptions>;
|
|
93
|
+
//#endregion
|
|
94
|
+
//#region src/plugins/versioning.d.ts
|
|
95
|
+
interface VersioningOptions {
|
|
96
|
+
/** Versioning strategy */
|
|
97
|
+
type: "header" | "prefix";
|
|
98
|
+
/** Default version when none specified (default: '1') */
|
|
99
|
+
defaultVersion?: string;
|
|
100
|
+
/** Header name to read (default: 'accept-version') */
|
|
101
|
+
headerName?: string;
|
|
102
|
+
/** Response header name (default: 'x-api-version') */
|
|
103
|
+
responseHeader?: string;
|
|
104
|
+
/** Deprecated versions — adds Deprecation + Sunset headers */
|
|
105
|
+
deprecated?: string[];
|
|
106
|
+
/** Sunset date for deprecated versions (ISO 8601) */
|
|
107
|
+
sunset?: string;
|
|
108
|
+
}
|
|
109
|
+
declare module "fastify" {
|
|
110
|
+
interface FastifyRequest {
|
|
111
|
+
apiVersion: string;
|
|
112
|
+
}
|
|
113
|
+
}
|
|
114
|
+
declare const versioningPlugin: FastifyPluginAsync<VersioningOptions>;
|
|
115
|
+
declare const _default: FastifyPluginAsync<VersioningOptions>;
|
|
116
|
+
//#endregion
|
|
117
|
+
export { MetricsCollector as a, metricsPlugin as c, ssePlugin as d, CachingOptions as f, cachingPlugin as h, MetricEntry as i, SSEOptions as l, _default$3 as m, _default as n, MetricsOptions as o, CachingRule as p, versioningPlugin as r, _default$1 as s, VersioningOptions as t, _default$2 as u };
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@classytic/arc",
|
|
3
|
-
"version": "2.
|
|
3
|
+
"version": "2.10.8",
|
|
4
4
|
"description": "Resource-oriented backend framework for Fastify — clean, minimal, powerful, tree-shakable",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"exports": {
|
|
@@ -64,6 +64,22 @@
|
|
|
64
64
|
"types": "./dist/hooks/index.d.mts",
|
|
65
65
|
"default": "./dist/hooks/index.mjs"
|
|
66
66
|
},
|
|
67
|
+
"./middleware": {
|
|
68
|
+
"types": "./dist/middleware/index.d.mts",
|
|
69
|
+
"default": "./dist/middleware/index.mjs"
|
|
70
|
+
},
|
|
71
|
+
"./pipeline": {
|
|
72
|
+
"types": "./dist/pipeline/index.d.mts",
|
|
73
|
+
"default": "./dist/pipeline/index.mjs"
|
|
74
|
+
},
|
|
75
|
+
"./context": {
|
|
76
|
+
"types": "./dist/context/index.d.mts",
|
|
77
|
+
"default": "./dist/context/index.mjs"
|
|
78
|
+
},
|
|
79
|
+
"./logger": {
|
|
80
|
+
"types": "./dist/logger/index.d.mts",
|
|
81
|
+
"default": "./dist/logger/index.mjs"
|
|
82
|
+
},
|
|
67
83
|
"./registry": {
|
|
68
84
|
"types": "./dist/registry/index.d.mts",
|
|
69
85
|
"default": "./dist/registry/index.mjs"
|
|
@@ -112,10 +128,6 @@
|
|
|
112
128
|
"types": "./dist/events/transports/redis-stream-entry.d.mts",
|
|
113
129
|
"default": "./dist/events/transports/redis-stream-entry.mjs"
|
|
114
130
|
},
|
|
115
|
-
"./dynamic": {
|
|
116
|
-
"types": "./dist/dynamic/index.d.mts",
|
|
117
|
-
"default": "./dist/dynamic/index.mjs"
|
|
118
|
-
},
|
|
119
131
|
"./testing": {
|
|
120
132
|
"types": "./dist/testing/index.d.mts",
|
|
121
133
|
"default": "./dist/testing/index.mjs"
|
|
@@ -124,10 +136,6 @@
|
|
|
124
136
|
"types": "./dist/testing/storageContract.d.mts",
|
|
125
137
|
"default": "./dist/testing/storageContract.mjs"
|
|
126
138
|
},
|
|
127
|
-
"./policies": {
|
|
128
|
-
"types": "./dist/policies/index.d.mts",
|
|
129
|
-
"default": "./dist/policies/index.mjs"
|
|
130
|
-
},
|
|
131
139
|
"./factory": {
|
|
132
140
|
"types": "./dist/factory/index.d.mts",
|
|
133
141
|
"default": "./dist/factory/index.mjs"
|
|
@@ -188,10 +196,6 @@
|
|
|
188
196
|
"types": "./dist/scope/index.d.mts",
|
|
189
197
|
"default": "./dist/scope/index.mjs"
|
|
190
198
|
},
|
|
191
|
-
"./rpc": {
|
|
192
|
-
"types": "./dist/rpc/index.d.mts",
|
|
193
|
-
"default": "./dist/rpc/index.mjs"
|
|
194
|
-
},
|
|
195
199
|
"./mcp": {
|
|
196
200
|
"types": "./dist/integrations/mcp/index.d.mts",
|
|
197
201
|
"default": "./dist/integrations/mcp/index.mjs"
|
|
@@ -235,7 +239,8 @@
|
|
|
235
239
|
"node": ">=22"
|
|
236
240
|
},
|
|
237
241
|
"peerDependencies": {
|
|
238
|
-
"@classytic/mongokit": ">=3.
|
|
242
|
+
"@classytic/mongokit": ">=3.11.0",
|
|
243
|
+
"@classytic/repo-core": ">=0.2.0",
|
|
239
244
|
"@classytic/streamline": ">=2.1.0",
|
|
240
245
|
"@fastify/cors": ">=11.0.0",
|
|
241
246
|
"@fastify/helmet": ">=13.0.0",
|
|
@@ -255,11 +260,11 @@
|
|
|
255
260
|
"@sinclair/typebox": ">=0.34.0",
|
|
256
261
|
"better-auth": ">=1.6.2",
|
|
257
262
|
"bullmq": ">=5.0.0",
|
|
258
|
-
"fastify": "
|
|
263
|
+
"fastify": "^5.8.5",
|
|
259
264
|
"fastify-raw-body": ">=5.0.0",
|
|
260
265
|
"ioredis": ">=5.0.0",
|
|
261
266
|
"mongodb": ">=7.0.0",
|
|
262
|
-
"mongoose": "
|
|
267
|
+
"mongoose": "^9.4.1",
|
|
263
268
|
"pino-pretty": ">=13.0.0",
|
|
264
269
|
"zod": ">=4.0.0"
|
|
265
270
|
},
|
|
@@ -267,6 +272,9 @@
|
|
|
267
272
|
"@classytic/mongokit": {
|
|
268
273
|
"optional": true
|
|
269
274
|
},
|
|
275
|
+
"@classytic/repo-core": {
|
|
276
|
+
"optional": true
|
|
277
|
+
},
|
|
270
278
|
"mongodb": {
|
|
271
279
|
"optional": true
|
|
272
280
|
},
|
|
@@ -352,9 +360,12 @@
|
|
|
352
360
|
"secure-json-parse": "^4.1.0"
|
|
353
361
|
},
|
|
354
362
|
"devDependencies": {
|
|
363
|
+
"@better-auth/drizzle-adapter": "^1.6.2",
|
|
355
364
|
"@better-auth/mongo-adapter": "^1.6.2",
|
|
356
365
|
"@biomejs/biome": "^2.4.11",
|
|
357
|
-
"@classytic/mongokit": "^3.
|
|
366
|
+
"@classytic/mongokit": "^3.11.0",
|
|
367
|
+
"@classytic/repo-core": "^0.2.0",
|
|
368
|
+
"@classytic/sqlitekit": "^0.2.0",
|
|
358
369
|
"@classytic/streamline": "^2.1.0",
|
|
359
370
|
"@fastify/cors": "^11.2.0",
|
|
360
371
|
"@fastify/helmet": "^13.0.2",
|
|
@@ -372,8 +383,10 @@
|
|
|
372
383
|
"@vitest/coverage-v8": "^3.2.4",
|
|
373
384
|
"ajv": "^8.18.0",
|
|
374
385
|
"better-auth": "^1.6.2",
|
|
386
|
+
"better-sqlite3": "^12.9.0",
|
|
375
387
|
"bullmq": "^5.73.5",
|
|
376
388
|
"dotenv": "^17.4.2",
|
|
389
|
+
"drizzle-orm": "^0.45.2",
|
|
377
390
|
"fast-check": "^4.6.0",
|
|
378
391
|
"fastify-raw-body": "^5.0.0",
|
|
379
392
|
"ioredis": "^5.10.1",
|
|
@@ -381,7 +394,7 @@
|
|
|
381
394
|
"knip": "^6.4.1",
|
|
382
395
|
"mongodb": "^7.1.0",
|
|
383
396
|
"mongodb-memory-server": "^11.0.1",
|
|
384
|
-
"mongoose": "
|
|
397
|
+
"mongoose": ">=9.4.1",
|
|
385
398
|
"tsdown": "^0.21.7",
|
|
386
399
|
"typescript": "^6.0.2",
|
|
387
400
|
"vitest": "^3.0.0",
|
package/skills/arc/SKILL.md
CHANGED
|
@@ -8,11 +8,11 @@ description: |
|
|
|
8
8
|
Triggers: arc, fastify resource, defineResource, createApp, BaseController, arc preset,
|
|
9
9
|
arc auth, arc events, arc jobs, arc websocket, arc mcp, arc plugin, arc testing, arc cli,
|
|
10
10
|
arc permissions, arc hooks, arc pipeline, arc factory, arc cache, arc QueryCache.
|
|
11
|
-
version: 2.
|
|
11
|
+
version: 2.10.3
|
|
12
12
|
license: MIT
|
|
13
13
|
metadata:
|
|
14
14
|
author: Classytic
|
|
15
|
-
version: "2.
|
|
15
|
+
version: "2.10.3"
|
|
16
16
|
tags:
|
|
17
17
|
- fastify
|
|
18
18
|
- rest-api
|
|
@@ -692,14 +692,11 @@ class ProductController extends BaseController<Product> {
|
|
|
692
692
|
import { createMongooseAdapter } from '@classytic/arc';
|
|
693
693
|
const adapter = createMongooseAdapter({ model: ProductModel, repository: productRepo });
|
|
694
694
|
|
|
695
|
-
// Custom adapter — implement
|
|
696
|
-
|
|
697
|
-
|
|
698
|
-
|
|
699
|
-
|
|
700
|
-
update(id: string, data): Promise<TDoc | null>;
|
|
701
|
-
delete(id: string): Promise<boolean>;
|
|
702
|
-
}
|
|
695
|
+
// Custom adapter — implement MinimalRepo from @classytic/repo-core/repository:
|
|
696
|
+
import type { MinimalRepo } from '@classytic/repo-core/repository';
|
|
697
|
+
// MinimalRepo<TDoc> = five-method floor (getAll, getById, create, update, delete)
|
|
698
|
+
// StandardRepo<TDoc> = MinimalRepo + optional batch ops, CAS, soft-delete, etc.
|
|
699
|
+
// Arc feature-detects optional methods at call sites.
|
|
703
700
|
```
|
|
704
701
|
|
|
705
702
|
## Events
|
|
@@ -790,7 +787,7 @@ afterUpdate(hooks, 'product', async (ctx) => { await invalidateCache(ctx.result.
|
|
|
790
787
|
## Pipeline
|
|
791
788
|
|
|
792
789
|
```typescript
|
|
793
|
-
import { guard, transform, intercept } from '@classytic/arc';
|
|
790
|
+
import { guard, transform, intercept } from '@classytic/arc/pipeline';
|
|
794
791
|
|
|
795
792
|
defineResource({
|
|
796
793
|
pipe: {
|
|
@@ -1178,7 +1175,6 @@ import {
|
|
|
1178
1175
|
} from '@classytic/arc/scope';
|
|
1179
1176
|
import { createTenantKeyGenerator } from '@classytic/arc/scope';
|
|
1180
1177
|
import { createRoleHierarchy } from '@classytic/arc/permissions';
|
|
1181
|
-
import { createServiceClient } from '@classytic/arc/rpc';
|
|
1182
1178
|
import { metricsPlugin, versioningPlugin } from '@classytic/arc/plugins';
|
|
1183
1179
|
import { webhookPlugin } from '@classytic/arc/integrations/webhooks';
|
|
1184
1180
|
import { mcpPlugin, createMcpServer, defineTool, definePrompt, fieldRulesToZod, resourceToTools } from '@classytic/arc/mcp';
|
|
@@ -366,28 +366,6 @@ const runner = new MigrationRunner(mongoose.connection.db);
|
|
|
366
366
|
await runner.up([v2]);
|
|
367
367
|
```
|
|
368
368
|
|
|
369
|
-
## Policies
|
|
370
|
-
|
|
371
|
-
Query-level authorization — modify queries based on user:
|
|
372
|
-
|
|
373
|
-
```typescript
|
|
374
|
-
import { createAccessControlPolicy } from '@classytic/arc/policies';
|
|
375
|
-
|
|
376
|
-
const editorPolicy = createAccessControlPolicy({
|
|
377
|
-
statements: [
|
|
378
|
-
{ resource: 'product', action: ['create', 'update'] },
|
|
379
|
-
{ resource: 'order', action: ['read'] },
|
|
380
|
-
],
|
|
381
|
-
});
|
|
382
|
-
|
|
383
|
-
defineResource({
|
|
384
|
-
permissions: {
|
|
385
|
-
create: editorPolicy,
|
|
386
|
-
update: editorPolicy,
|
|
387
|
-
},
|
|
388
|
-
});
|
|
389
|
-
```
|
|
390
|
-
|
|
391
369
|
## OpenAPI & External Paths
|
|
392
370
|
|
|
393
371
|
Arc auto-generates OpenAPI 3.0 specs from resource definitions. External integrations (auth adapters, custom routes) inject their paths via `ExternalOpenApiPaths`.
|
|
@@ -562,25 +540,6 @@ const relayed = await outbox.relay(); // publishes pending → transport
|
|
|
562
540
|
|
|
563
541
|
**OutboxStore interface**: `save(event)`, `getPending(limit)`, `acknowledge(eventId)` (+ optional `claimPending`, `fail`, `getDeadLettered`, `purge`). When you pass `repository`, arc adapts it internally.
|
|
564
542
|
|
|
565
|
-
## RPC Service Client — Schema Versioning
|
|
566
|
-
|
|
567
|
-
The service client supports a `schemaVersion` option for contract compatibility between services:
|
|
568
|
-
|
|
569
|
-
```typescript
|
|
570
|
-
import { createServiceClient } from '@classytic/arc/rpc';
|
|
571
|
-
|
|
572
|
-
const catalog = createServiceClient({
|
|
573
|
-
baseUrl: 'http://catalog:3000',
|
|
574
|
-
schemaVersion: '1.2.0', // sent as x-arc-schema-version header
|
|
575
|
-
correlationId: () => request.id,
|
|
576
|
-
retry: { maxRetries: 2 },
|
|
577
|
-
});
|
|
578
|
-
|
|
579
|
-
const products = await catalog.resource('product').list();
|
|
580
|
-
```
|
|
581
|
-
|
|
582
|
-
Receiving services can check `request.headers['x-arc-schema-version']` to detect version mismatches.
|
|
583
|
-
|
|
584
543
|
## Bulk Operations Preset
|
|
585
544
|
|
|
586
545
|
Adds bulk CRUD routes — repository must provide `createMany`, `updateMany`, `deleteMany`:
|
|
@@ -1,206 +0,0 @@
|
|
|
1
|
-
//#region src/utils/circuitBreaker.d.ts
|
|
2
|
-
/**
|
|
3
|
-
* Circuit Breaker Pattern
|
|
4
|
-
*
|
|
5
|
-
* Wraps external service calls with failure protection.
|
|
6
|
-
* Prevents cascading failures by "opening" the circuit when
|
|
7
|
-
* a service is failing, allowing it time to recover.
|
|
8
|
-
*
|
|
9
|
-
* States:
|
|
10
|
-
* - CLOSED: Normal operation, requests pass through
|
|
11
|
-
* - OPEN: Too many failures, all requests fail fast
|
|
12
|
-
* - HALF_OPEN: Testing if service recovered, limited requests
|
|
13
|
-
*
|
|
14
|
-
* @example
|
|
15
|
-
* import { CircuitBreaker } from '@classytic/arc/utils';
|
|
16
|
-
*
|
|
17
|
-
* const paymentBreaker = new CircuitBreaker(async (amount) => {
|
|
18
|
-
* return await stripe.charges.create({ amount });
|
|
19
|
-
* }, {
|
|
20
|
-
* failureThreshold: 5,
|
|
21
|
-
* resetTimeout: 30000,
|
|
22
|
-
* timeout: 5000,
|
|
23
|
-
* });
|
|
24
|
-
*
|
|
25
|
-
* try {
|
|
26
|
-
* const result = await paymentBreaker.call(100);
|
|
27
|
-
* } catch (error) {
|
|
28
|
-
* // Handle failure or circuit open
|
|
29
|
-
* }
|
|
30
|
-
*/
|
|
31
|
-
declare const CircuitState: {
|
|
32
|
-
readonly CLOSED: "CLOSED";
|
|
33
|
-
readonly OPEN: "OPEN";
|
|
34
|
-
readonly HALF_OPEN: "HALF_OPEN";
|
|
35
|
-
};
|
|
36
|
-
type CircuitState = (typeof CircuitState)[keyof typeof CircuitState];
|
|
37
|
-
interface CircuitBreakerOptions {
|
|
38
|
-
/**
|
|
39
|
-
* Number of failures before opening circuit
|
|
40
|
-
* @default 5
|
|
41
|
-
*/
|
|
42
|
-
failureThreshold?: number;
|
|
43
|
-
/**
|
|
44
|
-
* Time in ms before attempting to close circuit
|
|
45
|
-
* @default 60000 (60 seconds)
|
|
46
|
-
*/
|
|
47
|
-
resetTimeout?: number;
|
|
48
|
-
/**
|
|
49
|
-
* Request timeout in ms
|
|
50
|
-
* @default 10000 (10 seconds)
|
|
51
|
-
*/
|
|
52
|
-
timeout?: number;
|
|
53
|
-
/**
|
|
54
|
-
* Number of successful requests in HALF_OPEN before closing
|
|
55
|
-
* @default 1
|
|
56
|
-
*/
|
|
57
|
-
successThreshold?: number;
|
|
58
|
-
/**
|
|
59
|
-
* Fallback function when circuit is open.
|
|
60
|
-
* Receives the same arguments as the wrapped function.
|
|
61
|
-
*/
|
|
62
|
-
fallback?: (...args: unknown[]) => Promise<unknown>;
|
|
63
|
-
/**
|
|
64
|
-
* Callback when state changes
|
|
65
|
-
*/
|
|
66
|
-
onStateChange?: (from: CircuitState, to: CircuitState) => void;
|
|
67
|
-
/**
|
|
68
|
-
* Callback on error
|
|
69
|
-
*/
|
|
70
|
-
onError?: (error: Error) => void;
|
|
71
|
-
/**
|
|
72
|
-
* Name for logging/monitoring
|
|
73
|
-
*/
|
|
74
|
-
name?: string;
|
|
75
|
-
}
|
|
76
|
-
interface CircuitBreakerStats {
|
|
77
|
-
name?: string;
|
|
78
|
-
state: CircuitState;
|
|
79
|
-
failures: number;
|
|
80
|
-
successes: number;
|
|
81
|
-
totalCalls: number;
|
|
82
|
-
openedAt: number | null;
|
|
83
|
-
lastCallAt: number | null;
|
|
84
|
-
}
|
|
85
|
-
declare class CircuitBreakerError extends Error {
|
|
86
|
-
state: CircuitState;
|
|
87
|
-
constructor(message: string, state: CircuitState);
|
|
88
|
-
}
|
|
89
|
-
declare class CircuitBreaker<T extends (...args: any[]) => Promise<any>> {
|
|
90
|
-
private state;
|
|
91
|
-
private failures;
|
|
92
|
-
private successes;
|
|
93
|
-
private totalCalls;
|
|
94
|
-
private nextAttempt;
|
|
95
|
-
private lastCallAt;
|
|
96
|
-
private openedAt;
|
|
97
|
-
private readonly failureThreshold;
|
|
98
|
-
private readonly resetTimeout;
|
|
99
|
-
private readonly timeout;
|
|
100
|
-
private readonly successThreshold;
|
|
101
|
-
private readonly fallback?;
|
|
102
|
-
private readonly onStateChange?;
|
|
103
|
-
private readonly onError?;
|
|
104
|
-
private readonly name;
|
|
105
|
-
private readonly fn;
|
|
106
|
-
constructor(fn: T, options?: CircuitBreakerOptions);
|
|
107
|
-
/**
|
|
108
|
-
* Call the wrapped function with circuit breaker protection
|
|
109
|
-
*/
|
|
110
|
-
call(...args: Parameters<T>): Promise<ReturnType<T>>;
|
|
111
|
-
/**
|
|
112
|
-
* Execute function with timeout
|
|
113
|
-
*/
|
|
114
|
-
private executeWithTimeout;
|
|
115
|
-
/**
|
|
116
|
-
* Handle successful call
|
|
117
|
-
*/
|
|
118
|
-
private onSuccess;
|
|
119
|
-
/**
|
|
120
|
-
* Handle failed call
|
|
121
|
-
*/
|
|
122
|
-
private onFailure;
|
|
123
|
-
/**
|
|
124
|
-
* Change circuit state
|
|
125
|
-
*/
|
|
126
|
-
private setState;
|
|
127
|
-
/**
|
|
128
|
-
* Manually open the circuit
|
|
129
|
-
*/
|
|
130
|
-
open(): void;
|
|
131
|
-
/**
|
|
132
|
-
* Manually close the circuit
|
|
133
|
-
*/
|
|
134
|
-
close(): void;
|
|
135
|
-
/**
|
|
136
|
-
* Get current statistics
|
|
137
|
-
*/
|
|
138
|
-
getStats(): CircuitBreakerStats;
|
|
139
|
-
/**
|
|
140
|
-
* Get current state
|
|
141
|
-
*/
|
|
142
|
-
getState(): CircuitState;
|
|
143
|
-
/**
|
|
144
|
-
* Check if circuit is open
|
|
145
|
-
*/
|
|
146
|
-
isOpen(): boolean;
|
|
147
|
-
/**
|
|
148
|
-
* Check if circuit is closed
|
|
149
|
-
*/
|
|
150
|
-
isClosed(): boolean;
|
|
151
|
-
/**
|
|
152
|
-
* Reset statistics
|
|
153
|
-
*/
|
|
154
|
-
reset(): void;
|
|
155
|
-
}
|
|
156
|
-
/**
|
|
157
|
-
* Create a circuit breaker with sensible defaults
|
|
158
|
-
*
|
|
159
|
-
* @example
|
|
160
|
-
* const emailBreaker = createCircuitBreaker(
|
|
161
|
-
* async (to, subject, body) => sendEmail(to, subject, body),
|
|
162
|
-
* { name: 'email-service' }
|
|
163
|
-
* );
|
|
164
|
-
*/
|
|
165
|
-
declare function createCircuitBreaker<T extends (...args: any[]) => Promise<any>>(fn: T, options?: CircuitBreakerOptions): CircuitBreaker<T>;
|
|
166
|
-
/**
|
|
167
|
-
* Circuit breaker registry for managing multiple breakers
|
|
168
|
-
*/
|
|
169
|
-
declare class CircuitBreakerRegistry {
|
|
170
|
-
private breakers;
|
|
171
|
-
/**
|
|
172
|
-
* Register a circuit breaker
|
|
173
|
-
*/
|
|
174
|
-
register<T extends (...args: any[]) => Promise<any>>(name: string, fn: T, options?: Omit<CircuitBreakerOptions, "name">): CircuitBreaker<T>;
|
|
175
|
-
/**
|
|
176
|
-
* Get a circuit breaker by name
|
|
177
|
-
*/
|
|
178
|
-
get(name: string): CircuitBreaker<any> | undefined;
|
|
179
|
-
/**
|
|
180
|
-
* Get all breakers
|
|
181
|
-
*/
|
|
182
|
-
getAll(): Map<string, CircuitBreaker<any>>;
|
|
183
|
-
/**
|
|
184
|
-
* Get statistics for all breakers
|
|
185
|
-
*/
|
|
186
|
-
getAllStats(): Record<string, CircuitBreakerStats>;
|
|
187
|
-
/**
|
|
188
|
-
* Reset all breakers
|
|
189
|
-
*/
|
|
190
|
-
resetAll(): void;
|
|
191
|
-
/**
|
|
192
|
-
* Open all breakers
|
|
193
|
-
*/
|
|
194
|
-
openAll(): void;
|
|
195
|
-
/**
|
|
196
|
-
* Close all breakers
|
|
197
|
-
*/
|
|
198
|
-
closeAll(): void;
|
|
199
|
-
}
|
|
200
|
-
/**
|
|
201
|
-
* Create a new CircuitBreakerRegistry instance.
|
|
202
|
-
* Use this instead of a global singleton — attach to fastify.arc or pass explicitly.
|
|
203
|
-
*/
|
|
204
|
-
declare function createCircuitBreakerRegistry(): CircuitBreakerRegistry;
|
|
205
|
-
//#endregion
|
|
206
|
-
export { CircuitBreakerStats as a, createCircuitBreakerRegistry as c, CircuitBreakerRegistry as i, CircuitBreakerError as n, CircuitState as o, CircuitBreakerOptions as r, createCircuitBreaker as s, CircuitBreaker as t };
|