@classytic/arc 2.30.0 → 2.32.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +1 -1
- package/README.md +7 -0
- package/TRADEMARK.md +22 -0
- package/dist/{BaseController-B5DwufbB.mjs → BaseController-Dk34dxhI.mjs} +21 -19
- package/dist/{EventTransport-QmY7A258.d.mts → EventTransport-2uVV34WU.d.mts} +1 -0
- package/dist/{EventTransport-C6YA8xY9.mjs → EventTransport-BV10D0oG.mjs} +1 -0
- package/dist/{HookSystem-WkIougo6.mjs → HookSystem-vimw5D7Z.mjs} +2 -1
- package/dist/{QueryCache-DOMT_iq7.d.mts → QueryCache-DFA-QrU9.d.mts} +2 -1
- package/dist/{ResourceRegistry-y9Hnypus.mjs → ResourceRegistry-BVr7PgA0.mjs} +18 -15
- package/dist/{actionPermissions-CAphcmUG.mjs → actionPermissions-Dti7ebyv.mjs} +1 -0
- package/dist/audit/index.d.mts +3 -2
- package/dist/audit/index.mjs +2 -1
- package/dist/auth/audit.d.mts +1 -0
- package/dist/auth/audit.mjs +1 -0
- package/dist/auth/index.d.mts +5 -4
- package/dist/auth/index.mjs +11 -9
- package/dist/auth/redis-session.d.mts +2 -1
- package/dist/auth/redis-session.mjs +1 -0
- package/dist/{applyPermissionResult-Cf6zUCvb.mjs → authorizationDecision-__T3EJDA.mjs} +19 -10
- package/dist/{buildHandler-BYS3Vkfz.mjs → buildHandler-Dn7rzUJo.mjs} +6 -4
- package/dist/cache/index.d.mts +4 -3
- package/dist/cache/index.mjs +4 -3
- package/dist/{caching-_NJeDd1_.mjs → caching-DFJqHyFs.mjs} +2 -1
- package/dist/cleanup/index.d.mts +39 -16
- package/dist/cleanup/index.mjs +50 -38
- package/dist/cli/commands/describe.d.mts +2 -1
- package/dist/cli/commands/describe.mjs +4 -5
- package/dist/cli/commands/docs.d.mts +1 -0
- package/dist/cli/commands/docs.mjs +3 -2
- package/dist/cli/commands/doctor.d.mts +1 -0
- package/dist/cli/commands/doctor.mjs +1 -0
- package/dist/cli/commands/generate.d.mts +2 -1
- package/dist/cli/commands/generate.mjs +2 -1
- package/dist/cli/commands/init.d.mts +2 -1
- package/dist/cli/commands/init.mjs +2 -1
- package/dist/cli/commands/introspect.d.mts +1 -0
- package/dist/cli/commands/introspect.mjs +2 -1
- package/dist/cli/index.d.mts +3 -2
- package/dist/cli/index.mjs +3 -2
- package/dist/{constants-DNre0wRg.mjs → constants-J99LGTJI.mjs} +1 -0
- package/dist/context/index.d.mts +1 -0
- package/dist/context/index.mjs +3 -2
- package/dist/core/index.d.mts +3 -2
- package/dist/core/index.mjs +7 -6
- package/dist/{core-poalJLvp.mjs → core-BwwmNSp7.mjs} +3 -2
- package/dist/{createActionRouter-BxuywVRB.mjs → createActionRouter-DkJZWo0m.mjs} +14 -10
- package/dist/{createAggregationRouter-BiY-lGdY.mjs → createAggregationRouter-B1exOVD8.mjs} +10 -7
- package/dist/{createApp-CXrKFciH.mjs → createApp-Bv0m-Q6_.mjs} +625 -229
- package/dist/{defineAggregation-ufWjUwml.mjs → defineAggregation-gX4No1gt.mjs} +1 -0
- package/dist/{defineResource-CxWqDCAy.mjs → defineResource-XFPPUu2c.mjs} +84 -50
- package/dist/discovery/index.d.mts +1 -0
- package/dist/discovery/index.mjs +1 -0
- package/dist/docs/index.d.mts +3 -2
- package/dist/docs/index.mjs +3 -2
- package/dist/{elevation-C9s5hFP_.d.mts → elevation-BXCfVcNG.d.mts} +1 -0
- package/dist/{elevation-CIz1GjGm.mjs → elevation-DNXXxgjl.mjs} +4 -3
- package/dist/encryption/index.d.mts +2 -1
- package/dist/encryption/index.mjs +5 -3
- package/dist/{errorHandler-Bq2OeEQL.mjs → errorHandler-B2AZUJUj.mjs} +3 -2
- package/dist/{errorHandler-C6yv4ZTW.d.mts → errorHandler-CpYUJmPA.d.mts} +1 -0
- package/dist/{errors-DHcHVAgk.d.mts → errors-BGdVwP25.d.mts} +1 -0
- package/dist/{errors-DkO8Nk6-.mjs → errors-Bh1KK5ps.mjs} +1 -0
- package/dist/{eventPlugin-D2NNR4py.mjs → eventPlugin-Dk_TyXeE.mjs} +8 -6
- package/dist/{eventPlugin-Bg3cP2_u.d.mts → eventPlugin-GF5nnBIq.d.mts} +2 -1
- package/dist/events/index.d.mts +112 -44
- package/dist/events/index.mjs +177 -141
- package/dist/events/transports/redis-stream-entry.d.mts +2 -1
- package/dist/events/transports/redis-stream-entry.mjs +14 -9
- package/dist/events/transports/redis.d.mts +2 -1
- package/dist/events/transports/redis.mjs +1 -0
- package/dist/{externalPaths-BE6wFpYv.d.mts → externalPaths-CmGOxOtD.d.mts} +1 -0
- package/dist/factory/index.d.mts +9 -5
- package/dist/factory/index.mjs +131 -7
- package/dist/fastify-augmentation-BppG5bMT.d.mts +179 -0
- package/dist/{fields-CQ_7KQWw.mjs → fields-DEi65pGj.mjs} +6 -10
- package/dist/{fields-D3lhgzhD.d.mts → fields-I_EeRWND.d.mts} +1 -0
- package/dist/{generate-D0wZupBg.mjs → generate-CRSG0jTv.mjs} +2 -1
- package/dist/{generate-BnsL9GBm.d.mts → generate-DCGuEbqI.d.mts} +1 -0
- package/dist/{headers-BvlQSDMB.mjs → headers-BXjQYbeC.mjs} +2 -1
- package/dist/{health-CB_m4S32.mjs → health-5XnTa2uo.mjs} +1 -0
- package/dist/hooks/index.d.mts +2 -1
- package/dist/hooks/index.mjs +2 -1
- package/dist/idempotency/index.d.mts +3 -2
- package/dist/idempotency/index.mjs +8 -5
- package/dist/idempotency/redis.d.mts +2 -1
- package/dist/idempotency/redis.mjs +1 -0
- package/dist/{index-CG4yqMDB.d.mts → index-C-481Zf2.d.mts} +74 -18
- package/dist/{index-Q5gzEIWY.d.mts → index-CpzxqePp.d.mts} +107 -32
- package/dist/{index-Cx7bro4R.d.mts → index-DXlHwvuE.d.mts} +5 -4
- package/dist/index-ZWhLTp_s.d.mts +290 -0
- package/dist/{index-Ch16aAah.d.mts → index-zq6VrjaB.d.mts} +122 -15
- package/dist/index.d.mts +9 -8
- package/dist/index.mjs +11 -10
- package/dist/{init-tlRSt667.mjs → init-BbfP8aky.mjs} +25 -27
- package/dist/{init-BPEu8cs-.d.mts → init-Cscs4lab.d.mts} +1 -0
- package/dist/integrations/event-gateway.d.mts +3 -2
- package/dist/integrations/event-gateway.mjs +2 -1
- package/dist/integrations/index.d.mts +4 -3
- package/dist/integrations/index.mjs +1 -0
- package/dist/integrations/jobs/index.d.mts +2 -1
- package/dist/integrations/jobs/index.mjs +109 -33
- package/dist/integrations/mcp/index.d.mts +6 -6
- package/dist/integrations/mcp/index.mjs +2 -1
- package/dist/integrations/mcp/testing.d.mts +3 -2
- package/dist/integrations/mcp/testing.mjs +2 -2
- package/dist/integrations/streamline.d.mts +1 -0
- package/dist/integrations/streamline.mjs +3 -2
- package/dist/integrations/webhooks.d.mts +1 -0
- package/dist/integrations/webhooks.mjs +1 -0
- package/dist/integrations/websocket-pushref-redis.d.mts +2 -1
- package/dist/integrations/websocket-pushref-redis.mjs +1 -0
- package/dist/integrations/websocket-redis.d.mts +2 -1
- package/dist/integrations/websocket-redis.mjs +1 -0
- package/dist/integrations/websocket.d.mts +3 -2
- package/dist/integrations/websocket.mjs +38 -34
- package/dist/{interface-BGZCzOGL.d.mts → interface-BOk3YnTz.d.mts} +1 -0
- package/dist/{interface-n5hpKg1d.mjs → interface-BP4IJCBO.mjs} +1 -0
- package/dist/{interface-Uk8ONJv7.d.mts → interface-DEWNOMza.d.mts} +1 -0
- package/dist/{jsonBody-BlzAoGCe.mjs → jsonBody-DDSR2HFl.mjs} +2 -1
- package/dist/{keys-Cd316u_y.mjs → keys-B-HBTGXl.mjs} +1 -0
- package/dist/{loadResources-DpU8pfjV.mjs → loadResources-DisEAmkm.mjs} +4 -2
- package/dist/{loadResourcesFromEntry-kvGT6EcO.mjs → loadResourcesFromEntry-D1MJp13f.mjs} +2 -1
- package/dist/logger/index.d.mts +1 -0
- package/dist/logger/index.mjs +1 -0
- package/dist/{mcpPlugin-nRzm5g2s.mjs → mcpPlugin-CpfVpKHE.mjs} +38 -38
- package/dist/{memory-CmnzXE5Z.mjs → memory-DsuWnj4T.mjs} +5 -4
- package/dist/{metrics-fdxI5mn_.mjs → metrics-DdQL8syd.mjs} +2 -1
- package/dist/middleware/index.d.mts +2 -1
- package/dist/middleware/index.mjs +3 -2
- package/dist/migrations/index.d.mts +1 -0
- package/dist/migrations/index.mjs +3 -2
- package/dist/module-Br81am6m.mjs +78 -0
- package/dist/{multipartBody-D0eBvkeq.mjs → multipartBody-BnkBLN7v.mjs} +3 -2
- package/dist/{openapi-B8Vkbxbn.mjs → openapi-DIRzvxC0.mjs} +6 -5
- package/dist/outbox-admin/index.d.mts +85 -0
- package/dist/outbox-admin/index.mjs +115 -0
- package/dist/permissions/index.d.mts +5 -4
- package/dist/permissions/index.mjs +6 -5
- package/dist/{permissions-Bfu_8y1U.mjs → permissions-Dl_m0NCe.mjs} +308 -42
- package/dist/{pipe-BOLmGIxa.mjs → pipe-ELHCP3zj.mjs} +3 -4
- package/dist/pipeline/index.d.mts +2 -1
- package/dist/pipeline/index.mjs +2 -1
- package/dist/{pipeline-BJnpkY9i.mjs → pipeline-CPA-jx0M.mjs} +17 -14
- package/dist/plugins/index.d.mts +8 -7
- package/dist/plugins/index.mjs +16 -15
- package/dist/plugins/response-cache.d.mts +1 -0
- package/dist/plugins/response-cache.mjs +2 -1
- package/dist/plugins/tracing-entry.d.mts +2 -1
- package/dist/plugins/tracing-entry.mjs +2 -1
- package/dist/{pluralize-BhrgwZb9.mjs → pluralize-C2ZkKbsn.mjs} +1 -0
- package/dist/{preloadResources-DSY5HGpS.mjs → preloadResources-CDs87fTw.mjs} +1 -0
- package/dist/{preloadResources-DCfa2egz.d.mts → preloadResources-T1hmlTEZ.d.mts} +117 -12
- package/dist/presets/filesUpload.d.mts +6 -5
- package/dist/presets/filesUpload.mjs +10 -12
- package/dist/presets/index.d.mts +3 -2
- package/dist/presets/index.mjs +2 -1
- package/dist/presets/multiTenant.d.mts +2 -1
- package/dist/presets/multiTenant.mjs +3 -2
- package/dist/presets/search.d.mts +3 -2
- package/dist/presets/search.mjs +2 -1
- package/dist/{presets-CJOEXLuk.mjs → presets-Dntd_pUO.mjs} +6 -4
- package/dist/{projection-UBDgfMwu.mjs → projection-CjHhM5W1.mjs} +2 -1
- package/dist/{pushref-registry-BbcnpNTm.d.mts → pushref-registry-Dov9l4qE.d.mts} +1 -0
- package/dist/{queryCachePlugin-br4otiqF.d.mts → queryCachePlugin-BzqxconG.d.mts} +3 -2
- package/dist/{queryCachePlugin-DSGuSVLW.mjs → queryCachePlugin-Dhps-Ldt.mjs} +6 -5
- package/dist/{rateLimit-DrED-C4v.mjs → rateLimit-C7mmAGs3.mjs} +7 -3
- package/dist/{rateLimitKey-BoWRH3e7.mjs → rateLimitKey-CccEFBfk.mjs} +1 -0
- package/dist/{realtime-B9nmIY5V.mjs → realtime-DVvVZZFz.mjs} +20 -9
- package/dist/{redis-UDtLaNj6.d.mts → redis-CduhKsJi.d.mts} +2 -1
- package/dist/{redis-stream-uIaauNpZ.d.mts → redis-stream-qN6A-RFv.d.mts} +2 -1
- package/dist/{registerRoute-JkxIEBJq.mjs → registerRoute-B_QrjYZS.mjs} +10 -7
- package/dist/registry/index.d.mts +3 -2
- package/dist/registry/index.mjs +3 -2
- package/dist/{registry-BYXUUg21.mjs → registry-DqUmzA9W.mjs} +2 -1
- package/dist/{renewingLease-3cuQH7h8.mjs → renewingLease-BpP9wwj7.mjs} +1 -0
- package/dist/{replyHelpers-67tRawQZ.mjs → replyHelpers-DS-P0b32.mjs} +2 -1
- package/dist/{repositoryFilter-Dz8k3S4P.mjs → repositoryFilter-DotlxG3L.mjs} +1 -0
- package/dist/{requestContext-MrlJwECf.mjs → requestContext-B6vQF-sy.mjs} +1 -0
- package/dist/{rolldown-runtime-8H4AJuhK.mjs → rolldown-runtime-BhflRrBs.mjs} +1 -0
- package/dist/{schedules-CjOd2MG9.mjs → schedules-C2Q3uv2d.mjs} +3 -2
- package/dist/{schemaConverter-DJvodiHj.mjs → schemaConverter-Crjvczi_.mjs} +70 -17
- package/dist/{schemaIR-Ca7ySNPx.mjs → schemaIR-CnyKK_1j.mjs} +2 -1
- package/dist/schemas/index.d.mts +1 -0
- package/dist/schemas/index.mjs +1 -0
- package/dist/scim/index.d.mts +1 -0
- package/dist/scim/index.mjs +2 -1
- package/dist/scope/index.d.mts +3 -2
- package/dist/scope/index.mjs +5 -4
- package/dist/{scope-DfaPntpo.mjs → scope-afz_Ieek.mjs} +4 -3
- package/dist/{sessionManager-BHOM3N_0.d.mts → sessionManager-qimlKV0P.d.mts} +1 -0
- package/dist/{simpleEqualityMatcher-Cg05HPsn.mjs → simpleEqualityMatcher-C4KiMWUa.mjs} +1 -0
- package/dist/{sse-BQ1w0dfl.mjs → sse-B9P4Y-UI.mjs} +7 -5
- package/dist/{sseStream-BQ7Qaynp.mjs → sseStream-B3zb_d1h.mjs} +2 -1
- package/dist/{storage-DLUZ04kK.d.mts → storage-Cx9Sz3Ou.d.mts} +1 -0
- package/dist/{store-helpers-DY6xq4sM.mjs → store-helpers-DmEMHlPQ.mjs} +1 -0
- package/dist/{streaming-BQcW6_Kq.mjs → streaming-DPZdQmDz.mjs} +1 -0
- package/dist/{subscribe-helpers-DfzsivM6.mjs → subscribe-helpers-sp5xTDVA.mjs} +2 -1
- package/dist/sync/changelog.d.mts +1 -0
- package/dist/sync/changelog.mjs +1 -0
- package/dist/testing/cleanupStoreContract.d.mts +2 -1
- package/dist/testing/cleanupStoreContract.mjs +10 -5
- package/dist/testing/index.d.mts +166 -12
- package/dist/testing/index.mjs +315 -43
- package/dist/testing/outboxStoreContract.d.mts +12 -2
- package/dist/testing/outboxStoreContract.mjs +110 -20
- package/dist/testing/storageContract.d.mts +2 -1
- package/dist/testing/storageContract.mjs +11 -5
- package/dist/{tracing-CsGKQJGg.d.mts → tracing-Dro5eH6j.d.mts} +1 -0
- package/dist/{transactionContext-PNdoyLLm.mjs → transactionContext-BvLKvOIL.mjs} +1 -0
- package/dist/{typeGuards-C9KXUly6.mjs → typeGuards-B_5-hKjJ.mjs} +1 -0
- package/dist/types/index.d.mts +6 -5
- package/dist/types/index.mjs +1 -0
- package/dist/types/storage.d.mts +2 -1
- package/dist/types/storage.mjs +1 -0
- package/dist/{types-ChvvVNP8.d.mts → types-B7XeLEgK.d.mts} +34 -2
- package/dist/{types-Cc91nXuA.d.mts → types-ByHn9rEO.d.mts} +1 -0
- package/dist/{types-C_ZZRcD6.d.mts → types-C4QE8qnm.d.mts} +1 -0
- package/dist/{types-D_n8L6VY.d.mts → types-CT5vu6M0.d.mts} +132 -17
- package/dist/{types-DjC-KUCz.d.mts → types-ChuShpCP.d.mts} +1 -0
- package/dist/{types-Bmmn-fO0.d.mts → types-CwcUQ610.d.mts} +2 -6
- package/dist/{types-CSyCxl2V.mjs → types-DXYX7dIR.mjs} +4 -3
- package/dist/usage/index.d.mts +3 -2
- package/dist/usage/index.mjs +0 -0
- package/dist/{userHelpers-Cg_y3fBz.mjs → userHelpers-BNsOXzOd.mjs} +1 -0
- package/dist/utils/index.d.mts +16 -7
- package/dist/utils/index.mjs +10 -9
- package/dist/{utils-taUVD4p3.mjs → utils-BtOha_Sp.mjs} +3 -2
- package/dist/{validate-Bk44F2yT.mjs → validate-PMCPR9El.mjs} +2 -1
- package/dist/{versioning-gKWyryT3.mjs → versioning-CKv2jz_U.mjs} +4 -3
- package/dist/{versioning-DXgY5BUm.d.mts → versioning-D_rhHfZm.d.mts} +5 -2
- package/dist/{websocket-_Pd0CgR0.d.mts → websocket-BOXVggMf.d.mts} +2 -1
- package/package.json +557 -539
- package/skills/arc/SKILL.md +22 -8
- package/skills/arc/references/api-reference.md +4 -0
- package/skills/arc/references/auth.md +3 -0
- package/skills/arc/references/integrations.md +33 -0
- package/skills/arc/references/mcp.md +6 -7
- package/skills/arc/references/production.md +24 -25
- package/skills/arc-code-review/references/anti-patterns.md +3 -3
- package/dist/fastify-augmentation-0WhPnUH7.d.mts +0 -76
- package/dist/index-CCzVR-N3.d.mts +0 -107
- package/dist/module-_-M7C5ES.mjs +0 -29
|
@@ -1,21 +1,22 @@
|
|
|
1
|
-
|
|
1
|
+
/*! @classytic/arc v2.32.0 | MIT | https://classytic.com */
|
|
2
|
+
import { s as DEFAULT_UPDATE_METHOD, t as CRUD_OPERATIONS } from "./constants-J99LGTJI.mjs";
|
|
2
3
|
import { arcLog } from "./logger/index.mjs";
|
|
3
|
-
import { r as ForbiddenError } from "./errors-
|
|
4
|
-
import { n as getUserRoles } from "./userHelpers-
|
|
5
|
-
import { b as isElevated, n as PUBLIC_SCOPE, x as isMember } from "./types-
|
|
6
|
-
import { d as DEFAULT_FIELD_WRITE_DENIAL_POLICY, t as BaseController } from "./BaseController-
|
|
7
|
-
import { a as buildRouteHooks, c as buildAuthMiddleware, i as selectPluginMw, n as buildRouteConfig, o as resolveRoutePreHandlers, r as resolveRouterPluginMw, s as routeHookOptions, t as buildRateLimitConfig, u as buildArcDecorator } from "./rateLimit-
|
|
8
|
-
import { i as resolveEffectiveRoles, n as applyFieldWritePermissions } from "./fields-
|
|
9
|
-
import { r as buildCrudPermissionMw, t as tryRegisterRoute } from "./registerRoute-
|
|
10
|
-
import { c as getDefaultCrudSchemas } from "./utils-
|
|
11
|
-
import { n as convertRouteSchema, r as ensureSchemaConverter, t as convertOpenApiSchemas } from "./schemaConverter-
|
|
12
|
-
import { i as pipeUIMessageStreamToReply, r as isReadableStream } from "./streaming-
|
|
13
|
-
import { t as hasEvents } from "./typeGuards-
|
|
14
|
-
import { a as createFastifyHandler, i as createCrudHandlers, n as buildPipelineHandler, o as createRequestContext, r as resolvePipelineSteps } from "./pipeline-
|
|
15
|
-
import {
|
|
16
|
-
import { r as getAvailablePresets, t as applyPresets } from "./presets-
|
|
17
|
-
import { t as pluralize } from "./pluralize-
|
|
18
|
-
import { t as resolveActionPermission } from "./actionPermissions-
|
|
4
|
+
import { r as ForbiddenError } from "./errors-Bh1KK5ps.mjs";
|
|
5
|
+
import { n as getUserRoles } from "./userHelpers-BNsOXzOd.mjs";
|
|
6
|
+
import { b as isElevated, n as PUBLIC_SCOPE, x as isMember } from "./types-DXYX7dIR.mjs";
|
|
7
|
+
import { d as DEFAULT_FIELD_WRITE_DENIAL_POLICY, t as BaseController } from "./BaseController-Dk34dxhI.mjs";
|
|
8
|
+
import { a as buildRouteHooks, c as buildAuthMiddleware, i as selectPluginMw, n as buildRouteConfig, o as resolveRoutePreHandlers, r as resolveRouterPluginMw, s as routeHookOptions, t as buildRateLimitConfig, u as buildArcDecorator } from "./rateLimit-C7mmAGs3.mjs";
|
|
9
|
+
import { i as resolveEffectiveRoles, n as applyFieldWritePermissions } from "./fields-DEi65pGj.mjs";
|
|
10
|
+
import { r as buildCrudPermissionMw, t as tryRegisterRoute } from "./registerRoute-B_QrjYZS.mjs";
|
|
11
|
+
import { c as getDefaultCrudSchemas } from "./utils-BtOha_Sp.mjs";
|
|
12
|
+
import { n as convertRouteSchema, r as ensureSchemaConverter, t as convertOpenApiSchemas } from "./schemaConverter-Crjvczi_.mjs";
|
|
13
|
+
import { i as pipeUIMessageStreamToReply, r as isReadableStream } from "./streaming-DPZdQmDz.mjs";
|
|
14
|
+
import { t as hasEvents } from "./typeGuards-B_5-hKjJ.mjs";
|
|
15
|
+
import { a as createFastifyHandler, i as createCrudHandlers, n as buildPipelineHandler, o as createRequestContext, r as resolvePipelineSteps } from "./pipeline-CPA-jx0M.mjs";
|
|
16
|
+
import { B as requireAuth } from "./permissions-Dl_m0NCe.mjs";
|
|
17
|
+
import { r as getAvailablePresets, t as applyPresets } from "./presets-Dntd_pUO.mjs";
|
|
18
|
+
import { t as pluralize } from "./pluralize-C2ZkKbsn.mjs";
|
|
19
|
+
import { t as resolveActionPermission } from "./actionPermissions-Dti7ebyv.mjs";
|
|
19
20
|
//#region src/core/middlewares/fieldWrite.ts
|
|
20
21
|
/**
|
|
21
22
|
* Build a preHandler that enforces field-write permissions on the request body.
|
|
@@ -41,7 +42,10 @@ function buildFieldWritePreHandler(fieldPermissions, policy = DEFAULT_FIELD_WRIT
|
|
|
41
42
|
if (!body || typeof body !== "object" || Array.isArray(body)) return;
|
|
42
43
|
const scope = request.scope ?? PUBLIC_SCOPE;
|
|
43
44
|
if (isElevated(scope)) return;
|
|
44
|
-
const
|
|
45
|
+
const globalRoles = getUserRoles(request.user);
|
|
46
|
+
const orgRoles = isMember(scope) ? scope.orgRoles : [];
|
|
47
|
+
const effectiveRoles = resolveEffectiveRoles(globalRoles, orgRoles);
|
|
48
|
+
const { body: filtered, deniedFields } = applyFieldWritePermissions(body, fieldPermissions, effectiveRoles);
|
|
45
49
|
if (deniedFields.length > 0 && policy === "reject") throw new ForbiddenError(`Not permitted to write field${deniedFields.length === 1 ? "" : "s"}: ${deniedFields.join(", ")}`);
|
|
46
50
|
request.body = filtered;
|
|
47
51
|
};
|
|
@@ -58,25 +62,30 @@ function methodCarriesBody(method) {
|
|
|
58
62
|
//#region src/core/createCrudRouter.ts
|
|
59
63
|
/**
|
|
60
64
|
* Mount custom routes (from presets or user-defined `routes`) on Fastify.
|
|
61
|
-
* `wrapHandler` is derived inline from
|
|
65
|
+
* `wrapHandler` is derived inline from whether `rawHandler` was set.
|
|
62
66
|
*/
|
|
63
67
|
function createCustomRoutes(fastify, routes, controller, options) {
|
|
64
68
|
const { tag, resourceName, arcDecorator, rateLimitConfig, pluginMw, pipeline, routeGuards, tenantScopeMw, fieldPermissions, onFieldWriteDenied, extensions } = options;
|
|
65
69
|
for (const route of routes) {
|
|
70
|
+
if ("raw" in route) throw new Error(`Route ${route.method} ${route.path}: \`raw\` was removed in arc 2.31 — the FIELD now carries the intent. Move the function to \`rawHandler\` (Fastify-native \`(request, reply)\`) or leave it in \`handler\` (arc pipeline, \`(ctx)\`), and delete the flag. If this route comes from a dependency, that package needs a release built against arc >=2.31.`);
|
|
66
71
|
const routeWithRefs = route;
|
|
67
|
-
const hasHandler = route.handler !== void 0;
|
|
72
|
+
const hasHandler = route.handler !== void 0 || route.rawHandler !== void 0;
|
|
68
73
|
const hasControllerMethod = typeof routeWithRefs.controllerMethod === "function";
|
|
69
74
|
if (hasHandler && hasControllerMethod) throw new Error(`Route ${route.method} ${route.path}: pass either \`handler\` or \`controllerMethod\`, not both. Prefer \`controllerMethod: (c: MyController) => c.method\` for typed handler refs (TS catches typos).`);
|
|
70
|
-
if (!hasHandler && !hasControllerMethod) throw new Error(`Route ${route.method} ${route.path}: must declare
|
|
75
|
+
if (!hasHandler && !hasControllerMethod) throw new Error(`Route ${route.method} ${route.path}: must declare one of \`handler\` (arc pipeline), \`rawHandler\` (Fastify-native), or \`controllerMethod: (c) => c.method\` (typed function-ref form).`);
|
|
71
76
|
let resolvedHandler;
|
|
72
77
|
if (hasControllerMethod) {
|
|
73
78
|
if (!controller) throw new Error(`Route ${route.method} ${route.path}: \`controllerMethod\` requires a controller. Provide one via \`defineResource({ controller, … })\`, or use \`defineResource\` with an \`adapter\` so arc auto-creates a BaseController.`);
|
|
74
79
|
const referenced = routeWithRefs.controllerMethod?.(controller);
|
|
75
80
|
if (typeof referenced !== "function") throw new Error(`Route ${route.method} ${route.path}: \`controllerMethod\` did not return a function. Return the method itself: \`controllerMethod: (c) => c.myMethod\`.`);
|
|
76
81
|
resolvedHandler = referenced.bind(controller);
|
|
77
|
-
} else
|
|
82
|
+
} else {
|
|
83
|
+
if (route.rawHandler && route.handler) throw new Error(`Route ${route.method} ${route.path}: set either \`handler\` (arc pipeline) or \`rawHandler\` (Fastify-native), not both.`);
|
|
84
|
+
resolvedHandler = route.rawHandler ?? route.handler;
|
|
85
|
+
}
|
|
78
86
|
const opName = route.operation ?? (typeof resolvedHandler === "string" ? resolvedHandler : `${route.method.toLowerCase()}${route.path.replace(/[/:]/g, "_")}`);
|
|
79
|
-
const
|
|
87
|
+
const isRaw = route.rawHandler !== void 0;
|
|
88
|
+
const wrapHandler = !isRaw;
|
|
80
89
|
let handler;
|
|
81
90
|
if (typeof resolvedHandler === "string") {
|
|
82
91
|
if (!controller) throw new Error(`Route ${route.method} ${route.path}: string handler '${resolvedHandler}' requires a controller. Either provide a controller or use a function handler instead.`);
|
|
@@ -104,12 +113,13 @@ function createCustomRoutes(fastify, routes, controller, options) {
|
|
|
104
113
|
if (!tenantScopeMw || tenantScopeMw.length === 0) throw new Error(`Route ${route.method} ${route.path}: \`tenantScope: true\` requires a multi-tenant preset. Add \`multiTenantPreset()\` (or \`flexibleMultiTenantPreset()\`) to the resource's \`presets\`, or remove the \`tenantScope\` flag from this route.`);
|
|
105
114
|
customPreHandlers.unshift(...tenantScopeMw);
|
|
106
115
|
}
|
|
107
|
-
if (
|
|
116
|
+
if (!isRaw && route.fieldWrite !== false && methodCarriesBody(route.method)) {
|
|
108
117
|
const fieldWriteMw = buildFieldWritePreHandler(fieldPermissions, onFieldWriteDenied);
|
|
109
118
|
if (fieldWriteMw) customPreHandlers.push(fieldWriteMw);
|
|
110
119
|
}
|
|
120
|
+
const preAuthHandlers = route.preAuth ?? [];
|
|
111
121
|
const hooks = buildRouteHooks({
|
|
112
|
-
preAuth:
|
|
122
|
+
preAuth: preAuthHandlers,
|
|
113
123
|
arcDecorator,
|
|
114
124
|
authMw: buildAuthMiddleware(fastify, route.permissions),
|
|
115
125
|
permissionMw: buildCrudPermissionMw(route.permissions, resourceName, opName),
|
|
@@ -123,18 +133,18 @@ function createCustomRoutes(fastify, routes, controller, options) {
|
|
|
123
133
|
url: route.path,
|
|
124
134
|
schema,
|
|
125
135
|
...routeHookOptions(hooks),
|
|
126
|
-
handler: isStream ? async (request, reply)
|
|
136
|
+
handler: isStream ? async function streamRoute(request, reply) {
|
|
127
137
|
reply.raw.setHeader("Content-Type", "text/event-stream");
|
|
128
138
|
reply.raw.setHeader("Cache-Control", "no-cache");
|
|
129
139
|
reply.raw.setHeader("Connection", "keep-alive");
|
|
130
|
-
const result = await handler(request, reply);
|
|
140
|
+
const result = await handler.call(this, request, reply);
|
|
131
141
|
if (isReadableStream(result)) {
|
|
132
142
|
await pipeUIMessageStreamToReply(reply, result);
|
|
133
143
|
return reply;
|
|
134
144
|
}
|
|
135
145
|
return result;
|
|
136
146
|
} : handler,
|
|
137
|
-
...buildRouteConfig(route.rateLimit !== void 0 ? buildRateLimitConfig(route.rateLimit) : rateLimitConfig, extensions)
|
|
147
|
+
...buildRouteConfig(route.rateLimit !== void 0 ? buildRateLimitConfig(route.rateLimit) : rateLimitConfig, extensions, route.cors)
|
|
138
148
|
}, {
|
|
139
149
|
resourceName,
|
|
140
150
|
op: opName
|
|
@@ -281,7 +291,8 @@ function buildCrudHandlers(ctrl, pipeline, resourceName) {
|
|
|
281
291
|
for (const op of CRUD_OPERATIONS) {
|
|
282
292
|
const steps = resolvePipelineSteps(pipeline, op);
|
|
283
293
|
if (steps.length === 0) continue;
|
|
284
|
-
|
|
294
|
+
const method = ctrl[op].bind(ctrl);
|
|
295
|
+
wrapped[op] = buildPipelineHandler(method, steps, op, resourceName);
|
|
285
296
|
}
|
|
286
297
|
return wrapped;
|
|
287
298
|
}
|
|
@@ -640,8 +651,7 @@ function expandHistory(config) {
|
|
|
640
651
|
method: "GET",
|
|
641
652
|
path: "/:id/history",
|
|
642
653
|
summary: `Change history for a ${resourceName} record (audit-backed timeline)`,
|
|
643
|
-
|
|
644
|
-
handler: async (request, reply) => {
|
|
654
|
+
rawHandler: async (request, reply) => {
|
|
645
655
|
const audit = request.server.audit;
|
|
646
656
|
if (!audit?.query || audit._noop === true) return reply.code(503).send({
|
|
647
657
|
code: "history.audit_unavailable",
|
|
@@ -1221,11 +1231,13 @@ function buildResourcePlugin(resource) {
|
|
|
1221
1231
|
handler: hook.handler,
|
|
1222
1232
|
priority: hook.priority
|
|
1223
1233
|
});
|
|
1224
|
-
if (isFirstMount && resource.cache?.invalidateOn)
|
|
1225
|
-
pattern,
|
|
1226
|
-
|
|
1227
|
-
|
|
1228
|
-
|
|
1234
|
+
if (isFirstMount && resource.cache?.invalidateOn) {
|
|
1235
|
+
if (fastify.registerCacheInvalidationRule) for (const [pattern, tags] of Object.entries(resource.cache.invalidateOn)) fastify.registerCacheInvalidationRule({
|
|
1236
|
+
pattern,
|
|
1237
|
+
tags
|
|
1238
|
+
});
|
|
1239
|
+
else fastify.log?.warn?.(`[Arc] Resource '${resource.name}' declares \`cache.invalidateOn\` but \`queryCachePlugin\` is not registered. Cache-invalidation rules are silently ignored. Either register the plugin (\`await fastify.register(queryCachePlugin, ...)\`) or remove the \`cache.invalidateOn\` config to clean up the resource definition.`);
|
|
1240
|
+
}
|
|
1229
1241
|
if (isFirstMount && resource.audit) {
|
|
1230
1242
|
if (!(fastify.hasDecorator("audit") && typeof fastify.audit?.create === "function" && fastify.audit?._noop !== true)) fastify.log?.warn?.(`[Arc] Resource '${resource.name}' declares \`audit: ${JSON.stringify(resource.audit)}\` but \`auditPlugin\` is not registered (or is registered with a noop logger). Audit events for this resource will NOT be recorded. Either register \`auditPlugin\` (\`await fastify.register(auditPlugin, { store: ... })\`) or remove the \`audit\` flag from the resource definition.`);
|
|
1231
1243
|
}
|
|
@@ -1266,7 +1278,7 @@ function buildResourcePlugin(resource) {
|
|
|
1266
1278
|
extensions: resource.extensions
|
|
1267
1279
|
});
|
|
1268
1280
|
if (resource.actions && Object.keys(resource.actions).length > 0) {
|
|
1269
|
-
const { createActionRouter } = await import("./createActionRouter-
|
|
1281
|
+
const { createActionRouter } = await import("./createActionRouter-DkJZWo0m.mjs").then((n) => n.n);
|
|
1270
1282
|
createActionRouter(typedInstance, {
|
|
1271
1283
|
...normalizeActionsToRouterConfig(resource.actions, resource.actionPermissions, resource.tag, resource.permissions, resource.name, typedInstance.log),
|
|
1272
1284
|
resourceName: resource.name,
|
|
@@ -1281,7 +1293,7 @@ function buildResourcePlugin(resource) {
|
|
|
1281
1293
|
});
|
|
1282
1294
|
}
|
|
1283
1295
|
if (resource.aggregations && Object.keys(resource.aggregations).length > 0) {
|
|
1284
|
-
const [{ createAggregationRouter }, { adapterSupportsAggregate, ArcAggregationConfigError }] = await Promise.all([import("./createAggregationRouter-
|
|
1296
|
+
const [{ createAggregationRouter }, { adapterSupportsAggregate, ArcAggregationConfigError }] = await Promise.all([import("./createAggregationRouter-B1exOVD8.mjs"), import("./validate-PMCPR9El.mjs").then((n) => n.r)]);
|
|
1285
1297
|
const repoForAgg = resource.controller?.repository ?? resource.repository;
|
|
1286
1298
|
if (!adapterSupportsAggregate(repoForAgg)) {
|
|
1287
1299
|
const undispatchable = Object.entries(resource.aggregations).filter(([, cfg]) => !cfg.materialized).map(([name]) => name);
|
|
@@ -1481,10 +1493,12 @@ var ResourceDefinition = class {
|
|
|
1481
1493
|
_validateControllerMethods() {
|
|
1482
1494
|
const errors = [];
|
|
1483
1495
|
const enabledCrudRoutes = CRUD_OPERATIONS.filter((route) => !this.disabledRoutes.includes(route));
|
|
1484
|
-
if (!this.disableDefaultRoutes && enabledCrudRoutes.length > 0)
|
|
1485
|
-
|
|
1486
|
-
|
|
1487
|
-
|
|
1496
|
+
if (!this.disableDefaultRoutes && enabledCrudRoutes.length > 0) {
|
|
1497
|
+
if (!this.controller) errors.push("Controller is required when CRUD routes are enabled");
|
|
1498
|
+
else {
|
|
1499
|
+
const ctrl = this.controller;
|
|
1500
|
+
for (const method of enabledCrudRoutes) if (typeof ctrl[method] !== "function") errors.push(`CRUD method '${method}' not found on controller`);
|
|
1501
|
+
}
|
|
1488
1502
|
}
|
|
1489
1503
|
for (const route of this.routes) {
|
|
1490
1504
|
if (typeof route.handler !== "string") continue;
|
|
@@ -1537,7 +1551,7 @@ var ResourceDefinition = class {
|
|
|
1537
1551
|
summary: r.summary,
|
|
1538
1552
|
description: r.description,
|
|
1539
1553
|
permissions: r.permissions,
|
|
1540
|
-
raw: r.
|
|
1554
|
+
raw: r.rawHandler !== void 0,
|
|
1541
1555
|
schema: r.schema
|
|
1542
1556
|
})),
|
|
1543
1557
|
routes: [],
|
|
@@ -1815,17 +1829,22 @@ function validateResourceConfig(config, options = {}) {
|
|
|
1815
1829
|
}
|
|
1816
1830
|
function validateRouteHandlers(controller, routes, errors) {
|
|
1817
1831
|
const ctrl = controller;
|
|
1818
|
-
for (const route of routes)
|
|
1819
|
-
|
|
1832
|
+
for (const route of routes) {
|
|
1833
|
+
const named = typeof route.handler === "string" ? route.handler : route.rawHandler;
|
|
1834
|
+
if (typeof named !== "string") continue;
|
|
1835
|
+
if (typeof ctrl[named] !== "function") errors.push({
|
|
1820
1836
|
field: `routes[${route.method} ${route.path}]`,
|
|
1821
|
-
message: `Handler "${
|
|
1822
|
-
suggestion: `Add method "${
|
|
1837
|
+
message: `Handler "${named}" not found on controller`,
|
|
1838
|
+
suggestion: `Add method "${named}" to controller or use a function handler`
|
|
1823
1839
|
});
|
|
1824
1840
|
}
|
|
1825
1841
|
}
|
|
1826
1842
|
function validatePermissionKeys(config, options, _errors, warnings) {
|
|
1827
1843
|
const validKeys = /* @__PURE__ */ new Set([...CRUD_OPERATIONS, ...options.additionalPermissionKeys ?? []]);
|
|
1828
|
-
for (const route of config.routes ?? [])
|
|
1844
|
+
for (const route of config.routes ?? []) {
|
|
1845
|
+
if (typeof route.handler === "string") validKeys.add(route.handler);
|
|
1846
|
+
if (typeof route.rawHandler === "string") validKeys.add(route.rawHandler);
|
|
1847
|
+
}
|
|
1829
1848
|
for (const preset of config.presets ?? []) {
|
|
1830
1849
|
const presetName = typeof preset === "string" ? preset : preset.name;
|
|
1831
1850
|
if (presetName === "softDelete") {
|
|
@@ -1966,11 +1985,26 @@ function validateRoutes(routes, errors) {
|
|
|
1966
1985
|
suggestion: `Change to "/${route.path}"`
|
|
1967
1986
|
});
|
|
1968
1987
|
const routeWithRefs = route;
|
|
1969
|
-
if (!route.handler && typeof routeWithRefs.controllerMethod !== "function") errors.push({
|
|
1988
|
+
if (!route.handler && !route.rawHandler && typeof routeWithRefs.controllerMethod !== "function") errors.push({
|
|
1970
1989
|
field: `routes[${i}].handler`,
|
|
1971
|
-
message: "Route must declare
|
|
1990
|
+
message: "Route must declare one of `handler` (arc pipeline), `rawHandler` (Fastify-native), or `controllerMethod`",
|
|
1972
1991
|
suggestion: "Prefer `controllerMethod: (c: MyController) => c.method` for typed handler refs."
|
|
1973
1992
|
});
|
|
1993
|
+
if (route.handler && route.rawHandler) errors.push({
|
|
1994
|
+
field: `routes[${i}].rawHandler`,
|
|
1995
|
+
message: "Route declares BOTH `handler` and `rawHandler`",
|
|
1996
|
+
suggestion: "Use `handler` for the arc pipeline (receives IRequestContext) or `rawHandler` for a Fastify-native handler (receives request, reply) — not both."
|
|
1997
|
+
});
|
|
1998
|
+
if ("raw" in route) errors.push({
|
|
1999
|
+
field: `routes[${i}].raw`,
|
|
2000
|
+
message: "`raw` was removed in arc 2.31 — the field now carries the intent",
|
|
2001
|
+
suggestion: "Move the function to `rawHandler` (Fastify-native) or leave it in `handler` (arc pipeline), and delete the flag. If the route comes from a dependency, that package needs a release built against arc >=2.31."
|
|
2002
|
+
});
|
|
2003
|
+
if (route.streamResponse === true && route.rawHandler === void 0) errors.push({
|
|
2004
|
+
field: `routes[${i}].streamResponse`,
|
|
2005
|
+
message: "`streamResponse: true` requires `rawHandler`",
|
|
2006
|
+
suggestion: "Streaming routes are invoked with `(request, reply)` and own the socket — declare the function as `rawHandler` (not `handler` or `controllerMethod`)."
|
|
2007
|
+
});
|
|
1974
2008
|
const routeKey = `${route.method} ${route.path}`;
|
|
1975
2009
|
if (seenRoutes.has(routeKey)) errors.push({
|
|
1976
2010
|
field: `routes[${i}]`,
|
package/dist/discovery/index.mjs
CHANGED
package/dist/docs/index.d.mts
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
|
-
|
|
2
|
-
import {
|
|
1
|
+
|
|
2
|
+
import { p as RegistryEntry } from "../index-CpzxqePp.mjs";
|
|
3
|
+
import { t as ExternalOpenApiPaths } from "../externalPaths-CmGOxOtD.mjs";
|
|
3
4
|
import { FastifyPluginAsync } from "fastify";
|
|
4
5
|
//#region src/docs/openapi/types.d.ts
|
|
5
6
|
/**
|
package/dist/docs/index.mjs
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
|
-
|
|
2
|
-
import { n as
|
|
1
|
+
/*! @classytic/arc v2.32.0 | MIT | https://classytic.com */
|
|
2
|
+
import { n as getUserRoles } from "../userHelpers-BNsOXzOd.mjs";
|
|
3
|
+
import { n as openApiPlugin, r as openapi_default, t as buildOpenApiSpec } from "../openapi-DIRzvxC0.mjs";
|
|
3
4
|
import fp from "fastify-plugin";
|
|
4
5
|
//#region src/docs/scalar.ts
|
|
5
6
|
const scalarPlugin = async (fastify, opts = {}) => {
|
|
@@ -1,7 +1,8 @@
|
|
|
1
|
-
|
|
1
|
+
/*! @classytic/arc v2.32.0 | MIT | https://classytic.com */
|
|
2
|
+
import { t as __exportAll } from "./rolldown-runtime-BhflRrBs.mjs";
|
|
2
3
|
import { arcLog } from "./logger/index.mjs";
|
|
3
|
-
import { n as requireSingleHeaderValue } from "./headers-
|
|
4
|
-
import { n as getUserRoles } from "./userHelpers-
|
|
4
|
+
import { n as requireSingleHeaderValue } from "./headers-BXjQYbeC.mjs";
|
|
5
|
+
import { n as getUserRoles } from "./userHelpers-BNsOXzOd.mjs";
|
|
5
6
|
import fp from "fastify-plugin";
|
|
6
7
|
//#region src/scope/elevation.ts
|
|
7
8
|
var elevation_exports = /* @__PURE__ */ __exportAll({
|
|
@@ -1,4 +1,5 @@
|
|
|
1
|
-
|
|
1
|
+
|
|
2
|
+
import { a as JweAlg, c as KeyMaterial, i as EncryptionOptions, l as KeyProvider, n as EncryptionDirective, o as JweEnc, r as EncryptionMode, s as KeyContext, t as ActiveKey } from "../types-ByHn9rEO.mjs";
|
|
2
3
|
import { FastifyPluginAsync } from "fastify";
|
|
3
4
|
//#region src/encryption/encryptionPlugin.d.ts
|
|
4
5
|
declare const encryptionPlugin: FastifyPluginAsync<EncryptionOptions>;
|
|
@@ -1,5 +1,6 @@
|
|
|
1
|
-
|
|
2
|
-
import { t as
|
|
1
|
+
/*! @classytic/arc v2.32.0 | MIT | https://classytic.com */
|
|
2
|
+
import { t as ArcError } from "../errors-Bh1KK5ps.mjs";
|
|
3
|
+
import { t as parseJsonBody } from "../jsonBody-DDSR2HFl.mjs";
|
|
3
4
|
import { createCipheriv, createDecipheriv, randomBytes } from "node:crypto";
|
|
4
5
|
import fp from "fastify-plugin";
|
|
5
6
|
//#region src/encryption/fieldCipher.ts
|
|
@@ -70,7 +71,8 @@ function parseFieldEnvelope(token) {
|
|
|
70
71
|
}
|
|
71
72
|
/** Decrypt a parsed `arc.v1` envelope with the resolved key. Throws on tamper. */
|
|
72
73
|
function decryptField(envelope, key) {
|
|
73
|
-
const
|
|
74
|
+
const secret = toKeyBuffer(key);
|
|
75
|
+
const decipher = createDecipheriv("aes-256-gcm", secret, envelope.iv);
|
|
74
76
|
decipher.setAuthTag(envelope.tag);
|
|
75
77
|
return Buffer.concat([decipher.update(envelope.ciphertext), decipher.final()]).toString("utf8");
|
|
76
78
|
}
|
|
@@ -1,5 +1,6 @@
|
|
|
1
|
-
|
|
2
|
-
import {
|
|
1
|
+
/*! @classytic/arc v2.32.0 | MIT | https://classytic.com */
|
|
2
|
+
import { t as __exportAll } from "./rolldown-runtime-BhflRrBs.mjs";
|
|
3
|
+
import { m as statusToArcCode, p as isArcError } from "./errors-Bh1KK5ps.mjs";
|
|
3
4
|
import { isHttpError, toErrorContract } from "@classytic/repo-core/errors";
|
|
4
5
|
import fp from "fastify-plugin";
|
|
5
6
|
//#region src/plugins/errorHandler.ts
|
|
@@ -1,8 +1,9 @@
|
|
|
1
|
-
|
|
1
|
+
/*! @classytic/arc v2.32.0 | MIT | https://classytic.com */
|
|
2
|
+
import { t as __exportAll } from "./rolldown-runtime-BhflRrBs.mjs";
|
|
2
3
|
import { arcLog } from "./logger/index.mjs";
|
|
3
|
-
import { d as createDomainError } from "./errors-
|
|
4
|
-
import { n as requestContext } from "./requestContext-
|
|
5
|
-
import { n as createEvent, t as MemoryEventTransport } from "./EventTransport-
|
|
4
|
+
import { d as createDomainError } from "./errors-Bh1KK5ps.mjs";
|
|
5
|
+
import { n as requestContext } from "./requestContext-B6vQF-sy.mjs";
|
|
6
|
+
import { n as createEvent, t as MemoryEventTransport } from "./EventTransport-BV10D0oG.mjs";
|
|
6
7
|
import fp from "fastify-plugin";
|
|
7
8
|
//#region src/events/retry.ts
|
|
8
9
|
/**
|
|
@@ -138,10 +139,11 @@ const eventPlugin = async (fastify, opts = {}) => {
|
|
|
138
139
|
if (type.startsWith("$") && type !== "$deadLetter") throw new Error(`[Arc Events] Event type '${type}' uses reserved '$' prefix`);
|
|
139
140
|
if (type.length > 256) throw new Error("[Arc Events] Event type exceeds 256 characters");
|
|
140
141
|
const store = requestContext.get();
|
|
141
|
-
const
|
|
142
|
+
const enrichedMeta = {
|
|
142
143
|
...store?.requestId && !meta?.correlationId ? { correlationId: store.requestId } : {},
|
|
143
144
|
...meta
|
|
144
|
-
}
|
|
145
|
+
};
|
|
146
|
+
const event = createEvent(type, payload, enrichedMeta);
|
|
145
147
|
if (warnOnDuplicate && event.meta.correlationId) {
|
|
146
148
|
const now = Date.now();
|
|
147
149
|
evictExpiredPublishes(now);
|
|
@@ -1,4 +1,5 @@
|
|
|
1
|
-
|
|
1
|
+
|
|
2
|
+
import { a as EventTransport, i as EventLogger, n as DomainEvent, r as EventHandler } from "./EventTransport-2uVV34WU.mjs";
|
|
2
3
|
import { FastifyPluginAsync } from "fastify";
|
|
3
4
|
//#region src/events/defineEvent.d.ts
|
|
4
5
|
interface EventSchema {
|
package/dist/events/index.d.mts
CHANGED
|
@@ -1,8 +1,12 @@
|
|
|
1
|
-
|
|
2
|
-
import { a as
|
|
1
|
+
|
|
2
|
+
import { a as EventTransport, i as EventLogger, n as DomainEvent, o as MemoryEventTransport, r as EventHandler, s as MemoryEventTransportOptions, t as DeadLetteredEvent } from "../EventTransport-2uVV34WU.mjs";
|
|
3
|
+
import { t as ArcModule } from "../types-CT5vu6M0.mjs";
|
|
4
|
+
import { a as withRetry, c as EventDefinitionOutput, d as EventSchema, f as ValidationResult, i as createDeadLetterPublisher, l as EventRegistry, m as defineEvent, n as eventPlugin, o as CustomValidator, p as createEventRegistry, r as RetryOptions, s as EventDefinitionInput, t as EventPluginOptions, u as EventRegistryOptions } from "../eventPlugin-GF5nnBIq.mjs";
|
|
3
5
|
import { RedisEventTransportOptions, RedisLike } from "./transports/redis.mjs";
|
|
4
|
-
import { r as RedisStreamTransportOptions, t as RedisStreamLike } from "../redis-stream-
|
|
5
|
-
import {
|
|
6
|
+
import { r as RedisStreamTransportOptions, t as RedisStreamLike } from "../redis-stream-qN6A-RFv.mjs";
|
|
7
|
+
import { FastifyInstance } from "fastify";
|
|
8
|
+
import { MemoryOutboxStore } from "@classytic/primitives/memory-outbox";
|
|
9
|
+
import { OutboxFailurePolicy, OutboxStore, OutboxWriteOptions } from "@classytic/primitives/outbox";
|
|
6
10
|
import { RepositoryLike } from "@classytic/repo-core/adapter";
|
|
7
11
|
//#region src/events/eventTypes.d.ts
|
|
8
12
|
/**
|
|
@@ -55,44 +59,6 @@ declare const CACHE_EVENTS: Readonly<{
|
|
|
55
59
|
/** Type for cache event names */
|
|
56
60
|
type CacheEvent = (typeof CACHE_EVENTS)[keyof typeof CACHE_EVENTS];
|
|
57
61
|
//#endregion
|
|
58
|
-
//#region src/events/memory-outbox.d.ts
|
|
59
|
-
interface MemoryEntry {
|
|
60
|
-
event: DomainEvent;
|
|
61
|
-
status: "pending" | "delivered" | "dead_letter";
|
|
62
|
-
attempts: number;
|
|
63
|
-
visibleAt: number;
|
|
64
|
-
leaseOwner: string | null;
|
|
65
|
-
leaseExpiresAt: number;
|
|
66
|
-
deliveredAt: number | null;
|
|
67
|
-
firstFailedAt: number | null;
|
|
68
|
-
lastFailedAt: number | null;
|
|
69
|
-
lastError: OutboxErrorInfo | null;
|
|
70
|
-
dedupeKey?: string;
|
|
71
|
-
}
|
|
72
|
-
declare class MemoryOutboxStore implements OutboxStore {
|
|
73
|
-
private readonly entries;
|
|
74
|
-
private readonly seenDedupeKeys;
|
|
75
|
-
/**
|
|
76
|
-
* Drop every entry (including delivered / failed / dead-lettered rows) and
|
|
77
|
-
* the dedupe-key memory.
|
|
78
|
-
*
|
|
79
|
-
* For tests only — it is the in-memory analogue of truncating the table, and
|
|
80
|
-
* exists so a suite can isolate cases against ONE store instance (the
|
|
81
|
-
* `reset` hook of `runOutboxStoreContract`). Not part of `OutboxStore`; a
|
|
82
|
-
* production store must never expose this.
|
|
83
|
-
*/
|
|
84
|
-
clear(): void;
|
|
85
|
-
save(event: DomainEvent, options?: OutboxWriteOptions): Promise<void>;
|
|
86
|
-
getPending(limit: number): Promise<DomainEvent[]>;
|
|
87
|
-
claimPending(options?: OutboxClaimOptions): Promise<DomainEvent[]>;
|
|
88
|
-
acknowledge(eventId: string, options?: OutboxAcknowledgeOptions): Promise<void>;
|
|
89
|
-
fail(eventId: string, error: OutboxErrorInfo, options?: OutboxFailOptions): Promise<void>;
|
|
90
|
-
getDeadLettered(limit: number): Promise<DeadLetteredEvent[]>;
|
|
91
|
-
purge(olderThanMs: number): Promise<number>;
|
|
92
|
-
/** Test helper: inspect entry by id */
|
|
93
|
-
_getEntry(eventId: string): Readonly<MemoryEntry> | undefined;
|
|
94
|
-
}
|
|
95
|
-
//#endregion
|
|
96
62
|
//#region src/events/outbox/backoff.d.ts
|
|
97
63
|
/**
|
|
98
64
|
* Exponential backoff for outbox failure policies — pure date math shared
|
|
@@ -404,6 +370,97 @@ declare class EventOutbox {
|
|
|
404
370
|
purge(olderThanMs?: number): Promise<number>;
|
|
405
371
|
}
|
|
406
372
|
//#endregion
|
|
373
|
+
//#region src/events/outbox-module.d.ts
|
|
374
|
+
interface OutboxModuleOptions {
|
|
375
|
+
/**
|
|
376
|
+
* The durable store. Supply it directly, or as a factory when it needs the booted app
|
|
377
|
+
* (a mongoose connection decorated by an earlier plugin, for instance).
|
|
378
|
+
*/
|
|
379
|
+
store: OutboxStore | ((fastify: FastifyInstance) => OutboxStore);
|
|
380
|
+
/**
|
|
381
|
+
* Where relayed events are published. Omit to use `fastify.events` — the ordinary case; pass
|
|
382
|
+
* one only when the outbox must publish somewhere other than the app's own transport.
|
|
383
|
+
*/
|
|
384
|
+
transport?: EventTransport | ((fastify: FastifyInstance) => EventTransport);
|
|
385
|
+
/** Module name, and therefore the `dependsOn` key. Default `'outbox'`. */
|
|
386
|
+
name?: string;
|
|
387
|
+
/** Relay interval in ms. Default 5000. */
|
|
388
|
+
relayEveryMs?: number;
|
|
389
|
+
/**
|
|
390
|
+
* Rows per relay tick. Left to the store's own default when omitted — a batch size is a
|
|
391
|
+
* throughput tuning decision, not something this module should invent.
|
|
392
|
+
*/
|
|
393
|
+
batchSize?: number;
|
|
394
|
+
/** Lease duration for a claimed batch, in ms. */
|
|
395
|
+
leaseMs?: number;
|
|
396
|
+
/**
|
|
397
|
+
* Consumer id recorded on the lease. Default `'arc-outbox-relay'`.
|
|
398
|
+
*
|
|
399
|
+
* Give each relaying process a DISTINCT id when several run: the lease is what stops two
|
|
400
|
+
* relays publishing the same event, and it can only do that if they are distinguishable.
|
|
401
|
+
*/
|
|
402
|
+
consumerId?: string;
|
|
403
|
+
/**
|
|
404
|
+
* Called for every non-fatal relay problem — publish failures, ownership mismatches,
|
|
405
|
+
* acknowledge failures.
|
|
406
|
+
*
|
|
407
|
+
* **Wire this.** Silence here is the failure mode that matters: the relay keeps ticking, rows
|
|
408
|
+
* keep failing, and the only symptom is revenue that never posted.
|
|
409
|
+
*/
|
|
410
|
+
onError?: OutboxRelayErrorHandler;
|
|
411
|
+
/**
|
|
412
|
+
* Retry / dead-letter policy for a failed publish — `({ attempts }) => { retryAt } |
|
|
413
|
+
* { deadLetter: true }`.
|
|
414
|
+
*
|
|
415
|
+
* Omitting it takes `EventOutbox`'s default. Exposed because a real host had one and losing it
|
|
416
|
+
* on adoption would be a silent downgrade: without a policy a permanently-failing row either
|
|
417
|
+
* retries forever or dead-letters on the first blip, and both are wrong for money. A typical
|
|
418
|
+
* shape is exponential backoff up to N attempts, then dead-letter.
|
|
419
|
+
*/
|
|
420
|
+
failurePolicy?: OutboxFailurePolicy;
|
|
421
|
+
/**
|
|
422
|
+
* Run one relay pass at boot. Default `true` — a process that restarts with a backlog should
|
|
423
|
+
* drain it immediately rather than waiting a full interval, because that backlog is precisely
|
|
424
|
+
* the events the previous process failed to publish.
|
|
425
|
+
*/
|
|
426
|
+
runOnStart?: boolean;
|
|
427
|
+
}
|
|
428
|
+
/** What the module exports at `fastify.arc.modules.<name>`. */
|
|
429
|
+
interface OutboxModuleExports {
|
|
430
|
+
/** The store, for consumers that write events transactionally. */
|
|
431
|
+
store: OutboxStore;
|
|
432
|
+
/** The relay, for a host that wants to drain on demand (tests, admin actions). */
|
|
433
|
+
relay: EventOutbox;
|
|
434
|
+
}
|
|
435
|
+
/**
|
|
436
|
+
* Compose the outbox as a module.
|
|
437
|
+
*
|
|
438
|
+
* ```ts
|
|
439
|
+
* // host
|
|
440
|
+
* const model = createOutboxModel(connection, { visibleAtField: 'nextVisibleAt' });
|
|
441
|
+
* createApp({ modules: [
|
|
442
|
+
* createOutboxModule({
|
|
443
|
+
* store: repositoryAsOutboxStore(new Repository(model), { visibleAtField: 'nextVisibleAt' }),
|
|
444
|
+
* onError: (err, ctx) => app.log.error({ err, ...ctx }, 'outbox relay'),
|
|
445
|
+
* }),
|
|
446
|
+
* …
|
|
447
|
+
* ]});
|
|
448
|
+
*
|
|
449
|
+
* // consumer module
|
|
450
|
+
* defineModule({
|
|
451
|
+
* name: 'order',
|
|
452
|
+
* dependsOn: ['outbox'],
|
|
453
|
+
* bootstrap: (f) => createOrder({
|
|
454
|
+
* outbox: getModuleExports<OutboxModuleExports>(f, 'outbox').store,
|
|
455
|
+
* }),
|
|
456
|
+
* });
|
|
457
|
+
* ```
|
|
458
|
+
*
|
|
459
|
+
* `dependsOn` is doing real work there: it both orders the bootstraps and makes a missing outbox a
|
|
460
|
+
* boot error naming the module that needed it.
|
|
461
|
+
*/
|
|
462
|
+
declare function createOutboxModule(options: OutboxModuleOptions): ArcModule<OutboxModuleExports>;
|
|
463
|
+
//#endregion
|
|
407
464
|
//#region src/events/repository-outbox-adapter.d.ts
|
|
408
465
|
interface RepositoryOutboxStoreOptions {
|
|
409
466
|
/**
|
|
@@ -421,7 +478,18 @@ interface RepositoryOutboxStoreOptions {
|
|
|
421
478
|
*/
|
|
422
479
|
readonly visibleAtField?: string;
|
|
423
480
|
}
|
|
424
|
-
|
|
481
|
+
/**
|
|
482
|
+
* What this adapter GUARANTEES, as opposed to what `OutboxStore` permits.
|
|
483
|
+
*
|
|
484
|
+
* Most of the contract is optional so a minimal or non-relational store can implement
|
|
485
|
+
* the floor and no more. This adapter implements all of it, and saying so in the type
|
|
486
|
+
* is not cosmetic: with a plain `OutboxStore` return every caller of `getDeadLettered`,
|
|
487
|
+
* `requeue` or `countByStatus` has to write `store.requeue?.(id)`, and the `?.` silently
|
|
488
|
+
* evaluates to `undefined` if the method ever disappears — an operator's replay that
|
|
489
|
+
* quietly does nothing and reports success.
|
|
490
|
+
*/
|
|
491
|
+
type RepositoryOutboxStore = OutboxStore & Required<Pick<OutboxStore, "fail" | "getDeadLettered" | "purge" | "requeue" | "countByStatus" | "oldestPendingAgeMs">>;
|
|
492
|
+
declare function repositoryAsOutboxStore(repository: RepositoryLike, options?: RepositoryOutboxStoreOptions): RepositoryOutboxStore;
|
|
425
493
|
//#endregion
|
|
426
494
|
//#region src/events/subscribe-helpers.d.ts
|
|
427
495
|
/**
|
|
@@ -582,4 +650,4 @@ interface FastifyEventBus {
|
|
|
582
650
|
};
|
|
583
651
|
}
|
|
584
652
|
//#endregion
|
|
585
|
-
export { ARC_LIFECYCLE_EVENTS, type ArcLifecycleEvent, CACHE_EVENTS, CRUD_EVENT_SUFFIXES, type CacheEvent, type CrudEventSuffix, type CustomValidator, type EventDefinitionInput, type EventDefinitionOutput, EventOutbox, type EventOutboxOptions, type EventPluginOptions, type EventRegistry, type EventRegistryOptions, type EventSchema, type ExponentialBackoffOptions, MemoryEventTransport, type MemoryEventTransportOptions, MemoryOutboxStore, type OutboxRelayErrorHandler, type OutboxRelayErrorKind, type PayloadOf, type RedisEventTransportOptions, type RedisLike, type RedisStreamLike, type RedisStreamTransportOptions, type RelayResult, type RepositoryOutboxStoreOptions, type RetryOptions, type ValidationResult, type WrapWithBoundaryOptions, type WrapWithSchemaOptions, createDeadLetterPublisher, createEventRegistry, crudEventType, defineEvent, eventPlugin, exponentialBackoff, repositoryAsOutboxStore, subscribeWithBoundary, subscribeWithSchema, withRetry, wrapWithBoundary, wrapWithSchema };
|
|
653
|
+
export { ARC_LIFECYCLE_EVENTS, type ArcLifecycleEvent, CACHE_EVENTS, CRUD_EVENT_SUFFIXES, type CacheEvent, type CrudEventSuffix, type CustomValidator, type EventDefinitionInput, type EventDefinitionOutput, EventOutbox, type EventOutboxOptions, type EventPluginOptions, type EventRegistry, type EventRegistryOptions, type EventSchema, type ExponentialBackoffOptions, MemoryEventTransport, type MemoryEventTransportOptions, MemoryOutboxStore, type OutboxModuleExports, type OutboxModuleOptions, type OutboxRelayErrorHandler, type OutboxRelayErrorKind, type PayloadOf, type RedisEventTransportOptions, type RedisLike, type RedisStreamLike, type RedisStreamTransportOptions, type RelayResult, type RepositoryOutboxStore, type RepositoryOutboxStoreOptions, type RetryOptions, type ValidationResult, type WrapWithBoundaryOptions, type WrapWithSchemaOptions, createDeadLetterPublisher, createEventRegistry, createOutboxModule, crudEventType, defineEvent, eventPlugin, exponentialBackoff, repositoryAsOutboxStore, subscribeWithBoundary, subscribeWithSchema, withRetry, wrapWithBoundary, wrapWithSchema };
|