@classytic/arc 2.18.5 → 2.19.0
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/dist/{actionPermissions-h7DYDNMg.mjs → actionPermissions-D7NGwcAR.mjs} +1 -0
- package/dist/auth/index.d.mts +1 -1
- package/dist/auth/index.mjs +10 -1
- package/dist/cli/commands/describe.d.mts +1 -1
- package/dist/cli/commands/docs.mjs +1 -1
- package/dist/core/index.d.mts +2 -2
- package/dist/core/index.mjs +2 -2
- package/dist/{core-ZnQBa5rh.mjs → core-Ba-6Sue6.mjs} +16 -11
- package/dist/{createActionRouter-CagItpxI.mjs → createActionRouter-B_WzBFyf.mjs} +6 -5
- package/dist/{createAggregationRouter-DVKroXhC.mjs → createAggregationRouter-B_G_nIjm.mjs} +1 -1
- package/dist/docs/index.d.mts +1 -1
- package/dist/docs/index.mjs +1 -1
- package/dist/encryption/index.d.mts +222 -0
- package/dist/encryption/index.mjs +270 -0
- package/dist/factory/index.d.mts +1 -1
- package/dist/{fastify-augmentation-Cxb0ROIw.d.mts → fastify-augmentation-D-5_yQJi.d.mts} +1 -1
- package/dist/hooks/index.d.mts +1 -1
- package/dist/{index-Dh0_RSTd.d.mts → index-CP6A1Ffj.d.mts} +7 -7
- package/dist/{index-CHNQIhcb.d.mts → index-CUTG2Cuo.d.mts} +83 -1
- package/dist/index.d.mts +2 -2
- package/dist/index.mjs +3 -3
- 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/{mcpPlugin-D1wuRD2c.mjs → mcpPlugin-C088Nd-G.mjs} +1 -1
- package/dist/middleware/index.d.mts +1 -1
- package/dist/{openapi-xVJibapr.mjs → openapi-DZ-5a5Op.mjs} +2 -2
- package/dist/pipeline/index.d.mts +1 -1
- package/dist/plugins/index.d.mts +2 -2
- package/dist/plugins/tracing-entry.mjs +1 -1
- package/dist/presets/filesUpload.d.mts +1 -1
- package/dist/presets/index.d.mts +1 -1
- package/dist/presets/multiTenant.d.mts +1 -1
- package/dist/presets/search.d.mts +1 -1
- package/dist/{registerRoute-CPmdkZGu.mjs → registerRoute-BdV8DDwU.mjs} +19 -1
- package/dist/registry/index.d.mts +1 -1
- package/dist/testing/index.d.mts +2 -2
- package/dist/types/index.d.mts +2 -2
- package/dist/{types-CmkknLzI.d.mts → types-9_wtNPsI.d.mts} +1 -1
- package/dist/{types-D60PFwfG.d.mts → types-DpvSF5Hh.d.mts} +1 -1
- package/dist/utils/index.d.mts +1 -1
- package/package.json +10 -1
|
@@ -247,6 +247,7 @@ function sendControllerResponse(reply, response, request) {
|
|
|
247
247
|
function createFastifyHandler(controllerMethod) {
|
|
248
248
|
return async (req, reply) => {
|
|
249
249
|
sendControllerResponse(reply, await controllerMethod(createRequestContext(req)), req);
|
|
250
|
+
return reply;
|
|
250
251
|
};
|
|
251
252
|
}
|
|
252
253
|
/**
|
package/dist/auth/index.d.mts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { Ht as AuthPluginOptions, Vt as AuthHelpers } from "../index-CUTG2Cuo.mjs";
|
|
2
2
|
import { c as PermissionCheck } from "../fields-C7ahdGYf.mjs";
|
|
3
3
|
import { t as ExternalOpenApiPaths } from "../externalPaths-BE6wFpYv.mjs";
|
|
4
4
|
import { a as SessionManagerOptions, c as createSessionManager, i as SessionData, n as MemorySessionStoreOptions, o as SessionManagerResult, r as SessionCookieOptions, s as SessionStore, t as MemorySessionStore } from "../sessionManager-BXG-HGwG.mjs";
|
package/dist/auth/index.mjs
CHANGED
|
@@ -317,11 +317,20 @@ function toFetchRequest(request) {
|
|
|
317
317
|
else if (contentType.includes("application/json") || contentType.includes("text/") || !contentType) body = JSON.stringify(request.body);
|
|
318
318
|
else request.log?.warn?.("toFetchRequest: cannot reconstruct %s body without rawBody plugin", contentType);
|
|
319
319
|
}
|
|
320
|
-
|
|
320
|
+
const init = {
|
|
321
321
|
method: request.method,
|
|
322
322
|
headers,
|
|
323
323
|
body
|
|
324
|
+
};
|
|
325
|
+
const fetchRequest = new Request(url, init);
|
|
326
|
+
Object.defineProperty(fetchRequest, "clone", {
|
|
327
|
+
configurable: true,
|
|
328
|
+
writable: true,
|
|
329
|
+
value() {
|
|
330
|
+
return new Request(url, init);
|
|
331
|
+
}
|
|
324
332
|
});
|
|
333
|
+
return fetchRequest;
|
|
325
334
|
}
|
|
326
335
|
/**
|
|
327
336
|
* Pipe a Fetch API Response back into Fastify's reply.
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { V as ResourceDefinition,
|
|
1
|
+
import { V as ResourceDefinition, ht as RouteSchemaOptions, it as RateLimitConfig } from "../../index-CUTG2Cuo.mjs";
|
|
2
2
|
|
|
3
3
|
//#region src/cli/commands/describe.d.ts
|
|
4
4
|
interface DescribedResource {
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { t as buildOpenApiSpec } from "../../openapi-
|
|
1
|
+
import { t as buildOpenApiSpec } from "../../openapi-DZ-5a5Op.mjs";
|
|
2
2
|
import { t as loadResourcesFromEntry } from "../../loadResourcesFromEntry-BTlFEx05.mjs";
|
|
3
3
|
import { dirname, resolve } from "node:path";
|
|
4
4
|
import { mkdirSync, writeFileSync } from "node:fs";
|
package/dist/core/index.d.mts
CHANGED
|
@@ -1,3 +1,3 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import { A as MAX_FILTER_DEPTH, C as DEFAULT_SORT, D as HOOK_PHASES, E as HOOK_OPERATIONS, F as RESERVED_QUERY_PARAMS, I as SYSTEM_FIELDS, M as MAX_SEARCH_LENGTH, N as MUTATION_OPERATIONS, O as HookOperation, P as MutationOperation, S as DEFAULT_MAX_LIMIT, T as DEFAULT_UPDATE_METHOD, _ as defineAggregation, a as createRequestContext, b as DEFAULT_ID_FIELD, c as sendControllerResponse, d as getEntityQuery, f as defineResourceVariants, g as createPermissionMiddleware, h as createCrudRouter, i as createFastifyHandler, j as MAX_REGEX_LENGTH, k as HookPhase, l as getEntityId, m as defineAction, n as isFieldReadable, o as getControllerContext, p as DefineActionConfig, r as createCrudHandlers, s as getControllerScope, t as collectReadBlockedFields, u as getEntityIdField, v as CRUD_OPERATIONS, w as DEFAULT_TENANT_FIELD, x as DEFAULT_LIMIT, y as CrudOperation } from "../index-
|
|
1
|
+
import { At as AggregationMaterializedResult, B as defineResource, Bn as BodySanitizerConfig, Ct as AggMeasureInput, Dt as AggregationDateRangeRequirement, Et as AggregationConfig, Fn as BaseControllerOptions, In as ControllerConfigurableOptions, Ln as ControllerConstructionOptions, Mt as AggregationsMap, Ot as AggregationIndexHint, Rn as ListResult, Tt as AggregationCacheConfig, V as ResourceDefinition, an as SlugExt, cn as BulkMixin, dn as QueryResolverConfig, en as BaseController, fn as AccessControl, in as TreeMixin, jt as AggregationRateLimit, kt as AggregationMaterializedContext, ln as BaseCrudController, nn as SoftDeleteMixin, on as SlugMixin, pn as AccessControlConfig, rn as TreeExt, sn as BulkExt, tn as SoftDeleteExt, un as QueryResolver, wt as AggMeasureShorthand, zn as BodySanitizer } from "../index-CUTG2Cuo.mjs";
|
|
2
|
+
import { A as MAX_FILTER_DEPTH, C as DEFAULT_SORT, D as HOOK_PHASES, E as HOOK_OPERATIONS, F as RESERVED_QUERY_PARAMS, I as SYSTEM_FIELDS, M as MAX_SEARCH_LENGTH, N as MUTATION_OPERATIONS, O as HookOperation, P as MutationOperation, S as DEFAULT_MAX_LIMIT, T as DEFAULT_UPDATE_METHOD, _ as defineAggregation, a as createRequestContext, b as DEFAULT_ID_FIELD, c as sendControllerResponse, d as getEntityQuery, f as defineResourceVariants, g as createPermissionMiddleware, h as createCrudRouter, i as createFastifyHandler, j as MAX_REGEX_LENGTH, k as HookPhase, l as getEntityId, m as defineAction, n as isFieldReadable, o as getControllerContext, p as DefineActionConfig, r as createCrudHandlers, s as getControllerScope, t as collectReadBlockedFields, u as getEntityIdField, v as CRUD_OPERATIONS, w as DEFAULT_TENANT_FIELD, x as DEFAULT_LIMIT, y as CrudOperation } from "../index-CP6A1Ffj.mjs";
|
|
3
3
|
export { AccessControl, type AccessControlConfig, type AggMeasureInput, type AggMeasureShorthand, type AggregationCacheConfig, type AggregationConfig, type AggregationDateRangeRequirement, type AggregationIndexHint, type AggregationMaterializedContext, type AggregationMaterializedResult, type AggregationRateLimit, type AggregationsMap, BaseController, type BaseControllerOptions, BaseCrudController, BodySanitizer, type BodySanitizerConfig, type BulkExt, BulkMixin, CRUD_OPERATIONS, type ControllerConfigurableOptions, type ControllerConstructionOptions, CrudOperation, DEFAULT_ID_FIELD, DEFAULT_LIMIT, DEFAULT_MAX_LIMIT, DEFAULT_SORT, DEFAULT_TENANT_FIELD, DEFAULT_UPDATE_METHOD, type DefineActionConfig, HOOK_OPERATIONS, HOOK_PHASES, HookOperation, HookPhase, type ListResult, MAX_FILTER_DEPTH, MAX_REGEX_LENGTH, MAX_SEARCH_LENGTH, MUTATION_OPERATIONS, MutationOperation, QueryResolver, type QueryResolverConfig, RESERVED_QUERY_PARAMS, ResourceDefinition, SYSTEM_FIELDS, type SlugExt, SlugMixin, type SoftDeleteExt, SoftDeleteMixin, type TreeExt, TreeMixin, collectReadBlockedFields, createCrudHandlers, createCrudRouter, createFastifyHandler, createPermissionMiddleware, createRequestContext, defineAction, defineAggregation, defineResource, defineResourceVariants, getControllerContext, getControllerScope, getEntityId, getEntityIdField, getEntityQuery, isFieldReadable, sendControllerResponse };
|
package/dist/core/index.mjs
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
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-gaEeMlz-.mjs";
|
|
2
2
|
import { a as BulkMixin, c as collectReadBlockedFields, f as AccessControl, i as SlugMixin, l as isFieldReadable, n as TreeMixin, o as BaseCrudController, r as SoftDeleteMixin, s as QueryResolver, t as BaseController, u as BodySanitizer } from "../BaseController-Dy6EQHjM.mjs";
|
|
3
|
-
import { a as defineResource, c as createCrudRouter, i as defineResourceVariants, l as createPermissionMiddleware, n as getEntityIdField, o as ResourceDefinition, r as getEntityQuery, s as defineAction, t as getEntityId, u as defineAggregation } from "../core-
|
|
4
|
-
import { a as createCrudHandlers, c as getControllerContext, l as getControllerScope, o as createFastifyHandler, s as createRequestContext, u as sendControllerResponse } from "../actionPermissions-
|
|
3
|
+
import { a as defineResource, c as createCrudRouter, i as defineResourceVariants, l as createPermissionMiddleware, n as getEntityIdField, o as ResourceDefinition, r as getEntityQuery, s as defineAction, t as getEntityId, u as defineAggregation } from "../core-Ba-6Sue6.mjs";
|
|
4
|
+
import { a as createCrudHandlers, c as getControllerContext, l as getControllerScope, o as createFastifyHandler, s as createRequestContext, u as sendControllerResponse } from "../actionPermissions-D7NGwcAR.mjs";
|
|
5
5
|
export { AccessControl, BaseController, BaseCrudController, BodySanitizer, BulkMixin, 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, SlugMixin, SoftDeleteMixin, TreeMixin, collectReadBlockedFields, createCrudHandlers, createCrudRouter, createFastifyHandler, createPermissionMiddleware, createRequestContext, defineAction, defineAggregation, defineResource, defineResourceVariants, getControllerContext, getControllerScope, getEntityId, getEntityIdField, getEntityQuery, isFieldReadable, sendControllerResponse };
|
|
@@ -8,8 +8,8 @@ import { F as applyFieldWritePermissions, L as resolveEffectiveRoles } from "./p
|
|
|
8
8
|
import { t as getUserRoles } from "./types-XTApa8b6.mjs";
|
|
9
9
|
import { n as convertRouteSchema, t as convertOpenApiSchemas } from "./schemaConverter-U0MnO9vd.mjs";
|
|
10
10
|
import { i as pipeUIMessageStreamToReply, r as isReadableStream } from "./streaming-BQcW6_Kq.mjs";
|
|
11
|
-
import { a as createCrudHandlers, d as buildRequestScopeProjection, i as resolvePipelineSteps, o as createFastifyHandler, r as buildPipelineHandler, s as createRequestContext, t as resolveActionPermission } from "./actionPermissions-
|
|
12
|
-
import { c as
|
|
11
|
+
import { a as createCrudHandlers, d as buildRequestScopeProjection, i as resolvePipelineSteps, o as createFastifyHandler, r as buildPipelineHandler, s as createRequestContext, t as resolveActionPermission } from "./actionPermissions-D7NGwcAR.mjs";
|
|
12
|
+
import { a as selectPluginMw, c as buildPreHandlerChain, f as buildArcDecorator, i as resolveRouterPluginMw, l as resolveRoutePreHandlers, n as buildRateLimitConfig, r as buildRouteConfig, s as buildCrudPermissionMw, t as tryRegisterRoute, u as buildAuthMiddleware } from "./registerRoute-BdV8DDwU.mjs";
|
|
13
13
|
import { t as applyPresets } from "./presets-BtyFwTO4.mjs";
|
|
14
14
|
import { t as hasEvents } from "./typeGuards-C9KXUly6.mjs";
|
|
15
15
|
import { t as pluralize } from "./pluralize-DccFV9R7.mjs";
|
|
@@ -104,7 +104,7 @@ function methodCarriesBody(method) {
|
|
|
104
104
|
* `wrapHandler` is derived inline from `!route.raw`.
|
|
105
105
|
*/
|
|
106
106
|
function createCustomRoutes(fastify, routes, controller, options) {
|
|
107
|
-
const { tag, resourceName, arcDecorator, rateLimitConfig, pluginMw, pipeline, routeGuards, tenantScopeMw, fieldPermissions, onFieldWriteDenied } = options;
|
|
107
|
+
const { tag, resourceName, arcDecorator, rateLimitConfig, pluginMw, pipeline, routeGuards, tenantScopeMw, fieldPermissions, onFieldWriteDenied, extensions } = options;
|
|
108
108
|
for (const route of routes) {
|
|
109
109
|
const routeWithRefs = route;
|
|
110
110
|
const hasHandler = route.handler !== void 0;
|
|
@@ -177,7 +177,7 @@ function createCustomRoutes(fastify, routes, controller, options) {
|
|
|
177
177
|
}
|
|
178
178
|
return result;
|
|
179
179
|
} : handler,
|
|
180
|
-
...rateLimitConfig
|
|
180
|
+
...buildRouteConfig(rateLimitConfig, extensions)
|
|
181
181
|
}, {
|
|
182
182
|
resourceName,
|
|
183
183
|
op: opName
|
|
@@ -194,7 +194,7 @@ function createCustomRoutes(fastify, routes, controller, options) {
|
|
|
194
194
|
* @param options - Router configuration
|
|
195
195
|
*/
|
|
196
196
|
function createCrudRouter(fastify, controller, options = {}) {
|
|
197
|
-
const { tag = "Resource", schemas = {}, permissions = {}, middlewares = {}, routeGuards = [], routes: customRoutes = [], disableDefaultRoutes = false, disabledRoutes = [], resourceName = "unknown", schemaOptions, rateLimit, pipe: pipeline, fields: fieldPermissions, onFieldWriteDenied, updateMethod = DEFAULT_UPDATE_METHOD, idField } = options;
|
|
197
|
+
const { tag = "Resource", schemas = {}, permissions = {}, middlewares = {}, routeGuards = [], routes: customRoutes = [], disableDefaultRoutes = false, disabledRoutes = [], resourceName = "unknown", schemaOptions, rateLimit, pipe: pipeline, fields: fieldPermissions, onFieldWriteDenied, updateMethod = DEFAULT_UPDATE_METHOD, idField, extensions } = options;
|
|
198
198
|
const rateLimitConfig = buildRateLimitConfig(rateLimit);
|
|
199
199
|
const resourceHasQueryCache = fastify.hasDecorator("queryCache") && controller && typeof controller._cacheConfig !== "undefined" && controller._cacheConfig !== void 0;
|
|
200
200
|
const pluginMw = resolveRouterPluginMw(fastify, Boolean(resourceHasQueryCache));
|
|
@@ -295,7 +295,7 @@ function createCrudRouter(fastify, controller, options = {}) {
|
|
|
295
295
|
}, defaultSchemas[spec.op], schemas[spec.op]),
|
|
296
296
|
preHandler: preHandler.length > 0 ? preHandler : void 0,
|
|
297
297
|
handler: handlers[spec.op],
|
|
298
|
-
...rateLimitConfig
|
|
298
|
+
...buildRouteConfig(rateLimitConfig, extensions)
|
|
299
299
|
}, {
|
|
300
300
|
resourceName,
|
|
301
301
|
op: spec.op
|
|
@@ -313,7 +313,8 @@ function createCrudRouter(fastify, controller, options = {}) {
|
|
|
313
313
|
routeGuards,
|
|
314
314
|
tenantScopeMw,
|
|
315
315
|
fieldPermissions,
|
|
316
|
-
onFieldWriteDenied
|
|
316
|
+
onFieldWriteDenied,
|
|
317
|
+
extensions
|
|
317
318
|
});
|
|
318
319
|
}
|
|
319
320
|
function buildCrudHandlers(ctrl, pipeline, resourceName) {
|
|
@@ -1242,10 +1243,11 @@ function buildResourcePlugin(resource) {
|
|
|
1242
1243
|
pipe: resource.pipe,
|
|
1243
1244
|
fields: resource.fields,
|
|
1244
1245
|
onFieldWriteDenied: resource.onFieldWriteDenied,
|
|
1245
|
-
idField: resource.idField
|
|
1246
|
+
idField: resource.idField,
|
|
1247
|
+
extensions: resource.extensions
|
|
1246
1248
|
});
|
|
1247
1249
|
if (resource.actions && Object.keys(resource.actions).length > 0) {
|
|
1248
|
-
const { createActionRouter } = await import("./createActionRouter-
|
|
1250
|
+
const { createActionRouter } = await import("./createActionRouter-B_WzBFyf.mjs").then((n) => n.n);
|
|
1249
1251
|
createActionRouter(typedInstance, {
|
|
1250
1252
|
...normalizeActionsToRouterConfig(resource.actions, resource.actionPermissions, resource.tag, resource.permissions, resource.name, typedInstance.log),
|
|
1251
1253
|
resourceName: resource.name,
|
|
@@ -1255,11 +1257,12 @@ function buildResourcePlugin(resource) {
|
|
|
1255
1257
|
permissions: resource.permissions,
|
|
1256
1258
|
routeGuards: resource.routeGuards,
|
|
1257
1259
|
pipeline: resource.pipe,
|
|
1258
|
-
rateLimit: resource.rateLimit
|
|
1260
|
+
rateLimit: resource.rateLimit,
|
|
1261
|
+
extensions: resource.extensions
|
|
1259
1262
|
});
|
|
1260
1263
|
}
|
|
1261
1264
|
if (resource.aggregations && Object.keys(resource.aggregations).length > 0) {
|
|
1262
|
-
const [{ createAggregationRouter }, { adapterSupportsAggregate, ArcAggregationConfigError }] = await Promise.all([import("./createAggregationRouter-
|
|
1265
|
+
const [{ createAggregationRouter }, { adapterSupportsAggregate, ArcAggregationConfigError }] = await Promise.all([import("./createAggregationRouter-B_G_nIjm.mjs"), import("./validate-CRcaWzp2.mjs").then((n) => n.r)]);
|
|
1263
1266
|
const repoForAgg = resource.controller?.repository ?? resource.repository;
|
|
1264
1267
|
if (!adapterSupportsAggregate(repoForAgg)) {
|
|
1265
1268
|
const undispatchable = Object.entries(resource.aggregations).filter(([, cfg]) => !cfg.materialized).map(([name]) => name);
|
|
@@ -1332,6 +1335,7 @@ var ResourceDefinition = class {
|
|
|
1332
1335
|
*/
|
|
1333
1336
|
onFieldWriteDenied;
|
|
1334
1337
|
cache;
|
|
1338
|
+
extensions;
|
|
1335
1339
|
referenceData;
|
|
1336
1340
|
/**
|
|
1337
1341
|
* Per-resource MCP opt-out. `false` keeps the resource out of every
|
|
@@ -1429,6 +1433,7 @@ var ResourceDefinition = class {
|
|
|
1429
1433
|
this.fields = config.fields;
|
|
1430
1434
|
this.onFieldWriteDenied = config.onFieldWriteDenied;
|
|
1431
1435
|
this.cache = config.cache;
|
|
1436
|
+
this.extensions = config.extensions ? Object.freeze({ ...config.extensions }) : void 0;
|
|
1432
1437
|
this.mcp = config.mcp !== false;
|
|
1433
1438
|
this.referenceData = config.referenceData === true;
|
|
1434
1439
|
this.tenantField = config.tenantField;
|
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
import { t as __exportAll } from "./rolldown-runtime-8H4AJuhK.mjs";
|
|
2
2
|
import "./constants-gaEeMlz-.mjs";
|
|
3
3
|
import { f as createError } from "./errors-DkO8Nk6-.mjs";
|
|
4
|
-
import { i as resolvePipelineSteps, n as buildActionPipelineHandler, u as sendControllerResponse } from "./actionPermissions-
|
|
5
|
-
import { a as
|
|
4
|
+
import { i as resolvePipelineSteps, n as buildActionPipelineHandler, u as sendControllerResponse } from "./actionPermissions-D7NGwcAR.mjs";
|
|
5
|
+
import { a as selectPluginMw, c as buildPreHandlerChain, d as buildAuthMiddlewareForPermissions, f as buildArcDecorator, i as resolveRouterPluginMw, n as buildRateLimitConfig, o as buildActionPermissionMw, r as buildRouteConfig, t as tryRegisterRoute } from "./registerRoute-BdV8DDwU.mjs";
|
|
6
6
|
import { n as schemaIRToJsonSchemaBranch, t as normalizeSchemaIR } from "./schemaIR-D1MJwqmp.mjs";
|
|
7
7
|
//#region src/core/createActionRouter.ts
|
|
8
8
|
var createActionRouter_exports = /* @__PURE__ */ __exportAll({
|
|
@@ -18,7 +18,7 @@ var createActionRouter_exports = /* @__PURE__ */ __exportAll({
|
|
|
18
18
|
* (keyed by `body.action` at request time).
|
|
19
19
|
*/
|
|
20
20
|
function createActionRouter(fastify, config) {
|
|
21
|
-
const { tag, resourceName = tag ?? "action", actions, actionPermissions = {}, actionSchemas = {}, globalAuth, onError, fields: fieldPermissions, schemaOptions, idField = "_id", permissions: resourcePermissions, routeGuards = [], pipeline, rateLimit, idLessActionNames = [] } = config;
|
|
21
|
+
const { tag, resourceName = tag ?? "action", actions, actionPermissions = {}, actionSchemas = {}, globalAuth, onError, fields: fieldPermissions, schemaOptions, idField = "_id", permissions: resourcePermissions, routeGuards = [], pipeline, rateLimit, idLessActionNames = [], extensions } = config;
|
|
22
22
|
const allActionNames = Object.keys(actions);
|
|
23
23
|
if (allActionNames.length === 0) {
|
|
24
24
|
fastify.log.warn("[createActionRouter] No actions defined, skipping route creation");
|
|
@@ -121,7 +121,7 @@ function createActionRouter(fastify, config) {
|
|
|
121
121
|
schema: routeSchema,
|
|
122
122
|
attachValidation: true,
|
|
123
123
|
preHandler: preHandler.length > 0 ? preHandler : void 0,
|
|
124
|
-
...rateLimitConfig
|
|
124
|
+
...buildRouteConfig(rateLimitConfig, extensions),
|
|
125
125
|
handler: async (req, reply) => {
|
|
126
126
|
const { action, ...data } = req.body;
|
|
127
127
|
const id = hasId ? req.params.id : "";
|
|
@@ -135,7 +135,8 @@ function createActionRouter(fastify, config) {
|
|
|
135
135
|
const handler = wrappedHandlers.get(action);
|
|
136
136
|
if (!handler) throw createError(400, `Invalid action '${action}'. Valid actions: ${actionSubset.join(", ")}`, { validActions: actionSubset });
|
|
137
137
|
try {
|
|
138
|
-
|
|
138
|
+
sendControllerResponse(reply, await handler(id, data, req), req);
|
|
139
|
+
return reply;
|
|
139
140
|
} catch (error) {
|
|
140
141
|
if (onError) {
|
|
141
142
|
const { statusCode, error: errorMsg, code } = onError(error, action, id);
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { f as createError, l as UnauthorizedError, r as ForbiddenError } from "./errors-DkO8Nk6-.mjs";
|
|
2
|
-
import {
|
|
2
|
+
import { a as selectPluginMw, c as buildPreHandlerChain, f as buildArcDecorator, i as resolveRouterPluginMw, n as buildRateLimitConfig, u as buildAuthMiddleware } from "./registerRoute-BdV8DDwU.mjs";
|
|
3
3
|
import { n as validateAggregations } from "./validate-CRcaWzp2.mjs";
|
|
4
4
|
import { t as buildAggregationHandler } from "./buildHandler-B0qvAguf.mjs";
|
|
5
5
|
//#region src/core/aggregation/createAggregationRouter.ts
|
package/dist/docs/index.d.mts
CHANGED
package/dist/docs/index.mjs
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { t as getUserRoles } from "../types-XTApa8b6.mjs";
|
|
2
|
-
import { n as openApiPlugin, r as openapi_default, t as buildOpenApiSpec } from "../openapi-
|
|
2
|
+
import { n as openApiPlugin, r as openapi_default, t as buildOpenApiSpec } from "../openapi-DZ-5a5Op.mjs";
|
|
3
3
|
import fp from "fastify-plugin";
|
|
4
4
|
//#region src/docs/scalar.ts
|
|
5
5
|
const scalarPlugin = async (fastify, opts = {}) => {
|
|
@@ -0,0 +1,222 @@
|
|
|
1
|
+
import { FastifyPluginAsync, FastifyRequest } from "fastify";
|
|
2
|
+
|
|
3
|
+
//#region src/encryption/types.d.ts
|
|
4
|
+
/** JWE key-management algorithm — the protected-header `alg`. */
|
|
5
|
+
type JweAlg = "RSA-OAEP-256" | "RSA-OAEP-384" | "RSA-OAEP-512" | "ECDH-ES" | "ECDH-ES+A256KW" | "A256GCMKW" | "dir";
|
|
6
|
+
/** JWE content-encryption algorithm — the protected-header `enc`. AEAD only. */
|
|
7
|
+
type JweEnc = "A128GCM" | "A192GCM" | "A256GCM";
|
|
8
|
+
/** Encryption strategy for a route's response. */
|
|
9
|
+
type EncryptionMode = "jwe" | "fields";
|
|
10
|
+
/**
|
|
11
|
+
* Opaque key material. For `jwe` mode this is a `jose` `KeyLike` /
|
|
12
|
+
* `CryptoKey` / `Uint8Array`; for `fields` mode a 32-byte secret
|
|
13
|
+
* (`Buffer` / `Uint8Array`). Kept `unknown` at the contract layer so the
|
|
14
|
+
* `KeyProvider` interface carries no hard `jose` type dependency — hosts
|
|
15
|
+
* without `jose` installed can still implement field-mode providers.
|
|
16
|
+
*/
|
|
17
|
+
type KeyMaterial = unknown;
|
|
18
|
+
/** A key paired with its identifier — what encrypt operations consume. */
|
|
19
|
+
interface ActiveKey {
|
|
20
|
+
/** Key id, stamped into the JWE `kid` header / field envelope for rotation. */
|
|
21
|
+
readonly kid: string;
|
|
22
|
+
/** The key material (public key for JWE, 32-byte secret for fields). */
|
|
23
|
+
readonly key: KeyMaterial;
|
|
24
|
+
/** Optional per-key `alg` override (else the plugin default applies). */
|
|
25
|
+
readonly alg?: JweAlg;
|
|
26
|
+
}
|
|
27
|
+
/** Context handed to the key provider so resolution can be request-scoped. */
|
|
28
|
+
interface KeyContext {
|
|
29
|
+
readonly request: FastifyRequest;
|
|
30
|
+
}
|
|
31
|
+
type MaybePromise<T> = T | Promise<T>;
|
|
32
|
+
/**
|
|
33
|
+
* Pluggable key resolution. Arc owns the request-time plumbing — `kid` →
|
|
34
|
+
* key, algorithm allowlist enforcement; the host owns the trust store —
|
|
35
|
+
* which keys are valid and where they live (file / env / AWS KMS / GCP KMS
|
|
36
|
+
* / Vault). Encryption and decryption are deliberately separate operations
|
|
37
|
+
* because they often use DIFFERENT keys (JWE: a public key encrypts the
|
|
38
|
+
* outbound response, a private key by `kid` decrypts the inbound request —
|
|
39
|
+
* exactly Visa's two-key-pair model).
|
|
40
|
+
*
|
|
41
|
+
* Rotation mirrors `@fastify/secure-session`: the active key encrypts; any
|
|
42
|
+
* key resolvable by `kid` can decrypt, so you can roll keys without dropping
|
|
43
|
+
* in-flight traffic.
|
|
44
|
+
*/
|
|
45
|
+
interface KeyProvider {
|
|
46
|
+
/** Active key for encrypting OUTBOUND responses. */
|
|
47
|
+
encryptionKey(ctx: KeyContext): MaybePromise<ActiveKey>;
|
|
48
|
+
/** Resolve a DECRYPTION key by the inbound message's `kid`. */
|
|
49
|
+
decryptionKey(kid: string, ctx: KeyContext): MaybePromise<KeyMaterial | undefined>;
|
|
50
|
+
}
|
|
51
|
+
/**
|
|
52
|
+
* Per-resource encryption directive — the typed slice plugins register on
|
|
53
|
+
* `ResourceExtensions`. Declared on a resource via
|
|
54
|
+
* `defineResource({ extensions: { encryption: { … } } })`; arc threads it
|
|
55
|
+
* to request time on `request.routeOptions.config.arcExtensions.encryption`.
|
|
56
|
+
*/
|
|
57
|
+
interface EncryptionDirective {
|
|
58
|
+
/** Strategy. Defaults to the plugin-level `mode` (default `'jwe'`). */
|
|
59
|
+
readonly mode?: EncryptionMode;
|
|
60
|
+
/**
|
|
61
|
+
* For `mode: 'fields'` — dot-paths of fields to encrypt, resolved against
|
|
62
|
+
* the serialized response body (e.g. `'cardNumber'`, `'account.iban'`).
|
|
63
|
+
* Ignored for `mode: 'jwe'` (the whole body is encrypted).
|
|
64
|
+
*/
|
|
65
|
+
readonly fields?: readonly string[];
|
|
66
|
+
/**
|
|
67
|
+
* Explicit opt-out — set `false` to disable encryption for this resource
|
|
68
|
+
* even when a plugin-level default (`routes`) would otherwise apply.
|
|
69
|
+
*/
|
|
70
|
+
readonly enabled?: boolean;
|
|
71
|
+
}
|
|
72
|
+
/**
|
|
73
|
+
* Plugin-level options. `keyProvider` is the only required field; everything
|
|
74
|
+
* else has a secure default.
|
|
75
|
+
*/
|
|
76
|
+
interface EncryptionOptions {
|
|
77
|
+
/**
|
|
78
|
+
* Master on/off. Default `true`. Set `false` to register the plugin as a
|
|
79
|
+
* typed no-op (config-gated environments) — same pattern as
|
|
80
|
+
* `idempotencyPlugin`.
|
|
81
|
+
*/
|
|
82
|
+
readonly enabled?: boolean;
|
|
83
|
+
/** Key provider — resolves `kid` → key for encrypt/decrypt. Required. */
|
|
84
|
+
readonly keyProvider: KeyProvider;
|
|
85
|
+
/** Default mode when a route opts in without naming one. Default `'jwe'`. */
|
|
86
|
+
readonly mode?: EncryptionMode;
|
|
87
|
+
/** JWE key-management algorithm. Default `'RSA-OAEP-256'`. */
|
|
88
|
+
readonly alg?: JweAlg;
|
|
89
|
+
/** JWE content-encryption algorithm. Default `'A256GCM'`. */
|
|
90
|
+
readonly enc?: JweEnc;
|
|
91
|
+
/**
|
|
92
|
+
* Inbound-decryption algorithm allowlists — block algorithm-substitution
|
|
93
|
+
* downgrade attacks. Default `[alg]` / `[enc]`. Set explicitly only when a
|
|
94
|
+
* client legitimately uses more than one algorithm.
|
|
95
|
+
*/
|
|
96
|
+
readonly allowedAlgs?: readonly JweAlg[];
|
|
97
|
+
readonly allowedEncs?: readonly JweEnc[];
|
|
98
|
+
/**
|
|
99
|
+
* Fallback opt-in for routes that don't declare
|
|
100
|
+
* `extensions.encryption`. Return a directive (or `true` for the default
|
|
101
|
+
* mode) to encrypt, `false`/`undefined` to skip. The per-resource
|
|
102
|
+
* `extensions.encryption` ALWAYS wins when present. Use this to scope ALE
|
|
103
|
+
* to sensitive prefixes without touching every resource.
|
|
104
|
+
*
|
|
105
|
+
* @example confine ALE to `/payments` and `/accounts`
|
|
106
|
+
* ```ts
|
|
107
|
+
* routes: (req) => /^\/(payments|accounts)\b/.test(req.url)
|
|
108
|
+
* ```
|
|
109
|
+
*/
|
|
110
|
+
readonly routes?: (request: FastifyRequest) => EncryptionDirective | boolean | undefined;
|
|
111
|
+
/** Decrypt inbound `application/jose` request bodies. Default `true`. */
|
|
112
|
+
readonly decryptRequests?: boolean;
|
|
113
|
+
/** Media type that signals an encrypted REQUEST body. Default `'application/jose'`. */
|
|
114
|
+
readonly contentType?: string;
|
|
115
|
+
/** Content-Type set on full-body JWE RESPONSES. Default `'application/jose'`. */
|
|
116
|
+
readonly responseContentType?: string;
|
|
117
|
+
/**
|
|
118
|
+
* Response header advertising that the body is encrypted, so generic
|
|
119
|
+
* client SDKs know to decrypt. Default `'x-encrypted'`; set `false` to omit.
|
|
120
|
+
*/
|
|
121
|
+
readonly flagHeader?: string | false;
|
|
122
|
+
}
|
|
123
|
+
/** Request decorations added by the plugin. */
|
|
124
|
+
declare module "fastify" {
|
|
125
|
+
interface FastifyRequest {
|
|
126
|
+
/** `true` when the inbound body was decrypted from a JWE by this plugin. */
|
|
127
|
+
requestDecrypted?: boolean;
|
|
128
|
+
/** `true` when the outbound body was encrypted by this plugin. */
|
|
129
|
+
responseEncrypted?: boolean;
|
|
130
|
+
}
|
|
131
|
+
}
|
|
132
|
+
//#endregion
|
|
133
|
+
//#region src/encryption/encryptionPlugin.d.ts
|
|
134
|
+
declare const encryptionPlugin: FastifyPluginAsync<EncryptionOptions>;
|
|
135
|
+
declare const _default: FastifyPluginAsync<EncryptionOptions>;
|
|
136
|
+
//#endregion
|
|
137
|
+
//#region src/encryption/fieldCipher.d.ts
|
|
138
|
+
/**
|
|
139
|
+
* Field-level cipher — `node:crypto` AES-256-GCM, zero external deps.
|
|
140
|
+
*
|
|
141
|
+
* Used by `mode: 'fields'` to encrypt individual field values in place while
|
|
142
|
+
* leaving the JSON structure intact. The envelope is an authenticated,
|
|
143
|
+
* versioned, `kid`-tagged token:
|
|
144
|
+
*
|
|
145
|
+
* arc.v1.<b64url(kid)>.<b64url(iv)>.<b64url(ciphertext)>.<b64url(tag)>
|
|
146
|
+
*
|
|
147
|
+
* This is NOT a hand-rolled cipher — it's the correct use of a vetted AEAD
|
|
148
|
+
* primitive (AES-256-GCM): a fresh random 96-bit IV per operation (never
|
|
149
|
+
* caller-supplied — IV reuse under GCM is catastrophic), authentication via
|
|
150
|
+
* the GCM tag, and a version prefix so the format can evolve. Same shape, in
|
|
151
|
+
* spirit, as MongoDB CSFLE's BSON-binary subtype-6 envelope. For
|
|
152
|
+
* cross-vendor interop or asymmetric keys use `mode: 'jwe'` (RFC 7516)
|
|
153
|
+
* instead.
|
|
154
|
+
*/
|
|
155
|
+
/** Encrypt a UTF-8 string into the `arc.v1` field envelope. */
|
|
156
|
+
declare function encryptField(plaintext: string, kid: string, key: unknown): string;
|
|
157
|
+
/** Parsed envelope header — exposes `kid` for key resolution before decrypt. */
|
|
158
|
+
interface ParsedFieldEnvelope {
|
|
159
|
+
readonly kid: string;
|
|
160
|
+
readonly iv: Buffer;
|
|
161
|
+
readonly ciphertext: Buffer;
|
|
162
|
+
readonly tag: Buffer;
|
|
163
|
+
}
|
|
164
|
+
/** Returns the parsed envelope, or `null` when the token isn't `arc.v1`. */
|
|
165
|
+
declare function parseFieldEnvelope(token: string): ParsedFieldEnvelope | null;
|
|
166
|
+
/** Decrypt a parsed `arc.v1` envelope with the resolved key. Throws on tamper. */
|
|
167
|
+
declare function decryptField(envelope: ParsedFieldEnvelope, key: unknown): string;
|
|
168
|
+
//#endregion
|
|
169
|
+
//#region src/encryption/keyProvider.d.ts
|
|
170
|
+
interface StaticKeyProviderOptions {
|
|
171
|
+
/**
|
|
172
|
+
* Active key used to ENCRYPT outbound responses. For `jwe` mode this is the
|
|
173
|
+
* recipient's PUBLIC key; for `fields` mode a 32-byte symmetric secret.
|
|
174
|
+
*/
|
|
175
|
+
readonly encryptionKey: ActiveKey;
|
|
176
|
+
/**
|
|
177
|
+
* Keys used to DECRYPT inbound requests, indexed by `kid`. For `jwe` mode
|
|
178
|
+
* these are PRIVATE keys; for `fields` mode the same symmetric secrets.
|
|
179
|
+
*
|
|
180
|
+
* Defaults to `{ [encryptionKey.kid]: encryptionKey.key }` — correct for
|
|
181
|
+
* symmetric field mode where one key both encrypts and decrypts. For
|
|
182
|
+
* asymmetric JWE you MUST pass the private key(s) here; the public
|
|
183
|
+
* `encryptionKey` cannot decrypt. Keep 2–3 entries during a rotation so
|
|
184
|
+
* in-flight messages signed with the previous `kid` still decrypt.
|
|
185
|
+
*/
|
|
186
|
+
readonly decryptionKeys?: Readonly<Record<string, KeyMaterial>>;
|
|
187
|
+
}
|
|
188
|
+
/**
|
|
189
|
+
* A static, in-memory key provider with `kid`-based rotation. Keys are held
|
|
190
|
+
* for the process lifetime — resolve them from env / a secrets file at boot
|
|
191
|
+
* and pass the imported key material in (don't re-parse PEM per request).
|
|
192
|
+
*
|
|
193
|
+
* @example symmetric field mode
|
|
194
|
+
* ```ts
|
|
195
|
+
* const provider = createStaticKeyProvider({
|
|
196
|
+
* encryptionKey: { kid: "2026-06", key: Buffer.from(process.env.FIELD_KEY!, "base64") },
|
|
197
|
+
* });
|
|
198
|
+
* ```
|
|
199
|
+
*
|
|
200
|
+
* @example asymmetric JWE (jose key material)
|
|
201
|
+
* ```ts
|
|
202
|
+
* const provider = createStaticKeyProvider({
|
|
203
|
+
* encryptionKey: { kid: "client-1", key: await importSPKI(clientPubPem, "RSA-OAEP-256") },
|
|
204
|
+
* decryptionKeys: { "server-1": await importPKCS8(serverPrivPem, "RSA-OAEP-256") },
|
|
205
|
+
* });
|
|
206
|
+
* ```
|
|
207
|
+
*/
|
|
208
|
+
declare function createStaticKeyProvider(options: StaticKeyProviderOptions): KeyProvider;
|
|
209
|
+
//#endregion
|
|
210
|
+
//#region src/encryption/index.d.ts
|
|
211
|
+
declare module "../types/resource.js" {
|
|
212
|
+
interface ResourceExtensions {
|
|
213
|
+
/**
|
|
214
|
+
* Encrypt this resource's responses — full-body JWE or specific fields.
|
|
215
|
+
* Threaded to request time on
|
|
216
|
+
* `request.routeOptions.config.arcExtensions.encryption`.
|
|
217
|
+
*/
|
|
218
|
+
encryption?: EncryptionDirective;
|
|
219
|
+
}
|
|
220
|
+
}
|
|
221
|
+
//#endregion
|
|
222
|
+
export { type ActiveKey, type EncryptionDirective, type EncryptionMode, type EncryptionOptions, type JweAlg, type JweEnc, type KeyContext, type KeyMaterial, type KeyProvider, type ParsedFieldEnvelope, type StaticKeyProviderOptions, createStaticKeyProvider, decryptField, _default as default, encryptField, encryptionPlugin, parseFieldEnvelope };
|