@classytic/arc 2.11.3 → 2.13.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +27 -18
- package/dist/{BaseController-swXruJ2_.mjs → BaseController-DX_T-bDB.mjs} +388 -423
- package/dist/EventTransport-CT_52aWU.d.mts +34 -0
- package/dist/EventTransport-DLWoUMHy.mjs +103 -0
- package/dist/{QueryCache-DOBNHBE0.d.mts → QueryCache-D41bfdBB.d.mts} +1 -1
- package/dist/{ResourceRegistry-DkAeAuTX.mjs → ResourceRegistry-CTERg_2x.mjs} +139 -66
- package/dist/audit/index.d.mts +2 -2
- package/dist/audit/index.mjs +1 -1
- package/dist/auth/audit.d.mts +199 -0
- package/dist/auth/audit.mjs +288 -0
- package/dist/auth/index.d.mts +5 -5
- package/dist/auth/index.mjs +117 -191
- package/dist/auth/redis-session.d.mts +1 -1
- package/dist/{betterAuthOpenApi-DwxtK3uG.mjs → betterAuthOpenApi--M_i87dQ.mjs} +1 -1
- package/dist/buildHandler-olo-gt94.mjs +610 -0
- package/dist/cache/index.d.mts +3 -3
- package/dist/cache/index.mjs +3 -3
- package/dist/cli/commands/describe.d.mts +89 -13
- package/dist/cli/commands/describe.mjs +56 -2
- package/dist/cli/commands/docs.mjs +2 -2
- package/dist/cli/commands/generate.mjs +147 -48
- package/dist/cli/commands/init.d.mts +13 -0
- package/dist/cli/commands/init.mjs +237 -112
- package/dist/cli/commands/introspect.mjs +8 -1
- package/dist/context/index.mjs +1 -1
- package/dist/core/index.d.mts +3 -3
- package/dist/core/index.mjs +5 -5
- package/dist/core-D72ia0EH.mjs +1399 -0
- package/dist/{createActionRouter-u3ql2EDo.mjs → createActionRouter-CEvzKcy8.mjs} +7 -20
- package/dist/createAggregationRouter-CyecOxnO.mjs +114 -0
- package/dist/{createApp-BFxtdKy6.mjs → createApp-XX2-N0Yd.mjs} +31 -27
- package/dist/defineEvent-D5h7EvAx.mjs +188 -0
- package/dist/docs/index.d.mts +2 -2
- package/dist/docs/index.mjs +2 -2
- package/dist/{elevation-DOFoxoDs.mjs → elevation-DgoeTyfX.mjs} +1 -1
- package/dist/errorHandler-Bk-AGhkU.mjs +174 -0
- package/dist/errorHandler-DFr45ZG4.d.mts +45 -0
- package/dist/errors-j4aJm1Wg.mjs +184 -0
- package/dist/{eventPlugin-KrFIQ097.mjs → eventPlugin-CaKTYkYM.mjs} +35 -137
- package/dist/{eventPlugin-CUNjYYRY.d.mts → eventPlugin-qXpqTebY.d.mts} +57 -7
- package/dist/events/index.d.mts +164 -5
- package/dist/events/index.mjs +133 -209
- package/dist/events/transports/redis-stream-entry.d.mts +1 -1
- package/dist/events/transports/redis-stream-entry.mjs +204 -31
- package/dist/events/transports/redis.d.mts +1 -1
- package/dist/factory/index.d.mts +2 -2
- package/dist/factory/index.mjs +2 -2
- package/dist/{fields-C8Y0XLAu.d.mts → fields-COhcH3fk.d.mts} +23 -2
- package/dist/hooks/index.d.mts +1 -1
- package/dist/hooks/index.mjs +1 -1
- package/dist/idempotency/index.d.mts +3 -3
- package/dist/idempotency/index.mjs +1 -20
- package/dist/idempotency/redis.d.mts +1 -1
- package/dist/idempotency/redis.mjs +1 -1
- package/dist/{index-BYCqHCVu.d.mts → index-BTqLEvhu.d.mts} +164 -4
- package/dist/{index-6u4_Gg6G.d.mts → index-BtW7qYwa.d.mts} +661 -281
- package/dist/{index-BdXnTPRj.d.mts → index-Ds61mrJE.d.mts} +50 -4
- package/dist/{index-DdQ3O9Pg.d.mts → index-Dz5IKsrE.d.mts} +360 -219
- package/dist/index.d.mts +6 -7
- package/dist/index.mjs +9 -10
- package/dist/integrations/event-gateway.d.mts +2 -2
- package/dist/integrations/event-gateway.mjs +1 -1
- package/dist/integrations/index.d.mts +2 -2
- 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/integrations/streamline.d.mts +60 -11
- package/dist/integrations/streamline.mjs +75 -85
- package/dist/integrations/websocket-redis.d.mts +1 -1
- package/dist/integrations/websocket.d.mts +1 -1
- package/dist/integrations/websocket.mjs +2 -8
- package/dist/middleware/index.d.mts +1 -1
- package/dist/middleware/index.mjs +2 -2
- package/dist/migrations/index.d.mts +23 -3
- package/dist/migrations/index.mjs +0 -7
- package/dist/{multipartBody-CvTR1Un6.mjs → multipartBody-BOvVSVCD.mjs} +11 -8
- package/dist/{openapi-BGUn7Ki1.mjs → openapi-CiOMVW1p.mjs} +143 -13
- package/dist/org/index.d.mts +2 -2
- package/dist/org/index.mjs +1 -1
- package/dist/permissions/index.d.mts +3 -3
- package/dist/permissions/index.mjs +3 -3
- package/dist/{permissions-gd_aUWrR.mjs → permissions-ohQyv50e.mjs} +404 -176
- package/dist/{pipe-DVoIheVC.mjs → pipe-Zr0KXjQe.mjs} +1 -1
- package/dist/pipeline/index.d.mts +1 -1
- package/dist/pipeline/index.mjs +1 -1
- package/dist/plugins/index.d.mts +18 -33
- package/dist/plugins/index.mjs +33 -13
- package/dist/plugins/response-cache.mjs +1 -1
- package/dist/plugins/tracing-entry.d.mts +1 -1
- package/dist/plugins/tracing-entry.mjs +1 -1
- package/dist/presets/filesUpload.d.mts +5 -5
- package/dist/presets/filesUpload.mjs +6 -9
- 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/multiTenant.mjs +2 -2
- package/dist/presets/search.d.mts +2 -2
- package/dist/presets/search.mjs +6 -8
- package/dist/{presets-Z7P5w4gF.mjs → presets-BbkjdPeH.mjs} +6 -28
- package/dist/{queryCachePlugin-BUXBSm4F.d.mts → queryCachePlugin-CqMdLI2-.d.mts} +2 -2
- package/dist/{queryCachePlugin-Bq6bO6vc.mjs → queryCachePlugin-m1XsgAIJ.mjs} +3 -3
- package/dist/{redis-Cm1gnRDf.d.mts → redis-DiMkdHEl.d.mts} +1 -1
- package/dist/redis-stream-D6HzR1Z_.d.mts +232 -0
- package/dist/registry/index.d.mts +1 -1
- package/dist/registry/index.mjs +2 -2
- package/dist/{replyHelpers-ByllIXXV.mjs → replyHelpers-CK-FNO8E.mjs} +3 -21
- package/dist/{resourceToTools-ByZpgjeH.mjs → resourceToTools-C5coh64w.mjs} +224 -71
- package/dist/{routerShared-BqLRb5l7.mjs → routerShared-D6_fEGHh.mjs} +40 -36
- package/dist/{schemaIR-BlG9bY7v.mjs → schemaIR-7Vl611Qs.mjs} +1 -1
- package/dist/schemas/index.d.mts +100 -30
- package/dist/schemas/index.mjs +86 -29
- package/dist/scim/index.d.mts +264 -0
- package/dist/scim/index.mjs +963 -0
- package/dist/scope/index.d.mts +3 -3
- package/dist/scope/index.mjs +4 -4
- package/dist/{sse-V7aXc3bW.mjs → sse-Bz-5ZeTt.mjs} +1 -1
- package/dist/{store-helpers-BhrzxvyQ.mjs → store-helpers-BkIN9-vu.mjs} +1 -1
- package/dist/testing/index.d.mts +2 -8
- package/dist/testing/index.mjs +16 -24
- package/dist/testing/storageContract.d.mts +1 -1
- package/dist/types/index.d.mts +4 -4
- package/dist/types/storage.d.mts +1 -1
- package/dist/{types-BH7dEGvU.d.mts → types-BvqwCCSx.d.mts} +77 -29
- package/dist/{types-tgR4Pt8F.d.mts → types-CTYvcwHe.d.mts} +195 -1
- package/dist/{types-AOD8fxIw.mjs → types-C_s5moIu.mjs} +117 -1
- package/dist/{types-9beEMe25.d.mts → types-DQHFc8PM.d.mts} +1 -1
- package/dist/utils/index.d.mts +2 -2
- package/dist/utils/index.mjs +5 -5
- package/dist/{utils-CcYTj09l.mjs → utils-_h9B3c57.mjs} +1269 -1334
- package/dist/{versioning-M9lNLhO8.d.mts → versioning-DTTvc80y.d.mts} +1 -1
- package/package.json +24 -34
- package/skills/arc/SKILL.md +521 -785
- package/skills/arc/references/agent-auth.md +238 -0
- package/skills/arc/references/api-reference.md +187 -0
- package/skills/arc/references/auth.md +354 -7
- package/skills/arc/references/enterprise-auth.md +94 -0
- package/skills/arc/references/events.md +8 -6
- package/skills/arc/references/mcp.md +2 -2
- package/skills/arc/references/multi-tenancy.md +11 -2
- package/skills/arc/references/production.md +10 -9
- package/skills/arc/references/scim.md +247 -0
- package/skills/arc/references/testing.md +1 -1
- package/skills/arc-code-review/SKILL.md +141 -0
- package/skills/arc-code-review/references/anti-patterns.md +911 -0
- package/skills/arc-code-review/references/arc-cheatsheet.md +380 -0
- package/skills/arc-code-review/references/migration-recipes.md +700 -0
- package/skills/arc-code-review/references/mongokit-migration.md +386 -0
- package/skills/arc-code-review/references/scaffolding.md +230 -0
- package/skills/arc-code-review/references/severity.md +127 -0
- package/dist/EventTransport-CfVEGaEl.d.mts +0 -293
- package/dist/adapters/index.d.mts +0 -3
- package/dist/adapters/index.mjs +0 -2
- package/dist/adapters-D0tT2Tyo.mjs +0 -949
- package/dist/auth/mongoose.d.mts +0 -191
- package/dist/auth/mongoose.mjs +0 -73
- package/dist/core-DnUsRpuX.mjs +0 -1049
- package/dist/errorHandler-BQm8ZxTK.mjs +0 -173
- package/dist/errorHandler-Co3lnVmJ.d.mts +0 -114
- package/dist/errors-D5c-5BJL.mjs +0 -232
- package/dist/index-BbMrcvGp.d.mts +0 -362
- package/dist/redis-stream-CM8TXTix.d.mts +0 -110
- /package/dist/{HookSystem-CGsMd6oK.mjs → HookSystem-Iiebom92.mjs} +0 -0
- /package/dist/{actionPermissions-sUUKDhtP.mjs → actionPermissions-CyUkQu6O.mjs} +0 -0
- /package/dist/{caching-CheW3m-S.mjs → caching-SM8gghN6.mjs} +0 -0
- /package/dist/{constants-BhY1OHoH.mjs → constants-Cxde4rpC.mjs} +0 -0
- /package/dist/{elevation-s5ykdNHr.d.mts → elevation-BXOWoGCF.d.mts} +0 -0
- /package/dist/{externalPaths-Bapitwvd.d.mts → externalPaths-BD5nw6St.d.mts} +0 -0
- /package/dist/{interface-CkkWm5uR.d.mts → interface-DfLGcus7.d.mts} +0 -0
- /package/dist/{interface-Da0r7Lna.d.mts → interface-beEtJyWM.d.mts} +0 -0
- /package/dist/{keys-CARyUjiR.mjs → keys-CGcCbNyu.mjs} +0 -0
- /package/dist/{loadResources-CPpkyKfM.mjs → loadResources-DBMQg_Aj.mjs} +0 -0
- /package/dist/{memory-DikHSvWa.mjs → memory-UBydS5ku.mjs} +0 -0
- /package/dist/{metrics-Csh4nsvv.mjs → metrics-Qnvwc-LQ.mjs} +0 -0
- /package/dist/{pluralize-BneOJkpi.mjs → pluralize-DQgqgifU.mjs} +0 -0
- /package/dist/{registry-D63ee7fl.mjs → registry-I-ogLgL9.mjs} +0 -0
- /package/dist/{requestContext-C5XeK3VA.mjs → requestContext-SSaaTgW8.mjs} +0 -0
- /package/dist/{schemaConverter-B0oKLuqI.mjs → schemaConverter-De34B1ZG.mjs} +0 -0
- /package/dist/{sessionManager-D-oNWHz3.d.mts → sessionManager-C4Le_UB3.d.mts} +0 -0
- /package/dist/{storage-BwGQXUpd.d.mts → storage-Dfzt4VTl.d.mts} +0 -0
- /package/dist/{tracing-DokiEsuz.d.mts → tracing-QJVprktp.d.mts} +0 -0
- /package/dist/{typeGuards-CcFZXgU7.mjs → typeGuards-BzkXkvVv.mjs} +0 -0
- /package/dist/{types-DV9WDfeg.mjs → types-D57iXYb8.mjs} +0 -0
- /package/dist/{versioning-CGPjkqAg.mjs → versioning-BUrT5aP4.mjs} +0 -0
- /package/dist/{websocket-CyJ1VIFI.d.mts → websocket-ChC2rqe1.d.mts} +0 -0
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { t as __exportAll } from "./chunk-BpYLSNr0.mjs";
|
|
2
|
-
import {
|
|
3
|
-
import { n as
|
|
2
|
+
import { f as createError } from "./errors-j4aJm1Wg.mjs";
|
|
3
|
+
import { a as buildAuthMiddlewareForPermissions, c as buildPreHandlerChain, f as resolveRouterPluginMw, l as buildRateLimitConfig, n as buildActionPipelineHandler, p as selectPluginMw, r as buildArcDecorator, t as buildActionPermissionMw, u as resolvePipelineSteps, y as sendControllerResponse } from "./routerShared-D6_fEGHh.mjs";
|
|
4
|
+
import { n as schemaIRToJsonSchemaBranch, t as normalizeSchemaIR } from "./schemaIR-7Vl611Qs.mjs";
|
|
4
5
|
//#region src/core/createActionRouter.ts
|
|
5
6
|
var createActionRouter_exports = /* @__PURE__ */ __exportAll({
|
|
6
7
|
buildActionBodySchema: () => buildActionBodySchema,
|
|
@@ -69,23 +70,13 @@ function createActionRouter(fastify, config) {
|
|
|
69
70
|
const { action, ...data } = req.body;
|
|
70
71
|
const { id } = req.params;
|
|
71
72
|
const handler = wrappedHandlers.get(action);
|
|
72
|
-
if (!handler)
|
|
73
|
-
success: false,
|
|
74
|
-
status: 400,
|
|
75
|
-
error: `Invalid action '${action}'. Valid actions: ${actionEnum.join(", ")}`,
|
|
76
|
-
meta: { validActions: actionEnum }
|
|
77
|
-
}, req);
|
|
73
|
+
if (!handler) throw createError(400, `Invalid action '${action}'. Valid actions: ${actionEnum.join(", ")}`, { validActions: actionEnum });
|
|
78
74
|
try {
|
|
79
75
|
return sendControllerResponse(reply, await handler(id, data, req), req);
|
|
80
76
|
} catch (error) {
|
|
81
77
|
if (onError) {
|
|
82
78
|
const { statusCode, error: errorMsg, code } = onError(error, action, id);
|
|
83
|
-
|
|
84
|
-
success: false,
|
|
85
|
-
status: statusCode,
|
|
86
|
-
error: errorMsg,
|
|
87
|
-
...code ? { meta: { code } } : {}
|
|
88
|
-
}, req);
|
|
79
|
+
throw createError(statusCode, errorMsg, code ? { code } : void 0);
|
|
89
80
|
}
|
|
90
81
|
const err = error;
|
|
91
82
|
const statusCode = err.statusCode || err.status || 500;
|
|
@@ -95,12 +86,8 @@ function createActionRouter(fastify, config) {
|
|
|
95
86
|
action,
|
|
96
87
|
id
|
|
97
88
|
}, "Action handler error");
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
status: statusCode,
|
|
101
|
-
error: err.message || `Failed to execute '${action}' action`,
|
|
102
|
-
meta: { code: errorCode }
|
|
103
|
-
}, req);
|
|
89
|
+
if (error?.name === "ArcError" || error instanceof Error === false) throw error;
|
|
90
|
+
throw createError(statusCode, err.message || `Failed to execute '${action}' action`, { code: errorCode });
|
|
104
91
|
}
|
|
105
92
|
}
|
|
106
93
|
});
|
|
@@ -0,0 +1,114 @@
|
|
|
1
|
+
import { f as createError, l as UnauthorizedError, r as ForbiddenError } from "./errors-j4aJm1Wg.mjs";
|
|
2
|
+
import { c as buildPreHandlerChain, f as resolveRouterPluginMw, i as buildAuthMiddleware, l as buildRateLimitConfig, p as selectPluginMw, r as buildArcDecorator } from "./routerShared-D6_fEGHh.mjs";
|
|
3
|
+
import { r as validateAggregations, t as buildAggregationHandler } from "./buildHandler-olo-gt94.mjs";
|
|
4
|
+
//#region src/core/aggregation/createAggregationRouter.ts
|
|
5
|
+
/**
|
|
6
|
+
* Register one Fastify route per aggregation. No-op when the map is
|
|
7
|
+
* empty — same convention `createActionRouter` follows.
|
|
8
|
+
*/
|
|
9
|
+
function createAggregationRouter(fastify, config) {
|
|
10
|
+
const { tag, resourceName, aggregations, fields: fieldPermissions, schemaOptions, permissions: resourcePermissions, routeGuards = [], repository, buildOptions } = config;
|
|
11
|
+
if (!aggregations || Object.keys(aggregations).length === 0) return;
|
|
12
|
+
const normalized = validateAggregations(resourceName, aggregations, schemaOptions);
|
|
13
|
+
const arcDecorator = buildArcDecorator({
|
|
14
|
+
resourceName,
|
|
15
|
+
schemaOptions,
|
|
16
|
+
permissions: resourcePermissions,
|
|
17
|
+
hooks: fastify.arc?.hooks,
|
|
18
|
+
events: fastify.events,
|
|
19
|
+
fields: fieldPermissions
|
|
20
|
+
});
|
|
21
|
+
for (const aggregation of normalized) registerOne(fastify, aggregation, {
|
|
22
|
+
tag,
|
|
23
|
+
arcDecorator,
|
|
24
|
+
routeGuards,
|
|
25
|
+
repository,
|
|
26
|
+
buildOptions
|
|
27
|
+
});
|
|
28
|
+
fastify.log?.debug?.({
|
|
29
|
+
aggregations: normalized.map((a) => a.name),
|
|
30
|
+
resourceName
|
|
31
|
+
}, `[createAggregationRouter] registered ${normalized.length} aggregation route(s)`);
|
|
32
|
+
}
|
|
33
|
+
function registerOne(fastify, normalized, ctx) {
|
|
34
|
+
const { tag, arcDecorator, routeGuards, repository, buildOptions } = ctx;
|
|
35
|
+
const { name } = normalized;
|
|
36
|
+
const config = normalized.base;
|
|
37
|
+
const authMw = buildAuthMiddleware(fastify, config.permissions);
|
|
38
|
+
const permissionFn = config.permissions;
|
|
39
|
+
const permissionMw = async (req, _reply) => {
|
|
40
|
+
const raw = await permissionFn(buildPermissionContextLite(req, normalized.name));
|
|
41
|
+
if (!normalizePermissionGranted(raw)) {
|
|
42
|
+
const status = req.user ? 403 : 401;
|
|
43
|
+
const reason = normalizePermissionReason(raw);
|
|
44
|
+
if (status === 401) throw new UnauthorizedError(reason ?? "Authentication required to access this aggregation.");
|
|
45
|
+
throw new ForbiddenError(reason ?? "You do not have permission to access this aggregation.");
|
|
46
|
+
}
|
|
47
|
+
};
|
|
48
|
+
const preHandler = buildPreHandlerChain({
|
|
49
|
+
arcDecorator,
|
|
50
|
+
authMw,
|
|
51
|
+
permissionMw,
|
|
52
|
+
pluginMw: selectPluginMw("GET", resolveRouterPluginMw(fastify, false)),
|
|
53
|
+
routeGuards
|
|
54
|
+
});
|
|
55
|
+
const rateLimitConfig = buildRateLimitConfig(config.rateLimit ? {
|
|
56
|
+
max: config.rateLimit.max,
|
|
57
|
+
timeWindow: `${config.rateLimit.windowMs}ms`
|
|
58
|
+
} : void 0);
|
|
59
|
+
const handler = buildAggregationHandler(normalized, {
|
|
60
|
+
repo: repository,
|
|
61
|
+
buildOptions
|
|
62
|
+
});
|
|
63
|
+
const routeSchema = {
|
|
64
|
+
tags: tag ? [tag] : void 0,
|
|
65
|
+
summary: config.summary ?? `Aggregation: ${name}`,
|
|
66
|
+
description: config.description ?? "Portable aggregation generated by arc. Filters from query string compose with the declaration's base filter + tenant scope."
|
|
67
|
+
};
|
|
68
|
+
fastify.route({
|
|
69
|
+
method: "GET",
|
|
70
|
+
url: `/aggregations/${name}`,
|
|
71
|
+
schema: routeSchema,
|
|
72
|
+
preHandler: preHandler.length > 0 ? preHandler : void 0,
|
|
73
|
+
...rateLimitConfig ? { config: rateLimitConfig } : {},
|
|
74
|
+
handler: async (req, reply) => {
|
|
75
|
+
try {
|
|
76
|
+
return await handler(req, reply);
|
|
77
|
+
} catch (err) {
|
|
78
|
+
const message = err instanceof Error ? err.message : String(err);
|
|
79
|
+
req.log.error({
|
|
80
|
+
err,
|
|
81
|
+
aggregation: name
|
|
82
|
+
}, "Aggregation handler error");
|
|
83
|
+
throw createError(500, `Aggregation "${name}" failed: ${message}`);
|
|
84
|
+
}
|
|
85
|
+
}
|
|
86
|
+
});
|
|
87
|
+
}
|
|
88
|
+
/**
|
|
89
|
+
* Minimal `PermissionContext` for aggregation routes. Aggregations are
|
|
90
|
+
* read-shape so the action is `'list'` and `data` / `resourceId` stay
|
|
91
|
+
* undefined unless the URL includes them (none do today — `:name` is
|
|
92
|
+
* the only path param).
|
|
93
|
+
*/
|
|
94
|
+
function buildPermissionContextLite(req, aggregationName) {
|
|
95
|
+
const reqWithExtras = req;
|
|
96
|
+
return {
|
|
97
|
+
user: reqWithExtras.user ?? null,
|
|
98
|
+
request: req,
|
|
99
|
+
resource: reqWithExtras.arc?.resource ?? "aggregation",
|
|
100
|
+
action: `aggregation:${aggregationName}`
|
|
101
|
+
};
|
|
102
|
+
}
|
|
103
|
+
/** PermissionCheck returns `boolean | PermissionResult`. Pull `granted`. */
|
|
104
|
+
function normalizePermissionGranted(raw) {
|
|
105
|
+
if (typeof raw === "boolean") return raw;
|
|
106
|
+
return raw.granted;
|
|
107
|
+
}
|
|
108
|
+
/** Pull `reason` when the check returned a structured `PermissionResult`. */
|
|
109
|
+
function normalizePermissionReason(raw) {
|
|
110
|
+
if (typeof raw === "boolean") return void 0;
|
|
111
|
+
return raw.reason;
|
|
112
|
+
}
|
|
113
|
+
//#endregion
|
|
114
|
+
export { createAggregationRouter };
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { t as __exportAll } from "./chunk-BpYLSNr0.mjs";
|
|
2
|
-
import {
|
|
2
|
+
import { arcLog } from "./logger/index.mjs";
|
|
3
|
+
import { n as PUBLIC_SCOPE } from "./types-C_s5moIu.mjs";
|
|
3
4
|
import Fastify from "fastify";
|
|
4
5
|
import qs from "qs";
|
|
5
6
|
//#region src/factory/presets.ts
|
|
@@ -117,10 +118,7 @@ const developmentPreset = {
|
|
|
117
118
|
]
|
|
118
119
|
},
|
|
119
120
|
rateLimit: false,
|
|
120
|
-
underPressure:
|
|
121
|
-
exposeStatusRoute: true,
|
|
122
|
-
maxEventLoopDelay: 5e3
|
|
123
|
-
}
|
|
121
|
+
underPressure: false
|
|
124
122
|
};
|
|
125
123
|
/**
|
|
126
124
|
* Testing preset - minimal setup, fast startup
|
|
@@ -204,7 +202,7 @@ async function registerArcCore(fastify, config, trackPlugin) {
|
|
|
204
202
|
await fastify.register(arcCorePlugin, { emitEvents: config.arcPlugins?.emitEvents !== false });
|
|
205
203
|
trackPlugin("arc-core");
|
|
206
204
|
if (config.arcPlugins?.events !== false) {
|
|
207
|
-
const { default: eventPlugin } = await import("./eventPlugin-
|
|
205
|
+
const { default: eventPlugin } = await import("./eventPlugin-CaKTYkYM.mjs").then((n) => n.n);
|
|
208
206
|
const eventOpts = typeof config.arcPlugins?.events === "object" ? config.arcPlugins.events : {};
|
|
209
207
|
await fastify.register(eventPlugin, {
|
|
210
208
|
...eventOpts,
|
|
@@ -240,15 +238,15 @@ async function registerArcPlugins(fastify, config, trackPlugin, modules) {
|
|
|
240
238
|
trackPlugin("arc-graceful-shutdown");
|
|
241
239
|
}
|
|
242
240
|
if (config.arcPlugins?.caching) {
|
|
243
|
-
const { default: cachingPlugin } = await import("./caching-
|
|
241
|
+
const { default: cachingPlugin } = await import("./caching-SM8gghN6.mjs").then((n) => n.r);
|
|
244
242
|
const opts = config.arcPlugins.caching === true ? {} : config.arcPlugins.caching;
|
|
245
243
|
await fastify.register(cachingPlugin, opts);
|
|
246
244
|
trackPlugin("arc-caching", opts);
|
|
247
245
|
}
|
|
248
246
|
if (config.arcPlugins?.queryCache) {
|
|
249
|
-
const { queryCachePlugin } = await import("./queryCachePlugin-
|
|
247
|
+
const { queryCachePlugin } = await import("./queryCachePlugin-m1XsgAIJ.mjs").then((n) => n.n);
|
|
250
248
|
const opts = config.arcPlugins.queryCache === true ? {} : config.arcPlugins.queryCache;
|
|
251
|
-
const store = config.stores?.queryCache ?? new (await (import("./memory-
|
|
249
|
+
const store = config.stores?.queryCache ?? new (await (import("./memory-UBydS5ku.mjs").then((n) => n.n))).MemoryCacheStore();
|
|
252
250
|
await fastify.register(queryCachePlugin, {
|
|
253
251
|
store,
|
|
254
252
|
...opts
|
|
@@ -257,19 +255,19 @@ async function registerArcPlugins(fastify, config, trackPlugin, modules) {
|
|
|
257
255
|
}
|
|
258
256
|
if (config.arcPlugins?.sse) if (config.arcPlugins?.events === false) fastify.log.warn("SSE plugin requires events plugin (arcPlugins.events). SSE disabled.");
|
|
259
257
|
else {
|
|
260
|
-
const { default: ssePlugin } = await import("./sse-
|
|
258
|
+
const { default: ssePlugin } = await import("./sse-Bz-5ZeTt.mjs").then((n) => n.r);
|
|
261
259
|
const opts = config.arcPlugins.sse === true ? {} : config.arcPlugins.sse;
|
|
262
260
|
await fastify.register(ssePlugin, opts);
|
|
263
261
|
trackPlugin("arc-sse", opts);
|
|
264
262
|
}
|
|
265
263
|
if (config.arcPlugins?.metrics) {
|
|
266
|
-
const { default: metricsPlugin } = await import("./metrics-
|
|
264
|
+
const { default: metricsPlugin } = await import("./metrics-Qnvwc-LQ.mjs").then((n) => n.r);
|
|
267
265
|
const opts = config.arcPlugins.metrics === true ? {} : config.arcPlugins.metrics;
|
|
268
266
|
await fastify.register(metricsPlugin, opts);
|
|
269
267
|
trackPlugin("arc-metrics", opts);
|
|
270
268
|
}
|
|
271
269
|
if (config.arcPlugins?.versioning) {
|
|
272
|
-
const { default: versioningPlugin } = await import("./versioning-
|
|
270
|
+
const { default: versioningPlugin } = await import("./versioning-BUrT5aP4.mjs").then((n) => n.r);
|
|
273
271
|
await fastify.register(versioningPlugin, config.arcPlugins.versioning);
|
|
274
272
|
trackPlugin("arc-versioning", config.arcPlugins.versioning);
|
|
275
273
|
}
|
|
@@ -338,7 +336,7 @@ async function registerAuth(fastify, config, trackPlugin) {
|
|
|
338
336
|
*/
|
|
339
337
|
async function registerElevation(fastify, config, trackPlugin) {
|
|
340
338
|
if (!config.elevation) return;
|
|
341
|
-
const { elevationPlugin } = await import("./elevation-
|
|
339
|
+
const { elevationPlugin } = await import("./elevation-DgoeTyfX.mjs").then((n) => n.r);
|
|
342
340
|
await fastify.register(elevationPlugin, config.elevation);
|
|
343
341
|
trackPlugin("arc-elevation", config.elevation);
|
|
344
342
|
fastify.log.debug("Elevation plugin enabled");
|
|
@@ -348,7 +346,7 @@ async function registerElevation(fastify, config, trackPlugin) {
|
|
|
348
346
|
*/
|
|
349
347
|
async function registerErrorHandler(fastify, config, trackPlugin) {
|
|
350
348
|
if (config.errorHandler === false) return;
|
|
351
|
-
const { errorHandlerPlugin } = await import("./errorHandler-
|
|
349
|
+
const { errorHandlerPlugin } = await import("./errorHandler-Bk-AGhkU.mjs").then((n) => n.r);
|
|
352
350
|
const errorOpts = typeof config.errorHandler === "object" ? config.errorHandler : { includeStack: config.preset !== "production" };
|
|
353
351
|
await fastify.register(errorHandlerPlugin, errorOpts);
|
|
354
352
|
trackPlugin("arc-error-handler", errorOpts);
|
|
@@ -402,7 +400,7 @@ async function registerOne(parent, resource) {
|
|
|
402
400
|
}
|
|
403
401
|
/**
|
|
404
402
|
* Execute the full resource lifecycle:
|
|
405
|
-
* 1. plugins() — infra (DB,
|
|
403
|
+
* 1. plugins() — infra (DB, data, webhooks)
|
|
406
404
|
* 2. bootstrap[] — domain init (singletons, event handlers)
|
|
407
405
|
* 3. resources factory (if any) — resolved AFTER bootstrap, so engine-backed
|
|
408
406
|
* adapters can `await ensureEngine()` and pass
|
|
@@ -448,7 +446,7 @@ async function registerResources(fastify, config) {
|
|
|
448
446
|
let discoveryPath;
|
|
449
447
|
let discoveryYieldedZero = false;
|
|
450
448
|
if (resolvedResources === void 0 && config.resourceDir) {
|
|
451
|
-
const { loadResources } = await import("./loadResources-
|
|
449
|
+
const { loadResources } = await import("./loadResources-DBMQg_Aj.mjs").then((n) => n.n);
|
|
452
450
|
const { resolve, dirname } = await import("node:path");
|
|
453
451
|
const { fileURLToPath } = await import("node:url");
|
|
454
452
|
const rawDir = config.resourceDir;
|
|
@@ -605,7 +603,8 @@ async function registerSecurityPlugins(fastify, config) {
|
|
|
605
603
|
if (config.cors !== false) {
|
|
606
604
|
const cors = await loadPlugin("cors");
|
|
607
605
|
const corsOptions = { ...config.cors ?? {} };
|
|
608
|
-
|
|
606
|
+
const originDeclared = "origin" in corsOptions && corsOptions.origin !== void 0;
|
|
607
|
+
if (config.preset === "production" && !originDeclared) fastify.log.warn("CORS origin is not explicitly configured in production. Browser apps: set cors.origin to allowed domains (e.g. ['https://app.example.com']) with credentials: true. Server-to-server / API-key services: cors: { origin: '*', credentials: false } OR cors: false to disable. Tip: when wiring cors.origin from an env var, fail fast on missing (`if (!process.env.ALLOWED_ORIGINS) throw ...`) instead of letting `undefined` slip through.");
|
|
609
608
|
if (corsOptions.credentials && corsOptions.origin === "*") corsOptions.origin = true;
|
|
610
609
|
await fastify.register(cors, corsOptions);
|
|
611
610
|
fastify.log.debug("CORS enabled");
|
|
@@ -747,7 +746,7 @@ function validateDistributedRuntime(options) {
|
|
|
747
746
|
* 4. Arc core (fastify.arc, events)
|
|
748
747
|
* 5. Arc plugins (requestId, health, caching, SSE, metrics, versioning)
|
|
749
748
|
* 6. Auth (scope decoration, auth strategy, elevation, error handler)
|
|
750
|
-
* 7. plugins() — user infra (DB,
|
|
749
|
+
* 7. plugins() — user infra (DB, data, webhooks)
|
|
751
750
|
* 8. bootstrap[] — domain init (singletons, event handlers)
|
|
752
751
|
* 9. resources[] — auto-discovered routes (prefix + skipGlobalPrefix)
|
|
753
752
|
* 10. afterResources() — post-registration wiring
|
|
@@ -812,17 +811,22 @@ async function createApp(options) {
|
|
|
812
811
|
await registerErrorHandler(fastify, config, trackPlugin);
|
|
813
812
|
await registerResources(fastify, config);
|
|
814
813
|
if (config.replyHelpers) {
|
|
815
|
-
const { replyHelpersPlugin } = await import("./replyHelpers-
|
|
814
|
+
const { replyHelpersPlugin } = await import("./replyHelpers-CK-FNO8E.mjs").then((n) => n.n);
|
|
816
815
|
await fastify.register(replyHelpersPlugin);
|
|
817
816
|
}
|
|
818
|
-
if (config.serializeBigInt)
|
|
819
|
-
|
|
820
|
-
|
|
821
|
-
|
|
822
|
-
|
|
823
|
-
return payload;
|
|
824
|
-
|
|
825
|
-
|
|
817
|
+
if (config.serializeBigInt) {
|
|
818
|
+
const mode = config.serializeBigInt === "string" ? "string" : "number";
|
|
819
|
+
if (config.serializeBigInt === true) arcLog("createApp").warn("serializeBigInt: true is a back-compat alias for 'number' (lossy above Number.MAX_SAFE_INTEGER = 9007199254740991). For IDs / money / counters / ledger values use serializeBigInt: 'string' (lossless). The boolean form will be removed in a future major.");
|
|
820
|
+
else if (mode === "number") arcLog("createApp").warn("serializeBigInt: 'number' loses precision above Number.MAX_SAFE_INTEGER (9007199254740991). Use 'string' instead unless you've audited that no bigint payload field can exceed the safe range.");
|
|
821
|
+
fastify.addHook("preSerialization", async (_request, _reply, payload) => {
|
|
822
|
+
if (payload === null || payload === void 0) return payload;
|
|
823
|
+
try {
|
|
824
|
+
return JSON.parse(JSON.stringify(payload, (_key, value) => typeof value === "bigint" ? mode === "string" ? value.toString() : Number(value) : value));
|
|
825
|
+
} catch {
|
|
826
|
+
return payload;
|
|
827
|
+
}
|
|
828
|
+
});
|
|
829
|
+
}
|
|
826
830
|
const authMode = config.auth === false ? "none" : config.auth ? config.auth.type : "none";
|
|
827
831
|
fastify.log.info({
|
|
828
832
|
preset: config.preset ?? "custom",
|
|
@@ -0,0 +1,188 @@
|
|
|
1
|
+
import { t as __exportAll } from "./chunk-BpYLSNr0.mjs";
|
|
2
|
+
import { n as createEvent } from "./EventTransport-DLWoUMHy.mjs";
|
|
3
|
+
//#region src/events/defineEvent.ts
|
|
4
|
+
/**
|
|
5
|
+
* defineEvent — Typed Event Definitions with Optional Schema Validation
|
|
6
|
+
*
|
|
7
|
+
* Provides:
|
|
8
|
+
* 1. defineEvent() — declare an event with name, schema, version, description
|
|
9
|
+
* 2. EventRegistry — catalog of all known events + payload validation
|
|
10
|
+
* 3. .create() helper — build DomainEvent with auto-generated metadata
|
|
11
|
+
*
|
|
12
|
+
* The built-in validator checks: object type, required fields, and top-level
|
|
13
|
+
* property types. It does NOT recurse into nested objects, validate arrays,
|
|
14
|
+
* enums, patterns, formats, or $ref. This is intentional — it's a lightweight
|
|
15
|
+
* guard, not a full JSON Schema engine.
|
|
16
|
+
*
|
|
17
|
+
* For full validation, pass a custom `validate` function to `createEventRegistry()`:
|
|
18
|
+
*
|
|
19
|
+
* @example
|
|
20
|
+
* ```typescript
|
|
21
|
+
* import Ajv from 'ajv';
|
|
22
|
+
* const ajv = new Ajv();
|
|
23
|
+
*
|
|
24
|
+
* const registry = createEventRegistry({
|
|
25
|
+
* validate: (schema, payload) => {
|
|
26
|
+
* const valid = ajv.validate(schema, payload);
|
|
27
|
+
* return valid
|
|
28
|
+
* ? { valid: true }
|
|
29
|
+
* : { valid: false, errors: ajv.errorsText().split(', ') };
|
|
30
|
+
* },
|
|
31
|
+
* });
|
|
32
|
+
* ```
|
|
33
|
+
*
|
|
34
|
+
* @example
|
|
35
|
+
* ```typescript
|
|
36
|
+
* import { defineEvent, createEventRegistry } from '@classytic/arc/events';
|
|
37
|
+
*
|
|
38
|
+
* const OrderCreated = defineEvent({
|
|
39
|
+
* name: 'order.created',
|
|
40
|
+
* version: 1,
|
|
41
|
+
* schema: {
|
|
42
|
+
* type: 'object',
|
|
43
|
+
* properties: {
|
|
44
|
+
* orderId: { type: 'string' },
|
|
45
|
+
* total: { type: 'number' },
|
|
46
|
+
* },
|
|
47
|
+
* required: ['orderId', 'total'],
|
|
48
|
+
* },
|
|
49
|
+
* });
|
|
50
|
+
*
|
|
51
|
+
* // Type-safe event creation
|
|
52
|
+
* const event = OrderCreated.create({ orderId: 'o-1', total: 100 });
|
|
53
|
+
* await fastify.events.publish(event.type, event.payload, event.meta);
|
|
54
|
+
*
|
|
55
|
+
* // Registry for introspection + validation
|
|
56
|
+
* const registry = createEventRegistry();
|
|
57
|
+
* registry.register(OrderCreated);
|
|
58
|
+
* const result = registry.validate('order.created', payload);
|
|
59
|
+
* ```
|
|
60
|
+
*/
|
|
61
|
+
var defineEvent_exports = /* @__PURE__ */ __exportAll({
|
|
62
|
+
createEventRegistry: () => createEventRegistry,
|
|
63
|
+
defineEvent: () => defineEvent
|
|
64
|
+
});
|
|
65
|
+
/**
|
|
66
|
+
* Define a typed event with optional schema validation.
|
|
67
|
+
*
|
|
68
|
+
* @example
|
|
69
|
+
* const OrderCreated = defineEvent({
|
|
70
|
+
* name: 'order.created',
|
|
71
|
+
* schema: { type: 'object', properties: { orderId: { type: 'string' } }, required: ['orderId'] },
|
|
72
|
+
* });
|
|
73
|
+
*
|
|
74
|
+
* const event = OrderCreated.create({ orderId: '123' });
|
|
75
|
+
*/
|
|
76
|
+
function defineEvent(input) {
|
|
77
|
+
const { name, schema, version = 1, description } = input;
|
|
78
|
+
return {
|
|
79
|
+
name,
|
|
80
|
+
schema,
|
|
81
|
+
version,
|
|
82
|
+
description,
|
|
83
|
+
create(payload, meta) {
|
|
84
|
+
return createEvent(name, payload, {
|
|
85
|
+
schemaVersion: version,
|
|
86
|
+
...meta
|
|
87
|
+
});
|
|
88
|
+
}
|
|
89
|
+
};
|
|
90
|
+
}
|
|
91
|
+
/**
|
|
92
|
+
* Create an event registry for cataloging and validating events.
|
|
93
|
+
*
|
|
94
|
+
* The registry is opt-in — unregistered events pass validation.
|
|
95
|
+
* This allows gradual adoption without breaking existing code.
|
|
96
|
+
*
|
|
97
|
+
* @param options.validate - Custom validator replacing the built-in minimal validator.
|
|
98
|
+
* The built-in validator only checks top-level object structure (type, required, property types).
|
|
99
|
+
* For nested objects, arrays, enums, patterns, or $ref, provide AJV or similar.
|
|
100
|
+
*/
|
|
101
|
+
function createEventRegistry(options) {
|
|
102
|
+
const customValidator = options?.validate;
|
|
103
|
+
const definitions = /* @__PURE__ */ new Map();
|
|
104
|
+
function registryKey(name, version) {
|
|
105
|
+
return `${name}:v${version}`;
|
|
106
|
+
}
|
|
107
|
+
return {
|
|
108
|
+
register(definition) {
|
|
109
|
+
const key = registryKey(definition.name, definition.version);
|
|
110
|
+
if (definitions.has(key)) throw new Error(`Event '${definition.name}' v${definition.version} is already registered. Use a different version number for schema evolution.`);
|
|
111
|
+
definitions.set(key, definition);
|
|
112
|
+
},
|
|
113
|
+
get(name, version) {
|
|
114
|
+
if (version !== void 0) return definitions.get(registryKey(name, version));
|
|
115
|
+
let latest;
|
|
116
|
+
let latestVersion = -1;
|
|
117
|
+
for (const def of definitions.values()) if (def.name === name && def.version > latestVersion) {
|
|
118
|
+
latest = def;
|
|
119
|
+
latestVersion = def.version;
|
|
120
|
+
}
|
|
121
|
+
return latest;
|
|
122
|
+
},
|
|
123
|
+
catalog() {
|
|
124
|
+
return Array.from(definitions.values()).map((def) => ({
|
|
125
|
+
name: def.name,
|
|
126
|
+
version: def.version,
|
|
127
|
+
description: def.description,
|
|
128
|
+
schema: def.schema
|
|
129
|
+
}));
|
|
130
|
+
},
|
|
131
|
+
validate(name, payload, version) {
|
|
132
|
+
let target = version !== void 0 ? definitions.get(registryKey(name, version)) : void 0;
|
|
133
|
+
if (!target && version === void 0) {
|
|
134
|
+
let latestVersion = -1;
|
|
135
|
+
for (const candidate of definitions.values()) if (candidate.name === name && candidate.version > latestVersion) {
|
|
136
|
+
target = candidate;
|
|
137
|
+
latestVersion = candidate.version;
|
|
138
|
+
}
|
|
139
|
+
}
|
|
140
|
+
if (!target) return { valid: true };
|
|
141
|
+
if (!target.schema) return { valid: true };
|
|
142
|
+
return customValidator ? customValidator(target.schema, payload) : validatePayload(payload, target.schema);
|
|
143
|
+
}
|
|
144
|
+
};
|
|
145
|
+
}
|
|
146
|
+
/**
|
|
147
|
+
* Built-in minimal validator — lightweight guard, NOT a full JSON Schema engine.
|
|
148
|
+
*
|
|
149
|
+
* Checks:
|
|
150
|
+
* - payload is an object (not null, not array)
|
|
151
|
+
* - required fields are present
|
|
152
|
+
* - top-level property types match (string, number, boolean, array, object)
|
|
153
|
+
*
|
|
154
|
+
* Does NOT check:
|
|
155
|
+
* - nested object properties
|
|
156
|
+
* - array item types
|
|
157
|
+
* - enum, pattern, format, minLength, minimum, $ref
|
|
158
|
+
*
|
|
159
|
+
* For full validation, pass a custom `validate` function to `createEventRegistry()`.
|
|
160
|
+
*/
|
|
161
|
+
function validatePayload(payload, schema) {
|
|
162
|
+
const errors = [];
|
|
163
|
+
if (schema.type === "object") {
|
|
164
|
+
if (payload === null || payload === void 0 || typeof payload !== "object" || Array.isArray(payload)) return {
|
|
165
|
+
valid: false,
|
|
166
|
+
errors: ["Payload must be an object"]
|
|
167
|
+
};
|
|
168
|
+
const record = payload;
|
|
169
|
+
if (schema.required) {
|
|
170
|
+
for (const field of schema.required) if (!(field in record) || record[field] === void 0) errors.push(`Missing required field: '${field}'`);
|
|
171
|
+
}
|
|
172
|
+
if (schema.properties) {
|
|
173
|
+
for (const [key, propSchema] of Object.entries(schema.properties)) if (key in record && record[key] !== void 0 && record[key] !== null) {
|
|
174
|
+
const expectedType = propSchema.type;
|
|
175
|
+
if (expectedType) {
|
|
176
|
+
const actualType = Array.isArray(record[key]) ? "array" : typeof record[key];
|
|
177
|
+
if (expectedType !== actualType) errors.push(`Field '${key}': expected ${expectedType}, got ${actualType}`);
|
|
178
|
+
}
|
|
179
|
+
}
|
|
180
|
+
}
|
|
181
|
+
}
|
|
182
|
+
return errors.length === 0 ? { valid: true } : {
|
|
183
|
+
valid: false,
|
|
184
|
+
errors
|
|
185
|
+
};
|
|
186
|
+
}
|
|
187
|
+
//#endregion
|
|
188
|
+
export { defineEvent as n, defineEvent_exports as r, createEventRegistry as t };
|
package/dist/docs/index.d.mts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import { p as RegistryEntry } from "../index-
|
|
2
|
-
import { t as ExternalOpenApiPaths } from "../externalPaths-
|
|
1
|
+
import { p as RegistryEntry } from "../index-BtW7qYwa.mjs";
|
|
2
|
+
import { t as ExternalOpenApiPaths } from "../externalPaths-BD5nw6St.mjs";
|
|
3
3
|
import { FastifyPluginAsync } from "fastify";
|
|
4
4
|
|
|
5
5
|
//#region src/docs/openapi.d.ts
|
package/dist/docs/index.mjs
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import { t as getUserRoles } from "../types-
|
|
2
|
-
import { n as openApiPlugin, r as openapi_default, t as buildOpenApiSpec } from "../openapi-
|
|
1
|
+
import { t as getUserRoles } from "../types-D57iXYb8.mjs";
|
|
2
|
+
import { n as openApiPlugin, r as openapi_default, t as buildOpenApiSpec } from "../openapi-CiOMVW1p.mjs";
|
|
3
3
|
import fp from "fastify-plugin";
|
|
4
4
|
//#region src/docs/scalar.ts
|
|
5
5
|
const scalarPlugin = async (fastify, opts = {}) => {
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { t as __exportAll } from "./chunk-BpYLSNr0.mjs";
|
|
2
2
|
import { arcLog } from "./logger/index.mjs";
|
|
3
|
-
import { t as getUserRoles } from "./types-
|
|
3
|
+
import { t as getUserRoles } from "./types-D57iXYb8.mjs";
|
|
4
4
|
import fp from "fastify-plugin";
|
|
5
5
|
//#region src/scope/elevation.ts
|
|
6
6
|
var elevation_exports = /* @__PURE__ */ __exportAll({
|