@classytic/arc 2.10.3 → 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 +1 -1
- package/dist/{BaseController-CbKKIflT.mjs → BaseController-DVNKvoX4.mjs} +151 -131
- package/dist/actionPermissions-TUVR3uiZ.mjs +22 -0
- package/dist/adapters/index.d.mts +2 -2
- package/dist/audit/index.d.mts +2 -2
- package/dist/audit/index.mjs +15 -17
- package/dist/auth/index.d.mts +4 -4
- package/dist/auth/index.mjs +5 -5
- package/dist/auth/redis-session.d.mts +1 -1
- package/dist/cache/index.d.mts +2 -2
- package/dist/cache/index.mjs +3 -3
- 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/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 +2 -2
- package/dist/{core-CcR01lup.mjs → core-3MWJosCH.mjs} +139 -91
- package/dist/{createApp-BuvPma24.mjs → createApp-BwnEAO2h.mjs} +54 -20
- package/dist/docs/index.d.mts +2 -2
- package/dist/docs/index.mjs +2 -2
- package/dist/{elevation-C7hgL_aI.mjs → elevation-Dci0AYLT.mjs} +2 -2
- package/dist/errorHandler-2ii4RIYr.d.mts +114 -0
- package/dist/{errorHandler-Bb49BvPD.mjs → errorHandler-CSxe7KIM.mjs} +1 -1
- package/dist/{eventPlugin-DCUjuiQT.mjs → eventPlugin-ByU4Cv0e.mjs} +1 -1
- package/dist/{eventPlugin-CxWgpd6K.d.mts → eventPlugin-D1ThQ1Pp.d.mts} +1 -1
- package/dist/events/index.d.mts +4 -4
- package/dist/events/index.mjs +69 -51
- 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/{fields-Lo1VUDpt.d.mts → fields-C8Y0XLAu.d.mts} +1 -1
- package/dist/hooks/index.d.mts +1 -1
- package/dist/hooks/index.mjs +1 -1
- package/dist/idempotency/index.d.mts +3 -3
- package/dist/idempotency/index.mjs +38 -27
- package/dist/idempotency/redis.d.mts +1 -1
- package/dist/{index-Cl0uoKd5.d.mts → index-BGbpGVyM.d.mts} +2362 -2155
- package/dist/{index-DStwgFUK.d.mts → index-BziRPS4H.d.mts} +1 -1
- package/dist/{index-ChIw3776.d.mts → index-C_Noptz-.d.mts} +3 -3
- package/dist/{index-8qw4y6ff.d.mts → index-EqQN6p0W.d.mts} +3 -3
- package/dist/index.d.mts +7 -219
- package/dist/index.mjs +8 -128
- 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/logger/index.d.mts +81 -0
- package/dist/{logger-DLg8-Ueg.mjs → logger/index.mjs} +1 -6
- 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-B5F8AddX.mjs → openapi-DpNpqBmo.mjs} +9 -7
- package/dist/org/index.d.mts +2 -2
- package/dist/permissions/index.d.mts +2 -2
- package/dist/permissions/index.mjs +3 -3
- package/dist/{permissions-Dk6mshja.mjs → permissions-wkqRwicB.mjs} +2 -2
- 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 +25 -5
- package/dist/plugins/index.mjs +9 -9
- package/dist/plugins/tracing-entry.d.mts +1 -1
- package/dist/plugins/tracing-entry.mjs +1 -1
- package/dist/presets/filesUpload.d.mts +4 -4
- 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 +42 -8
- package/dist/presets/search.d.mts +2 -2
- package/dist/presets/search.mjs +1 -1
- package/dist/{presets-fLJVXdVn.mjs → presets-CrwOvuXI.mjs} +1 -1
- package/dist/{queryCachePlugin-DQCEfJis.mjs → queryCachePlugin-ChLNZvFT.mjs} +2 -2
- package/dist/{queryCachePlugin-BKbWjgDG.d.mts → queryCachePlugin-Dumka73q.d.mts} +1 -1
- package/dist/{queryParser-DBqBB6AC.mjs → queryParser-NR__Qiju.mjs} +68 -1
- package/dist/{redis-DqyeggCa.d.mts → redis-MXLp1oOf.d.mts} +1 -1
- package/dist/{redis-stream-CakIQmwR.d.mts → redis-stream-bkO88VHx.d.mts} +1 -1
- package/dist/registry/index.d.mts +1 -1
- package/dist/registry/index.mjs +2 -2
- package/dist/{requestContext-xHIKedG6.mjs → requestContext-C38GskNt.mjs} +1 -1
- package/dist/{resourceToTools-BElv3xPT.mjs → resourceToTools-BhF3JV5p.mjs} +8 -3
- package/dist/scope/index.d.mts +2 -2
- package/dist/scope/index.mjs +2 -2
- package/dist/{sse-yBCgOLGu.mjs → sse-D8UeDwis.mjs} +1 -1
- package/dist/{store-helpers-ZCSMJJAX.mjs → store-helpers-DYYUQbQN.mjs} +4 -0
- package/dist/testing/index.d.mts +2 -2
- package/dist/testing/index.mjs +11 -2
- package/dist/testing/storageContract.d.mts +1 -1
- package/dist/types/index.d.mts +4 -4
- package/dist/types/index.mjs +1 -1
- package/dist/types/storage.d.mts +1 -1
- package/dist/{types-Btdda02s.d.mts → types-CVKBssX5.d.mts} +1 -1
- package/dist/{types-Co8k3NyS.d.mts → types-CVdgPXBW.d.mts} +22 -9
- package/dist/utils/index.d.mts +73 -3
- package/dist/utils/index.mjs +4 -4
- package/dist/{utils-B2fNOD_i.mjs → utils-LMwVidKy.mjs} +20 -2
- package/dist/versioning-CeUXHfjw.d.mts +117 -0
- package/package.json +22 -6
- package/skills/arc/SKILL.md +1 -1
- package/dist/errorHandler-DRQ3EqfL.d.mts +0 -218
- package/dist/filesUpload-t21LS-py.mjs +0 -377
- /package/dist/{EventTransport-CUw5NNWe.d.mts → EventTransport-CfVEGaEl.d.mts} +0 -0
- /package/dist/{HookSystem-BNYKnrXF.mjs → HookSystem-BjFu7zf1.mjs} +0 -0
- /package/dist/{ResourceRegistry-BPd6NQDm.mjs → ResourceRegistry-CcN2LVrc.mjs} +0 -0
- /package/dist/{betterAuthOpenApi-BBRVhjQN.mjs → betterAuthOpenApi--rdY15Ld.mjs} +0 -0
- /package/dist/{caching-CBpK_SCM.mjs → caching-3h93rkJM.mjs} +0 -0
- /package/dist/{createActionRouter-Bp_5c_2b.mjs → createActionRouter-C8UUB3Px.mjs} +0 -0
- /package/dist/{elevation-C5SwtkAn.d.mts → elevation-s5ykdNHr.d.mts} +0 -0
- /package/dist/{errors-CCSsMpXE.d.mts → errors-BI8kEKsO.d.mts} +0 -0
- /package/dist/{errors-D5c-5BJL.mjs → errors-BqdUDja_.mjs} +0 -0
- /package/dist/{externalPaths-BQ8QijNH.d.mts → externalPaths-Bapitwvd.d.mts} +0 -0
- /package/dist/{fields-bxkeltzz.mjs → fields-CTMWOUDt.mjs} +0 -0
- /package/dist/{interface-CSbZdv_3.d.mts → interface-B-pe8fhj.d.mts} +0 -0
- /package/dist/{interface-D218ikEo.d.mts → interface-yhyb_pLY.d.mts} +0 -0
- /package/dist/{keys-qcD-TVJl.mjs → keys-nWQGUTu1.mjs} +0 -0
- /package/dist/{loadResources-BAzJItAJ.mjs → loadResources-Bksk8ydA.mjs} +0 -0
- /package/dist/{memory-B5Amv9A1.mjs → memory-DqI-449b.mjs} +0 -0
- /package/dist/{metrics-DuhiSEZI.mjs → metrics-TuOmguhi.mjs} +0 -0
- /package/dist/{pluralize-A0tWEl1K.mjs → pluralize-CWP6MB39.mjs} +0 -0
- /package/dist/{registry-B3lRFBWo.mjs → registry-B0Wl7uVV.mjs} +0 -0
- /package/dist/{replyHelpers-CXtJDAZ0.mjs → replyHelpers-BLojtuvR.mjs} +0 -0
- /package/dist/{sessionManager-BkzVU8h2.d.mts → sessionManager-D-oNWHz3.d.mts} +0 -0
- /package/dist/{storage-CVk_SEn2.d.mts → storage-BwGQXUpd.d.mts} +0 -0
- /package/dist/{tracing-65B51Dw3.d.mts → tracing-xqXzWeaf.d.mts} +0 -0
- /package/dist/{types-Csi3FLfq.mjs → types-CDnTEpga.mjs} +0 -0
- /package/dist/{types-DV9WDfeg.mjs → types-D57iXYb8.mjs} +0 -0
- /package/dist/{types-BD85MlEK.d.mts → types-tgR4Pt8F.d.mts} +0 -0
- /package/dist/{versioning-C2U_bLY0.mjs → versioning-B6mimogM.mjs} +0 -0
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
import { AsyncLocalStorage } from "node:async_hooks";
|
|
2
|
+
|
|
3
|
+
//#region src/context/requestContext.d.ts
|
|
4
|
+
/**
|
|
5
|
+
* Shape of the request-scoped context store.
|
|
6
|
+
* Populated by Arc's onRequest hook in arcCorePlugin.
|
|
7
|
+
*/
|
|
8
|
+
interface RequestStore {
|
|
9
|
+
/** Unique request identifier */
|
|
10
|
+
requestId?: string;
|
|
11
|
+
/** Authenticated user (if any) */
|
|
12
|
+
user?: {
|
|
13
|
+
id?: string;
|
|
14
|
+
_id?: string;
|
|
15
|
+
roles?: string[];
|
|
16
|
+
[key: string]: unknown;
|
|
17
|
+
} | null;
|
|
18
|
+
/** Active organization ID (multi-tenant) */
|
|
19
|
+
organizationId?: string;
|
|
20
|
+
/** Active team ID (team-scoped resources) */
|
|
21
|
+
teamId?: string;
|
|
22
|
+
/** Current resource name (set by arcDecorator in CRUD routes) */
|
|
23
|
+
resourceName?: string;
|
|
24
|
+
/** Request start time (for timing) */
|
|
25
|
+
startTime: number;
|
|
26
|
+
/** Additional context — extensible by app */
|
|
27
|
+
[key: string]: unknown;
|
|
28
|
+
}
|
|
29
|
+
/**
|
|
30
|
+
* Request context API.
|
|
31
|
+
*
|
|
32
|
+
* - `get()` — returns current store or undefined if outside request scope
|
|
33
|
+
* - `run(store, fn)` — run a function with a specific store (used by Arc internals)
|
|
34
|
+
* - `getStore()` — alias for get() (matches Node.js API naming)
|
|
35
|
+
*/
|
|
36
|
+
declare const requestContext: {
|
|
37
|
+
/**
|
|
38
|
+
* Get the current request context.
|
|
39
|
+
* Returns undefined if called outside a request lifecycle.
|
|
40
|
+
*/
|
|
41
|
+
get(): RequestStore | undefined;
|
|
42
|
+
/**
|
|
43
|
+
* Alias for get() — matches Node.js AsyncLocalStorage API naming.
|
|
44
|
+
*/
|
|
45
|
+
getStore(): RequestStore | undefined;
|
|
46
|
+
/**
|
|
47
|
+
* Run a function within a specific request context.
|
|
48
|
+
* Used internally by Arc's onRequest hook.
|
|
49
|
+
*/
|
|
50
|
+
run<T>(store: RequestStore, fn: () => T): T;
|
|
51
|
+
/**
|
|
52
|
+
* The underlying AsyncLocalStorage instance.
|
|
53
|
+
* Exposed for advanced use cases (testing, custom integrations).
|
|
54
|
+
*/
|
|
55
|
+
storage: AsyncLocalStorage<RequestStore>;
|
|
56
|
+
};
|
|
57
|
+
//#endregion
|
|
58
|
+
export { type RequestStore, requestContext };
|
package/dist/core/index.d.mts
CHANGED
|
@@ -1,3 +1,3 @@
|
|
|
1
|
-
import {
|
|
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-
|
|
1
|
+
import { B as ResourceDefinition, V as defineResource, an as BaseControllerOptions, cn as BodySanitizer, dn as AccessControlConfig, in as BaseController, ln as BodySanitizerConfig, on as QueryResolver, sn as QueryResolverConfig, un as AccessControl } from "../index-BGbpGVyM.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-EqQN6p0W.mjs";
|
|
3
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,4 +1,4 @@
|
|
|
1
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-
|
|
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-
|
|
2
|
+
import { i as AccessControl, n as QueryResolver, r as BodySanitizer, t as BaseController } from "../BaseController-DVNKvoX4.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-3MWJosCH.mjs";
|
|
4
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,73 +1,29 @@
|
|
|
1
1
|
import { s as DEFAULT_UPDATE_METHOD, t as CRUD_OPERATIONS } from "./constants-BhY1OHoH.mjs";
|
|
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 { t as requestContext } from "./requestContext-xHIKedG6.mjs";
|
|
2
|
+
import { _ as isElevated, n as PUBLIC_SCOPE, o as getOrgId, p as getUserId, v as isMember } from "./types-AOD8fxIw.mjs";
|
|
3
|
+
import { t as BaseController } from "./BaseController-DVNKvoX4.mjs";
|
|
4
|
+
import { i as resolveEffectiveRoles, t as applyFieldReadPermissions } from "./fields-CTMWOUDt.mjs";
|
|
5
|
+
import { t as getUserRoles } from "./types-D57iXYb8.mjs";
|
|
6
|
+
import { t as requestContext } from "./requestContext-C38GskNt.mjs";
|
|
8
7
|
import { n as normalizePermissionResult, t as applyPermissionResult } from "./applyPermissionResult-QhV1Pa-g.mjs";
|
|
9
|
-
import { i as getDefaultCrudSchemas } from "./utils-
|
|
8
|
+
import { i as getDefaultCrudSchemas } from "./utils-LMwVidKy.mjs";
|
|
10
9
|
import { n as convertRouteSchema, t as convertOpenApiSchemas } from "./schemaConverter-BxFDdtXu.mjs";
|
|
11
10
|
import { t as hasEvents } from "./typeGuards-Cj5Rgvlg.mjs";
|
|
12
|
-
import {
|
|
13
|
-
|
|
11
|
+
import { t as executePipeline } from "./pipe-CGJxqDGx.mjs";
|
|
12
|
+
import { r as getAvailablePresets, t as applyPresets } from "./presets-CrwOvuXI.mjs";
|
|
13
|
+
import { t as resolveActionPermission } from "./actionPermissions-TUVR3uiZ.mjs";
|
|
14
|
+
//#region src/scope/projection.ts
|
|
14
15
|
/**
|
|
15
|
-
*
|
|
16
|
-
*
|
|
16
|
+
* Compute the request-scope projection. Returns `undefined` when no
|
|
17
|
+
* scope is attached (public / unscoped routes) so hosts can idiomatically
|
|
18
|
+
* write `ctx.scope?.organizationId` without a double-null check.
|
|
17
19
|
*/
|
|
18
|
-
function
|
|
19
|
-
return
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
if (!step.operations || step.operations.length === 0) return true;
|
|
26
|
-
return step.operations.includes(operation);
|
|
27
|
-
}
|
|
28
|
-
/**
|
|
29
|
-
* Execute a pipeline against a request context.
|
|
30
|
-
*
|
|
31
|
-
* This is the core runtime that createCrudRouter uses to execute pipelines.
|
|
32
|
-
* External usage is not needed — this is wired automatically when `pipe` is set.
|
|
33
|
-
*
|
|
34
|
-
* @param steps - Pipeline steps to execute
|
|
35
|
-
* @param ctx - The pipeline context (extends IRequestContext)
|
|
36
|
-
* @param handler - The actual controller method to call
|
|
37
|
-
* @param operation - The CRUD operation name
|
|
38
|
-
* @returns The controller response (possibly modified by interceptors)
|
|
39
|
-
*/
|
|
40
|
-
async function executePipeline(steps, ctx, handler, operation) {
|
|
41
|
-
const guards = [];
|
|
42
|
-
const transforms = [];
|
|
43
|
-
const interceptors = [];
|
|
44
|
-
for (const step of steps) {
|
|
45
|
-
if (!appliesTo(step, operation)) continue;
|
|
46
|
-
switch (step._type) {
|
|
47
|
-
case "guard":
|
|
48
|
-
guards.push(step);
|
|
49
|
-
break;
|
|
50
|
-
case "transform":
|
|
51
|
-
transforms.push(step);
|
|
52
|
-
break;
|
|
53
|
-
case "interceptor":
|
|
54
|
-
interceptors.push(step);
|
|
55
|
-
break;
|
|
56
|
-
}
|
|
57
|
-
}
|
|
58
|
-
for (const g of guards) if (!await g.handler(ctx)) throw new ForbiddenError(`Guard '${g.name}' denied access`);
|
|
59
|
-
let currentCtx = ctx;
|
|
60
|
-
for (const t of transforms) {
|
|
61
|
-
const result = await t.handler(currentCtx);
|
|
62
|
-
if (result) currentCtx = result;
|
|
63
|
-
}
|
|
64
|
-
let chain = () => handler(currentCtx);
|
|
65
|
-
for (let i = interceptors.length - 1; i >= 0; i--) {
|
|
66
|
-
const interceptor = interceptors[i];
|
|
67
|
-
const next = chain;
|
|
68
|
-
chain = () => interceptor.handler(currentCtx, next);
|
|
69
|
-
}
|
|
70
|
-
return chain();
|
|
20
|
+
function buildRequestScopeProjection(scope) {
|
|
21
|
+
if (!scope) return void 0;
|
|
22
|
+
return {
|
|
23
|
+
organizationId: getOrgId(scope),
|
|
24
|
+
userId: getUserId(scope),
|
|
25
|
+
orgRoles: isMember(scope) ? scope.orgRoles : void 0
|
|
26
|
+
};
|
|
71
27
|
}
|
|
72
28
|
//#endregion
|
|
73
29
|
//#region src/core/fastifyAdapter.ts
|
|
@@ -119,6 +75,8 @@ function createRequestContext(req) {
|
|
|
119
75
|
queryCache: srv && "queryCache" in srv ? srv.queryCache : void 0,
|
|
120
76
|
log: req.log
|
|
121
77
|
};
|
|
78
|
+
const rawScope = reqWithExtras.scope;
|
|
79
|
+
const scopeProjection = buildRequestScopeProjection(rawScope);
|
|
122
80
|
return {
|
|
123
81
|
query: reqWithExtras.query ?? {},
|
|
124
82
|
body: reqWithExtras.body ?? {},
|
|
@@ -135,10 +93,11 @@ function createRequestContext(req) {
|
|
|
135
93
|
};
|
|
136
94
|
})() : null,
|
|
137
95
|
context: requestContext,
|
|
96
|
+
scope: scopeProjection,
|
|
138
97
|
metadata: {
|
|
139
98
|
...reqWithExtras.context,
|
|
140
99
|
arc: reqWithExtras.arc,
|
|
141
|
-
_scope:
|
|
100
|
+
_scope: rawScope,
|
|
142
101
|
_ownershipCheck: reqWithExtras._ownershipCheck,
|
|
143
102
|
_policyFilters: reqWithExtras._policyFilters ?? {},
|
|
144
103
|
log: reqWithExtras.log
|
|
@@ -667,6 +626,52 @@ function createPermissionMiddleware(permission, resourceName, action) {
|
|
|
667
626
|
return buildPermissionMiddleware(permission, resourceName, action);
|
|
668
627
|
}
|
|
669
628
|
//#endregion
|
|
629
|
+
//#region src/core/schemaOptions.ts
|
|
630
|
+
/**
|
|
631
|
+
* Inject the tenant-scoping field rule into `schemaOptions.fieldRules`:
|
|
632
|
+
*
|
|
633
|
+
* { [tenantField]: { systemManaged: true, preserveForElevated: true } }
|
|
634
|
+
*
|
|
635
|
+
* Why both flags: `systemManaged` tells `BodySanitizer` to strip the
|
|
636
|
+
* field from inbound bodies (so member clients can't forge a target
|
|
637
|
+
* tenant). `preserveForElevated` exempts elevated-admin scopes from the
|
|
638
|
+
* strip, so platform admins without a pinned org can still pick a target
|
|
639
|
+
* org via the request body (the only channel they have —
|
|
640
|
+
* `BaseController.create` can't re-stamp from scope when scope has no
|
|
641
|
+
* orgId).
|
|
642
|
+
*
|
|
643
|
+
* **Returns a new `RouteSchemaOptions`** — the input is never mutated.
|
|
644
|
+
* Callers should assign the return value to whatever config slot they
|
|
645
|
+
* read from downstream (always the `resolvedConfig`, never raw `config`).
|
|
646
|
+
*
|
|
647
|
+
* **No-op when:**
|
|
648
|
+
* - `tenantField` is `false` (platform-universal resource)
|
|
649
|
+
* - `tenantField` is undefined
|
|
650
|
+
* - The caller already declared `fieldRules[tenantField].systemManaged`
|
|
651
|
+
* (even as `false`) — explicit opt-outs are respected
|
|
652
|
+
*
|
|
653
|
+
* `preserveForElevated` defaults to `true` but is preserved verbatim
|
|
654
|
+
* when the caller set it explicitly.
|
|
655
|
+
*/
|
|
656
|
+
function autoInjectTenantFieldRules(schemaOptions, tenantField) {
|
|
657
|
+
if (tenantField === false || tenantField === void 0) return schemaOptions;
|
|
658
|
+
const fieldName = tenantField || "organizationId";
|
|
659
|
+
const existing = schemaOptions?.fieldRules ?? {};
|
|
660
|
+
const existingRule = existing[fieldName];
|
|
661
|
+
if (existingRule && existingRule.systemManaged !== void 0) return schemaOptions;
|
|
662
|
+
return {
|
|
663
|
+
...schemaOptions ?? {},
|
|
664
|
+
fieldRules: {
|
|
665
|
+
...existing,
|
|
666
|
+
[fieldName]: {
|
|
667
|
+
...existingRule ?? {},
|
|
668
|
+
systemManaged: true,
|
|
669
|
+
preserveForElevated: existingRule?.preserveForElevated ?? true
|
|
670
|
+
}
|
|
671
|
+
}
|
|
672
|
+
};
|
|
673
|
+
}
|
|
674
|
+
//#endregion
|
|
670
675
|
//#region src/core/validateResourceConfig.ts
|
|
671
676
|
/**
|
|
672
677
|
* Resource Configuration Validator
|
|
@@ -926,6 +931,7 @@ function defineResource(config) {
|
|
|
926
931
|
const originalPresets = (config.presets ?? []).map((p) => typeof p === "string" ? p : p.name);
|
|
927
932
|
const resolvedConfig = config.presets?.length ? applyPresets(config, config.presets) : config;
|
|
928
933
|
resolvedConfig._appliedPresets = originalPresets;
|
|
934
|
+
resolvedConfig.schemaOptions = autoInjectTenantFieldRules(resolvedConfig.schemaOptions, resolvedConfig.tenantField);
|
|
929
935
|
let controller = resolvedConfig.controller;
|
|
930
936
|
if (!controller && hasCrudRoutes && repository) {
|
|
931
937
|
const qp = resolvedConfig.queryParser;
|
|
@@ -941,6 +947,7 @@ function defineResource(config) {
|
|
|
941
947
|
maxLimit: maxLimitFromParser,
|
|
942
948
|
tenantField: resolvedConfig.tenantField,
|
|
943
949
|
idField: resolvedConfig.idField,
|
|
950
|
+
...resolvedConfig.defaultSort !== void 0 ? { defaultSort: resolvedConfig.defaultSort } : {},
|
|
944
951
|
matchesFilter: config.adapter?.matchesFilter,
|
|
945
952
|
cache: resolvedConfig.cache,
|
|
946
953
|
onFieldWriteDenied: resolvedConfig.onFieldWriteDenied,
|
|
@@ -965,11 +972,17 @@ function defineResource(config) {
|
|
|
965
972
|
if (config.hooks) {
|
|
966
973
|
const h = config.hooks;
|
|
967
974
|
const inlineHooks = [];
|
|
968
|
-
const toCtx = (ctx) =>
|
|
969
|
-
|
|
970
|
-
|
|
971
|
-
|
|
972
|
-
|
|
975
|
+
const toCtx = (ctx) => {
|
|
976
|
+
const context = ctx.context;
|
|
977
|
+
const rawScope = context?._scope;
|
|
978
|
+
return {
|
|
979
|
+
data: ctx.data ?? ctx.result ?? {},
|
|
980
|
+
user: ctx.user,
|
|
981
|
+
context,
|
|
982
|
+
scope: buildRequestScopeProjection(rawScope),
|
|
983
|
+
meta: ctx.meta
|
|
984
|
+
};
|
|
985
|
+
};
|
|
973
986
|
if (h.beforeCreate) {
|
|
974
987
|
const fn = h.beforeCreate;
|
|
975
988
|
inlineHooks.push({
|
|
@@ -1028,15 +1041,15 @@ function defineResource(config) {
|
|
|
1028
1041
|
}
|
|
1029
1042
|
if (!config.skipRegistry) try {
|
|
1030
1043
|
let openApiSchemas;
|
|
1031
|
-
if (
|
|
1044
|
+
if (resolvedConfig.adapter?.generateSchemas) {
|
|
1032
1045
|
const adapterContext = {
|
|
1033
|
-
idField:
|
|
1034
|
-
resourceName:
|
|
1046
|
+
idField: resolvedConfig.idField,
|
|
1047
|
+
resourceName: resolvedConfig.name
|
|
1035
1048
|
};
|
|
1036
|
-
const generated =
|
|
1049
|
+
const generated = resolvedConfig.adapter.generateSchemas(resolvedConfig.schemaOptions, adapterContext);
|
|
1037
1050
|
if (generated) openApiSchemas = generated;
|
|
1038
1051
|
}
|
|
1039
|
-
if (
|
|
1052
|
+
if (resolvedConfig.idField && resolvedConfig.idField !== "_id" && openApiSchemas?.params && typeof openApiSchemas.params === "object") {
|
|
1040
1053
|
const params = openApiSchemas.params;
|
|
1041
1054
|
const properties = params.properties;
|
|
1042
1055
|
const idProp = properties?.id;
|
|
@@ -1047,7 +1060,7 @@ function defineResource(config) {
|
|
|
1047
1060
|
delete cleanedId.pattern;
|
|
1048
1061
|
delete cleanedId.minLength;
|
|
1049
1062
|
delete cleanedId.maxLength;
|
|
1050
|
-
if (!cleanedId.description) cleanedId.description = `${
|
|
1063
|
+
if (!cleanedId.description) cleanedId.description = `${resolvedConfig.idField} (custom ID field)`;
|
|
1051
1064
|
openApiSchemas = {
|
|
1052
1065
|
...openApiSchemas,
|
|
1053
1066
|
params: {
|
|
@@ -1061,7 +1074,7 @@ function defineResource(config) {
|
|
|
1061
1074
|
}
|
|
1062
1075
|
}
|
|
1063
1076
|
}
|
|
1064
|
-
const queryParser =
|
|
1077
|
+
const queryParser = resolvedConfig.queryParser;
|
|
1065
1078
|
if (queryParser?.getQuerySchema) {
|
|
1066
1079
|
const querySchema = queryParser.getQuerySchema();
|
|
1067
1080
|
if (querySchema) openApiSchemas = {
|
|
@@ -1069,13 +1082,13 @@ function defineResource(config) {
|
|
|
1069
1082
|
listQuery: querySchema
|
|
1070
1083
|
};
|
|
1071
1084
|
}
|
|
1072
|
-
if (
|
|
1085
|
+
if (resolvedConfig.openApiSchemas) openApiSchemas = {
|
|
1073
1086
|
...openApiSchemas,
|
|
1074
|
-
...
|
|
1087
|
+
...resolvedConfig.openApiSchemas
|
|
1075
1088
|
};
|
|
1076
1089
|
if (openApiSchemas) openApiSchemas = convertOpenApiSchemas(openApiSchemas);
|
|
1077
1090
|
resource._registryMeta = {
|
|
1078
|
-
module:
|
|
1091
|
+
module: resolvedConfig.module,
|
|
1079
1092
|
openApiSchemas
|
|
1080
1093
|
};
|
|
1081
1094
|
} catch {}
|
|
@@ -1287,8 +1300,8 @@ var ResourceDefinition = class {
|
|
|
1287
1300
|
fields: self.fields
|
|
1288
1301
|
});
|
|
1289
1302
|
if (self.actions && Object.keys(self.actions).length > 0) {
|
|
1290
|
-
const { createActionRouter } = await import("./createActionRouter-
|
|
1291
|
-
createActionRouter(instance, normalizeActionsToRouterConfig(self.actions, self.actionPermissions, self.tag));
|
|
1303
|
+
const { createActionRouter } = await import("./createActionRouter-C8UUB3Px.mjs").then((n) => n.n);
|
|
1304
|
+
createActionRouter(instance, normalizeActionsToRouterConfig(self.actions, self.actionPermissions, self.tag, self.permissions, self.name, typedInstance.log));
|
|
1292
1305
|
}
|
|
1293
1306
|
if (self.events && Object.keys(self.events).length > 0) typedInstance.log?.debug?.(`Resource '${self.name}' defined ${Object.keys(self.events).length} events`);
|
|
1294
1307
|
}, { prefix: self.prefix });
|
|
@@ -1354,18 +1367,53 @@ function capitalize(str) {
|
|
|
1354
1367
|
return str.charAt(0).toUpperCase() + str.slice(1);
|
|
1355
1368
|
}
|
|
1356
1369
|
/**
|
|
1357
|
-
* Normalize ActionsMap into the ActionRouterConfig shape that
|
|
1370
|
+
* Normalize `ActionsMap` into the `ActionRouterConfig` shape that
|
|
1371
|
+
* `createActionRouter` expects.
|
|
1372
|
+
*
|
|
1373
|
+
* **Permission fallback chain (fail-closed, v2.10.5):**
|
|
1374
|
+
* Actions mutate state, so "no permission declared" historically meant
|
|
1375
|
+
* "authenticated users can call it" — a silent authz hole for apps using
|
|
1376
|
+
* the function shorthand `actions: { send: async (id, data, req) => ... }`.
|
|
1377
|
+
*
|
|
1378
|
+
* The chain is now:
|
|
1379
|
+
* 1. `ActionDefinition.permissions` — explicit per-action check.
|
|
1380
|
+
* 2. Resource-level `actionPermissions` — explicit global-for-actions.
|
|
1381
|
+
* 3. Resource-level `permissions.update` — sensible default (actions mutate).
|
|
1382
|
+
* 4. Boot-time error — forces the author to pick an explicit gate.
|
|
1383
|
+
*
|
|
1384
|
+
* When step 3 fires, we log a warning (not a throw) so upgrading apps
|
|
1385
|
+
* aren't bricked by the behavior change, but the gap is visible. Apps
|
|
1386
|
+
* that genuinely want public actions must declare `allowPublic()`
|
|
1387
|
+
* explicitly — auth-by-accident is no longer a supported state.
|
|
1358
1388
|
*/
|
|
1359
|
-
function normalizeActionsToRouterConfig(actions, globalAuth, tag) {
|
|
1389
|
+
function normalizeActionsToRouterConfig(actions, globalAuth, tag, resourcePermissions, resourceName, log) {
|
|
1360
1390
|
const handlers = {};
|
|
1361
1391
|
const permissions = {};
|
|
1362
1392
|
const schemas = {};
|
|
1363
|
-
for (const [name, entry] of Object.entries(actions))
|
|
1364
|
-
|
|
1365
|
-
|
|
1366
|
-
|
|
1367
|
-
|
|
1368
|
-
|
|
1393
|
+
for (const [name, entry] of Object.entries(actions)) {
|
|
1394
|
+
const explicit = typeof entry !== "function" && entry.permissions ? entry.permissions : void 0;
|
|
1395
|
+
if (typeof entry === "function") handlers[name] = entry;
|
|
1396
|
+
else {
|
|
1397
|
+
const def = entry;
|
|
1398
|
+
handlers[name] = def.handler;
|
|
1399
|
+
if (def.permissions) permissions[name] = def.permissions;
|
|
1400
|
+
if (def.schema) schemas[name] = def.schema;
|
|
1401
|
+
}
|
|
1402
|
+
const effective = resolveActionPermission({
|
|
1403
|
+
action: entry,
|
|
1404
|
+
resourcePermissions,
|
|
1405
|
+
resourceActionPermissions: void 0,
|
|
1406
|
+
globalAuth
|
|
1407
|
+
});
|
|
1408
|
+
if (!explicit && !globalAuth && effective && effective === resourcePermissions?.update) {
|
|
1409
|
+
permissions[name] = effective;
|
|
1410
|
+
log?.warn?.({
|
|
1411
|
+
resource: resourceName,
|
|
1412
|
+
action: name,
|
|
1413
|
+
fallback: "permissions.update"
|
|
1414
|
+
}, `[Arc] Action '${resourceName}.${name}' has no explicit permission — falling back to the resource's \`permissions.update\` gate. Declare \`actions.${name}.permissions\` (or resource \`actionPermissions\`) to silence this.`);
|
|
1415
|
+
}
|
|
1416
|
+
if (!effective) throw new Error(`[Arc] Resource '${resourceName}': action '${name}' has no permission gate and the resource defines no \`permissions.update\` fallback. Declare one of:\n - \`actions.${name}.permissions: <PermissionCheck>\` (per-action)\n - \`actionPermissions: <PermissionCheck>\` (resource-wide)\n - \`permissions.update: <PermissionCheck>\` (inherited by actions)\nUse \`allowPublic()\` if you genuinely want the action unauthenticated.`);
|
|
1369
1417
|
}
|
|
1370
1418
|
return {
|
|
1371
1419
|
tag,
|
|
@@ -1408,4 +1456,4 @@ function defineResourceVariants(base, variants) {
|
|
|
1408
1456
|
return out;
|
|
1409
1457
|
}
|
|
1410
1458
|
//#endregion
|
|
1411
|
-
export { formatValidationErrors as a, createPermissionMiddleware as c, createRequestContext as d, getControllerContext as f,
|
|
1459
|
+
export { formatValidationErrors as a, createPermissionMiddleware as c, createRequestContext as d, getControllerContext as f, 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 };
|
|
@@ -116,10 +116,7 @@ const developmentPreset = {
|
|
|
116
116
|
"x-request-id"
|
|
117
117
|
]
|
|
118
118
|
},
|
|
119
|
-
rateLimit:
|
|
120
|
-
max: 1e3,
|
|
121
|
-
timeWindow: "1 minute"
|
|
122
|
-
},
|
|
119
|
+
rateLimit: false,
|
|
123
120
|
underPressure: {
|
|
124
121
|
exposeStatusRoute: true,
|
|
125
122
|
maxEventLoopDelay: 5e3
|
|
@@ -207,7 +204,7 @@ async function registerArcCore(fastify, config, trackPlugin) {
|
|
|
207
204
|
await fastify.register(arcCorePlugin, { emitEvents: config.arcPlugins?.emitEvents !== false });
|
|
208
205
|
trackPlugin("arc-core");
|
|
209
206
|
if (config.arcPlugins?.events !== false) {
|
|
210
|
-
const { default: eventPlugin } = await import("./eventPlugin-
|
|
207
|
+
const { default: eventPlugin } = await import("./eventPlugin-ByU4Cv0e.mjs").then((n) => n.n);
|
|
211
208
|
const eventOpts = typeof config.arcPlugins?.events === "object" ? config.arcPlugins.events : {};
|
|
212
209
|
await fastify.register(eventPlugin, {
|
|
213
210
|
...eventOpts,
|
|
@@ -243,15 +240,15 @@ async function registerArcPlugins(fastify, config, trackPlugin, modules) {
|
|
|
243
240
|
trackPlugin("arc-graceful-shutdown");
|
|
244
241
|
}
|
|
245
242
|
if (config.arcPlugins?.caching) {
|
|
246
|
-
const { default: cachingPlugin } = await import("./caching-
|
|
243
|
+
const { default: cachingPlugin } = await import("./caching-3h93rkJM.mjs").then((n) => n.r);
|
|
247
244
|
const opts = config.arcPlugins.caching === true ? {} : config.arcPlugins.caching;
|
|
248
245
|
await fastify.register(cachingPlugin, opts);
|
|
249
246
|
trackPlugin("arc-caching", opts);
|
|
250
247
|
}
|
|
251
248
|
if (config.arcPlugins?.queryCache) {
|
|
252
|
-
const { queryCachePlugin } = await import("./queryCachePlugin-
|
|
249
|
+
const { queryCachePlugin } = await import("./queryCachePlugin-ChLNZvFT.mjs").then((n) => n.n);
|
|
253
250
|
const opts = config.arcPlugins.queryCache === true ? {} : config.arcPlugins.queryCache;
|
|
254
|
-
const store = config.stores?.queryCache ?? new (await (import("./memory-
|
|
251
|
+
const store = config.stores?.queryCache ?? new (await (import("./memory-DqI-449b.mjs").then((n) => n.n))).MemoryCacheStore();
|
|
255
252
|
await fastify.register(queryCachePlugin, {
|
|
256
253
|
store,
|
|
257
254
|
...opts
|
|
@@ -260,19 +257,19 @@ async function registerArcPlugins(fastify, config, trackPlugin, modules) {
|
|
|
260
257
|
}
|
|
261
258
|
if (config.arcPlugins?.sse) if (config.arcPlugins?.events === false) fastify.log.warn("SSE plugin requires events plugin (arcPlugins.events). SSE disabled.");
|
|
262
259
|
else {
|
|
263
|
-
const { default: ssePlugin } = await import("./sse-
|
|
260
|
+
const { default: ssePlugin } = await import("./sse-D8UeDwis.mjs").then((n) => n.r);
|
|
264
261
|
const opts = config.arcPlugins.sse === true ? {} : config.arcPlugins.sse;
|
|
265
262
|
await fastify.register(ssePlugin, opts);
|
|
266
263
|
trackPlugin("arc-sse", opts);
|
|
267
264
|
}
|
|
268
265
|
if (config.arcPlugins?.metrics) {
|
|
269
|
-
const { default: metricsPlugin } = await import("./metrics-
|
|
266
|
+
const { default: metricsPlugin } = await import("./metrics-TuOmguhi.mjs").then((n) => n.r);
|
|
270
267
|
const opts = config.arcPlugins.metrics === true ? {} : config.arcPlugins.metrics;
|
|
271
268
|
await fastify.register(metricsPlugin, opts);
|
|
272
269
|
trackPlugin("arc-metrics", opts);
|
|
273
270
|
}
|
|
274
271
|
if (config.arcPlugins?.versioning) {
|
|
275
|
-
const { default: versioningPlugin } = await import("./versioning-
|
|
272
|
+
const { default: versioningPlugin } = await import("./versioning-B6mimogM.mjs").then((n) => n.r);
|
|
276
273
|
await fastify.register(versioningPlugin, config.arcPlugins.versioning);
|
|
277
274
|
trackPlugin("arc-versioning", config.arcPlugins.versioning);
|
|
278
275
|
}
|
|
@@ -303,7 +300,8 @@ async function registerAuth(fastify, config, trackPlugin) {
|
|
|
303
300
|
const { plugin, openapi } = authConfig.betterAuth;
|
|
304
301
|
await fastify.register(plugin);
|
|
305
302
|
trackPlugin("auth-better-auth");
|
|
306
|
-
|
|
303
|
+
const arc = fastify.arc;
|
|
304
|
+
if (arc && openapi && !arc.externalOpenApiPaths.includes(openapi)) arc.externalOpenApiPaths.push(openapi);
|
|
307
305
|
fastify.log.debug("Better Auth authentication enabled");
|
|
308
306
|
break;
|
|
309
307
|
}
|
|
@@ -340,7 +338,7 @@ async function registerAuth(fastify, config, trackPlugin) {
|
|
|
340
338
|
*/
|
|
341
339
|
async function registerElevation(fastify, config, trackPlugin) {
|
|
342
340
|
if (!config.elevation) return;
|
|
343
|
-
const { elevationPlugin } = await import("./elevation-
|
|
341
|
+
const { elevationPlugin } = await import("./elevation-Dci0AYLT.mjs").then((n) => n.r);
|
|
344
342
|
await fastify.register(elevationPlugin, config.elevation);
|
|
345
343
|
trackPlugin("arc-elevation", config.elevation);
|
|
346
344
|
fastify.log.debug("Elevation plugin enabled");
|
|
@@ -350,7 +348,7 @@ async function registerElevation(fastify, config, trackPlugin) {
|
|
|
350
348
|
*/
|
|
351
349
|
async function registerErrorHandler(fastify, config, trackPlugin) {
|
|
352
350
|
if (config.errorHandler === false) return;
|
|
353
|
-
const { errorHandlerPlugin } = await import("./errorHandler-
|
|
351
|
+
const { errorHandlerPlugin } = await import("./errorHandler-CSxe7KIM.mjs").then((n) => n.r);
|
|
354
352
|
const errorOpts = typeof config.errorHandler === "object" ? config.errorHandler : { includeStack: config.preset !== "production" };
|
|
355
353
|
await fastify.register(errorHandlerPlugin, errorOpts);
|
|
356
354
|
trackPlugin("arc-error-handler", errorOpts);
|
|
@@ -417,7 +415,7 @@ async function registerResources(fastify, config) {
|
|
|
417
415
|
fastify.log.debug(`${config.bootstrap.length} bootstrap function(s) executed`);
|
|
418
416
|
}
|
|
419
417
|
if (!config.resources?.length && config.resourceDir) {
|
|
420
|
-
const { loadResources } = await import("./loadResources-
|
|
418
|
+
const { loadResources } = await import("./loadResources-Bksk8ydA.mjs").then((n) => n.n);
|
|
421
419
|
const { resolve } = await import("node:path");
|
|
422
420
|
const dir = resolve(config.resourceDir);
|
|
423
421
|
config = {
|
|
@@ -463,6 +461,40 @@ async function registerResources(fastify, config) {
|
|
|
463
461
|
}
|
|
464
462
|
//#endregion
|
|
465
463
|
//#region src/factory/registerSecurity.ts
|
|
464
|
+
/**
|
|
465
|
+
* Translate `skipPaths` sugar into a `@fastify/rate-limit` `allowList`
|
|
466
|
+
* function. A user-supplied `allowList` (array of IPs or function) is
|
|
467
|
+
* preserved and OR-ed with the path match.
|
|
468
|
+
*/
|
|
469
|
+
function buildRateLimitOpts(input) {
|
|
470
|
+
const { skipPaths, allowList, ...rest } = input;
|
|
471
|
+
if (!skipPaths || skipPaths.length === 0) return allowList === void 0 ? rest : {
|
|
472
|
+
...rest,
|
|
473
|
+
allowList
|
|
474
|
+
};
|
|
475
|
+
const matchesPath = compilePathMatcher(skipPaths);
|
|
476
|
+
const combined = async (req, key) => {
|
|
477
|
+
if (matchesPath((req.url ?? "").split("?", 1)[0] ?? "")) return true;
|
|
478
|
+
if (typeof allowList === "function") return await allowList(req, key);
|
|
479
|
+
if (Array.isArray(allowList)) return allowList.includes(key);
|
|
480
|
+
return false;
|
|
481
|
+
};
|
|
482
|
+
return {
|
|
483
|
+
...rest,
|
|
484
|
+
allowList: combined
|
|
485
|
+
};
|
|
486
|
+
}
|
|
487
|
+
function compilePathMatcher(patterns) {
|
|
488
|
+
const prefixes = [];
|
|
489
|
+
const exact = /* @__PURE__ */ new Set();
|
|
490
|
+
for (const p of patterns) if (p.endsWith("*")) prefixes.push(p.slice(0, -1));
|
|
491
|
+
else exact.add(p);
|
|
492
|
+
return (path) => {
|
|
493
|
+
if (exact.has(path)) return true;
|
|
494
|
+
for (const pre of prefixes) if (path.startsWith(pre)) return true;
|
|
495
|
+
return false;
|
|
496
|
+
};
|
|
497
|
+
}
|
|
466
498
|
const PLUGIN_REGISTRY = {
|
|
467
499
|
cors: {
|
|
468
500
|
package: "@fastify/cors",
|
|
@@ -532,10 +564,10 @@ async function registerSecurityPlugins(fastify, config) {
|
|
|
532
564
|
} else fastify.log.warn("CORS disabled");
|
|
533
565
|
if (config.rateLimit !== false) {
|
|
534
566
|
const rateLimit = await loadPlugin("rateLimit");
|
|
535
|
-
const rateLimitOpts = config.rateLimit ?? {
|
|
567
|
+
const rateLimitOpts = buildRateLimitOpts(config.rateLimit ?? {
|
|
536
568
|
max: 100,
|
|
537
569
|
timeWindow: "1 minute"
|
|
538
|
-
};
|
|
570
|
+
});
|
|
539
571
|
await fastify.register(rateLimit, rateLimitOpts);
|
|
540
572
|
if (!(typeof rateLimitOpts === "object" && "store" in rateLimitOpts)) {
|
|
541
573
|
if (config.runtime === "distributed") throw new Error("[Arc] runtime: 'distributed' with rate limiting requires a shared store.\nProvide rateLimit: { store: new RedisStore({ ... }) } or disable rate limiting: rateLimit: false");
|
|
@@ -676,7 +708,7 @@ function validateDistributedRuntime(options) {
|
|
|
676
708
|
*/
|
|
677
709
|
async function createApp(options) {
|
|
678
710
|
if (options.debug !== void 0 && options.debug !== false) {
|
|
679
|
-
const { configureArcLogger } = await import("./logger
|
|
711
|
+
const { configureArcLogger } = await import("./logger/index.mjs");
|
|
680
712
|
configureArcLogger({ debug: options.debug });
|
|
681
713
|
}
|
|
682
714
|
validateAuthOptions(options);
|
|
@@ -717,7 +749,9 @@ async function createApp(options) {
|
|
|
717
749
|
await registerSecurityPlugins(fastify, config);
|
|
718
750
|
await registerUtilityPlugins(fastify, config);
|
|
719
751
|
const trackPlugin = (name, opts) => {
|
|
720
|
-
fastify.arc
|
|
752
|
+
const arc = fastify.arc;
|
|
753
|
+
if (!arc) return;
|
|
754
|
+
arc.plugins.set(name, {
|
|
721
755
|
name,
|
|
722
756
|
options: opts,
|
|
723
757
|
registeredAt: (/* @__PURE__ */ new Date()).toISOString()
|
|
@@ -730,7 +764,7 @@ async function createApp(options) {
|
|
|
730
764
|
await registerErrorHandler(fastify, config, trackPlugin);
|
|
731
765
|
await registerResources(fastify, config);
|
|
732
766
|
if (config.replyHelpers) {
|
|
733
|
-
const { replyHelpersPlugin } = await import("./replyHelpers-
|
|
767
|
+
const { replyHelpersPlugin } = await import("./replyHelpers-BLojtuvR.mjs").then((n) => n.n);
|
|
734
768
|
await fastify.register(replyHelpersPlugin);
|
|
735
769
|
}
|
|
736
770
|
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 { p as RegistryEntry } from "../index-BGbpGVyM.mjs";
|
|
2
|
+
import { t as ExternalOpenApiPaths } from "../externalPaths-Bapitwvd.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-D57iXYb8.mjs";
|
|
2
|
+
import { n as openApiPlugin, r as openapi_default, t as buildOpenApiSpec } from "../openapi-DpNpqBmo.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 {
|
|
3
|
-
import { t as
|
|
2
|
+
import { arcLog } from "./logger/index.mjs";
|
|
3
|
+
import { t as getUserRoles } from "./types-D57iXYb8.mjs";
|
|
4
4
|
import fp from "fastify-plugin";
|
|
5
5
|
//#region src/scope/elevation.ts
|
|
6
6
|
var elevation_exports = /* @__PURE__ */ __exportAll({
|