@classytic/arc 2.28.0 → 2.31.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-B65zbz9m.mjs → BaseController-De7GqHck.mjs} +25 -24
- package/dist/{EventTransport-QmY7A258.d.mts → EventTransport-2uVV34WU.d.mts} +1 -0
- package/dist/EventTransport-9mlTgqzE.mjs +104 -0
- package/dist/{HookSystem-C1vB_Gc9.mjs → HookSystem-BcmPyq5I.mjs} +16 -35
- package/dist/{QueryCache-DOMT_iq7.d.mts → QueryCache-DFA-QrU9.d.mts} +2 -1
- package/dist/{ResourceRegistry-y9Hnypus.mjs → ResourceRegistry-qq0mn2s9.mjs} +3 -2
- package/dist/{actionPermissions-CAphcmUG.mjs → actionPermissions-BE5NwaQk.mjs} +1 -0
- package/dist/audit/index.d.mts +3 -2
- package/dist/audit/index.mjs +3 -2
- 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 +6 -5
- package/dist/auth/redis-session.d.mts +2 -1
- package/dist/auth/redis-session.mjs +1 -0
- package/dist/authorizationDecision-ChvUQGG1.mjs +266 -0
- package/dist/{buildHandler-Dtwd8fdz.mjs → buildHandler-CDrCJzmB.mjs} +34 -7
- package/dist/cache/index.d.mts +4 -3
- package/dist/cache/index.mjs +4 -3
- package/dist/{caching-_NJeDd1_.mjs → caching-BFHA6Scf.mjs} +2 -1
- package/dist/cleanup/index.d.mts +4 -3
- package/dist/cleanup/index.mjs +9 -13
- package/dist/cli/commands/describe.d.mts +2 -1
- package/dist/cli/commands/describe.mjs +3 -2
- 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-D3Dv4LAV.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 -5
- package/dist/{core-BGa8TXJR.mjs → core-BGpIxINN.mjs} +4 -49
- package/dist/{createActionRouter-C-N7z2EL.mjs → createActionRouter-CDXEdasl.mjs} +8 -7
- package/dist/{createAggregationRouter-wzwFa_dW.mjs → createAggregationRouter-KrQDzx-k.mjs} +10 -22
- package/dist/{createApp-Du5G54TM.mjs → createApp-Df89BEni.mjs} +505 -209
- package/dist/defineAggregation-DbPPS8RI.mjs +48 -0
- package/dist/{defineResource-2zvVVpxg.mjs → defineResource-Bv6EtN1x.mjs} +90 -72
- 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-D2l1mh4g.mjs} +4 -3
- package/dist/encryption/index.d.mts +2 -1
- package/dist/encryption/index.mjs +3 -2
- package/dist/{errorHandler-Bq2OeEQL.mjs → errorHandler-C5NeD6bT.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-Cj42AntO.mjs} +1 -0
- package/dist/{eventPlugin-DrQ_FesK.mjs → eventPlugin-CkffEihm.mjs} +6 -106
- package/dist/{eventPlugin-Bg3cP2_u.d.mts → eventPlugin-GF5nnBIq.d.mts} +2 -1
- package/dist/events/index.d.mts +4 -3
- package/dist/events/index.mjs +17 -340
- package/dist/events/transports/redis-stream-entry.d.mts +2 -1
- package/dist/events/transports/redis-stream-entry.mjs +1 -0
- 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 +7 -6
- package/dist/{fastify-augmentation-OzYGIiU1.d.mts → fastify-augmentation-zh0_AYHa.d.mts} +13 -4
- package/dist/{applyPermissionResult-BnyVE9eD.mjs → fields-CB5Cuzk9.mjs} +2 -107
- package/dist/{fields-D3lhgzhD.d.mts → fields-I_EeRWND.d.mts} +1 -0
- package/dist/{generate-BnsL9GBm.d.mts → generate-DCGuEbqI.d.mts} +1 -0
- package/dist/{generate-D0wZupBg.mjs → generate-EcA6ui4i.mjs} +2 -1
- package/dist/{headers-BvlQSDMB.mjs → headers-3zvE0rM3.mjs} +2 -1
- package/dist/{health-CB_m4S32.mjs → health-DjZYjM7H.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 +5 -4
- package/dist/idempotency/redis.d.mts +2 -1
- package/dist/idempotency/redis.mjs +1 -0
- package/dist/{index-CjVg-Epg.d.mts → index-Bc2ox8_U.d.mts} +6 -5
- package/dist/{index-CG4yqMDB.d.mts → index-C-481Zf2.d.mts} +74 -18
- package/dist/{index-BMnbzvtO.d.mts → index-CEJNZFtX.d.mts} +242 -24
- package/dist/index-CFThn1K9.d.mts +224 -0
- package/dist/{index-DPRH0Cme.d.mts → index-CpzxqePp.d.mts} +203 -97
- package/dist/index.d.mts +9 -8
- package/dist/index.mjs +11 -9
- package/dist/{init-tlRSt667.mjs → init-BEiNZBsJ.mjs} +19 -23
- 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 +108 -32
- package/dist/integrations/mcp/index.d.mts +9 -9
- 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 +1 -0
- package/dist/{interface-BGZCzOGL.d.mts → interface-BOk3YnTz.d.mts} +1 -0
- package/dist/{interface-Uk8ONJv7.d.mts → interface-DEWNOMza.d.mts} +1 -0
- package/dist/{interface-n5hpKg1d.mjs → interface-DYzM0q-u.mjs} +1 -0
- package/dist/{jsonBody-BlzAoGCe.mjs → jsonBody-BYbLDi5D.mjs} +2 -1
- package/dist/{keys-Cd316u_y.mjs → keys-C0_pa1tY.mjs} +1 -0
- package/dist/{loadResources-DpU8pfjV.mjs → loadResources-yawi9tAE.mjs} +2 -1
- package/dist/{loadResourcesFromEntry-kvGT6EcO.mjs → loadResourcesFromEntry-BAjYZonx.mjs} +2 -1
- package/dist/logger/index.d.mts +1 -0
- package/dist/logger/index.mjs +1 -0
- package/dist/{mcpPlugin-Cs6nRGCt.mjs → mcpPlugin-B_es0hiF.mjs} +52 -60
- package/dist/{memory-CmnzXE5Z.mjs → memory-DR6wpa9F.mjs} +2 -1
- package/dist/{metrics-fdxI5mn_.mjs → metrics-CUdw_wLB.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 -3
- package/dist/migrations/index.mjs +2 -4
- package/dist/module-0pFJA6YW.mjs +78 -0
- package/dist/{multipartBody-D0eBvkeq.mjs → multipartBody-wOgj_fnj.mjs} +1 -0
- package/dist/{openapi-BLV4OMXb.mjs → openapi-B2L4js1v.mjs} +6 -5
- package/dist/permissions/index.d.mts +5 -4
- package/dist/permissions/index.mjs +6 -4
- package/dist/{permissions-BrtiBKGs.mjs → permissions-L--QDAir.mjs} +518 -316
- package/dist/{pipe-BOLmGIxa.mjs → pipe-DRDNsI2T.mjs} +2 -1
- package/dist/pipeline/index.d.mts +2 -1
- package/dist/pipeline/index.mjs +2 -1
- package/dist/{pipeline-BGRnV-To.mjs → pipeline-UPQFatit.mjs} +7 -20
- 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-O0FZgESl.mjs} +1 -0
- package/dist/{preloadResources-CM4uUaFv.d.mts → preloadResources-CyoJMSiO.d.mts} +117 -12
- package/dist/{preloadResources-DSY5HGpS.mjs → preloadResources-Dz6BjzM6.mjs} +1 -0
- package/dist/presets/filesUpload.d.mts +6 -5
- package/dist/presets/filesUpload.mjs +9 -11
- package/dist/presets/index.d.mts +15 -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-DwFDVKP0.mjs → presets-C4-g4_4P.mjs} +27 -10
- package/dist/projection-BHw6xlOQ.mjs +18 -0
- package/dist/{pushref-registry-BbcnpNTm.d.mts → pushref-registry-Dov9l4qE.d.mts} +1 -0
- package/dist/{queryCachePlugin-DSGuSVLW.mjs → queryCachePlugin-BZr8v46E.mjs} +5 -4
- package/dist/{queryCachePlugin-br4otiqF.d.mts → queryCachePlugin-BzqxconG.d.mts} +3 -2
- package/dist/{rateLimit-DrED-C4v.mjs → rateLimit-CMPY5Qms.mjs} +7 -3
- package/dist/{rateLimitKey-BoWRH3e7.mjs → rateLimitKey-Dn5JNmkj.mjs} +1 -0
- package/dist/{realtime-DEuDu9io.mjs → realtime-C5NoxWFY.mjs} +20 -8
- 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-DTp18Ah_.mjs → registerRoute-CBN84fc7.mjs} +5 -2
- package/dist/registry/index.d.mts +3 -2
- package/dist/registry/index.mjs +3 -2
- package/dist/{registry-9km_w1aP.mjs → registry-4213U4zb.mjs} +3 -2
- package/dist/{renewingLease-3cuQH7h8.mjs → renewingLease-Dll8mGgc.mjs} +1 -0
- package/dist/{replyHelpers-67tRawQZ.mjs → replyHelpers-DIiZKGUt.mjs} +2 -1
- package/dist/repositoryFilter-f9eAfq_l.mjs +45 -0
- package/dist/{requestContext-MrlJwECf.mjs → requestContext-2TxXOJ7T.mjs} +1 -0
- package/dist/{rolldown-runtime-8H4AJuhK.mjs → rolldown-runtime-CUsjNX9R.mjs} +1 -0
- package/dist/{schedules-CjOd2MG9.mjs → schedules-DdtlICTf.mjs} +3 -2
- package/dist/{schemaConverter-DJvodiHj.mjs → schemaConverter-D--wdJM-.mjs} +70 -17
- package/dist/{schemaIR-Ca7ySNPx.mjs → schemaIR-Bm2enbnh.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-DHx5Fous.mjs} +4 -3
- package/dist/{sessionManager-BHOM3N_0.d.mts → sessionManager-qimlKV0P.d.mts} +1 -0
- package/dist/{simpleEqualityMatcher-Cg05HPsn.mjs → simpleEqualityMatcher-DjQJ7FKA.mjs} +1 -0
- package/dist/{sse-BQ1w0dfl.mjs → sse-CUTo9y0X.mjs} +5 -4
- package/dist/{sseStream-BQ7Qaynp.mjs → sseStream-B_5XcVVd.mjs} +2 -1
- package/dist/{storage-DLUZ04kK.d.mts → storage-Cx9Sz3Ou.d.mts} +1 -0
- package/dist/{store-helpers-DY6xq4sM.mjs → store-helpers-D1MP_gRG.mjs} +1 -0
- package/dist/{streaming-BQcW6_Kq.mjs → streaming-PSZwi8JV.mjs} +1 -0
- package/dist/subscribe-helpers-C0Tb2aUp.mjs +340 -0
- 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 +1 -0
- package/dist/testing/index.d.mts +167 -12
- package/dist/testing/index.mjs +522 -205
- package/dist/testing/outboxStoreContract.d.mts +1 -0
- package/dist/testing/outboxStoreContract.mjs +1 -0
- package/dist/testing/storageContract.d.mts +2 -1
- package/dist/testing/storageContract.mjs +1 -0
- package/dist/{tracing-CsGKQJGg.d.mts → tracing-Dro5eH6j.d.mts} +1 -0
- package/dist/{transactionContext-PNdoyLLm.mjs → transactionContext-dAfBVUrk.mjs} +1 -0
- package/dist/{typeGuards-C9KXUly6.mjs → typeGuards-B0ryPo3I.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-Dmw-6Qeo.d.mts → types-B7XeLEgK.d.mts} +127 -90
- 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-Cu6vVJmh.d.mts → types-CBgvkp8O.d.mts} +82 -12
- package/dist/{types-DjC-KUCz.d.mts → types-ChuShpCP.d.mts} +1 -0
- package/dist/{types-QFGc3Cjv.d.mts → types-CwcUQ610.d.mts} +2 -6
- package/dist/{types-CSyCxl2V.mjs → types-Cys0ko9Z.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-ChYJ6CdK.mjs} +1 -0
- package/dist/utils/index.d.mts +16 -10
- package/dist/utils/index.mjs +10 -9
- package/dist/{utils-taUVD4p3.mjs → utils-vL8BrDE2.mjs} +3 -2
- package/dist/{validate-Bk44F2yT.mjs → validate-_KbifpHF.mjs} +2 -1
- package/dist/{versioning-gKWyryT3.mjs → versioning-CHDTor3P.mjs} +3 -2
- 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 +15 -7
- 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/index-CZY4EGga.d.mts +0 -107
- package/dist/module-_-M7C5ES.mjs +0 -29
|
@@ -1,10 +1,12 @@
|
|
|
1
|
-
|
|
2
|
-
import {
|
|
3
|
-
import {
|
|
4
|
-
import "./
|
|
5
|
-
import
|
|
6
|
-
import { n as
|
|
7
|
-
import { n as
|
|
1
|
+
|
|
2
|
+
import { a as QueryCacheConfig } from "./QueryCache-DFA-QrU9.mjs";
|
|
3
|
+
import { P as ArcHeaders, i as RequestScope } from "./types-ChuShpCP.mjs";
|
|
4
|
+
import { a as UserBase, l as AnyRecord, r as PermissionCheck } from "./types-B7XeLEgK.mjs";
|
|
5
|
+
import "./elevation-BXCfVcNG.mjs";
|
|
6
|
+
import { n as FieldPermissionMap } from "./fields-I_EeRWND.mjs";
|
|
7
|
+
import { n as EncryptionDirective } from "./types-ByHn9rEO.mjs";
|
|
8
|
+
import { n as DomainEvent } from "./EventTransport-2uVV34WU.mjs";
|
|
9
|
+
import { Filter } from "@classytic/repo-core/filter";
|
|
8
10
|
import { KeysetPaginationResult, OffsetPaginationResult } from "@classytic/repo-core/pagination";
|
|
9
11
|
import { FastifyInstance, FastifyPluginAsync, FastifyReply, FastifyRequest, RouteHandlerMethod, RouteHandlerMethod as RouteHandlerMethod$1 } from "fastify";
|
|
10
12
|
import { DataAdapter, RepositoryLike } from "@classytic/repo-core/adapter";
|
|
@@ -188,6 +190,22 @@ interface ControllerConfigurableOptions {
|
|
|
188
190
|
*/
|
|
189
191
|
interface BaseControllerOptions extends ControllerConstructionOptions, ControllerConfigurableOptions {}
|
|
190
192
|
//#endregion
|
|
193
|
+
//#region src/scope/projection.d.ts
|
|
194
|
+
/**
|
|
195
|
+
* Lightweight projection of `RequestScope` — just the fields tenant-aware
|
|
196
|
+
* hooks and controllers usually read. Full discriminated-union access is
|
|
197
|
+
* still available through the underlying `RequestScope` for advanced
|
|
198
|
+
* branching on `scope.kind`.
|
|
199
|
+
*/
|
|
200
|
+
interface RequestScopeProjection$1 {
|
|
201
|
+
/** Tenant the caller is scoped to (member, pinned elevated admin, service key bound to an org). */
|
|
202
|
+
organizationId?: string;
|
|
203
|
+
/** Caller's user id when authenticated — undefined for public / service-only scopes. */
|
|
204
|
+
userId?: string;
|
|
205
|
+
/** Org-level roles (e.g. `['admin', 'warehouse-manager']`) — separate from global `user.roles`. */
|
|
206
|
+
orgRoles?: string[];
|
|
207
|
+
}
|
|
208
|
+
//#endregion
|
|
191
209
|
//#region src/hooks/HookSystem.d.ts
|
|
192
210
|
type HookPhase = "before" | "around" | "after";
|
|
193
211
|
type HookOperation = "create" | "update" | "delete" | "restore" | "read" | "list";
|
|
@@ -200,6 +218,18 @@ interface HookContext<T = AnyRecord> {
|
|
|
200
218
|
user?: UserBase;
|
|
201
219
|
context?: RequestContext;
|
|
202
220
|
meta?: AnyRecord;
|
|
221
|
+
/**
|
|
222
|
+
* Tenant/user projection of the validated `RequestScope` — the same shape
|
|
223
|
+
* `ResourceHookContext.scope` and `IRequestContext.scope` expose, from the
|
|
224
|
+
* same helper, so every surface reads identity identically.
|
|
225
|
+
*
|
|
226
|
+
* THIS is the identity source in a global hook. `ctx.user` comes from the
|
|
227
|
+
* auth adapter and is not scope-validated; the canonical `updatedBy` hook is
|
|
228
|
+
* `ctx.scope?.userId`, not `ctx.user?.id`. `undefined` on public/unscoped
|
|
229
|
+
* routes. Branch on `scope.kind` via `ctx.context._scope` when you need the
|
|
230
|
+
* full discriminated union.
|
|
231
|
+
*/
|
|
232
|
+
scope?: RequestScopeProjection$1;
|
|
203
233
|
}
|
|
204
234
|
type HookHandler<T = AnyRecord> = (ctx: HookContext<T>) => void | Promise<void> | T | Promise<T>;
|
|
205
235
|
/**
|
|
@@ -230,14 +260,8 @@ declare class HookSystem {
|
|
|
230
260
|
private logger;
|
|
231
261
|
private warn;
|
|
232
262
|
constructor(options?: HookSystemOptions);
|
|
233
|
-
/**
|
|
234
|
-
* Generate hook key
|
|
235
|
-
*/
|
|
236
263
|
private getKey;
|
|
237
|
-
/**
|
|
238
|
-
* Register a hook
|
|
239
|
-
* Supports both object parameter and positional arguments
|
|
240
|
-
*/
|
|
264
|
+
/** Supports both the object-parameter and positional argument forms. */
|
|
241
265
|
register<T = AnyRecord>(resourceOrOptions: string | {
|
|
242
266
|
name?: string;
|
|
243
267
|
resource: string;
|
|
@@ -247,13 +271,7 @@ declare class HookSystem {
|
|
|
247
271
|
priority?: number;
|
|
248
272
|
dependsOn?: string[];
|
|
249
273
|
}, operation?: HookOperation, phase?: HookPhase, handler?: HookHandler<T>, priority?: number): () => void;
|
|
250
|
-
/**
|
|
251
|
-
* Register before hook
|
|
252
|
-
*/
|
|
253
274
|
before<T = AnyRecord>(resource: string, operation: HookOperation, handler: HookHandler<T>, priority?: number): () => void;
|
|
254
|
-
/**
|
|
255
|
-
* Register after hook
|
|
256
|
-
*/
|
|
257
275
|
after<T = AnyRecord>(resource: string, operation: HookOperation, handler: HookHandler<T>, priority?: number): () => void;
|
|
258
276
|
/**
|
|
259
277
|
* Register around hook — wraps the core operation.
|
|
@@ -269,22 +287,13 @@ declare class HookSystem {
|
|
|
269
287
|
context?: RequestContext;
|
|
270
288
|
meta?: AnyRecord;
|
|
271
289
|
}): Promise<T | undefined>;
|
|
272
|
-
/**
|
|
273
|
-
* Execute hooks for a given context
|
|
274
|
-
*/
|
|
275
290
|
execute<T = AnyRecord>(ctx: HookContext<T>): Promise<T | undefined>;
|
|
276
|
-
/**
|
|
277
|
-
* Execute before hooks
|
|
278
|
-
*/
|
|
279
291
|
executeBefore<T = AnyRecord>(resource: string, operation: HookOperation, data: T, options?: {
|
|
280
292
|
user?: UserBase;
|
|
281
293
|
context?: RequestContext;
|
|
282
294
|
meta?: AnyRecord;
|
|
283
295
|
}): Promise<T>;
|
|
284
|
-
/**
|
|
285
|
-
* Execute after hooks
|
|
286
|
-
* Errors in after hooks are logged but don't fail the request
|
|
287
|
-
*/
|
|
296
|
+
/** Errors in after hooks are logged but never fail the request. */
|
|
288
297
|
executeAfter<T = AnyRecord>(resource: string, operation: HookOperation, result: T | T[], options?: {
|
|
289
298
|
user?: UserBase;
|
|
290
299
|
context?: RequestContext;
|
|
@@ -297,13 +306,7 @@ declare class HookSystem {
|
|
|
297
306
|
* Hooks without names or dependencies pass through in their original order.
|
|
298
307
|
*/
|
|
299
308
|
private topologicalSort;
|
|
300
|
-
/**
|
|
301
|
-
* Get all registered hooks
|
|
302
|
-
*/
|
|
303
309
|
getAll(): HookRegistration[];
|
|
304
|
-
/**
|
|
305
|
-
* Get hooks for a specific resource
|
|
306
|
-
*/
|
|
307
310
|
getForResource(resource: string): HookRegistration[];
|
|
308
311
|
/**
|
|
309
312
|
* Get hooks matching filter criteria.
|
|
@@ -347,13 +350,7 @@ declare class HookSystem {
|
|
|
347
350
|
* Check if any hooks exist for a specific resource/operation/phase combination.
|
|
348
351
|
*/
|
|
349
352
|
has(resource: string, operation: HookOperation, phase: HookPhase): boolean;
|
|
350
|
-
/**
|
|
351
|
-
* Clear all hooks
|
|
352
|
-
*/
|
|
353
353
|
clear(): void;
|
|
354
|
-
/**
|
|
355
|
-
* Clear hooks for a specific resource
|
|
356
|
-
*/
|
|
357
354
|
clearResource(resource: string): void;
|
|
358
355
|
}
|
|
359
356
|
/**
|
|
@@ -464,7 +461,7 @@ interface AccessControlConfig {
|
|
|
464
461
|
/** Minimal repository interface for access-controlled fetch operations */
|
|
465
462
|
interface AccessControlRepository {
|
|
466
463
|
getById(id: string, options?: QueryOptions): Promise<unknown>;
|
|
467
|
-
getOne?: (filter: AnyRecord, options?: QueryOptions) => Promise<unknown>;
|
|
464
|
+
getOne?: (filter: AnyRecord | Filter, options?: QueryOptions) => Promise<unknown>;
|
|
468
465
|
}
|
|
469
466
|
declare class AccessControl {
|
|
470
467
|
private readonly tenantField;
|
|
@@ -483,7 +480,7 @@ declare class AccessControl {
|
|
|
483
480
|
* Build filter for single-item operations (get/update/delete)
|
|
484
481
|
* Combines ID filter with policy/org filters for proper security enforcement
|
|
485
482
|
*/
|
|
486
|
-
buildIdFilter(id: string, req: IRequestContext): AnyRecord;
|
|
483
|
+
buildIdFilter(id: string, req: IRequestContext): AnyRecord | Filter;
|
|
487
484
|
/**
|
|
488
485
|
* Check if a post-fetch item matches the request's `_policyFilters`.
|
|
489
486
|
*
|
|
@@ -657,7 +654,7 @@ declare class BaseCrudController<TDoc = AnyRecord, TRepository extends Repositor
|
|
|
657
654
|
/**
|
|
658
655
|
* Composable access control (ID filtering, policy checks, org scope, ownership).
|
|
659
656
|
*
|
|
660
|
-
* Not `readonly`
|
|
657
|
+
* Not `readonly` — `configure()` rebuilds it when the host
|
|
661
658
|
* supplies tenant/idField/matchesFilter post-construction. Same model as
|
|
662
659
|
* `queryResolver` after `setQueryParser` shipped in 2.10.9.
|
|
663
660
|
*/
|
|
@@ -665,7 +662,7 @@ declare class BaseCrudController<TDoc = AnyRecord, TRepository extends Repositor
|
|
|
665
662
|
/**
|
|
666
663
|
* Composable body sanitization (field permissions, system fields).
|
|
667
664
|
*
|
|
668
|
-
* Not `readonly`
|
|
665
|
+
* Not `readonly` — `configure()` rebuilds it when the host
|
|
669
666
|
* supplies schemaOptions/onFieldWriteDenied post-construction.
|
|
670
667
|
*/
|
|
671
668
|
bodySanitizer: BodySanitizer;
|
|
@@ -854,9 +851,9 @@ declare class BaseCrudController<TDoc = AnyRecord, TRepository extends Repositor
|
|
|
854
851
|
*/
|
|
855
852
|
private scheduleCacheRefresh;
|
|
856
853
|
/** Cached `list()` flow with SWR semantics. Returns null when cache is disabled. */
|
|
857
|
-
protected withListCache(req: IRequestContext, options:
|
|
854
|
+
protected withListCache(req: IRequestContext, options: ControllerQueryOptions): Promise<IControllerResponse<ListResult<TDoc>> | null>;
|
|
858
855
|
/** Cached `get()` flow with SWR semantics. Returns null when cache is disabled. */
|
|
859
|
-
protected withGetCache(req: IRequestContext, id: string, options:
|
|
856
|
+
protected withGetCache(req: IRequestContext, id: string, options: ControllerQueryOptions): Promise<IControllerResponse<TDoc> | null>;
|
|
860
857
|
list(req: IRequestContext): Promise<IControllerResponse<ListResult<TDoc>>>;
|
|
861
858
|
/**
|
|
862
859
|
* Resource-dispatch verbs router. Returns `null` when the request is
|
|
@@ -897,10 +894,10 @@ declare class BaseCrudController<TDoc = AnyRecord, TRepository extends Repositor
|
|
|
897
894
|
*/
|
|
898
895
|
protected isFieldExposedForRead(field: string): boolean;
|
|
899
896
|
/** Execute list query through hooks (extracted for cache revalidation) */
|
|
900
|
-
protected executeListQuery(options:
|
|
897
|
+
protected executeListQuery(options: ControllerQueryOptions, req: IRequestContext): Promise<ListResult<TDoc>>;
|
|
901
898
|
get(req: IRequestContext): Promise<IControllerResponse<TDoc>>;
|
|
902
899
|
/** Execute get query through hooks (extracted for cache revalidation) */
|
|
903
|
-
protected executeGetQuery(id: string, options:
|
|
900
|
+
protected executeGetQuery(id: string, options: ControllerQueryOptions, req: IRequestContext): Promise<{
|
|
904
901
|
doc: TDoc | null;
|
|
905
902
|
reason: FetchDenialReason | null;
|
|
906
903
|
}>;
|
|
@@ -1242,11 +1239,27 @@ interface AuthPluginOptions {
|
|
|
1242
1239
|
strictTokenType?: boolean;
|
|
1243
1240
|
}
|
|
1244
1241
|
//#endregion
|
|
1242
|
+
//#region src/core/middlewares/rateLimit.d.ts
|
|
1243
|
+
/**
|
|
1244
|
+
* Per-route CORS override, read by `@fastify/cors` off `routeOptions.config.cors`.
|
|
1245
|
+
*
|
|
1246
|
+
* The app-wide policy cannot serve both an API and public assets: an API wants
|
|
1247
|
+
* `credentials: true` with a pinned origin list, while a public asset wants
|
|
1248
|
+
* `origin: "*"` — and `*` with credentials is forbidden by the CORS spec (arc
|
|
1249
|
+
* throws at boot on that combination). Per-route is the granularity that
|
|
1250
|
+
* resolves it. `false` disables CORS for the route entirely.
|
|
1251
|
+
*
|
|
1252
|
+
* Arc does not interpret the value; `@fastify/cors` owns the semantics. Typed
|
|
1253
|
+
* loosely on purpose so a host is never blocked by arc lagging the plugin's
|
|
1254
|
+
* option surface.
|
|
1255
|
+
*/
|
|
1256
|
+
type RouteCorsConfig = Record<string, unknown> | false;
|
|
1257
|
+
//#endregion
|
|
1245
1258
|
//#region src/types/handlers.d.ts
|
|
1246
1259
|
/**
|
|
1247
1260
|
* Minimal server accessor — exposes safe, read-only server decorators.
|
|
1248
1261
|
* Allows controller handlers to publish events, log, and audit
|
|
1249
|
-
* without switching to `
|
|
1262
|
+
* without switching to `rawHandler`.
|
|
1250
1263
|
*/
|
|
1251
1264
|
interface ServerAccessor {
|
|
1252
1265
|
/** Event bus — publish domain events from any handler */
|
|
@@ -1325,11 +1338,9 @@ interface ServerAccessor {
|
|
|
1325
1338
|
/**
|
|
1326
1339
|
* First-class projection of `request._scope` for controller handlers.
|
|
1327
1340
|
*
|
|
1328
|
-
*
|
|
1329
|
-
*
|
|
1330
|
-
*
|
|
1331
|
-
* lifts the two fields most hosts need directly onto `req` so cross-kit
|
|
1332
|
-
* controller code reads:
|
|
1341
|
+
* Lifts the two fields most hosts need directly onto `req`, so a controller
|
|
1342
|
+
* override never digs through `req.metadata._scope` and calls
|
|
1343
|
+
* `getOrgId(scope)` / `getUserId(user)` by hand:
|
|
1333
1344
|
*
|
|
1334
1345
|
* ```ts
|
|
1335
1346
|
* async create(req: IRequestContext) {
|
|
@@ -1402,7 +1413,7 @@ interface IRequestContext<TBody = unknown, TParams extends Record<string, string
|
|
|
1402
1413
|
metadata?: TMetadata;
|
|
1403
1414
|
/**
|
|
1404
1415
|
* Fastify server accessor — publish events, log, and audit
|
|
1405
|
-
* from any handler without switching to `
|
|
1416
|
+
* from any handler without switching to `rawHandler`.
|
|
1406
1417
|
*
|
|
1407
1418
|
* @example
|
|
1408
1419
|
* ```typescript
|
|
@@ -1437,7 +1448,7 @@ interface IControllerResponse<T = unknown> {
|
|
|
1437
1448
|
* Controller handler — Arc's standard pattern.
|
|
1438
1449
|
*
|
|
1439
1450
|
* Receives a request context object, returns IControllerResponse.
|
|
1440
|
-
* Use with `
|
|
1451
|
+
* Use with `handler` in routes.
|
|
1441
1452
|
*
|
|
1442
1453
|
* **Generic parameters:**
|
|
1443
1454
|
* - `TResponse` — shape of `IControllerResponse.data` (default: `unknown`)
|
|
@@ -1474,7 +1485,6 @@ interface IControllerResponse<T = unknown> {
|
|
|
1474
1485
|
* path: '/products',
|
|
1475
1486
|
* handler: createProduct,
|
|
1476
1487
|
* permissions: requireAuth(),
|
|
1477
|
-
* raw: false, // Arc wraps this into Fastify handler
|
|
1478
1488
|
* }]
|
|
1479
1489
|
* ```
|
|
1480
1490
|
*/
|
|
@@ -1483,7 +1493,7 @@ type ControllerHandler<TResponse = unknown, TBody = unknown, TParams extends Rec
|
|
|
1483
1493
|
* Fastify native handler
|
|
1484
1494
|
*
|
|
1485
1495
|
* Standard Fastify request/reply pattern.
|
|
1486
|
-
* Use with `
|
|
1496
|
+
* Use with `rawHandler` in routes.
|
|
1487
1497
|
*
|
|
1488
1498
|
* @example
|
|
1489
1499
|
* ```typescript
|
|
@@ -1496,12 +1506,31 @@ type ControllerHandler<TResponse = unknown, TBody = unknown, TParams extends Rec
|
|
|
1496
1506
|
* routes: [{
|
|
1497
1507
|
* method: 'GET',
|
|
1498
1508
|
* path: '/files/:id/download',
|
|
1499
|
-
*
|
|
1509
|
+
* rawHandler: downloadFile,
|
|
1500
1510
|
* permissions: requireAuth(),
|
|
1501
|
-
* raw: true, // Use as-is, no wrapping
|
|
1502
1511
|
* }]
|
|
1503
1512
|
* ```
|
|
1504
1513
|
*/
|
|
1514
|
+
type RawRouteHandler = {
|
|
1515
|
+
/**
|
|
1516
|
+
* Fastify-native route handler — the type behind `RouteDefinition.rawHandler`.
|
|
1517
|
+
*
|
|
1518
|
+
* Declared with METHOD syntax and read back through an indexed access, which
|
|
1519
|
+
* buys two properties a plain arrow alias can't have at once:
|
|
1520
|
+
*
|
|
1521
|
+
* - **Bivariant parameters.** `strictFunctionTypes` exempts method
|
|
1522
|
+
* declarations, so a handler annotated `FastifyRequest<{ Body: CreateBody }>`
|
|
1523
|
+
* assigns without a cast. Fastify's own `RouteHandlerMethod` is an arrow
|
|
1524
|
+
* alias, so it rejects those — hosts used to write `as any`.
|
|
1525
|
+
* - **One call signature.** TypeScript contextually types parameters only
|
|
1526
|
+
* when a single constituent is callable, so inline
|
|
1527
|
+
* `rawHandler: async (request, reply) => …` infers instead of reporting
|
|
1528
|
+
* TS7006.
|
|
1529
|
+
*
|
|
1530
|
+
* The runtime always passes the real `(request, reply)` pair.
|
|
1531
|
+
*/
|
|
1532
|
+
handler(this: FastifyInstance, request: FastifyRequest, reply: FastifyReply): unknown;
|
|
1533
|
+
}["handler"];
|
|
1505
1534
|
type FastifyHandler<RouteGeneric extends Record<string, unknown> = Record<string, unknown>> = (request: FastifyRequest<RouteGeneric>, reply: FastifyReply) => Promise<unknown> | unknown;
|
|
1506
1535
|
/**
|
|
1507
1536
|
* Union type for route handlers
|
|
@@ -1530,11 +1559,11 @@ interface IController<TDoc = unknown> {
|
|
|
1530
1559
|
* Flexible controller interface — accepts controllers with any handler style,
|
|
1531
1560
|
* including class instances with extra methods / private fields.
|
|
1532
1561
|
*
|
|
1533
|
-
*
|
|
1534
|
-
*
|
|
1535
|
-
*
|
|
1536
|
-
*
|
|
1537
|
-
*
|
|
1562
|
+
* Deliberately NO index signature: one would make real class instances fail
|
|
1563
|
+
* structural assignment (`new ScrapController()` would need an
|
|
1564
|
+
* `as unknown as ControllerLike` cast, because class instances don't have
|
|
1565
|
+
* one). Arc only invokes the CRUD methods at runtime, so the rest of the
|
|
1566
|
+
* shape is the caller's concern.
|
|
1538
1567
|
*
|
|
1539
1568
|
* The five CRUD slots stay optional so partial controllers (e.g. read-only)
|
|
1540
1569
|
* assign too. Additional domain methods on the controller are allowed by
|
|
@@ -1580,7 +1609,7 @@ interface RouteMcpConfig {
|
|
|
1580
1609
|
*
|
|
1581
1610
|
* - `handler: 'string'` → controller method → full Arc pipeline + MCP tool
|
|
1582
1611
|
* - `handler: function` → inline handler → full Arc pipeline + MCP tool
|
|
1583
|
-
* - `
|
|
1612
|
+
* - `rawHandler` → raw Fastify handler → no pipeline, no MCP by default
|
|
1584
1613
|
*/
|
|
1585
1614
|
interface RouteDefinition {
|
|
1586
1615
|
readonly method: RouteMethod;
|
|
@@ -1589,14 +1618,14 @@ interface RouteDefinition {
|
|
|
1589
1618
|
/**
|
|
1590
1619
|
* Route handler.
|
|
1591
1620
|
* - String: controller method name (Arc pipeline) — runtime lookup, no TS coverage on typos.
|
|
1592
|
-
* - Function
|
|
1593
|
-
* -
|
|
1621
|
+
* - Function: receives IRequestContext, returns IControllerResponse (Arc pipeline)
|
|
1622
|
+
* - For a raw Fastify handler `(request, reply)`, use {@link rawHandler}
|
|
1594
1623
|
*
|
|
1595
1624
|
* Prefer `controllerMethod` (2.16) over the string form when you want the
|
|
1596
1625
|
* compiler to catch typos. `handler` stays optional when `controllerMethod`
|
|
1597
1626
|
* is set — exactly one of the two must be declared.
|
|
1598
1627
|
*/
|
|
1599
|
-
readonly handler?: string | ControllerHandler
|
|
1628
|
+
readonly handler?: string | ControllerHandler;
|
|
1600
1629
|
/**
|
|
1601
1630
|
* Typed function-reference handler (2.16). Receives the live controller
|
|
1602
1631
|
* instance and returns the method to invoke — TypeScript catches typos
|
|
@@ -1608,11 +1637,15 @@ interface RouteDefinition {
|
|
|
1608
1637
|
* handlers go through), so there's no per-request lookup overhead.
|
|
1609
1638
|
* The returned method is bound to the controller before dispatch.
|
|
1610
1639
|
*
|
|
1640
|
+
* PIPELINE-ONLY: the referenced method is invoked as a `ControllerHandler`
|
|
1641
|
+
* (one `IRequestContext`), because the router derives "is this raw?" from
|
|
1642
|
+
* {@link rawHandler} alone and this field is mutually exclusive with it.
|
|
1643
|
+
* There is no flag that could make a `controllerMethod` route raw. For a
|
|
1644
|
+
* Fastify-native controller method, put its NAME in `rawHandler` — the
|
|
1645
|
+
* lookup is the same, and the field states the calling convention.
|
|
1646
|
+
*
|
|
1611
1647
|
* Mutually exclusive with `handler` — passing both throws at boot
|
|
1612
|
-
* with a clear "pick one" message.
|
|
1613
|
-
* returned function accepts mirrors the `handler` rules: a plain
|
|
1614
|
-
* `ControllerHandler` for pipeline routes, a raw Fastify handler when
|
|
1615
|
-
* the route is `raw: true`.
|
|
1648
|
+
* with a clear "pick one" message.
|
|
1616
1649
|
*
|
|
1617
1650
|
* Typed loosely (`(controller: unknown) => …`) at the type-system
|
|
1618
1651
|
* boundary because `RouteDefinition` is not generic over the
|
|
@@ -1636,14 +1669,33 @@ interface RouteDefinition {
|
|
|
1636
1669
|
* });
|
|
1637
1670
|
* ```
|
|
1638
1671
|
*/
|
|
1639
|
-
readonly controllerMethod?: (controller: unknown) => ControllerHandler
|
|
1672
|
+
readonly controllerMethod?: (controller: unknown) => ControllerHandler;
|
|
1640
1673
|
/** Permission check — REQUIRED */
|
|
1641
1674
|
readonly permissions: PermissionCheck;
|
|
1642
1675
|
/**
|
|
1643
|
-
*
|
|
1644
|
-
*
|
|
1645
|
-
|
|
1646
|
-
|
|
1676
|
+
* Fastify-native handler — receives `(request, reply)` and owns the response.
|
|
1677
|
+
* Bypasses arc's pipeline entirely: no `IControllerResponse` shaping, no
|
|
1678
|
+
* field-write sanitization.
|
|
1679
|
+
*
|
|
1680
|
+
* Mutually exclusive with {@link handler}. The FIELD carries the intent, so
|
|
1681
|
+
* there is no flag to forget: the previous `raw: boolean` let a Fastify-shaped
|
|
1682
|
+
* function sit in `handler` without it (silently run through the pipeline) or
|
|
1683
|
+
* a pipeline handler sit there with it (silently unwrapped), and neither
|
|
1684
|
+
* mismatch was a type error.
|
|
1685
|
+
*
|
|
1686
|
+
* Splitting the two also restores inference. One field carrying both shapes
|
|
1687
|
+
* meant a union of several function types, which has no single contextual
|
|
1688
|
+
* signature — so an inline `handler: async (req) => …` reported TS7006 and
|
|
1689
|
+
* every author annotated `req` by hand. Fastify types its own
|
|
1690
|
+
* `RouteOptions.handler` as ONE function type for exactly this reason; see
|
|
1691
|
+
* {@link RawRouteHandler} for why arc declares its own rather than reusing
|
|
1692
|
+
* `RouteHandlerMethod` directly.
|
|
1693
|
+
*
|
|
1694
|
+
* Accepts a controller-method NAME as well, same as {@link handler} — the
|
|
1695
|
+
* lookup is identical and only the calling convention differs, so a
|
|
1696
|
+
* Fastify-native controller method stays reachable by name.
|
|
1697
|
+
*/
|
|
1698
|
+
readonly rawHandler?: string | RawRouteHandler;
|
|
1647
1699
|
/** Logical operation name (pipeline keys, MCP tool naming). */
|
|
1648
1700
|
readonly operation?: string;
|
|
1649
1701
|
/** OpenAPI summary */
|
|
@@ -1675,7 +1727,8 @@ interface RouteDefinition {
|
|
|
1675
1727
|
*/
|
|
1676
1728
|
readonly tenantScope?: boolean;
|
|
1677
1729
|
/**
|
|
1678
|
-
* SSE streaming mode.
|
|
1730
|
+
* SSE streaming mode. Requires {@link rawHandler} — the handler is invoked
|
|
1731
|
+
* with `(request, reply)` and owns the response. Two shapes work:
|
|
1679
1732
|
*
|
|
1680
1733
|
* - Write to `reply.raw` directly (NDJSON, custom SSE — historical contract).
|
|
1681
1734
|
* - Return a Web `ReadableStream` (Vercel AI SDK's `result.toUIMessageStream()`,
|
|
@@ -1706,7 +1759,7 @@ interface RouteDefinition {
|
|
|
1706
1759
|
* `onFieldWriteDenied: 'reject' | 'strip'` setting governs how denials
|
|
1707
1760
|
* are surfaced.
|
|
1708
1761
|
*
|
|
1709
|
-
* `
|
|
1762
|
+
* `rawHandler` routes ALWAYS bypass this — they've opted out of the
|
|
1710
1763
|
* pipeline entirely.
|
|
1711
1764
|
*/
|
|
1712
1765
|
readonly fieldWrite?: boolean;
|
|
@@ -1729,6 +1782,22 @@ interface RouteDefinition {
|
|
|
1729
1782
|
* it to a dedicated `routes:` entry, or rely on the resource-level limit.
|
|
1730
1783
|
*/
|
|
1731
1784
|
readonly rateLimit?: RateLimitConfig | false;
|
|
1785
|
+
/**
|
|
1786
|
+
* Per-route CORS override, forwarded to `@fastify/cors` as
|
|
1787
|
+
* `routeOptions.config.cors`. `undefined` inherits the app policy; `false`
|
|
1788
|
+
* disables CORS for this route; an object replaces the app policy for it.
|
|
1789
|
+
*
|
|
1790
|
+
* Needed because one app-wide policy cannot serve both an API and a public
|
|
1791
|
+
* asset: an API wants `credentials: true` with a pinned origin list, a public
|
|
1792
|
+
* asset wants `origin: "*"`, and `*` + credentials is forbidden by the spec
|
|
1793
|
+
* (arc throws at boot on that pair).
|
|
1794
|
+
*
|
|
1795
|
+
* ```ts
|
|
1796
|
+
* { method: "GET", path: "/manifest.json", permissions: allowPublic(),
|
|
1797
|
+
* cors: { origin: "*", credentials: false }, handler }
|
|
1798
|
+
* ```
|
|
1799
|
+
*/
|
|
1800
|
+
readonly cors?: RouteCorsConfig;
|
|
1732
1801
|
/**
|
|
1733
1802
|
* Fastify route schema. Each slot (`body`, `querystring`, `params`,
|
|
1734
1803
|
* `headers`, `response[status]`) accepts a plain JSON Schema object
|
|
@@ -2318,12 +2387,9 @@ interface ResourceExtensions {
|
|
|
2318
2387
|
* Hook context passed to resource-level hook handlers. Mirrors
|
|
2319
2388
|
* HookSystem's HookContext but with a simpler API for inline use.
|
|
2320
2389
|
*
|
|
2321
|
-
*
|
|
2322
|
-
*
|
|
2323
|
-
*
|
|
2324
|
-
* user info — they had to bypass the documented API and push directly
|
|
2325
|
-
* into `resource._pendingHooks` to get the raw internal shape. Now the
|
|
2326
|
-
* documented DX is complete:
|
|
2390
|
+
* `context` and a first-class `scope` projection are forwarded from the
|
|
2391
|
+
* internal `HookContext`, so an inline handler reaches the caller's tenant
|
|
2392
|
+
* and user through the documented API:
|
|
2327
2393
|
*
|
|
2328
2394
|
* ```ts
|
|
2329
2395
|
* hooks: {
|
|
@@ -2358,7 +2424,7 @@ interface ResourceHookContext {
|
|
|
2358
2424
|
* First-class projection of request scope — `{ organizationId?, userId?, orgRoles? }`.
|
|
2359
2425
|
* Populated for every scoped request so multi-tenant hooks don't have to
|
|
2360
2426
|
* drill into `context._scope.organizationId` themselves. Matches the
|
|
2361
|
-
* identically-named field on `IRequestContext`
|
|
2427
|
+
* identically-named field on `IRequestContext` so the same
|
|
2362
2428
|
* read pattern works in controllers and hooks.
|
|
2363
2429
|
*/
|
|
2364
2430
|
scope?: {
|
|
@@ -2670,6 +2736,16 @@ interface PresetResult {
|
|
|
2670
2736
|
/** Preset routes — merged into the resource's `routes` array. */
|
|
2671
2737
|
routes?: RouteDefinition[] | ((permissions: ResourcePermissions) => RouteDefinition[]);
|
|
2672
2738
|
middlewares?: MiddlewareConfig;
|
|
2739
|
+
/**
|
|
2740
|
+
* Permission gates the preset contributes as SECURE DEFAULTS. Merged per-op
|
|
2741
|
+
* into the resource's `permissions`, but ONLY for operations the host has not
|
|
2742
|
+
* explicitly gated — the host's own permission always wins. Lets a preset make
|
|
2743
|
+
* authorization part of the permission/introspection model (required auth at
|
|
2744
|
+
* `onRequest`, MCP parity) instead of relying on middleware alone. Example:
|
|
2745
|
+
* `ownedByUser` injects `requireAuth()` on update/delete so an ownership
|
|
2746
|
+
* resource is never public-by-omission.
|
|
2747
|
+
*/
|
|
2748
|
+
permissions?: Partial<ResourcePermissions>;
|
|
2673
2749
|
schemaOptions?: RouteSchemaOptions;
|
|
2674
2750
|
controllerOptions?: Record<string, unknown>;
|
|
2675
2751
|
hooks?: PresetHook[];
|
|
@@ -2828,7 +2904,7 @@ interface ResourceConfig<TDoc = AnyRecord> {
|
|
|
2828
2904
|
* object, primitive) is supported via auto-envelope (2.17+) but the
|
|
2829
2905
|
* explicit envelope is the canonical contract — declared `fields:`
|
|
2830
2906
|
* permissions, custom `status`, `meta`, and `headers` only work when
|
|
2831
|
-
* you return the envelope.
|
|
2907
|
+
* you return the envelope. Declare a route's function via `rawHandler` to opt out of
|
|
2832
2908
|
* arc's pipeline entirely (custom streaming, SSE, manual `reply.send()`).
|
|
2833
2909
|
*
|
|
2834
2910
|
* **Path collisions with auto-CRUD are detected at `defineResource()` time.**
|
|
@@ -2843,7 +2919,7 @@ interface ResourceConfig<TDoc = AnyRecord> {
|
|
|
2843
2919
|
* ```typescript
|
|
2844
2920
|
* routes: [
|
|
2845
2921
|
* { method: 'GET', path: '/stats', handler: 'getStats', permissions: auth() },
|
|
2846
|
-
* { method: 'POST', path: '/webhook',
|
|
2922
|
+
* { method: 'POST', path: '/webhook', rawHandler: webhookFn, permissions: auth() },
|
|
2847
2923
|
* ]
|
|
2848
2924
|
* ```
|
|
2849
2925
|
*
|
|
@@ -3035,6 +3111,18 @@ interface ResourceConfig<TDoc = AnyRecord> {
|
|
|
3035
3111
|
skipValidation?: boolean;
|
|
3036
3112
|
/** Don't register in introspection */
|
|
3037
3113
|
skipRegistry?: boolean;
|
|
3114
|
+
/**
|
|
3115
|
+
* Fail-closed permission invariant. When `true`, any enabled CRUD WRITE
|
|
3116
|
+
* (create/update/delete) that mounts WITHOUT a permission gate is a FATAL
|
|
3117
|
+
* define-time error — `defineResource()` throws instead of shipping an
|
|
3118
|
+
* unauthenticated write with only a warning. Reads that are ungated stay a
|
|
3119
|
+
* non-fatal `info` hint (public catalogs are legitimate).
|
|
3120
|
+
*
|
|
3121
|
+
* Off by default (public-by-omission still only warns) so existing hosts are
|
|
3122
|
+
* not broken. Opt in per-resource here, or app-wide via the
|
|
3123
|
+
* `ARC_STRICT_PERMISSIONS=true` env — the recommended production posture.
|
|
3124
|
+
*/
|
|
3125
|
+
strictPermissions?: boolean;
|
|
3038
3126
|
/**
|
|
3039
3127
|
* Default `limit` value when the request omits `?limit=`. Default 20.
|
|
3040
3128
|
* Surfaced at the resource level (rather than only via a custom
|
|
@@ -3238,16 +3326,19 @@ interface ResourceConfig<TDoc = AnyRecord> {
|
|
|
3238
3326
|
* "this resource will never work" failures.
|
|
3239
3327
|
*/
|
|
3240
3328
|
/**
|
|
3241
|
-
* One
|
|
3329
|
+
* One diagnostic produced during resource validation.
|
|
3242
3330
|
*
|
|
3243
|
-
* - `severity`:
|
|
3244
|
-
* the
|
|
3331
|
+
* - `severity`: `error` is FATAL — `defineResource()` throws at define-time,
|
|
3332
|
+
* the resource never boots (used by strict security invariants like ungated
|
|
3333
|
+
* CRUD under `strictPermissions`). `warn` flags a misconfiguration the host
|
|
3334
|
+
* should clean up; `info` is a deprecation hint. `warn`/`info` are non-fatal
|
|
3335
|
+
* and flushed through the host logger on first mount.
|
|
3245
3336
|
* - `code`: stable identifier for the diagnostic — hosts and tests can match
|
|
3246
3337
|
* on this without parsing the human-readable message.
|
|
3247
3338
|
* - `message`: pre-formatted line, resource name already interpolated.
|
|
3248
3339
|
*/
|
|
3249
3340
|
interface ResourceDiagnostic {
|
|
3250
|
-
severity: "warn" | "info";
|
|
3341
|
+
severity: "error" | "warn" | "info";
|
|
3251
3342
|
code: string;
|
|
3252
3343
|
message: string;
|
|
3253
3344
|
}
|
|
@@ -3787,6 +3878,7 @@ interface ArcInternalMetadata extends RequestContext {
|
|
|
3787
3878
|
_ownershipCheck?: {
|
|
3788
3879
|
field: string;
|
|
3789
3880
|
userId: string;
|
|
3881
|
+
missingOwner?: "deny" | "allow";
|
|
3790
3882
|
};
|
|
3791
3883
|
/** Arc instance references (hooks, field permissions, etc.) */
|
|
3792
3884
|
arc?: {
|
|
@@ -3819,7 +3911,13 @@ interface ControllerQueryOptions {
|
|
|
3819
3911
|
*/
|
|
3820
3912
|
lookups?: LookupOption[];
|
|
3821
3913
|
select?: string | string[] | Record<string, 0 | 1>;
|
|
3822
|
-
|
|
3914
|
+
/**
|
|
3915
|
+
* Resolved row filter. Flat equality filters are a plain record; filters
|
|
3916
|
+
* carrying `$`-operators (`$or`/`$and`/`$gte`) are normalized to the portable
|
|
3917
|
+
* repo-core `Filter` IR at the repository boundary (see `toRepositoryFilter`)
|
|
3918
|
+
* so every kit compiles them. Repositories accept both (`FilterInput`).
|
|
3919
|
+
*/
|
|
3920
|
+
filters?: Record<string, unknown> | Filter;
|
|
3823
3921
|
search?: string;
|
|
3824
3922
|
lean?: boolean;
|
|
3825
3923
|
after?: string;
|
|
@@ -3875,6 +3973,14 @@ interface PopulateOption {
|
|
|
3875
3973
|
* parsers (MongoKit, PrismaKit) add fields without breaking Arc's types.
|
|
3876
3974
|
*/
|
|
3877
3975
|
interface ParsedQuery {
|
|
3976
|
+
/**
|
|
3977
|
+
* Row filter — a plain record in the parser's operator dialect. This is the
|
|
3978
|
+
* PARSER contract: a `QueryParser` (arc built-in, MongoKit, PrismaKit) emits a
|
|
3979
|
+
* record, never repo-core `Filter` IR. IR appears only AFTER security
|
|
3980
|
+
* composition, on `ControllerQueryOptions.filters` (via `toRepositoryFilter`).
|
|
3981
|
+
* Keeping this record-only means `QueryResolver` never has to guess whether a
|
|
3982
|
+
* parsed filter is an IR node it must not blindly spread tenant/policy onto.
|
|
3983
|
+
*/
|
|
3878
3984
|
filters?: Record<string, unknown>;
|
|
3879
3985
|
limit?: number;
|
|
3880
3986
|
sort?: string | Record<string, 1 | -1>;
|
|
@@ -4189,4 +4295,4 @@ interface ValidateOptions {
|
|
|
4189
4295
|
strict?: boolean;
|
|
4190
4296
|
}
|
|
4191
4297
|
//#endregion
|
|
4192
|
-
export { OpenApiSchemas as $,
|
|
4298
|
+
export { OpenApiSchemas as $, TreeMixin as $t, RequestIdOptions as A, BaseControllerOptions as An, RouteDefinition as At, defineResource as B, RawRouteHandler as Bt, RequestContext as C, createHookSystem as Cn, PipelineStep as Ct, HealthCheck as D, ArcGetResult as Dn, ActionEntry as Dt, GracefulShutdownOptions as E, ArcDeleteResult as En, ActionDefinition as Et, FastifyWithDecorators as F, BodySanitizerConfig as Fn, ControllerLike as Ft, OnTenantDeleteConfig as G, Authenticator as Gt, CrudController as H, ServerAccessor as Ht, MiddlewareHandler as I, FastifyHandler as It, PresetHook as J, TokenPair as Jt, ResolvedTenantPurge as K, AuthenticatorContext as Kt, RequestWithExtras as L, IController as Lt, EventsDecorator as M, ControllerConstructionOptions as Mn, RouteMethod as Mt, FastifyRequestExtras as N, ListResult as Nn, RateLimitConfig as Nt, HealthOptions as O, ArcListResult as On, ActionHandlerFn as Ot, FastifyWithAuth as P, BodySanitizer as Pn, ControllerHandler as Pt, MiddlewareConfig as Q, TreeExt as Qt, RegisterOptions as R, IControllerResponse as Rt, QueryParserInterface as S, beforeUpdate as Sn, PipelineContext as St, CrudRouterOptions as T, ArcCreateResult as Tn, ResourceCacheConfig as Tt, ResourceConfig as U, AuthHelpers as Ut, ResourceDefinition as V, RouteHandler as Vt, ResourcePermissions as W, AuthPluginOptions as Wt, CrudRouteKey as X, SoftDeleteExt as Xt, PresetResult as Y, BaseController as Yt, CrudSchemas as Z, SoftDeleteMixin as Zt, ControllerQueryOptions as _, afterCreate as _n, Guard as _t, InferAdapterDoc as a, QueryResolver as an, ResourceExtensions as at, ParsedQuery as b, beforeCreate as bn, OperationFilter as bt, TypedController as c, AccessControlConfig as cn, AggMeasureShorthand as ct, PaginationResult as d, HookHandler as dn, AggregationDateRangeRequirement as dt, SlugExt as en, RouteSchemaOptions as et, IntrospectionData as f, HookOperation as fn, AggregationIndexHint as ft, ArcInternalMetadata as g, HookSystemOptions as gn, AggregationsMap as gt, ResourceMetadata as h, HookSystem as hn, AggregationRateLimit as ht, ValidationResult as i, BaseCrudController as in, ResourceHooks as it, ArcDecorator as j, ControllerConfigurableOptions as jn, RouteMcpConfig as jt, IntrospectionPluginOptions as k, ArcUpdateResult as kn, ActionsMap as kt, TypedRepository as l, DefineHookOptions as ln, AggregationCacheConfig as lt, RegistryStats as m, HookRegistration as mn, AggregationMaterializedResult as mt, ConfigError as n, BulkExt as nn, FieldRule$1 as nt, InferDocType as o, QueryResolverConfig as on, EventDefinition as ot, RegistryEntry as p, HookPhase as pn, AggregationMaterializedContext as pt, PresetFunction as q, JwtContext as qt, ValidateOptions as r, BulkMixin as rn, ResourceHookContext as rt, InferResourceDoc as s, AccessControl as sn, AggMeasureInput as st, RouteHandlerMethod$1 as t, SlugMixin as tn, ArcFieldRule as tt, TypedResourceConfig as u, HookContext as un, AggregationConfig as ut, LookupOption as v, afterDelete as vn, Interceptor as vt, ServiceContext as w, defineHook as wn, Transform as wt, PopulateOption as x, beforeDelete as xn, PipelineConfig as xt, OwnershipCheck as y, afterUpdate as yn, NextFunction as yt, ResourceRegistry as z, IRequestContext as zt };
|