@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/cli/index.mjs
CHANGED
|
@@ -1,156 +1,7 @@
|
|
|
1
|
+
import describe from "./commands/describe.mjs";
|
|
2
|
+
import { exportDocs } from "./commands/docs.mjs";
|
|
3
|
+
import { doctor } from "./commands/doctor.mjs";
|
|
1
4
|
import generate from "./commands/generate.mjs";
|
|
2
5
|
import init from "./commands/init.mjs";
|
|
3
6
|
import introspect from "./commands/introspect.mjs";
|
|
4
|
-
|
|
5
|
-
import { exportDocs } from "./commands/docs.mjs";
|
|
6
|
-
import { existsSync, readFileSync } from "node:fs";
|
|
7
|
-
import { join, resolve } from "node:path";
|
|
8
|
-
|
|
9
|
-
//#region src/cli/commands/doctor.ts
|
|
10
|
-
/**
|
|
11
|
-
* Arc CLI - Doctor Command
|
|
12
|
-
*
|
|
13
|
-
* Health check utility that validates the development environment.
|
|
14
|
-
* Checks Node.js version, dependencies, configuration, and env variables.
|
|
15
|
-
*/
|
|
16
|
-
async function doctor(_args = []) {
|
|
17
|
-
console.log("\nArc Doctor\n");
|
|
18
|
-
const results = [];
|
|
19
|
-
const cwd = process.cwd();
|
|
20
|
-
const nodeVersion = process.versions.node;
|
|
21
|
-
if (parseInt(nodeVersion.split(".")[0] ?? "0", 10) >= 22) results.push({
|
|
22
|
-
status: "pass",
|
|
23
|
-
label: `Node.js ${nodeVersion}`,
|
|
24
|
-
detail: "required: >=22"
|
|
25
|
-
});
|
|
26
|
-
else results.push({
|
|
27
|
-
status: "fail",
|
|
28
|
-
label: `Node.js ${nodeVersion}`,
|
|
29
|
-
detail: "required: >=22"
|
|
30
|
-
});
|
|
31
|
-
const pkg = findPackageJson(cwd);
|
|
32
|
-
const allDeps = {
|
|
33
|
-
...pkg?.dependencies ?? {},
|
|
34
|
-
...pkg?.devDependencies ?? {}
|
|
35
|
-
};
|
|
36
|
-
const arcVersion = allDeps["@classytic/arc"];
|
|
37
|
-
if (arcVersion) results.push({
|
|
38
|
-
status: "pass",
|
|
39
|
-
label: `@classytic/arc ${arcVersion}`
|
|
40
|
-
});
|
|
41
|
-
else results.push({
|
|
42
|
-
status: "warn",
|
|
43
|
-
label: "@classytic/arc not found in dependencies"
|
|
44
|
-
});
|
|
45
|
-
const fastifyVersion = allDeps["fastify"];
|
|
46
|
-
if (fastifyVersion) {
|
|
47
|
-
const clean = fastifyVersion.replace(/^[\^~>=<]+/, "");
|
|
48
|
-
if (parseInt(clean.split(".")[0] ?? "0", 10) >= 5) results.push({
|
|
49
|
-
status: "pass",
|
|
50
|
-
label: `fastify ${fastifyVersion}`,
|
|
51
|
-
detail: "required: ^5.0.0"
|
|
52
|
-
});
|
|
53
|
-
else results.push({
|
|
54
|
-
status: "fail",
|
|
55
|
-
label: `fastify ${fastifyVersion}`,
|
|
56
|
-
detail: "required: ^5.0.0 — Arc requires Fastify 5"
|
|
57
|
-
});
|
|
58
|
-
} else results.push({
|
|
59
|
-
status: "fail",
|
|
60
|
-
label: "fastify not found in dependencies",
|
|
61
|
-
detail: "required: ^5.0.0"
|
|
62
|
-
});
|
|
63
|
-
const tsconfigPath = resolve(cwd, "tsconfig.json");
|
|
64
|
-
if (existsSync(tsconfigPath)) try {
|
|
65
|
-
const stripped = readFileSync(tsconfigPath, "utf-8").replace(/\/\/.*$/gm, "");
|
|
66
|
-
const moduleRes = JSON.parse(stripped)?.compilerOptions?.moduleResolution;
|
|
67
|
-
if (moduleRes && ![
|
|
68
|
-
"nodenext",
|
|
69
|
-
"node16",
|
|
70
|
-
"bundler"
|
|
71
|
-
].includes(moduleRes.toLowerCase())) results.push({
|
|
72
|
-
status: "warn",
|
|
73
|
-
label: "tsconfig.json found",
|
|
74
|
-
detail: `moduleResolution "${moduleRes}" — recommend "NodeNext" or "Bundler"`
|
|
75
|
-
});
|
|
76
|
-
else results.push({
|
|
77
|
-
status: "pass",
|
|
78
|
-
label: "tsconfig.json found"
|
|
79
|
-
});
|
|
80
|
-
} catch {
|
|
81
|
-
results.push({
|
|
82
|
-
status: "pass",
|
|
83
|
-
label: "tsconfig.json found"
|
|
84
|
-
});
|
|
85
|
-
}
|
|
86
|
-
else results.push({
|
|
87
|
-
status: "warn",
|
|
88
|
-
label: "tsconfig.json not found"
|
|
89
|
-
});
|
|
90
|
-
for (const dep of [
|
|
91
|
-
{
|
|
92
|
-
name: "@fastify/rate-limit",
|
|
93
|
-
purpose: "rate limiting"
|
|
94
|
-
},
|
|
95
|
-
{
|
|
96
|
-
name: "@fastify/helmet",
|
|
97
|
-
purpose: "security headers"
|
|
98
|
-
},
|
|
99
|
-
{
|
|
100
|
-
name: "@fastify/cors",
|
|
101
|
-
purpose: "CORS support"
|
|
102
|
-
}
|
|
103
|
-
]) if (allDeps[dep.name]) results.push({
|
|
104
|
-
status: "pass",
|
|
105
|
-
label: `${dep.name} installed`
|
|
106
|
-
});
|
|
107
|
-
else results.push({
|
|
108
|
-
status: "warn",
|
|
109
|
-
label: `${dep.name} not installed`,
|
|
110
|
-
detail: `${dep.purpose} disabled`
|
|
111
|
-
});
|
|
112
|
-
if (allDeps["better-auth"]) results.push({
|
|
113
|
-
status: "pass",
|
|
114
|
-
label: `better-auth ${allDeps["better-auth"]}`
|
|
115
|
-
});
|
|
116
|
-
for (const env of [{
|
|
117
|
-
name: "MONGO_URI",
|
|
118
|
-
severity: "warn",
|
|
119
|
-
detail: "required at runtime for MongoDB"
|
|
120
|
-
}, {
|
|
121
|
-
name: "BETTER_AUTH_SECRET",
|
|
122
|
-
severity: "warn",
|
|
123
|
-
detail: "required for Better Auth session encryption"
|
|
124
|
-
}]) if (process.env[env.name]) results.push({
|
|
125
|
-
status: "pass",
|
|
126
|
-
label: `${env.name} set`
|
|
127
|
-
});
|
|
128
|
-
else results.push({
|
|
129
|
-
status: env.severity,
|
|
130
|
-
label: `${env.name} not set`,
|
|
131
|
-
detail: env.detail
|
|
132
|
-
});
|
|
133
|
-
let passCount = 0;
|
|
134
|
-
let warnCount = 0;
|
|
135
|
-
let failCount = 0;
|
|
136
|
-
for (const r of results) {
|
|
137
|
-
const icon = r.status === "pass" ? "[pass]" : r.status === "warn" ? "[warn]" : "[FAIL]";
|
|
138
|
-
const detail = r.detail ? ` (${r.detail})` : "";
|
|
139
|
-
console.log(` ${icon} ${r.label}${detail}`);
|
|
140
|
-
if (r.status === "pass") passCount++;
|
|
141
|
-
else if (r.status === "warn") warnCount++;
|
|
142
|
-
else failCount++;
|
|
143
|
-
}
|
|
144
|
-
console.log(`\n${passCount} passed, ${warnCount} warnings, ${failCount} failures\n`);
|
|
145
|
-
if (failCount > 0) process.exitCode = 1;
|
|
146
|
-
}
|
|
147
|
-
function findPackageJson(dir) {
|
|
148
|
-
const paths = [join(dir, "package.json"), join(dir, "..", "package.json")];
|
|
149
|
-
for (const p of paths) try {
|
|
150
|
-
if (existsSync(p)) return JSON.parse(readFileSync(p, "utf-8"));
|
|
151
|
-
} catch {}
|
|
152
|
-
return null;
|
|
153
|
-
}
|
|
154
|
-
|
|
155
|
-
//#endregion
|
|
156
|
-
export { describe, doctor, exportDocs, generate, init, introspect };
|
|
7
|
+
export { describe, doctor, exportDocs, generate, init, introspect };
|
|
@@ -79,6 +79,5 @@ const RESERVED_QUERY_PARAMS = Object.freeze(new Set([
|
|
|
79
79
|
"lean",
|
|
80
80
|
"_policyFilters"
|
|
81
81
|
]));
|
|
82
|
-
|
|
83
82
|
//#endregion
|
|
84
|
-
export { DEFAULT_SORT as a, HOOK_OPERATIONS as c, MAX_REGEX_LENGTH as d, MAX_SEARCH_LENGTH as f, SYSTEM_FIELDS as h, DEFAULT_MAX_LIMIT as i, HOOK_PHASES as l, RESERVED_QUERY_PARAMS as m, DEFAULT_ID_FIELD as n, DEFAULT_TENANT_FIELD as o, MUTATION_OPERATIONS as p, DEFAULT_LIMIT as r, DEFAULT_UPDATE_METHOD as s, CRUD_OPERATIONS as t, MAX_FILTER_DEPTH as u };
|
|
83
|
+
export { DEFAULT_SORT as a, HOOK_OPERATIONS as c, MAX_REGEX_LENGTH as d, MAX_SEARCH_LENGTH as f, SYSTEM_FIELDS as h, DEFAULT_MAX_LIMIT as i, HOOK_PHASES as l, RESERVED_QUERY_PARAMS as m, DEFAULT_ID_FIELD as n, DEFAULT_TENANT_FIELD as o, MUTATION_OPERATIONS as p, DEFAULT_LIMIT as r, DEFAULT_UPDATE_METHOD as s, CRUD_OPERATIONS as t, MAX_FILTER_DEPTH as u };
|
package/dist/core/index.d.mts
CHANGED
|
@@ -1,5 +1,3 @@
|
|
|
1
|
-
import "../
|
|
2
|
-
import { E as
|
|
3
|
-
|
|
4
|
-
import { A as createCrudRouter, C as MutationOperation, D as ActionRouterConfig, E as ActionHandler, O as IdempotencyService, S as MUTATION_OPERATIONS, T as SYSTEM_FIELDS, _ as HookOperation, a as getControllerScope, b as MAX_REGEX_LENGTH, c as CrudOperation, d as DEFAULT_MAX_LIMIT, f as DEFAULT_SORT, g as HOOK_PHASES, h as HOOK_OPERATIONS, i as getControllerContext, j as createPermissionMiddleware, k as createActionRouter, l as DEFAULT_ID_FIELD, m as DEFAULT_UPDATE_METHOD, n as createFastifyHandler, o as sendControllerResponse, p as DEFAULT_TENANT_FIELD, r as createRequestContext, s as CRUD_OPERATIONS, t as createCrudHandlers, u as DEFAULT_LIMIT, v as HookPhase, w as RESERVED_QUERY_PARAMS, x as MAX_SEARCH_LENGTH, y as MAX_FILTER_DEPTH } from "../fastifyAdapter-6b_eRDBw.mjs";
|
|
5
|
-
export { AccessControl, type AccessControlConfig, type ActionHandler, type ActionRouterConfig, BaseController, type BaseControllerOptions, BodySanitizer, type BodySanitizerConfig, CRUD_OPERATIONS, CrudOperation, DEFAULT_ID_FIELD, DEFAULT_LIMIT, DEFAULT_MAX_LIMIT, DEFAULT_SORT, DEFAULT_TENANT_FIELD, DEFAULT_UPDATE_METHOD, HOOK_OPERATIONS, HOOK_PHASES, HookOperation, HookPhase, type IdempotencyService, MAX_FILTER_DEPTH, MAX_REGEX_LENGTH, MAX_SEARCH_LENGTH, MUTATION_OPERATIONS, MutationOperation, QueryResolver, type QueryResolverConfig, RESERVED_QUERY_PARAMS, ResourceDefinition, SYSTEM_FIELDS, createActionRouter, createCrudHandlers, createCrudRouter, createFastifyHandler, createPermissionMiddleware, createRequestContext, defineResource, getControllerContext, getControllerScope, sendControllerResponse };
|
|
1
|
+
import { Ct as QueryResolverConfig, Dt as AccessControlConfig, Et as AccessControl, Lt as ResourceDefinition, Rt as defineResource, St as QueryResolver, Tt as BodySanitizerConfig, bt as BaseController, wt as BodySanitizer, xt as BaseControllerOptions } from "../interface-DGmPxakH.mjs";
|
|
2
|
+
import { A as RESERVED_QUERY_PARAMS, C as HookOperation, D as MAX_SEARCH_LENGTH, E as MAX_REGEX_LENGTH, O as MUTATION_OPERATIONS, S as HOOK_PHASES, T as MAX_FILTER_DEPTH, _ as DEFAULT_MAX_LIMIT, a as getControllerScope, b as DEFAULT_UPDATE_METHOD, c as createPermissionMiddleware, d as IdempotencyService, f as createActionRouter, g as DEFAULT_LIMIT, h as DEFAULT_ID_FIELD, i as getControllerContext, j as SYSTEM_FIELDS, k as MutationOperation, l as ActionHandler, m as CrudOperation, n as createFastifyHandler, o as sendControllerResponse, p as CRUD_OPERATIONS, r as createRequestContext, s as createCrudRouter, t as createCrudHandlers, u as ActionRouterConfig, v as DEFAULT_SORT, w as HookPhase, x as HOOK_OPERATIONS, y as DEFAULT_TENANT_FIELD } from "../index-BL8CaQih.mjs";
|
|
3
|
+
export { AccessControl, AccessControlConfig, ActionHandler, ActionRouterConfig, BaseController, BaseControllerOptions, BodySanitizer, BodySanitizerConfig, CRUD_OPERATIONS, CrudOperation, DEFAULT_ID_FIELD, DEFAULT_LIMIT, DEFAULT_MAX_LIMIT, DEFAULT_SORT, DEFAULT_TENANT_FIELD, DEFAULT_UPDATE_METHOD, HOOK_OPERATIONS, HOOK_PHASES, HookOperation, HookPhase, IdempotencyService, MAX_FILTER_DEPTH, MAX_REGEX_LENGTH, MAX_SEARCH_LENGTH, MUTATION_OPERATIONS, MutationOperation, QueryResolver, QueryResolverConfig, RESERVED_QUERY_PARAMS, ResourceDefinition, SYSTEM_FIELDS, createActionRouter, createCrudHandlers, createCrudRouter, createFastifyHandler, createPermissionMiddleware, createRequestContext, defineResource, getControllerContext, getControllerScope, sendControllerResponse };
|
package/dist/core/index.mjs
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
|
-
import { a as DEFAULT_SORT, c as HOOK_OPERATIONS, d as MAX_REGEX_LENGTH, f as MAX_SEARCH_LENGTH, h as SYSTEM_FIELDS, i as DEFAULT_MAX_LIMIT, l as HOOK_PHASES, m as RESERVED_QUERY_PARAMS, n as DEFAULT_ID_FIELD, o as DEFAULT_TENANT_FIELD, p as MUTATION_OPERATIONS, r as DEFAULT_LIMIT, s as DEFAULT_UPDATE_METHOD, t as CRUD_OPERATIONS, u as MAX_FILTER_DEPTH } from "../constants-
|
|
2
|
-
import {
|
|
3
|
-
|
|
4
|
-
|
|
1
|
+
import { a as DEFAULT_SORT, c as HOOK_OPERATIONS, d as MAX_REGEX_LENGTH, f as MAX_SEARCH_LENGTH, h as SYSTEM_FIELDS, i as DEFAULT_MAX_LIMIT, l as HOOK_PHASES, m as RESERVED_QUERY_PARAMS, n as DEFAULT_ID_FIELD, o as DEFAULT_TENANT_FIELD, p as MUTATION_OPERATIONS, r as DEFAULT_LIMIT, s as DEFAULT_UPDATE_METHOD, t as CRUD_OPERATIONS, u as MAX_FILTER_DEPTH } from "../constants-Cxde4rpC.mjs";
|
|
2
|
+
import { i as AccessControl, n as QueryResolver, r as BodySanitizer, t as BaseController } from "../BaseController-CkM5dUh_.mjs";
|
|
3
|
+
import { t as createActionRouter } from "../core-C1XCMtqM.mjs";
|
|
4
|
+
import { c as createCrudHandlers, d as getControllerContext, f as getControllerScope, l as createFastifyHandler, n as defineResource, o as createCrudRouter, p as sendControllerResponse, s as createPermissionMiddleware, t as ResourceDefinition, u as createRequestContext } from "../defineResource-D9aY5Cy6.mjs";
|
|
5
|
+
export { AccessControl, BaseController, BodySanitizer, CRUD_OPERATIONS, DEFAULT_ID_FIELD, DEFAULT_LIMIT, DEFAULT_MAX_LIMIT, DEFAULT_SORT, DEFAULT_TENANT_FIELD, DEFAULT_UPDATE_METHOD, HOOK_OPERATIONS, HOOK_PHASES, MAX_FILTER_DEPTH, MAX_REGEX_LENGTH, MAX_SEARCH_LENGTH, MUTATION_OPERATIONS, QueryResolver, RESERVED_QUERY_PARAMS, ResourceDefinition, SYSTEM_FIELDS, createActionRouter, createCrudHandlers, createCrudRouter, createFastifyHandler, createPermissionMiddleware, createRequestContext, defineResource, getControllerContext, getControllerScope, sendControllerResponse };
|
|
@@ -0,0 +1,185 @@
|
|
|
1
|
+
//#region src/core/createActionRouter.ts
|
|
2
|
+
/**
|
|
3
|
+
* Create action-based state transition endpoint
|
|
4
|
+
*
|
|
5
|
+
* Registers: POST /:id/action
|
|
6
|
+
* Body: { action: string, ...actionData }
|
|
7
|
+
*
|
|
8
|
+
* @param fastify - Fastify instance
|
|
9
|
+
* @param config - Action router configuration
|
|
10
|
+
*/
|
|
11
|
+
function createActionRouter(fastify, config) {
|
|
12
|
+
const { tag, actions, actionPermissions = {}, actionSchemas = {}, globalAuth, idempotencyService, onError } = config;
|
|
13
|
+
const actionEnum = Object.keys(actions);
|
|
14
|
+
if (actionEnum.length === 0) {
|
|
15
|
+
fastify.log.warn("[createActionRouter] No actions defined, skipping route creation");
|
|
16
|
+
return;
|
|
17
|
+
}
|
|
18
|
+
const bodyProperties = { action: {
|
|
19
|
+
type: "string",
|
|
20
|
+
enum: actionEnum,
|
|
21
|
+
description: `Action to perform: ${actionEnum.join(" | ")}`
|
|
22
|
+
} };
|
|
23
|
+
Object.entries(actionSchemas).forEach(([actionName, schema]) => {
|
|
24
|
+
if (schema && typeof schema === "object") Object.entries(schema).forEach(([propName, propSchema]) => {
|
|
25
|
+
bodyProperties[propName] = {
|
|
26
|
+
...propSchema,
|
|
27
|
+
description: `${propSchema.description || ""} (for ${actionName} action)`.trim()
|
|
28
|
+
};
|
|
29
|
+
});
|
|
30
|
+
});
|
|
31
|
+
const routeSchema = {
|
|
32
|
+
tags: tag ? [tag] : void 0,
|
|
33
|
+
summary: `Perform action (${actionEnum.join("/")})`,
|
|
34
|
+
description: buildActionDescription(actions, actionPermissions),
|
|
35
|
+
params: {
|
|
36
|
+
type: "object",
|
|
37
|
+
properties: { id: {
|
|
38
|
+
type: "string",
|
|
39
|
+
description: "Resource ID"
|
|
40
|
+
} },
|
|
41
|
+
required: ["id"]
|
|
42
|
+
},
|
|
43
|
+
body: {
|
|
44
|
+
type: "object",
|
|
45
|
+
properties: bodyProperties,
|
|
46
|
+
required: ["action"]
|
|
47
|
+
}
|
|
48
|
+
};
|
|
49
|
+
const preHandler = [];
|
|
50
|
+
const hasPublicActions = Object.entries(actionPermissions).some(([, p]) => p?._isPublic) || globalAuth && globalAuth?._isPublic;
|
|
51
|
+
const hasProtectedActions = Object.entries(actionPermissions).some(([, p]) => !p?._isPublic) || globalAuth && !globalAuth?._isPublic;
|
|
52
|
+
if (hasProtectedActions && !hasPublicActions && fastify.authenticate) preHandler.push(fastify.authenticate);
|
|
53
|
+
fastify.post("/:id/action", {
|
|
54
|
+
schema: routeSchema,
|
|
55
|
+
preHandler: preHandler.length ? preHandler : void 0
|
|
56
|
+
}, async (req, reply) => {
|
|
57
|
+
const { action, ...data } = req.body;
|
|
58
|
+
const { id } = req.params;
|
|
59
|
+
const rawIdempotencyKey = req.headers["idempotency-key"];
|
|
60
|
+
const idempotencyKey = Array.isArray(rawIdempotencyKey) ? rawIdempotencyKey[0] : rawIdempotencyKey;
|
|
61
|
+
const handler = actions[action];
|
|
62
|
+
if (!handler) return reply.code(400).send({
|
|
63
|
+
success: false,
|
|
64
|
+
error: `Invalid action '${action}'. Valid actions: ${actionEnum.join(", ")}`,
|
|
65
|
+
validActions: actionEnum
|
|
66
|
+
});
|
|
67
|
+
const permissionCheck = actionPermissions[action] ?? globalAuth;
|
|
68
|
+
if (hasPublicActions && hasProtectedActions && permissionCheck) {
|
|
69
|
+
if (!permissionCheck?._isPublic && fastify.authenticate) {
|
|
70
|
+
try {
|
|
71
|
+
await fastify.authenticate(req, reply);
|
|
72
|
+
} catch {
|
|
73
|
+
if (!reply.sent) return reply.code(401).send({
|
|
74
|
+
success: false,
|
|
75
|
+
error: "Authentication required"
|
|
76
|
+
});
|
|
77
|
+
return;
|
|
78
|
+
}
|
|
79
|
+
if (reply.sent) return;
|
|
80
|
+
}
|
|
81
|
+
}
|
|
82
|
+
if (permissionCheck) {
|
|
83
|
+
const context = {
|
|
84
|
+
user: req.user ?? null,
|
|
85
|
+
request: req,
|
|
86
|
+
resource: tag ?? "action",
|
|
87
|
+
action,
|
|
88
|
+
resourceId: id,
|
|
89
|
+
params: req.params,
|
|
90
|
+
data
|
|
91
|
+
};
|
|
92
|
+
let result;
|
|
93
|
+
try {
|
|
94
|
+
result = await permissionCheck(context);
|
|
95
|
+
} catch (err) {
|
|
96
|
+
req.log?.warn?.({
|
|
97
|
+
err,
|
|
98
|
+
resource: tag ?? "action",
|
|
99
|
+
action
|
|
100
|
+
}, "Permission check threw");
|
|
101
|
+
return reply.code(403).send({
|
|
102
|
+
success: false,
|
|
103
|
+
error: "Permission denied"
|
|
104
|
+
});
|
|
105
|
+
}
|
|
106
|
+
if (typeof result === "boolean") {
|
|
107
|
+
if (!result) return reply.code(context.user ? 403 : 401).send({
|
|
108
|
+
success: false,
|
|
109
|
+
error: context.user ? `Permission denied for '${action}'` : "Authentication required"
|
|
110
|
+
});
|
|
111
|
+
} else {
|
|
112
|
+
const permResult = result;
|
|
113
|
+
if (!permResult.granted) return reply.code(context.user ? 403 : 401).send({
|
|
114
|
+
success: false,
|
|
115
|
+
error: permResult.reason ?? (context.user ? `Permission denied for '${action}'` : "Authentication required")
|
|
116
|
+
});
|
|
117
|
+
}
|
|
118
|
+
}
|
|
119
|
+
try {
|
|
120
|
+
if (idempotencyKey && idempotencyService) {
|
|
121
|
+
const user = req.user;
|
|
122
|
+
const payloadForHash = {
|
|
123
|
+
action,
|
|
124
|
+
id,
|
|
125
|
+
data,
|
|
126
|
+
userId: (user?._id)?.toString?.() || user?.id || null
|
|
127
|
+
};
|
|
128
|
+
const idempotencyResult = await idempotencyService.check(idempotencyKey, payloadForHash);
|
|
129
|
+
if (!idempotencyResult.isNew && "existingResult" in idempotencyResult) return reply.send({
|
|
130
|
+
success: true,
|
|
131
|
+
data: idempotencyResult.existingResult,
|
|
132
|
+
cached: true
|
|
133
|
+
});
|
|
134
|
+
}
|
|
135
|
+
const result = await handler(id, data, req);
|
|
136
|
+
if (idempotencyService) await idempotencyService.complete(idempotencyKey, result);
|
|
137
|
+
return reply.send({
|
|
138
|
+
success: true,
|
|
139
|
+
data: result
|
|
140
|
+
});
|
|
141
|
+
} catch (error) {
|
|
142
|
+
if (idempotencyService) await idempotencyService.fail(idempotencyKey, error);
|
|
143
|
+
if (onError) {
|
|
144
|
+
const { statusCode, error: errorMsg, code } = onError(error, action, id);
|
|
145
|
+
return reply.code(statusCode).send({
|
|
146
|
+
success: false,
|
|
147
|
+
error: errorMsg,
|
|
148
|
+
code
|
|
149
|
+
});
|
|
150
|
+
}
|
|
151
|
+
const err = error;
|
|
152
|
+
const statusCode = err.statusCode || err.status || 500;
|
|
153
|
+
const errorCode = err.code || "ACTION_FAILED";
|
|
154
|
+
if (statusCode >= 500) req.log.error({
|
|
155
|
+
err: error,
|
|
156
|
+
action,
|
|
157
|
+
id
|
|
158
|
+
}, "Action handler error");
|
|
159
|
+
return reply.code(statusCode).send({
|
|
160
|
+
success: false,
|
|
161
|
+
error: err.message || `Failed to execute '${action}' action`,
|
|
162
|
+
code: errorCode
|
|
163
|
+
});
|
|
164
|
+
}
|
|
165
|
+
});
|
|
166
|
+
fastify.log.debug({
|
|
167
|
+
actions: actionEnum,
|
|
168
|
+
tag
|
|
169
|
+
}, "[createActionRouter] Registered action endpoint: POST /:id/action");
|
|
170
|
+
}
|
|
171
|
+
/**
|
|
172
|
+
* Build description with action details
|
|
173
|
+
* Uses _roles metadata from PermissionCheck functions for OpenAPI docs
|
|
174
|
+
*/
|
|
175
|
+
function buildActionDescription(actions, actionPermissions) {
|
|
176
|
+
const lines = ["Unified action endpoint for state transitions.\n\n**Available actions:**"];
|
|
177
|
+
Object.keys(actions).forEach((action) => {
|
|
178
|
+
const roles = actionPermissions[action]?._roles;
|
|
179
|
+
const roleStr = roles?.length ? ` (requires: ${roles.join(" or ")})` : "";
|
|
180
|
+
lines.push(`- \`${action}\`${roleStr}`);
|
|
181
|
+
});
|
|
182
|
+
return lines.join("\n");
|
|
183
|
+
}
|
|
184
|
+
//#endregion
|
|
185
|
+
export { createActionRouter as t };
|
|
@@ -1,8 +1,7 @@
|
|
|
1
|
-
import { t as __exportAll } from "./chunk-
|
|
2
|
-
import { n as PUBLIC_SCOPE } from "./types-
|
|
1
|
+
import { t as __exportAll } from "./chunk-BpYLSNr0.mjs";
|
|
2
|
+
import { n as PUBLIC_SCOPE } from "./types-C6TQjtdi.mjs";
|
|
3
3
|
import Fastify from "fastify";
|
|
4
4
|
import qs from "qs";
|
|
5
|
-
|
|
6
5
|
//#region src/factory/presets.ts
|
|
7
6
|
/**
|
|
8
7
|
* Production preset - strict security, performance optimized
|
|
@@ -61,7 +60,7 @@ const productionPreset = {
|
|
|
61
60
|
},
|
|
62
61
|
underPressure: {
|
|
63
62
|
exposeStatusRoute: true,
|
|
64
|
-
maxEventLoopDelay:
|
|
63
|
+
maxEventLoopDelay: 3e3,
|
|
65
64
|
maxHeapUsedBytes: 1024 * 1024 * 1024,
|
|
66
65
|
maxRssBytes: 1024 * 1024 * 1024
|
|
67
66
|
}
|
|
@@ -128,18 +127,29 @@ const testingPreset = {
|
|
|
128
127
|
} }
|
|
129
128
|
};
|
|
130
129
|
/**
|
|
131
|
-
* Edge/Serverless preset
|
|
130
|
+
* Edge/Serverless preset — minimal cold-start overhead
|
|
132
131
|
*
|
|
133
|
-
*
|
|
134
|
-
*
|
|
135
|
-
* -
|
|
136
|
-
* -
|
|
137
|
-
* -
|
|
138
|
-
* -
|
|
139
|
-
* - Raw body parsing (register per-route if needed)
|
|
132
|
+
* Strips non-essential plugins to reduce startup time. Designed for:
|
|
133
|
+
* - Cloudflare Workers (requires `nodejs_compat` flag + `toFetchHandler()`)
|
|
134
|
+
* - AWS Lambda via `@fastify/aws-lambda` or `toFetchHandler()`
|
|
135
|
+
* - Vercel Serverless Functions (Node.js runtime)
|
|
136
|
+
* - Google Cloud Functions (Node.js runtime)
|
|
137
|
+
* - Any environment where the platform handles security, rate limiting, and health checks
|
|
140
138
|
*
|
|
141
|
-
*
|
|
142
|
-
*
|
|
139
|
+
* Use with `toFetchHandler()` from `@classytic/arc/factory` for edge runtimes:
|
|
140
|
+
* ```typescript
|
|
141
|
+
* import { createApp, toFetchHandler } from '@classytic/arc/factory';
|
|
142
|
+
* const app = await createApp({ preset: 'edge' });
|
|
143
|
+
* export default { fetch: toFetchHandler(app) };
|
|
144
|
+
* ```
|
|
145
|
+
*
|
|
146
|
+
* **Edge runtime requirements:**
|
|
147
|
+
* - Cloudflare Workers: enable `nodejs_compat` in wrangler.toml
|
|
148
|
+
* - Vercel: use Node.js runtime (not Edge Runtime) or enable nodejs compat
|
|
149
|
+
*
|
|
150
|
+
* Arc uses `node:crypto` and `AsyncLocalStorage` — both supported by
|
|
151
|
+
* modern edge runtimes with Node.js compat flags. No TCP server is needed
|
|
152
|
+
* when using `toFetchHandler()` (routes through Fastify's `.inject()`).
|
|
143
153
|
*/
|
|
144
154
|
const edgePreset = {
|
|
145
155
|
logger: { level: "warn" },
|
|
@@ -170,7 +180,6 @@ function getPreset(name) {
|
|
|
170
180
|
default: throw new Error(`Unknown preset: ${name}`);
|
|
171
181
|
}
|
|
172
182
|
}
|
|
173
|
-
|
|
174
183
|
//#endregion
|
|
175
184
|
//#region src/factory/createApp.ts
|
|
176
185
|
/**
|
|
@@ -280,7 +289,7 @@ async function loadPlugin(name, logger) {
|
|
|
280
289
|
*/
|
|
281
290
|
async function createApp(options) {
|
|
282
291
|
if (options.debug !== void 0 && options.debug !== false) {
|
|
283
|
-
const { configureArcLogger } = await import("./logger-
|
|
292
|
+
const { configureArcLogger } = await import("./logger-Dz3j1ItV.mjs").then((n) => n.r);
|
|
284
293
|
configureArcLogger({ debug: options.debug });
|
|
285
294
|
}
|
|
286
295
|
const authConfig = options.auth;
|
|
@@ -288,15 +297,19 @@ async function createApp(options) {
|
|
|
288
297
|
if (!isAuthDisabled && authConfig && authConfig.type === "jwt") {
|
|
289
298
|
if (!authConfig.jwt?.secret && !authConfig.authenticate) throw new Error("createApp: JWT secret required when Arc auth is enabled.\nProvide auth.jwt.secret, auth.authenticate, or set auth: false to disable.\nExample: auth: { type: 'jwt', jwt: { secret: process.env.JWT_SECRET } }");
|
|
290
299
|
}
|
|
300
|
+
const deferredWarnings = [];
|
|
291
301
|
if (options.runtime === "distributed") {
|
|
292
302
|
const MEMORY_NAMES = new Set(["memory", "memory-cache"]);
|
|
293
303
|
const missing = [];
|
|
294
304
|
const eventsTransport = options.stores?.events;
|
|
295
305
|
if (!eventsTransport || MEMORY_NAMES.has(eventsTransport.name)) missing.push("events transport");
|
|
296
|
-
|
|
297
|
-
|
|
306
|
+
if (options.arcPlugins?.caching) {
|
|
307
|
+
const cacheStore = options.stores?.cache;
|
|
308
|
+
if (!cacheStore || MEMORY_NAMES.has(cacheStore.name)) missing.push("cache store");
|
|
309
|
+
}
|
|
298
310
|
const idempotencyStore = options.stores?.idempotency;
|
|
299
|
-
if (
|
|
311
|
+
if (idempotencyStore && MEMORY_NAMES.has(idempotencyStore.name)) missing.push("idempotency store (memory-backed in distributed mode)");
|
|
312
|
+
else if (!idempotencyStore) deferredWarnings.push("runtime: 'distributed' — no idempotency store configured. Write-path deduplication will be instance-local. If resources use the idempotency plugin, provide stores.idempotency with a Redis/MongoDB store.");
|
|
300
313
|
if (options.arcPlugins?.queryCache) {
|
|
301
314
|
const qcStore = options.stores?.queryCache;
|
|
302
315
|
if (!qcStore || MEMORY_NAMES.has(qcStore.name)) missing.push("queryCache store");
|
|
@@ -307,7 +320,7 @@ async function createApp(options) {
|
|
|
307
320
|
...options.preset ? getPreset(options.preset) : {},
|
|
308
321
|
...options
|
|
309
322
|
};
|
|
310
|
-
|
|
323
|
+
const fastify = Fastify({
|
|
311
324
|
logger: config.logger ?? true,
|
|
312
325
|
trustProxy: config.trustProxy ?? false,
|
|
313
326
|
routerOptions: { querystringParser: (str) => qs.parse(str) },
|
|
@@ -318,6 +331,7 @@ async function createApp(options) {
|
|
|
318
331
|
keywords: ["example", ...config.ajv?.keywords ?? []]
|
|
319
332
|
} }
|
|
320
333
|
});
|
|
334
|
+
for (const warning of deferredWarnings) fastify.log.warn(warning);
|
|
321
335
|
if (config.typeProvider === "typebox") try {
|
|
322
336
|
const { TypeBoxValidatorCompiler } = await import("@fastify/type-provider-typebox");
|
|
323
337
|
fastify.setValidatorCompiler(TypeBoxValidatorCompiler);
|
|
@@ -342,7 +356,7 @@ async function createApp(options) {
|
|
|
342
356
|
if (config.cors !== false) {
|
|
343
357
|
const cors = await loadPlugin("cors");
|
|
344
358
|
const corsOptions = { ...config.cors ?? {} };
|
|
345
|
-
if (config.preset === "production" &&
|
|
359
|
+
if (config.preset === "production" && corsOptions && !("origin" in corsOptions)) fastify.log.warn("CORS origin is not explicitly configured in production. Set cors.origin to allowed domains, cors: { origin: '*' }, or cors: false to disable.");
|
|
346
360
|
if (corsOptions.credentials && corsOptions.origin === "*") corsOptions.origin = true;
|
|
347
361
|
await fastify.register(cors, corsOptions);
|
|
348
362
|
fastify.log.debug("CORS enabled");
|
|
@@ -354,8 +368,11 @@ async function createApp(options) {
|
|
|
354
368
|
timeWindow: "1 minute"
|
|
355
369
|
};
|
|
356
370
|
await fastify.register(rateLimit, rateLimitOpts);
|
|
357
|
-
if (
|
|
358
|
-
if (
|
|
371
|
+
if (!(typeof rateLimitOpts === "object" && "store" in rateLimitOpts)) {
|
|
372
|
+
if (config.runtime === "distributed") {
|
|
373
|
+
fastify.log.error("Rate limiting is using in-memory store in distributed mode. Each instance tracks limits independently — this breaks rate limiting. Configure a Redis store: rateLimit: { store: new RedisStore({ ... }) }");
|
|
374
|
+
throw new Error("[Arc] runtime: 'distributed' with rate limiting requires a shared store.\nProvide rateLimit: { store: new RedisStore({ ... }) } or disable rate limiting: rateLimit: false");
|
|
375
|
+
} else if (config.preset === "production") fastify.log.warn("Rate limiting is using in-memory store. In multi-instance deployments, each instance tracks limits independently. Configure a Redis store for distributed rate limiting: rateLimit: { store: new RedisStore({ ... }) }");
|
|
359
376
|
}
|
|
360
377
|
fastify.log.debug("Rate limiting enabled");
|
|
361
378
|
} else fastify.log.warn("Rate limiting disabled");
|
|
@@ -409,7 +426,7 @@ async function createApp(options) {
|
|
|
409
426
|
};
|
|
410
427
|
trackPlugin("arc-core");
|
|
411
428
|
if (config.arcPlugins?.events !== false) {
|
|
412
|
-
const { default: eventPlugin } = await import("./eventPlugin-
|
|
429
|
+
const { default: eventPlugin } = await import("./eventPlugin-Ba00swHF.mjs").then((n) => n.n);
|
|
413
430
|
const eventOpts = typeof config.arcPlugins?.events === "object" ? config.arcPlugins.events : {};
|
|
414
431
|
await fastify.register(eventPlugin, {
|
|
415
432
|
...eventOpts,
|
|
@@ -434,16 +451,16 @@ async function createApp(options) {
|
|
|
434
451
|
fastify.log.debug("Arc gracefulShutdown plugin enabled");
|
|
435
452
|
}
|
|
436
453
|
if (config.arcPlugins?.caching) {
|
|
437
|
-
const { default: cachingPlugin } = await import("./caching-
|
|
454
|
+
const { default: cachingPlugin } = await import("./caching-BSXB-Xr7.mjs").then((n) => n.r);
|
|
438
455
|
const cachingOpts = config.arcPlugins.caching === true ? {} : config.arcPlugins.caching;
|
|
439
456
|
await fastify.register(cachingPlugin, cachingOpts);
|
|
440
457
|
trackPlugin("arc-caching", cachingOpts);
|
|
441
458
|
fastify.log.debug("Arc caching plugin enabled");
|
|
442
459
|
}
|
|
443
460
|
if (config.arcPlugins?.queryCache) {
|
|
444
|
-
const { queryCachePlugin } = await import("./queryCachePlugin-
|
|
461
|
+
const { queryCachePlugin } = await import("./queryCachePlugin-ClosZdNS.mjs").then((n) => n.n);
|
|
445
462
|
const qcOpts = config.arcPlugins.queryCache === true ? {} : config.arcPlugins.queryCache;
|
|
446
|
-
const store = options.stores?.queryCache ?? new (await (import("./memory-
|
|
463
|
+
const store = options.stores?.queryCache ?? new (await (import("./memory-Cb_7iy9e.mjs").then((n) => n.n))).MemoryCacheStore();
|
|
447
464
|
await fastify.register(queryCachePlugin, {
|
|
448
465
|
store,
|
|
449
466
|
...qcOpts
|
|
@@ -453,12 +470,25 @@ async function createApp(options) {
|
|
|
453
470
|
}
|
|
454
471
|
if (config.arcPlugins?.sse) if (config.arcPlugins?.events === false) fastify.log.warn("SSE plugin requires events plugin (arcPlugins.events). SSE disabled.");
|
|
455
472
|
else {
|
|
456
|
-
const { default: ssePlugin } = await import("./sse-
|
|
473
|
+
const { default: ssePlugin } = await import("./sse-BkViJPlT.mjs").then((n) => n.r);
|
|
457
474
|
const sseOpts = config.arcPlugins.sse === true ? {} : config.arcPlugins.sse;
|
|
458
475
|
await fastify.register(ssePlugin, sseOpts);
|
|
459
476
|
trackPlugin("arc-sse", sseOpts);
|
|
460
477
|
fastify.log.debug("Arc SSE plugin enabled");
|
|
461
478
|
}
|
|
479
|
+
if (config.arcPlugins?.metrics) {
|
|
480
|
+
const { default: metricsPlugin } = await import("./metrics-Csh4nsvv.mjs").then((n) => n.r);
|
|
481
|
+
const metricsOpts = config.arcPlugins.metrics === true ? {} : config.arcPlugins.metrics;
|
|
482
|
+
await fastify.register(metricsPlugin, metricsOpts);
|
|
483
|
+
trackPlugin("arc-metrics", metricsOpts);
|
|
484
|
+
fastify.log.debug("Arc metrics plugin enabled");
|
|
485
|
+
}
|
|
486
|
+
if (config.arcPlugins?.versioning) {
|
|
487
|
+
const { default: versioningPlugin } = await import("./versioning-BzfeHmhj.mjs").then((n) => n.r);
|
|
488
|
+
await fastify.register(versioningPlugin, config.arcPlugins.versioning);
|
|
489
|
+
trackPlugin("arc-versioning", config.arcPlugins.versioning);
|
|
490
|
+
fastify.log.debug("Arc versioning plugin enabled");
|
|
491
|
+
}
|
|
462
492
|
fastify.decorateRequest("scope", null);
|
|
463
493
|
fastify.addHook("onRequest", async (request) => {
|
|
464
494
|
if (!request.scope) request.scope = PUBLIC_SCOPE;
|
|
@@ -480,13 +510,13 @@ async function createApp(options) {
|
|
|
480
510
|
break;
|
|
481
511
|
case "authenticator": {
|
|
482
512
|
const { authenticate, optionalAuthenticate } = authConfig;
|
|
483
|
-
fastify.decorate("authenticate", async
|
|
513
|
+
fastify.decorate("authenticate", async (request, reply) => {
|
|
484
514
|
await authenticate(request, reply);
|
|
485
515
|
});
|
|
486
|
-
if (!fastify.hasDecorator("optionalAuthenticate")) if (optionalAuthenticate) fastify.decorate("optionalAuthenticate", async
|
|
516
|
+
if (!fastify.hasDecorator("optionalAuthenticate")) if (optionalAuthenticate) fastify.decorate("optionalAuthenticate", async (request, reply) => {
|
|
487
517
|
await optionalAuthenticate(request, reply);
|
|
488
518
|
});
|
|
489
|
-
else fastify.decorate("optionalAuthenticate", async
|
|
519
|
+
else fastify.decorate("optionalAuthenticate", async (request, reply) => {
|
|
490
520
|
let intercepted = false;
|
|
491
521
|
const proxyReply = new Proxy(reply, { get(target, prop) {
|
|
492
522
|
if (prop === "code") return (statusCode) => {
|
|
@@ -521,13 +551,13 @@ async function createApp(options) {
|
|
|
521
551
|
}
|
|
522
552
|
}
|
|
523
553
|
if (config.elevation) {
|
|
524
|
-
const { elevationPlugin } = await import("./elevation-
|
|
554
|
+
const { elevationPlugin } = await import("./elevation-BEdACOLB.mjs").then((n) => n.r);
|
|
525
555
|
await fastify.register(elevationPlugin, config.elevation);
|
|
526
556
|
trackPlugin("arc-elevation", config.elevation);
|
|
527
557
|
fastify.log.debug("Elevation plugin enabled");
|
|
528
558
|
}
|
|
529
559
|
if (config.errorHandler !== false) {
|
|
530
|
-
const { errorHandlerPlugin } = await import("./errorHandler
|
|
560
|
+
const { errorHandlerPlugin } = await import("./errorHandler--zp54tGc.mjs").then((n) => n.n);
|
|
531
561
|
const errorOpts = typeof config.errorHandler === "object" ? config.errorHandler : { includeStack: config.preset !== "production" };
|
|
532
562
|
await fastify.register(errorHandlerPlugin, errorOpts);
|
|
533
563
|
trackPlugin("arc-error-handler", errorOpts);
|
|
@@ -583,6 +613,5 @@ const ArcFactory = {
|
|
|
583
613
|
});
|
|
584
614
|
}
|
|
585
615
|
};
|
|
586
|
-
|
|
587
616
|
//#endregion
|
|
588
|
-
export { getPreset as a, developmentPreset as i, createApp as n, productionPreset as o, createApp_exports as r, testingPreset as s, ArcFactory as t };
|
|
617
|
+
export { getPreset as a, developmentPreset as i, createApp as n, productionPreset as o, createApp_exports as r, testingPreset as s, ArcFactory as t };
|