@classytic/arc 2.16.0 → 2.17.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/README.md +22 -2
- package/dist/{BaseController-DlCCTIxJ.mjs → BaseController-Dmeh4Y_y.mjs} +5 -47
- package/dist/{QueryCache-SvmT_9ti.d.mts → QueryCache-8YmAQ-zW.d.mts} +1 -1
- package/dist/{ResourceRegistry-f48hFk3m.mjs → ResourceRegistry-CzSzVK1m.mjs} +3 -0
- package/dist/audit/index.d.mts +1 -1
- package/dist/audit/index.mjs +2 -1
- package/dist/auth/index.d.mts +4 -4
- package/dist/auth/index.mjs +2 -2
- package/dist/auth/redis-session.d.mts +1 -1
- package/dist/auth/redis-session.mjs +28 -2
- package/dist/{buildHandler-BZX6zzDM.mjs → buildHandler-C_SkBNF3.mjs} +6 -4
- package/dist/cache/index.d.mts +3 -3
- package/dist/cli/commands/describe.d.mts +1 -1
- package/dist/cli/commands/describe.mjs +1 -1
- package/dist/cli/commands/docs.mjs +2 -2
- package/dist/cli/commands/generate.d.mts +1 -1
- package/dist/cli/commands/generate.mjs +1 -1
- package/dist/cli/commands/init.d.mts +1 -1
- package/dist/cli/commands/init.mjs +1 -1
- package/dist/cli/commands/introspect.mjs +1 -1
- package/dist/cli/index.d.mts +2 -2
- package/dist/cli/index.mjs +2 -2
- package/dist/core/index.d.mts +2 -2
- package/dist/core/index.mjs +3 -3
- package/dist/{core-DBJ_j6rX.mjs → core-DYJvu7aI.mjs} +174 -31
- package/dist/{createActionRouter-DUpN3Dd1.mjs → createActionRouter-BHUMb1-y.mjs} +6 -3
- package/dist/{createAggregationRouter-Dq-TUCuY.mjs → createAggregationRouter-BYVvhfc5.mjs} +3 -3
- package/dist/{createApp-DNccuhyI.mjs → createApp-CzCEEsEm.mjs} +9 -9
- package/dist/{defineEvent-DRwY0fYm.mjs → defineEvent-NPKUJRt8.mjs} +1 -1
- package/dist/docs/index.d.mts +2 -2
- package/dist/docs/index.mjs +2 -2
- package/dist/{elevation-Dci0AYLT.mjs → elevation-6mfTLBC-.mjs} +1 -1
- package/dist/{errorHandler-DpoXQHZ9.mjs → errorHandler-wQejMBT3.mjs} +45 -4
- package/dist/{eventPlugin-C2cGqtRO.mjs → eventPlugin-CnJCUyM0.mjs} +1 -1
- package/dist/{eventPlugin-CtHC_av1.d.mts → eventPlugin-DjEx_J3j.d.mts} +1 -1
- package/dist/events/index.d.mts +3 -3
- package/dist/events/index.mjs +5 -5
- package/dist/events/transports/redis-stream-entry.d.mts +1 -1
- package/dist/events/transports/redis-stream-entry.mjs +34 -1
- package/dist/events/transports/redis.d.mts +1 -1
- package/dist/events/transports/redis.mjs +17 -1
- package/dist/factory/index.d.mts +2 -2
- package/dist/factory/index.mjs +4 -3
- package/dist/fastify-augmentation-7LIR0nDv.d.mts +42 -0
- package/dist/{fields-Anj0xdih.d.mts → fields-DqZLp5d6.d.mts} +1 -1
- package/dist/hooks/index.d.mts +1 -1
- package/dist/idempotency/index.d.mts +2 -2
- package/dist/idempotency/index.mjs +21 -5
- package/dist/idempotency/redis.d.mts +1 -1
- package/dist/{index-3oIimXQn.d.mts → index-3L-adFJO.d.mts} +3 -3
- package/dist/{index-CkW0flkU.d.mts → index-C1JSBan-.d.mts} +256 -8
- package/dist/{index-B-ulKx5P.d.mts → index-CruZir1o.d.mts} +36 -16
- package/dist/index.d.mts +5 -5
- package/dist/index.mjs +5 -5
- package/dist/integrations/event-gateway.d.mts +2 -2
- package/dist/integrations/event-gateway.mjs +1 -1
- package/dist/integrations/index.d.mts +2 -2
- package/dist/integrations/jobs.d.mts +36 -1
- package/dist/integrations/jobs.mjs +82 -9
- package/dist/integrations/mcp/index.d.mts +67 -5
- package/dist/integrations/mcp/index.mjs +93 -2
- package/dist/integrations/mcp/testing.d.mts +1 -1
- package/dist/integrations/mcp/testing.mjs +1 -1
- package/dist/integrations/websocket-redis.d.mts +1 -1
- package/dist/integrations/websocket.d.mts +1 -1
- package/dist/integrations/websocket.mjs +2 -1
- package/dist/{loadResourcesFromEntry-BLMEI2Xa.mjs → loadResourcesFromEntry-DCjYMgfC.mjs} +1 -1
- package/dist/{mcpPlugin-7vGV51ED.mjs → mcpPlugin-hZE00H74.mjs} +114 -11
- package/dist/middleware/index.d.mts +1 -1
- package/dist/middleware/index.mjs +1 -1
- package/dist/{multipartBody-BOvVSVCD.mjs → multipartBody-VjVWmZl8.mjs} +2 -1
- package/dist/{openapi-34T9yNwd.mjs → openapi-DKDs9NfD.mjs} +37 -10
- package/dist/permissions/index.d.mts +2 -2
- package/dist/permissions/index.mjs +2 -2
- package/dist/{permissions-CTxMrreC.mjs → permissions-B1Qy55fg.mjs} +169 -169
- package/dist/pipeline/index.d.mts +1 -1
- package/dist/plugins/index.d.mts +5 -59
- package/dist/plugins/index.mjs +7 -7
- package/dist/plugins/tracing-entry.d.mts +1 -1
- package/dist/plugins/tracing-entry.mjs +1 -1
- package/dist/preloadResources-BOXzPhKU.mjs +45 -0
- package/dist/presets/filesUpload.d.mts +4 -4
- package/dist/presets/filesUpload.mjs +2 -2
- package/dist/presets/index.d.mts +1 -1
- package/dist/presets/index.mjs +1 -1
- package/dist/presets/multiTenant.d.mts +1 -1
- package/dist/presets/search.d.mts +2 -2
- package/dist/presets/search.mjs +1 -1
- package/dist/{presets-C9BE6WaZ.mjs → presets-BYbPobbF.mjs} +5 -3
- package/dist/{queryCachePlugin-Biqzfbi5.d.mts → queryCachePlugin-Do4P_WBC.d.mts} +2 -2
- package/dist/{redis-Cyzrz6SX.d.mts → redis-cpqVb85w.d.mts} +1 -1
- package/dist/{redis-stream-DT-YjzrB.d.mts → redis-stream-BuiAS3aE.d.mts} +1 -1
- package/dist/registry/index.d.mts +30 -8
- package/dist/registry/index.mjs +2 -2
- package/dist/{registry-BBE23CDj.mjs → registry-BrlHkrRS.mjs} +14 -8
- package/dist/{routerShared-CZV5aabX.mjs → routerShared-nCyJGOKX.mjs} +164 -17
- package/dist/scope/index.d.mts +2 -2
- package/dist/scope/index.mjs +2 -2
- package/dist/testing/index.d.mts +2 -25
- package/dist/testing/index.mjs +2 -45
- package/dist/testing/storageContract.d.mts +1 -1
- package/dist/types/index.d.mts +4 -4
- package/dist/types/storage.d.mts +1 -1
- package/dist/{types-D-fYtKjb.d.mts → types-BRmS8TNa.d.mts} +34 -11
- package/dist/{types-BsJMEQ4D.d.mts → types-CoMjNmkF.d.mts} +12 -1
- package/dist/utils/index.d.mts +83 -5
- package/dist/utils/index.mjs +2 -2
- package/dist/{utils-DC5ycPfr.mjs → utils-CvD3glpM.mjs} +168 -3
- package/dist/{validate-By96rH0r.mjs → validate-DFIP2VrM.mjs} +65 -3
- package/dist/{versioning-ZwX9tmbS.d.mts → versioning-5H4930tj.d.mts} +1 -1
- package/package.json +3 -3
- package/skills/arc/SKILL.md +43 -1
- package/skills/arc/references/api-reference.md +2 -0
- package/skills/arc/references/mcp.md +30 -1
- /package/dist/{EventTransport-C-2oAHtw.d.mts → EventTransport-B1GFiYUy.d.mts} +0 -0
- /package/dist/{EventTransport-Hxvv5QQz.mjs → EventTransport-ClEDfiMS.mjs} +0 -0
- /package/dist/{caching-TeHE8G-v.mjs → caching-8MfrrUYU.mjs} +0 -0
- /package/dist/{elevation-0YBpa663.d.mts → elevation-DvwtjDwu.d.mts} +0 -0
- /package/dist/{errorHandler-mHuyWzZE.d.mts → errorHandler-DuqTqeqn.d.mts} +0 -0
- /package/dist/{errors-C1lX_jlm.d.mts → errors-4ZloYFvm.d.mts} +0 -0
- /package/dist/{externalPaths-DFg-2KTp.d.mts → externalPaths-ClykOQqa.d.mts} +0 -0
- /package/dist/{generate-CYac-OLv.mjs → generate-CcM24hoq.mjs} +0 -0
- /package/dist/{generate-BWFwgcCM.d.mts → generate-cRnsafMS.d.mts} +0 -0
- /package/dist/{init-Dv71MsJr.d.mts → init-D-vrTZ1G.d.mts} +0 -0
- /package/dist/{init-HDvoO9L5.mjs → init-DYzfniuX.mjs} +0 -0
- /package/dist/{interface-CH0OQudo.d.mts → interface-BVkZsvEe.d.mts} +0 -0
- /package/dist/{interface-NwJ_qPlY.d.mts → interface-DoE6UoC2.d.mts} +0 -0
- /package/dist/{loadResources-ChQEj8ih.mjs → loadResources-fol6JuvW.mjs} +0 -0
- /package/dist/{metrics-TuOmguhi.mjs → metrics-B-PU4-Yu.mjs} +0 -0
- /package/dist/{replyHelpers-C-gD32oF.mjs → replyHelpers-boS5Rx26.mjs} +0 -0
- /package/dist/{schemaIR-Ctc89DSn.mjs → schemaIR-DAOvBWgT.mjs} +0 -0
- /package/dist/{sessionManager-BqFegc0W.d.mts → sessionManager-DlmaHUAl.d.mts} +0 -0
- /package/dist/{sse-BY6sTy4P.mjs → sse-jh5OqFjR.mjs} +0 -0
- /package/dist/{storage-D2KZJAmn.d.mts → storage-HcAgP5Ni.d.mts} +0 -0
- /package/dist/{store-helpers-B0sunfZZ.mjs → store-helpers-5JgaxieH.mjs} +0 -0
- /package/dist/{tracing-Dm8n7Cnn.d.mts → tracing-bRqqUxFO.d.mts} +0 -0
- /package/dist/{types-DVfpSfx2.d.mts → types-CfapW2t8.d.mts} +0 -0
- /package/dist/{types-D57iXYb8.mjs → types-DV9WDfeg.mjs} +0 -0
- /package/dist/{versioning-B6mimogM.mjs → versioning-CuIwUQZ2.mjs} +0 -0
- /package/dist/{websocket-BkjeGZRn.d.mts → websocket-BJ0w-3Dt.d.mts} +0 -0
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { J as CrudRouteKey, R as RegisterOptions, V as ResourceDefinition, at as ResolvedTenantPurge, k as IntrospectionPluginOptions, p as RegistryEntry, z as ResourceRegistry } from "../index-
|
|
1
|
+
import { J as CrudRouteKey, R as RegisterOptions, V as ResourceDefinition, at as ResolvedTenantPurge, k as IntrospectionPluginOptions, p as RegistryEntry, z as ResourceRegistry } from "../index-C1JSBan-.mjs";
|
|
2
2
|
import { FastifyPluginAsync } from "fastify";
|
|
3
3
|
import { TenantPurgeProgress, TenantPurgeStrategy } from "@classytic/repo-core/repository";
|
|
4
4
|
|
|
@@ -20,7 +20,14 @@ interface AssertNoTenantDataOptions {
|
|
|
20
20
|
interface TenantDataLeak {
|
|
21
21
|
readonly resource: string;
|
|
22
22
|
readonly tenantField: string;
|
|
23
|
-
|
|
23
|
+
/**
|
|
24
|
+
* Full `TenantPurgeStrategy` discriminated union — not just `.type`.
|
|
25
|
+
* `anonymize` carries the field map; `custom` carries the handler
|
|
26
|
+
* descriptor; auditors get the actionable signal without re-resolving
|
|
27
|
+
* the resource definition. Narrow on `.strategy.type` for typed
|
|
28
|
+
* access.
|
|
29
|
+
*/
|
|
30
|
+
readonly strategy: TenantPurgeStrategy;
|
|
24
31
|
readonly expected: number;
|
|
25
32
|
readonly actual: number;
|
|
26
33
|
readonly reason?: string;
|
|
@@ -64,7 +71,14 @@ declare function assertNoTenantData(registry: ResourceRegistry, options: AssertN
|
|
|
64
71
|
interface PurgeResourceOutcome {
|
|
65
72
|
readonly resource: string;
|
|
66
73
|
readonly tenantField: string;
|
|
67
|
-
|
|
74
|
+
/**
|
|
75
|
+
* Full `TenantPurgeStrategy` discriminated union. Audit consumers
|
|
76
|
+
* narrow on `.strategy.type` for typed access — `skip` carries the
|
|
77
|
+
* mandatory `reason`, `anonymize` carries the field map, `custom`
|
|
78
|
+
* carries the handler descriptor. Pre-2.17 this was the `.type` tag
|
|
79
|
+
* only, which dropped the audit-critical `reason` field.
|
|
80
|
+
*/
|
|
81
|
+
readonly strategy: TenantPurgeStrategy;
|
|
68
82
|
readonly processed: number;
|
|
69
83
|
readonly ok: boolean;
|
|
70
84
|
readonly path: "purgeByField" | "legacy-deleteMany" | "skipped" | "unsupported";
|
|
@@ -92,11 +106,13 @@ interface CascadeResourceReport {
|
|
|
92
106
|
*/
|
|
93
107
|
readonly deletedCount: number;
|
|
94
108
|
/**
|
|
95
|
-
*
|
|
96
|
-
* `'
|
|
97
|
-
* "did data physically leave the system?".
|
|
109
|
+
* Full `TenantPurgeStrategy` that ran for this resource. Branch on
|
|
110
|
+
* `.strategy.type` (`'hard'` / `'soft'` / `'anonymize'` / `'skip'` /
|
|
111
|
+
* `'custom'`) to answer "did data physically leave the system?". The
|
|
112
|
+
* `skip` variant carries the mandatory `reason`; `anonymize` carries
|
|
113
|
+
* the field map; `custom` carries the handler descriptor.
|
|
98
114
|
*/
|
|
99
|
-
readonly strategy?: TenantPurgeStrategy
|
|
115
|
+
readonly strategy?: TenantPurgeStrategy;
|
|
100
116
|
/**
|
|
101
117
|
* Where the strategy came from — `'declared'` (host wrote
|
|
102
118
|
* `onTenantDelete`) or `'disabled'` (filtered out before reaching
|
|
@@ -221,11 +237,17 @@ declare function getCascadingResources(registry: ResourceRegistry): readonly str
|
|
|
221
237
|
* cascading resource. Use for audit dashboards that answer "what
|
|
222
238
|
* happens to this resource on org-delete?" without grepping the
|
|
223
239
|
* source.
|
|
240
|
+
*
|
|
241
|
+
* **Returns the full `TenantPurgeStrategy` discriminated union**, not
|
|
242
|
+
* just its `type` tag. The `skip` variant carries a required `reason`
|
|
243
|
+
* field — auditors need it to sign off on every "we deliberately don't
|
|
244
|
+
* purge this" entry. Anonymize carries the field map. Custom carries
|
|
245
|
+
* the handler reference. Narrow on `.strategy.type` for typed access.
|
|
224
246
|
*/
|
|
225
247
|
declare function getCascadingResourcesWithMetadata(registry: ResourceRegistry): readonly {
|
|
226
248
|
name: string;
|
|
227
249
|
tenantField: string;
|
|
228
|
-
strategy: TenantPurgeStrategy
|
|
250
|
+
strategy: TenantPurgeStrategy;
|
|
229
251
|
source: ResolvedTenantPurge["source"];
|
|
230
252
|
priority: number;
|
|
231
253
|
}[];
|
package/dist/registry/index.mjs
CHANGED
|
@@ -1,3 +1,3 @@
|
|
|
1
|
-
import { a as cascadeDeleteForOrganization, c as assertNoTenantData, i as introspectionPlugin_default, n as buildResourceManifestFromRegistry, o as getCascadingResources, r as introspectionPlugin, s as getCascadingResourcesWithMetadata, t as buildResourceManifest } from "../registry-
|
|
2
|
-
import { t as ResourceRegistry } from "../ResourceRegistry-
|
|
1
|
+
import { a as cascadeDeleteForOrganization, c as assertNoTenantData, i as introspectionPlugin_default, n as buildResourceManifestFromRegistry, o as getCascadingResources, r as introspectionPlugin, s as getCascadingResourcesWithMetadata, t as buildResourceManifest } from "../registry-BrlHkrRS.mjs";
|
|
2
|
+
import { t as ResourceRegistry } from "../ResourceRegistry-CzSzVK1m.mjs";
|
|
3
3
|
export { ResourceRegistry, assertNoTenantData, buildResourceManifest, buildResourceManifestFromRegistry, cascadeDeleteForOrganization, getCascadingResources, getCascadingResourcesWithMetadata, introspectionPlugin_default as introspectionPlugin, introspectionPlugin as introspectionPluginFn };
|
|
@@ -59,7 +59,7 @@ async function assertNoTenantData(registry, options) {
|
|
|
59
59
|
if (actual !== expected) leaks.push({
|
|
60
60
|
resource: r.name,
|
|
61
61
|
tenantField,
|
|
62
|
-
strategy
|
|
62
|
+
strategy,
|
|
63
63
|
expected,
|
|
64
64
|
actual
|
|
65
65
|
});
|
|
@@ -88,7 +88,7 @@ async function purgeResource(resourceName, tenantField, organizationId, resolved
|
|
|
88
88
|
if (strategy.type === "skip") return {
|
|
89
89
|
resource: resourceName,
|
|
90
90
|
tenantField,
|
|
91
|
-
strategy
|
|
91
|
+
strategy,
|
|
92
92
|
processed: 0,
|
|
93
93
|
ok: true,
|
|
94
94
|
path: "skipped",
|
|
@@ -103,7 +103,7 @@ async function purgeResource(resourceName, tenantField, organizationId, resolved
|
|
|
103
103
|
return {
|
|
104
104
|
resource: resourceName,
|
|
105
105
|
tenantField,
|
|
106
|
-
strategy
|
|
106
|
+
strategy,
|
|
107
107
|
processed: result.processed,
|
|
108
108
|
ok: result.ok,
|
|
109
109
|
path: "purgeByField",
|
|
@@ -114,7 +114,7 @@ async function purgeResource(resourceName, tenantField, organizationId, resolved
|
|
|
114
114
|
if (strategy.type !== "hard") return {
|
|
115
115
|
resource: resourceName,
|
|
116
116
|
tenantField,
|
|
117
|
-
strategy
|
|
117
|
+
strategy,
|
|
118
118
|
processed: 0,
|
|
119
119
|
ok: false,
|
|
120
120
|
path: "unsupported",
|
|
@@ -127,7 +127,7 @@ async function purgeResource(resourceName, tenantField, organizationId, resolved
|
|
|
127
127
|
if (!op) return {
|
|
128
128
|
resource: resourceName,
|
|
129
129
|
tenantField,
|
|
130
|
-
strategy
|
|
130
|
+
strategy,
|
|
131
131
|
processed: 0,
|
|
132
132
|
ok: false,
|
|
133
133
|
path: "unsupported",
|
|
@@ -142,7 +142,7 @@ async function purgeResource(resourceName, tenantField, organizationId, resolved
|
|
|
142
142
|
return {
|
|
143
143
|
resource: resourceName,
|
|
144
144
|
tenantField,
|
|
145
|
-
strategy
|
|
145
|
+
strategy,
|
|
146
146
|
processed: typeof result === "number" ? result : typeof result?.deletedCount === "number" ? result.deletedCount : typeof result?.count === "number" ? result.count : -1,
|
|
147
147
|
ok: true,
|
|
148
148
|
path: "legacy-deleteMany",
|
|
@@ -152,7 +152,7 @@ async function purgeResource(resourceName, tenantField, organizationId, resolved
|
|
|
152
152
|
return {
|
|
153
153
|
resource: resourceName,
|
|
154
154
|
tenantField,
|
|
155
|
-
strategy
|
|
155
|
+
strategy,
|
|
156
156
|
processed: 0,
|
|
157
157
|
ok: false,
|
|
158
158
|
path: "legacy-deleteMany",
|
|
@@ -178,6 +178,12 @@ function getCascadingResources(registry) {
|
|
|
178
178
|
* cascading resource. Use for audit dashboards that answer "what
|
|
179
179
|
* happens to this resource on org-delete?" without grepping the
|
|
180
180
|
* source.
|
|
181
|
+
*
|
|
182
|
+
* **Returns the full `TenantPurgeStrategy` discriminated union**, not
|
|
183
|
+
* just its `type` tag. The `skip` variant carries a required `reason`
|
|
184
|
+
* field — auditors need it to sign off on every "we deliberately don't
|
|
185
|
+
* purge this" entry. Anonymize carries the field map. Custom carries
|
|
186
|
+
* the handler reference. Narrow on `.strategy.type` for typed access.
|
|
181
187
|
*/
|
|
182
188
|
function getCascadingResourcesWithMetadata(registry) {
|
|
183
189
|
return registry.getAll().filter((r) => isResourceCascading(r)).map((r) => {
|
|
@@ -191,7 +197,7 @@ function getCascadingResourcesWithMetadata(registry) {
|
|
|
191
197
|
return {
|
|
192
198
|
name: r.name,
|
|
193
199
|
tenantField,
|
|
194
|
-
strategy: r0.strategy
|
|
200
|
+
strategy: r0.strategy,
|
|
195
201
|
source: r0.source,
|
|
196
202
|
priority: r0.priority
|
|
197
203
|
};
|
|
@@ -1,10 +1,63 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { h as SYSTEM_FIELDS } from "./constants-TrJVIJl0.mjs";
|
|
2
|
+
import { f as createError, r as ForbiddenError } from "./errors-j4aJm1Wg.mjs";
|
|
2
3
|
import { b as isElevated, c as getOrgId, g as getUserId, n as PUBLIC_SCOPE, x as isMember } from "./types-Bi0r0vjG.mjs";
|
|
3
4
|
import { t as requestContext } from "./requestContext-SSaaTgW8.mjs";
|
|
4
|
-
import { I as evaluateAndApplyPermission,
|
|
5
|
-
import { t as getUserRoles } from "./types-
|
|
5
|
+
import { I as evaluateAndApplyPermission, M as applyFieldWritePermissions, P as resolveEffectiveRoles, j as applyFieldReadPermissions } from "./permissions-B1Qy55fg.mjs";
|
|
6
|
+
import { t as getUserRoles } from "./types-DV9WDfeg.mjs";
|
|
6
7
|
import { t as executePipeline } from "./pipe-DiCyvyPN.mjs";
|
|
7
8
|
import { isPaginatedResult, toCanonicalList } from "@classytic/repo-core/pagination";
|
|
9
|
+
//#region src/core/BodySanitizer.ts
|
|
10
|
+
/**
|
|
11
|
+
* BodySanitizer - Composable body sanitization logic extracted from BaseController.
|
|
12
|
+
*
|
|
13
|
+
* Strips readonly fields, system-managed fields, and applies field-level
|
|
14
|
+
* write permissions from request bodies before create/update operations.
|
|
15
|
+
*
|
|
16
|
+
* Designed to be used standalone or composed into controllers.
|
|
17
|
+
*/
|
|
18
|
+
const DEFAULT_FIELD_WRITE_DENIAL_POLICY = "reject";
|
|
19
|
+
var BodySanitizer = class {
|
|
20
|
+
schemaOptions;
|
|
21
|
+
onFieldWriteDenied;
|
|
22
|
+
constructor(config) {
|
|
23
|
+
this.schemaOptions = config.schemaOptions;
|
|
24
|
+
this.onFieldWriteDenied = config.onFieldWriteDenied ?? "reject";
|
|
25
|
+
}
|
|
26
|
+
/**
|
|
27
|
+
* Strip readonly and system-managed fields from request body.
|
|
28
|
+
* Prevents clients from overwriting _id, timestamps, __v, etc.
|
|
29
|
+
*
|
|
30
|
+
* Also applies field-level write permissions when the request has
|
|
31
|
+
* field permission metadata.
|
|
32
|
+
*/
|
|
33
|
+
sanitize(body, _operation, req, meta) {
|
|
34
|
+
let sanitized = { ...body };
|
|
35
|
+
for (const field of SYSTEM_FIELDS) delete sanitized[field];
|
|
36
|
+
const scopeForRules = req ? (meta ?? req.metadata)?._scope ?? PUBLIC_SCOPE : void 0;
|
|
37
|
+
const scopeIsElevated = scopeForRules ? isElevated(scopeForRules) : false;
|
|
38
|
+
const fieldRules = this.schemaOptions.fieldRules ?? {};
|
|
39
|
+
for (const [field, rules] of Object.entries(fieldRules)) {
|
|
40
|
+
const bypass = Boolean(rules.preserveForElevated) && scopeIsElevated;
|
|
41
|
+
if ((rules.systemManaged || rules.readonly) && !bypass) delete sanitized[field];
|
|
42
|
+
if (_operation === "update" && (rules.immutable || rules.immutableAfterCreate) && !bypass) delete sanitized[field];
|
|
43
|
+
}
|
|
44
|
+
if (req) {
|
|
45
|
+
const arcContext = meta ?? req.metadata;
|
|
46
|
+
const scope = arcContext?._scope ?? PUBLIC_SCOPE;
|
|
47
|
+
if (!isElevated(scope)) {
|
|
48
|
+
const fieldPerms = arcContext?.arc?.fields;
|
|
49
|
+
if (fieldPerms) {
|
|
50
|
+
const effectiveRoles = resolveEffectiveRoles(getUserRoles(req.user), isMember(scope) ? scope.orgRoles : []);
|
|
51
|
+
const { body: filtered, deniedFields } = applyFieldWritePermissions(sanitized, fieldPerms, effectiveRoles);
|
|
52
|
+
if (deniedFields.length > 0 && this.onFieldWriteDenied === "reject") throw new ForbiddenError(`Not permitted to write field${deniedFields.length === 1 ? "" : "s"}: ${deniedFields.join(", ")}`);
|
|
53
|
+
sanitized = filtered;
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
}
|
|
57
|
+
return sanitized;
|
|
58
|
+
}
|
|
59
|
+
};
|
|
60
|
+
//#endregion
|
|
8
61
|
//#region src/scope/projection.ts
|
|
9
62
|
/**
|
|
10
63
|
* Compute the request-scope projection. Returns `undefined` when no
|
|
@@ -55,9 +108,13 @@ function applyFieldMask(data, fieldMask) {
|
|
|
55
108
|
return data;
|
|
56
109
|
}
|
|
57
110
|
/**
|
|
58
|
-
*
|
|
59
|
-
*
|
|
60
|
-
*
|
|
111
|
+
* Project `FastifyRequest` into the curated `IRequestContext` shape that
|
|
112
|
+
* arc's controllers receive. This is a developer-facing facade, not a
|
|
113
|
+
* portability boundary — arc is Fastify-only. The projection lifts the
|
|
114
|
+
* fields tenant-scoped controllers reach for (`organizationId`,
|
|
115
|
+
* `teamId`, `scope`, `context`, server accessor) onto a flat, typed
|
|
116
|
+
* surface so handler code doesn't have to dig through Fastify's
|
|
117
|
+
* decorator surface or `req.metadata._scope` internals.
|
|
61
118
|
*/
|
|
62
119
|
function createRequestContext(req) {
|
|
63
120
|
const reqWithExtras = req;
|
|
@@ -145,12 +202,33 @@ function computeFieldCapabilities(fieldPerms, effectiveRoles) {
|
|
|
145
202
|
return caps;
|
|
146
203
|
}
|
|
147
204
|
/**
|
|
148
|
-
* Send IControllerResponse
|
|
205
|
+
* Send a controller's `IControllerResponse` through Fastify's reply.
|
|
149
206
|
*
|
|
150
|
-
*
|
|
151
|
-
*
|
|
207
|
+
* The `IControllerResponse<T>` envelope is arc's internal pipeline
|
|
208
|
+
* contract — it's how custom `status`, `meta`, `headers`, and field
|
|
209
|
+
* permissions thread from a handler through interceptors to the wire
|
|
210
|
+
* without each step having to touch `reply` directly. Not a portability
|
|
211
|
+
* abstraction (arc is Fastify-only); a pipeline-internal one.
|
|
212
|
+
*
|
|
213
|
+
* **Auto-envelope of bare returns.** Custom route handlers are
|
|
214
|
+
* expected to return the envelope shape so this function can apply
|
|
215
|
+
* field permissions, attach `fieldCaps`, and flatten paginated lists.
|
|
216
|
+
* A handler that returns raw `T` (array, plain object, primitive) used
|
|
217
|
+
* to silently produce a broken response — `response.data` was
|
|
218
|
+
* `undefined`, the body sent was `undefined`, and any declared
|
|
219
|
+
* `fields:` permission map was bypassed (the leak shape mentora
|
|
220
|
+
* flagged). 2.17 detects bare returns and wraps them into
|
|
221
|
+
* `{ data: response, status: 200 }` before the field-permission
|
|
222
|
+
* pipeline runs, so `fields:` applies to ALL custom routes whether the
|
|
223
|
+
* handler envelopes its return value or not. The envelope shape is
|
|
224
|
+
* still the canonical contract — auto-wrapping is a safety net, not
|
|
225
|
+
* an invitation to drop the envelope.
|
|
152
226
|
*/
|
|
153
227
|
function sendControllerResponse(reply, response, request) {
|
|
228
|
+
if (response === null || typeof response !== "object" || !Object.hasOwn(response, "data")) response = {
|
|
229
|
+
data: response,
|
|
230
|
+
status: 200
|
|
231
|
+
};
|
|
154
232
|
const reqWithExtras = request;
|
|
155
233
|
const fieldMaskConfig = reqWithExtras?.fieldMask;
|
|
156
234
|
const arcMeta = reqWithExtras?.arc;
|
|
@@ -207,18 +285,17 @@ function sendControllerResponse(reply, response, request) {
|
|
|
207
285
|
reply.code(status).send(filteredData);
|
|
208
286
|
}
|
|
209
287
|
/**
|
|
210
|
-
*
|
|
211
|
-
*
|
|
212
|
-
*
|
|
288
|
+
* Bridge an `IController` method (`(ctx: IRequestContext) => Promise<IControllerResponse<T>>`)
|
|
289
|
+
* into a Fastify route handler. Translates between arc's curated request
|
|
290
|
+
* context surface and Fastify's request/reply, and routes the envelope
|
|
291
|
+
* through `sendControllerResponse` so the pipeline applies field
|
|
292
|
+
* permissions, status, and meta uniformly. Internal plumbing, not a
|
|
293
|
+
* portability layer — arc is Fastify-only.
|
|
213
294
|
*
|
|
214
295
|
* @example
|
|
215
296
|
* ```typescript
|
|
216
297
|
* const controller = new BaseController(repository);
|
|
217
|
-
*
|
|
218
|
-
* // Create Fastify handler
|
|
219
298
|
* const listHandler = createFastifyHandler(controller.list.bind(controller));
|
|
220
|
-
*
|
|
221
|
-
* // Register route
|
|
222
299
|
* fastify.get('/products', listHandler);
|
|
223
300
|
* ```
|
|
224
301
|
*/
|
|
@@ -573,5 +650,75 @@ function describeValue(v) {
|
|
|
573
650
|
if (Array.isArray(v)) return `an array of length ${v.length}`;
|
|
574
651
|
return `${typeof v} (${JSON.stringify(v).slice(0, 80)})`;
|
|
575
652
|
}
|
|
653
|
+
/**
|
|
654
|
+
* Build a preHandler that enforces field-write permissions on the request body.
|
|
655
|
+
*
|
|
656
|
+
* Auto-CRUD's create/update routes get this for free via `BodySanitizer.sanitize()`
|
|
657
|
+
* inside `BaseController`. Custom routes (`config.routes`, presets, action endpoints)
|
|
658
|
+
* never went through that path — so a host that declared
|
|
659
|
+
* `fields: { role: fields.writableBy(['admin']) }` and a custom
|
|
660
|
+
* `POST /users/promote` happily accepted `{ role: 'admin' }` from any caller.
|
|
661
|
+
*
|
|
662
|
+
* This preHandler closes that gap. It applies ONLY the `applyFieldWritePermissions`
|
|
663
|
+
* step — no `SYSTEM_FIELDS` strip, no `fieldRules` strip — because custom-route
|
|
664
|
+
* bodies don't necessarily mirror the resource's adapter shape, and we'd
|
|
665
|
+
* generate surprising 403s by enforcing rules tied to that shape.
|
|
666
|
+
*
|
|
667
|
+
* Returns `null` when the resource has no field permissions configured — the
|
|
668
|
+
* caller should then skip wiring it into the preHandler chain.
|
|
669
|
+
*/
|
|
670
|
+
function buildFieldWritePreHandler(fieldPermissions, policy = DEFAULT_FIELD_WRITE_DENIAL_POLICY) {
|
|
671
|
+
if (!fieldPermissions || Object.keys(fieldPermissions).length === 0) return null;
|
|
672
|
+
return async (request, _reply) => {
|
|
673
|
+
const body = request.body;
|
|
674
|
+
if (!body || typeof body !== "object" || Array.isArray(body)) return;
|
|
675
|
+
const scope = request.scope ?? PUBLIC_SCOPE;
|
|
676
|
+
if (isElevated(scope)) return;
|
|
677
|
+
const { body: filtered, deniedFields } = applyFieldWritePermissions(body, fieldPermissions, resolveEffectiveRoles(getUserRoles(request.user), isMember(scope) ? scope.orgRoles : []));
|
|
678
|
+
if (deniedFields.length > 0 && policy === "reject") throw new ForbiddenError(`Not permitted to write field${deniedFields.length === 1 ? "" : "s"}: ${deniedFields.join(", ")}`);
|
|
679
|
+
request.body = filtered;
|
|
680
|
+
};
|
|
681
|
+
}
|
|
682
|
+
/**
|
|
683
|
+
* True for HTTP methods that carry a request body and should run the
|
|
684
|
+
* field-write preHandler. GET/DELETE/HEAD/OPTIONS skip enforcement.
|
|
685
|
+
*/
|
|
686
|
+
function methodCarriesBody(method) {
|
|
687
|
+
const m = method.toUpperCase();
|
|
688
|
+
return m === "POST" || m === "PUT" || m === "PATCH";
|
|
689
|
+
}
|
|
690
|
+
/**
|
|
691
|
+
* Register a route with friendly handling of `FST_ERR_DUPLICATED_ROUTE`.
|
|
692
|
+
*
|
|
693
|
+
* Boot-time `validateRouteCrudCollisions()` catches custom-route → auto-CRUD
|
|
694
|
+
* overlaps before Fastify ever runs, but a few cases still slip through to
|
|
695
|
+
* Fastify itself: two presets contributing the same `routes` entry, hosts
|
|
696
|
+
* mounting two resources at the same prefix, custom routes that share a
|
|
697
|
+
* URL with another plugin. Fastify's default message ("Method 'GET'
|
|
698
|
+
* already declared for route '/'") gives no hint at the resource-shaped
|
|
699
|
+
* fix — `disabledRoutes: ['list']` — so wrap it with one.
|
|
700
|
+
*/
|
|
701
|
+
function tryRegisterRoute(fastify, opts, context) {
|
|
702
|
+
try {
|
|
703
|
+
fastify.route(opts);
|
|
704
|
+
} catch (err) {
|
|
705
|
+
const fastifyErr = err;
|
|
706
|
+
if (fastifyErr?.code !== "FST_ERR_DUPLICATED_ROUTE") throw err;
|
|
707
|
+
const method = opts.method;
|
|
708
|
+
const url = opts.url;
|
|
709
|
+
const opHint = context.op ? ` (op: ${context.op})` : "";
|
|
710
|
+
const fixHint = context.op && [
|
|
711
|
+
"list",
|
|
712
|
+
"get",
|
|
713
|
+
"create",
|
|
714
|
+
"update",
|
|
715
|
+
"delete"
|
|
716
|
+
].includes(context.op) ? ` Suppress the auto-CRUD route with \`disabledRoutes: ['${context.op}']\`.` : " If a custom route in `routes:` collides with auto-CRUD, add `disabledRoutes: ['list' | 'get' | 'create' | 'update' | 'delete']` to `defineResource()` to suppress the matching auto-CRUD route.";
|
|
717
|
+
const enhanced = /* @__PURE__ */ new Error(`[arc] Duplicate route ${method} ${url} on resource "${context.resourceName}"${opHint}.${fixHint} Original: ${fastifyErr.message ?? "FST_ERR_DUPLICATED_ROUTE"}`);
|
|
718
|
+
enhanced.code = "FST_ERR_DUPLICATED_ROUTE";
|
|
719
|
+
enhanced.cause = err;
|
|
720
|
+
throw enhanced;
|
|
721
|
+
}
|
|
722
|
+
}
|
|
576
723
|
//#endregion
|
|
577
|
-
export {
|
|
724
|
+
export { buildRequestScopeProjection as C, sendControllerResponse as S, createCrudHandlers as _, buildAuthMiddlewareForPermissions as a, getControllerContext as b, buildPipelineHandler as c, methodCarriesBody as d, resolvePipelineSteps as f, tryRegisterRoute as g, selectPluginMw as h, buildAuthMiddleware as i, buildPreHandlerChain as l, resolveRouterPluginMw as m, buildActionPipelineHandler as n, buildCrudPermissionMw as o, resolveRoutePreHandlers as p, buildArcDecorator as r, buildFieldWritePreHandler as s, buildActionPermissionMw as t, buildRateLimitConfig as u, createFastifyHandler as v, BodySanitizer as w, getControllerScope as x, createRequestContext as y };
|
package/dist/scope/index.d.mts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import { C as isMember, D as requireOrgId, E as requireClientId, O as requireTeamId, S as isElevated, T as isService, _ as getTenantFromRequest, a as getAncestorOrgIds, b as hasOrgAccess, c as getMandate, d as getOrgRoles, f as getRequestScope, g as getTeamId, h as getServiceScopes, i as RequestScope, k as requireUserId, l as getOrgContext, m as getScopeContextMap, n as Mandate, o as getClientId, p as getScopeContext, r as PUBLIC_SCOPE, s as getDPoPJkt, t as AUTHENTICATED_SCOPE, u as getOrgId, v as getUserId, w as isOrgInScope, x as isAuthenticated, y as getUserRoles } from "../types-
|
|
2
|
-
import { i as elevationPlugin, n as ElevationOptions, r as _default, t as ElevationEvent } from "../elevation-
|
|
1
|
+
import { C as isMember, D as requireOrgId, E as requireClientId, O as requireTeamId, S as isElevated, T as isService, _ as getTenantFromRequest, a as getAncestorOrgIds, b as hasOrgAccess, c as getMandate, d as getOrgRoles, f as getRequestScope, g as getTeamId, h as getServiceScopes, i as RequestScope, k as requireUserId, l as getOrgContext, m as getScopeContextMap, n as Mandate, o as getClientId, p as getScopeContext, r as PUBLIC_SCOPE, s as getDPoPJkt, t as AUTHENTICATED_SCOPE, u as getOrgId, v as getUserId, w as isOrgInScope, x as isAuthenticated, y as getUserRoles } from "../types-CfapW2t8.mjs";
|
|
2
|
+
import { i as elevationPlugin, n as ElevationOptions, r as _default, t as ElevationEvent } from "../elevation-DvwtjDwu.mjs";
|
|
3
3
|
import { FastifyReply, FastifyRequest } from "fastify";
|
|
4
4
|
|
|
5
5
|
//#region src/scope/rateLimitKey.d.ts
|
package/dist/scope/index.mjs
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { C as isService, D as requireUserId, E as requireTeamId, S as isOrgInScope, T as requireOrgId, _ as getUserRoles, a as getDPoPJkt, b as isElevated, c as getOrgId, d as getScopeContext, f as getScopeContextMap, g as getUserId, h as getTenantFromRequest, i as getClientId, l as getOrgRoles, m as getTeamId, n as PUBLIC_SCOPE, o as getMandate, p as getServiceScopes, r as getAncestorOrgIds, s as getOrgContext, t as AUTHENTICATED_SCOPE, u as getRequestScope, v as hasOrgAccess, w as requireClientId, x as isMember, y as isAuthenticated } from "../types-Bi0r0vjG.mjs";
|
|
2
|
-
import { n as normalizeRoles } from "../types-
|
|
3
|
-
import { n as elevation_default, t as elevationPlugin } from "../elevation-
|
|
2
|
+
import { n as normalizeRoles } from "../types-DV9WDfeg.mjs";
|
|
3
|
+
import { n as elevation_default, t as elevationPlugin } from "../elevation-6mfTLBC-.mjs";
|
|
4
4
|
//#region src/scope/rateLimitKey.ts
|
|
5
5
|
function createTenantKeyGenerator(opts) {
|
|
6
6
|
if (opts?.strategy) return opts.strategy;
|
package/dist/testing/index.d.mts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import { Kt as AnyRecord, V as ResourceDefinition } from "../index-
|
|
2
|
-
import { d as
|
|
1
|
+
import { Kt as AnyRecord, V as ResourceDefinition } from "../index-C1JSBan-.mjs";
|
|
2
|
+
import { d as preloadResourcesAsync, r as CreateAppOptions, u as preloadResources } from "../types-BRmS8TNa.mjs";
|
|
3
3
|
import { StorageContractSetup, StorageContractSetupResult, runStorageContract } from "./storageContract.mjs";
|
|
4
4
|
import { FastifyInstance, FastifyServerOptions } from "fastify";
|
|
5
5
|
import { Mock } from "vitest";
|
|
@@ -464,29 +464,6 @@ declare function createTestTimer(): {
|
|
|
464
464
|
reset: () => void;
|
|
465
465
|
};
|
|
466
466
|
//#endregion
|
|
467
|
-
//#region src/testing/preloadResources.d.ts
|
|
468
|
-
/** Eager glob result: `{ '/path/to/file.ts': resourceModule }` */
|
|
469
|
-
type EagerGlobResult = Record<string, unknown>;
|
|
470
|
-
/** Lazy glob result: `{ '/path/to/file.ts': () => Promise<unknown> }` */
|
|
471
|
-
type LazyGlobResult = Record<string, () => Promise<unknown>>;
|
|
472
|
-
/**
|
|
473
|
-
* Normalize an eager `import.meta.glob` result into a `ResourceLike[]`.
|
|
474
|
-
*
|
|
475
|
-
* Accepts either:
|
|
476
|
-
* - `{ import: 'default' }` form: values are the resource directly
|
|
477
|
-
* - default form: values are the full module — picks first export with `toPlugin()`
|
|
478
|
-
*
|
|
479
|
-
* Throws if any module doesn't yield a valid `ResourceLike`.
|
|
480
|
-
*/
|
|
481
|
-
declare function preloadResources(globResult: EagerGlobResult): ResourceLike[];
|
|
482
|
-
/**
|
|
483
|
-
* Normalize a lazy `import.meta.glob` result into a `Promise<ResourceLike[]>`.
|
|
484
|
-
*
|
|
485
|
-
* Use this when resources depend on prior bootstrap (e.g., engine init) and
|
|
486
|
-
* cannot be evaluated at import time of the preload file.
|
|
487
|
-
*/
|
|
488
|
-
declare function preloadResourcesAsync(globResult: LazyGlobResult): Promise<ResourceLike[]>;
|
|
489
|
-
//#endregion
|
|
490
467
|
//#region src/testing/testApp.d.ts
|
|
491
468
|
type DbMode = "in-memory" | {
|
|
492
469
|
uri: string;
|
package/dist/testing/index.mjs
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { t as CRUD_OPERATIONS } from "../constants-TrJVIJl0.mjs";
|
|
2
|
+
import { n as preloadResourcesAsync, t as preloadResources } from "../preloadResources-BOXzPhKU.mjs";
|
|
2
3
|
import { runStorageContract } from "./storageContract.mjs";
|
|
3
4
|
import Fastify from "fastify";
|
|
4
5
|
import { afterAll, describe, expect, it, vi } from "vitest";
|
|
@@ -970,50 +971,6 @@ function createTestTimer() {
|
|
|
970
971
|
};
|
|
971
972
|
}
|
|
972
973
|
//#endregion
|
|
973
|
-
//#region src/testing/preloadResources.ts
|
|
974
|
-
/**
|
|
975
|
-
* Normalize an eager `import.meta.glob` result into a `ResourceLike[]`.
|
|
976
|
-
*
|
|
977
|
-
* Accepts either:
|
|
978
|
-
* - `{ import: 'default' }` form: values are the resource directly
|
|
979
|
-
* - default form: values are the full module — picks first export with `toPlugin()`
|
|
980
|
-
*
|
|
981
|
-
* Throws if any module doesn't yield a valid `ResourceLike`.
|
|
982
|
-
*/
|
|
983
|
-
function preloadResources(globResult) {
|
|
984
|
-
const resources = [];
|
|
985
|
-
for (const [path, value] of Object.entries(globResult)) {
|
|
986
|
-
const resource = pickResource(value);
|
|
987
|
-
if (!resource) throw new Error(`preloadResources: ${path} does not export a valid resource.\n Expected: a default export OR a named export with toPlugin().`);
|
|
988
|
-
resources.push(resource);
|
|
989
|
-
}
|
|
990
|
-
return resources.sort((a, b) => (a.name ?? "").localeCompare(b.name ?? ""));
|
|
991
|
-
}
|
|
992
|
-
/**
|
|
993
|
-
* Normalize a lazy `import.meta.glob` result into a `Promise<ResourceLike[]>`.
|
|
994
|
-
*
|
|
995
|
-
* Use this when resources depend on prior bootstrap (e.g., engine init) and
|
|
996
|
-
* cannot be evaluated at import time of the preload file.
|
|
997
|
-
*/
|
|
998
|
-
async function preloadResourcesAsync(globResult) {
|
|
999
|
-
return (await Promise.all(Object.entries(globResult).map(async ([path, loader]) => {
|
|
1000
|
-
const resource = pickResource(await loader());
|
|
1001
|
-
if (!resource) throw new Error(`preloadResourcesAsync: ${path} does not export a valid resource.\n Expected: a default export OR a named export with toPlugin().`);
|
|
1002
|
-
return resource;
|
|
1003
|
-
}))).sort((a, b) => (a.name ?? "").localeCompare(b.name ?? ""));
|
|
1004
|
-
}
|
|
1005
|
-
function pickResource(value) {
|
|
1006
|
-
if (!value || typeof value !== "object") return void 0;
|
|
1007
|
-
if (typeof value.toPlugin === "function") return value;
|
|
1008
|
-
const mod = value;
|
|
1009
|
-
const candidates = [
|
|
1010
|
-
mod.default,
|
|
1011
|
-
mod.resource,
|
|
1012
|
-
...Object.values(mod)
|
|
1013
|
-
];
|
|
1014
|
-
for (const c of candidates) if (c && typeof c === "object" && typeof c.toPlugin === "function") return c;
|
|
1015
|
-
}
|
|
1016
|
-
//#endregion
|
|
1017
974
|
//#region src/testing/testApp.ts
|
|
1018
975
|
/**
|
|
1019
976
|
* createTestApp — test app factory for arc
|
|
@@ -1079,7 +1036,7 @@ function pickDefaultAuth(authMode, callerAuth) {
|
|
|
1079
1036
|
};
|
|
1080
1037
|
}
|
|
1081
1038
|
async function createTestApp(options = {}) {
|
|
1082
|
-
const { createApp } = await import("../createApp-
|
|
1039
|
+
const { createApp } = await import("../createApp-CzCEEsEm.mjs").then((n) => n.r);
|
|
1083
1040
|
const { resources = [], db = "in-memory", connectMongoose = false, authMode = "jwt", defaultOrgId, plugins, auth: callerAuth, ...appOptions } = options;
|
|
1084
1041
|
let dbHandle;
|
|
1085
1042
|
let dbUri;
|
package/dist/types/index.d.mts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import { $ as OnTenantDeleteConfig, A as RequestIdOptions, Bt as AuthHelpers, C as RequestContext, D as HealthCheck, E as GracefulShutdownOptions, F as FastifyWithDecorators, G as ActionsMap, Gt as TokenPair, H as ActionDefinition, Ht as Authenticator, I as MiddlewareHandler, J as CrudRouteKey, Jt as ArcRequest, K as ArcFieldRule, Kt as AnyRecord, L as RequestWithExtras, M as EventsDecorator, N as FastifyRequestExtras, O as HealthOptions, P as FastifyWithAuth, Q as MiddlewareConfig, Qt as UserOrganization, S as QueryParserInterface, T as CrudRouterOptions, U as ActionEntry, Ut as AuthenticatorContext, Vt as AuthPluginOptions, W as ActionHandlerFn, Wt as JwtContext, X as EventDefinition, Xt as ObjectId, Y as CrudSchemas, Yt as JWTPayload, Z as FieldRule, Zt as UserLike, _ as ControllerQueryOptions, _t as FastifyHandler, a as InferAdapterDoc, at as ResolvedTenantPurge, b as ParsedQuery, bt as IRequestContext, c as TypedController, cn as BaseControllerOptions, ct as ResourceHookContext, d as PaginationResult, dt as RouteDefinition, et as OpenApiSchemas, f as IntrospectionData, ft as RouteMcpConfig, g as ArcInternalMetadata, gt as ControllerLike, h as ResourceMetadata, ht as ControllerHandler, i as ValidationResult, it as RateLimitConfig, j as ArcDecorator, k as IntrospectionPluginOptions, l as TypedRepository, lt as ResourceHooks, m as RegistryStats, mt as RouteSchemaOptions, n as ConfigError, nt as PresetHook, o as InferDocType, ot as ResourceCacheConfig, p as RegistryEntry, pt as RouteMethod, q as CrudController, qt as ApiResponse, r as ValidateOptions, rt as PresetResult, s as InferResourceDoc, st as ResourceConfig, t as RouteHandlerMethod, tt as PresetFunction, u as TypedResourceConfig, ut as ResourcePermissions, v as LookupOption, vt as IController, w as ServiceContext, x as PopulateOption, xt as RouteHandler, y as OwnershipCheck, yt as IControllerResponse } from "../index-
|
|
2
|
-
import { i as RequestScope } from "../types-
|
|
3
|
-
import { c as PermissionCheck, d as UserBase, l as PermissionContext, u as PermissionResult } from "../fields-
|
|
4
|
-
import { n as ElevationOptions, t as ElevationEvent } from "../elevation-
|
|
1
|
+
import { $ as OnTenantDeleteConfig, A as RequestIdOptions, Bt as AuthHelpers, C as RequestContext, D as HealthCheck, E as GracefulShutdownOptions, F as FastifyWithDecorators, G as ActionsMap, Gt as TokenPair, H as ActionDefinition, Ht as Authenticator, I as MiddlewareHandler, J as CrudRouteKey, Jt as ArcRequest, K as ArcFieldRule, Kt as AnyRecord, L as RequestWithExtras, M as EventsDecorator, N as FastifyRequestExtras, O as HealthOptions, P as FastifyWithAuth, Q as MiddlewareConfig, Qt as UserOrganization, S as QueryParserInterface, T as CrudRouterOptions, U as ActionEntry, Ut as AuthenticatorContext, Vt as AuthPluginOptions, W as ActionHandlerFn, Wt as JwtContext, X as EventDefinition, Xt as ObjectId, Y as CrudSchemas, Yt as JWTPayload, Z as FieldRule, Zt as UserLike, _ as ControllerQueryOptions, _t as FastifyHandler, a as InferAdapterDoc, at as ResolvedTenantPurge, b as ParsedQuery, bt as IRequestContext, c as TypedController, cn as BaseControllerOptions, ct as ResourceHookContext, d as PaginationResult, dt as RouteDefinition, et as OpenApiSchemas, f as IntrospectionData, ft as RouteMcpConfig, g as ArcInternalMetadata, gt as ControllerLike, h as ResourceMetadata, ht as ControllerHandler, i as ValidationResult, it as RateLimitConfig, j as ArcDecorator, k as IntrospectionPluginOptions, l as TypedRepository, lt as ResourceHooks, m as RegistryStats, mt as RouteSchemaOptions, n as ConfigError, nt as PresetHook, o as InferDocType, ot as ResourceCacheConfig, p as RegistryEntry, pt as RouteMethod, q as CrudController, qt as ApiResponse, r as ValidateOptions, rt as PresetResult, s as InferResourceDoc, st as ResourceConfig, t as RouteHandlerMethod, tt as PresetFunction, u as TypedResourceConfig, ut as ResourcePermissions, v as LookupOption, vt as IController, w as ServiceContext, x as PopulateOption, xt as RouteHandler, y as OwnershipCheck, yt as IControllerResponse } from "../index-C1JSBan-.mjs";
|
|
2
|
+
import { i as RequestScope } from "../types-CfapW2t8.mjs";
|
|
3
|
+
import { c as PermissionCheck, d as UserBase, l as PermissionContext, u as PermissionResult } from "../fields-DqZLp5d6.mjs";
|
|
4
|
+
import { n as ElevationOptions, t as ElevationEvent } from "../elevation-DvwtjDwu.mjs";
|
|
5
5
|
export { ActionDefinition, ActionEntry, ActionHandlerFn, ActionsMap, AnyRecord, ApiResponse, ArcDecorator, ArcFieldRule, ArcInternalMetadata, ArcRequest, AuthHelpers, AuthPluginOptions, Authenticator, AuthenticatorContext, BaseControllerOptions, ConfigError, ControllerHandler, ControllerLike, ControllerQueryOptions, CrudController, CrudRouteKey, CrudRouterOptions, CrudSchemas, ElevationEvent, ElevationOptions, EventDefinition, EventsDecorator, FastifyHandler, FastifyRequestExtras, FastifyWithAuth, FastifyWithDecorators, FieldRule, GracefulShutdownOptions, HealthCheck, HealthOptions, IController, IControllerResponse, IRequestContext, InferAdapterDoc, InferDocType, InferResourceDoc, IntrospectionData, IntrospectionPluginOptions, JWTPayload, JwtContext, LookupOption, MiddlewareConfig, MiddlewareHandler, ObjectId, OnTenantDeleteConfig, OpenApiSchemas, OwnershipCheck, PaginationResult, ParsedQuery, PermissionCheck, PermissionContext, PermissionResult, PopulateOption, PresetFunction, PresetHook, PresetResult, QueryParserInterface, RateLimitConfig, RegistryEntry, RegistryStats, RequestContext, RequestIdOptions, RequestScope, RequestWithExtras, ResolvedTenantPurge, ResourceCacheConfig, ResourceConfig, ResourceHookContext, ResourceHooks, ResourceMetadata, ResourcePermissions, RouteDefinition, RouteHandler, RouteHandlerMethod, RouteMcpConfig, RouteMethod, RouteSchemaOptions, ServiceContext, TokenPair, TypedController, TypedRepository, TypedResourceConfig, UserBase, UserLike, UserOrganization, ValidateOptions, ValidationResult };
|
package/dist/types/storage.d.mts
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
import { a as StorageReadResult, i as StorageReadRange, n as StorageContext, o as StorageUploadInput, r as StorageFile, t as Storage } from "../storage-
|
|
1
|
+
import { a as StorageReadResult, i as StorageReadRange, n as StorageContext, o as StorageUploadInput, r as StorageFile, t as Storage } from "../storage-HcAgP5Ni.mjs";
|
|
2
2
|
export { Storage, StorageContext, StorageFile, StorageReadRange, StorageReadResult, StorageUploadInput };
|
|
@@ -1,13 +1,13 @@
|
|
|
1
|
-
import { r as CacheStore } from "./interface-
|
|
2
|
-
import { Ht as Authenticator } from "./index-
|
|
3
|
-
import { n as ElevationOptions } from "./elevation-
|
|
4
|
-
import { a as EventTransport } from "./EventTransport-
|
|
5
|
-
import { t as ExternalOpenApiPaths } from "./externalPaths-
|
|
6
|
-
import { r as QueryCachePluginOptions } from "./queryCachePlugin-
|
|
7
|
-
import { t as EventPluginOptions } from "./eventPlugin-
|
|
8
|
-
import { _ as HealthOptions, f as CachingOptions, l as SSEOptions, o as MetricsOptions, t as VersioningOptions } from "./versioning-
|
|
9
|
-
import { t as ErrorHandlerOptions } from "./errorHandler-
|
|
10
|
-
import { r as IdempotencyStore } from "./interface-
|
|
1
|
+
import { r as CacheStore } from "./interface-BVkZsvEe.mjs";
|
|
2
|
+
import { Ht as Authenticator } from "./index-C1JSBan-.mjs";
|
|
3
|
+
import { n as ElevationOptions } from "./elevation-DvwtjDwu.mjs";
|
|
4
|
+
import { a as EventTransport } from "./EventTransport-B1GFiYUy.mjs";
|
|
5
|
+
import { t as ExternalOpenApiPaths } from "./externalPaths-ClykOQqa.mjs";
|
|
6
|
+
import { r as QueryCachePluginOptions } from "./queryCachePlugin-Do4P_WBC.mjs";
|
|
7
|
+
import { t as EventPluginOptions } from "./eventPlugin-DjEx_J3j.mjs";
|
|
8
|
+
import { _ as HealthOptions, f as CachingOptions, l as SSEOptions, o as MetricsOptions, t as VersioningOptions } from "./versioning-5H4930tj.mjs";
|
|
9
|
+
import { t as ErrorHandlerOptions } from "./errorHandler-DuqTqeqn.mjs";
|
|
10
|
+
import { r as IdempotencyStore } from "./interface-DoE6UoC2.mjs";
|
|
11
11
|
import { FastifyInstance, FastifyPluginAsync, FastifyReply, FastifyRequest, FastifyServerOptions } from "fastify";
|
|
12
12
|
|
|
13
13
|
//#region src/factory/loadResources.d.ts
|
|
@@ -203,6 +203,29 @@ interface LoadResourcesOptions<TContext = unknown> {
|
|
|
203
203
|
*/
|
|
204
204
|
declare function loadResources<TContext = unknown>(dir: string, options?: LoadResourcesOptions<TContext>): Promise<ResourceLike[]>;
|
|
205
205
|
//#endregion
|
|
206
|
+
//#region src/testing/preloadResources.d.ts
|
|
207
|
+
/** Eager glob result: `{ '/path/to/file.ts': resourceModule }` */
|
|
208
|
+
type EagerGlobResult = Record<string, unknown>;
|
|
209
|
+
/** Lazy glob result: `{ '/path/to/file.ts': () => Promise<unknown> }` */
|
|
210
|
+
type LazyGlobResult = Record<string, () => Promise<unknown>>;
|
|
211
|
+
/**
|
|
212
|
+
* Normalize an eager `import.meta.glob` result into a `ResourceLike[]`.
|
|
213
|
+
*
|
|
214
|
+
* Accepts either:
|
|
215
|
+
* - `{ import: 'default' }` form: values are the resource directly
|
|
216
|
+
* - default form: values are the full module — picks first export with `toPlugin()`
|
|
217
|
+
*
|
|
218
|
+
* Throws if any module doesn't yield a valid `ResourceLike`.
|
|
219
|
+
*/
|
|
220
|
+
declare function preloadResources(globResult: EagerGlobResult): ResourceLike[];
|
|
221
|
+
/**
|
|
222
|
+
* Normalize a lazy `import.meta.glob` result into a `Promise<ResourceLike[]>`.
|
|
223
|
+
*
|
|
224
|
+
* Use this when resources depend on prior bootstrap (e.g., engine init) and
|
|
225
|
+
* cannot be evaluated at import time of the preload file.
|
|
226
|
+
*/
|
|
227
|
+
declare function preloadResourcesAsync(globResult: LazyGlobResult): Promise<ResourceLike[]>;
|
|
228
|
+
//#endregion
|
|
206
229
|
//#region src/factory/types.d.ts
|
|
207
230
|
type CorsOptions = Record<string, unknown> & {
|
|
208
231
|
origin?: unknown;
|
|
@@ -1041,4 +1064,4 @@ interface RawBodyOptions {
|
|
|
1041
1064
|
runFirst?: boolean;
|
|
1042
1065
|
}
|
|
1043
1066
|
//#endregion
|
|
1044
|
-
export { CustomPluginAuthOption as a, RawBodyOptions as c,
|
|
1067
|
+
export { CustomPluginAuthOption as a, RawBodyOptions as c, preloadResourcesAsync as d, LoadResourcesOptions as f, loadResources as h, CustomAuthenticatorOption as i, UnderPressureOptions as l, ResourceModule as m, BetterAuthOption as n, JwtAuthOption as o, ResourceLike as p, CreateAppOptions as r, MultipartOptions as s, AuthOption as t, preloadResources as u };
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { V as ResourceDefinition } from "./index-
|
|
1
|
+
import { V as ResourceDefinition } from "./index-C1JSBan-.mjs";
|
|
2
2
|
import { z } from "zod";
|
|
3
3
|
|
|
4
4
|
//#region src/integrations/mcp/types.d.ts
|
|
@@ -93,6 +93,17 @@ interface ToolDefinition<TSession = McpAuthResult> {
|
|
|
93
93
|
outputSchema?: Record<string, z.ZodTypeAny>;
|
|
94
94
|
annotations?: ToolAnnotations;
|
|
95
95
|
handler: (input: Record<string, unknown>, ctx: ToolContext<TSession>) => Promise<CallToolResult>;
|
|
96
|
+
/**
|
|
97
|
+
* Origin label used by collision diagnostics — names BOTH the resource
|
|
98
|
+
* and the surface that produced this tool (`'crud:post:list'`,
|
|
99
|
+
* `'action:post:approve'`, `'route:post:GET /export'`,
|
|
100
|
+
* `'preset:softDelete:post:restore'`). When two tools share a name,
|
|
101
|
+
* `createMcpServer()` prints both labels so the host can see which
|
|
102
|
+
* surfaces collided without spelunking through the MCP SDK's stack
|
|
103
|
+
* trace. Optional — hand-written tools without a `source` simply
|
|
104
|
+
* appear as `'(unknown)'` in collision messages.
|
|
105
|
+
*/
|
|
106
|
+
source?: string;
|
|
96
107
|
}
|
|
97
108
|
/** Output of definePrompt() — plain data, not yet registered */
|
|
98
109
|
interface PromptDefinition {
|