@classytic/arc 2.41.1 → 2.44.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-DSqD_Xsg.mjs → EventTransport-DZWDsaPa.mjs} +22 -17
- package/dist/{HookSystem-CMF94SEA.mjs → HookSystem-yjEgn7Cm.mjs} +2 -2
- package/dist/{QueryCache-DFA-QrU9.d.mts → QueryCache--5uAuYPW.d.mts} +7 -3
- package/dist/{ResourceRegistry-MGpXoxHZ.mjs → ResourceRegistry-DJxHUpE4.mjs} +44 -13
- package/dist/acquire-BozJ-9yt.mjs +35 -0
- package/dist/actionPermissions-5N04zBL4.mjs +772 -0
- package/dist/audit/index.d.mts +46 -8
- package/dist/audit/index.mjs +48 -26
- package/dist/auth/audit.d.mts +8 -8
- package/dist/auth/audit.mjs +1 -1
- package/dist/auth/index.d.mts +46 -16
- package/dist/auth/index.mjs +134 -156
- package/dist/auth/redis-session.d.mts +4 -5
- package/dist/auth/redis-session.mjs +1 -1
- package/dist/{authorizationDecision-DawKGoXb.mjs → authorizationDecision-wX3SA9w_.mjs} +7 -102
- package/dist/{base-CxrV1LmG.d.mts → base-CZyixEQR.d.mts} +27 -5
- package/dist/boundedMap-DWiKW7lP.mjs +57 -0
- package/dist/{buildHandler-CvxKOc0m.mjs → buildHandler-Bswu_vNb.mjs} +4 -4
- package/dist/cache/index.d.mts +31 -43
- package/dist/cache/index.mjs +4 -4
- package/dist/{caching-DbCBxlgw.mjs → caching-Drp68DIS.mjs} +3 -3
- package/dist/claimed-DYvQoZLv.mjs +8 -0
- package/dist/cleanup/index.d.mts +18 -22
- package/dist/cleanup/index.mjs +27 -73
- package/dist/cli/commands/describe.d.mts +7 -5
- package/dist/cli/commands/describe.mjs +7 -4
- package/dist/cli/commands/docs.d.mts +2 -3
- package/dist/cli/commands/docs.mjs +3 -3
- package/dist/cli/commands/doctor.d.mts +2 -3
- package/dist/cli/commands/doctor.mjs +1 -1
- package/dist/cli/commands/generate.mjs +2 -3
- package/dist/cli/commands/init.mjs +2 -3
- package/dist/cli/commands/introspect.d.mts +2 -3
- package/dist/cli/commands/introspect.mjs +2 -2
- package/dist/cli/index.mjs +3 -3
- package/dist/{constants-D5Ubn1fe.mjs → constants-B2QHb6u6.mjs} +4 -2
- package/dist/context/index.d.mts +11 -14
- 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-5lazhva9.mjs → core-COcqo0Cp.mjs} +15 -5
- package/dist/{createActionRouter-DNqoaK81.mjs → createActionRouter-zG13rpPJ.mjs} +97 -39
- package/dist/{createAggregationRouter-BcbQJf10.mjs → createAggregationRouter-CWRz96Vr.mjs} +11 -8
- package/dist/{preloadResources-f3boenVZ.mjs → createApp-BGLYtErt.mjs} +305 -394
- package/dist/{createMcpServer-CbDWkZ1T.d.mts → createMcpServer-DSztofa9.d.mts} +1 -1
- package/dist/{defineAggregation-D_eJjm5A.mjs → defineAggregation--3xguLkk.mjs} +1 -1
- package/dist/{defineResource-DxkMTjRn.mjs → defineResource-gUp9ug66.mjs} +118 -1024
- package/dist/discovery/index.d.mts +6 -7
- package/dist/discovery/index.mjs +1 -1
- package/dist/docs/index.d.mts +11 -5
- package/dist/docs/index.mjs +4 -4
- package/dist/{elevation-CkvG0LG2.mjs → elevation-BG4gnMsJ.mjs} +41 -12
- package/dist/{elevation-BXCfVcNG.d.mts → elevation-BW2J3tNj.d.mts} +1 -2
- package/dist/encryption/index.d.mts +5 -5
- package/dist/encryption/index.mjs +3 -3
- package/dist/{errorHandler-D1GHKGlV.mjs → errorHandler-COHw3N1b.mjs} +26 -5
- package/dist/{errors-BH7YU_9O.mjs → errors-CW4anJBy.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-zZY-unhr.mjs → eventPlugin-BweHAVws.mjs} +82 -21
- package/dist/{subscribe-helpers-DatDkcuG.mjs → eventTypes-N4-omQFR.mjs} +151 -17
- package/dist/events/index.d.mts +43 -18
- 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 +5 -6
- package/dist/events/transports/redis.mjs +2 -2
- package/dist/factory/index.d.mts +42 -27
- package/dist/factory/index.mjs +213 -32
- package/dist/{fastify-augmentation-Cq2nHVaW.d.mts → fastify-augmentation-DRlCIWE9.d.mts} +11 -25
- package/dist/{fields-BHrkoF5y.mjs → fields-A5crhsJb.mjs} +1 -1
- package/dist/{fields-I_EeRWND.d.mts → fields-BBSpf8pS.d.mts} +1 -1
- package/dist/filter-merge-DGM-GQ1J.mjs +99 -0
- package/dist/{generate-BLRf2m3y.mjs → generate-BXKo2Gc8.mjs} +2 -2
- package/dist/{headers-BIXWKrzQ.mjs → headers-DKZQKL-Z.mjs} +2 -2
- package/dist/health-BYAwA6YS.mjs +22 -0
- package/dist/{health-ZRHeDHXH.mjs → health-CrqUp3sz.mjs} +115 -55
- package/dist/hooks/index.d.mts +2 -2
- package/dist/hooks/index.mjs +2 -3
- package/dist/idempotency/index.d.mts +36 -10
- package/dist/idempotency/index.mjs +230 -111
- package/dist/idempotency/redis.d.mts +1 -1
- package/dist/idempotency/redis.mjs +77 -12
- package/dist/{index-D4u76eQW.d.mts → index-Beg3aNnK.d.mts} +23 -101
- package/dist/{preloadResources-CjS2OfpU.d.mts → index-BkV55WlV.d.mts} +37 -38
- package/dist/{index-BHdZD8zK.d.mts → index-Bxriq3so.d.mts} +84 -21
- package/dist/index-CUJvoyw3.d.mts +510 -0
- package/dist/{index-CbJiYrfv.d.mts → index-Chgz3d0i.d.mts} +1 -1
- package/dist/{index-DHbyPo5j.d.mts → index-WH7xgvxS.d.mts} +304 -254
- package/dist/index.d.mts +9 -9
- package/dist/index.mjs +11 -11
- package/dist/{init-CpLkjU18.mjs → init-BPzK5Lnr.mjs} +1 -1
- package/dist/integrations/event-gateway.d.mts +11 -7
- 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 +102 -89
- package/dist/integrations/mcp/index.mjs +19 -4
- package/dist/integrations/mcp/testing.d.mts +8 -9
- package/dist/integrations/mcp/testing.mjs +2 -2
- package/dist/integrations/streamline.d.mts +85 -144
- package/dist/integrations/streamline.mjs +131 -55
- package/dist/integrations/webhooks.d.mts +83 -143
- package/dist/integrations/webhooks.mjs +36 -80
- package/dist/integrations/websocket-pushref-redis.d.mts +5 -6
- package/dist/integrations/websocket-pushref-redis.mjs +1 -1
- package/dist/integrations/websocket-redis.d.mts +5 -6
- 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-JJzwH4Ro.d.mts} +34 -2
- package/dist/{jsonBody-ELR9lfeU.mjs → jsonBody-YIq-vGLv.mjs} +2 -2
- package/dist/{keys-cpZCtcIE.mjs → keys-CpoiZ8hU.mjs} +7 -18
- package/dist/{loadResources-BPfWiI5s.mjs → loadResources-6jO52-RR.mjs} +3 -3
- package/dist/{loadResourcesFromEntry-ixtfL3J2.mjs → loadResourcesFromEntry-MP8xEppu.mjs} +2 -2
- package/dist/logger/index.d.mts +9 -17
- package/dist/logger/index.mjs +2 -153
- package/dist/logger-D4V1zGLI.mjs +160 -0
- package/dist/{mcpPlugin-BX5PujI0.mjs → mcpPlugin-srPc7ItN.mjs} +446 -170
- package/dist/{memory-Iotz0cCx.mjs → memory-CTa5pOCW.mjs} +36 -34
- package/dist/{metrics-NMsmpRmY.mjs → metrics-DsZrvnpB.mjs} +39 -72
- package/dist/middleware/index.d.mts +5 -5
- package/dist/middleware/index.mjs +3 -3
- package/dist/migrations/index.d.mts +13 -13
- package/dist/migrations/index.mjs +2 -2
- package/dist/{module-DpLJG2_N.mjs → module--L0iBIZz.mjs} +8 -8
- package/dist/mongodb-memory-server-CHhYSq5y.mjs +35539 -0
- package/dist/mongoose-CYJCLyi-.mjs +67451 -0
- package/dist/multiTenant-CAtYlW6H.d.mts +104 -0
- package/dist/{multipartBody-DtRWM6_U.mjs → multipartBody-Cc7eZ9cq.mjs} +46 -7
- package/dist/node-D8UZv_8c.mjs +41236 -0
- package/dist/{openapi-CmFf3yq_.mjs → openapi-CMegfsm8.mjs} +48 -10
- package/dist/outbox-admin/index.d.mts +5 -5
- package/dist/outbox-admin/index.mjs +6 -18
- package/dist/{outbox-module-D2z5WkDY.mjs → outbox-module-Ryk270jd.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-D8TBH44E.mjs → permissions-C5ijIz5Q.mjs} +149 -33
- package/dist/{pipe-FMWNheSO.mjs → pipe-D81zMtWs.mjs} +2 -2
- package/dist/pipeline/index.d.mts +7 -7
- package/dist/pipeline/index.mjs +2 -2
- package/dist/plan-CJGMKoui.mjs +565 -0
- package/dist/{BaseController-CYEwjyjN.mjs → plugin-DALd9dcF.mjs} +1269 -346
- package/dist/plugins/index.d.mts +17 -14
- package/dist/plugins/index.mjs +44 -22
- package/dist/plugins/response-cache.d.mts +5 -6
- package/dist/plugins/response-cache.mjs +8 -14
- package/dist/plugins/tracing-entry.mjs +2 -2
- package/dist/{pluralize-CXWZIzTv.mjs → pluralize-COUNYo3d.mjs} +1 -1
- package/dist/presets/filesUpload.d.mts +8 -8
- package/dist/presets/filesUpload.mjs +5 -5
- package/dist/presets/index.d.mts +35 -40
- 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 +9 -10
- package/dist/presets/search.mjs +2 -2
- package/dist/{presets-ZJSPgmT2.mjs → presets-CONf2Qem.mjs} +99 -107
- package/dist/{projection-BzIgzoKZ.mjs → projection-wFoLqdss.mjs} +2 -2
- package/dist/{pushref-registry-Dov9l4qE.d.mts → pushref-registry-UxCkoA4C.d.mts} +1 -1
- package/dist/{queryCachePlugin-DZHPOm9p.mjs → queryCachePlugin-BATECpyh.mjs} +12 -14
- package/dist/{queryCachePlugin-BzqxconG.d.mts → queryCachePlugin-C2lZjTxk.d.mts} +1 -1
- package/dist/{rateLimitKey-BvOvcBDi.mjs → rateLimitKey-DVEZ_UpV.mjs} +1 -1
- package/dist/{realtime-8tIQWZz4.mjs → realtime-xyubMKSB.mjs} +25 -21
- package/dist/{redis-Bzi1y3F9.d.mts → redis-BDgv9Glw.d.mts} +17 -24
- package/dist/redis-stream-BMk-bpQS.d.mts +219 -0
- package/dist/redisShapes-CQksY67q.d.mts +55 -0
- package/dist/registry/index.d.mts +15 -10
- package/dist/registry/index.mjs +4 -4
- package/dist/{registry-DNkgGQcK.mjs → registry-Bdzg-hdm.mjs} +13 -10
- package/dist/{renewingLease-yF8BdW4b.mjs → renewingLease-BFJxGPuh.mjs} +1 -1
- package/dist/{replyHelpers-CNdvMfM4.mjs → replyHelpers-Bnc6lJyy.mjs} +2 -2
- package/dist/{requestContext-cDkAUr-P.mjs → requestContext-C10UeFe5.mjs} +1 -1
- package/dist/requirement-Bu50QALH.mjs +35 -0
- package/dist/requirement-CbiLdQ1n.d.mts +22 -0
- package/dist/resourceRateLimit-Wf6d6FL9.d.mts +30 -0
- package/dist/resourceRateLimit-urWPJxrt.mjs +180 -0
- package/dist/rolldown-runtime-MYrLNGnq.mjs +45 -0
- package/dist/routeValidator-DHtpzrzk.mjs +70 -0
- package/dist/{runtimeCapabilities-CkhS6jn6.mjs → runtimeCapabilities-DIMKMV2G.mjs} +1 -1
- package/dist/{schedules-j6uXXAeJ.mjs → schedules-CKTs5ys8.mjs} +46 -37
- package/dist/{schemaConverter-BVA7cFRG.mjs → schemaConverter-BVjkQ5DD.mjs} +2 -2
- package/dist/{schemaIR-CTvPDoPB.mjs → schemaIR-BA0Hb21Y.mjs} +2 -2
- package/dist/schemas/index.d.mts +20 -10
- package/dist/schemas/index.mjs +8 -3
- package/dist/scim/index.d.mts +13 -13
- package/dist/scim/index.mjs +2 -2
- package/dist/scope/index.d.mts +4 -4
- package/dist/scope/index.mjs +6 -6
- package/dist/{scope-BUi1xV1I.mjs → scope-DINaj7Ax.mjs} +4 -4
- package/dist/shutdown-LpLMH8T1.mjs +59 -0
- package/dist/{simpleEqualityMatcher-C9bzq2gO.mjs → simpleEqualityMatcher-DbwIM1f1.mjs} +1 -1
- package/dist/{sse-BeqVKQKu.mjs → sse-BzJFPX_1.mjs} +11 -15
- package/dist/{sseStream-Ppzo-WUH.mjs → sseStream-DQChzGTs.mjs} +2 -2
- package/dist/{store-helpers-dW71XVWL.mjs → store-helpers-Coj9RhOZ.mjs} +1 -1
- package/dist/{streaming-IcRFjWQU.mjs → streaming-JzB5CAga.mjs} +1 -1
- package/dist/subscriberIdentity-DeyppIif.mjs +28 -0
- package/dist/tenancy-DxV8OuX2.mjs +43 -0
- package/dist/tenantPreparation-CBvvAcAj.mjs +67 -0
- package/dist/testing/cleanupStoreContract.d.mts +4 -5
- package/dist/testing/cleanupStoreContract.mjs +1 -1
- package/dist/testing/index.d.mts +215 -197
- package/dist/testing/index.mjs +289 -214
- package/dist/testing/outboxStoreContract.d.mts +4 -5
- package/dist/testing/outboxStoreContract.mjs +137 -2
- package/dist/testing/requiredPorts.d.mts +16 -0
- package/dist/testing/requiredPorts.mjs +65 -0
- package/dist/testing/storageContract.d.mts +4 -5
- package/dist/testing/storageContract.mjs +1 -1
- package/dist/timing-CntavTlR.mjs +46 -0
- package/dist/{transactionContext-CPbdlnVw.mjs → transactionContext-orvnGu4F.mjs} +1 -1
- package/dist/{typeGuards-BLjPgVBm.mjs → typeGuards-kEaP_WmO.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-DfVDvkdP.d.mts → types-BJ-2XqVF.d.mts} +189 -204
- package/dist/{types-CWQn9-IC.d.mts → types-D3Ixsh00.d.mts} +22 -9
- package/dist/{types-CgenB1gv.mjs → types-fvO6Ovzx.mjs} +4 -4
- package/dist/usage/index.d.mts +5 -6
- package/dist/usage/index.mjs +5 -5
- package/dist/{userHelpers-ChRvkuSZ.mjs → userHelpers-C76zD5pa.mjs} +1 -1
- package/dist/utils/index.d.mts +125 -264
- package/dist/utils/index.mjs +13 -12
- package/dist/{utils-CxcctrAh.mjs → utils-BpkVZ8rB.mjs} +315 -404
- package/dist/{validate-DrYWnOzI.mjs → validate-CWp-lsrm.mjs} +6 -14
- package/dist/{versioning-D6JIK8Tk.d.mts → versioning-Bsb51gGT.d.mts} +28 -13
- package/dist/{versioning-DKkT4hNJ.mjs → versioning-CHhL9P3o.mjs} +4 -4
- package/dist/{websocket-BOXVggMf.d.mts → websocket-Cs4awVyD.d.mts} +1 -1
- package/dist/{workScope-CxKqoIFn.mjs → workScope-n9a7d_lZ.mjs} +2 -2
- package/package.json +57 -59
- 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-BoPGQ5-4.mjs +0 -350
- package/dist/index-CFfLKMH4.d.mts +0 -290
- package/dist/interface-YzIbF7Ii.mjs +0 -17
- package/dist/redis-stream-BPKscsTB.d.mts +0 -260
- package/dist/registerRoute-Bsnk6NiZ.mjs +0 -364
- package/dist/rolldown-runtime-eysyzFAF.mjs +0 -15
- package/dist/sync/changelog.d.mts +0 -3
- package/dist/sync/changelog.mjs +0 -3
|
@@ -1,3 +1,2 @@
|
|
|
1
|
-
/*! @classytic/arc v2.
|
|
2
|
-
|
|
3
|
-
export { init };
|
|
1
|
+
/*! @classytic/arc v2.44.0 | MIT | https://classytic.com */
|
|
2
|
+
export { t as init } from "../../init-BPzK5Lnr.mjs";
|
|
@@ -6,6 +6,5 @@
|
|
|
6
6
|
* Shows all registered resources and their configuration.
|
|
7
7
|
* Requires an entry file that exports defineResource() results.
|
|
8
8
|
*/
|
|
9
|
-
declare function introspect(args: string[]): Promise<void>;
|
|
10
|
-
//#endregion
|
|
11
|
-
export { introspect };
|
|
9
|
+
export declare function introspect(args: string[]): Promise<void>;
|
|
10
|
+
//#endregion
|
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
/*! @classytic/arc v2.
|
|
2
|
-
import { t as loadResourcesFromEntry } from "../../loadResourcesFromEntry-
|
|
1
|
+
/*! @classytic/arc v2.44.0 | MIT | https://classytic.com */
|
|
2
|
+
import { t as loadResourcesFromEntry } from "../../loadResourcesFromEntry-MP8xEppu.mjs";
|
|
3
3
|
//#region src/cli/commands/introspect.ts
|
|
4
4
|
function describePermission(value) {
|
|
5
5
|
if (!value) return "none";
|
package/dist/cli/index.mjs
CHANGED
|
@@ -1,8 +1,8 @@
|
|
|
1
|
-
/*! @classytic/arc v2.
|
|
1
|
+
/*! @classytic/arc v2.44.0 | MIT | https://classytic.com */
|
|
2
2
|
import { describe } from "./commands/describe.mjs";
|
|
3
3
|
import { exportDocs } from "./commands/docs.mjs";
|
|
4
4
|
import { doctor } from "./commands/doctor.mjs";
|
|
5
|
-
import { t as generate } from "../generate-
|
|
6
|
-
import { t as init } from "../init-
|
|
5
|
+
import { t as generate } from "../generate-BXKo2Gc8.mjs";
|
|
6
|
+
import { t as init } from "../init-BPzK5Lnr.mjs";
|
|
7
7
|
import { introspect } from "./commands/introspect.mjs";
|
|
8
8
|
export { describe, doctor, exportDocs, generate, init, introspect };
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
/*! @classytic/arc v2.
|
|
1
|
+
/*! @classytic/arc v2.44.0 | MIT | https://classytic.com */
|
|
2
2
|
//#region src/constants.ts
|
|
3
3
|
/**
|
|
4
4
|
* Arc Framework Constants — Single Source of Truth
|
|
@@ -50,6 +50,8 @@ const DEFAULT_ID_FIELD = "_id";
|
|
|
50
50
|
const DEFAULT_TENANT_FIELD = "organizationId";
|
|
51
51
|
/** Default HTTP method for update routes */
|
|
52
52
|
const DEFAULT_UPDATE_METHOD = "PATCH";
|
|
53
|
+
/** Default version field under `concurrency: {}` (optimistic concurrency) */
|
|
54
|
+
const DEFAULT_VERSION_FIELD = "__v";
|
|
53
55
|
/** System-managed fields that cannot be set via request body */
|
|
54
56
|
const SYSTEM_FIELDS = Object.freeze([
|
|
55
57
|
"_id",
|
|
@@ -88,4 +90,4 @@ const RESERVED_QUERY_PARAMS = Object.freeze(/* @__PURE__ */ new Set([
|
|
|
88
90
|
"_policyFilters"
|
|
89
91
|
]));
|
|
90
92
|
//#endregion
|
|
91
|
-
export { DEFAULT_SORT as a,
|
|
93
|
+
export { DEFAULT_SORT as a, DEFAULT_VERSION_FIELD as c, MAX_FILTER_DEPTH as d, MAX_REGEX_LENGTH as f, SYSTEM_FIELDS as g, RESERVED_QUERY_PARAMS as h, DEFAULT_MAX_LIMIT as i, HOOK_OPERATIONS as l, MUTATION_OPERATIONS as m, DEFAULT_ID_FIELD as n, DEFAULT_TENANT_FIELD as o, MAX_SEARCH_LENGTH as p, DEFAULT_LIMIT as r, DEFAULT_UPDATE_METHOD as s, CRUD_OPERATIONS as t, HOOK_PHASES as u };
|
package/dist/context/index.d.mts
CHANGED
|
@@ -52,7 +52,7 @@ interface RequestStore {
|
|
|
52
52
|
* - `run(store, fn)` — run a function with a specific store (used by Arc internals)
|
|
53
53
|
* - `getStore()` — alias for get() (matches Node.js API naming)
|
|
54
54
|
*/
|
|
55
|
-
declare const requestContext: {
|
|
55
|
+
export declare const requestContext: {
|
|
56
56
|
/**
|
|
57
57
|
* Get the current request context.
|
|
58
58
|
* Returns undefined if called outside a request lifecycle.
|
|
@@ -90,22 +90,19 @@ declare const requestContext: {
|
|
|
90
90
|
* await outbox.store(event, { headers: traceHeaders });
|
|
91
91
|
* ```
|
|
92
92
|
*/
|
|
93
|
-
declare function getTraceHeaders(): Record<string, string> | undefined;
|
|
93
|
+
export declare function getTraceHeaders(): Record<string, string> | undefined;
|
|
94
94
|
//#endregion
|
|
95
95
|
//#region src/context/requestScopedCache.d.ts
|
|
96
96
|
/**
|
|
97
|
-
* The cache for the CURRENT unit of work
|
|
98
|
-
*
|
|
99
|
-
*
|
|
100
|
-
* Lazily created on first use, so a scope that never touches a cached
|
|
101
|
-
* repository allocates nothing.
|
|
97
|
+
* The cache for the CURRENT unit of work (see `runWorkScope`), or `undefined`
|
|
98
|
+
* outside one. Lazily created on first use.
|
|
102
99
|
*/
|
|
103
|
-
declare function requestScopedCache(): CacheAdapter | undefined;
|
|
100
|
+
export declare function requestScopedCache(): CacheAdapter | undefined;
|
|
104
101
|
/**
|
|
105
102
|
* Whether the current scope has allocated a cache yet. Diagnostics and
|
|
106
103
|
* tests only — never branch application behaviour on this.
|
|
107
104
|
*/
|
|
108
|
-
declare function hasRequestScopedCache(): boolean;
|
|
105
|
+
export declare function hasRequestScopedCache(): boolean;
|
|
109
106
|
//#endregion
|
|
110
107
|
//#region src/context/transactionContext.d.ts
|
|
111
108
|
/** DB session/transaction handle. Typed as unknown — arc is DB-agnostic. */
|
|
@@ -116,7 +113,7 @@ type DbSession = unknown;
|
|
|
116
113
|
* - `run(session, fn)` — install an active DB session for the duration of `fn`
|
|
117
114
|
* - `get()` — read the current session (undefined if outside a transaction scope)
|
|
118
115
|
*/
|
|
119
|
-
declare const transactionContext: {
|
|
116
|
+
export declare const transactionContext: {
|
|
120
117
|
/**
|
|
121
118
|
* Run `fn` with `session` as the active DB transaction context.
|
|
122
119
|
* Any call to `get()` within `fn`'s async call stack returns `session`.
|
|
@@ -139,17 +136,17 @@ interface WorkSeed {
|
|
|
139
136
|
readonly organizationId?: string;
|
|
140
137
|
}
|
|
141
138
|
/** Enter a scope for `fn`, or run it inside the active one. */
|
|
142
|
-
declare function runWorkScope<T>(seed: WorkSeed, fn: () => T): T;
|
|
139
|
+
export declare function runWorkScope<T>(seed: WorkSeed, fn: () => T): T;
|
|
143
140
|
/**
|
|
144
141
|
* A per-scope memo slot: created once on first use, the same instance after.
|
|
145
142
|
* `undefined` outside a scope.
|
|
146
143
|
*/
|
|
147
|
-
declare function scopedValue<T>(slot: symbol, create: () => T): T | undefined;
|
|
144
|
+
export declare function scopedValue<T>(slot: symbol, create: () => T): T | undefined;
|
|
148
145
|
/**
|
|
149
146
|
* The scope an event dispatch runs in. The correlation id is the scope id so a
|
|
150
147
|
* child event published from a handler chains to the same correlation; the
|
|
151
148
|
* tenant rides `meta`, never the payload.
|
|
152
149
|
*/
|
|
153
|
-
declare function workSeedFromEvent(event: DomainEvent): WorkSeed;
|
|
150
|
+
export declare function workSeedFromEvent(event: DomainEvent): WorkSeed;
|
|
154
151
|
//#endregion
|
|
155
|
-
export {
|
|
152
|
+
export type { DbSession, RequestStore, WorkKind, WorkSeed };
|
package/dist/context/index.mjs
CHANGED
|
@@ -1,34 +1,23 @@
|
|
|
1
|
-
/*! @classytic/arc v2.
|
|
2
|
-
import { t as transactionContext } from "../transactionContext-
|
|
3
|
-
import { n as requestContext, t as getTraceHeaders } from "../requestContext-
|
|
4
|
-
import { n as
|
|
1
|
+
/*! @classytic/arc v2.44.0 | MIT | https://classytic.com */
|
|
2
|
+
import { t as transactionContext } from "../transactionContext-orvnGu4F.mjs";
|
|
3
|
+
import { n as requestContext, t as getTraceHeaders } from "../requestContext-C10UeFe5.mjs";
|
|
4
|
+
import { n as evictToSize, r as touch } from "../boundedMap-DWiKW7lP.mjs";
|
|
5
|
+
import { n as scopedValue, r as workSeedFromEvent, t as runWorkScope } from "../workScope-n9a7d_lZ.mjs";
|
|
5
6
|
//#region src/context/requestScopedCache.ts
|
|
6
7
|
/** The memo slot the per-scope store hangs off. */
|
|
7
8
|
const SLOT = Symbol.for("arc.requestScopedCache");
|
|
8
|
-
/**
|
|
9
|
-
* Entry cap for one scope's cache. A request touching this many distinct keys
|
|
10
|
-
* is already pathological; a JOB RUN reaching it is ordinary, and that is the
|
|
11
|
-
* case this exists for (see the eviction note below).
|
|
12
|
-
*/
|
|
9
|
+
/** Entry cap for one scope's cache — sized for long job runs, not requests. */
|
|
13
10
|
const MAX_ENTRIES = 1e4;
|
|
14
11
|
/**
|
|
15
12
|
* A `Map`-backed `CacheAdapter` with NO TTL and LRU eviction above
|
|
16
13
|
* {@link MAX_ENTRIES}.
|
|
17
14
|
*
|
|
18
|
-
* TTL is
|
|
19
|
-
*
|
|
20
|
-
* on one question.
|
|
15
|
+
* TTL is ignored: repo-core stamps freshness INTO the envelope and re-checks
|
|
16
|
+
* on read, so honouring it here would put two clocks on one question.
|
|
21
17
|
*
|
|
22
|
-
*
|
|
23
|
-
*
|
|
24
|
-
*
|
|
25
|
-
* milliseconds and bounded by what one request touched, and an LRU would have
|
|
26
|
-
* guarded a lifetime that could not occur. 2.40 extended scopes to job runs
|
|
27
|
-
* and event dispatches — and `schedulesPlugin` wraps the WHOLE handler in one
|
|
28
|
-
* scope, so a nightly job sweeping a million rows holds a single cache for the
|
|
29
|
-
* duration. That is now a lifetime that occurs, and the old premise no longer
|
|
30
|
-
* holds. Evicting is always safe here: this is a cache, so the worst a miss
|
|
31
|
-
* costs is the re-read it was avoiding.
|
|
18
|
+
* The cap matters because `schedulesPlugin` wraps a WHOLE job handler in one
|
|
19
|
+
* scope, so a long sweep holds a single cache throughout. Evicting is always
|
|
20
|
+
* safe — a miss costs only the re-read it was avoiding.
|
|
32
21
|
*/
|
|
33
22
|
function createRequestCacheAdapter() {
|
|
34
23
|
const map = /* @__PURE__ */ new Map();
|
|
@@ -36,25 +25,19 @@ function createRequestCacheAdapter() {
|
|
|
36
25
|
async get(key) {
|
|
37
26
|
if (!map.has(key)) return void 0;
|
|
38
27
|
const value = map.get(key);
|
|
39
|
-
map
|
|
40
|
-
map.set(key, value);
|
|
28
|
+
touch(map, key, value);
|
|
41
29
|
return value;
|
|
42
30
|
},
|
|
43
31
|
async set(key, value, _ttlSeconds) {
|
|
44
|
-
map
|
|
45
|
-
map
|
|
46
|
-
if (map.size > MAX_ENTRIES) {
|
|
47
|
-
const oldest = map.keys().next();
|
|
48
|
-
if (!oldest.done) map.delete(oldest.value);
|
|
49
|
-
}
|
|
32
|
+
touch(map, key, value);
|
|
33
|
+
evictToSize(map, MAX_ENTRIES);
|
|
50
34
|
},
|
|
51
35
|
async delete(key) {
|
|
52
36
|
map.delete(key);
|
|
53
37
|
},
|
|
54
38
|
/**
|
|
55
|
-
* Ignores the pattern and wipes the whole store
|
|
56
|
-
*
|
|
57
|
-
* store that dies in milliseconds does not earn.
|
|
39
|
+
* Ignores the pattern and wipes the whole store — over-invalidating a
|
|
40
|
+
* short-lived store costs at most a re-read.
|
|
58
41
|
*/
|
|
59
42
|
async clear(_pattern) {
|
|
60
43
|
map.clear();
|
|
@@ -62,11 +45,8 @@ function createRequestCacheAdapter() {
|
|
|
62
45
|
};
|
|
63
46
|
}
|
|
64
47
|
/**
|
|
65
|
-
* The cache for the CURRENT unit of work
|
|
66
|
-
*
|
|
67
|
-
*
|
|
68
|
-
* Lazily created on first use, so a scope that never touches a cached
|
|
69
|
-
* repository allocates nothing.
|
|
48
|
+
* The cache for the CURRENT unit of work (see `runWorkScope`), or `undefined`
|
|
49
|
+
* outside one. Lazily created on first use.
|
|
70
50
|
*/
|
|
71
51
|
function requestScopedCache() {
|
|
72
52
|
return scopedValue(SLOT, createRequestCacheAdapter);
|
package/dist/core/index.d.mts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
1
|
|
|
2
|
-
import { $t as
|
|
3
|
-
import { A as
|
|
4
|
-
export { AccessControl, type AccessControlConfig, type AdapterLike, type AggMeasureInput, type AggMeasureShorthand, type AggregationCacheConfig, type AggregationConfig, type AggregationDateRangeRequirement, type AggregationIndexHint, type AggregationMaterializedContext, type AggregationMaterializedResult, type AggregationRateLimit, type AggregationsMap, BaseController, type BaseControllerOptions, BaseCrudController, BodySanitizer, type BodySanitizerConfig, type BulkExt, BulkMixin, CRUD_OPERATIONS, type ConfigError, type ControllerConfigurableOptions, type ControllerConstructionOptions, CrudOperation, DEFAULT_ID_FIELD, DEFAULT_LIMIT, DEFAULT_MAX_LIMIT, DEFAULT_SORT, DEFAULT_TENANT_FIELD, DEFAULT_UPDATE_METHOD, type DefineActionConfig, HOOK_OPERATIONS, HOOK_PHASES, HookOperation, HookPhase, type ListResult, MAX_FILTER_DEPTH, MAX_REGEX_LENGTH, MAX_SEARCH_LENGTH, MUTATION_OPERATIONS, MutationOperation, type NativePolicyFilter, QueryResolver, type QueryResolverConfig, RESERVED_QUERY_PARAMS, ResourceDefinition, type ResourceSeams, SYSTEM_FIELDS, type SeamedResourceConfig, type SlugExt, SlugMixin, type SoftDeleteExt, SoftDeleteMixin, type TreeExt, TreeMixin, type ValidateOptions, type ValidationResult, assertValidConfig, collectReadBlockedFields,
|
|
2
|
+
import { $t as AccessControl, An as MixinConstructor, Bt as TreeMixin, Dn as ControllerConfigurableOptions, En as BaseControllerOptions, F as ResourceDefinition, Gt as BaseCrudController, Ht as SlugMixin, It as BaseController, Jt as ActionTargetCheck, Kt as QueryResolver, Lt as SoftDeleteExt, Mn as BodySanitizerConfig, On as ControllerConstructionOptions, P as defineResource, Qt as getEntityQuery, Rt as SoftDeleteMixin, Ut as BulkExt, Vt as SlugExt, Wt as BulkMixin, Xt as getEntityId, Yt as getActionTarget, Zt as getEntityIdField, at as AggregationCacheConfig, ct as AggregationIndexHint, dt as AggregationRateLimit, en as AccessControlConfig, ft as AggregationsMap, it as AggMeasureShorthand, jn as BodySanitizer, kn as ListResult, lt as AggregationMaterializedContext, nn as FetchDenialReason, ot as AggregationConfig, qt as QueryResolverConfig, rn as FetchResult, rt as AggMeasureInput, st as AggregationDateRangeRequirement, tn as AccessControlRepository, ut as AggregationMaterializedResult, zt as TreeExt } from "../index-WH7xgvxS.mjs";
|
|
3
|
+
import { A as CrudOperation, B as HookOperation, C as mergeResourceConfig, D as createPermissionMiddleware, E as createCrudRouter, F as DEFAULT_TENANT_FIELD, G as MUTATION_OPERATIONS, H as MAX_FILTER_DEPTH, I as DEFAULT_UPDATE_METHOD, J as SYSTEM_FIELDS, K as MutationOperation, L as DEFAULT_VERSION_FIELD, M as DEFAULT_LIMIT, N as DEFAULT_MAX_LIMIT, O as defineAggregation, P as DEFAULT_SORT, R as HOOK_OPERATIONS, S as SeamedResourceConfig, T as defineAction, U as MAX_REGEX_LENGTH, V as HookPhase, W as MAX_SEARCH_LENGTH, _ as VariantsMap, a as formatValidationErrors, b as AdapterLike, c as nativePolicyFilter, d as createFastifyHandler, f as createRequestContext, g as VariantOverride, h as sendControllerResponse, i as assertValidConfig, j as DEFAULT_ID_FIELD, k as CRUD_OPERATIONS, l as collectReadBlockedFields, m as getControllerScope, n as ValidateOptions, o as validateResourceConfig, p as getControllerContext, q as RESERVED_QUERY_PARAMS, r as ValidationResult, s as NativePolicyFilter, t as ConfigError, u as isFieldReadable, v as VariantsResult, w as DefineActionConfig, x as ResourceSeams, y as defineResourceVariants, z as HOOK_PHASES } from "../index-Beg3aNnK.mjs";
|
|
4
|
+
export { AccessControl, type AccessControlConfig, type AccessControlRepository, type ActionTargetCheck, type AdapterLike, type AggMeasureInput, type AggMeasureShorthand, type AggregationCacheConfig, type AggregationConfig, type AggregationDateRangeRequirement, type AggregationIndexHint, type AggregationMaterializedContext, type AggregationMaterializedResult, type AggregationRateLimit, type AggregationsMap, BaseController, type BaseControllerOptions, BaseCrudController, BodySanitizer, type BodySanitizerConfig, type BulkExt, BulkMixin, CRUD_OPERATIONS, type ConfigError, type ControllerConfigurableOptions, type ControllerConstructionOptions, CrudOperation, DEFAULT_ID_FIELD, DEFAULT_LIMIT, DEFAULT_MAX_LIMIT, DEFAULT_SORT, DEFAULT_TENANT_FIELD, DEFAULT_UPDATE_METHOD, DEFAULT_VERSION_FIELD, type DefineActionConfig, type FetchDenialReason, type FetchResult, HOOK_OPERATIONS, HOOK_PHASES, HookOperation, HookPhase, type ListResult, MAX_FILTER_DEPTH, MAX_REGEX_LENGTH, MAX_SEARCH_LENGTH, MUTATION_OPERATIONS, type MixinConstructor, MutationOperation, type NativePolicyFilter, QueryResolver, type QueryResolverConfig, RESERVED_QUERY_PARAMS, ResourceDefinition, type ResourceSeams, SYSTEM_FIELDS, type SeamedResourceConfig, type SlugExt, SlugMixin, type SoftDeleteExt, SoftDeleteMixin, type TreeExt, TreeMixin, type ValidateOptions, type ValidationResult, type VariantOverride, type VariantsMap, type VariantsResult, assertValidConfig, collectReadBlockedFields, createCrudRouter, createFastifyHandler, createPermissionMiddleware, createRequestContext, defineAction, defineAggregation, defineResource, defineResourceVariants, formatValidationErrors, getActionTarget, getControllerContext, getControllerScope, getEntityId, getEntityIdField, getEntityQuery, isFieldReadable, mergeResourceConfig, nativePolicyFilter, sendControllerResponse, validateResourceConfig };
|
package/dist/core/index.mjs
CHANGED
|
@@ -1,8 +1,8 @@
|
|
|
1
|
-
/*! @classytic/arc v2.
|
|
2
|
-
import { a as DEFAULT_SORT, c as
|
|
3
|
-
import {
|
|
4
|
-
import { t as defineAggregation } from "../defineAggregation
|
|
5
|
-
import {
|
|
6
|
-
import { a as
|
|
7
|
-
import { a as
|
|
8
|
-
export { AccessControl, BaseController, BaseCrudController, BodySanitizer, BulkMixin, CRUD_OPERATIONS, DEFAULT_ID_FIELD, DEFAULT_LIMIT, DEFAULT_MAX_LIMIT, DEFAULT_SORT, DEFAULT_TENANT_FIELD, DEFAULT_UPDATE_METHOD, HOOK_OPERATIONS, HOOK_PHASES, MAX_FILTER_DEPTH, MAX_REGEX_LENGTH, MAX_SEARCH_LENGTH, MUTATION_OPERATIONS, QueryResolver, RESERVED_QUERY_PARAMS, ResourceDefinition, SYSTEM_FIELDS, SlugMixin, SoftDeleteMixin, TreeMixin, assertValidConfig, collectReadBlockedFields,
|
|
1
|
+
/*! @classytic/arc v2.44.0 | MIT | https://classytic.com */
|
|
2
|
+
import { a as DEFAULT_SORT, c as DEFAULT_VERSION_FIELD, d as MAX_FILTER_DEPTH, f as MAX_REGEX_LENGTH, g as SYSTEM_FIELDS, h as RESERVED_QUERY_PARAMS, i as DEFAULT_MAX_LIMIT, l as HOOK_OPERATIONS, m as MUTATION_OPERATIONS, n as DEFAULT_ID_FIELD, o as DEFAULT_TENANT_FIELD, p as MAX_SEARCH_LENGTH, r as DEFAULT_LIMIT, s as DEFAULT_UPDATE_METHOD, t as CRUD_OPERATIONS, u as HOOK_PHASES } from "../constants-B2QHb6u6.mjs";
|
|
3
|
+
import { S as nativePolicyFilter, b as BodySanitizer, c as BaseController, d as SlugMixin, f as BulkMixin, g as isFieldReadable, h as collectReadBlockedFields, l as TreeMixin, m as QueryResolver, o as createCrudRouter, p as BaseCrudController, s as createPermissionMiddleware, u as SoftDeleteMixin, x as AccessControl } from "../plugin-DALd9dcF.mjs";
|
|
4
|
+
import { t as defineAggregation } from "../defineAggregation--3xguLkk.mjs";
|
|
5
|
+
import { C as sendControllerResponse, S as getControllerScope, b as createRequestContext, x as getControllerContext, y as createFastifyHandler } from "../actionPermissions-5N04zBL4.mjs";
|
|
6
|
+
import { a as defineResourceVariants, i as getEntityQuery, n as getEntityId, o as mergeResourceConfig, r as getEntityIdField, s as defineAction, t as getActionTarget } from "../core-COcqo0Cp.mjs";
|
|
7
|
+
import { a as ResourceDefinition, i as validateResourceConfig, n as assertValidConfig, r as formatValidationErrors, t as defineResource } from "../defineResource-gUp9ug66.mjs";
|
|
8
|
+
export { AccessControl, BaseController, BaseCrudController, BodySanitizer, BulkMixin, CRUD_OPERATIONS, DEFAULT_ID_FIELD, DEFAULT_LIMIT, DEFAULT_MAX_LIMIT, DEFAULT_SORT, DEFAULT_TENANT_FIELD, DEFAULT_UPDATE_METHOD, DEFAULT_VERSION_FIELD, HOOK_OPERATIONS, HOOK_PHASES, MAX_FILTER_DEPTH, MAX_REGEX_LENGTH, MAX_SEARCH_LENGTH, MUTATION_OPERATIONS, QueryResolver, RESERVED_QUERY_PARAMS, ResourceDefinition, SYSTEM_FIELDS, SlugMixin, SoftDeleteMixin, TreeMixin, assertValidConfig, collectReadBlockedFields, createCrudRouter, createFastifyHandler, createPermissionMiddleware, createRequestContext, defineAction, defineAggregation, defineResource, defineResourceVariants, formatValidationErrors, getActionTarget, getControllerContext, getControllerScope, getEntityId, getEntityIdField, getEntityQuery, isFieldReadable, mergeResourceConfig, nativePolicyFilter, sendControllerResponse, validateResourceConfig };
|
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
/*! @classytic/arc v2.
|
|
2
|
-
import "./constants-
|
|
3
|
-
import { t as defineResource } from "./defineResource-
|
|
1
|
+
/*! @classytic/arc v2.44.0 | MIT | https://classytic.com */
|
|
2
|
+
import "./constants-B2QHb6u6.mjs";
|
|
3
|
+
import { t as defineResource } from "./defineResource-gUp9ug66.mjs";
|
|
4
4
|
//#region src/core/defineAction.ts
|
|
5
5
|
/**
|
|
6
6
|
* Build an `ActionDefinition` with a typed handler. The literal schema
|
|
@@ -19,7 +19,8 @@ function defineAction(config) {
|
|
|
19
19
|
schema: config.schema,
|
|
20
20
|
description: config.description,
|
|
21
21
|
id: config.id,
|
|
22
|
-
mcp: config.mcp
|
|
22
|
+
mcp: config.mcp,
|
|
23
|
+
...config.idempotency ? { idempotency: config.idempotency } : {}
|
|
23
24
|
};
|
|
24
25
|
}
|
|
25
26
|
//#endregion
|
|
@@ -172,5 +173,14 @@ function getEntityQuery(req) {
|
|
|
172
173
|
if (id === void 0) return {};
|
|
173
174
|
return { [getEntityIdField(req)]: id };
|
|
174
175
|
}
|
|
176
|
+
/**
|
|
177
|
+
* The row the action-target fence loaded for this request, so the handler need not read it
|
|
178
|
+
* again. `undefined` when no fence read happened (untenanted resource, `scope: 'handler'`,
|
|
179
|
+
* id-less action) — load it yourself then. It is a snapshot taken after the idempotency lock
|
|
180
|
+
* and before the handler: a guarded write must still carry its precondition in the filter.
|
|
181
|
+
*/
|
|
182
|
+
function getActionTarget(req) {
|
|
183
|
+
return req.arc?.actionTarget;
|
|
184
|
+
}
|
|
175
185
|
//#endregion
|
|
176
|
-
export {
|
|
186
|
+
export { defineResourceVariants as a, getEntityQuery as i, getEntityId as n, mergeResourceConfig as o, getEntityIdField as r, defineAction as s, getActionTarget as t };
|
|
@@ -1,16 +1,34 @@
|
|
|
1
|
-
/*! @classytic/arc v2.
|
|
2
|
-
import {
|
|
3
|
-
import "./constants-
|
|
4
|
-
import { f as createError } from "./errors-
|
|
5
|
-
import { i as resolvePipelineSteps, n as buildActionPipelineHandler, u as
|
|
6
|
-
import {
|
|
7
|
-
import { n as schemaIRToJsonSchemaBranch, t as normalizeSchemaIR } from "./schemaIR-CTvPDoPB.mjs";
|
|
1
|
+
/*! @classytic/arc v2.44.0 | MIT | https://classytic.com */
|
|
2
|
+
import { r as __exportAll } from "./rolldown-runtime-MYrLNGnq.mjs";
|
|
3
|
+
import "./constants-B2QHb6u6.mjs";
|
|
4
|
+
import { f as createError, i as NotFoundError, u as ValidationError } from "./errors-CW4anJBy.mjs";
|
|
5
|
+
import { C as sendControllerResponse, _ as buildArcDecorator, a as resolveRouterPluginMw, f as buildRouteHooks, g as buildAuthMiddlewareForPermissions, i as buildRouteConfig, l as resolvePipelineSteps, m as routeHookOptions, n as tryRegisterRoute, o as selectPluginMw, r as buildRateLimitConfig, s as buildActionPipelineHandler, u as buildActionPermissionMw } from "./actionPermissions-5N04zBL4.mjs";
|
|
6
|
+
import { n as schemaIRToJsonSchemaBranch, t as normalizeSchemaIR } from "./schemaIR-BA0Hb21Y.mjs";
|
|
8
7
|
//#region src/core/createActionRouter.ts
|
|
9
8
|
var createActionRouter_exports = /* @__PURE__ */ __exportAll({
|
|
10
9
|
buildActionBodySchema: () => buildActionBodySchema,
|
|
11
10
|
createActionRouter: () => createActionRouter
|
|
12
11
|
});
|
|
13
12
|
/**
|
|
13
|
+
* Record a successful action. The actor and organization come from the request's
|
|
14
|
+
* validated scope (`auditContext`); a failed write is logged, never turned into a 500
|
|
15
|
+
* after the action has already happened.
|
|
16
|
+
*/
|
|
17
|
+
async function auditAction(req, resource, id, action) {
|
|
18
|
+
const audit = req.server.audit;
|
|
19
|
+
if (!audit?.custom || audit._noop === true) return;
|
|
20
|
+
try {
|
|
21
|
+
await audit.custom(resource, id, action, { ...req.auditMetadata ?? {} }, req.auditContext);
|
|
22
|
+
} catch (err) {
|
|
23
|
+
req.log.error({
|
|
24
|
+
err,
|
|
25
|
+
resource,
|
|
26
|
+
id,
|
|
27
|
+
action
|
|
28
|
+
}, "[arc] action audit write failed");
|
|
29
|
+
}
|
|
30
|
+
}
|
|
31
|
+
/**
|
|
14
32
|
* Register the unified action endpoint: `POST /:id/action`.
|
|
15
33
|
*
|
|
16
34
|
* Shares every lifecycle primitive with the CRUD router — the preHandler
|
|
@@ -19,7 +37,8 @@ var createActionRouter_exports = /* @__PURE__ */ __exportAll({
|
|
|
19
37
|
* (keyed by `body.action` at request time).
|
|
20
38
|
*/
|
|
21
39
|
function createActionRouter(fastify, config) {
|
|
22
|
-
const { tag, resourceName = tag ?? "action", actions, actionPermissions = {}, actionSchemas = {}, globalAuth, onError, fields: fieldPermissions, schemaOptions, idField = "_id", permissions: resourcePermissions, routeGuards = [], pipeline, rateLimit, idLessActionNames = [], extensions } = config;
|
|
40
|
+
const { tag, resourceName = tag ?? "action", actions, actionPermissions = {}, actionSchemas = {}, globalAuth, onError, fields: fieldPermissions, schemaOptions, idField = "_id", permissions: resourcePermissions, routeGuards = [], tenantMiddlewares = [], pipeline, rateLimit, idLessActionNames = [], extensions, isTargetVisible, loadTarget, handlerScopedActionNames = [], idempotentActionNames = [], handlerDedupOwners = {}, auditActions = false } = config;
|
|
41
|
+
const handlerScoped = new Set(handlerScopedActionNames);
|
|
23
42
|
const allActionNames = Object.keys(actions);
|
|
24
43
|
if (allActionNames.length === 0) {
|
|
25
44
|
fastify.log.warn("[createActionRouter] No actions defined, skipping route creation");
|
|
@@ -82,23 +101,26 @@ function createActionRouter(fastify, config) {
|
|
|
82
101
|
} },
|
|
83
102
|
required: ["id"]
|
|
84
103
|
};
|
|
104
|
+
const actionDispatchCheck = async (req, _reply) => {
|
|
105
|
+
if (!req.validationError) return;
|
|
106
|
+
const body = req.body;
|
|
107
|
+
const submitted = typeof body?.action === "string" ? body.action : void 0;
|
|
108
|
+
if (submitted && subsetSet.has(submitted)) return;
|
|
109
|
+
const wrongMount = submitted && allActionNames.includes(submitted) ? submitted : void 0;
|
|
110
|
+
const otherMount = hasId ? "/action" : "/:id/action";
|
|
111
|
+
const message = wrongMount ? `Action '${wrongMount}' on '${resourceName}' is mounted at 'POST ${otherMount}', not 'POST ${urlPath}'` : submitted ? `Unknown action '${submitted}' on '${resourceName}'. Valid at 'POST ${urlPath}': ${actionSubset.join(", ")}` : `Missing 'action' field on '${resourceName}'. Valid at 'POST ${urlPath}': ${actionSubset.join(", ")}`;
|
|
112
|
+
throw createError(400, message, {
|
|
113
|
+
code: "arc.invalid_action",
|
|
114
|
+
validActions: actionSubset,
|
|
115
|
+
mount: urlPath,
|
|
116
|
+
...submitted ? { submitted } : {},
|
|
117
|
+
...wrongMount ? { mountedAt: otherMount } : {}
|
|
118
|
+
});
|
|
119
|
+
};
|
|
85
120
|
const actionValidationFormatter = async (req, _reply) => {
|
|
86
121
|
const validationError = req.validationError;
|
|
87
122
|
if (!validationError) return;
|
|
88
|
-
const
|
|
89
|
-
const submitted = typeof body?.action === "string" ? body.action : void 0;
|
|
90
|
-
if (!submitted || !subsetSet.has(submitted)) {
|
|
91
|
-
const wrongMount = submitted && allActionNames.includes(submitted) ? submitted : void 0;
|
|
92
|
-
const otherMount = hasId ? "/action" : "/:id/action";
|
|
93
|
-
const message = wrongMount ? `Action '${wrongMount}' on '${resourceName}' is mounted at 'POST ${otherMount}', not 'POST ${urlPath}'` : submitted ? `Unknown action '${submitted}' on '${resourceName}'. Valid at 'POST ${urlPath}': ${actionSubset.join(", ")}` : `Missing 'action' field on '${resourceName}'. Valid at 'POST ${urlPath}': ${actionSubset.join(", ")}`;
|
|
94
|
-
throw createError(400, message, {
|
|
95
|
-
code: "arc.invalid_action",
|
|
96
|
-
validActions: actionSubset,
|
|
97
|
-
mount: urlPath,
|
|
98
|
-
...submitted ? { submitted } : {},
|
|
99
|
-
...wrongMount ? { mountedAt: otherMount } : {}
|
|
100
|
-
});
|
|
101
|
-
}
|
|
123
|
+
const submitted = req.body.action;
|
|
102
124
|
const branchPath = `/oneOf/${actionSubset.indexOf(submitted)}/`;
|
|
103
125
|
const ajvErrors = validationError.validation ?? [];
|
|
104
126
|
const scoped = ajvErrors.filter((e) => e.keyword !== "oneOf" && (e.schemaPath?.includes(branchPath) || !e.schemaPath?.includes("/oneOf/")));
|
|
@@ -111,24 +133,30 @@ function createActionRouter(fastify, config) {
|
|
|
111
133
|
scopedError.meta = { action: submitted };
|
|
112
134
|
throw scopedError;
|
|
113
135
|
};
|
|
136
|
+
const requiredActions = actionSubset.filter((name) => idempotentActionNames.includes(name));
|
|
137
|
+
const handlerDedup = Object.fromEntries(requiredActions.flatMap((name) => {
|
|
138
|
+
const owner = handlerDedupOwners[name];
|
|
139
|
+
return owner === void 0 ? [] : [[name, owner]];
|
|
140
|
+
}));
|
|
114
141
|
const hooks = buildRouteHooks({
|
|
115
142
|
arcDecorator,
|
|
116
143
|
authMw,
|
|
144
|
+
prePermission: actionDispatchCheck,
|
|
117
145
|
permissionMw,
|
|
146
|
+
validationFormatter: actionValidationFormatter,
|
|
118
147
|
pluginMw: selectPluginMw("POST", pluginMw),
|
|
119
|
-
routeGuards
|
|
148
|
+
routeGuards,
|
|
149
|
+
customMws: tenantMiddlewares
|
|
120
150
|
});
|
|
121
|
-
const preHandler = [actionValidationFormatter, ...hooks.preHandler];
|
|
122
151
|
tryRegisterRoute(fastify, {
|
|
123
152
|
method: "POST",
|
|
124
153
|
url: urlPath,
|
|
125
154
|
schema: routeSchema,
|
|
126
|
-
|
|
127
|
-
...
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
}),
|
|
131
|
-
...buildRouteConfig(rateLimitConfig, extensions),
|
|
155
|
+
...routeHookOptions(hooks),
|
|
156
|
+
...buildRouteConfig(rateLimitConfig, extensions, void 0, requiredActions.length > 0 ? {
|
|
157
|
+
requiredActions,
|
|
158
|
+
...Object.keys(handlerDedup).length > 0 ? { handlerDedup } : {}
|
|
159
|
+
} : void 0),
|
|
132
160
|
handler: async (req, reply) => {
|
|
133
161
|
const { action, ...data } = req.body;
|
|
134
162
|
const id = hasId ? req.params.id : "";
|
|
@@ -141,8 +169,20 @@ function createActionRouter(fastify, config) {
|
|
|
141
169
|
}
|
|
142
170
|
const handler = wrappedHandlers.get(action);
|
|
143
171
|
if (!handler) throw createError(400, `Invalid action '${action}'. Valid actions: ${actionSubset.join(", ")}`, { validActions: actionSubset });
|
|
172
|
+
if (hasId && (loadTarget || isTargetVisible) && !handlerScoped.has(action)) {
|
|
173
|
+
const target = loadTarget ? await loadTarget(id, req) : { visible: await isTargetVisible(id, req) };
|
|
174
|
+
if (!target.visible) throw new NotFoundError(resourceName);
|
|
175
|
+
if (target.doc !== void 0) {
|
|
176
|
+
const reqWithExtras = req;
|
|
177
|
+
reqWithExtras.arc = {
|
|
178
|
+
...reqWithExtras.arc ?? {},
|
|
179
|
+
actionTarget: target.doc
|
|
180
|
+
};
|
|
181
|
+
}
|
|
182
|
+
}
|
|
144
183
|
try {
|
|
145
184
|
const response = await handler(id, data, req);
|
|
185
|
+
if (auditActions && (response.status ?? 200) < 400) await auditAction(req, resourceName, id, action);
|
|
146
186
|
sendControllerResponse(reply, response, req);
|
|
147
187
|
return reply;
|
|
148
188
|
} catch (error) {
|
|
@@ -150,6 +190,8 @@ function createActionRouter(fastify, config) {
|
|
|
150
190
|
const { statusCode, error: errorMsg, code } = onError(error, action, id);
|
|
151
191
|
throw createError(statusCode, errorMsg, code ? { code } : void 0);
|
|
152
192
|
}
|
|
193
|
+
const validation = zodIssues(error);
|
|
194
|
+
if (validation) throw new ValidationError(`Invalid '${action}' payload`, validation);
|
|
153
195
|
const err = error;
|
|
154
196
|
const statusCode = err.statusCode || err.status || 500;
|
|
155
197
|
const errorCode = err.code || "ACTION_FAILED";
|
|
@@ -190,7 +232,7 @@ function createActionRouter(fastify, config) {
|
|
|
190
232
|
* `action: { const }` and carrying its own `required`.
|
|
191
233
|
*
|
|
192
234
|
* EVERY BRANCH LISTS THE FULL PROPERTY UNION — the non-obvious part. AJV's
|
|
193
|
-
* `removeAdditional
|
|
235
|
+
* `removeAdditional` (Fastify's default strips) strips a field during a
|
|
194
236
|
* branch's evaluation when that branch's `properties` omits it, EVEN IF
|
|
195
237
|
* another branch would have allowed it. The strip mutates the body before
|
|
196
238
|
* `oneOf` finishes discriminating, so the winning branch receives a body that
|
|
@@ -199,11 +241,10 @@ function createActionRouter(fastify, config) {
|
|
|
199
241
|
* every branch makes each walk see every field as its own, so nothing is
|
|
200
242
|
* stripped; `required` stays per-action, so dispatch still gates correctly.
|
|
201
243
|
*
|
|
202
|
-
*
|
|
203
|
-
*
|
|
204
|
-
*
|
|
205
|
-
*
|
|
206
|
-
* (`removeAdditional: false`) strict rejection works normally.
|
|
244
|
+
* Arc compiles this route with its own validator (`removeAdditional: false`, see
|
|
245
|
+
* `./routeValidator.ts`), whatever the host's AJV says — so per-branch
|
|
246
|
+
* `additionalProperties: false` rejects unknown fields on every host. The full
|
|
247
|
+
* property union stays because a host-supplied `validatorCompiler` may still strip.
|
|
207
248
|
*
|
|
208
249
|
* Pinned by `tests/core/action-discriminator-strip.test.ts` and
|
|
209
250
|
* `action-strict-schema-parity.test.ts`. Exported so OpenAPI and MCP tool
|
|
@@ -211,6 +252,7 @@ function createActionRouter(fastify, config) {
|
|
|
211
252
|
*/
|
|
212
253
|
function buildActionBodySchema(actionEnum, actionSchemas = {}) {
|
|
213
254
|
const unionProperties = {};
|
|
255
|
+
const conflicting = /* @__PURE__ */ new Set();
|
|
214
256
|
const irs = [];
|
|
215
257
|
for (const actionName of actionEnum) {
|
|
216
258
|
const ir = normalizeSchemaIR(actionSchemas[actionName]);
|
|
@@ -218,13 +260,19 @@ function buildActionBodySchema(actionEnum, actionSchemas = {}) {
|
|
|
218
260
|
name: actionName,
|
|
219
261
|
ir
|
|
220
262
|
});
|
|
221
|
-
for (const [key, val] of Object.entries(ir.properties))
|
|
263
|
+
for (const [key, val] of Object.entries(ir.properties)) {
|
|
264
|
+
const prior = unionProperties[key];
|
|
265
|
+
if (prior !== void 0 && JSON.stringify(prior) !== JSON.stringify(val)) conflicting.add(key);
|
|
266
|
+
unionProperties[key] = val;
|
|
267
|
+
}
|
|
222
268
|
}
|
|
269
|
+
const topLevelProperties = { ...unionProperties };
|
|
270
|
+
for (const key of conflicting) topLevelProperties[key] = {};
|
|
223
271
|
const branches = [];
|
|
224
272
|
for (const { name, ir } of irs) branches.push(schemaIRToJsonSchemaBranch({
|
|
225
273
|
...ir,
|
|
226
274
|
properties: {
|
|
227
|
-
...
|
|
275
|
+
...topLevelProperties,
|
|
228
276
|
...ir.properties
|
|
229
277
|
}
|
|
230
278
|
}, {
|
|
@@ -242,7 +290,7 @@ function buildActionBodySchema(actionEnum, actionSchemas = {}) {
|
|
|
242
290
|
type: "string",
|
|
243
291
|
enum: [...actionEnum]
|
|
244
292
|
},
|
|
245
|
-
...
|
|
293
|
+
...topLevelProperties
|
|
246
294
|
},
|
|
247
295
|
oneOf: branches
|
|
248
296
|
};
|
|
@@ -261,5 +309,15 @@ function buildActionDescription(actions, actionPermissions) {
|
|
|
261
309
|
}
|
|
262
310
|
return lines.join("\n");
|
|
263
311
|
}
|
|
312
|
+
/** A ZodError's issues as `{ field, message }`, or `undefined` for any other error. */
|
|
313
|
+
function zodIssues(error) {
|
|
314
|
+
if (!(error instanceof Error) || error.name !== "ZodError" || !("issues" in error)) return void 0;
|
|
315
|
+
const issues = error.issues;
|
|
316
|
+
if (!Array.isArray(issues)) return void 0;
|
|
317
|
+
return issues.map((issue) => ({
|
|
318
|
+
field: Array.isArray(issue.path) ? issue.path.map(String).join(".") || "(payload)" : "(payload)",
|
|
319
|
+
message: typeof issue.message === "string" ? issue.message : "invalid"
|
|
320
|
+
}));
|
|
321
|
+
}
|
|
264
322
|
//#endregion
|
|
265
323
|
export { createActionRouter_exports as n, buildActionBodySchema as t };
|
|
@@ -1,10 +1,10 @@
|
|
|
1
|
-
/*! @classytic/arc v2.
|
|
2
|
-
import {
|
|
3
|
-
import {
|
|
4
|
-
import {
|
|
5
|
-
import {
|
|
6
|
-
import { n as validateAggregations } from "./validate-
|
|
7
|
-
import { t as buildAggregationHandler } from "./buildHandler-
|
|
1
|
+
/*! @classytic/arc v2.44.0 | MIT | https://classytic.com */
|
|
2
|
+
import { f as createError } from "./errors-CW4anJBy.mjs";
|
|
3
|
+
import { u as getRequestScope } from "./types-fvO6Ovzx.mjs";
|
|
4
|
+
import { _ as buildArcDecorator, a as resolveRouterPluginMw, f as buildRouteHooks, h as buildAuthMiddleware, m as routeHookOptions, n as tryRegisterRoute, o as selectPluginMw, r as buildRateLimitConfig } from "./actionPermissions-5N04zBL4.mjs";
|
|
5
|
+
import { n as evaluateAndApplyPermission } from "./authorizationDecision-wX3SA9w_.mjs";
|
|
6
|
+
import { n as validateAggregations } from "./validate-CWp-lsrm.mjs";
|
|
7
|
+
import { t as buildAggregationHandler } from "./buildHandler-Bswu_vNb.mjs";
|
|
8
8
|
//#region src/core/aggregation/createAggregationRouter.ts
|
|
9
9
|
/**
|
|
10
10
|
* Register one Fastify route per aggregation. No-op when the map is
|
|
@@ -68,7 +68,7 @@ function registerOne(fastify, normalized, ctx) {
|
|
|
68
68
|
summary: config.summary ?? `Aggregation: ${name}`,
|
|
69
69
|
description: config.description ?? "Portable aggregation generated by arc. Filters from query string compose with the declaration's base filter + tenant scope."
|
|
70
70
|
};
|
|
71
|
-
fastify
|
|
71
|
+
tryRegisterRoute(fastify, {
|
|
72
72
|
method: "GET",
|
|
73
73
|
url: `/aggregations/${name}`,
|
|
74
74
|
schema: routeSchema,
|
|
@@ -86,6 +86,9 @@ function registerOne(fastify, normalized, ctx) {
|
|
|
86
86
|
throw createError(500, `Aggregation "${name}" failed: ${message}`);
|
|
87
87
|
}
|
|
88
88
|
}
|
|
89
|
+
}, {
|
|
90
|
+
resourceName: tag ?? name,
|
|
91
|
+
op: `aggregation:${name}`
|
|
89
92
|
});
|
|
90
93
|
}
|
|
91
94
|
/**
|