@classytic/arc 2.31.0 → 2.32.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-De7GqHck.mjs → BaseController-Dk34dxhI.mjs} +16 -13
- package/dist/{EventTransport-9mlTgqzE.mjs → EventTransport-BV10D0oG.mjs} +1 -1
- package/dist/{HookSystem-BcmPyq5I.mjs → HookSystem-vimw5D7Z.mjs} +2 -2
- package/dist/{ResourceRegistry-qq0mn2s9.mjs → ResourceRegistry-BVr7PgA0.mjs} +17 -15
- package/dist/{actionPermissions-BE5NwaQk.mjs → actionPermissions-Dti7ebyv.mjs} +1 -1
- package/dist/audit/index.mjs +2 -2
- package/dist/auth/audit.mjs +1 -1
- package/dist/auth/index.mjs +11 -10
- package/dist/auth/redis-session.mjs +1 -1
- package/dist/{authorizationDecision-ChvUQGG1.mjs → authorizationDecision-__T3EJDA.mjs} +2 -2
- package/dist/{buildHandler-CDrCJzmB.mjs → buildHandler-Dn7rzUJo.mjs} +6 -5
- package/dist/cache/index.mjs +4 -4
- package/dist/{caching-BFHA6Scf.mjs → caching-DFJqHyFs.mjs} +2 -2
- package/dist/cleanup/index.d.mts +36 -14
- package/dist/cleanup/index.mjs +45 -29
- package/dist/cli/commands/describe.mjs +4 -6
- package/dist/cli/commands/docs.mjs +3 -3
- package/dist/cli/commands/doctor.mjs +1 -1
- package/dist/cli/commands/generate.mjs +2 -2
- package/dist/cli/commands/init.mjs +2 -2
- package/dist/cli/commands/introspect.mjs +2 -2
- package/dist/cli/index.mjs +3 -3
- package/dist/{constants-D3Dv4LAV.mjs → constants-J99LGTJI.mjs} +1 -1
- package/dist/context/index.mjs +3 -3
- package/dist/core/index.d.mts +1 -1
- package/dist/core/index.mjs +7 -7
- package/dist/{core-BGpIxINN.mjs → core-BwwmNSp7.mjs} +3 -3
- package/dist/{createActionRouter-CDXEdasl.mjs → createActionRouter-DkJZWo0m.mjs} +14 -11
- package/dist/{createAggregationRouter-KrQDzx-k.mjs → createAggregationRouter-B1exOVD8.mjs} +10 -8
- package/dist/{createApp-Df89BEni.mjs → createApp-Bv0m-Q6_.mjs} +182 -61
- package/dist/{defineAggregation-DbPPS8RI.mjs → defineAggregation-gX4No1gt.mjs} +1 -1
- package/dist/{defineResource-Bv6EtN1x.mjs → defineResource-XFPPUu2c.mjs} +41 -32
- package/dist/discovery/index.mjs +1 -1
- package/dist/docs/index.mjs +3 -3
- package/dist/{elevation-D2l1mh4g.mjs → elevation-DNXXxgjl.mjs} +4 -4
- package/dist/encryption/index.mjs +5 -4
- package/dist/{errorHandler-C5NeD6bT.mjs → errorHandler-B2AZUJUj.mjs} +3 -3
- package/dist/{errors-Cj42AntO.mjs → errors-Bh1KK5ps.mjs} +1 -1
- package/dist/{eventPlugin-CkffEihm.mjs → eventPlugin-Dk_TyXeE.mjs} +8 -7
- package/dist/events/index.d.mts +108 -41
- package/dist/events/index.mjs +166 -142
- package/dist/events/transports/redis-stream-entry.mjs +14 -10
- package/dist/events/transports/redis.mjs +1 -1
- package/dist/factory/index.d.mts +5 -5
- package/dist/factory/index.mjs +131 -8
- package/dist/fastify-augmentation-BppG5bMT.d.mts +179 -0
- package/dist/{fields-CB5Cuzk9.mjs → fields-DEi65pGj.mjs} +6 -11
- package/dist/{generate-EcA6ui4i.mjs → generate-CRSG0jTv.mjs} +2 -2
- package/dist/{headers-3zvE0rM3.mjs → headers-BXjQYbeC.mjs} +2 -2
- package/dist/{health-DjZYjM7H.mjs → health-5XnTa2uo.mjs} +1 -1
- package/dist/hooks/index.mjs +2 -2
- package/dist/idempotency/index.mjs +8 -6
- package/dist/idempotency/redis.mjs +1 -1
- package/dist/{index-Bc2ox8_U.d.mts → index-DXlHwvuE.d.mts} +1 -1
- package/dist/{index-CFThn1K9.d.mts → index-ZWhLTp_s.d.mts} +70 -4
- package/dist/{index-CEJNZFtX.d.mts → index-zq6VrjaB.d.mts} +70 -1
- package/dist/index.d.mts +3 -3
- package/dist/index.mjs +11 -11
- package/dist/{init-BEiNZBsJ.mjs → init-BbfP8aky.mjs} +7 -5
- package/dist/integrations/event-gateway.mjs +2 -2
- package/dist/integrations/index.mjs +1 -1
- package/dist/integrations/jobs/index.mjs +3 -3
- package/dist/integrations/mcp/index.mjs +2 -2
- package/dist/integrations/mcp/testing.mjs +2 -2
- package/dist/integrations/streamline.mjs +3 -3
- package/dist/integrations/webhooks.mjs +1 -1
- package/dist/integrations/websocket-pushref-redis.mjs +1 -1
- package/dist/integrations/websocket-redis.mjs +1 -1
- package/dist/integrations/websocket.mjs +38 -35
- package/dist/{interface-DYzM0q-u.mjs → interface-BP4IJCBO.mjs} +1 -1
- package/dist/{jsonBody-BYbLDi5D.mjs → jsonBody-DDSR2HFl.mjs} +2 -2
- package/dist/{keys-C0_pa1tY.mjs → keys-B-HBTGXl.mjs} +1 -1
- package/dist/{loadResources-yawi9tAE.mjs → loadResources-DisEAmkm.mjs} +4 -3
- package/dist/{loadResourcesFromEntry-BAjYZonx.mjs → loadResourcesFromEntry-D1MJp13f.mjs} +2 -2
- package/dist/logger/index.mjs +1 -1
- package/dist/{mcpPlugin-B_es0hiF.mjs → mcpPlugin-CpfVpKHE.mjs} +32 -31
- package/dist/{memory-DR6wpa9F.mjs → memory-DsuWnj4T.mjs} +5 -5
- package/dist/{metrics-CUdw_wLB.mjs → metrics-DdQL8syd.mjs} +2 -2
- package/dist/middleware/index.mjs +3 -3
- package/dist/migrations/index.mjs +3 -3
- package/dist/{module-0pFJA6YW.mjs → module-Br81am6m.mjs} +1 -1
- package/dist/{multipartBody-wOgj_fnj.mjs → multipartBody-BnkBLN7v.mjs} +3 -3
- package/dist/{openapi-B2L4js1v.mjs → openapi-DIRzvxC0.mjs} +6 -6
- package/dist/outbox-admin/index.d.mts +85 -0
- package/dist/outbox-admin/index.mjs +115 -0
- package/dist/permissions/index.d.mts +2 -2
- package/dist/permissions/index.mjs +6 -6
- package/dist/{permissions-L--QDAir.mjs → permissions-Dl_m0NCe.mjs} +101 -8
- package/dist/{pipe-DRDNsI2T.mjs → pipe-ELHCP3zj.mjs} +3 -5
- package/dist/pipeline/index.mjs +2 -2
- package/dist/{pipeline-UPQFatit.mjs → pipeline-CPA-jx0M.mjs} +17 -15
- package/dist/plugins/index.d.mts +2 -2
- package/dist/plugins/index.mjs +16 -16
- package/dist/plugins/response-cache.mjs +2 -2
- package/dist/plugins/tracing-entry.mjs +2 -2
- package/dist/{pluralize-O0FZgESl.mjs → pluralize-C2ZkKbsn.mjs} +1 -1
- package/dist/{preloadResources-Dz6BjzM6.mjs → preloadResources-CDs87fTw.mjs} +1 -1
- package/dist/{preloadResources-CyoJMSiO.d.mts → preloadResources-T1hmlTEZ.d.mts} +2 -2
- package/dist/presets/filesUpload.mjs +6 -6
- package/dist/presets/index.mjs +2 -2
- package/dist/presets/multiTenant.mjs +3 -3
- package/dist/presets/search.mjs +2 -2
- package/dist/{presets-C4-g4_4P.mjs → presets-Dntd_pUO.mjs} +6 -5
- package/dist/{projection-BHw6xlOQ.mjs → projection-CjHhM5W1.mjs} +2 -2
- package/dist/{queryCachePlugin-BZr8v46E.mjs → queryCachePlugin-Dhps-Ldt.mjs} +6 -6
- package/dist/{rateLimit-CMPY5Qms.mjs → rateLimit-C7mmAGs3.mjs} +2 -2
- package/dist/{rateLimitKey-Dn5JNmkj.mjs → rateLimitKey-CccEFBfk.mjs} +1 -1
- package/dist/{realtime-C5NoxWFY.mjs → realtime-DVvVZZFz.mjs} +10 -10
- package/dist/{registerRoute-CBN84fc7.mjs → registerRoute-B_QrjYZS.mjs} +10 -8
- package/dist/registry/index.d.mts +1 -1
- package/dist/registry/index.mjs +3 -3
- package/dist/{registry-4213U4zb.mjs → registry-DqUmzA9W.mjs} +2 -2
- package/dist/{renewingLease-Dll8mGgc.mjs → renewingLease-BpP9wwj7.mjs} +1 -1
- package/dist/{replyHelpers-DIiZKGUt.mjs → replyHelpers-DS-P0b32.mjs} +2 -2
- package/dist/{repositoryFilter-f9eAfq_l.mjs → repositoryFilter-DotlxG3L.mjs} +1 -1
- package/dist/{requestContext-2TxXOJ7T.mjs → requestContext-B6vQF-sy.mjs} +1 -1
- package/dist/{rolldown-runtime-CUsjNX9R.mjs → rolldown-runtime-BhflRrBs.mjs} +1 -1
- package/dist/{schedules-DdtlICTf.mjs → schedules-C2Q3uv2d.mjs} +3 -3
- package/dist/{schemaConverter-D--wdJM-.mjs → schemaConverter-Crjvczi_.mjs} +1 -1
- package/dist/{schemaIR-Bm2enbnh.mjs → schemaIR-CnyKK_1j.mjs} +2 -2
- package/dist/schemas/index.mjs +1 -1
- package/dist/scim/index.mjs +2 -2
- package/dist/scope/index.mjs +5 -5
- package/dist/{scope-DHx5Fous.mjs → scope-afz_Ieek.mjs} +4 -4
- package/dist/{simpleEqualityMatcher-DjQJ7FKA.mjs → simpleEqualityMatcher-C4KiMWUa.mjs} +1 -1
- package/dist/{sse-CUTo9y0X.mjs → sse-B9P4Y-UI.mjs} +7 -6
- package/dist/{sseStream-B_5XcVVd.mjs → sseStream-B3zb_d1h.mjs} +2 -2
- package/dist/{store-helpers-D1MP_gRG.mjs → store-helpers-DmEMHlPQ.mjs} +1 -1
- package/dist/{streaming-PSZwi8JV.mjs → streaming-DPZdQmDz.mjs} +1 -1
- package/dist/{subscribe-helpers-C0Tb2aUp.mjs → subscribe-helpers-sp5xTDVA.mjs} +2 -2
- package/dist/sync/changelog.mjs +1 -1
- package/dist/testing/cleanupStoreContract.mjs +10 -6
- package/dist/testing/index.d.mts +31 -4
- package/dist/testing/index.mjs +97 -39
- package/dist/testing/outboxStoreContract.d.mts +11 -2
- package/dist/testing/outboxStoreContract.mjs +110 -21
- package/dist/testing/storageContract.mjs +11 -6
- package/dist/{transactionContext-dAfBVUrk.mjs → transactionContext-BvLKvOIL.mjs} +1 -1
- package/dist/{typeGuards-B0ryPo3I.mjs → typeGuards-B_5-hKjJ.mjs} +1 -1
- package/dist/types/index.mjs +1 -1
- package/dist/types/storage.mjs +1 -1
- package/dist/{types-CBgvkp8O.d.mts → types-CT5vu6M0.d.mts} +87 -11
- package/dist/{types-Cys0ko9Z.mjs → types-DXYX7dIR.mjs} +4 -4
- package/dist/usage/index.mjs +0 -0
- package/dist/{userHelpers-ChYJ6CdK.mjs → userHelpers-BNsOXzOd.mjs} +1 -1
- package/dist/utils/index.mjs +10 -10
- package/dist/{utils-vL8BrDE2.mjs → utils-BtOha_Sp.mjs} +3 -3
- package/dist/{validate-_KbifpHF.mjs → validate-PMCPR9El.mjs} +2 -2
- package/dist/{versioning-CHDTor3P.mjs → versioning-CKv2jz_U.mjs} +4 -4
- package/package.json +23 -13
- package/dist/fastify-augmentation-zh0_AYHa.d.mts +0 -85
|
@@ -1,22 +1,22 @@
|
|
|
1
|
-
/*! @classytic/arc v2.
|
|
2
|
-
import { s as DEFAULT_UPDATE_METHOD, t as CRUD_OPERATIONS } from "./constants-
|
|
1
|
+
/*! @classytic/arc v2.32.0 | MIT | https://classytic.com */
|
|
2
|
+
import { s as DEFAULT_UPDATE_METHOD, t as CRUD_OPERATIONS } from "./constants-J99LGTJI.mjs";
|
|
3
3
|
import { arcLog } from "./logger/index.mjs";
|
|
4
|
-
import { r as ForbiddenError } from "./errors-
|
|
5
|
-
import { n as getUserRoles } from "./userHelpers-
|
|
6
|
-
import { b as isElevated, n as PUBLIC_SCOPE, x as isMember } from "./types-
|
|
7
|
-
import { d as DEFAULT_FIELD_WRITE_DENIAL_POLICY, t as BaseController } from "./BaseController-
|
|
8
|
-
import { a as buildRouteHooks, c as buildAuthMiddleware, i as selectPluginMw, n as buildRouteConfig, o as resolveRoutePreHandlers, r as resolveRouterPluginMw, s as routeHookOptions, t as buildRateLimitConfig, u as buildArcDecorator } from "./rateLimit-
|
|
9
|
-
import { i as resolveEffectiveRoles, n as applyFieldWritePermissions } from "./fields-
|
|
10
|
-
import { r as buildCrudPermissionMw, t as tryRegisterRoute } from "./registerRoute-
|
|
11
|
-
import { c as getDefaultCrudSchemas } from "./utils-
|
|
12
|
-
import { n as convertRouteSchema, r as ensureSchemaConverter, t as convertOpenApiSchemas } from "./schemaConverter-
|
|
13
|
-
import { i as pipeUIMessageStreamToReply, r as isReadableStream } from "./streaming-
|
|
14
|
-
import { t as hasEvents } from "./typeGuards-
|
|
15
|
-
import { a as createFastifyHandler, i as createCrudHandlers, n as buildPipelineHandler, o as createRequestContext, r as resolvePipelineSteps } from "./pipeline-
|
|
16
|
-
import {
|
|
17
|
-
import { r as getAvailablePresets, t as applyPresets } from "./presets-
|
|
18
|
-
import { t as pluralize } from "./pluralize-
|
|
19
|
-
import { t as resolveActionPermission } from "./actionPermissions-
|
|
4
|
+
import { r as ForbiddenError } from "./errors-Bh1KK5ps.mjs";
|
|
5
|
+
import { n as getUserRoles } from "./userHelpers-BNsOXzOd.mjs";
|
|
6
|
+
import { b as isElevated, n as PUBLIC_SCOPE, x as isMember } from "./types-DXYX7dIR.mjs";
|
|
7
|
+
import { d as DEFAULT_FIELD_WRITE_DENIAL_POLICY, t as BaseController } from "./BaseController-Dk34dxhI.mjs";
|
|
8
|
+
import { a as buildRouteHooks, c as buildAuthMiddleware, i as selectPluginMw, n as buildRouteConfig, o as resolveRoutePreHandlers, r as resolveRouterPluginMw, s as routeHookOptions, t as buildRateLimitConfig, u as buildArcDecorator } from "./rateLimit-C7mmAGs3.mjs";
|
|
9
|
+
import { i as resolveEffectiveRoles, n as applyFieldWritePermissions } from "./fields-DEi65pGj.mjs";
|
|
10
|
+
import { r as buildCrudPermissionMw, t as tryRegisterRoute } from "./registerRoute-B_QrjYZS.mjs";
|
|
11
|
+
import { c as getDefaultCrudSchemas } from "./utils-BtOha_Sp.mjs";
|
|
12
|
+
import { n as convertRouteSchema, r as ensureSchemaConverter, t as convertOpenApiSchemas } from "./schemaConverter-Crjvczi_.mjs";
|
|
13
|
+
import { i as pipeUIMessageStreamToReply, r as isReadableStream } from "./streaming-DPZdQmDz.mjs";
|
|
14
|
+
import { t as hasEvents } from "./typeGuards-B_5-hKjJ.mjs";
|
|
15
|
+
import { a as createFastifyHandler, i as createCrudHandlers, n as buildPipelineHandler, o as createRequestContext, r as resolvePipelineSteps } from "./pipeline-CPA-jx0M.mjs";
|
|
16
|
+
import { B as requireAuth } from "./permissions-Dl_m0NCe.mjs";
|
|
17
|
+
import { r as getAvailablePresets, t as applyPresets } from "./presets-Dntd_pUO.mjs";
|
|
18
|
+
import { t as pluralize } from "./pluralize-C2ZkKbsn.mjs";
|
|
19
|
+
import { t as resolveActionPermission } from "./actionPermissions-Dti7ebyv.mjs";
|
|
20
20
|
//#region src/core/middlewares/fieldWrite.ts
|
|
21
21
|
/**
|
|
22
22
|
* Build a preHandler that enforces field-write permissions on the request body.
|
|
@@ -42,7 +42,10 @@ function buildFieldWritePreHandler(fieldPermissions, policy = DEFAULT_FIELD_WRIT
|
|
|
42
42
|
if (!body || typeof body !== "object" || Array.isArray(body)) return;
|
|
43
43
|
const scope = request.scope ?? PUBLIC_SCOPE;
|
|
44
44
|
if (isElevated(scope)) return;
|
|
45
|
-
const
|
|
45
|
+
const globalRoles = getUserRoles(request.user);
|
|
46
|
+
const orgRoles = isMember(scope) ? scope.orgRoles : [];
|
|
47
|
+
const effectiveRoles = resolveEffectiveRoles(globalRoles, orgRoles);
|
|
48
|
+
const { body: filtered, deniedFields } = applyFieldWritePermissions(body, fieldPermissions, effectiveRoles);
|
|
46
49
|
if (deniedFields.length > 0 && policy === "reject") throw new ForbiddenError(`Not permitted to write field${deniedFields.length === 1 ? "" : "s"}: ${deniedFields.join(", ")}`);
|
|
47
50
|
request.body = filtered;
|
|
48
51
|
};
|
|
@@ -114,8 +117,9 @@ function createCustomRoutes(fastify, routes, controller, options) {
|
|
|
114
117
|
const fieldWriteMw = buildFieldWritePreHandler(fieldPermissions, onFieldWriteDenied);
|
|
115
118
|
if (fieldWriteMw) customPreHandlers.push(fieldWriteMw);
|
|
116
119
|
}
|
|
120
|
+
const preAuthHandlers = route.preAuth ?? [];
|
|
117
121
|
const hooks = buildRouteHooks({
|
|
118
|
-
preAuth:
|
|
122
|
+
preAuth: preAuthHandlers,
|
|
119
123
|
arcDecorator,
|
|
120
124
|
authMw: buildAuthMiddleware(fastify, route.permissions),
|
|
121
125
|
permissionMw: buildCrudPermissionMw(route.permissions, resourceName, opName),
|
|
@@ -287,7 +291,8 @@ function buildCrudHandlers(ctrl, pipeline, resourceName) {
|
|
|
287
291
|
for (const op of CRUD_OPERATIONS) {
|
|
288
292
|
const steps = resolvePipelineSteps(pipeline, op);
|
|
289
293
|
if (steps.length === 0) continue;
|
|
290
|
-
|
|
294
|
+
const method = ctrl[op].bind(ctrl);
|
|
295
|
+
wrapped[op] = buildPipelineHandler(method, steps, op, resourceName);
|
|
291
296
|
}
|
|
292
297
|
return wrapped;
|
|
293
298
|
}
|
|
@@ -1226,11 +1231,13 @@ function buildResourcePlugin(resource) {
|
|
|
1226
1231
|
handler: hook.handler,
|
|
1227
1232
|
priority: hook.priority
|
|
1228
1233
|
});
|
|
1229
|
-
if (isFirstMount && resource.cache?.invalidateOn)
|
|
1230
|
-
pattern,
|
|
1231
|
-
|
|
1232
|
-
|
|
1233
|
-
|
|
1234
|
+
if (isFirstMount && resource.cache?.invalidateOn) {
|
|
1235
|
+
if (fastify.registerCacheInvalidationRule) for (const [pattern, tags] of Object.entries(resource.cache.invalidateOn)) fastify.registerCacheInvalidationRule({
|
|
1236
|
+
pattern,
|
|
1237
|
+
tags
|
|
1238
|
+
});
|
|
1239
|
+
else fastify.log?.warn?.(`[Arc] Resource '${resource.name}' declares \`cache.invalidateOn\` but \`queryCachePlugin\` is not registered. Cache-invalidation rules are silently ignored. Either register the plugin (\`await fastify.register(queryCachePlugin, ...)\`) or remove the \`cache.invalidateOn\` config to clean up the resource definition.`);
|
|
1240
|
+
}
|
|
1234
1241
|
if (isFirstMount && resource.audit) {
|
|
1235
1242
|
if (!(fastify.hasDecorator("audit") && typeof fastify.audit?.create === "function" && fastify.audit?._noop !== true)) fastify.log?.warn?.(`[Arc] Resource '${resource.name}' declares \`audit: ${JSON.stringify(resource.audit)}\` but \`auditPlugin\` is not registered (or is registered with a noop logger). Audit events for this resource will NOT be recorded. Either register \`auditPlugin\` (\`await fastify.register(auditPlugin, { store: ... })\`) or remove the \`audit\` flag from the resource definition.`);
|
|
1236
1243
|
}
|
|
@@ -1271,7 +1278,7 @@ function buildResourcePlugin(resource) {
|
|
|
1271
1278
|
extensions: resource.extensions
|
|
1272
1279
|
});
|
|
1273
1280
|
if (resource.actions && Object.keys(resource.actions).length > 0) {
|
|
1274
|
-
const { createActionRouter } = await import("./createActionRouter-
|
|
1281
|
+
const { createActionRouter } = await import("./createActionRouter-DkJZWo0m.mjs").then((n) => n.n);
|
|
1275
1282
|
createActionRouter(typedInstance, {
|
|
1276
1283
|
...normalizeActionsToRouterConfig(resource.actions, resource.actionPermissions, resource.tag, resource.permissions, resource.name, typedInstance.log),
|
|
1277
1284
|
resourceName: resource.name,
|
|
@@ -1286,7 +1293,7 @@ function buildResourcePlugin(resource) {
|
|
|
1286
1293
|
});
|
|
1287
1294
|
}
|
|
1288
1295
|
if (resource.aggregations && Object.keys(resource.aggregations).length > 0) {
|
|
1289
|
-
const [{ createAggregationRouter }, { adapterSupportsAggregate, ArcAggregationConfigError }] = await Promise.all([import("./createAggregationRouter-
|
|
1296
|
+
const [{ createAggregationRouter }, { adapterSupportsAggregate, ArcAggregationConfigError }] = await Promise.all([import("./createAggregationRouter-B1exOVD8.mjs"), import("./validate-PMCPR9El.mjs").then((n) => n.r)]);
|
|
1290
1297
|
const repoForAgg = resource.controller?.repository ?? resource.repository;
|
|
1291
1298
|
if (!adapterSupportsAggregate(repoForAgg)) {
|
|
1292
1299
|
const undispatchable = Object.entries(resource.aggregations).filter(([, cfg]) => !cfg.materialized).map(([name]) => name);
|
|
@@ -1486,10 +1493,12 @@ var ResourceDefinition = class {
|
|
|
1486
1493
|
_validateControllerMethods() {
|
|
1487
1494
|
const errors = [];
|
|
1488
1495
|
const enabledCrudRoutes = CRUD_OPERATIONS.filter((route) => !this.disabledRoutes.includes(route));
|
|
1489
|
-
if (!this.disableDefaultRoutes && enabledCrudRoutes.length > 0)
|
|
1490
|
-
|
|
1491
|
-
|
|
1492
|
-
|
|
1496
|
+
if (!this.disableDefaultRoutes && enabledCrudRoutes.length > 0) {
|
|
1497
|
+
if (!this.controller) errors.push("Controller is required when CRUD routes are enabled");
|
|
1498
|
+
else {
|
|
1499
|
+
const ctrl = this.controller;
|
|
1500
|
+
for (const method of enabledCrudRoutes) if (typeof ctrl[method] !== "function") errors.push(`CRUD method '${method}' not found on controller`);
|
|
1501
|
+
}
|
|
1493
1502
|
}
|
|
1494
1503
|
for (const route of this.routes) {
|
|
1495
1504
|
if (typeof route.handler !== "string") continue;
|
package/dist/discovery/index.mjs
CHANGED
package/dist/docs/index.mjs
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
/*! @classytic/arc v2.
|
|
2
|
-
import { n as getUserRoles } from "../userHelpers-
|
|
3
|
-
import { n as openApiPlugin, r as openapi_default, t as buildOpenApiSpec } from "../openapi-
|
|
1
|
+
/*! @classytic/arc v2.32.0 | MIT | https://classytic.com */
|
|
2
|
+
import { n as getUserRoles } from "../userHelpers-BNsOXzOd.mjs";
|
|
3
|
+
import { n as openApiPlugin, r as openapi_default, t as buildOpenApiSpec } from "../openapi-DIRzvxC0.mjs";
|
|
4
4
|
import fp from "fastify-plugin";
|
|
5
5
|
//#region src/docs/scalar.ts
|
|
6
6
|
const scalarPlugin = async (fastify, opts = {}) => {
|
|
@@ -1,8 +1,8 @@
|
|
|
1
|
-
/*! @classytic/arc v2.
|
|
2
|
-
import { t as __exportAll } from "./rolldown-runtime-
|
|
1
|
+
/*! @classytic/arc v2.32.0 | MIT | https://classytic.com */
|
|
2
|
+
import { t as __exportAll } from "./rolldown-runtime-BhflRrBs.mjs";
|
|
3
3
|
import { arcLog } from "./logger/index.mjs";
|
|
4
|
-
import { n as requireSingleHeaderValue } from "./headers-
|
|
5
|
-
import { n as getUserRoles } from "./userHelpers-
|
|
4
|
+
import { n as requireSingleHeaderValue } from "./headers-BXjQYbeC.mjs";
|
|
5
|
+
import { n as getUserRoles } from "./userHelpers-BNsOXzOd.mjs";
|
|
6
6
|
import fp from "fastify-plugin";
|
|
7
7
|
//#region src/scope/elevation.ts
|
|
8
8
|
var elevation_exports = /* @__PURE__ */ __exportAll({
|
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
/*! @classytic/arc v2.
|
|
2
|
-
import { t as ArcError } from "../errors-
|
|
3
|
-
import { t as parseJsonBody } from "../jsonBody-
|
|
1
|
+
/*! @classytic/arc v2.32.0 | MIT | https://classytic.com */
|
|
2
|
+
import { t as ArcError } from "../errors-Bh1KK5ps.mjs";
|
|
3
|
+
import { t as parseJsonBody } from "../jsonBody-DDSR2HFl.mjs";
|
|
4
4
|
import { createCipheriv, createDecipheriv, randomBytes } from "node:crypto";
|
|
5
5
|
import fp from "fastify-plugin";
|
|
6
6
|
//#region src/encryption/fieldCipher.ts
|
|
@@ -71,7 +71,8 @@ function parseFieldEnvelope(token) {
|
|
|
71
71
|
}
|
|
72
72
|
/** Decrypt a parsed `arc.v1` envelope with the resolved key. Throws on tamper. */
|
|
73
73
|
function decryptField(envelope, key) {
|
|
74
|
-
const
|
|
74
|
+
const secret = toKeyBuffer(key);
|
|
75
|
+
const decipher = createDecipheriv("aes-256-gcm", secret, envelope.iv);
|
|
75
76
|
decipher.setAuthTag(envelope.tag);
|
|
76
77
|
return Buffer.concat([decipher.update(envelope.ciphertext), decipher.final()]).toString("utf8");
|
|
77
78
|
}
|
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
/*! @classytic/arc v2.
|
|
2
|
-
import { t as __exportAll } from "./rolldown-runtime-
|
|
3
|
-
import { m as statusToArcCode, p as isArcError } from "./errors-
|
|
1
|
+
/*! @classytic/arc v2.32.0 | MIT | https://classytic.com */
|
|
2
|
+
import { t as __exportAll } from "./rolldown-runtime-BhflRrBs.mjs";
|
|
3
|
+
import { m as statusToArcCode, p as isArcError } from "./errors-Bh1KK5ps.mjs";
|
|
4
4
|
import { isHttpError, toErrorContract } from "@classytic/repo-core/errors";
|
|
5
5
|
import fp from "fastify-plugin";
|
|
6
6
|
//#region src/plugins/errorHandler.ts
|
|
@@ -1,9 +1,9 @@
|
|
|
1
|
-
/*! @classytic/arc v2.
|
|
2
|
-
import { t as __exportAll } from "./rolldown-runtime-
|
|
1
|
+
/*! @classytic/arc v2.32.0 | MIT | https://classytic.com */
|
|
2
|
+
import { t as __exportAll } from "./rolldown-runtime-BhflRrBs.mjs";
|
|
3
3
|
import { arcLog } from "./logger/index.mjs";
|
|
4
|
-
import { d as createDomainError } from "./errors-
|
|
5
|
-
import { n as requestContext } from "./requestContext-
|
|
6
|
-
import { n as createEvent, t as MemoryEventTransport } from "./EventTransport-
|
|
4
|
+
import { d as createDomainError } from "./errors-Bh1KK5ps.mjs";
|
|
5
|
+
import { n as requestContext } from "./requestContext-B6vQF-sy.mjs";
|
|
6
|
+
import { n as createEvent, t as MemoryEventTransport } from "./EventTransport-BV10D0oG.mjs";
|
|
7
7
|
import fp from "fastify-plugin";
|
|
8
8
|
//#region src/events/retry.ts
|
|
9
9
|
/**
|
|
@@ -139,10 +139,11 @@ const eventPlugin = async (fastify, opts = {}) => {
|
|
|
139
139
|
if (type.startsWith("$") && type !== "$deadLetter") throw new Error(`[Arc Events] Event type '${type}' uses reserved '$' prefix`);
|
|
140
140
|
if (type.length > 256) throw new Error("[Arc Events] Event type exceeds 256 characters");
|
|
141
141
|
const store = requestContext.get();
|
|
142
|
-
const
|
|
142
|
+
const enrichedMeta = {
|
|
143
143
|
...store?.requestId && !meta?.correlationId ? { correlationId: store.requestId } : {},
|
|
144
144
|
...meta
|
|
145
|
-
}
|
|
145
|
+
};
|
|
146
|
+
const event = createEvent(type, payload, enrichedMeta);
|
|
146
147
|
if (warnOnDuplicate && event.meta.correlationId) {
|
|
147
148
|
const now = Date.now();
|
|
148
149
|
evictExpiredPublishes(now);
|
package/dist/events/index.d.mts
CHANGED
|
@@ -1,9 +1,12 @@
|
|
|
1
1
|
|
|
2
2
|
import { a as EventTransport, i as EventLogger, n as DomainEvent, o as MemoryEventTransport, r as EventHandler, s as MemoryEventTransportOptions, t as DeadLetteredEvent } from "../EventTransport-2uVV34WU.mjs";
|
|
3
|
+
import { t as ArcModule } from "../types-CT5vu6M0.mjs";
|
|
3
4
|
import { a as withRetry, c as EventDefinitionOutput, d as EventSchema, f as ValidationResult, i as createDeadLetterPublisher, l as EventRegistry, m as defineEvent, n as eventPlugin, o as CustomValidator, p as createEventRegistry, r as RetryOptions, s as EventDefinitionInput, t as EventPluginOptions, u as EventRegistryOptions } from "../eventPlugin-GF5nnBIq.mjs";
|
|
4
5
|
import { RedisEventTransportOptions, RedisLike } from "./transports/redis.mjs";
|
|
5
6
|
import { r as RedisStreamTransportOptions, t as RedisStreamLike } from "../redis-stream-qN6A-RFv.mjs";
|
|
6
|
-
import {
|
|
7
|
+
import { FastifyInstance } from "fastify";
|
|
8
|
+
import { MemoryOutboxStore } from "@classytic/primitives/memory-outbox";
|
|
9
|
+
import { OutboxFailurePolicy, OutboxStore, OutboxWriteOptions } from "@classytic/primitives/outbox";
|
|
7
10
|
import { RepositoryLike } from "@classytic/repo-core/adapter";
|
|
8
11
|
//#region src/events/eventTypes.d.ts
|
|
9
12
|
/**
|
|
@@ -56,44 +59,6 @@ declare const CACHE_EVENTS: Readonly<{
|
|
|
56
59
|
/** Type for cache event names */
|
|
57
60
|
type CacheEvent = (typeof CACHE_EVENTS)[keyof typeof CACHE_EVENTS];
|
|
58
61
|
//#endregion
|
|
59
|
-
//#region src/events/memory-outbox.d.ts
|
|
60
|
-
interface MemoryEntry {
|
|
61
|
-
event: DomainEvent;
|
|
62
|
-
status: "pending" | "delivered" | "dead_letter";
|
|
63
|
-
attempts: number;
|
|
64
|
-
visibleAt: number;
|
|
65
|
-
leaseOwner: string | null;
|
|
66
|
-
leaseExpiresAt: number;
|
|
67
|
-
deliveredAt: number | null;
|
|
68
|
-
firstFailedAt: number | null;
|
|
69
|
-
lastFailedAt: number | null;
|
|
70
|
-
lastError: OutboxErrorInfo | null;
|
|
71
|
-
dedupeKey?: string;
|
|
72
|
-
}
|
|
73
|
-
declare class MemoryOutboxStore implements OutboxStore {
|
|
74
|
-
private readonly entries;
|
|
75
|
-
private readonly seenDedupeKeys;
|
|
76
|
-
/**
|
|
77
|
-
* Drop every entry (including delivered / failed / dead-lettered rows) and
|
|
78
|
-
* the dedupe-key memory.
|
|
79
|
-
*
|
|
80
|
-
* For tests only — it is the in-memory analogue of truncating the table, and
|
|
81
|
-
* exists so a suite can isolate cases against ONE store instance (the
|
|
82
|
-
* `reset` hook of `runOutboxStoreContract`). Not part of `OutboxStore`; a
|
|
83
|
-
* production store must never expose this.
|
|
84
|
-
*/
|
|
85
|
-
clear(): void;
|
|
86
|
-
save(event: DomainEvent, options?: OutboxWriteOptions): Promise<void>;
|
|
87
|
-
getPending(limit: number): Promise<DomainEvent[]>;
|
|
88
|
-
claimPending(options?: OutboxClaimOptions): Promise<DomainEvent[]>;
|
|
89
|
-
acknowledge(eventId: string, options?: OutboxAcknowledgeOptions): Promise<void>;
|
|
90
|
-
fail(eventId: string, error: OutboxErrorInfo, options?: OutboxFailOptions): Promise<void>;
|
|
91
|
-
getDeadLettered(limit: number): Promise<DeadLetteredEvent[]>;
|
|
92
|
-
purge(olderThanMs: number): Promise<number>;
|
|
93
|
-
/** Test helper: inspect entry by id */
|
|
94
|
-
_getEntry(eventId: string): Readonly<MemoryEntry> | undefined;
|
|
95
|
-
}
|
|
96
|
-
//#endregion
|
|
97
62
|
//#region src/events/outbox/backoff.d.ts
|
|
98
63
|
/**
|
|
99
64
|
* Exponential backoff for outbox failure policies — pure date math shared
|
|
@@ -405,6 +370,97 @@ declare class EventOutbox {
|
|
|
405
370
|
purge(olderThanMs?: number): Promise<number>;
|
|
406
371
|
}
|
|
407
372
|
//#endregion
|
|
373
|
+
//#region src/events/outbox-module.d.ts
|
|
374
|
+
interface OutboxModuleOptions {
|
|
375
|
+
/**
|
|
376
|
+
* The durable store. Supply it directly, or as a factory when it needs the booted app
|
|
377
|
+
* (a mongoose connection decorated by an earlier plugin, for instance).
|
|
378
|
+
*/
|
|
379
|
+
store: OutboxStore | ((fastify: FastifyInstance) => OutboxStore);
|
|
380
|
+
/**
|
|
381
|
+
* Where relayed events are published. Omit to use `fastify.events` — the ordinary case; pass
|
|
382
|
+
* one only when the outbox must publish somewhere other than the app's own transport.
|
|
383
|
+
*/
|
|
384
|
+
transport?: EventTransport | ((fastify: FastifyInstance) => EventTransport);
|
|
385
|
+
/** Module name, and therefore the `dependsOn` key. Default `'outbox'`. */
|
|
386
|
+
name?: string;
|
|
387
|
+
/** Relay interval in ms. Default 5000. */
|
|
388
|
+
relayEveryMs?: number;
|
|
389
|
+
/**
|
|
390
|
+
* Rows per relay tick. Left to the store's own default when omitted — a batch size is a
|
|
391
|
+
* throughput tuning decision, not something this module should invent.
|
|
392
|
+
*/
|
|
393
|
+
batchSize?: number;
|
|
394
|
+
/** Lease duration for a claimed batch, in ms. */
|
|
395
|
+
leaseMs?: number;
|
|
396
|
+
/**
|
|
397
|
+
* Consumer id recorded on the lease. Default `'arc-outbox-relay'`.
|
|
398
|
+
*
|
|
399
|
+
* Give each relaying process a DISTINCT id when several run: the lease is what stops two
|
|
400
|
+
* relays publishing the same event, and it can only do that if they are distinguishable.
|
|
401
|
+
*/
|
|
402
|
+
consumerId?: string;
|
|
403
|
+
/**
|
|
404
|
+
* Called for every non-fatal relay problem — publish failures, ownership mismatches,
|
|
405
|
+
* acknowledge failures.
|
|
406
|
+
*
|
|
407
|
+
* **Wire this.** Silence here is the failure mode that matters: the relay keeps ticking, rows
|
|
408
|
+
* keep failing, and the only symptom is revenue that never posted.
|
|
409
|
+
*/
|
|
410
|
+
onError?: OutboxRelayErrorHandler;
|
|
411
|
+
/**
|
|
412
|
+
* Retry / dead-letter policy for a failed publish — `({ attempts }) => { retryAt } |
|
|
413
|
+
* { deadLetter: true }`.
|
|
414
|
+
*
|
|
415
|
+
* Omitting it takes `EventOutbox`'s default. Exposed because a real host had one and losing it
|
|
416
|
+
* on adoption would be a silent downgrade: without a policy a permanently-failing row either
|
|
417
|
+
* retries forever or dead-letters on the first blip, and both are wrong for money. A typical
|
|
418
|
+
* shape is exponential backoff up to N attempts, then dead-letter.
|
|
419
|
+
*/
|
|
420
|
+
failurePolicy?: OutboxFailurePolicy;
|
|
421
|
+
/**
|
|
422
|
+
* Run one relay pass at boot. Default `true` — a process that restarts with a backlog should
|
|
423
|
+
* drain it immediately rather than waiting a full interval, because that backlog is precisely
|
|
424
|
+
* the events the previous process failed to publish.
|
|
425
|
+
*/
|
|
426
|
+
runOnStart?: boolean;
|
|
427
|
+
}
|
|
428
|
+
/** What the module exports at `fastify.arc.modules.<name>`. */
|
|
429
|
+
interface OutboxModuleExports {
|
|
430
|
+
/** The store, for consumers that write events transactionally. */
|
|
431
|
+
store: OutboxStore;
|
|
432
|
+
/** The relay, for a host that wants to drain on demand (tests, admin actions). */
|
|
433
|
+
relay: EventOutbox;
|
|
434
|
+
}
|
|
435
|
+
/**
|
|
436
|
+
* Compose the outbox as a module.
|
|
437
|
+
*
|
|
438
|
+
* ```ts
|
|
439
|
+
* // host
|
|
440
|
+
* const model = createOutboxModel(connection, { visibleAtField: 'nextVisibleAt' });
|
|
441
|
+
* createApp({ modules: [
|
|
442
|
+
* createOutboxModule({
|
|
443
|
+
* store: repositoryAsOutboxStore(new Repository(model), { visibleAtField: 'nextVisibleAt' }),
|
|
444
|
+
* onError: (err, ctx) => app.log.error({ err, ...ctx }, 'outbox relay'),
|
|
445
|
+
* }),
|
|
446
|
+
* …
|
|
447
|
+
* ]});
|
|
448
|
+
*
|
|
449
|
+
* // consumer module
|
|
450
|
+
* defineModule({
|
|
451
|
+
* name: 'order',
|
|
452
|
+
* dependsOn: ['outbox'],
|
|
453
|
+
* bootstrap: (f) => createOrder({
|
|
454
|
+
* outbox: getModuleExports<OutboxModuleExports>(f, 'outbox').store,
|
|
455
|
+
* }),
|
|
456
|
+
* });
|
|
457
|
+
* ```
|
|
458
|
+
*
|
|
459
|
+
* `dependsOn` is doing real work there: it both orders the bootstraps and makes a missing outbox a
|
|
460
|
+
* boot error naming the module that needed it.
|
|
461
|
+
*/
|
|
462
|
+
declare function createOutboxModule(options: OutboxModuleOptions): ArcModule<OutboxModuleExports>;
|
|
463
|
+
//#endregion
|
|
408
464
|
//#region src/events/repository-outbox-adapter.d.ts
|
|
409
465
|
interface RepositoryOutboxStoreOptions {
|
|
410
466
|
/**
|
|
@@ -422,7 +478,18 @@ interface RepositoryOutboxStoreOptions {
|
|
|
422
478
|
*/
|
|
423
479
|
readonly visibleAtField?: string;
|
|
424
480
|
}
|
|
425
|
-
|
|
481
|
+
/**
|
|
482
|
+
* What this adapter GUARANTEES, as opposed to what `OutboxStore` permits.
|
|
483
|
+
*
|
|
484
|
+
* Most of the contract is optional so a minimal or non-relational store can implement
|
|
485
|
+
* the floor and no more. This adapter implements all of it, and saying so in the type
|
|
486
|
+
* is not cosmetic: with a plain `OutboxStore` return every caller of `getDeadLettered`,
|
|
487
|
+
* `requeue` or `countByStatus` has to write `store.requeue?.(id)`, and the `?.` silently
|
|
488
|
+
* evaluates to `undefined` if the method ever disappears — an operator's replay that
|
|
489
|
+
* quietly does nothing and reports success.
|
|
490
|
+
*/
|
|
491
|
+
type RepositoryOutboxStore = OutboxStore & Required<Pick<OutboxStore, "fail" | "getDeadLettered" | "purge" | "requeue" | "countByStatus" | "oldestPendingAgeMs">>;
|
|
492
|
+
declare function repositoryAsOutboxStore(repository: RepositoryLike, options?: RepositoryOutboxStoreOptions): RepositoryOutboxStore;
|
|
426
493
|
//#endregion
|
|
427
494
|
//#region src/events/subscribe-helpers.d.ts
|
|
428
495
|
/**
|
|
@@ -583,4 +650,4 @@ interface FastifyEventBus {
|
|
|
583
650
|
};
|
|
584
651
|
}
|
|
585
652
|
//#endregion
|
|
586
|
-
export { ARC_LIFECYCLE_EVENTS, type ArcLifecycleEvent, CACHE_EVENTS, CRUD_EVENT_SUFFIXES, type CacheEvent, type CrudEventSuffix, type CustomValidator, type EventDefinitionInput, type EventDefinitionOutput, EventOutbox, type EventOutboxOptions, type EventPluginOptions, type EventRegistry, type EventRegistryOptions, type EventSchema, type ExponentialBackoffOptions, MemoryEventTransport, type MemoryEventTransportOptions, MemoryOutboxStore, type OutboxRelayErrorHandler, type OutboxRelayErrorKind, type PayloadOf, type RedisEventTransportOptions, type RedisLike, type RedisStreamLike, type RedisStreamTransportOptions, type RelayResult, type RepositoryOutboxStoreOptions, type RetryOptions, type ValidationResult, type WrapWithBoundaryOptions, type WrapWithSchemaOptions, createDeadLetterPublisher, createEventRegistry, crudEventType, defineEvent, eventPlugin, exponentialBackoff, repositoryAsOutboxStore, subscribeWithBoundary, subscribeWithSchema, withRetry, wrapWithBoundary, wrapWithSchema };
|
|
653
|
+
export { ARC_LIFECYCLE_EVENTS, type ArcLifecycleEvent, CACHE_EVENTS, CRUD_EVENT_SUFFIXES, type CacheEvent, type CrudEventSuffix, type CustomValidator, type EventDefinitionInput, type EventDefinitionOutput, EventOutbox, type EventOutboxOptions, type EventPluginOptions, type EventRegistry, type EventRegistryOptions, type EventSchema, type ExponentialBackoffOptions, MemoryEventTransport, type MemoryEventTransportOptions, MemoryOutboxStore, type OutboxModuleExports, type OutboxModuleOptions, type OutboxRelayErrorHandler, type OutboxRelayErrorKind, type PayloadOf, type RedisEventTransportOptions, type RedisLike, type RedisStreamLike, type RedisStreamTransportOptions, type RelayResult, type RepositoryOutboxStore, type RepositoryOutboxStoreOptions, type RetryOptions, type ValidationResult, type WrapWithBoundaryOptions, type WrapWithSchemaOptions, createDeadLetterPublisher, createEventRegistry, createOutboxModule, crudEventType, defineEvent, eventPlugin, exponentialBackoff, repositoryAsOutboxStore, subscribeWithBoundary, subscribeWithSchema, withRetry, wrapWithBoundary, wrapWithSchema };
|