@classytic/arc 2.41.0 → 2.43.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/{EventTransport-CbUKDzZZ.d.mts → EventTransport-CiicKz2Z.d.mts} +1 -1
- package/dist/{EventTransport-n7qO5Cz3.mjs → EventTransport-tzA7CAB4.mjs} +22 -17
- package/dist/{HookSystem-CxvZBo77.mjs → HookSystem-B6UaKRbF.mjs} +2 -2
- package/dist/{QueryCache-DFA-QrU9.d.mts → QueryCache--5uAuYPW.d.mts} +7 -3
- package/dist/{ResourceRegistry-Cu7uTJp9.mjs → ResourceRegistry-CuCARu2B.mjs} +44 -13
- package/dist/actionPermissions-BotX1YOa.mjs +776 -0
- package/dist/audit/index.d.mts +43 -5
- package/dist/audit/index.mjs +48 -26
- package/dist/auth/audit.mjs +1 -1
- package/dist/auth/index.d.mts +43 -13
- package/dist/auth/index.mjs +130 -155
- package/dist/auth/redis-session.mjs +1 -1
- package/dist/{authorizationDecision-2YY_Kaj1.mjs → authorizationDecision-DQYrkl5f.mjs} +7 -102
- package/dist/{base-CxrV1LmG.d.mts → base-CZyixEQR.d.mts} +27 -5
- package/dist/boundedMap-BULzfXYU.mjs +57 -0
- package/dist/{buildHandler-CUhjGHGZ.mjs → buildHandler-CRQ8EcX5.mjs} +4 -4
- package/dist/cache/index.d.mts +22 -34
- package/dist/cache/index.mjs +4 -4
- package/dist/{caching-DsWDjIxx.mjs → caching-Cc2M1DwO.mjs} +3 -3
- package/dist/claimed-B10RTfkM.mjs +8 -0
- package/dist/cleanup/index.d.mts +6 -10
- package/dist/cleanup/index.mjs +27 -73
- package/dist/cli/commands/describe.d.mts +4 -2
- package/dist/cli/commands/describe.mjs +7 -4
- 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-BxLgNFF7.mjs → constants-Cu7Mx7_U.mjs} +4 -2
- package/dist/context/index.d.mts +2 -5
- package/dist/context/index.mjs +18 -38
- package/dist/core/index.d.mts +3 -3
- package/dist/core/index.mjs +8 -8
- package/dist/{core-iNGqYerO.mjs → core-Cb0LjccA.mjs} +5 -4
- package/dist/{createActionRouter-BBlbPs3k.mjs → createActionRouter-xjqonMRO.mjs} +87 -39
- package/dist/{createAggregationRouter-Dv5SuK2x.mjs → createAggregationRouter-zRYWw_5D.mjs} +11 -8
- package/dist/{preloadResources-Bbp4QJ8V.mjs → createApp-D-953ZAq.mjs} +299 -390
- package/dist/{createMcpServer-CbDWkZ1T.d.mts → createMcpServer-C9cxS8zt.d.mts} +1 -1
- package/dist/{defineAggregation-UyeurGkp.mjs → defineAggregation-Bm8RjMbq.mjs} +1 -1
- package/dist/{defineResource-DzGT-2LP.mjs → defineResource-DIC7JZGU.mjs} +118 -1024
- package/dist/discovery/index.mjs +1 -1
- package/dist/docs/index.d.mts +10 -4
- package/dist/docs/index.mjs +4 -4
- package/dist/{elevation-BXCfVcNG.d.mts → elevation-BW2J3tNj.d.mts} +1 -2
- package/dist/{elevation-BF-vhGxY.mjs → elevation-DG8SrLN-.mjs} +41 -12
- package/dist/encryption/index.mjs +3 -3
- package/dist/{errorHandler-CIMkCOWm.mjs → errorHandler-BnfDviL1.mjs} +26 -5
- package/dist/{errors-C_YhELhe.mjs → errors-CtrywMHP.mjs} +58 -23
- package/dist/{errors-BGdVwP25.d.mts → errors-JYc9vwqE.d.mts} +9 -0
- package/dist/{eventPlugin-B52abJ8W.d.mts → eventPlugin-BtmA4-3P.d.mts} +65 -9
- package/dist/{eventPlugin-CmXaPWLv.mjs → eventPlugin-Q6BhzHzs.mjs} +82 -21
- package/dist/{subscribe-helpers-AVYPgqTl.mjs → eventTypes-Cdhm6ZlV.mjs} +151 -17
- package/dist/events/index.d.mts +33 -8
- package/dist/events/index.mjs +49 -53
- package/dist/events/transports/redis-stream-entry.d.mts +1 -1
- package/dist/events/transports/redis-stream-entry.mjs +139 -157
- package/dist/events/transports/redis.d.mts +1 -1
- package/dist/events/transports/redis.mjs +2 -2
- package/dist/factory/index.d.mts +32 -17
- package/dist/factory/index.mjs +213 -32
- package/dist/{fastify-augmentation-Cq2nHVaW.d.mts → fastify-augmentation-DhUJ-mvo.d.mts} +11 -25
- package/dist/{fields-I_EeRWND.d.mts → fields-BBSpf8pS.d.mts} +1 -1
- package/dist/{fields-BTyPLHK0.mjs → fields-ToPYpMqS.mjs} +1 -1
- package/dist/filter-merge-IpGywvym.mjs +99 -0
- package/dist/{generate-D_YmiCNf.mjs → generate-DRTkLlIl.mjs} +2 -2
- package/dist/{headers-CNECDE6q.mjs → headers-BbkIF6vA.mjs} +2 -2
- package/dist/health-D-Ft6JM6.mjs +22 -0
- package/dist/{health-CxQzbJmr.mjs → health-DqbsG-_w.mjs} +115 -55
- package/dist/hooks/index.d.mts +2 -2
- package/dist/hooks/index.mjs +2 -2
- package/dist/idempotency/index.d.mts +31 -7
- package/dist/idempotency/index.mjs +155 -98
- package/dist/idempotency/redis.d.mts +1 -1
- package/dist/idempotency/redis.mjs +48 -11
- package/dist/{index-BHdZD8zK.d.mts → index-Bxriq3so.d.mts} +84 -21
- package/dist/{preloadResources-CjS2OfpU.d.mts → index-C8zVGYsk.d.mts} +37 -38
- package/dist/{index-D4u76eQW.d.mts → index-CRoPtISf.d.mts} +23 -73
- package/dist/{index-CbJiYrfv.d.mts → index-Chgz3d0i.d.mts} +1 -1
- package/dist/index-JdbZAIsm.d.mts +510 -0
- package/dist/{index-DHbyPo5j.d.mts → index-mqaqZNpe.d.mts} +253 -254
- package/dist/index.d.mts +8 -8
- package/dist/index.mjs +11 -11
- package/dist/{init-Y2MlSN9n.mjs → init-B_oUYDXI.mjs} +1 -1
- package/dist/integrations/event-gateway.d.mts +8 -3
- package/dist/integrations/event-gateway.mjs +4 -4
- package/dist/integrations/index.d.mts +3 -3
- package/dist/integrations/index.mjs +1 -1
- package/dist/integrations/jobs/index.d.mts +1 -1
- package/dist/integrations/jobs/index.mjs +18 -5
- package/dist/integrations/mcp/index.d.mts +72 -59
- package/dist/integrations/mcp/index.mjs +19 -4
- package/dist/integrations/mcp/testing.d.mts +2 -2
- package/dist/integrations/mcp/testing.mjs +2 -2
- package/dist/integrations/streamline.d.mts +78 -136
- package/dist/integrations/streamline.mjs +116 -54
- package/dist/integrations/webhooks.d.mts +60 -120
- package/dist/integrations/webhooks.mjs +36 -80
- package/dist/integrations/websocket-pushref-redis.d.mts +1 -1
- package/dist/integrations/websocket-pushref-redis.mjs +1 -1
- package/dist/integrations/websocket-redis.d.mts +1 -1
- package/dist/integrations/websocket-redis.mjs +1 -1
- package/dist/integrations/websocket.d.mts +3 -3
- package/dist/integrations/websocket.mjs +2 -2
- package/dist/{interface-DEWNOMza.d.mts → interface-C0395yUN.d.mts} +13 -1
- package/dist/{jsonBody-Cq9KYl2Z.mjs → jsonBody-B4YVGcql.mjs} +2 -2
- package/dist/{keys-Cmv4cBRa.mjs → keys-B2KtgMLJ.mjs} +7 -18
- package/dist/{loadResources-CblF7xt2.mjs → loadResources-Dn4lalw2.mjs} +3 -3
- package/dist/{loadResourcesFromEntry-99fIMC3p.mjs → loadResourcesFromEntry-3sLYbuse.mjs} +2 -2
- package/dist/logger/index.d.mts +1 -8
- package/dist/logger/index.mjs +3 -153
- package/dist/logger-DXjfIqjV.mjs +160 -0
- package/dist/{mcpPlugin-xF3Gw-HW.mjs → mcpPlugin-C08HalN5.mjs} +433 -170
- package/dist/{memory-CpDZ9NPw.mjs → memory-9Cp7611C.mjs} +36 -34
- package/dist/{metrics-BaKnq8ag.mjs → metrics-CWI11eRu.mjs} +39 -72
- package/dist/middleware/index.d.mts +2 -2
- package/dist/middleware/index.mjs +3 -3
- package/dist/migrations/index.mjs +2 -2
- package/dist/{module-BkQEesNQ.mjs → module-BSvOQC7E.mjs} +8 -8
- package/dist/multiTenant-cY0FkDiA.d.mts +104 -0
- package/dist/{multipartBody-C92aJ8am.mjs → multipartBody-CXM2a-Dy.mjs} +46 -7
- package/dist/{openapi-C3LFRzPq.mjs → openapi-DT_G4VhY.mjs} +48 -10
- package/dist/outbox-admin/index.d.mts +3 -3
- package/dist/outbox-admin/index.mjs +6 -18
- package/dist/{outbox-module-dsCyLxXH.mjs → outbox-module-Bjgzl6tg.mjs} +203 -33
- package/dist/{outbox-module-BHdUnuuK.d.mts → outbox-module-gZCHoE4H.d.mts} +79 -6
- package/dist/permissions/index.d.mts +5 -4
- package/dist/permissions/index.mjs +8 -6
- package/dist/{permissions-DiHXy8Ck.mjs → permissions-Cm0uYHvd.mjs} +149 -33
- package/dist/{pipe-CBVmUJ-j.mjs → pipe-BqeGT6UO.mjs} +2 -2
- package/dist/pipeline/index.d.mts +2 -2
- package/dist/pipeline/index.mjs +2 -2
- package/dist/plan-FVR6LP59.mjs +565 -0
- package/dist/{BaseController-DYJRZ0EJ.mjs → plugin-BccET1tL.mjs} +1256 -344
- package/dist/plugins/index.d.mts +14 -11
- package/dist/plugins/index.mjs +44 -22
- package/dist/plugins/response-cache.mjs +8 -14
- package/dist/plugins/tracing-entry.mjs +2 -2
- package/dist/{pluralize-2_U6QZQg.mjs → pluralize-CVIOcgMF.mjs} +1 -1
- package/dist/presets/filesUpload.d.mts +2 -2
- package/dist/presets/filesUpload.mjs +5 -5
- package/dist/presets/index.d.mts +26 -31
- package/dist/presets/index.mjs +4 -4
- package/dist/presets/multiTenant.d.mts +2 -107
- package/dist/presets/multiTenant.mjs +22 -162
- package/dist/presets/search.d.mts +2 -2
- package/dist/presets/search.mjs +2 -2
- package/dist/{presets-BdlugYTc.mjs → presets-Cpvj6fGO.mjs} +99 -107
- package/dist/{projection-Uvgp-EEj.mjs → projection-Ce4aQhQk.mjs} +2 -2
- package/dist/{pushref-registry-Dov9l4qE.d.mts → pushref-registry-UxCkoA4C.d.mts} +1 -1
- package/dist/{queryCachePlugin-BzqxconG.d.mts → queryCachePlugin-C2lZjTxk.d.mts} +1 -1
- package/dist/{queryCachePlugin-B5F09bYr.mjs → queryCachePlugin-CFWNH8aT.mjs} +12 -14
- package/dist/{rateLimitKey-CK-EU2L8.mjs → rateLimitKey-C4O-kPm0.mjs} +1 -1
- package/dist/{realtime-FnnD0n3Z.mjs → realtime-DmPE9S54.mjs} +25 -21
- package/dist/redis-stream-BMk-bpQS.d.mts +219 -0
- package/dist/{redis-Bzi1y3F9.d.mts → redis-vLvtcGy_.d.mts} +13 -24
- package/dist/redisShapes-CQksY67q.d.mts +55 -0
- package/dist/registry/index.d.mts +9 -4
- package/dist/registry/index.mjs +4 -4
- package/dist/{registry-D3y8VEJ1.mjs → registry-Bcz9G1bh.mjs} +10 -8
- package/dist/{renewingLease-B3VYh5aU.mjs → renewingLease-qODa7KJQ.mjs} +1 -1
- package/dist/{replyHelpers-DPHsdw-5.mjs → replyHelpers-CG_VtVgS.mjs} +2 -2
- package/dist/{requestContext-cSVKnYPk.mjs → requestContext-Dsu9P1bV.mjs} +1 -1
- package/dist/requirement-CbiLdQ1n.d.mts +22 -0
- package/dist/requirement-KDdoRcOP.mjs +35 -0
- package/dist/resourceRateLimit-Wf6d6FL9.d.mts +30 -0
- package/dist/resourceRateLimit-dSjVr1pk.mjs +180 -0
- package/dist/{rolldown-runtime-DjDuyglE.mjs → rolldown-runtime-D1w9Y1VJ.mjs} +1 -1
- package/dist/routeValidator-D36SW8qt.mjs +70 -0
- package/dist/{runtimeCapabilities-DSA_dH1_.mjs → runtimeCapabilities-0tccjRlj.mjs} +1 -1
- package/dist/{schedules-BktJb3Y3.mjs → schedules-C-OpmHBq.mjs} +46 -37
- package/dist/{schemaConverter-BLCSmNbr.mjs → schemaConverter-BORek3O-.mjs} +2 -2
- package/dist/{schemaIR-K16Bb5BX.mjs → schemaIR-C7Vbp71I.mjs} +2 -2
- package/dist/schemas/index.d.mts +10 -0
- package/dist/schemas/index.mjs +8 -3
- package/dist/scim/index.mjs +2 -2
- package/dist/scope/index.d.mts +2 -2
- package/dist/scope/index.mjs +6 -6
- package/dist/{scope-jSxuBTFM.mjs → scope-DQthe88b.mjs} +4 -4
- package/dist/shutdown-MqA81z_P.mjs +59 -0
- package/dist/{simpleEqualityMatcher-BiOnyflP.mjs → simpleEqualityMatcher-auW8QjXN.mjs} +1 -1
- package/dist/{sse-CH7Slxc1.mjs → sse-CYzQBVFe.mjs} +11 -15
- package/dist/{sseStream-CN-Lh1Tu.mjs → sseStream-B5_Afiqn.mjs} +2 -2
- package/dist/{store-helpers-9DX9-1j_.mjs → store-helpers-BOzk-CuX.mjs} +1 -1
- package/dist/{streaming-dvppnxsr.mjs → streaming-DjTQhZrN.mjs} +1 -1
- package/dist/subscriberIdentity-CW39uA8G.mjs +28 -0
- package/dist/tenancy--ed9eH_z.mjs +43 -0
- package/dist/tenantPreparation-BKon6Yox.mjs +67 -0
- package/dist/testing/cleanupStoreContract.mjs +1 -1
- package/dist/testing/index.d.mts +193 -175
- package/dist/testing/index.mjs +286 -212
- package/dist/testing/outboxStoreContract.mjs +137 -2
- package/dist/testing/requiredPorts.d.mts +17 -0
- package/dist/testing/requiredPorts.mjs +65 -0
- package/dist/testing/storageContract.mjs +1 -1
- package/dist/timing-qczZ0YC-.mjs +46 -0
- package/dist/{transactionContext-DCWMI6hV.mjs → transactionContext-DjFkrNG6.mjs} +1 -1
- package/dist/{typeGuards-D7tHFdH6.mjs → typeGuards-CySF12G-.mjs} +1 -1
- package/dist/types/index.d.mts +4 -4
- package/dist/types/index.mjs +1 -1
- package/dist/types/storage.mjs +1 -1
- package/dist/{types-CWQn9-IC.d.mts → types-B4Aq4Sdj.d.mts} +22 -9
- package/dist/{types-DfVDvkdP.d.mts → types-BJ-2XqVF.d.mts} +189 -204
- package/dist/{types-CDy3-zw1.mjs → types-Bk9NOgOC.mjs} +4 -4
- package/dist/usage/index.d.mts +2 -3
- package/dist/usage/index.mjs +5 -5
- package/dist/{userHelpers-DDMOZ_Pm.mjs → userHelpers-aklawRaK.mjs} +1 -1
- package/dist/utils/index.d.mts +84 -223
- package/dist/utils/index.mjs +13 -12
- package/dist/{utils-B-zj_b__.mjs → utils-ec1oTo1n.mjs} +315 -404
- package/dist/{validate-R0G_JrnK.mjs → validate-9yzHC1e2.mjs} +6 -14
- package/dist/{versioning-D6JIK8Tk.d.mts → versioning-Bsb51gGT.d.mts} +28 -13
- package/dist/{versioning-DuVWcu-F.mjs → versioning-fhhWHV74.mjs} +4 -4
- package/dist/{websocket-BOXVggMf.d.mts → websocket-Cs4awVyD.d.mts} +1 -1
- package/dist/{workScope-BhsWde4k.mjs → workScope-zpPyge4t.mjs} +2 -2
- package/package.json +578 -574
- package/skills/arc/SKILL.md +13 -4
- package/skills/arc/references/auth.md +8 -5
- package/skills/arc/references/integrations.md +1 -1
- package/skills/arc/references/mcp.md +8 -6
- package/skills/arc/references/multi-tenancy.md +19 -14
- package/skills/arc/references/production.md +27 -10
- package/dist/actionPermissions-DPwPWPOV.mjs +0 -350
- package/dist/index-CFfLKMH4.d.mts +0 -290
- package/dist/interface-BIpgzbZl.mjs +0 -17
- package/dist/redis-stream-BPKscsTB.d.mts +0 -260
- package/dist/registerRoute-DRsSHPSM.mjs +0 -364
- package/dist/sync/changelog.d.mts +0 -3
- package/dist/sync/changelog.mjs +0 -3
|
@@ -1,364 +0,0 @@
|
|
|
1
|
-
/*! @classytic/arc v2.41.0 | MIT | https://classytic.com */
|
|
2
|
-
import { n as evaluateAndApplyPermission } from "./authorizationDecision-2YY_Kaj1.mjs";
|
|
3
|
-
import { f as createError } from "./errors-C_YhELhe.mjs";
|
|
4
|
-
import { u as getRequestScope } from "./types-CDy3-zw1.mjs";
|
|
5
|
-
import { r as deriveHiddenFieldPermissions } from "./fields-BTyPLHK0.mjs";
|
|
6
|
-
import { n as requestContext } from "./requestContext-cSVKnYPk.mjs";
|
|
7
|
-
//#region src/core/middlewares/arcDecorator.ts
|
|
8
|
-
/**
|
|
9
|
-
* Build the `arcDecorator` preHandler for a resource.
|
|
10
|
-
*
|
|
11
|
-
* The decorator is a closure over frozen metadata — allocated once per
|
|
12
|
-
* resource and shared across every request. Stamps `req.arc` with the
|
|
13
|
-
* resource's field permissions, hooks, events bus, and schema options
|
|
14
|
-
* so `sendControllerResponse`, `BaseController.run*`, and custom
|
|
15
|
-
* middleware can read a consistent view.
|
|
16
|
-
*
|
|
17
|
-
* Also populates `requestContext.resourceName` for async-context access
|
|
18
|
-
* in code paths that can't reach `req.arc` directly (e.g. detached logger
|
|
19
|
-
* formatters).
|
|
20
|
-
*/
|
|
21
|
-
function buildArcDecorator(meta) {
|
|
22
|
-
const frozen = Object.freeze({
|
|
23
|
-
...meta,
|
|
24
|
-
fields: deriveHiddenFieldPermissions(meta.schemaOptions, meta.fields)
|
|
25
|
-
});
|
|
26
|
-
return async (req, _reply) => {
|
|
27
|
-
req.arc = frozen;
|
|
28
|
-
const store = requestContext.get();
|
|
29
|
-
if (store) store.resourceName = frozen.resourceName;
|
|
30
|
-
};
|
|
31
|
-
}
|
|
32
|
-
//#endregion
|
|
33
|
-
//#region src/core/middlewares/auth.ts
|
|
34
|
-
/**
|
|
35
|
-
* A permission requires authentication unless it carries the `_isPublic`
|
|
36
|
-
* marker set by `allowPublic()`. Absence of a permission is treated as
|
|
37
|
-
* public (no auth) — matches historical CRUD behaviour.
|
|
38
|
-
*/
|
|
39
|
-
function requiresAuthentication(permission) {
|
|
40
|
-
if (!permission) return false;
|
|
41
|
-
return !permission._isPublic;
|
|
42
|
-
}
|
|
43
|
-
/**
|
|
44
|
-
* Pick the right Fastify auth decorator for a single-permission route:
|
|
45
|
-
* - protected route → `fastify.authenticate` (401 on missing token)
|
|
46
|
-
* - public route → `fastify.optionalAuthenticate` (parses token if present)
|
|
47
|
-
*
|
|
48
|
-
* Public routes still get optional auth so downstream multi-tenant filters
|
|
49
|
-
* can narrow queries when a Bearer token IS supplied.
|
|
50
|
-
*/
|
|
51
|
-
function buildAuthMiddleware(fastify, permission) {
|
|
52
|
-
if (requiresAuthentication(permission)) return fastify.authenticate ?? null;
|
|
53
|
-
return fastify.optionalAuthenticate ?? null;
|
|
54
|
-
}
|
|
55
|
-
/**
|
|
56
|
-
* Pick the right auth decorator for a multi-permission route (Action router).
|
|
57
|
-
*
|
|
58
|
-
* The input is the array of resolved per-action permissions — one slot per
|
|
59
|
-
* action, in registration order, already flattened against `globalAuth`
|
|
60
|
-
* fallback by the caller (`actionPermissions[name] ?? globalAuth`). A slot
|
|
61
|
-
* may be `undefined` when the action has no per-action check AND no
|
|
62
|
-
* `globalAuth` fallback — that is "public by omission" and must be honored
|
|
63
|
-
* here the same way `buildActionPermissionMw` honors it (by skipping the
|
|
64
|
-
* permission evaluation entirely). If we filtered undefineds out at this
|
|
65
|
-
* layer, a mixed endpoint like `{ ping: undefined, promote: requireRoles(...) }`
|
|
66
|
-
* would collapse to "all protected" and 401 the public `ping` action at the
|
|
67
|
-
* auth layer before the permission prehandler could let it through.
|
|
68
|
-
*
|
|
69
|
-
* Rules:
|
|
70
|
-
* - ALL public (explicit allowPublic OR omission) → `optionalAuthenticate`
|
|
71
|
-
* - ALL protected → `authenticate` (fail-fast)
|
|
72
|
-
* - MIXED → `optionalAuthenticate`
|
|
73
|
-
* (parse token if present; per-action check fails-closed when user=null)
|
|
74
|
-
*
|
|
75
|
-
* The mixed case was previously handled by an in-handler
|
|
76
|
-
* `fastify.authenticate()` call that bypassed the preHandler chain; this
|
|
77
|
-
* helper moves that logic back into the preHandler stack so the request
|
|
78
|
-
* lifecycle is consistent across router types.
|
|
79
|
-
*/
|
|
80
|
-
function buildAuthMiddlewareForPermissions(fastify, permissions) {
|
|
81
|
-
if (permissions.length === 0) return fastify.optionalAuthenticate ?? null;
|
|
82
|
-
const hasProtected = permissions.some((p) => requiresAuthentication(p));
|
|
83
|
-
const hasPublic = permissions.some((p) => p && p._isPublic === true) || permissions.some((p) => !p);
|
|
84
|
-
if (hasProtected && !hasPublic) return fastify.authenticate ?? null;
|
|
85
|
-
return fastify.optionalAuthenticate ?? null;
|
|
86
|
-
}
|
|
87
|
-
//#endregion
|
|
88
|
-
//#region src/core/middlewares/chain.ts
|
|
89
|
-
/**
|
|
90
|
-
* Compose the route-level `onRequest` + `preHandler` arrays in the canonical
|
|
91
|
-
* order. Every null/undefined entry is dropped. Keeps the three routers from
|
|
92
|
-
* accidentally ordering the same ingredients differently (regression risk:
|
|
93
|
-
* cache before auth → user-scoped cache keys leak across users; auth after
|
|
94
|
-
* body parse → anonymous schema probing).
|
|
95
|
-
*
|
|
96
|
-
* onRequest constraints (Fastify lifecycle): `request.body` is undefined —
|
|
97
|
-
* everything placed there must be header/cookie/param/query-driven. arc's
|
|
98
|
-
* `authenticate` / `optionalAuthenticate` qualify by contract (Bearer
|
|
99
|
-
* header, session cookie, api-key header); `preAuth` handlers exist
|
|
100
|
-
* precisely to promote query tokens into headers (SSE `?token=`); the arc
|
|
101
|
-
* decorator stamps static route metadata.
|
|
102
|
-
*/
|
|
103
|
-
function buildRouteHooks(parts) {
|
|
104
|
-
return {
|
|
105
|
-
onRequest: [
|
|
106
|
-
...parts.preAuth ?? [],
|
|
107
|
-
parts.arcDecorator,
|
|
108
|
-
parts.authMw ?? null
|
|
109
|
-
].filter(Boolean),
|
|
110
|
-
preHandler: [
|
|
111
|
-
parts.permissionMw ?? null,
|
|
112
|
-
parts.pluginMw ?? null,
|
|
113
|
-
...parts.routeGuards ?? [],
|
|
114
|
-
...parts.customMws ?? []
|
|
115
|
-
].filter(Boolean)
|
|
116
|
-
};
|
|
117
|
-
}
|
|
118
|
-
/**
|
|
119
|
-
* Spread helper — turns `RouteHookChains` into the two Fastify route-option
|
|
120
|
-
* fields, omitting empty arrays so routes without middleware stay
|
|
121
|
-
* option-free (Fastify skips absent hooks faster than empty arrays).
|
|
122
|
-
*/
|
|
123
|
-
function routeHookOptions(hooks) {
|
|
124
|
-
return {
|
|
125
|
-
...hooks.onRequest.length > 0 ? { onRequest: hooks.onRequest } : {},
|
|
126
|
-
...hooks.preHandler.length > 0 ? { preHandler: hooks.preHandler } : {}
|
|
127
|
-
};
|
|
128
|
-
}
|
|
129
|
-
/**
|
|
130
|
-
* `RouteDefinition.preHandler` accepts two shapes:
|
|
131
|
-
*
|
|
132
|
-
* 1. **Array form** — `RouteHandlerMethod[]`. Used directly.
|
|
133
|
-
* 2. **Factory form** — `(fastify) => RouteHandlerMethod[]`. Called once at
|
|
134
|
-
* route-registration time with the Fastify instance, so handlers can
|
|
135
|
-
* capture decorators (`fastify.authenticate`, `fastify.events`, etc.)
|
|
136
|
-
* that aren't on the request.
|
|
137
|
-
*
|
|
138
|
-
* The two forms are equally idiomatic, but the discrimination is by
|
|
139
|
-
* `typeof preHandler === "function"`. Single-function shapes such as
|
|
140
|
-
* `multipartBody({...})` (a `RouteHandlerMethod`) **structurally satisfy
|
|
141
|
-
* the factory branch** at the call site, then fail with a cryptic
|
|
142
|
-
* `Cannot read properties of undefined (reading 'content-type')` once the
|
|
143
|
-
* handler runs with `fastify` in the request slot.
|
|
144
|
-
*
|
|
145
|
-
* This resolver:
|
|
146
|
-
* 1. Distinguishes the two valid shapes.
|
|
147
|
-
* 2. Validates the factory's RETURN — must be an array of functions.
|
|
148
|
-
* 3. Throws an actionable error pointing at the route + the fix when
|
|
149
|
-
* a single `RouteHandlerMethod` was passed instead of an array, OR
|
|
150
|
-
* when a factory returned the wrong shape.
|
|
151
|
-
*
|
|
152
|
-
* The error message names the route (`{method} {path}`) and the
|
|
153
|
-
* canonical fix (`preHandler: [yourHandler]`) so the failure mode is
|
|
154
|
-
* obvious instead of debug-archaeology.
|
|
155
|
-
*
|
|
156
|
-
* @param preHandler The `route.preHandler` value (any of the valid shapes
|
|
157
|
-
* plus the common bare-handler mistake).
|
|
158
|
-
* @param fastify Passed to factory-form preHandlers.
|
|
159
|
-
* @param routeId `"GET /todos/:id/attach"` (or similar) — used in the
|
|
160
|
-
* error message so a multi-route file points at the
|
|
161
|
-
* actual offender.
|
|
162
|
-
*/
|
|
163
|
-
function resolveRoutePreHandlers(preHandler, fastify, routeId) {
|
|
164
|
-
if (preHandler === void 0 || preHandler === null) return [];
|
|
165
|
-
if (Array.isArray(preHandler)) return preHandler.filter((h) => typeof h === "function");
|
|
166
|
-
if (typeof preHandler === "function") {
|
|
167
|
-
let result;
|
|
168
|
-
try {
|
|
169
|
-
result = preHandler(fastify);
|
|
170
|
-
} catch (err) {
|
|
171
|
-
const msg = err instanceof Error ? err.message : String(err);
|
|
172
|
-
throw new TypeError(`Route ${routeId}: preHandler factory threw during route registration: ${msg}.\nIf you intended to pass a single handler (e.g. \`multipartBody({...})\`), wrap it in an array: \`preHandler: [yourHandler]\`. The factory form is \`(fastify) => RouteHandlerMethod[]\` — it must return an array.`, { cause: err instanceof Error ? err : void 0 });
|
|
173
|
-
}
|
|
174
|
-
if (result && typeof result.then === "function") result.catch(() => void 0);
|
|
175
|
-
if (!Array.isArray(result)) throw new TypeError(`Route ${routeId}: preHandler factory must return an array of handlers, got ${describeValue(result)}.\nCommon cause: passing a single \`RouteHandlerMethod\` (e.g. \`multipartBody({...})\`) where an array was expected. Wrap it: \`preHandler: [yourHandler]\`. The factory form \`(fastify) => RouteHandlerMethod[]\` is for cases that need the Fastify instance — e.g. \`(fastify) => [fastify.authenticate, myHandler]\`.`);
|
|
176
|
-
return result.filter((h) => typeof h === "function");
|
|
177
|
-
}
|
|
178
|
-
throw new TypeError(`Route ${routeId}: preHandler must be an array of handlers OR a factory \`(fastify) => RouteHandlerMethod[]\`. Got ${describeValue(preHandler)}.`);
|
|
179
|
-
}
|
|
180
|
-
function describeValue(v) {
|
|
181
|
-
if (v === null) return "null";
|
|
182
|
-
if (v === void 0) return "undefined";
|
|
183
|
-
if (typeof v === "function") return "a function (single handler — wrap in array)";
|
|
184
|
-
if (Array.isArray(v)) return `an array of length ${v.length}`;
|
|
185
|
-
return `${typeof v} (${JSON.stringify(v).slice(0, 80)})`;
|
|
186
|
-
}
|
|
187
|
-
//#endregion
|
|
188
|
-
//#region src/core/middlewares/permissions.ts
|
|
189
|
-
/**
|
|
190
|
-
* Build a PermissionContext from a Fastify request. Extracted so the CRUD
|
|
191
|
-
* permission middleware and the dynamic action-permission check use the same
|
|
192
|
-
* field layout — divergence here silently broke policy filters for actions.
|
|
193
|
-
*/
|
|
194
|
-
function buildPermissionContext(req, opts) {
|
|
195
|
-
const reqWithExtras = req;
|
|
196
|
-
const params = req.params;
|
|
197
|
-
return {
|
|
198
|
-
user: reqWithExtras.user ?? null,
|
|
199
|
-
scope: getRequestScope(req),
|
|
200
|
-
request: req,
|
|
201
|
-
resource: opts.resource,
|
|
202
|
-
action: opts.action,
|
|
203
|
-
resourceId: opts.resourceId ?? params?.id,
|
|
204
|
-
params,
|
|
205
|
-
data: opts.data ?? req.body
|
|
206
|
-
};
|
|
207
|
-
}
|
|
208
|
-
/**
|
|
209
|
-
* Static per-route CRUD permission gate. The permission and action are known
|
|
210
|
-
* at route-registration time, so the gate is a plain preHandler.
|
|
211
|
-
*
|
|
212
|
-
* Actions use the dynamic counterpart `buildActionPermissionMw` — their
|
|
213
|
-
* permission is resolved from `body.action` at request time.
|
|
214
|
-
*/
|
|
215
|
-
function buildCrudPermissionMw(permissionCheck, resourceName, action) {
|
|
216
|
-
if (!permissionCheck) return null;
|
|
217
|
-
return async (req, reply) => {
|
|
218
|
-
const context = buildPermissionContext(req, {
|
|
219
|
-
resource: resourceName,
|
|
220
|
-
action
|
|
221
|
-
});
|
|
222
|
-
await evaluateAndApplyPermission(permissionCheck, context, req, reply);
|
|
223
|
-
};
|
|
224
|
-
}
|
|
225
|
-
/**
|
|
226
|
-
* Dynamic per-action permission gate for the action router.
|
|
227
|
-
*
|
|
228
|
-
* Resolves the permission from `body.action` at request time and runs
|
|
229
|
-
* `evaluateAndApplyPermission` from the canonical `permissionMw` slot — so
|
|
230
|
-
* `_policyFilters` and `request.scope` are installed BEFORE `pluginMw`
|
|
231
|
-
* (idempotency) and `routeGuards` run. Previously this check lived inside
|
|
232
|
-
* the main action handler, which meant idempotency recorded unauthorized
|
|
233
|
-
* requests and route guards saw unfiltered scope — the very divergence
|
|
234
|
-
* routerShared exists to prevent.
|
|
235
|
-
*
|
|
236
|
-
* Also acts as a defensive fallback for invalid action names — the
|
|
237
|
-
* `oneOf` body schema normally rejects these at AJV validation, but
|
|
238
|
-
* hosts that disable schema validation still get a 400 here.
|
|
239
|
-
*/
|
|
240
|
-
function buildActionPermissionMw(actionEnum, actionPermissions, globalAuth, resourceName) {
|
|
241
|
-
const enumSet = new Set(actionEnum);
|
|
242
|
-
const validActions = [...actionEnum];
|
|
243
|
-
return async (req, reply) => {
|
|
244
|
-
const body = req.body ?? {};
|
|
245
|
-
const action = body.action;
|
|
246
|
-
if (!action || !enumSet.has(action)) throw createError(400, `Invalid action '${action ?? ""}'. Valid actions: ${validActions.join(", ")}`, { validActions });
|
|
247
|
-
const permissionCheck = actionPermissions[action] ?? globalAuth;
|
|
248
|
-
if (!permissionCheck) return;
|
|
249
|
-
const { action: _discard, ...data } = body;
|
|
250
|
-
const params = req.params;
|
|
251
|
-
const context = buildPermissionContext(req, {
|
|
252
|
-
resource: resourceName,
|
|
253
|
-
action,
|
|
254
|
-
resourceId: params?.id,
|
|
255
|
-
data
|
|
256
|
-
});
|
|
257
|
-
await evaluateAndApplyPermission(permissionCheck, context, req, reply, { defaultDenialMessage: (user) => user ? `Permission denied for '${action}'` : "Authentication required" });
|
|
258
|
-
};
|
|
259
|
-
}
|
|
260
|
-
//#endregion
|
|
261
|
-
//#region src/core/middlewares/pluginMw.ts
|
|
262
|
-
/**
|
|
263
|
-
* Pick the request-lifecycle plugin middleware for an HTTP method:
|
|
264
|
-
* - GET / HEAD → response cache (if present)
|
|
265
|
-
* - POST / PUT / PATCH → idempotency (if present)
|
|
266
|
-
* - DELETE → none
|
|
267
|
-
*
|
|
268
|
-
* Either field may be `null` if the corresponding plugin wasn't registered.
|
|
269
|
-
*/
|
|
270
|
-
function selectPluginMw(method, mws) {
|
|
271
|
-
const upper = method.toUpperCase();
|
|
272
|
-
if (upper === "GET" || upper === "HEAD") return mws.cacheMw;
|
|
273
|
-
if (upper === "POST" || upper === "PUT" || upper === "PATCH") return mws.idempotencyMw;
|
|
274
|
-
return null;
|
|
275
|
-
}
|
|
276
|
-
/**
|
|
277
|
-
* Resolve the default cache/idempotency middlewares for a resource.
|
|
278
|
-
*
|
|
279
|
-
* Skips response-cache when the resource has QueryCache active — QueryCache
|
|
280
|
-
* handles caching at the controller level with SWR, so the HTTP-level
|
|
281
|
-
* response-cache would double-cache.
|
|
282
|
-
*/
|
|
283
|
-
function resolveRouterPluginMw(fastify, resourceHasQueryCache) {
|
|
284
|
-
return {
|
|
285
|
-
cacheMw: !resourceHasQueryCache && fastify.hasDecorator("responseCache") ? fastify.responseCache.middleware : null,
|
|
286
|
-
idempotencyMw: fastify.hasDecorator("idempotency") ? fastify.idempotency.middleware : null
|
|
287
|
-
};
|
|
288
|
-
}
|
|
289
|
-
//#endregion
|
|
290
|
-
//#region src/core/middlewares/rateLimit.ts
|
|
291
|
-
/**
|
|
292
|
-
* Build the per-route Fastify `config` object shared by the CRUD, custom, and
|
|
293
|
-
* action routers. Two independent concerns ride on `routeOptions.config`:
|
|
294
|
-
* - `config.rateLimit` — read by `@fastify/rate-limit`.
|
|
295
|
-
* - `config.cors` — read by `@fastify/cors` as a per-route override.
|
|
296
|
-
* - `config.arcExtensions` — read by arc plugins (encryption, …) at request
|
|
297
|
-
* time, sourced from `defineResource({ extensions })`.
|
|
298
|
-
*
|
|
299
|
-
* Returns `{}` (no `config` key) when neither is present, so routes without
|
|
300
|
-
* either stay config-free and the spread is a genuine no-op. Both fields are
|
|
301
|
-
* optional in the return type, so no cast is needed when only one is set.
|
|
302
|
-
*/
|
|
303
|
-
function buildRouteConfig(rateLimitConfig, extensions, cors) {
|
|
304
|
-
const hasCors = cors !== void 0;
|
|
305
|
-
if (!rateLimitConfig && !extensions && !hasCors) return {};
|
|
306
|
-
return { config: {
|
|
307
|
-
...rateLimitConfig ?? {},
|
|
308
|
-
...hasCors ? { cors } : {},
|
|
309
|
-
...extensions ? { arcExtensions: extensions } : {}
|
|
310
|
-
} };
|
|
311
|
-
}
|
|
312
|
-
/**
|
|
313
|
-
* Build the `config` object for Fastify route options so
|
|
314
|
-
* @fastify/rate-limit picks up per-route overrides.
|
|
315
|
-
*
|
|
316
|
-
* - `undefined` → no override (inherits instance config)
|
|
317
|
-
* - `false` → explicitly disable rate limiting
|
|
318
|
-
* - `{ max, timeWindow }` → apply that limit
|
|
319
|
-
*/
|
|
320
|
-
function buildRateLimitConfig(rateLimit) {
|
|
321
|
-
if (rateLimit === void 0) return void 0;
|
|
322
|
-
if (rateLimit === false) return { rateLimit: false };
|
|
323
|
-
return { rateLimit: {
|
|
324
|
-
max: rateLimit.max,
|
|
325
|
-
timeWindow: rateLimit.timeWindow
|
|
326
|
-
} };
|
|
327
|
-
}
|
|
328
|
-
//#endregion
|
|
329
|
-
//#region src/core/middlewares/registerRoute.ts
|
|
330
|
-
/**
|
|
331
|
-
* Register a route with friendly handling of `FST_ERR_DUPLICATED_ROUTE`.
|
|
332
|
-
*
|
|
333
|
-
* Boot-time `validateRouteCrudCollisions()` catches custom-route → auto-CRUD
|
|
334
|
-
* overlaps before Fastify ever runs, but a few cases still slip through to
|
|
335
|
-
* Fastify itself: two presets contributing the same `routes` entry, hosts
|
|
336
|
-
* mounting two resources at the same prefix, custom routes that share a
|
|
337
|
-
* URL with another plugin. Fastify's default message ("Method 'GET'
|
|
338
|
-
* already declared for route '/'") gives no hint at the resource-shaped
|
|
339
|
-
* fix — `disabledRoutes: ['list']` — so wrap it with one.
|
|
340
|
-
*/
|
|
341
|
-
function tryRegisterRoute(fastify, opts, context) {
|
|
342
|
-
try {
|
|
343
|
-
fastify.route(opts);
|
|
344
|
-
} catch (err) {
|
|
345
|
-
const fastifyErr = err;
|
|
346
|
-
if (fastifyErr?.code !== "FST_ERR_DUPLICATED_ROUTE") throw err;
|
|
347
|
-
const method = opts.method;
|
|
348
|
-
const url = opts.url;
|
|
349
|
-
const opHint = context.op ? ` (op: ${context.op})` : "";
|
|
350
|
-
const fixHint = context.op && [
|
|
351
|
-
"list",
|
|
352
|
-
"get",
|
|
353
|
-
"create",
|
|
354
|
-
"update",
|
|
355
|
-
"delete"
|
|
356
|
-
].includes(context.op) ? ` Suppress the auto-CRUD route with \`disabledRoutes: ['${context.op}']\`.` : " If a custom route in `routes:` collides with auto-CRUD, add `disabledRoutes: ['list' | 'get' | 'create' | 'update' | 'delete']` to `defineResource()` to suppress the matching auto-CRUD route.";
|
|
357
|
-
const enhanced = /* @__PURE__ */ new Error(`[arc] Duplicate route ${method} ${url} on resource "${context.resourceName}"${opHint}.${fixHint} Original: ${fastifyErr.message ?? "FST_ERR_DUPLICATED_ROUTE"}`);
|
|
358
|
-
enhanced.code = "FST_ERR_DUPLICATED_ROUTE";
|
|
359
|
-
enhanced.cause = err;
|
|
360
|
-
throw enhanced;
|
|
361
|
-
}
|
|
362
|
-
}
|
|
363
|
-
//#endregion
|
|
364
|
-
export { selectPluginMw as a, buildRouteHooks as c, buildAuthMiddleware as d, buildAuthMiddlewareForPermissions as f, resolveRouterPluginMw as i, resolveRoutePreHandlers as l, buildRateLimitConfig as n, buildActionPermissionMw as o, buildArcDecorator as p, buildRouteConfig as r, buildCrudPermissionMw as s, tryRegisterRoute as t, routeHookOptions as u };
|
|
@@ -1,3 +0,0 @@
|
|
|
1
|
-
|
|
2
|
-
import { ChangeEntry, ChangeLogAppendOptions, ChangeLogStore, ChangeOp, ChangesPage, ChangesSinceOptions, CursorExpiredError, MemoryChangeLogStore, PushMutation, PushVerdict, PushVerdictStatus } from "@classytic/repo-core/sync";
|
|
3
|
-
export { type ChangeEntry, type ChangeLogAppendOptions, type ChangeLogStore, type ChangeOp, type ChangesPage, type ChangesSinceOptions, CursorExpiredError, MemoryChangeLogStore, type PushMutation, type PushVerdict, type PushVerdictStatus };
|
package/dist/sync/changelog.mjs
DELETED