@classytic/arc 2.22.0 → 2.23.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/{BaseController-C6FlRWxF.mjs → BaseController-BQ8KNvWC.mjs} +3 -2
- package/dist/{ResourceRegistry-7RnrEWNP.mjs → ResourceRegistry-y9Hnypus.mjs} +1 -0
- package/dist/{actionPermissions-ze70xXJi.mjs → actionPermissions-lszoJle1.mjs} +1 -1
- package/dist/applyPermissionResult-Tw4iXtlf.mjs +264 -0
- package/dist/audit/index.d.mts +1 -1
- package/dist/auth/index.d.mts +1 -1
- package/dist/auth/index.mjs +2 -2
- 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/init.mjs +1 -1
- package/dist/cli/commands/introspect.mjs +1 -1
- package/dist/cli/index.mjs +1 -1
- package/dist/core/index.d.mts +2 -2
- package/dist/core/index.mjs +3 -3
- package/dist/{core-b6fgFgnd.mjs → core-DQ6rDTyH.mjs} +10 -9
- package/dist/{createActionRouter-h1cEEaEX.mjs → createActionRouter-BuOv-m4Y.mjs} +3 -3
- package/dist/{createAggregationRouter-B1C0kvTL.mjs → createAggregationRouter-CgJv_BU_.mjs} +1 -1
- package/dist/{createApp-Ci7yG5gv.mjs → createApp-CjOeWJ9_.mjs} +11 -2
- package/dist/docs/index.d.mts +1 -1
- package/dist/docs/index.mjs +1 -1
- package/dist/{errorHandler-BTzXSwIg.mjs → errorHandler-Bq2OeEQL.mjs} +10 -7
- package/dist/events/index.mjs +5 -2
- package/dist/factory/index.d.mts +20 -4
- package/dist/factory/index.mjs +31 -11
- package/dist/{fastify-augmentation-9_fuetB4.d.mts → fastify-augmentation-g_f9omSc.d.mts} +1 -1
- package/dist/hooks/index.d.mts +1 -1
- package/dist/idempotency/index.d.mts +7 -0
- package/dist/idempotency/index.mjs +5 -17
- package/dist/{index-DbtgCOCj.d.mts → index-BBsvQt7U.d.mts} +26 -4
- package/dist/{index-BEu4wjbL.d.mts → index-Ba10fdKp.d.mts} +11 -2
- package/dist/{index-D8MKYGDa.d.mts → index-CC5ruDGh.d.mts} +2 -2
- package/dist/{index-ogIHvOjo.d.mts → index-wMoWBLkn.d.mts} +67 -1
- package/dist/index.d.mts +4 -4
- package/dist/index.mjs +6 -5
- package/dist/{init-DleK3p9f.mjs → init-CjO4p_q1.mjs} +5 -2
- package/dist/integrations/event-gateway.mjs +1 -1
- package/dist/integrations/index.d.mts +1 -1
- package/dist/integrations/mcp/index.d.mts +66 -3
- package/dist/integrations/mcp/index.mjs +1 -1
- package/dist/integrations/mcp/testing.d.mts +1 -1
- package/dist/integrations/mcp/testing.mjs +1 -1
- package/dist/integrations/webhooks.d.mts +123 -3
- package/dist/integrations/webhooks.mjs +160 -5
- package/dist/interface-n5hpKg1d.mjs +16 -0
- package/dist/{loadResourcesFromEntry-OMCvIEE7.mjs → loadResourcesFromEntry-kvGT6EcO.mjs} +1 -1
- package/dist/{mcpPlugin-ByA2tf6D.mjs → mcpPlugin-BtzIL_kM.mjs} +308 -39
- package/dist/middleware/index.d.mts +1 -1
- package/dist/{openapi-BRpMZD7C.mjs → openapi-jSDx-VDv.mjs} +3 -3
- package/dist/permissions/index.d.mts +2 -2
- package/dist/permissions/index.mjs +3 -2
- package/dist/{permissions-BJMuKMl7.mjs → permissions-tbxFHxjZ.mjs} +77 -264
- package/dist/pipeline/index.d.mts +1 -1
- package/dist/plugins/index.d.mts +6 -6
- package/dist/plugins/index.mjs +5 -4
- package/dist/plugins/tracing-entry.mjs +1 -1
- package/dist/presets/filesUpload.d.mts +1 -1
- package/dist/presets/filesUpload.mjs +1 -1
- 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 +1 -1
- package/dist/presets/search.mjs +1 -1
- package/dist/{presets-gw0ciIUD.mjs → presets-BOe4Ugkl.mjs} +1 -1
- package/dist/realtime-I8OrfnMR.mjs +375 -0
- package/dist/{registerRoute-xHc42fwB.mjs → registerRoute-CWO6mdYe.mjs} +1 -1
- package/dist/registry/index.d.mts +2 -2
- package/dist/registry/index.mjs +1 -1
- package/dist/{schemaConverter-Ds5reSGs.mjs → schemaConverter-DJvodiHj.mjs} +4 -1
- package/dist/{schemaIR-B-6SPe7g.mjs → schemaIR-Ca7ySNPx.mjs} +1 -1
- package/dist/scope/index.mjs +1 -1
- package/dist/scope-LONQ3mfZ.mjs +37 -0
- package/dist/simpleEqualityMatcher-Cg05HPsn.mjs +68 -0
- package/dist/{sse-CfUjwjDk.mjs → sse-0u4P4-OI.mjs} +7 -30
- package/dist/sseStream-BQ7Qaynp.mjs +57 -0
- package/dist/testing/index.d.mts +2 -2
- package/dist/testing/index.mjs +2 -2
- package/dist/types/index.d.mts +1 -1
- package/dist/{types-Bc42CUyk.d.mts → types-5OuqeTDX.d.mts} +37 -2
- package/dist/utils/index.d.mts +62 -2
- package/dist/utils/index.mjs +5 -3
- package/dist/{utils-FhJ88TJE.mjs → utils-CZyeRXTh.mjs} +21 -69
- package/dist/{versioning-DLwklPlG.d.mts → versioning-D6PvCwpq.d.mts} +43 -4
- package/package.json +64 -4
- package/skills/arc/references/production.md +734 -696
- package/dist/scope-k15RXfcq.mjs +0 -64
|
@@ -2,9 +2,10 @@ import { h as SYSTEM_FIELDS, o as DEFAULT_TENANT_FIELD } from "./constants-DNre0
|
|
|
2
2
|
import { arcLog } from "./logger/index.mjs";
|
|
3
3
|
import { d as createDomainError, f as createError, i as NotFoundError, r as ForbiddenError } from "./errors-DkO8Nk6-.mjs";
|
|
4
4
|
import { b as isElevated, c as getOrgId, n as PUBLIC_SCOPE, x as isMember } from "./types-bHblZ9YN.mjs";
|
|
5
|
-
import {
|
|
6
|
-
import {
|
|
5
|
+
import { t as simpleEqualityMatcher } from "./simpleEqualityMatcher-Cg05HPsn.mjs";
|
|
6
|
+
import { a as applyFieldWritePermissions, s as resolveEffectiveRoles } from "./applyPermissionResult-Tw4iXtlf.mjs";
|
|
7
7
|
import { t as getUserRoles } from "./types-XTApa8b6.mjs";
|
|
8
|
+
import { _ as ArcQueryParser, r as scheduleBackground, t as getUserId } from "./utils-CZyeRXTh.mjs";
|
|
8
9
|
import { t as buildQueryKey } from "./keys-Cd316u_y.mjs";
|
|
9
10
|
//#region src/core/AccessControl.ts
|
|
10
11
|
const log = arcLog("access-control");
|
|
@@ -71,6 +71,7 @@ var ResourceRegistry = class {
|
|
|
71
71
|
disabledRoutes: resource.disabledRoutes ? [...resource.disabledRoutes] : void 0,
|
|
72
72
|
openApiSchemas: options.openApiSchemas,
|
|
73
73
|
fieldPermissions: extractFieldPermissions(resource.fields),
|
|
74
|
+
fields: resource.fields && Object.keys(resource.fields).length > 0 ? resource.fields : void 0,
|
|
74
75
|
pipelineSteps: extractPipelineSteps(resource.pipe),
|
|
75
76
|
rateLimit: resource.rateLimit,
|
|
76
77
|
audit: resource.audit,
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { b as isElevated, c as getOrgId, g as getUserId, n as PUBLIC_SCOPE, x as isMember } from "./types-bHblZ9YN.mjs";
|
|
2
|
-
import {
|
|
2
|
+
import { i as applyFieldReadPermissions, s as resolveEffectiveRoles } from "./applyPermissionResult-Tw4iXtlf.mjs";
|
|
3
3
|
import { t as getUserRoles } from "./types-XTApa8b6.mjs";
|
|
4
4
|
import { t as executePipeline } from "./pipe-BOLmGIxa.mjs";
|
|
5
5
|
import { isPaginatedResult, toCanonicalList } from "@classytic/repo-core/pagination";
|
|
@@ -0,0 +1,264 @@
|
|
|
1
|
+
//#region src/permissions/fields.ts
|
|
2
|
+
/**
|
|
3
|
+
* Field-Level Permissions
|
|
4
|
+
*
|
|
5
|
+
* Control field visibility and writability per role.
|
|
6
|
+
* Integrated into the response path (read) and sanitization path (write).
|
|
7
|
+
*
|
|
8
|
+
* @example
|
|
9
|
+
* ```typescript
|
|
10
|
+
* import { fields, defineResource } from '@classytic/arc';
|
|
11
|
+
*
|
|
12
|
+
* const userResource = defineResource({
|
|
13
|
+
* name: 'user',
|
|
14
|
+
* adapter: userAdapter,
|
|
15
|
+
* fields: {
|
|
16
|
+
* salary: fields.visibleTo(['admin', 'hr']),
|
|
17
|
+
* internalNotes: fields.writableBy(['admin']),
|
|
18
|
+
* email: fields.redactFor(['viewer']),
|
|
19
|
+
* password: fields.hidden(),
|
|
20
|
+
* },
|
|
21
|
+
* });
|
|
22
|
+
* ```
|
|
23
|
+
*/
|
|
24
|
+
/** Type guard for Mongoose-like documents with toObject() */
|
|
25
|
+
function isMongooseDoc(obj) {
|
|
26
|
+
return !!obj && typeof obj === "object" && "toObject" in obj && typeof obj.toObject === "function";
|
|
27
|
+
}
|
|
28
|
+
const fields = {
|
|
29
|
+
/**
|
|
30
|
+
* Field is never included in responses. Not writable via API.
|
|
31
|
+
*
|
|
32
|
+
* @example
|
|
33
|
+
* ```typescript
|
|
34
|
+
* fields: { password: fields.hidden() }
|
|
35
|
+
* ```
|
|
36
|
+
*/
|
|
37
|
+
hidden() {
|
|
38
|
+
return { _type: "hidden" };
|
|
39
|
+
},
|
|
40
|
+
/**
|
|
41
|
+
* Field is only visible to users with specified roles.
|
|
42
|
+
* Other users don't see the field at all.
|
|
43
|
+
*
|
|
44
|
+
* @example
|
|
45
|
+
* ```typescript
|
|
46
|
+
* fields: { salary: fields.visibleTo(['admin', 'hr']) }
|
|
47
|
+
* ```
|
|
48
|
+
*/
|
|
49
|
+
visibleTo(roles) {
|
|
50
|
+
return {
|
|
51
|
+
_type: "visibleTo",
|
|
52
|
+
roles
|
|
53
|
+
};
|
|
54
|
+
},
|
|
55
|
+
/**
|
|
56
|
+
* Field is only writable by users with specified roles.
|
|
57
|
+
* All users can still read the field. Users without the role
|
|
58
|
+
* have the field silently stripped from write operations.
|
|
59
|
+
*
|
|
60
|
+
* @example
|
|
61
|
+
* ```typescript
|
|
62
|
+
* fields: { role: fields.writableBy(['admin']) }
|
|
63
|
+
* ```
|
|
64
|
+
*/
|
|
65
|
+
writableBy(roles) {
|
|
66
|
+
return {
|
|
67
|
+
_type: "writableBy",
|
|
68
|
+
roles
|
|
69
|
+
};
|
|
70
|
+
},
|
|
71
|
+
/**
|
|
72
|
+
* Field is redacted (replaced with a placeholder) for specified roles.
|
|
73
|
+
* Other users see the real value.
|
|
74
|
+
*
|
|
75
|
+
* @param roles - Roles that see the redacted value
|
|
76
|
+
* @param redactValue - Replacement value (default: '***')
|
|
77
|
+
*
|
|
78
|
+
* @example
|
|
79
|
+
* ```typescript
|
|
80
|
+
* fields: {
|
|
81
|
+
* email: fields.redactFor(['viewer']),
|
|
82
|
+
* ssn: fields.redactFor(['basic'], '***-**-****'),
|
|
83
|
+
* }
|
|
84
|
+
* ```
|
|
85
|
+
*/
|
|
86
|
+
redactFor(roles, redactValue = "***") {
|
|
87
|
+
return {
|
|
88
|
+
_type: "redactFor",
|
|
89
|
+
roles,
|
|
90
|
+
redactValue
|
|
91
|
+
};
|
|
92
|
+
}
|
|
93
|
+
};
|
|
94
|
+
/**
|
|
95
|
+
* Apply field-level READ permissions to a response object.
|
|
96
|
+
* Strips hidden fields, enforces visibility, and applies redaction.
|
|
97
|
+
*
|
|
98
|
+
* @param data - The response object (mutated in place for performance)
|
|
99
|
+
* @param fieldPermissions - Field permission map from resource config
|
|
100
|
+
* @param userRoles - Current user's roles (empty array for unauthenticated)
|
|
101
|
+
* @returns The filtered object
|
|
102
|
+
*/
|
|
103
|
+
function applyFieldReadPermissions(data, fieldPermissions, userRoles) {
|
|
104
|
+
if (!data || typeof data !== "object") return data;
|
|
105
|
+
const result = { ...isMongooseDoc(data) ? data.toObject() : data };
|
|
106
|
+
for (const [field, perm] of Object.entries(fieldPermissions)) switch (perm._type) {
|
|
107
|
+
case "hidden":
|
|
108
|
+
delete result[field];
|
|
109
|
+
break;
|
|
110
|
+
case "visibleTo":
|
|
111
|
+
if (!perm.roles?.some((r) => userRoles.includes(r))) delete result[field];
|
|
112
|
+
break;
|
|
113
|
+
case "redactFor":
|
|
114
|
+
if (perm.roles?.some((r) => userRoles.includes(r))) result[field] = perm.redactValue ?? "***";
|
|
115
|
+
break;
|
|
116
|
+
case "writableBy": break;
|
|
117
|
+
}
|
|
118
|
+
return result;
|
|
119
|
+
}
|
|
120
|
+
/**
|
|
121
|
+
* Apply field-level WRITE permissions to request body.
|
|
122
|
+
*
|
|
123
|
+
* Returns both the filtered body and the list of denied fields. Callers are
|
|
124
|
+
* expected to reject the request when `deniedFields.length > 0` — silently
|
|
125
|
+
* stripping fields hides misconfigurations and real attacks. See
|
|
126
|
+
* `BodySanitizer` for the default policy.
|
|
127
|
+
*
|
|
128
|
+
* @param body - The request body (returns a new filtered copy)
|
|
129
|
+
* @param fieldPermissions - Field permission map from resource config
|
|
130
|
+
* @param userRoles - Current user's roles
|
|
131
|
+
*/
|
|
132
|
+
function applyFieldWritePermissions(body, fieldPermissions, userRoles) {
|
|
133
|
+
const result = { ...body };
|
|
134
|
+
const deniedFields = [];
|
|
135
|
+
for (const [field, perm] of Object.entries(fieldPermissions)) switch (perm._type) {
|
|
136
|
+
case "hidden":
|
|
137
|
+
if (field in result) {
|
|
138
|
+
deniedFields.push(field);
|
|
139
|
+
delete result[field];
|
|
140
|
+
}
|
|
141
|
+
break;
|
|
142
|
+
case "writableBy":
|
|
143
|
+
if (field in result && !perm.roles?.some((r) => userRoles.includes(r))) {
|
|
144
|
+
deniedFields.push(field);
|
|
145
|
+
delete result[field];
|
|
146
|
+
}
|
|
147
|
+
break;
|
|
148
|
+
}
|
|
149
|
+
return {
|
|
150
|
+
body: result,
|
|
151
|
+
deniedFields
|
|
152
|
+
};
|
|
153
|
+
}
|
|
154
|
+
/**
|
|
155
|
+
* Resolve effective roles by merging global user roles with org-level roles.
|
|
156
|
+
*
|
|
157
|
+
* Global roles come from `req.user.role` (normalized via getUserRoles()).
|
|
158
|
+
* Org roles come from `req.context.orgRoles` (set by BA adapter's org bridge).
|
|
159
|
+
*
|
|
160
|
+
* When no org context exists, returns global roles only — backward compatible.
|
|
161
|
+
*/
|
|
162
|
+
function resolveEffectiveRoles(userRoles, orgRoles) {
|
|
163
|
+
if (orgRoles.length === 0) return [...userRoles];
|
|
164
|
+
if (userRoles.length === 0) return [...orgRoles];
|
|
165
|
+
return [.../* @__PURE__ */ new Set([...userRoles, ...orgRoles])];
|
|
166
|
+
}
|
|
167
|
+
//#endregion
|
|
168
|
+
//#region src/permissions/applyPermissionResult.ts
|
|
169
|
+
/**
|
|
170
|
+
* Normalize a permission check return value (`boolean | PermissionResult`)
|
|
171
|
+
* into a concrete `PermissionResult`. This is the only place in Arc that
|
|
172
|
+
* promotes booleans to results — keeps the type narrowing honest everywhere.
|
|
173
|
+
*/
|
|
174
|
+
function normalizePermissionResult(result) {
|
|
175
|
+
if (typeof result === "boolean") return { granted: result };
|
|
176
|
+
return result;
|
|
177
|
+
}
|
|
178
|
+
/**
|
|
179
|
+
* Apply a granted `PermissionResult` to a Fastify request — merges row-level
|
|
180
|
+
* filters into `_policyFilters` and conditionally installs the scope.
|
|
181
|
+
*
|
|
182
|
+
* **Scope install rule:** only writes `scope` when the current request scope
|
|
183
|
+
* is absent or `public`. This prevents downgrading an already-authenticated
|
|
184
|
+
* request (e.g. Better Auth set `member`, then a permission check returns a
|
|
185
|
+
* narrower `service` scope — the original `member` wins because it came from
|
|
186
|
+
* a more authoritative source).
|
|
187
|
+
*
|
|
188
|
+
* Safe to call with a non-granted result — it simply no-ops. Callers should
|
|
189
|
+
* still check `result.granted` and send an error response before reaching here,
|
|
190
|
+
* but this function tolerates the misuse defensively.
|
|
191
|
+
*/
|
|
192
|
+
function applyPermissionResult(result, request) {
|
|
193
|
+
if (!result.granted) return;
|
|
194
|
+
if (result.filters) request._policyFilters = {
|
|
195
|
+
...request._policyFilters ?? {},
|
|
196
|
+
...result.filters
|
|
197
|
+
};
|
|
198
|
+
if (result.scope) {
|
|
199
|
+
const current = request.scope;
|
|
200
|
+
if (!current || current.kind === "public") request.scope = result.scope;
|
|
201
|
+
}
|
|
202
|
+
}
|
|
203
|
+
/**
|
|
204
|
+
* Max length of a `PermissionResult.reason` string before we fall back to the
|
|
205
|
+
* generic default message. Upstream checks can return arbitrary strings; we
|
|
206
|
+
* clamp to prevent accidental leakage of internal diagnostics or oversized
|
|
207
|
+
* payloads via the 4xx response body.
|
|
208
|
+
*/
|
|
209
|
+
const MAX_DENIAL_REASON_LENGTH = 100;
|
|
210
|
+
/**
|
|
211
|
+
* End-to-end evaluator: runs the permission check, catches throws, normalizes
|
|
212
|
+
* the result, sends a 401/403 response on denial, and applies side-effects on
|
|
213
|
+
* grant. Returns `true` if the caller should continue, `false` if a response
|
|
214
|
+
* has been sent and the caller should return.
|
|
215
|
+
*
|
|
216
|
+
* This is the single source of truth for the 5-step sequence shared by the
|
|
217
|
+
* CRUD router, action router, and MCP tool handlers:
|
|
218
|
+
*
|
|
219
|
+
* 1. `try { await check(ctx) } catch { reply 403 }`
|
|
220
|
+
* 2. `normalizePermissionResult(result)`
|
|
221
|
+
* 3. If denied → 401 (no user) or 403 (user) with clamped reason
|
|
222
|
+
* 4. If granted → `applyPermissionResult` (filters + scope)
|
|
223
|
+
* 5. Return true/false so the caller knows whether to keep going
|
|
224
|
+
*
|
|
225
|
+
* Context construction, pre-check auth gating, and success-path handler
|
|
226
|
+
* invocation stay at the callsite — those are genuinely different per router
|
|
227
|
+
* and don't belong here.
|
|
228
|
+
*
|
|
229
|
+
* @returns `true` if authorized (caller continues), `false` if a response was sent
|
|
230
|
+
*/
|
|
231
|
+
async function evaluateAndApplyPermission(check, context, request, reply, opts) {
|
|
232
|
+
let result;
|
|
233
|
+
try {
|
|
234
|
+
result = await check(context);
|
|
235
|
+
} catch (err) {
|
|
236
|
+
request.log?.warn?.({
|
|
237
|
+
err,
|
|
238
|
+
resource: context.resource,
|
|
239
|
+
action: context.action
|
|
240
|
+
}, "Permission check threw");
|
|
241
|
+
reply.code(403).send({
|
|
242
|
+
code: "arc.forbidden",
|
|
243
|
+
message: "Permission denied",
|
|
244
|
+
status: 403
|
|
245
|
+
});
|
|
246
|
+
return false;
|
|
247
|
+
}
|
|
248
|
+
const permResult = normalizePermissionResult(result);
|
|
249
|
+
if (!permResult.granted) {
|
|
250
|
+
const defaultMsg = opts?.defaultDenialMessage?.(context.user) ?? (context.user ? "Permission denied" : "Authentication required");
|
|
251
|
+
const reason = permResult.reason && permResult.reason.length <= MAX_DENIAL_REASON_LENGTH ? permResult.reason : defaultMsg;
|
|
252
|
+
const status = context.user ? 403 : 401;
|
|
253
|
+
reply.code(status).send({
|
|
254
|
+
code: context.user ? "arc.forbidden" : "arc.unauthorized",
|
|
255
|
+
message: reason,
|
|
256
|
+
status
|
|
257
|
+
});
|
|
258
|
+
return false;
|
|
259
|
+
}
|
|
260
|
+
applyPermissionResult(permResult, request);
|
|
261
|
+
return true;
|
|
262
|
+
}
|
|
263
|
+
//#endregion
|
|
264
|
+
export { applyFieldWritePermissions as a, applyFieldReadPermissions as i, evaluateAndApplyPermission as n, fields as o, normalizePermissionResult as r, resolveEffectiveRoles as s, applyPermissionResult as t };
|
package/dist/audit/index.d.mts
CHANGED
package/dist/auth/index.d.mts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { Ht as
|
|
1
|
+
import { Ht as AuthHelpers, Ut as AuthPluginOptions } from "../index-BBsvQt7U.mjs";
|
|
2
2
|
import { t as PermissionCheck } from "../types-Csxga7d3.mjs";
|
|
3
3
|
import { t as ExternalOpenApiPaths } from "../externalPaths-BE6wFpYv.mjs";
|
|
4
4
|
import { a as SessionManagerOptions, c as createSessionManager, i as SessionData, n as MemorySessionStoreOptions, o as SessionManagerResult, r as SessionCookieOptions, s as SessionStore, t as MemorySessionStore } from "../sessionManager-BHOM3N_0.mjs";
|
package/dist/auth/index.mjs
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { d as createDomainError, l as UnauthorizedError, p as isArcError, t as ArcError } from "../errors-DkO8Nk6-.mjs";
|
|
2
|
-
import { h as requireOrgRole, m as requireOrgMembership, v as requireTeamMembership } from "../permissions-BJMuKMl7.mjs";
|
|
3
2
|
import { n as normalizeRoles, t as getUserRoles } from "../types-XTApa8b6.mjs";
|
|
4
|
-
import {
|
|
3
|
+
import { _ as requireOrgMembership, v as requireOrgRole, x as requireTeamMembership } from "../permissions-tbxFHxjZ.mjs";
|
|
4
|
+
import { o as toJsonSchema } from "../schemaConverter-DJvodiHj.mjs";
|
|
5
5
|
import { createHmac, randomUUID, timingSafeEqual } from "node:crypto";
|
|
6
6
|
import fp from "fastify-plugin";
|
|
7
7
|
//#region src/auth/authPlugin.ts
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { Nt as RateLimitConfig, V as ResourceDefinition, et as RouteSchemaOptions } from "../../index-
|
|
1
|
+
import { Nt as RateLimitConfig, V as ResourceDefinition, et as RouteSchemaOptions } from "../../index-BBsvQt7U.mjs";
|
|
2
2
|
//#region src/cli/commands/describe.d.ts
|
|
3
3
|
interface DescribedResource {
|
|
4
4
|
name: string;
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { t as CRUD_OPERATIONS } from "../../constants-DNre0wRg.mjs";
|
|
2
|
-
import { n as stringifyMeasureMap } from "../../ResourceRegistry-
|
|
2
|
+
import { n as stringifyMeasureMap } from "../../ResourceRegistry-y9Hnypus.mjs";
|
|
3
3
|
import { resolve } from "node:path";
|
|
4
4
|
import { pathToFileURL } from "node:url";
|
|
5
5
|
//#region src/cli/commands/describe.ts
|
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import { t as buildOpenApiSpec } from "../../openapi-
|
|
2
|
-
import { t as loadResourcesFromEntry } from "../../loadResourcesFromEntry-
|
|
1
|
+
import { t as buildOpenApiSpec } from "../../openapi-jSDx-VDv.mjs";
|
|
2
|
+
import { t as loadResourcesFromEntry } from "../../loadResourcesFromEntry-kvGT6EcO.mjs";
|
|
3
3
|
import { dirname, resolve } from "node:path";
|
|
4
4
|
import { mkdirSync, writeFileSync } from "node:fs";
|
|
5
5
|
//#region src/cli/commands/docs.ts
|
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
import { t as init } from "../../init-
|
|
1
|
+
import { t as init } from "../../init-CjO4p_q1.mjs";
|
|
2
2
|
export { init };
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { t as loadResourcesFromEntry } from "../../loadResourcesFromEntry-
|
|
1
|
+
import { t as loadResourcesFromEntry } from "../../loadResourcesFromEntry-kvGT6EcO.mjs";
|
|
2
2
|
//#region src/cli/commands/introspect.ts
|
|
3
3
|
function describePermission(value) {
|
|
4
4
|
if (!value) return "none";
|
package/dist/cli/index.mjs
CHANGED
|
@@ -2,6 +2,6 @@ import { describe } from "./commands/describe.mjs";
|
|
|
2
2
|
import { exportDocs } from "./commands/docs.mjs";
|
|
3
3
|
import { doctor } from "./commands/doctor.mjs";
|
|
4
4
|
import { t as generate } from "../generate-usRSdQmP.mjs";
|
|
5
|
-
import { t as init } from "../init-
|
|
5
|
+
import { t as init } from "../init-CjO4p_q1.mjs";
|
|
6
6
|
import { introspect } from "./commands/introspect.mjs";
|
|
7
7
|
export { describe, doctor, exportDocs, generate, init, introspect };
|
package/dist/core/index.d.mts
CHANGED
|
@@ -1,3 +1,3 @@
|
|
|
1
|
-
import { B as defineResource, Bn as
|
|
2
|
-
import { A as HOOK_OPERATIONS, B as SYSTEM_FIELDS, C as CrudOperation, D as DEFAULT_SORT, E as DEFAULT_MAX_LIMIT, F as MAX_REGEX_LENGTH, I as MAX_SEARCH_LENGTH, L as MUTATION_OPERATIONS, M as HookOperation, N as HookPhase, O as DEFAULT_TENANT_FIELD, P as MAX_FILTER_DEPTH, R as MutationOperation, S as CRUD_OPERATIONS, T as DEFAULT_LIMIT, _ as DefineActionConfig, a as createRequestContext, b as createPermissionMiddleware, c as sendControllerResponse, d as getEntityQuery, f as defineResourceVariants, g as mergeResourceConfig, h as SeamedResourceConfig, i as createFastifyHandler, j as HOOK_PHASES, k as DEFAULT_UPDATE_METHOD, l as getEntityId, m as ResourceSeams, n as isFieldReadable, o as getControllerContext, p as AdapterLike, r as createCrudHandlers, s as getControllerScope, t as collectReadBlockedFields, u as getEntityIdField, v as defineAction, w as DEFAULT_ID_FIELD, x as defineAggregation, y as createCrudRouter, z as RESERVED_QUERY_PARAMS } from "../index-
|
|
1
|
+
import { B as defineResource, Bn as BodySanitizer, In as BaseControllerOptions, Ln as ControllerConfigurableOptions, Rn as ControllerConstructionOptions, V as ResourceDefinition, Vn as BodySanitizerConfig, an as TreeMixin, cn as BulkExt, ct as AggMeasureShorthand, dn as QueryResolver, dt as AggregationDateRangeRequirement, fn as QueryResolverConfig, ft as AggregationIndexHint, gt as AggregationsMap, ht as AggregationRateLimit, in as TreeExt, ln as BulkMixin, lt as AggregationCacheConfig, mn as AccessControlConfig, mt as AggregationMaterializedResult, nn as SoftDeleteExt, on as SlugExt, pn as AccessControl, pt as AggregationMaterializedContext, rn as SoftDeleteMixin, sn as SlugMixin, st as AggMeasureInput, tn as BaseController, un as BaseCrudController, ut as AggregationConfig, zn as ListResult } from "../index-BBsvQt7U.mjs";
|
|
2
|
+
import { A as HOOK_OPERATIONS, B as SYSTEM_FIELDS, C as CrudOperation, D as DEFAULT_SORT, E as DEFAULT_MAX_LIMIT, F as MAX_REGEX_LENGTH, I as MAX_SEARCH_LENGTH, L as MUTATION_OPERATIONS, M as HookOperation, N as HookPhase, O as DEFAULT_TENANT_FIELD, P as MAX_FILTER_DEPTH, R as MutationOperation, S as CRUD_OPERATIONS, T as DEFAULT_LIMIT, _ as DefineActionConfig, a as createRequestContext, b as createPermissionMiddleware, c as sendControllerResponse, d as getEntityQuery, f as defineResourceVariants, g as mergeResourceConfig, h as SeamedResourceConfig, i as createFastifyHandler, j as HOOK_PHASES, k as DEFAULT_UPDATE_METHOD, l as getEntityId, m as ResourceSeams, n as isFieldReadable, o as getControllerContext, p as AdapterLike, r as createCrudHandlers, s as getControllerScope, t as collectReadBlockedFields, u as getEntityIdField, v as defineAction, w as DEFAULT_ID_FIELD, x as defineAggregation, y as createCrudRouter, z as RESERVED_QUERY_PARAMS } from "../index-CC5ruDGh.mjs";
|
|
3
3
|
export { AccessControl, type AccessControlConfig, type AdapterLike, type AggMeasureInput, type AggMeasureShorthand, type AggregationCacheConfig, type AggregationConfig, type AggregationDateRangeRequirement, type AggregationIndexHint, type AggregationMaterializedContext, type AggregationMaterializedResult, type AggregationRateLimit, type AggregationsMap, BaseController, type BaseControllerOptions, BaseCrudController, BodySanitizer, type BodySanitizerConfig, type BulkExt, BulkMixin, CRUD_OPERATIONS, type ControllerConfigurableOptions, type ControllerConstructionOptions, CrudOperation, DEFAULT_ID_FIELD, DEFAULT_LIMIT, DEFAULT_MAX_LIMIT, DEFAULT_SORT, DEFAULT_TENANT_FIELD, DEFAULT_UPDATE_METHOD, type DefineActionConfig, HOOK_OPERATIONS, HOOK_PHASES, HookOperation, HookPhase, type ListResult, MAX_FILTER_DEPTH, MAX_REGEX_LENGTH, MAX_SEARCH_LENGTH, MUTATION_OPERATIONS, MutationOperation, QueryResolver, type QueryResolverConfig, RESERVED_QUERY_PARAMS, ResourceDefinition, type ResourceSeams, SYSTEM_FIELDS, type SeamedResourceConfig, type SlugExt, SlugMixin, type SoftDeleteExt, SoftDeleteMixin, type TreeExt, TreeMixin, collectReadBlockedFields, createCrudHandlers, createCrudRouter, createFastifyHandler, createPermissionMiddleware, createRequestContext, defineAction, defineAggregation, defineResource, defineResourceVariants, getControllerContext, getControllerScope, getEntityId, getEntityIdField, getEntityQuery, isFieldReadable, mergeResourceConfig, sendControllerResponse };
|
package/dist/core/index.mjs
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { a as DEFAULT_SORT, c as HOOK_OPERATIONS, d as MAX_REGEX_LENGTH, f as MAX_SEARCH_LENGTH, h as SYSTEM_FIELDS, i as DEFAULT_MAX_LIMIT, l as HOOK_PHASES, m as RESERVED_QUERY_PARAMS, n as DEFAULT_ID_FIELD, o as DEFAULT_TENANT_FIELD, p as MUTATION_OPERATIONS, r as DEFAULT_LIMIT, s as DEFAULT_UPDATE_METHOD, t as CRUD_OPERATIONS, u as MAX_FILTER_DEPTH } from "../constants-DNre0wRg.mjs";
|
|
2
|
-
import { a as BulkMixin, c as collectReadBlockedFields, f as AccessControl, i as SlugMixin, l as isFieldReadable, n as TreeMixin, o as BaseCrudController, r as SoftDeleteMixin, s as QueryResolver, t as BaseController, u as BodySanitizer } from "../BaseController-
|
|
3
|
-
import { a as defineResource, c as defineAction, d as defineAggregation, i as defineResourceVariants, l as createCrudRouter, n as getEntityIdField, o as ResourceDefinition, r as getEntityQuery, s as mergeResourceConfig, t as getEntityId, u as createPermissionMiddleware } from "../core-
|
|
4
|
-
import { a as createCrudHandlers, c as getControllerContext, l as getControllerScope, o as createFastifyHandler, s as createRequestContext, u as sendControllerResponse } from "../actionPermissions-
|
|
2
|
+
import { a as BulkMixin, c as collectReadBlockedFields, f as AccessControl, i as SlugMixin, l as isFieldReadable, n as TreeMixin, o as BaseCrudController, r as SoftDeleteMixin, s as QueryResolver, t as BaseController, u as BodySanitizer } from "../BaseController-BQ8KNvWC.mjs";
|
|
3
|
+
import { a as defineResource, c as defineAction, d as defineAggregation, i as defineResourceVariants, l as createCrudRouter, n as getEntityIdField, o as ResourceDefinition, r as getEntityQuery, s as mergeResourceConfig, t as getEntityId, u as createPermissionMiddleware } from "../core-DQ6rDTyH.mjs";
|
|
4
|
+
import { a as createCrudHandlers, c as getControllerContext, l as getControllerScope, o as createFastifyHandler, s as createRequestContext, u as sendControllerResponse } from "../actionPermissions-lszoJle1.mjs";
|
|
5
5
|
export { AccessControl, BaseController, BaseCrudController, BodySanitizer, BulkMixin, CRUD_OPERATIONS, DEFAULT_ID_FIELD, DEFAULT_LIMIT, DEFAULT_MAX_LIMIT, DEFAULT_SORT, DEFAULT_TENANT_FIELD, DEFAULT_UPDATE_METHOD, HOOK_OPERATIONS, HOOK_PHASES, MAX_FILTER_DEPTH, MAX_REGEX_LENGTH, MAX_SEARCH_LENGTH, MUTATION_OPERATIONS, QueryResolver, RESERVED_QUERY_PARAMS, ResourceDefinition, SYSTEM_FIELDS, SlugMixin, SoftDeleteMixin, TreeMixin, collectReadBlockedFields, createCrudHandlers, createCrudRouter, createFastifyHandler, createPermissionMiddleware, createRequestContext, defineAction, defineAggregation, defineResource, defineResourceVariants, getControllerContext, getControllerScope, getEntityId, getEntityIdField, getEntityQuery, isFieldReadable, mergeResourceConfig, sendControllerResponse };
|
|
@@ -2,16 +2,17 @@ import { s as DEFAULT_UPDATE_METHOD, t as CRUD_OPERATIONS } from "./constants-DN
|
|
|
2
2
|
import { arcLog } from "./logger/index.mjs";
|
|
3
3
|
import { r as ForbiddenError } from "./errors-DkO8Nk6-.mjs";
|
|
4
4
|
import { b as isElevated, n as PUBLIC_SCOPE, x as isMember } from "./types-bHblZ9YN.mjs";
|
|
5
|
-
import {
|
|
6
|
-
import { d as
|
|
7
|
-
import { a as
|
|
8
|
-
import { I as applyFieldWritePermissions, R as resolveEffectiveRoles, w as requireAuth } from "./permissions-BJMuKMl7.mjs";
|
|
5
|
+
import { d as DEFAULT_FIELD_WRITE_DENIAL_POLICY, t as BaseController } from "./BaseController-BQ8KNvWC.mjs";
|
|
6
|
+
import { a as selectPluginMw, c as buildRouteHooks, d as buildAuthMiddleware, i as resolveRouterPluginMw, l as resolveRoutePreHandlers, n as buildRateLimitConfig, p as buildArcDecorator, r as buildRouteConfig, s as buildCrudPermissionMw, t as tryRegisterRoute, u as routeHookOptions } from "./registerRoute-CWO6mdYe.mjs";
|
|
7
|
+
import { a as applyFieldWritePermissions, s as resolveEffectiveRoles } from "./applyPermissionResult-Tw4iXtlf.mjs";
|
|
9
8
|
import { t as getUserRoles } from "./types-XTApa8b6.mjs";
|
|
10
|
-
import { t as applyPresets } from "./presets-
|
|
11
|
-
import {
|
|
9
|
+
import { t as applyPresets } from "./presets-BOe4Ugkl.mjs";
|
|
10
|
+
import { D as requireAuth } from "./permissions-tbxFHxjZ.mjs";
|
|
11
|
+
import { N as assertValidConfig, l as getDefaultCrudSchemas } from "./utils-CZyeRXTh.mjs";
|
|
12
|
+
import { n as convertRouteSchema, r as ensureSchemaConverter, t as convertOpenApiSchemas } from "./schemaConverter-DJvodiHj.mjs";
|
|
12
13
|
import { i as pipeUIMessageStreamToReply, r as isReadableStream } from "./streaming-BQcW6_Kq.mjs";
|
|
13
14
|
import { t as hasEvents } from "./typeGuards-C9KXUly6.mjs";
|
|
14
|
-
import { a as createCrudHandlers, d as buildRequestScopeProjection, i as resolvePipelineSteps, o as createFastifyHandler, r as buildPipelineHandler, s as createRequestContext, t as resolveActionPermission } from "./actionPermissions-
|
|
15
|
+
import { a as createCrudHandlers, d as buildRequestScopeProjection, i as resolvePipelineSteps, o as createFastifyHandler, r as buildPipelineHandler, s as createRequestContext, t as resolveActionPermission } from "./actionPermissions-lszoJle1.mjs";
|
|
15
16
|
import { t as pluralize } from "./pluralize-BhrgwZb9.mjs";
|
|
16
17
|
//#region src/core/middlewares/fieldWrite.ts
|
|
17
18
|
/**
|
|
@@ -1395,7 +1396,7 @@ function buildResourcePlugin(resource) {
|
|
|
1395
1396
|
extensions: resource.extensions
|
|
1396
1397
|
});
|
|
1397
1398
|
if (resource.actions && Object.keys(resource.actions).length > 0) {
|
|
1398
|
-
const { createActionRouter } = await import("./createActionRouter-
|
|
1399
|
+
const { createActionRouter } = await import("./createActionRouter-BuOv-m4Y.mjs").then((n) => n.n);
|
|
1399
1400
|
createActionRouter(typedInstance, {
|
|
1400
1401
|
...normalizeActionsToRouterConfig(resource.actions, resource.actionPermissions, resource.tag, resource.permissions, resource.name, typedInstance.log),
|
|
1401
1402
|
resourceName: resource.name,
|
|
@@ -1410,7 +1411,7 @@ function buildResourcePlugin(resource) {
|
|
|
1410
1411
|
});
|
|
1411
1412
|
}
|
|
1412
1413
|
if (resource.aggregations && Object.keys(resource.aggregations).length > 0) {
|
|
1413
|
-
const [{ createAggregationRouter }, { adapterSupportsAggregate, ArcAggregationConfigError }] = await Promise.all([import("./createAggregationRouter-
|
|
1414
|
+
const [{ createAggregationRouter }, { adapterSupportsAggregate, ArcAggregationConfigError }] = await Promise.all([import("./createAggregationRouter-CgJv_BU_.mjs"), import("./validate-C2mGgSXg.mjs").then((n) => n.r)]);
|
|
1414
1415
|
const repoForAgg = resource.controller?.repository ?? resource.repository;
|
|
1415
1416
|
if (!adapterSupportsAggregate(repoForAgg)) {
|
|
1416
1417
|
const undispatchable = Object.entries(resource.aggregations).filter(([, cfg]) => !cfg.materialized).map(([name]) => name);
|
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
import { t as __exportAll } from "./rolldown-runtime-8H4AJuhK.mjs";
|
|
2
2
|
import "./constants-DNre0wRg.mjs";
|
|
3
3
|
import { f as createError } from "./errors-DkO8Nk6-.mjs";
|
|
4
|
-
import { a as selectPluginMw, c as buildRouteHooks, f as buildAuthMiddlewareForPermissions, i as resolveRouterPluginMw, n as buildRateLimitConfig, o as buildActionPermissionMw, p as buildArcDecorator, r as buildRouteConfig, t as tryRegisterRoute, u as routeHookOptions } from "./registerRoute-
|
|
5
|
-
import { i as resolvePipelineSteps, n as buildActionPipelineHandler, u as sendControllerResponse } from "./actionPermissions-
|
|
6
|
-
import { n as schemaIRToJsonSchemaBranch, t as normalizeSchemaIR } from "./schemaIR-
|
|
4
|
+
import { a as selectPluginMw, c as buildRouteHooks, f as buildAuthMiddlewareForPermissions, i as resolveRouterPluginMw, n as buildRateLimitConfig, o as buildActionPermissionMw, p as buildArcDecorator, r as buildRouteConfig, t as tryRegisterRoute, u as routeHookOptions } from "./registerRoute-CWO6mdYe.mjs";
|
|
5
|
+
import { i as resolvePipelineSteps, n as buildActionPipelineHandler, u as sendControllerResponse } from "./actionPermissions-lszoJle1.mjs";
|
|
6
|
+
import { n as schemaIRToJsonSchemaBranch, t as normalizeSchemaIR } from "./schemaIR-Ca7ySNPx.mjs";
|
|
7
7
|
//#region src/core/createActionRouter.ts
|
|
8
8
|
var createActionRouter_exports = /* @__PURE__ */ __exportAll({
|
|
9
9
|
buildActionBodySchema: () => buildActionBodySchema,
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { f as createError, l as UnauthorizedError, r as ForbiddenError } from "./errors-DkO8Nk6-.mjs";
|
|
2
|
-
import { a as selectPluginMw, c as buildRouteHooks, d as buildAuthMiddleware, i as resolveRouterPluginMw, n as buildRateLimitConfig, p as buildArcDecorator, u as routeHookOptions } from "./registerRoute-
|
|
2
|
+
import { a as selectPluginMw, c as buildRouteHooks, d as buildAuthMiddleware, i as resolveRouterPluginMw, n as buildRateLimitConfig, p as buildArcDecorator, u as routeHookOptions } from "./registerRoute-CWO6mdYe.mjs";
|
|
3
3
|
import { n as validateAggregations } from "./validate-C2mGgSXg.mjs";
|
|
4
4
|
import { t as buildAggregationHandler } from "./buildHandler-D_2ULWxZ.mjs";
|
|
5
5
|
//#region src/core/aggregation/createAggregationRouter.ts
|
|
@@ -456,11 +456,18 @@ async function registerArcPlugins(fastify, config, trackPlugin, modules) {
|
|
|
456
456
|
}
|
|
457
457
|
if (config.arcPlugins?.sse) if (config.arcPlugins?.events === false) fastify.log.warn("SSE plugin requires events plugin (arcPlugins.events). SSE disabled.");
|
|
458
458
|
else {
|
|
459
|
-
const { default: ssePlugin } = await import("./sse-
|
|
459
|
+
const { default: ssePlugin } = await import("./sse-0u4P4-OI.mjs").then((n) => n.r);
|
|
460
460
|
const opts = config.arcPlugins.sse === true ? {} : config.arcPlugins.sse;
|
|
461
461
|
await fastify.register(ssePlugin, opts);
|
|
462
462
|
trackPlugin("arc-sse", opts);
|
|
463
463
|
}
|
|
464
|
+
if (config.arcPlugins?.realtime) if (config.arcPlugins?.events === false) fastify.log.warn("Realtime plugin requires events plugin (arcPlugins.events). Realtime disabled.");
|
|
465
|
+
else {
|
|
466
|
+
const { default: realtimePlugin } = await import("./realtime-I8OrfnMR.mjs").then((n) => n.r);
|
|
467
|
+
const opts = config.arcPlugins.realtime === true ? {} : config.arcPlugins.realtime;
|
|
468
|
+
await fastify.register(realtimePlugin, opts);
|
|
469
|
+
trackPlugin("arc-realtime", opts);
|
|
470
|
+
}
|
|
464
471
|
if (config.arcPlugins?.metrics) {
|
|
465
472
|
const { default: metricsPlugin } = await import("./metrics-fdxI5mn_.mjs").then((n) => n.r);
|
|
466
473
|
const opts = config.arcPlugins.metrics === true ? {} : config.arcPlugins.metrics;
|
|
@@ -552,7 +559,7 @@ async function registerElevation(fastify, config, trackPlugin) {
|
|
|
552
559
|
*/
|
|
553
560
|
async function registerErrorHandler(fastify, config, trackPlugin, moduleErrorMappers) {
|
|
554
561
|
if (config.errorHandler === false) return;
|
|
555
|
-
const { errorHandlerPlugin } = await import("./errorHandler-
|
|
562
|
+
const { errorHandlerPlugin } = await import("./errorHandler-Bq2OeEQL.mjs").then((n) => n.r);
|
|
556
563
|
let errorOpts = typeof config.errorHandler === "object" ? config.errorHandler : { includeStack: config.preset !== "production" };
|
|
557
564
|
if (moduleErrorMappers && moduleErrorMappers.length > 0) errorOpts = {
|
|
558
565
|
...errorOpts,
|
|
@@ -1220,6 +1227,7 @@ async function createApp(options) {
|
|
|
1220
1227
|
...options.preset ? getPreset(options.preset) : {},
|
|
1221
1228
|
...options
|
|
1222
1229
|
};
|
|
1230
|
+
const inheritedPermissiveTrustProxy = options.preset === "production" && options.trustProxy === void 0;
|
|
1223
1231
|
const resolvedModules = orderModules(await Promise.all((config.modules ?? []).map((m) => resolveModule(m))));
|
|
1224
1232
|
const moduleErrorMappers = resolvedModules.flatMap((m) => m.errorMappers ?? []);
|
|
1225
1233
|
const resolvedLogger = resolveLoggerConfig(config.logger);
|
|
@@ -1255,6 +1263,7 @@ async function createApp(options) {
|
|
|
1255
1263
|
writer: createPinoWriter(fastify.log)
|
|
1256
1264
|
});
|
|
1257
1265
|
for (const warning of deferredWarnings) fastify.log.warn(warning);
|
|
1266
|
+
if (inheritedPermissiveTrustProxy) fastify.log.warn("[arc] preset: 'production' defaults to trustProxy: true (trust EVERY proxy). If this app is reachable without your load balancer, clients can spoof X-Forwarded-For/Proto (rate-limit bypass, wrong audit IPs, forged protocol). Silence this by setting trustProxy explicitly: a hop count (trustProxy: 1), a CIDR allow-list ('10.0.0.0/8'), a resolver function, or true if you accept the trade-off. v3 flips this preset default to false — set it now to be upgrade-proof.");
|
|
1258
1267
|
if (config.typeProvider === "typebox") try {
|
|
1259
1268
|
const { TypeBoxValidatorCompiler } = await import("@fastify/type-provider-typebox");
|
|
1260
1269
|
fastify.setValidatorCompiler(TypeBoxValidatorCompiler);
|
package/dist/docs/index.d.mts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { p as RegistryEntry } from "../index-
|
|
1
|
+
import { p as RegistryEntry } from "../index-BBsvQt7U.mjs";
|
|
2
2
|
import { t as ExternalOpenApiPaths } from "../externalPaths-BE6wFpYv.mjs";
|
|
3
3
|
import { FastifyPluginAsync } from "fastify";
|
|
4
4
|
//#region src/docs/openapi/types.d.ts
|
package/dist/docs/index.mjs
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { t as getUserRoles } from "../types-XTApa8b6.mjs";
|
|
2
|
-
import { n as openApiPlugin, r as openapi_default, t as buildOpenApiSpec } from "../openapi-
|
|
2
|
+
import { n as openApiPlugin, r as openapi_default, t as buildOpenApiSpec } from "../openapi-jSDx-VDv.mjs";
|
|
3
3
|
import fp from "fastify-plugin";
|
|
4
4
|
//#region src/docs/scalar.ts
|
|
5
5
|
const scalarPlugin = async (fastify, opts = {}) => {
|
|
@@ -14,13 +14,16 @@ var errorHandler_exports = /* @__PURE__ */ __exportAll({
|
|
|
14
14
|
* {@link ErrorHandlerOptions.isDuplicateKeyError}.
|
|
15
15
|
*/
|
|
16
16
|
function defaultIsDuplicateKeyError(err) {
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
17
|
+
let current = err;
|
|
18
|
+
for (let depth = 0; depth < 5 && current && typeof current === "object"; depth++) {
|
|
19
|
+
const e = current;
|
|
20
|
+
if (e.code === 11e3 || e.codeName === "DuplicateKey") return true;
|
|
21
|
+
if (e.code === "P2002") return true;
|
|
22
|
+
if (e.code === "23505") return true;
|
|
23
|
+
if (e.code === "ER_DUP_ENTRY" || e.errno === 1062) return true;
|
|
24
|
+
if (e.code === "SQLITE_CONSTRAINT_UNIQUE" || e.code === "SQLITE_CONSTRAINT_PRIMARYKEY") return true;
|
|
25
|
+
current = e.cause;
|
|
26
|
+
}
|
|
24
27
|
return false;
|
|
25
28
|
}
|
|
26
29
|
function extractDuplicateFields(err) {
|
package/dist/events/index.mjs
CHANGED
|
@@ -176,8 +176,11 @@ function validatePayload(payload, schema) {
|
|
|
176
176
|
for (const [key, propSchema] of Object.entries(schema.properties)) if (key in record && record[key] !== void 0 && record[key] !== null) {
|
|
177
177
|
const expectedType = propSchema.type;
|
|
178
178
|
if (expectedType) {
|
|
179
|
-
const
|
|
180
|
-
|
|
179
|
+
const value = record[key];
|
|
180
|
+
const actualType = Array.isArray(value) ? "array" : typeof value;
|
|
181
|
+
if (expectedType === "integer") {
|
|
182
|
+
if (actualType !== "number" || !Number.isInteger(value)) errors.push(`Field '${key}': expected integer, got ${actualType}`);
|
|
183
|
+
} else if (expectedType !== actualType) errors.push(`Field '${key}': expected ${expectedType}, got ${actualType}`);
|
|
181
184
|
}
|
|
182
185
|
}
|
|
183
186
|
}
|
package/dist/factory/index.d.mts
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { a as getModuleExports, c as LoadResourcesOptions, d as loadResources, i as defineModule, l as ResourceLike, n as ArcModuleInput, o as orderModules, r as ArcModuleRegistry, s as resolveModule, t as ArcModule, u as ResourceModule } from "../module-B4u10iOJ.mjs";
|
|
2
|
-
import "../fastify-augmentation-
|
|
3
|
-
import { a as AuthOption, c as CustomPluginAuthOption, d as preloadResourcesAsync, i as UnderPressureOptions, l as JwtAuthOption, n as MultipartOptions, o as BetterAuthOption, r as RawBodyOptions, s as CustomAuthenticatorOption, t as CreateAppOptions, u as preloadResources } from "../index-
|
|
4
|
-
import {
|
|
2
|
+
import "../fastify-augmentation-g_f9omSc.mjs";
|
|
3
|
+
import { a as AuthOption, c as CustomPluginAuthOption, d as preloadResourcesAsync, i as UnderPressureOptions, l as JwtAuthOption, n as MultipartOptions, o as BetterAuthOption, r as RawBodyOptions, s as CustomAuthenticatorOption, t as CreateAppOptions, u as preloadResources } from "../index-Ba10fdKp.mjs";
|
|
4
|
+
import { b as HealthCheck } from "../versioning-D6PvCwpq.mjs";
|
|
5
5
|
import { FastifyInstance, FastifyServerOptions } from "fastify";
|
|
6
6
|
//#region src/factory/createApp.d.ts
|
|
7
7
|
/**
|
|
@@ -64,15 +64,31 @@ interface FetchHandlerOptions {
|
|
|
64
64
|
* @default true
|
|
65
65
|
*/
|
|
66
66
|
autoReady?: boolean;
|
|
67
|
+
/**
|
|
68
|
+
* Reject request bodies larger than this many bytes with 413
|
|
69
|
+
* `arc.payload_too_large`. Enforcement precision matters: bodies with a
|
|
70
|
+
* declared `Content-Length` are rejected BEFORE buffering; chunked or
|
|
71
|
+
* undeclared bodies are necessarily buffered first and checked AFTER
|
|
72
|
+
* (`arrayBuffer()` allocates the full body — a consequence of the
|
|
73
|
+
* buffered-adapter design, not a bug). Platform/gateway body limits
|
|
74
|
+
* remain the real pre-allocation protection for hostile senders.
|
|
75
|
+
* Fastify's own `bodyLimit` still applies after this gate. Set to `0`
|
|
76
|
+
* to disable the gate.
|
|
77
|
+
* @default 1_048_576 (1 MiB — matches Fastify's default bodyLimit)
|
|
78
|
+
*/
|
|
79
|
+
maxRequestBytes?: number;
|
|
67
80
|
}
|
|
68
81
|
/**
|
|
69
82
|
* Convert a Fastify app into a Web Standards fetch handler.
|
|
70
83
|
*
|
|
71
84
|
* The returned function accepts a Web Standard `Request` and returns
|
|
72
|
-
* a Web Standard `Response` — the universal serverless
|
|
85
|
+
* a Web Standard `Response` — the universal serverless contract.
|
|
73
86
|
*
|
|
74
87
|
* Internally uses `app.inject()` which processes the request through
|
|
75
88
|
* the full Fastify pipeline (hooks, plugins, routes) without TCP.
|
|
89
|
+
* Bodies are read as raw bytes (`arrayBuffer()`), so binary payloads
|
|
90
|
+
* (protobuf, images, non-UTF-8 charsets) survive intact — the
|
|
91
|
+
* content-type parser decides how to decode, same as a real socket.
|
|
76
92
|
*/
|
|
77
93
|
declare function toFetchHandler(app: FastifyInstance, options?: FetchHandlerOptions): (request: Request) => Promise<Response>;
|
|
78
94
|
//#endregion
|