@classytic/arc 2.8.5 → 2.10.3
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 +50 -38
- package/dist/{BaseController-DAGGc5Xn.mjs → BaseController-CbKKIflT.mjs} +193 -143
- package/dist/EventTransport-CUw5NNWe.d.mts +293 -0
- package/dist/{ResourceRegistry-C6uXlWe3.mjs → ResourceRegistry-BPd6NQDm.mjs} +1 -1
- 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 +135 -11
- package/dist/audit/index.mjs +107 -20
- package/dist/auth/index.d.mts +17 -9
- package/dist/auth/index.mjs +14 -7
- package/dist/auth/redis-session.d.mts +1 -1
- package/dist/{betterAuthOpenApi-BuUcUEJq.mjs → betterAuthOpenApi-BBRVhjQN.mjs} +1 -1
- package/dist/cache/index.d.mts +17 -15
- package/dist/cache/index.mjs +15 -14
- package/dist/{caching-IMuYVjTL.mjs → caching-CBpK_SCM.mjs} +8 -3
- package/dist/cli/commands/describe.mjs +1 -1
- package/dist/cli/commands/docs.mjs +2 -2
- package/dist/cli/commands/generate.mjs +1 -1
- package/dist/cli/commands/init.mjs +1 -1
- package/dist/cli/commands/introspect.mjs +1 -1
- package/dist/core/index.d.mts +3 -3
- package/dist/core/index.mjs +4 -6
- package/dist/{defineResource-tcgySDo1.mjs → core-CcR01lup.mjs} +58 -61
- package/dist/{createActionRouter-BORM8f17.mjs → createActionRouter-Bp_5c_2b.mjs} +3 -3
- package/dist/{createApp-B1EY8zxa.mjs → createApp-BuvPma24.mjs} +15 -14
- package/dist/docs/index.d.mts +2 -2
- package/dist/docs/index.mjs +2 -2
- package/dist/{elevation-DtFxrG0s.mjs → elevation-C7hgL_aI.mjs} +22 -8
- package/dist/{errorHandler-f869_8PQ.mjs → errorHandler-Bb49BvPD.mjs} +59 -7
- package/dist/{errorHandler-Bah5JhBd.d.mts → errorHandler-DRQ3EqfL.d.mts} +37 -2
- package/dist/{eventPlugin-D9DKB2zM.d.mts → eventPlugin-CxWgpd6K.d.mts} +14 -2
- package/dist/{eventPlugin-CDjVTM82.mjs → eventPlugin-DCUjuiQT.mjs} +83 -5
- package/dist/events/index.d.mts +150 -36
- package/dist/events/index.mjs +355 -101
- 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 +2 -2
- package/dist/{types-DZi1aYhm.d.mts → fields-Lo1VUDpt.d.mts} +121 -1
- package/dist/{fields-ipsbIRPK.mjs → fields-bxkeltzz.mjs} +18 -5
- package/dist/{filesUpload-C7r7HIeA.mjs → filesUpload-t21LS-py.mjs} +65 -7
- package/dist/hooks/index.d.mts +1 -1
- package/dist/hooks/index.mjs +1 -1
- package/dist/idempotency/index.d.mts +32 -5
- package/dist/idempotency/index.mjs +119 -12
- package/dist/idempotency/redis.d.mts +1 -1
- package/dist/{index-DtDzOBn8.d.mts → index-8qw4y6ff.d.mts} +4 -135
- package/dist/{index-BLXBmWud.d.mts → index-ChIw3776.d.mts} +283 -408
- package/dist/{interface-CMRutPfe.d.mts → index-Cl0uoKd5.d.mts} +1758 -2506
- package/dist/{index-C1meYuDn.d.mts → index-DStwgFUK.d.mts} +81 -7
- package/dist/index.d.mts +7 -8
- package/dist/index.mjs +11 -12
- 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 +26 -8
- package/dist/integrations/mcp/index.mjs +96 -17
- package/dist/integrations/mcp/testing.d.mts +1 -1
- package/dist/integrations/mcp/testing.mjs +1 -1
- package/dist/integrations/webhooks.d.mts +5 -0
- package/dist/integrations/webhooks.mjs +6 -0
- package/dist/interface-D218ikEo.d.mts +77 -0
- package/dist/{memory-Cp7_cAko.mjs → memory-B5Amv9A1.mjs} +23 -8
- package/dist/{openapi-CbKUJY_m.mjs → openapi-B5F8AddX.mjs} +3 -3
- 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-CH4cNwJi.mjs → permissions-Dk6mshja.mjs} +315 -397
- package/dist/plugins/index.d.mts +7 -7
- package/dist/plugins/index.mjs +14 -16
- package/dist/plugins/response-cache.mjs +2 -2
- package/dist/plugins/tracing-entry.d.mts +1 -1
- package/dist/plugins/tracing-entry.mjs +1 -1
- package/dist/presets/filesUpload.d.mts +27 -5
- package/dist/presets/filesUpload.mjs +1 -1
- package/dist/presets/index.d.mts +3 -2
- package/dist/presets/index.mjs +4 -3
- package/dist/presets/multiTenant.d.mts +1 -1
- package/dist/presets/multiTenant.mjs +2 -2
- package/dist/presets/search.d.mts +178 -0
- package/dist/presets/search.mjs +150 -0
- package/dist/{presets-C2xgzW6x.mjs → presets-fLJVXdVn.mjs} +1 -1
- package/dist/{queryCachePlugin-BJJGBTlu.d.mts → queryCachePlugin-BKbWjgDG.d.mts} +1 -1
- package/dist/{queryCachePlugin-BH-fidlv.mjs → queryCachePlugin-DQCEfJis.mjs} +9 -9
- package/dist/{queryParser-CgCtsjti.mjs → queryParser-DBqBB6AC.mjs} +1 -1
- package/dist/{redis-BM00zaPB.d.mts → redis-DqyeggCa.d.mts} +1 -1
- package/dist/{redis-stream-CrsfUmPt.d.mts → redis-stream-CakIQmwR.d.mts} +1 -1
- package/dist/registry/index.d.mts +1 -1
- package/dist/registry/index.mjs +2 -2
- package/dist/{resourceToTools-8s-EsCCe.mjs → resourceToTools-BElv3xPT.mjs} +65 -48
- package/dist/{schemaConverter-Y7nCYaLJ.mjs → schemaConverter-BxFDdtXu.mjs} +1 -1
- package/dist/scope/index.d.mts +1 -1
- package/dist/scope/index.mjs +2 -2
- package/dist/{sse-Ad7ypl9e.mjs → sse-yBCgOLGu.mjs} +1 -1
- package/dist/store-helpers-ZCSMJJAX.mjs +57 -0
- package/dist/testing/index.d.mts +9 -17
- package/dist/testing/index.mjs +27 -83
- package/dist/testing/storageContract.d.mts +1 -1
- package/dist/types/index.d.mts +4 -4
- package/dist/types/index.mjs +1 -31
- package/dist/types/storage.d.mts +1 -1
- package/dist/{types-BsbNMEDR.d.mts → types-Btdda02s.d.mts} +1 -1
- package/dist/{types-Ch9pTQbf.d.mts → types-Co8k3NyS.d.mts} +11 -9
- package/dist/types-Csi3FLfq.mjs +27 -0
- package/dist/utils/index.d.mts +208 -4
- package/dist/utils/index.mjs +5 -6
- package/dist/{utils-yYT3HDXt.mjs → utils-B2fNOD_i.mjs} +285 -2
- package/dist/{versioning-CDugduqI.mjs → versioning-C2U_bLY0.mjs} +3 -5
- package/package.json +20 -26
- package/skills/arc/SKILL.md +97 -23
- package/skills/arc/references/auth.md +94 -0
- package/skills/arc/references/events.md +200 -12
- package/skills/arc/references/mcp.md +4 -17
- package/skills/arc/references/multi-tenancy.md +43 -0
- package/skills/arc/references/production.md +34 -60
- package/dist/EventTransport-BXja8NOc.d.mts +0 -135
- package/dist/audit/mongodb.d.mts +0 -2
- package/dist/audit/mongodb.mjs +0 -2
- package/dist/circuitBreaker-cmi5XDv5.mjs +0 -284
- package/dist/circuitBreaker-dTtG-UyS.d.mts +0 -206
- package/dist/core-F0QoWBt2.mjs +0 -34
- package/dist/dynamic/index.d.mts +0 -93
- package/dist/dynamic/index.mjs +0 -122
- package/dist/fields-DpZQa_Q3.d.mts +0 -109
- package/dist/idempotency/mongodb.d.mts +0 -2
- package/dist/idempotency/mongodb.mjs +0 -123
- package/dist/interface-4y979v99.d.mts +0 -54
- package/dist/mongodb-BsP-WbhN.d.mts +0 -127
- package/dist/mongodb-CTcp0hQZ.d.mts +0 -80
- package/dist/mongodb-Utc5k_-0.mjs +0 -90
- package/dist/policies/index.d.mts +0 -432
- package/dist/policies/index.mjs +0 -318
- package/dist/rpc/index.d.mts +0 -90
- package/dist/rpc/index.mjs +0 -248
- /package/dist/{HookSystem-HprTmvVY.mjs → HookSystem-BNYKnrXF.mjs} +0 -0
- /package/dist/{applyPermissionResult-D6GPMsvh.mjs → applyPermissionResult-QhV1Pa-g.mjs} +0 -0
- /package/dist/{constants-Cxde4rpC.mjs → constants-BhY1OHoH.mjs} +0 -0
- /package/dist/{elevation-B6S5csVA.d.mts → elevation-C5SwtkAn.d.mts} +0 -0
- /package/dist/{errors-Ck2h67pm.d.mts → errors-CCSsMpXE.d.mts} +0 -0
- /package/dist/{errors-BF2bIOIS.mjs → errors-D5c-5BJL.mjs} +0 -0
- /package/dist/{externalPaths-BnkYrNzp.d.mts → externalPaths-BQ8QijNH.d.mts} +0 -0
- /package/dist/{interface-DfLGcus7.d.mts → interface-CSbZdv_3.d.mts} +0 -0
- /package/dist/{loadResources-PWd0OCpV.mjs → loadResources-BAzJItAJ.mjs} +0 -0
- /package/dist/{logger-D1YrIImS.mjs → logger-DLg8-Ueg.mjs} +0 -0
- /package/dist/{metrics-B-PU4-Yu.mjs → metrics-DuhiSEZI.mjs} +0 -0
- /package/dist/{pluralize-CWP6MB39.mjs → pluralize-A0tWEl1K.mjs} +0 -0
- /package/dist/{registry-BiTKT1Dg.mjs → registry-B3lRFBWo.mjs} +0 -0
- /package/dist/{replyHelpers-CxkYGT81.mjs → replyHelpers-CXtJDAZ0.mjs} +0 -0
- /package/dist/{requestContext-DYvHl113.mjs → requestContext-xHIKedG6.mjs} +0 -0
- /package/dist/{sessionManager-DDCmiNIo.d.mts → sessionManager-BkzVU8h2.d.mts} +0 -0
- /package/dist/{storage-Dfzt4VTl.d.mts → storage-CVk_SEn2.d.mts} +0 -0
- /package/dist/{tracing-DdN2-wHJ.d.mts → tracing-65B51Dw3.d.mts} +0 -0
- /package/dist/{typeGuards-CcFZXgU7.mjs → typeGuards-Cj5Rgvlg.mjs} +0 -0
- /package/dist/{types-ZUu_h0jp.mjs → types-DV9WDfeg.mjs} +0 -0
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import { t as ResourceRegistry } from "../../ResourceRegistry-
|
|
2
|
-
import { t as buildOpenApiSpec } from "../../openapi-
|
|
1
|
+
import { t as ResourceRegistry } from "../../ResourceRegistry-BPd6NQDm.mjs";
|
|
2
|
+
import { t as buildOpenApiSpec } from "../../openapi-B5F8AddX.mjs";
|
|
3
3
|
import { dirname, resolve } from "node:path";
|
|
4
4
|
import { pathToFileURL } from "node:url";
|
|
5
5
|
import { mkdirSync, writeFileSync } from "node:fs";
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { t as pluralize } from "../../pluralize-
|
|
1
|
+
import { t as pluralize } from "../../pluralize-A0tWEl1K.mjs";
|
|
2
2
|
import { join } from "node:path";
|
|
3
3
|
import { existsSync, mkdirSync, readFileSync, writeFileSync } from "node:fs";
|
|
4
4
|
//#region src/cli/commands/generate.ts
|
|
@@ -102,7 +102,7 @@ async function installDependencies(projectPath, config, pm) {
|
|
|
102
102
|
];
|
|
103
103
|
if (config.auth === "better-auth") deps.push("better-auth@^1.6.0", "mongodb@latest");
|
|
104
104
|
else deps.push("@fastify/jwt@latest", "bcryptjs@latest");
|
|
105
|
-
if (config.adapter === "mongokit") deps.push("@classytic/mongokit@^3.
|
|
105
|
+
if (config.adapter === "mongokit") deps.push("@classytic/mongokit@^3.10.2", "@classytic/repo-core@^0.1.0", "mongoose@^9.4.1");
|
|
106
106
|
const devDeps = ["vitest@latest", "pino-pretty@latest"];
|
|
107
107
|
if (config.typescript) devDeps.push("typescript@latest", "@types/node@latest", "tsx@latest");
|
|
108
108
|
const installCmd = getInstallCommand(pm, deps, false);
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { t as ResourceRegistry } from "../../ResourceRegistry-
|
|
1
|
+
import { t as ResourceRegistry } from "../../ResourceRegistry-BPd6NQDm.mjs";
|
|
2
2
|
import { resolve } from "node:path";
|
|
3
3
|
import { pathToFileURL } from "node:url";
|
|
4
4
|
//#region src/cli/commands/introspect.ts
|
package/dist/core/index.d.mts
CHANGED
|
@@ -1,3 +1,3 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import {
|
|
3
|
-
export { AccessControl, AccessControlConfig,
|
|
1
|
+
import { G as ResourceDefinition, K as defineResource, a as QueryResolverConfig, c as AccessControl, i as QueryResolver, l as AccessControlConfig, n as BaseController, o as BodySanitizer, r as BaseControllerOptions, s as BodySanitizerConfig } from "../index-Cl0uoKd5.mjs";
|
|
2
|
+
import { C as MAX_REGEX_LENGTH, D as RESERVED_QUERY_PARAMS, E as MutationOperation, O as SYSTEM_FIELDS, S as MAX_FILTER_DEPTH, T as MUTATION_OPERATIONS, _ as DEFAULT_UPDATE_METHOD, a as getControllerScope, b as HookOperation, c as createCrudRouter, d as CrudOperation, f as DEFAULT_ID_FIELD, g as DEFAULT_TENANT_FIELD, h as DEFAULT_SORT, i as getControllerContext, l as createPermissionMiddleware, m as DEFAULT_MAX_LIMIT, n as createFastifyHandler, o as sendControllerResponse, p as DEFAULT_LIMIT, r as createRequestContext, s as defineResourceVariants, t as createCrudHandlers, u as CRUD_OPERATIONS, v as HOOK_OPERATIONS, w as MAX_SEARCH_LENGTH, x as HookPhase, y as HOOK_PHASES } from "../index-8qw4y6ff.mjs";
|
|
3
|
+
export { AccessControl, AccessControlConfig, 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, MAX_FILTER_DEPTH, MAX_REGEX_LENGTH, MAX_SEARCH_LENGTH, MUTATION_OPERATIONS, MutationOperation, QueryResolver, QueryResolverConfig, RESERVED_QUERY_PARAMS, ResourceDefinition, SYSTEM_FIELDS, createCrudHandlers, createCrudRouter, createFastifyHandler, createPermissionMiddleware, createRequestContext, defineResource, defineResourceVariants, getControllerContext, getControllerScope, sendControllerResponse };
|
package/dist/core/index.mjs
CHANGED
|
@@ -1,6 +1,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-
|
|
2
|
-
import { i as AccessControl, n as QueryResolver, r as BodySanitizer, t as BaseController } from "../BaseController-
|
|
3
|
-
import { n as
|
|
4
|
-
|
|
5
|
-
import { t as defineResourceVariants } from "../core-F0QoWBt2.mjs";
|
|
6
|
-
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, buildActionBodySchema, createActionRouter, createCrudHandlers, createCrudRouter, createFastifyHandler, createPermissionMiddleware, createRequestContext, defineResource, defineResourceVariants, getControllerContext, getControllerScope, sendControllerResponse };
|
|
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-BhY1OHoH.mjs";
|
|
2
|
+
import { i as AccessControl, n as QueryResolver, r as BodySanitizer, t as BaseController } from "../BaseController-CbKKIflT.mjs";
|
|
3
|
+
import { c as createPermissionMiddleware, d as createRequestContext, f as getControllerContext, l as createCrudHandlers, m as sendControllerResponse, n as ResourceDefinition, p as getControllerScope, r as defineResource, s as createCrudRouter, t as defineResourceVariants, u as createFastifyHandler } from "../core-CcR01lup.mjs";
|
|
4
|
+
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, createCrudHandlers, createCrudRouter, createFastifyHandler, createPermissionMiddleware, createRequestContext, defineResource, defineResourceVariants, getControllerContext, getControllerScope, sendControllerResponse };
|
|
@@ -1,15 +1,15 @@
|
|
|
1
|
-
import { s as DEFAULT_UPDATE_METHOD, t as CRUD_OPERATIONS } from "./constants-
|
|
1
|
+
import { s as DEFAULT_UPDATE_METHOD, t as CRUD_OPERATIONS } from "./constants-BhY1OHoH.mjs";
|
|
2
2
|
import { _ as isElevated, n as PUBLIC_SCOPE, v as isMember } from "./types-AOD8fxIw.mjs";
|
|
3
|
-
import { t as BaseController } from "./BaseController-
|
|
4
|
-
import { i as resolveEffectiveRoles, t as applyFieldReadPermissions } from "./fields-
|
|
5
|
-
import { t as getUserRoles } from "./types-
|
|
6
|
-
import {
|
|
7
|
-
import {
|
|
8
|
-
import { t as
|
|
9
|
-
import { i as getDefaultCrudSchemas } from "./utils-
|
|
10
|
-
import {
|
|
11
|
-
import { t as hasEvents } from "./typeGuards-
|
|
12
|
-
import { r as getAvailablePresets, t as applyPresets } from "./presets-
|
|
3
|
+
import { t as BaseController } from "./BaseController-CbKKIflT.mjs";
|
|
4
|
+
import { i as resolveEffectiveRoles, t as applyFieldReadPermissions } from "./fields-bxkeltzz.mjs";
|
|
5
|
+
import { t as getUserRoles } from "./types-DV9WDfeg.mjs";
|
|
6
|
+
import { r as ForbiddenError } from "./errors-D5c-5BJL.mjs";
|
|
7
|
+
import { t as requestContext } from "./requestContext-xHIKedG6.mjs";
|
|
8
|
+
import { n as normalizePermissionResult, t as applyPermissionResult } from "./applyPermissionResult-QhV1Pa-g.mjs";
|
|
9
|
+
import { i as getDefaultCrudSchemas } from "./utils-B2fNOD_i.mjs";
|
|
10
|
+
import { n as convertRouteSchema, t as convertOpenApiSchemas } from "./schemaConverter-BxFDdtXu.mjs";
|
|
11
|
+
import { t as hasEvents } from "./typeGuards-Cj5Rgvlg.mjs";
|
|
12
|
+
import { r as getAvailablePresets, t as applyPresets } from "./presets-fLJVXdVn.mjs";
|
|
13
13
|
//#region src/pipeline/pipe.ts
|
|
14
14
|
/**
|
|
15
15
|
* Compose pipeline steps into an ordered array.
|
|
@@ -387,24 +387,26 @@ function buildPermissionMiddleware(permissionCheck, resourceName, action) {
|
|
|
387
387
|
};
|
|
388
388
|
}
|
|
389
389
|
/**
|
|
390
|
-
*
|
|
390
|
+
* Mount custom routes (from presets or user-defined `routes`) on Fastify.
|
|
391
|
+
* `wrapHandler` is derived inline from `!route.raw`.
|
|
391
392
|
*/
|
|
392
|
-
function
|
|
393
|
+
function createCustomRoutes(fastify, routes, controller, options) {
|
|
393
394
|
const { tag, resourceName, arcDecorator, rateLimitConfig, cacheMw, idempotencyMw, pipeline, routeGuards } = options;
|
|
394
395
|
for (const route of routes) {
|
|
395
396
|
const opName = route.operation ?? (typeof route.handler === "string" ? route.handler : `${route.method.toLowerCase()}${route.path.replace(/[/:]/g, "_")}`);
|
|
397
|
+
const wrapHandler = !route.raw;
|
|
396
398
|
let handler;
|
|
397
399
|
if (typeof route.handler === "string") {
|
|
398
400
|
if (!controller) throw new Error(`Route ${route.method} ${route.path}: string handler '${route.handler}' requires a controller. Either provide a controller or use a function handler instead.`);
|
|
399
401
|
const method = controller[route.handler];
|
|
400
402
|
if (typeof method !== "function") throw new Error(`Handler '${route.handler}' not found on controller`);
|
|
401
403
|
const boundMethod = method.bind(controller);
|
|
402
|
-
if (
|
|
404
|
+
if (wrapHandler) {
|
|
403
405
|
const steps = pipeline ? resolvePipelineSteps(pipeline, opName) : [];
|
|
404
406
|
if (steps.length > 0) handler = createPipelineHandler(boundMethod, steps, opName, resourceName);
|
|
405
407
|
else handler = createFastifyHandler(boundMethod);
|
|
406
408
|
} else handler = boundMethod;
|
|
407
|
-
} else if (
|
|
409
|
+
} else if (wrapHandler) {
|
|
408
410
|
const steps = pipeline ? resolvePipelineSteps(pipeline, opName) : [];
|
|
409
411
|
if (steps.length > 0) handler = createPipelineHandler(route.handler, steps, opName, resourceName);
|
|
410
412
|
else handler = createFastifyHandler(route.handler);
|
|
@@ -480,7 +482,7 @@ function createPipelineHandler(controllerMethod, steps, operation, resourceName)
|
|
|
480
482
|
* @param options - Router configuration
|
|
481
483
|
*/
|
|
482
484
|
function createCrudRouter(fastify, controller, options = {}) {
|
|
483
|
-
const { tag = "Resource", schemas = {}, permissions = {}, middlewares = {}, routeGuards = [],
|
|
485
|
+
const { tag = "Resource", schemas = {}, permissions = {}, middlewares = {}, routeGuards = [], routes: customRoutes = [], disableDefaultRoutes = false, disabledRoutes = [], resourceName = "unknown", schemaOptions, rateLimit, pipe: pipeline, fields: fieldPermissions, updateMethod = DEFAULT_UPDATE_METHOD } = options;
|
|
484
486
|
const rateLimitConfig = buildRateLimitConfig(rateLimit);
|
|
485
487
|
const cacheMw = !(fastify.hasDecorator("queryCache") && controller && typeof controller._cacheConfig !== "undefined" && controller._cacheConfig !== void 0) && fastify.hasDecorator("responseCache") ? fastify.responseCache.middleware : null;
|
|
486
488
|
const idempotencyMw = fastify.hasDecorator("idempotency") ? fastify.idempotency.middleware : null;
|
|
@@ -646,7 +648,7 @@ function createCrudRouter(fastify, controller, options = {}) {
|
|
|
646
648
|
});
|
|
647
649
|
}
|
|
648
650
|
}
|
|
649
|
-
if (
|
|
651
|
+
if (customRoutes.length > 0) createCustomRoutes(fastify, customRoutes, controller, {
|
|
650
652
|
tag,
|
|
651
653
|
resourceName,
|
|
652
654
|
arcDecorator,
|
|
@@ -706,7 +708,7 @@ function validateResourceConfig(config, options = {}) {
|
|
|
706
708
|
else if (!config.adapter.repository) errors.push({
|
|
707
709
|
field: "adapter.repository",
|
|
708
710
|
message: "Adapter must provide a repository",
|
|
709
|
-
suggestion: "Ensure your adapter returns a valid
|
|
711
|
+
suggestion: "Ensure your adapter returns a valid StandardRepo (see @classytic/repo-core)"
|
|
710
712
|
});
|
|
711
713
|
} else if (!config.adapter && !config.routes?.length) warnings.push({
|
|
712
714
|
field: "config",
|
|
@@ -941,6 +943,7 @@ function defineResource(config) {
|
|
|
941
943
|
idField: resolvedConfig.idField,
|
|
942
944
|
matchesFilter: config.adapter?.matchesFilter,
|
|
943
945
|
cache: resolvedConfig.cache,
|
|
946
|
+
onFieldWriteDenied: resolvedConfig.onFieldWriteDenied,
|
|
944
947
|
presetFields: resolvedConfig._controllerOptions ? {
|
|
945
948
|
slugField: resolvedConfig._controllerOptions.slugField,
|
|
946
949
|
parentField: resolvedConfig._controllerOptions.parentField
|
|
@@ -1088,16 +1091,6 @@ var ResourceDefinition = class {
|
|
|
1088
1091
|
schemaOptions;
|
|
1089
1092
|
customSchemas;
|
|
1090
1093
|
permissions;
|
|
1091
|
-
additionalRoutes;
|
|
1092
|
-
/**
|
|
1093
|
-
* Original v2.8 `routes` declaration — retained for downstream consumers
|
|
1094
|
-
* (OpenAPI, MCP, registry, CLI introspect). Preserves fields dropped during
|
|
1095
|
-
* normalization to `additionalRoutes` (notably `mcp`, `description`,
|
|
1096
|
-
* `annotations`). Undefined when the resource was defined with the legacy
|
|
1097
|
-
* `additionalRoutes` shape.
|
|
1098
|
-
*
|
|
1099
|
-
* Added in 2.8.1 — the source-of-truth fix for "canonical resource manifest".
|
|
1100
|
-
*/
|
|
1101
1094
|
routes;
|
|
1102
1095
|
middlewares;
|
|
1103
1096
|
routeGuards;
|
|
@@ -1130,8 +1123,7 @@ var ResourceDefinition = class {
|
|
|
1130
1123
|
this.schemaOptions = config.schemaOptions ?? {};
|
|
1131
1124
|
this.customSchemas = config.customSchemas ?? {};
|
|
1132
1125
|
this.permissions = config.permissions ?? {};
|
|
1133
|
-
this.routes = config.routes;
|
|
1134
|
-
this.additionalRoutes = config.routes ? convertRoutesToAdditionalRoutes(config.routes) : [];
|
|
1126
|
+
this.routes = config.routes ?? [];
|
|
1135
1127
|
this.middlewares = config.middlewares ?? {};
|
|
1136
1128
|
this.routeGuards = config.routeGuards;
|
|
1137
1129
|
this.disableDefaultRoutes = config.disableDefaultRoutes ?? false;
|
|
@@ -1150,7 +1142,6 @@ var ResourceDefinition = class {
|
|
|
1150
1142
|
this.queryParser = config.queryParser;
|
|
1151
1143
|
this._appliedPresets = config._appliedPresets ?? [];
|
|
1152
1144
|
this._pendingHooks = config._pendingHooks ?? [];
|
|
1153
|
-
if (config.onRegister) this._onRegister = config.onRegister;
|
|
1154
1145
|
}
|
|
1155
1146
|
/** Get repository from adapter (if available) */
|
|
1156
1147
|
get repository() {
|
|
@@ -1166,7 +1157,7 @@ var ResourceDefinition = class {
|
|
|
1166
1157
|
const ctrl = this.controller;
|
|
1167
1158
|
for (const method of enabledCrudRoutes) if (typeof ctrl[method] !== "function") errors.push(`CRUD method '${method}' not found on controller`);
|
|
1168
1159
|
}
|
|
1169
|
-
for (const route of this.
|
|
1160
|
+
for (const route of this.routes) if (typeof route.handler === "string") {
|
|
1170
1161
|
if (!this.controller) errors.push(`Route ${route.method} ${route.path}: string handler '${route.handler}' requires a controller`);
|
|
1171
1162
|
else if (typeof this.controller[route.handler] !== "function") errors.push(`Route ${route.method} ${route.path}: handler '${route.handler}' not found`);
|
|
1172
1163
|
}
|
|
@@ -1207,8 +1198,6 @@ var ResourceDefinition = class {
|
|
|
1207
1198
|
});
|
|
1208
1199
|
await fastify.register(async (instance) => {
|
|
1209
1200
|
const typedInstance = instance;
|
|
1210
|
-
const onRegister = self._onRegister;
|
|
1211
|
-
if (onRegister) await onRegister(instance);
|
|
1212
1201
|
let schemas = null;
|
|
1213
1202
|
const openApi = self._registryMeta?.openApiSchemas;
|
|
1214
1203
|
if (openApi && (!self.customSchemas || Object.keys(self.customSchemas).length === 0)) {
|
|
@@ -1281,14 +1270,13 @@ var ResourceDefinition = class {
|
|
|
1281
1270
|
schemas = schemas ?? {};
|
|
1282
1271
|
schemas.list = schemas.list ? deepMergeSchemas({ querystring: normalizedSchema }, schemas.list) : { querystring: normalizedSchema };
|
|
1283
1272
|
}
|
|
1284
|
-
const resolvedRoutes = self.additionalRoutes;
|
|
1285
1273
|
createCrudRouter(typedInstance, self.controller, {
|
|
1286
1274
|
tag: self.tag,
|
|
1287
1275
|
schemas: schemas ?? void 0,
|
|
1288
1276
|
permissions: self.permissions,
|
|
1289
1277
|
middlewares: self.middlewares,
|
|
1290
1278
|
routeGuards: self.routeGuards,
|
|
1291
|
-
|
|
1279
|
+
routes: self.routes,
|
|
1292
1280
|
disableDefaultRoutes: self.disableDefaultRoutes,
|
|
1293
1281
|
disabledRoutes: self.disabledRoutes,
|
|
1294
1282
|
resourceName: self.name,
|
|
@@ -1299,7 +1287,7 @@ var ResourceDefinition = class {
|
|
|
1299
1287
|
fields: self.fields
|
|
1300
1288
|
});
|
|
1301
1289
|
if (self.actions && Object.keys(self.actions).length > 0) {
|
|
1302
|
-
const { createActionRouter } = await import("./createActionRouter-
|
|
1290
|
+
const { createActionRouter } = await import("./createActionRouter-Bp_5c_2b.mjs").then((n) => n.n);
|
|
1303
1291
|
createActionRouter(instance, normalizeActionsToRouterConfig(self.actions, self.actionPermissions, self.tag));
|
|
1304
1292
|
}
|
|
1305
1293
|
if (self.events && Object.keys(self.events).length > 0) typedInstance.log?.debug?.(`Resource '${self.name}' defined ${Object.keys(self.events).length} events`);
|
|
@@ -1366,29 +1354,6 @@ function capitalize(str) {
|
|
|
1366
1354
|
return str.charAt(0).toUpperCase() + str.slice(1);
|
|
1367
1355
|
}
|
|
1368
1356
|
/**
|
|
1369
|
-
* Convert v2.8 RouteDefinition[] to internal AdditionalRoute[] format.
|
|
1370
|
-
* The internal format is what createCrudRouter understands.
|
|
1371
|
-
*/
|
|
1372
|
-
function convertRoutesToAdditionalRoutes(routes) {
|
|
1373
|
-
return routes.map((route) => ({
|
|
1374
|
-
method: route.method,
|
|
1375
|
-
path: route.path,
|
|
1376
|
-
handler: route.handler,
|
|
1377
|
-
permissions: route.permissions,
|
|
1378
|
-
wrapHandler: !route.raw,
|
|
1379
|
-
operation: route.operation,
|
|
1380
|
-
summary: route.summary,
|
|
1381
|
-
description: route.description,
|
|
1382
|
-
tags: route.tags,
|
|
1383
|
-
preHandler: route.preHandler,
|
|
1384
|
-
preAuth: route.preAuth,
|
|
1385
|
-
streamResponse: route.streamResponse,
|
|
1386
|
-
schema: route.schema,
|
|
1387
|
-
mcpHandler: route.mcpHandler,
|
|
1388
|
-
mcp: route.mcp
|
|
1389
|
-
}));
|
|
1390
|
-
}
|
|
1391
|
-
/**
|
|
1392
1357
|
* Normalize ActionsMap into the ActionRouterConfig shape that createActionRouter expects.
|
|
1393
1358
|
*/
|
|
1394
1359
|
function normalizeActionsToRouterConfig(actions, globalAuth, tag) {
|
|
@@ -1411,4 +1376,36 @@ function normalizeActionsToRouterConfig(actions, globalAuth, tag) {
|
|
|
1411
1376
|
};
|
|
1412
1377
|
}
|
|
1413
1378
|
//#endregion
|
|
1414
|
-
|
|
1379
|
+
//#region src/core/defineResourceVariants.ts
|
|
1380
|
+
/**
|
|
1381
|
+
* Define multiple resources from a shared base config and per-variant overrides.
|
|
1382
|
+
*
|
|
1383
|
+
* Each variant is independently passed through `defineResource()` — the
|
|
1384
|
+
* returned `ResourceDefinition`s are real, fully-registered resources.
|
|
1385
|
+
* Register each one's plugin in your app:
|
|
1386
|
+
*
|
|
1387
|
+
* ```typescript
|
|
1388
|
+
* await app.register(articlePublic.toPlugin());
|
|
1389
|
+
* await app.register(articleAdmin.toPlugin());
|
|
1390
|
+
* ```
|
|
1391
|
+
*
|
|
1392
|
+
* @param base Shared config — adapter, queryParser, schemaOptions, hooks, etc.
|
|
1393
|
+
* Must NOT include `name` or `prefix` (those are per-variant).
|
|
1394
|
+
* @param variants Map of variant key → override. Each variant must declare
|
|
1395
|
+
* its own `name` and `prefix`. Other fields override the base.
|
|
1396
|
+
* @returns A record where each key from `variants` maps to a real
|
|
1397
|
+
* `ResourceDefinition` ready for `.toPlugin()` registration.
|
|
1398
|
+
*/
|
|
1399
|
+
function defineResourceVariants(base, variants) {
|
|
1400
|
+
const out = {};
|
|
1401
|
+
for (const key of Object.keys(variants)) {
|
|
1402
|
+
const override = variants[key];
|
|
1403
|
+
out[key] = defineResource({
|
|
1404
|
+
...base,
|
|
1405
|
+
...override
|
|
1406
|
+
});
|
|
1407
|
+
}
|
|
1408
|
+
return out;
|
|
1409
|
+
}
|
|
1410
|
+
//#endregion
|
|
1411
|
+
export { formatValidationErrors as a, createPermissionMiddleware as c, createRequestContext as d, getControllerContext as f, pipe as h, assertValidConfig as i, createCrudHandlers as l, sendControllerResponse as m, ResourceDefinition as n, validateResourceConfig as o, getControllerScope as p, defineResource as r, createCrudRouter as s, defineResourceVariants as t, createFastifyHandler as u };
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { t as __exportAll } from "./chunk-BpYLSNr0.mjs";
|
|
2
|
-
import { n as normalizePermissionResult, t as applyPermissionResult } from "./applyPermissionResult-
|
|
3
|
-
import { a as toJsonSchema } from "./schemaConverter-
|
|
2
|
+
import { n as normalizePermissionResult, t as applyPermissionResult } from "./applyPermissionResult-QhV1Pa-g.mjs";
|
|
3
|
+
import { a as toJsonSchema } from "./schemaConverter-BxFDdtXu.mjs";
|
|
4
4
|
//#region src/core/createActionRouter.ts
|
|
5
5
|
var createActionRouter_exports = /* @__PURE__ */ __exportAll({
|
|
6
6
|
buildActionBodySchema: () => buildActionBodySchema,
|
|
@@ -246,4 +246,4 @@ function buildActionDescription(actions, actionPermissions) {
|
|
|
246
246
|
return lines.join("\n");
|
|
247
247
|
}
|
|
248
248
|
//#endregion
|
|
249
|
-
export {
|
|
249
|
+
export { createActionRouter_exports as n, buildActionBodySchema as t };
|
|
@@ -207,7 +207,7 @@ async function registerArcCore(fastify, config, trackPlugin) {
|
|
|
207
207
|
await fastify.register(arcCorePlugin, { emitEvents: config.arcPlugins?.emitEvents !== false });
|
|
208
208
|
trackPlugin("arc-core");
|
|
209
209
|
if (config.arcPlugins?.events !== false) {
|
|
210
|
-
const { default: eventPlugin } = await import("./eventPlugin-
|
|
210
|
+
const { default: eventPlugin } = await import("./eventPlugin-DCUjuiQT.mjs").then((n) => n.n);
|
|
211
211
|
const eventOpts = typeof config.arcPlugins?.events === "object" ? config.arcPlugins.events : {};
|
|
212
212
|
await fastify.register(eventPlugin, {
|
|
213
213
|
...eventOpts,
|
|
@@ -243,15 +243,15 @@ async function registerArcPlugins(fastify, config, trackPlugin, modules) {
|
|
|
243
243
|
trackPlugin("arc-graceful-shutdown");
|
|
244
244
|
}
|
|
245
245
|
if (config.arcPlugins?.caching) {
|
|
246
|
-
const { default: cachingPlugin } = await import("./caching-
|
|
246
|
+
const { default: cachingPlugin } = await import("./caching-CBpK_SCM.mjs").then((n) => n.r);
|
|
247
247
|
const opts = config.arcPlugins.caching === true ? {} : config.arcPlugins.caching;
|
|
248
248
|
await fastify.register(cachingPlugin, opts);
|
|
249
249
|
trackPlugin("arc-caching", opts);
|
|
250
250
|
}
|
|
251
251
|
if (config.arcPlugins?.queryCache) {
|
|
252
|
-
const { queryCachePlugin } = await import("./queryCachePlugin-
|
|
252
|
+
const { queryCachePlugin } = await import("./queryCachePlugin-DQCEfJis.mjs").then((n) => n.n);
|
|
253
253
|
const opts = config.arcPlugins.queryCache === true ? {} : config.arcPlugins.queryCache;
|
|
254
|
-
const store = config.stores?.queryCache ?? new (await (import("./memory-
|
|
254
|
+
const store = config.stores?.queryCache ?? new (await (import("./memory-B5Amv9A1.mjs").then((n) => n.n))).MemoryCacheStore();
|
|
255
255
|
await fastify.register(queryCachePlugin, {
|
|
256
256
|
store,
|
|
257
257
|
...opts
|
|
@@ -260,19 +260,19 @@ async function registerArcPlugins(fastify, config, trackPlugin, modules) {
|
|
|
260
260
|
}
|
|
261
261
|
if (config.arcPlugins?.sse) if (config.arcPlugins?.events === false) fastify.log.warn("SSE plugin requires events plugin (arcPlugins.events). SSE disabled.");
|
|
262
262
|
else {
|
|
263
|
-
const { default: ssePlugin } = await import("./sse-
|
|
263
|
+
const { default: ssePlugin } = await import("./sse-yBCgOLGu.mjs").then((n) => n.r);
|
|
264
264
|
const opts = config.arcPlugins.sse === true ? {} : config.arcPlugins.sse;
|
|
265
265
|
await fastify.register(ssePlugin, opts);
|
|
266
266
|
trackPlugin("arc-sse", opts);
|
|
267
267
|
}
|
|
268
268
|
if (config.arcPlugins?.metrics) {
|
|
269
|
-
const { default: metricsPlugin } = await import("./metrics-
|
|
269
|
+
const { default: metricsPlugin } = await import("./metrics-DuhiSEZI.mjs").then((n) => n.r);
|
|
270
270
|
const opts = config.arcPlugins.metrics === true ? {} : config.arcPlugins.metrics;
|
|
271
271
|
await fastify.register(metricsPlugin, opts);
|
|
272
272
|
trackPlugin("arc-metrics", opts);
|
|
273
273
|
}
|
|
274
274
|
if (config.arcPlugins?.versioning) {
|
|
275
|
-
const { default: versioningPlugin } = await import("./versioning-
|
|
275
|
+
const { default: versioningPlugin } = await import("./versioning-C2U_bLY0.mjs").then((n) => n.r);
|
|
276
276
|
await fastify.register(versioningPlugin, config.arcPlugins.versioning);
|
|
277
277
|
trackPlugin("arc-versioning", config.arcPlugins.versioning);
|
|
278
278
|
}
|
|
@@ -340,7 +340,7 @@ async function registerAuth(fastify, config, trackPlugin) {
|
|
|
340
340
|
*/
|
|
341
341
|
async function registerElevation(fastify, config, trackPlugin) {
|
|
342
342
|
if (!config.elevation) return;
|
|
343
|
-
const { elevationPlugin } = await import("./elevation-
|
|
343
|
+
const { elevationPlugin } = await import("./elevation-C7hgL_aI.mjs").then((n) => n.r);
|
|
344
344
|
await fastify.register(elevationPlugin, config.elevation);
|
|
345
345
|
trackPlugin("arc-elevation", config.elevation);
|
|
346
346
|
fastify.log.debug("Elevation plugin enabled");
|
|
@@ -350,7 +350,7 @@ async function registerElevation(fastify, config, trackPlugin) {
|
|
|
350
350
|
*/
|
|
351
351
|
async function registerErrorHandler(fastify, config, trackPlugin) {
|
|
352
352
|
if (config.errorHandler === false) return;
|
|
353
|
-
const { errorHandlerPlugin } = await import("./errorHandler-
|
|
353
|
+
const { errorHandlerPlugin } = await import("./errorHandler-Bb49BvPD.mjs").then((n) => n.r);
|
|
354
354
|
const errorOpts = typeof config.errorHandler === "object" ? config.errorHandler : { includeStack: config.preset !== "production" };
|
|
355
355
|
await fastify.register(errorHandlerPlugin, errorOpts);
|
|
356
356
|
trackPlugin("arc-error-handler", errorOpts);
|
|
@@ -417,7 +417,7 @@ async function registerResources(fastify, config) {
|
|
|
417
417
|
fastify.log.debug(`${config.bootstrap.length} bootstrap function(s) executed`);
|
|
418
418
|
}
|
|
419
419
|
if (!config.resources?.length && config.resourceDir) {
|
|
420
|
-
const { loadResources } = await import("./loadResources-
|
|
420
|
+
const { loadResources } = await import("./loadResources-BAzJItAJ.mjs").then((n) => n.n);
|
|
421
421
|
const { resolve } = await import("node:path");
|
|
422
422
|
const dir = resolve(config.resourceDir);
|
|
423
423
|
config = {
|
|
@@ -643,14 +643,14 @@ function validateDistributedRuntime(options) {
|
|
|
643
643
|
if (!events || MEMORY_STORE_NAMES.has(events.name)) missing.push("events transport");
|
|
644
644
|
if (options.arcPlugins?.caching) {
|
|
645
645
|
const cache = options.stores?.cache;
|
|
646
|
-
if (!cache || MEMORY_STORE_NAMES.has(cache.name)) missing.push("cache store");
|
|
646
|
+
if (!cache || cache.name !== void 0 && MEMORY_STORE_NAMES.has(cache.name)) missing.push("cache store");
|
|
647
647
|
}
|
|
648
648
|
const idempotency = options.stores?.idempotency;
|
|
649
649
|
if (idempotency && MEMORY_STORE_NAMES.has(idempotency.name)) missing.push("idempotency store (memory-backed in distributed mode)");
|
|
650
650
|
else if (!idempotency) 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.");
|
|
651
651
|
if (options.arcPlugins?.queryCache) {
|
|
652
652
|
const qc = options.stores?.queryCache;
|
|
653
|
-
if (!qc || MEMORY_STORE_NAMES.has(qc.name)) missing.push("queryCache store");
|
|
653
|
+
if (!qc || qc.name !== void 0 && MEMORY_STORE_NAMES.has(qc.name)) missing.push("queryCache store");
|
|
654
654
|
}
|
|
655
655
|
if (missing.length > 0) throw new Error(`[Arc] runtime: 'distributed' requires Redis/durable adapters.\nMissing: ${missing.join(", ")}.\nProvide Redis-backed stores or use runtime: 'memory' for development.`);
|
|
656
656
|
return deferredWarnings;
|
|
@@ -676,7 +676,7 @@ function validateDistributedRuntime(options) {
|
|
|
676
676
|
*/
|
|
677
677
|
async function createApp(options) {
|
|
678
678
|
if (options.debug !== void 0 && options.debug !== false) {
|
|
679
|
-
const { configureArcLogger } = await import("./logger-
|
|
679
|
+
const { configureArcLogger } = await import("./logger-DLg8-Ueg.mjs").then((n) => n.r);
|
|
680
680
|
configureArcLogger({ debug: options.debug });
|
|
681
681
|
}
|
|
682
682
|
validateAuthOptions(options);
|
|
@@ -688,6 +688,7 @@ async function createApp(options) {
|
|
|
688
688
|
const fastify = Fastify({
|
|
689
689
|
logger: config.logger ?? true,
|
|
690
690
|
trustProxy: config.trustProxy ?? false,
|
|
691
|
+
pluginTimeout: config.pluginTimeout ?? 1e4,
|
|
691
692
|
routerOptions: { querystringParser: (str) => qs.parse(str) },
|
|
692
693
|
ajv: { customOptions: {
|
|
693
694
|
coerceTypes: true,
|
|
@@ -729,7 +730,7 @@ async function createApp(options) {
|
|
|
729
730
|
await registerErrorHandler(fastify, config, trackPlugin);
|
|
730
731
|
await registerResources(fastify, config);
|
|
731
732
|
if (config.replyHelpers) {
|
|
732
|
-
const { replyHelpersPlugin } = await import("./replyHelpers-
|
|
733
|
+
const { replyHelpersPlugin } = await import("./replyHelpers-CXtJDAZ0.mjs").then((n) => n.n);
|
|
733
734
|
await fastify.register(replyHelpersPlugin);
|
|
734
735
|
}
|
|
735
736
|
if (config.serializeBigInt) fastify.addHook("preSerialization", async (_request, _reply, payload) => {
|
package/dist/docs/index.d.mts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import { t as ExternalOpenApiPaths } from "../externalPaths-
|
|
1
|
+
import { x as RegistryEntry } from "../index-Cl0uoKd5.mjs";
|
|
2
|
+
import { t as ExternalOpenApiPaths } from "../externalPaths-BQ8QijNH.mjs";
|
|
3
3
|
import { FastifyPluginAsync } from "fastify";
|
|
4
4
|
|
|
5
5
|
//#region src/docs/openapi.d.ts
|
package/dist/docs/index.mjs
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import { t as getUserRoles } from "../types-
|
|
2
|
-
import { n as openApiPlugin, r as openapi_default, t as buildOpenApiSpec } from "../openapi-
|
|
1
|
+
import { t as getUserRoles } from "../types-DV9WDfeg.mjs";
|
|
2
|
+
import { n as openApiPlugin, r as openapi_default, t as buildOpenApiSpec } from "../openapi-B5F8AddX.mjs";
|
|
3
3
|
import fp from "fastify-plugin";
|
|
4
4
|
//#region src/docs/scalar.ts
|
|
5
5
|
const scalarPlugin = async (fastify, opts = {}) => {
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { t as __exportAll } from "./chunk-BpYLSNr0.mjs";
|
|
2
|
-
import { t as getUserRoles } from "./types-
|
|
3
|
-
import { t as arcLog } from "./logger-
|
|
2
|
+
import { t as getUserRoles } from "./types-DV9WDfeg.mjs";
|
|
3
|
+
import { t as arcLog } from "./logger-DLg8-Ueg.mjs";
|
|
4
4
|
import fp from "fastify-plugin";
|
|
5
5
|
//#region src/scope/elevation.ts
|
|
6
6
|
var elevation_exports = /* @__PURE__ */ __exportAll({
|
|
@@ -57,13 +57,27 @@ const elevationPlugin = async (fastify, opts = {}) => {
|
|
|
57
57
|
userId,
|
|
58
58
|
organizationId: orgId
|
|
59
59
|
});
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
60
|
+
const event = {
|
|
61
|
+
userId,
|
|
62
|
+
organizationId: orgId || void 0,
|
|
63
|
+
request,
|
|
64
|
+
timestamp: /* @__PURE__ */ new Date()
|
|
65
|
+
};
|
|
66
|
+
const publish = fastify.events?.publish;
|
|
67
|
+
if (publish) try {
|
|
68
|
+
await publish("arc.scope.elevated", {
|
|
69
|
+
userId: event.userId,
|
|
70
|
+
organizationId: event.organizationId,
|
|
71
|
+
route: request.routeOptions?.url ?? request.url,
|
|
72
|
+
method: request.method,
|
|
73
|
+
requestId: request.id,
|
|
74
|
+
timestamp: event.timestamp.toISOString()
|
|
66
75
|
});
|
|
76
|
+
} catch (err) {
|
|
77
|
+
log.warn("Failed to publish arc.scope.elevated event", { error: err instanceof Error ? err.message : String(err) });
|
|
78
|
+
}
|
|
79
|
+
if (onElevation) try {
|
|
80
|
+
await onElevation(event);
|
|
67
81
|
} catch {
|
|
68
82
|
log.warn("onElevation callback threw — continuing request");
|
|
69
83
|
}
|
|
@@ -1,11 +1,61 @@
|
|
|
1
1
|
import { t as __exportAll } from "./chunk-BpYLSNr0.mjs";
|
|
2
|
-
import { p as isArcError } from "./errors-
|
|
2
|
+
import { p as isArcError } from "./errors-D5c-5BJL.mjs";
|
|
3
3
|
import fp from "fastify-plugin";
|
|
4
4
|
//#region src/plugins/errorHandler.ts
|
|
5
|
-
var errorHandler_exports = /* @__PURE__ */ __exportAll({
|
|
5
|
+
var errorHandler_exports = /* @__PURE__ */ __exportAll({
|
|
6
|
+
defaultIsDuplicateKeyError: () => defaultIsDuplicateKeyError,
|
|
7
|
+
errorHandlerPlugin: () => errorHandlerPlugin
|
|
8
|
+
});
|
|
9
|
+
/**
|
|
10
|
+
* Default duplicate-key detector covering the mainstream drivers arc sees
|
|
11
|
+
* most. Detection is strictly by known driver codes — never by message
|
|
12
|
+
* string matching — because false positives on dup-key silently mask real
|
|
13
|
+
* errors (WriteConflict, NotWritablePrimary, etc.) as 409s. For long-tail
|
|
14
|
+
* drivers (Neo4j, MSSQL, DynamoDB, custom kits), compose rather than
|
|
15
|
+
* replace:
|
|
16
|
+
*
|
|
17
|
+
* ```ts
|
|
18
|
+
* import { defaultIsDuplicateKeyError } from '@classytic/arc/plugins';
|
|
19
|
+
*
|
|
20
|
+
* errorHandler: {
|
|
21
|
+
* isDuplicateKeyError: (err) =>
|
|
22
|
+
* defaultIsDuplicateKeyError(err) || isNeo4jDupKey(err),
|
|
23
|
+
* }
|
|
24
|
+
* ```
|
|
25
|
+
*
|
|
26
|
+
* Drizzle apps get coverage transitively (Drizzle doesn't wrap driver
|
|
27
|
+
* errors — pg/mysql2/better-sqlite3 codes propagate as-is). Neon is
|
|
28
|
+
* Postgres-wire-compatible → `23505` covers `@neondatabase/serverless`.
|
|
29
|
+
*/
|
|
30
|
+
function defaultIsDuplicateKeyError(err) {
|
|
31
|
+
if (!err || typeof err !== "object") return false;
|
|
32
|
+
const e = err;
|
|
33
|
+
if (e.code === 11e3 || e.codeName === "DuplicateKey") return true;
|
|
34
|
+
if (e.code === "P2002") return true;
|
|
35
|
+
if (e.code === "23505") return true;
|
|
36
|
+
if (e.code === "ER_DUP_ENTRY" || e.errno === 1062) return true;
|
|
37
|
+
if (e.code === "SQLITE_CONSTRAINT_UNIQUE" || e.code === "SQLITE_CONSTRAINT_PRIMARYKEY") return true;
|
|
38
|
+
return false;
|
|
39
|
+
}
|
|
40
|
+
/**
|
|
41
|
+
* Extract the duplicate-field names for the `details.duplicateFields`
|
|
42
|
+
* response. Only called when the caller has opted into detail exposure
|
|
43
|
+
* (`includeStack: true`) — shape differs per driver.
|
|
44
|
+
*/
|
|
45
|
+
function extractDuplicateFields(err) {
|
|
46
|
+
if (!err || typeof err !== "object") return null;
|
|
47
|
+
const e = err;
|
|
48
|
+
if (e.keyValue && typeof e.keyValue === "object") return Object.keys(e.keyValue);
|
|
49
|
+
if (e.meta?.target) {
|
|
50
|
+
if (Array.isArray(e.meta.target)) return e.meta.target.map(String);
|
|
51
|
+
if (typeof e.meta.target === "string") return [e.meta.target];
|
|
52
|
+
}
|
|
53
|
+
if (typeof e.constraint === "string") return [e.constraint];
|
|
54
|
+
return null;
|
|
55
|
+
}
|
|
6
56
|
async function errorHandlerPluginFn(fastify, options = {}) {
|
|
7
57
|
const isProduction = process.env.NODE_ENV === "production";
|
|
8
|
-
const { includeStack = !isProduction, onError, errorMap = {}, errorMappers = [] } = options;
|
|
58
|
+
const { includeStack = !isProduction, onError, errorMap = {}, errorMappers = [], isDuplicateKeyError = defaultIsDuplicateKeyError } = options;
|
|
9
59
|
fastify.setErrorHandler(async (error, request, reply) => {
|
|
10
60
|
if (onError) try {
|
|
11
61
|
await onError(error, request);
|
|
@@ -75,12 +125,14 @@ async function errorHandlerPluginFn(fastify, options = {}) {
|
|
|
75
125
|
statusCode = 400;
|
|
76
126
|
response.code = "INVALID_ID";
|
|
77
127
|
response.error = "Invalid identifier format";
|
|
78
|
-
} else if (error
|
|
128
|
+
} else if (isDuplicateKeyError(error)) {
|
|
79
129
|
statusCode = 409;
|
|
80
130
|
response.code = "DUPLICATE_KEY";
|
|
81
131
|
response.error = "Resource already exists";
|
|
82
|
-
|
|
83
|
-
|
|
132
|
+
if (includeStack) {
|
|
133
|
+
const duplicateFields = extractDuplicateFields(error);
|
|
134
|
+
if (duplicateFields && duplicateFields.length > 0) response.details = { duplicateFields };
|
|
135
|
+
}
|
|
84
136
|
}
|
|
85
137
|
if (includeStack && error.stack) response.stack = error.stack;
|
|
86
138
|
if (statusCode >= 500) request.log.error({
|
|
@@ -118,4 +170,4 @@ const errorHandlerPlugin = fp(errorHandlerPluginFn, {
|
|
|
118
170
|
fastify: "5.x"
|
|
119
171
|
});
|
|
120
172
|
//#endregion
|
|
121
|
-
export {
|
|
173
|
+
export { errorHandlerPlugin as n, errorHandler_exports as r, defaultIsDuplicateKeyError as t };
|