@classytic/arc 2.21.0 → 2.23.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/{BaseController-BnLYgE9A.mjs → BaseController-BQ8KNvWC.mjs} +3 -2
- package/dist/{ResourceRegistry-7RnrEWNP.mjs → ResourceRegistry-y9Hnypus.mjs} +1 -0
- package/dist/{actionPermissions-ze70xXJi.mjs → actionPermissions-lszoJle1.mjs} +1 -1
- package/dist/applyPermissionResult-Tw4iXtlf.mjs +264 -0
- package/dist/audit/index.d.mts +2 -2
- package/dist/auth/index.d.mts +30 -2
- package/dist/auth/index.mjs +6 -3
- package/dist/cli/commands/describe.d.mts +1 -1
- package/dist/cli/commands/describe.mjs +1 -1
- package/dist/cli/commands/docs.mjs +2 -2
- package/dist/cli/commands/init.mjs +1 -1
- package/dist/cli/commands/introspect.mjs +1 -1
- package/dist/cli/index.mjs +1 -1
- package/dist/core/index.d.mts +2 -2
- package/dist/core/index.mjs +3 -3
- package/dist/{core-DZ6Xe1jR.mjs → core-DQ6rDTyH.mjs} +126 -68
- package/dist/{createActionRouter-BWMDW-dL.mjs → createActionRouter-BuOv-m4Y.mjs} +10 -6
- package/dist/{createAggregationRouter-l5mNdYrZ.mjs → createAggregationRouter-CgJv_BU_.mjs} +3 -3
- package/dist/{createApp-Bi61XxqU.mjs → createApp-CjOeWJ9_.mjs} +252 -57
- package/dist/docs/index.d.mts +1 -1
- package/dist/docs/index.mjs +1 -1
- package/dist/encryption/index.mjs +9 -5
- package/dist/{errorHandler-Dxf6P_kV.mjs → errorHandler-Bq2OeEQL.mjs} +34 -10
- package/dist/{errorHandler-B0e-bq-O.d.mts → errorHandler-C6yv4ZTW.d.mts} +13 -0
- package/dist/events/index.mjs +5 -2
- package/dist/factory/index.d.mts +46 -9
- package/dist/factory/index.mjs +64 -13
- package/dist/{fastify-augmentation-DafigGQp.d.mts → fastify-augmentation-g_f9omSc.d.mts} +2 -2
- package/dist/fields-D3lhgzhD.d.mts +121 -0
- package/dist/health-ScyjIVo5.mjs +202 -0
- package/dist/hooks/index.d.mts +1 -1
- package/dist/idempotency/index.d.mts +7 -0
- package/dist/idempotency/index.mjs +7 -17
- package/dist/{index-Dtrj6XZk.d.mts → index-BBsvQt7U.d.mts} +1310 -1226
- package/dist/{types-DpxoNX5v.d.mts → index-Ba10fdKp.d.mts} +196 -66
- package/dist/{index-B5N-NaHs.d.mts → index-CC5ruDGh.d.mts} +3 -3
- package/dist/{index-B5PoNbfu.d.mts → index-wMoWBLkn.d.mts} +68 -2
- package/dist/index.d.mts +6 -5
- package/dist/index.mjs +6 -5
- package/dist/{init-0YhHkXGk.mjs → init-CjO4p_q1.mjs} +123 -4
- package/dist/integrations/event-gateway.mjs +1 -1
- package/dist/integrations/index.d.mts +1 -1
- package/dist/integrations/mcp/index.d.mts +67 -4
- 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/integrations/webhooks.d.mts +131 -7
- package/dist/integrations/webhooks.mjs +169 -10
- package/dist/interface-n5hpKg1d.mjs +16 -0
- package/dist/jsonBody-BlzAoGCe.mjs +43 -0
- package/dist/{loadResourcesFromEntry-OMCvIEE7.mjs → loadResourcesFromEntry-kvGT6EcO.mjs} +1 -1
- package/dist/logger/index.d.mts +26 -1
- package/dist/logger/index.mjs +39 -1
- package/dist/{mcpPlugin-B9XjjNVG.mjs → mcpPlugin-BtzIL_kM.mjs} +312 -39
- package/dist/middleware/index.d.mts +1 -1
- package/dist/{module-WeuU5jfd.d.mts → module-B4u10iOJ.d.mts} +26 -1
- package/dist/{openapi-Kyod_di6.mjs → openapi-jSDx-VDv.mjs} +3 -3
- package/dist/permissions/index.d.mts +4 -3
- package/dist/permissions/index.mjs +3 -2
- package/dist/{permissions-BJMuKMl7.mjs → permissions-tbxFHxjZ.mjs} +77 -264
- package/dist/pipeline/index.d.mts +1 -1
- package/dist/plugins/index.d.mts +24 -7
- package/dist/plugins/index.mjs +14 -282
- package/dist/plugins/response-cache.mjs +1 -0
- package/dist/plugins/tracing-entry.mjs +7 -4
- package/dist/presets/filesUpload.d.mts +2 -2
- package/dist/presets/filesUpload.mjs +1 -1
- package/dist/presets/index.d.mts +1 -1
- package/dist/presets/index.mjs +1 -1
- package/dist/presets/multiTenant.d.mts +1 -1
- package/dist/presets/search.d.mts +2 -2
- package/dist/presets/search.mjs +1 -1
- package/dist/{presets-gw0ciIUD.mjs → presets-BOe4Ugkl.mjs} +1 -1
- package/dist/rateLimitKey-BoWRH3e7.mjs +14 -0
- package/dist/realtime-I8OrfnMR.mjs +375 -0
- package/dist/{registerRoute-oGAl0oA8.mjs → registerRoute-CWO6mdYe.mjs} +38 -18
- package/dist/registry/index.d.mts +2 -2
- package/dist/registry/index.mjs +1 -1
- package/dist/requestId-5BhTVzBv.mjs +113 -0
- package/dist/schemaConverter-DJvodiHj.mjs +171 -0
- package/dist/{schemaIR-ajetw3Fv.mjs → schemaIR-Ca7ySNPx.mjs} +2 -2
- package/dist/scim/index.mjs +6 -7
- package/dist/scope/index.mjs +2 -76
- package/dist/scope-LONQ3mfZ.mjs +37 -0
- package/dist/simpleEqualityMatcher-Cg05HPsn.mjs +68 -0
- package/dist/{sse-CfUjwjDk.mjs → sse-0u4P4-OI.mjs} +7 -30
- package/dist/sseStream-BQ7Qaynp.mjs +57 -0
- package/dist/testing/index.d.mts +83 -3
- package/dist/testing/index.mjs +70 -3
- package/dist/types/index.d.mts +2 -2
- package/dist/{types-DRj-Ut4n.d.mts → types-5OuqeTDX.d.mts} +37 -2
- package/dist/{fields-BGqrAa_u.d.mts → types-Csxga7d3.d.mts} +1 -121
- package/dist/usage/index.d.mts +142 -0
- package/dist/usage/index.mjs +0 -0
- package/dist/utils/index.d.mts +101 -19
- package/dist/utils/index.mjs +5 -3
- package/dist/{utils-CO8a3B_1.mjs → utils-CZyeRXTh.mjs} +80 -82
- package/dist/{versioning-DQ4mCvg0.d.mts → versioning-D6PvCwpq.d.mts} +43 -9
- package/llms.txt +39 -0
- package/package.json +513 -447
- package/skills/arc/SKILL.md +14 -8
- package/skills/arc/references/api-reference.md +34 -0
- package/skills/arc/references/auth.md +21 -4
- package/skills/arc/references/mcp.md +1 -1
- package/skills/arc/references/production.md +734 -654
- package/skills/arc-code-review/SKILL.md +1 -1
- package/skills/arc-code-review/references/anti-patterns.md +71 -18
- package/skills/arc-code-review/references/arc-cheatsheet.md +15 -2
- package/skills/arc-code-review/references/migration-recipes.md +7 -7
- package/skills/arc-code-review/references/mongokit-migration.md +4 -3
- package/skills/arc-module-publishing/SKILL.md +2 -1
- package/skills/arc-module-publishing/references/authoring-modules.md +55 -0
- package/skills/arc-module-publishing/references/package-manifest.md +4 -2
- package/dist/preloadResources-CHw3_oA_.mjs +0 -45
- package/dist/schemaConverter-U0MnO9vd.mjs +0 -110
|
@@ -2,16 +2,17 @@ import { s as DEFAULT_UPDATE_METHOD, t as CRUD_OPERATIONS } from "./constants-DN
|
|
|
2
2
|
import { arcLog } from "./logger/index.mjs";
|
|
3
3
|
import { r as ForbiddenError } from "./errors-DkO8Nk6-.mjs";
|
|
4
4
|
import { b as isElevated, n as PUBLIC_SCOPE, x as isMember } from "./types-bHblZ9YN.mjs";
|
|
5
|
-
import {
|
|
6
|
-
import { d as
|
|
7
|
-
import { a as
|
|
8
|
-
import { I as applyFieldWritePermissions, R as resolveEffectiveRoles } from "./permissions-BJMuKMl7.mjs";
|
|
5
|
+
import { d as DEFAULT_FIELD_WRITE_DENIAL_POLICY, t as BaseController } from "./BaseController-BQ8KNvWC.mjs";
|
|
6
|
+
import { a as selectPluginMw, c as buildRouteHooks, d as buildAuthMiddleware, i as resolveRouterPluginMw, l as resolveRoutePreHandlers, n as buildRateLimitConfig, p as buildArcDecorator, r as buildRouteConfig, s as buildCrudPermissionMw, t as tryRegisterRoute, u as routeHookOptions } from "./registerRoute-CWO6mdYe.mjs";
|
|
7
|
+
import { a as applyFieldWritePermissions, s as resolveEffectiveRoles } from "./applyPermissionResult-Tw4iXtlf.mjs";
|
|
9
8
|
import { t as getUserRoles } from "./types-XTApa8b6.mjs";
|
|
10
|
-
import { t as applyPresets } from "./presets-
|
|
11
|
-
import {
|
|
9
|
+
import { t as applyPresets } from "./presets-BOe4Ugkl.mjs";
|
|
10
|
+
import { D as requireAuth } from "./permissions-tbxFHxjZ.mjs";
|
|
11
|
+
import { N as assertValidConfig, l as getDefaultCrudSchemas } from "./utils-CZyeRXTh.mjs";
|
|
12
|
+
import { n as convertRouteSchema, r as ensureSchemaConverter, t as convertOpenApiSchemas } from "./schemaConverter-DJvodiHj.mjs";
|
|
12
13
|
import { i as pipeUIMessageStreamToReply, r as isReadableStream } from "./streaming-BQcW6_Kq.mjs";
|
|
13
14
|
import { t as hasEvents } from "./typeGuards-C9KXUly6.mjs";
|
|
14
|
-
import { a as createCrudHandlers, d as buildRequestScopeProjection, i as resolvePipelineSteps, o as createFastifyHandler, r as buildPipelineHandler, s as createRequestContext, t as resolveActionPermission } from "./actionPermissions-
|
|
15
|
+
import { a as createCrudHandlers, d as buildRequestScopeProjection, i as resolvePipelineSteps, o as createFastifyHandler, r as buildPipelineHandler, s as createRequestContext, t as resolveActionPermission } from "./actionPermissions-lszoJle1.mjs";
|
|
15
16
|
import { t as pluralize } from "./pluralize-BhrgwZb9.mjs";
|
|
16
17
|
//#region src/core/middlewares/fieldWrite.ts
|
|
17
18
|
/**
|
|
@@ -151,7 +152,7 @@ function createCustomRoutes(fastify, routes, controller, options) {
|
|
|
151
152
|
const fieldWriteMw = buildFieldWritePreHandler(fieldPermissions, onFieldWriteDenied);
|
|
152
153
|
if (fieldWriteMw) customPreHandlers.push(fieldWriteMw);
|
|
153
154
|
}
|
|
154
|
-
const
|
|
155
|
+
const hooks = buildRouteHooks({
|
|
155
156
|
preAuth: route.preAuth ?? [],
|
|
156
157
|
arcDecorator,
|
|
157
158
|
authMw: buildAuthMiddleware(fastify, route.permissions),
|
|
@@ -165,7 +166,7 @@ function createCustomRoutes(fastify, routes, controller, options) {
|
|
|
165
166
|
method: route.method,
|
|
166
167
|
url: route.path,
|
|
167
168
|
schema,
|
|
168
|
-
|
|
169
|
+
...routeHookOptions(hooks),
|
|
169
170
|
handler: isStream ? async (request, reply) => {
|
|
170
171
|
reply.raw.setHeader("Content-Type", "text/event-stream");
|
|
171
172
|
reply.raw.setHeader("Cache-Control", "no-cache");
|
|
@@ -274,7 +275,7 @@ function createCrudRouter(fastify, controller, options = {}) {
|
|
|
274
275
|
for (const spec of crudTable) {
|
|
275
276
|
if (disabledRoutes.includes(spec.op)) continue;
|
|
276
277
|
const permission = permissions[spec.op];
|
|
277
|
-
const
|
|
278
|
+
const hooks = buildRouteHooks({
|
|
278
279
|
arcDecorator,
|
|
279
280
|
authMw: buildAuthMiddleware(fastify, permission),
|
|
280
281
|
permissionMw: buildCrudPermissionMw(permission, resourceName, spec.op),
|
|
@@ -293,7 +294,7 @@ function createCrudRouter(fastify, controller, options = {}) {
|
|
|
293
294
|
summary,
|
|
294
295
|
...spec.hasIdParams ? { params: idParamsSchema } : {}
|
|
295
296
|
}, defaultSchemas[spec.op], schemas[spec.op]),
|
|
296
|
-
|
|
297
|
+
...routeHookOptions(hooks),
|
|
297
298
|
handler: handlers[spec.op],
|
|
298
299
|
...buildRouteConfig(rateLimitConfig, extensions)
|
|
299
300
|
}, {
|
|
@@ -359,6 +360,58 @@ function defineAction(config) {
|
|
|
359
360
|
};
|
|
360
361
|
}
|
|
361
362
|
//#endregion
|
|
363
|
+
//#region src/core/defineResource/seams.ts
|
|
364
|
+
/** Plain object test — literals only; class instances must replace, not merge. */
|
|
365
|
+
function isPlainObject(value) {
|
|
366
|
+
if (value === null || typeof value !== "object" || Array.isArray(value)) return false;
|
|
367
|
+
const proto = Object.getPrototypeOf(value);
|
|
368
|
+
return proto === Object.prototype || proto === null;
|
|
369
|
+
}
|
|
370
|
+
/** Recursive plain-object merge; nested arrays REPLACE (they're value lists). */
|
|
371
|
+
function mergePlain(base, next) {
|
|
372
|
+
const out = { ...base };
|
|
373
|
+
for (const [key, value] of Object.entries(next)) {
|
|
374
|
+
if (value === void 0) continue;
|
|
375
|
+
const existing = out[key];
|
|
376
|
+
out[key] = isPlainObject(existing) && isPlainObject(value) ? mergePlain(existing, value) : value;
|
|
377
|
+
}
|
|
378
|
+
return out;
|
|
379
|
+
}
|
|
380
|
+
/**
|
|
381
|
+
* Canonical slot-aware merge of a base resource config with host seams.
|
|
382
|
+
* Later seams win over earlier ones; `undefined` values never clobber.
|
|
383
|
+
*
|
|
384
|
+
* Merge semantics (value-driven — no per-slot registry to drift):
|
|
385
|
+
*
|
|
386
|
+
* - **Top-level arrays CONCAT** — `routes`, `middlewares`, `presets`,
|
|
387
|
+
* `routeGuards`, `disabledRoutes`: additive collections, base first.
|
|
388
|
+
* - **Plain objects merge recursively** — `permissions`, `actions`,
|
|
389
|
+
* `schemaOptions` (incl. `fieldRules`), `aggregations`, `customSchemas`,
|
|
390
|
+
* `fields`, `cache`, `events`: per-key composition. Arrays NESTED inside
|
|
391
|
+
* them replace (they're value lists — enums, sort specs), not concat.
|
|
392
|
+
* - **Everything else last-wins** — class instances (`adapter`,
|
|
393
|
+
* `controller`, `queryParser`), functions, scalars.
|
|
394
|
+
*
|
|
395
|
+
* The single `as` at the return is the one documented erasure for the
|
|
396
|
+
* {@link AdapterLike}-widened seam slots — inside the helper so no module
|
|
397
|
+
* author ever writes one (mirrors mongokit's one-cast-at-the-boundary rule).
|
|
398
|
+
*/
|
|
399
|
+
function mergeResourceConfig(base, ...seams) {
|
|
400
|
+
const out = { ...base };
|
|
401
|
+
for (const seam of seams) {
|
|
402
|
+
if (!seam) continue;
|
|
403
|
+
for (const [key, value] of Object.entries(seam)) {
|
|
404
|
+
if (value === void 0) continue;
|
|
405
|
+
const existing = out[key];
|
|
406
|
+
if (Array.isArray(existing) && Array.isArray(value)) out[key] = [...existing, ...value];
|
|
407
|
+
else if (isPlainObject(existing) && isPlainObject(value)) out[key] = mergePlain(existing, value);
|
|
408
|
+
else if (Array.isArray(value)) out[key] = [...value];
|
|
409
|
+
else out[key] = value;
|
|
410
|
+
}
|
|
411
|
+
}
|
|
412
|
+
return out;
|
|
413
|
+
}
|
|
414
|
+
//#endregion
|
|
362
415
|
//#region src/core/defineResource/controller.ts
|
|
363
416
|
/**
|
|
364
417
|
* Phase 4 — pick (or auto-create) the resource's controller.
|
|
@@ -652,7 +705,7 @@ function resolveIdField(config, repository) {
|
|
|
652
705
|
* enabled but no adapter" (a confusing false-positive error).
|
|
653
706
|
*/
|
|
654
707
|
function normalizeResourceConfig(config) {
|
|
655
|
-
return resolveCrudAllowList(expandCustomRoutesOnly(expandReferenceData(config)));
|
|
708
|
+
return resolveCrudAllowList(expandHistory(expandCustomRoutesOnly(expandReferenceData(config))));
|
|
656
709
|
}
|
|
657
710
|
/**
|
|
658
711
|
* Expand the `referenceData: true` shorthand. Reference data is the
|
|
@@ -706,6 +759,61 @@ function expandCustomRoutesOnly(config) {
|
|
|
706
759
|
};
|
|
707
760
|
}
|
|
708
761
|
/**
|
|
762
|
+
* Expand the 2.22 `history: true` shorthand — the per-record change
|
|
763
|
+
* timeline. Injects `GET /:id/history` (audit-entry page for one
|
|
764
|
+
* document, newest-first per the audit store's contract) and implies
|
|
765
|
+
* `audit: true` so the rows the route reads actually get written.
|
|
766
|
+
*
|
|
767
|
+
* The gate defaults STRICTER than reads (history exposes before/after
|
|
768
|
+
* snapshots): resource `update` permission → `get` → route auth.
|
|
769
|
+
* `history: { permissions }` overrides. The flag is CONSUMED here
|
|
770
|
+
* (set to undefined) — later phases and the registry see only its
|
|
771
|
+
* expansion, same contract as `customRoutesOnly`.
|
|
772
|
+
*/
|
|
773
|
+
function expandHistory(config) {
|
|
774
|
+
if (!config.history) return config;
|
|
775
|
+
const opts = config.history === true ? {} : config.history;
|
|
776
|
+
const permissions = opts.permissions ?? config.permissions?.update ?? config.permissions?.get ?? requireAuth();
|
|
777
|
+
const defaultLimit = opts.limit ?? 50;
|
|
778
|
+
const resourceName = config.name;
|
|
779
|
+
const historyRoute = {
|
|
780
|
+
method: "GET",
|
|
781
|
+
path: "/:id/history",
|
|
782
|
+
summary: `Change history for a ${resourceName} record (audit-backed timeline)`,
|
|
783
|
+
raw: true,
|
|
784
|
+
handler: async (request, reply) => {
|
|
785
|
+
const audit = request.server.audit;
|
|
786
|
+
if (!audit?.query || audit._noop === true) return reply.code(503).send({
|
|
787
|
+
code: "history.audit_unavailable",
|
|
788
|
+
message: `history for '${resourceName}' requires auditPlugin to be registered (and enabled)`,
|
|
789
|
+
status: 503
|
|
790
|
+
});
|
|
791
|
+
const { id } = request.params;
|
|
792
|
+
const q = request.query;
|
|
793
|
+
const limit = Math.min(Number(q.limit) > 0 ? Number(q.limit) : defaultLimit, 200);
|
|
794
|
+
const offset = Number(q.offset) > 0 ? Number(q.offset) : 0;
|
|
795
|
+
const entries = await audit.query({
|
|
796
|
+
resource: resourceName,
|
|
797
|
+
documentId: id,
|
|
798
|
+
limit,
|
|
799
|
+
offset
|
|
800
|
+
});
|
|
801
|
+
return reply.send({
|
|
802
|
+
data: entries,
|
|
803
|
+
limit,
|
|
804
|
+
offset
|
|
805
|
+
});
|
|
806
|
+
},
|
|
807
|
+
permissions
|
|
808
|
+
};
|
|
809
|
+
return {
|
|
810
|
+
...config,
|
|
811
|
+
history: void 0,
|
|
812
|
+
audit: config.audit ?? true,
|
|
813
|
+
routes: [...config.routes ?? [], historyRoute]
|
|
814
|
+
};
|
|
815
|
+
}
|
|
816
|
+
/**
|
|
709
817
|
* Normalise the 2.16 `crud:` positive-form allow-list into the canonical
|
|
710
818
|
* `{ disabledRoutes, disableDefaultRoutes }` pair the rest of arc reads.
|
|
711
819
|
*
|
|
@@ -1221,7 +1329,8 @@ function normalizeActionsToRouterConfig(actions, resourceActionPermissions, tag,
|
|
|
1221
1329
|
* Fastify instance via `resource._sharedStateRegisteredOn`.
|
|
1222
1330
|
*/
|
|
1223
1331
|
function buildResourcePlugin(resource) {
|
|
1224
|
-
return async function resourcePlugin(fastify,
|
|
1332
|
+
return async function resourcePlugin(fastify, opts) {
|
|
1333
|
+
await ensureSchemaConverter();
|
|
1225
1334
|
const sharedRoot = fastify.server;
|
|
1226
1335
|
const isFirstMount = !resource._sharedStateRegisteredOn.has(sharedRoot);
|
|
1227
1336
|
if (isFirstMount) resource._sharedStateRegisteredOn.add(sharedRoot);
|
|
@@ -1257,6 +1366,7 @@ function buildResourcePlugin(resource) {
|
|
|
1257
1366
|
fastify.log?.warn?.(`[Arc] Resource '${resource.name}' declares events (${eventNames}) but \`eventPlugin\` is not registered. The events will silently NOT be emitted on CRUD mutations. Either register the plugin (\`await fastify.register(eventPlugin, { transport: ... })\`) or remove the \`events\` declaration from the resource.`);
|
|
1258
1367
|
}
|
|
1259
1368
|
}
|
|
1369
|
+
if (opts?.arcMountRoutes === false) return;
|
|
1260
1370
|
await fastify.register(async (instance) => {
|
|
1261
1371
|
const typedInstance = instance;
|
|
1262
1372
|
let schemas = buildGeneratedCrudSchemas(resource._registryMeta?.openApiSchemas, resource.customSchemas);
|
|
@@ -1286,7 +1396,7 @@ function buildResourcePlugin(resource) {
|
|
|
1286
1396
|
extensions: resource.extensions
|
|
1287
1397
|
});
|
|
1288
1398
|
if (resource.actions && Object.keys(resource.actions).length > 0) {
|
|
1289
|
-
const { createActionRouter } = await import("./createActionRouter-
|
|
1399
|
+
const { createActionRouter } = await import("./createActionRouter-BuOv-m4Y.mjs").then((n) => n.n);
|
|
1290
1400
|
createActionRouter(typedInstance, {
|
|
1291
1401
|
...normalizeActionsToRouterConfig(resource.actions, resource.actionPermissions, resource.tag, resource.permissions, resource.name, typedInstance.log),
|
|
1292
1402
|
resourceName: resource.name,
|
|
@@ -1301,7 +1411,7 @@ function buildResourcePlugin(resource) {
|
|
|
1301
1411
|
});
|
|
1302
1412
|
}
|
|
1303
1413
|
if (resource.aggregations && Object.keys(resource.aggregations).length > 0) {
|
|
1304
|
-
const [{ createAggregationRouter }, { adapterSupportsAggregate, ArcAggregationConfigError }] = await Promise.all([import("./createAggregationRouter-
|
|
1414
|
+
const [{ createAggregationRouter }, { adapterSupportsAggregate, ArcAggregationConfigError }] = await Promise.all([import("./createAggregationRouter-CgJv_BU_.mjs"), import("./validate-C2mGgSXg.mjs").then((n) => n.r)]);
|
|
1305
1415
|
const repoForAgg = resource.controller?.repository ?? resource.repository;
|
|
1306
1416
|
if (!adapterSupportsAggregate(repoForAgg)) {
|
|
1307
1417
|
const undispatchable = Object.entries(resource.aggregations).filter(([, cfg]) => !cfg.materialized).map(([name]) => name);
|
|
@@ -1913,58 +2023,6 @@ function defineResource(config) {
|
|
|
1913
2023
|
return resource;
|
|
1914
2024
|
}
|
|
1915
2025
|
//#endregion
|
|
1916
|
-
//#region src/core/defineResource/seams.ts
|
|
1917
|
-
/** Plain object test — literals only; class instances must replace, not merge. */
|
|
1918
|
-
function isPlainObject(value) {
|
|
1919
|
-
if (value === null || typeof value !== "object" || Array.isArray(value)) return false;
|
|
1920
|
-
const proto = Object.getPrototypeOf(value);
|
|
1921
|
-
return proto === Object.prototype || proto === null;
|
|
1922
|
-
}
|
|
1923
|
-
/** Recursive plain-object merge; nested arrays REPLACE (they're value lists). */
|
|
1924
|
-
function mergePlain(base, next) {
|
|
1925
|
-
const out = { ...base };
|
|
1926
|
-
for (const [key, value] of Object.entries(next)) {
|
|
1927
|
-
if (value === void 0) continue;
|
|
1928
|
-
const existing = out[key];
|
|
1929
|
-
out[key] = isPlainObject(existing) && isPlainObject(value) ? mergePlain(existing, value) : value;
|
|
1930
|
-
}
|
|
1931
|
-
return out;
|
|
1932
|
-
}
|
|
1933
|
-
/**
|
|
1934
|
-
* Canonical slot-aware merge of a base resource config with host seams.
|
|
1935
|
-
* Later seams win over earlier ones; `undefined` values never clobber.
|
|
1936
|
-
*
|
|
1937
|
-
* Merge semantics (value-driven — no per-slot registry to drift):
|
|
1938
|
-
*
|
|
1939
|
-
* - **Top-level arrays CONCAT** — `routes`, `middlewares`, `presets`,
|
|
1940
|
-
* `routeGuards`, `disabledRoutes`: additive collections, base first.
|
|
1941
|
-
* - **Plain objects merge recursively** — `permissions`, `actions`,
|
|
1942
|
-
* `schemaOptions` (incl. `fieldRules`), `aggregations`, `customSchemas`,
|
|
1943
|
-
* `fields`, `cache`, `events`: per-key composition. Arrays NESTED inside
|
|
1944
|
-
* them replace (they're value lists — enums, sort specs), not concat.
|
|
1945
|
-
* - **Everything else last-wins** — class instances (`adapter`,
|
|
1946
|
-
* `controller`, `queryParser`), functions, scalars.
|
|
1947
|
-
*
|
|
1948
|
-
* The single `as` at the return is the one documented erasure for the
|
|
1949
|
-
* {@link AdapterLike}-widened seam slots — inside the helper so no module
|
|
1950
|
-
* author ever writes one (mirrors mongokit's one-cast-at-the-boundary rule).
|
|
1951
|
-
*/
|
|
1952
|
-
function mergeResourceConfig(base, ...seams) {
|
|
1953
|
-
const out = { ...base };
|
|
1954
|
-
for (const seam of seams) {
|
|
1955
|
-
if (!seam) continue;
|
|
1956
|
-
for (const [key, value] of Object.entries(seam)) {
|
|
1957
|
-
if (value === void 0) continue;
|
|
1958
|
-
const existing = out[key];
|
|
1959
|
-
if (Array.isArray(existing) && Array.isArray(value)) out[key] = [...existing, ...value];
|
|
1960
|
-
else if (isPlainObject(existing) && isPlainObject(value)) out[key] = mergePlain(existing, value);
|
|
1961
|
-
else if (Array.isArray(value)) out[key] = [...value];
|
|
1962
|
-
else out[key] = value;
|
|
1963
|
-
}
|
|
1964
|
-
}
|
|
1965
|
-
return out;
|
|
1966
|
-
}
|
|
1967
|
-
//#endregion
|
|
1968
2026
|
//#region src/core/defineResourceVariants.ts
|
|
1969
2027
|
/**
|
|
1970
2028
|
* Define multiple resources from a shared base config and per-variant overrides.
|
|
@@ -2063,4 +2121,4 @@ function getEntityQuery(req) {
|
|
|
2063
2121
|
return { [getEntityIdField(req)]: id };
|
|
2064
2122
|
}
|
|
2065
2123
|
//#endregion
|
|
2066
|
-
export {
|
|
2124
|
+
export { defineResource as a, defineAction as c, defineAggregation as d, defineResourceVariants as i, createCrudRouter as l, getEntityIdField as n, ResourceDefinition as o, getEntityQuery as r, mergeResourceConfig as s, getEntityId as t, createPermissionMiddleware as u };
|
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
import { t as __exportAll } from "./rolldown-runtime-8H4AJuhK.mjs";
|
|
2
2
|
import "./constants-DNre0wRg.mjs";
|
|
3
3
|
import { f as createError } from "./errors-DkO8Nk6-.mjs";
|
|
4
|
-
import { a as selectPluginMw, c as
|
|
5
|
-
import { i as resolvePipelineSteps, n as buildActionPipelineHandler, u as sendControllerResponse } from "./actionPermissions-
|
|
6
|
-
import { n as schemaIRToJsonSchemaBranch, t as normalizeSchemaIR } from "./schemaIR-
|
|
4
|
+
import { a as selectPluginMw, c as buildRouteHooks, f as buildAuthMiddlewareForPermissions, i as resolveRouterPluginMw, n as buildRateLimitConfig, o as buildActionPermissionMw, p as buildArcDecorator, r as buildRouteConfig, t as tryRegisterRoute, u as routeHookOptions } from "./registerRoute-CWO6mdYe.mjs";
|
|
5
|
+
import { i as resolvePipelineSteps, n as buildActionPipelineHandler, u as sendControllerResponse } from "./actionPermissions-lszoJle1.mjs";
|
|
6
|
+
import { n as schemaIRToJsonSchemaBranch, t as normalizeSchemaIR } from "./schemaIR-Ca7ySNPx.mjs";
|
|
7
7
|
//#region src/core/createActionRouter.ts
|
|
8
8
|
var createActionRouter_exports = /* @__PURE__ */ __exportAll({
|
|
9
9
|
buildActionBodySchema: () => buildActionBodySchema,
|
|
@@ -108,19 +108,23 @@ function createActionRouter(fastify, config) {
|
|
|
108
108
|
scopedError.meta = { action: submitted };
|
|
109
109
|
throw scopedError;
|
|
110
110
|
};
|
|
111
|
-
const
|
|
111
|
+
const hooks = buildRouteHooks({
|
|
112
112
|
arcDecorator,
|
|
113
113
|
authMw,
|
|
114
114
|
permissionMw,
|
|
115
115
|
pluginMw: selectPluginMw("POST", pluginMw),
|
|
116
116
|
routeGuards
|
|
117
|
-
})
|
|
117
|
+
});
|
|
118
|
+
const preHandler = [actionValidationFormatter, ...hooks.preHandler];
|
|
118
119
|
tryRegisterRoute(fastify, {
|
|
119
120
|
method: "POST",
|
|
120
121
|
url: urlPath,
|
|
121
122
|
schema: routeSchema,
|
|
122
123
|
attachValidation: true,
|
|
123
|
-
|
|
124
|
+
...routeHookOptions({
|
|
125
|
+
onRequest: hooks.onRequest,
|
|
126
|
+
preHandler
|
|
127
|
+
}),
|
|
124
128
|
...buildRouteConfig(rateLimitConfig, extensions),
|
|
125
129
|
handler: async (req, reply) => {
|
|
126
130
|
const { action, ...data } = req.body;
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { f as createError, l as UnauthorizedError, r as ForbiddenError } from "./errors-DkO8Nk6-.mjs";
|
|
2
|
-
import { a as selectPluginMw, c as
|
|
2
|
+
import { a as selectPluginMw, c as buildRouteHooks, d as buildAuthMiddleware, i as resolveRouterPluginMw, n as buildRateLimitConfig, p as buildArcDecorator, u as routeHookOptions } from "./registerRoute-CWO6mdYe.mjs";
|
|
3
3
|
import { n as validateAggregations } from "./validate-C2mGgSXg.mjs";
|
|
4
4
|
import { t as buildAggregationHandler } from "./buildHandler-D_2ULWxZ.mjs";
|
|
5
5
|
//#region src/core/aggregation/createAggregationRouter.ts
|
|
@@ -48,7 +48,7 @@ function registerOne(fastify, normalized, ctx) {
|
|
|
48
48
|
throw new ForbiddenError(reason ?? "You do not have permission to access this aggregation.");
|
|
49
49
|
}
|
|
50
50
|
};
|
|
51
|
-
const
|
|
51
|
+
const hooks = buildRouteHooks({
|
|
52
52
|
arcDecorator,
|
|
53
53
|
authMw,
|
|
54
54
|
permissionMw,
|
|
@@ -73,7 +73,7 @@ function registerOne(fastify, normalized, ctx) {
|
|
|
73
73
|
method: "GET",
|
|
74
74
|
url: `/aggregations/${name}`,
|
|
75
75
|
schema: routeSchema,
|
|
76
|
-
|
|
76
|
+
...routeHookOptions(hooks),
|
|
77
77
|
...rateLimitConfig ? { config: rateLimitConfig } : {},
|
|
78
78
|
handler: async (req, reply) => {
|
|
79
79
|
try {
|