@classytic/arc 2.25.0 → 2.28.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 +2 -0
- package/dist/{BaseController--M8EMzt0.mjs → BaseController-B65zbz9m.mjs} +4 -2
- package/dist/{applyPermissionResult-Tw4iXtlf.mjs → applyPermissionResult-BnyVE9eD.mjs} +10 -0
- package/dist/cleanup/index.d.mts +313 -0
- package/dist/cleanup/index.mjs +1267 -0
- package/dist/cli/commands/docs.mjs +1 -1
- package/dist/core/index.d.mts +1 -1
- package/dist/core/index.mjs +4 -3
- package/dist/core-BGa8TXJR.mjs +221 -0
- package/dist/{createActionRouter-B6GvAuAX.mjs → createActionRouter-C-N7z2EL.mjs} +2 -2
- package/dist/{preloadResources-DfyngIN9.mjs → createApp-Du5G54TM.mjs} +2 -74
- package/dist/{core-EUNu0u07.mjs → defineResource-2zvVVpxg.mjs} +6 -224
- package/dist/docs/index.mjs +1 -1
- package/dist/events/index.d.mts +28 -2
- package/dist/events/index.mjs +20 -4
- package/dist/factory/index.d.mts +4 -3
- package/dist/factory/index.mjs +3 -1
- package/dist/{fastify-augmentation-dPNBVeIj.d.mts → fastify-augmentation-OzYGIiU1.d.mts} +2 -1
- package/dist/index-CZY4EGga.d.mts +107 -0
- package/dist/{index-f4PTrk3B.d.mts → index-CjVg-Epg.d.mts} +1 -1
- package/dist/index.d.mts +2 -2
- package/dist/index.mjs +6 -5
- package/dist/integrations/mcp/index.mjs +1 -1
- package/dist/integrations/mcp/testing.mjs +1 -1
- package/dist/{mcpPlugin-Cb7olw0j.mjs → mcpPlugin-Cs6nRGCt.mjs} +3 -3
- package/dist/module-_-M7C5ES.mjs +29 -0
- package/dist/{openapi-DrLtog_B.mjs → openapi-BLV4OMXb.mjs} +1 -1
- package/dist/permissions/index.mjs +1 -1
- package/dist/{pipeline-BN5G05rd.mjs → pipeline-BGRnV-To.mjs} +1 -1
- package/dist/plugins/index.d.mts +2 -2
- package/dist/plugins/index.mjs +1 -1
- package/dist/plugins/tracing-entry.mjs +1 -1
- package/dist/{preloadResources-0oe-JSSS.d.mts → preloadResources-CM4uUaFv.d.mts} +2 -1
- package/dist/preloadResources-DSY5HGpS.mjs +45 -0
- package/dist/{realtime-Cuw_deA-.mjs → realtime-DEuDu9io.mjs} +1 -1
- package/dist/{registerRoute-v0Wb6Plt.mjs → registerRoute-DTp18Ah_.mjs} +1 -1
- package/dist/registry/index.d.mts +1 -1
- package/dist/registry/index.mjs +1 -1
- package/dist/{registry-vcOCtPij.mjs → registry-9km_w1aP.mjs} +15 -0
- package/dist/testing/cleanupStoreContract.d.mts +10 -0
- package/dist/testing/cleanupStoreContract.mjs +375 -0
- package/dist/testing/index.d.mts +3 -2
- package/dist/testing/index.mjs +4 -2
- package/dist/testing/outboxStoreContract.d.mts +13 -0
- package/dist/testing/outboxStoreContract.mjs +247 -0
- package/dist/types-C_ZZRcD6.d.mts +450 -0
- package/dist/{index-KUoxjIq1.d.mts → types-Cu6vVJmh.d.mts} +1 -105
- package/package.json +27 -12
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { t as buildOpenApiSpec } from "../../openapi-
|
|
1
|
+
import { t as buildOpenApiSpec } from "../../openapi-BLV4OMXb.mjs";
|
|
2
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";
|
package/dist/core/index.d.mts
CHANGED
|
@@ -1,3 +1,3 @@
|
|
|
1
1
|
import { $t as SlugExt, An as ControllerConfigurableOptions, B as defineResource, Jt as BaseController, Mn as ListResult, Nn as BodySanitizer, Pn as BodySanitizerConfig, Qt as TreeMixin, V as ResourceDefinition, Xt as SoftDeleteMixin, Yt as SoftDeleteExt, Zt as TreeExt, an as QueryResolverConfig, ct as AggMeasureShorthand, dt as AggregationDateRangeRequirement, en as SlugMixin, ft as AggregationIndexHint, gt as AggregationsMap, ht as AggregationRateLimit, in as QueryResolver, jn as ControllerConstructionOptions, kn as BaseControllerOptions, lt as AggregationCacheConfig, mt as AggregationMaterializedResult, nn as BulkMixin, on as AccessControl, pt as AggregationMaterializedContext, rn as BaseCrudController, sn as AccessControlConfig, st as AggMeasureInput, tn as BulkExt, ut as AggregationConfig } from "../index-DPRH0Cme.mjs";
|
|
2
|
-
import { A as DEFAULT_ID_FIELD, B as MAX_FILTER_DEPTH, C as DefineActionConfig, D as defineAggregation, E as createPermissionMiddleware, F as DEFAULT_UPDATE_METHOD, G as RESERVED_QUERY_PARAMS, H as MAX_SEARCH_LENGTH, I as HOOK_OPERATIONS, K as SYSTEM_FIELDS, L as HOOK_PHASES, M as DEFAULT_MAX_LIMIT, N as DEFAULT_SORT, O as CRUD_OPERATIONS, P as DEFAULT_TENANT_FIELD, R as HookOperation, S as mergeResourceConfig, T as createCrudRouter, U as MUTATION_OPERATIONS, V as MAX_REGEX_LENGTH, W as MutationOperation, _ as getEntityQuery, a as formatValidationErrors, b as ResourceSeams, c as isFieldReadable, d as createRequestContext, f as getControllerContext, g as getEntityIdField, h as getEntityId, i as assertValidConfig, j as DEFAULT_LIMIT, k as CrudOperation, l as createCrudHandlers, m as sendControllerResponse, n as ValidateOptions, o as validateResourceConfig, p as getControllerScope, r as ValidationResult, s as collectReadBlockedFields, t as ConfigError, u as createFastifyHandler, v as defineResourceVariants, w as defineAction, x as SeamedResourceConfig, y as AdapterLike, z as HookPhase } from "../index-
|
|
2
|
+
import { A as DEFAULT_ID_FIELD, B as MAX_FILTER_DEPTH, C as DefineActionConfig, D as defineAggregation, E as createPermissionMiddleware, F as DEFAULT_UPDATE_METHOD, G as RESERVED_QUERY_PARAMS, H as MAX_SEARCH_LENGTH, I as HOOK_OPERATIONS, K as SYSTEM_FIELDS, L as HOOK_PHASES, M as DEFAULT_MAX_LIMIT, N as DEFAULT_SORT, O as CRUD_OPERATIONS, P as DEFAULT_TENANT_FIELD, R as HookOperation, S as mergeResourceConfig, T as createCrudRouter, U as MUTATION_OPERATIONS, V as MAX_REGEX_LENGTH, W as MutationOperation, _ as getEntityQuery, a as formatValidationErrors, b as ResourceSeams, c as isFieldReadable, d as createRequestContext, f as getControllerContext, g as getEntityIdField, h as getEntityId, i as assertValidConfig, j as DEFAULT_LIMIT, k as CrudOperation, l as createCrudHandlers, m as sendControllerResponse, n as ValidateOptions, o as validateResourceConfig, p as getControllerScope, r as ValidationResult, s as collectReadBlockedFields, t as ConfigError, u as createFastifyHandler, v as defineResourceVariants, w as defineAction, x as SeamedResourceConfig, y as AdapterLike, z as HookPhase } from "../index-CjVg-Epg.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 ConfigError, type ControllerConfigurableOptions, type ControllerConstructionOptions, CrudOperation, DEFAULT_ID_FIELD, DEFAULT_LIMIT, DEFAULT_MAX_LIMIT, DEFAULT_SORT, DEFAULT_TENANT_FIELD, DEFAULT_UPDATE_METHOD, type DefineActionConfig, HOOK_OPERATIONS, HOOK_PHASES, HookOperation, HookPhase, type ListResult, MAX_FILTER_DEPTH, MAX_REGEX_LENGTH, MAX_SEARCH_LENGTH, MUTATION_OPERATIONS, MutationOperation, QueryResolver, type QueryResolverConfig, RESERVED_QUERY_PARAMS, ResourceDefinition, type ResourceSeams, SYSTEM_FIELDS, type SeamedResourceConfig, type SlugExt, SlugMixin, type SoftDeleteExt, SoftDeleteMixin, type TreeExt, TreeMixin, type ValidateOptions, type ValidationResult, assertValidConfig, collectReadBlockedFields, createCrudHandlers, createCrudRouter, createFastifyHandler, createPermissionMiddleware, createRequestContext, defineAction, defineAggregation, defineResource, defineResourceVariants, formatValidationErrors, getControllerContext, getControllerScope, getEntityId, getEntityIdField, getEntityQuery, isFieldReadable, mergeResourceConfig, sendControllerResponse, validateResourceConfig };
|
package/dist/core/index.mjs
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
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
|
|
4
|
-
import { a as createFastifyHandler, c as getControllerScope, i as createCrudHandlers, l as sendControllerResponse, o as createRequestContext, s as getControllerContext } from "../pipeline-
|
|
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-B65zbz9m.mjs";
|
|
3
|
+
import { a as ResourceDefinition, i as validateResourceConfig, n as assertValidConfig, o as createCrudRouter, r as formatValidationErrors, s as createPermissionMiddleware, t as defineResource } from "../defineResource-2zvVVpxg.mjs";
|
|
4
|
+
import { a as createFastifyHandler, c as getControllerScope, i as createCrudHandlers, l as sendControllerResponse, o as createRequestContext, s as getControllerContext } from "../pipeline-BGRnV-To.mjs";
|
|
5
|
+
import { a as mergeResourceConfig, i as defineResourceVariants, n as getEntityIdField, o as defineAction, r as getEntityQuery, s as defineAggregation, t as getEntityId } from "../core-BGa8TXJR.mjs";
|
|
5
6
|
export { AccessControl, BaseController, BaseCrudController, BodySanitizer, BulkMixin, CRUD_OPERATIONS, DEFAULT_ID_FIELD, DEFAULT_LIMIT, DEFAULT_MAX_LIMIT, DEFAULT_SORT, DEFAULT_TENANT_FIELD, DEFAULT_UPDATE_METHOD, HOOK_OPERATIONS, HOOK_PHASES, MAX_FILTER_DEPTH, MAX_REGEX_LENGTH, MAX_SEARCH_LENGTH, MUTATION_OPERATIONS, QueryResolver, RESERVED_QUERY_PARAMS, ResourceDefinition, SYSTEM_FIELDS, SlugMixin, SoftDeleteMixin, TreeMixin, assertValidConfig, collectReadBlockedFields, createCrudHandlers, createCrudRouter, createFastifyHandler, createPermissionMiddleware, createRequestContext, defineAction, defineAggregation, defineResource, defineResourceVariants, formatValidationErrors, getControllerContext, getControllerScope, getEntityId, getEntityIdField, getEntityQuery, isFieldReadable, mergeResourceConfig, sendControllerResponse, validateResourceConfig };
|
|
@@ -0,0 +1,221 @@
|
|
|
1
|
+
import "./constants-DNre0wRg.mjs";
|
|
2
|
+
import { t as defineResource } from "./defineResource-2zvVVpxg.mjs";
|
|
3
|
+
//#region src/core/aggregation/defineAggregation.ts
|
|
4
|
+
/**
|
|
5
|
+
* Declare a single resource aggregation. Exported configs flow into
|
|
6
|
+
* `defineResource({ aggregations: { ... } })` either as named keys or
|
|
7
|
+
* via auto-discovery patterns (loadAggregations — future).
|
|
8
|
+
*
|
|
9
|
+
* @example Inline
|
|
10
|
+
* ```ts
|
|
11
|
+
* defineResource({
|
|
12
|
+
* name: 'order',
|
|
13
|
+
* aggregations: {
|
|
14
|
+
* revenueByStatus: defineAggregation({
|
|
15
|
+
* groupBy: 'status',
|
|
16
|
+
* measures: { count: 'count', revenue: 'sum:totalPrice' },
|
|
17
|
+
* permissions: requireRoles(['admin']),
|
|
18
|
+
* }),
|
|
19
|
+
* },
|
|
20
|
+
* });
|
|
21
|
+
* ```
|
|
22
|
+
*
|
|
23
|
+
* @example Multi-file
|
|
24
|
+
* ```ts
|
|
25
|
+
* // orders/aggregations/revenue-by-status.ts
|
|
26
|
+
* import { defineAggregation } from '@classytic/arc';
|
|
27
|
+
*
|
|
28
|
+
* export const revenueByStatus = defineAggregation({
|
|
29
|
+
* groupBy: 'status',
|
|
30
|
+
* measures: { count: 'count', revenue: 'sum:totalPrice' },
|
|
31
|
+
* permissions: requireRoles(['admin']),
|
|
32
|
+
* timeout: 5000,
|
|
33
|
+
* cache: { staleTime: 60 },
|
|
34
|
+
* });
|
|
35
|
+
*
|
|
36
|
+
* // orders/order.resource.ts
|
|
37
|
+
* import * as aggregations from './aggregations/index.js';
|
|
38
|
+
*
|
|
39
|
+
* defineResource({
|
|
40
|
+
* name: 'order',
|
|
41
|
+
* aggregations, // 30+ entries flow in
|
|
42
|
+
* });
|
|
43
|
+
* ```
|
|
44
|
+
*/
|
|
45
|
+
function defineAggregation(config) {
|
|
46
|
+
return config;
|
|
47
|
+
}
|
|
48
|
+
//#endregion
|
|
49
|
+
//#region src/core/defineAction.ts
|
|
50
|
+
/**
|
|
51
|
+
* Build an `ActionDefinition` with a typed handler. The literal schema
|
|
52
|
+
* type captured here flows into `data`, so `defineAction({ schema:
|
|
53
|
+
* z.object({...}), handler })` produces fully-typed code with no
|
|
54
|
+
* `as MyShape` cast.
|
|
55
|
+
*
|
|
56
|
+
* Behaviorally identical to a bare `ActionDefinition` object — same
|
|
57
|
+
* validation path (AJV), same permission resolution, same MCP wiring.
|
|
58
|
+
* The runtime shape is unchanged; only the type-level inference is new.
|
|
59
|
+
*/
|
|
60
|
+
function defineAction(config) {
|
|
61
|
+
return {
|
|
62
|
+
handler: config.handler,
|
|
63
|
+
permissions: config.permissions,
|
|
64
|
+
schema: config.schema,
|
|
65
|
+
description: config.description,
|
|
66
|
+
id: config.id,
|
|
67
|
+
mcp: config.mcp
|
|
68
|
+
};
|
|
69
|
+
}
|
|
70
|
+
//#endregion
|
|
71
|
+
//#region src/core/defineResource/seams.ts
|
|
72
|
+
/** Plain object test — literals only; class instances must replace, not merge. */
|
|
73
|
+
function isPlainObject(value) {
|
|
74
|
+
if (value === null || typeof value !== "object" || Array.isArray(value)) return false;
|
|
75
|
+
const proto = Object.getPrototypeOf(value);
|
|
76
|
+
return proto === Object.prototype || proto === null;
|
|
77
|
+
}
|
|
78
|
+
/** Recursive plain-object merge; nested arrays REPLACE (they're value lists). */
|
|
79
|
+
function mergePlain(base, next) {
|
|
80
|
+
const out = { ...base };
|
|
81
|
+
for (const [key, value] of Object.entries(next)) {
|
|
82
|
+
if (value === void 0) continue;
|
|
83
|
+
const existing = out[key];
|
|
84
|
+
out[key] = isPlainObject(existing) && isPlainObject(value) ? mergePlain(existing, value) : value;
|
|
85
|
+
}
|
|
86
|
+
return out;
|
|
87
|
+
}
|
|
88
|
+
/**
|
|
89
|
+
* Canonical slot-aware merge of a base resource config with host seams.
|
|
90
|
+
* Later seams win over earlier ones; `undefined` values never clobber.
|
|
91
|
+
*
|
|
92
|
+
* Merge semantics (value-driven — no per-slot registry to drift):
|
|
93
|
+
*
|
|
94
|
+
* - **Top-level arrays CONCAT** — `routes`, `middlewares`, `presets`,
|
|
95
|
+
* `routeGuards`, `disabledRoutes`: additive collections, base first.
|
|
96
|
+
* - **Plain objects merge recursively** — `permissions`, `actions`,
|
|
97
|
+
* `schemaOptions` (incl. `fieldRules`), `aggregations`, `customSchemas`,
|
|
98
|
+
* `fields`, `cache`, `events`: per-key composition. Arrays NESTED inside
|
|
99
|
+
* them replace (they're value lists — enums, sort specs), not concat.
|
|
100
|
+
* - **Everything else last-wins** — class instances (`adapter`,
|
|
101
|
+
* `controller`, `queryParser`), functions, scalars.
|
|
102
|
+
*
|
|
103
|
+
* The single `as` at the return is the one documented erasure for the
|
|
104
|
+
* {@link AdapterLike}-widened seam slots — inside the helper so no module
|
|
105
|
+
* author ever writes one (mirrors mongokit's one-cast-at-the-boundary rule).
|
|
106
|
+
*/
|
|
107
|
+
function mergeResourceConfig(base, ...seams) {
|
|
108
|
+
const out = { ...base };
|
|
109
|
+
for (const seam of seams) {
|
|
110
|
+
if (!seam) continue;
|
|
111
|
+
for (const [key, value] of Object.entries(seam)) {
|
|
112
|
+
if (value === void 0) continue;
|
|
113
|
+
const existing = out[key];
|
|
114
|
+
if (Array.isArray(existing) && Array.isArray(value)) out[key] = [...existing, ...value];
|
|
115
|
+
else if (isPlainObject(existing) && isPlainObject(value)) out[key] = mergePlain(existing, value);
|
|
116
|
+
else if (Array.isArray(value)) out[key] = [...value];
|
|
117
|
+
else out[key] = value;
|
|
118
|
+
}
|
|
119
|
+
}
|
|
120
|
+
return out;
|
|
121
|
+
}
|
|
122
|
+
//#endregion
|
|
123
|
+
//#region src/core/defineResourceVariants.ts
|
|
124
|
+
/**
|
|
125
|
+
* Define multiple resources from a shared base config and per-variant overrides.
|
|
126
|
+
*
|
|
127
|
+
* Each variant is independently passed through `defineResource()` — the
|
|
128
|
+
* returned `ResourceDefinition`s are real, fully-registered resources.
|
|
129
|
+
* Register each one's plugin in your app:
|
|
130
|
+
*
|
|
131
|
+
* ```typescript
|
|
132
|
+
* await app.register(articlePublic.toPlugin());
|
|
133
|
+
* await app.register(articleAdmin.toPlugin());
|
|
134
|
+
* ```
|
|
135
|
+
*
|
|
136
|
+
* @param base Shared config — adapter, queryParser, schemaOptions, hooks, etc.
|
|
137
|
+
* Must NOT include `name` or `prefix` (those are per-variant).
|
|
138
|
+
* @param variants Map of variant key → override. Each variant must declare
|
|
139
|
+
* its own `name` and `prefix`. Other fields override the base.
|
|
140
|
+
* @returns A record where each key from `variants` maps to a real
|
|
141
|
+
* `ResourceDefinition` ready for `.toPlugin()` registration.
|
|
142
|
+
*/
|
|
143
|
+
function defineResourceVariants(base, variants) {
|
|
144
|
+
const out = {};
|
|
145
|
+
for (const key of Object.keys(variants)) {
|
|
146
|
+
const override = variants[key];
|
|
147
|
+
out[key] = defineResource({
|
|
148
|
+
...base,
|
|
149
|
+
...override
|
|
150
|
+
});
|
|
151
|
+
}
|
|
152
|
+
return out;
|
|
153
|
+
}
|
|
154
|
+
//#endregion
|
|
155
|
+
//#region src/core/entityHelpers.ts
|
|
156
|
+
/**
|
|
157
|
+
* Per-request entity helpers — read the resource binding (idField + the
|
|
158
|
+
* URL `:id` value) off `req.arc`.
|
|
159
|
+
*
|
|
160
|
+
* Action handlers receive their `id` argument as the raw URL `:id` value.
|
|
161
|
+
* When the resource declares a custom `idField` (`slug`, `reportId`, …)
|
|
162
|
+
* that's NOT the document `_id`, a naive `Model.findById(id)` silently
|
|
163
|
+
* returns null. The historical footgun was a `findById(id)` typo where
|
|
164
|
+
* the handler author hadn't realised that `:id` resolves to the friendly
|
|
165
|
+
* handle.
|
|
166
|
+
*
|
|
167
|
+
* `getEntityQuery(req)` produces the canonical filter shape so handlers
|
|
168
|
+
* compose lookups with no resource-config recall:
|
|
169
|
+
*
|
|
170
|
+
* ```ts
|
|
171
|
+
* actions: {
|
|
172
|
+
* archive: {
|
|
173
|
+
* handler: async (id, data, req) => {
|
|
174
|
+
* const doc = await Model.findOne(getEntityQuery(req));
|
|
175
|
+
* if (!doc) throw new NotFoundError("Order");
|
|
176
|
+
* // ...
|
|
177
|
+
* },
|
|
178
|
+
* },
|
|
179
|
+
* }
|
|
180
|
+
* ```
|
|
181
|
+
*
|
|
182
|
+
* The router populates `req.arc.idField` and `req.arc.entityId` before
|
|
183
|
+
* invoking the handler — these helpers are zero-cost reads.
|
|
184
|
+
*/
|
|
185
|
+
/**
|
|
186
|
+
* Read the resource's configured `idField` for the current request.
|
|
187
|
+
* Falls back to the framework default (`_id`) when the route hasn't
|
|
188
|
+
* bound an idField — keeps handlers safe to author without checking
|
|
189
|
+
* the resource config.
|
|
190
|
+
*/
|
|
191
|
+
function getEntityIdField(req) {
|
|
192
|
+
return req.arc?.idField ?? "_id";
|
|
193
|
+
}
|
|
194
|
+
/**
|
|
195
|
+
* Read the URL `:id` path param value as the resource handle.
|
|
196
|
+
* Returns `undefined` when the route has no `:id` segment (collection
|
|
197
|
+
* routes) — handlers that need the entity must be on row routes.
|
|
198
|
+
*/
|
|
199
|
+
function getEntityId(req) {
|
|
200
|
+
if (req.arc?.entityId !== void 0) return req.arc.entityId;
|
|
201
|
+
return req.params?.id;
|
|
202
|
+
}
|
|
203
|
+
/**
|
|
204
|
+
* Compose a `findOne` filter that resolves the current request's
|
|
205
|
+
* entity, regardless of whether the resource binds `_id` or a custom
|
|
206
|
+
* field. Idiomatic shape for arc action handlers.
|
|
207
|
+
*
|
|
208
|
+
* ```ts
|
|
209
|
+
* const doc = await Model.findOne(getEntityQuery(req));
|
|
210
|
+
* ```
|
|
211
|
+
*
|
|
212
|
+
* Returns `{}` when the route has no entity context (collection routes
|
|
213
|
+
* or tests bypassing the router) — caller decides what that means.
|
|
214
|
+
*/
|
|
215
|
+
function getEntityQuery(req) {
|
|
216
|
+
const id = getEntityId(req);
|
|
217
|
+
if (id === void 0) return {};
|
|
218
|
+
return { [getEntityIdField(req)]: id };
|
|
219
|
+
}
|
|
220
|
+
//#endregion
|
|
221
|
+
export { mergeResourceConfig as a, defineResourceVariants as i, getEntityIdField as n, defineAction as o, getEntityQuery as r, defineAggregation as s, getEntityId as t };
|
|
@@ -2,8 +2,8 @@ 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
4
|
import { a as buildRouteHooks, i as selectPluginMw, l as buildAuthMiddlewareForPermissions, n as buildRouteConfig, r as resolveRouterPluginMw, s as routeHookOptions, t as buildRateLimitConfig, u as buildArcDecorator } from "./rateLimit-DrED-C4v.mjs";
|
|
5
|
-
import { n as buildActionPermissionMw, t as tryRegisterRoute } from "./registerRoute-
|
|
6
|
-
import { l as sendControllerResponse, r as resolvePipelineSteps, t as buildActionPipelineHandler } from "./pipeline-
|
|
5
|
+
import { n as buildActionPermissionMw, t as tryRegisterRoute } from "./registerRoute-DTp18Ah_.mjs";
|
|
6
|
+
import { l as sendControllerResponse, r as resolvePipelineSteps, t as buildActionPipelineHandler } from "./pipeline-BGRnV-To.mjs";
|
|
7
7
|
import { n as schemaIRToJsonSchemaBranch, t as normalizeSchemaIR } from "./schemaIR-Ca7ySNPx.mjs";
|
|
8
8
|
//#region src/core/createActionRouter.ts
|
|
9
9
|
var createActionRouter_exports = /* @__PURE__ */ __exportAll({
|
|
@@ -214,34 +214,6 @@ function describeModuleCycle(remaining, byName) {
|
|
|
214
214
|
return `[arc] module dependency cycle: ${cycle ? cycle.join(" → ") : [...remaining].join(", ")}. Modules cannot dependsOn each other circularly — break it with a shared module both point at, or wire the softer direction through an event/port instead of a hard dependsOn.`;
|
|
215
215
|
}
|
|
216
216
|
//#endregion
|
|
217
|
-
//#region src/factory/module/index.ts
|
|
218
|
-
/**
|
|
219
|
-
* Identity helper for authoring a typed module — mirrors `defineResource`.
|
|
220
|
-
* Gives inference + a single obvious construction site; does no work.
|
|
221
|
-
*
|
|
222
|
-
* `TExports` is inferred from the `bootstrap` return value, so a module
|
|
223
|
-
* author gets a typed public export for free:
|
|
224
|
-
*
|
|
225
|
-
* ```ts
|
|
226
|
-
* export const accountingModule = (deps: Deps) =>
|
|
227
|
-
* defineModule({
|
|
228
|
-
* name: "accounting",
|
|
229
|
-
* bootstrap: async () => createAccountingEngine(deps), // TExports inferred
|
|
230
|
-
* });
|
|
231
|
-
* ```
|
|
232
|
-
*/
|
|
233
|
-
function defineModule(module) {
|
|
234
|
-
return module;
|
|
235
|
-
}
|
|
236
|
-
function getModuleExports(fastify, name) {
|
|
237
|
-
const modules = fastify.arc?.modules;
|
|
238
|
-
if (!modules || !Object.hasOwn(modules, name)) {
|
|
239
|
-
const available = modules ? Object.keys(modules) : [];
|
|
240
|
-
throw new Error(`[arc] no public export recorded for module "${name}".\nA module's export is its bootstrap() return value; it is readable only AFTER that module's bootstrap ran (dependsOn / list order = init order).\nModules with recorded exports: ${available.length ? available.join(", ") : "(none)"}.`);
|
|
241
|
-
}
|
|
242
|
-
return modules[name];
|
|
243
|
-
}
|
|
244
|
-
//#endregion
|
|
245
217
|
//#region src/factory/presets.ts
|
|
246
218
|
/**
|
|
247
219
|
* Production preset - strict security, performance optimized
|
|
@@ -539,7 +511,7 @@ async function registerArcPlugins(fastify, config, trackPlugin, modules, moduleH
|
|
|
539
511
|
}
|
|
540
512
|
if (config.arcPlugins?.realtime) if (config.arcPlugins?.events === false) fastify.log.warn("Realtime plugin requires events plugin (arcPlugins.events). Realtime disabled.");
|
|
541
513
|
else {
|
|
542
|
-
const { default: realtimePlugin } = await import("./realtime-
|
|
514
|
+
const { default: realtimePlugin } = await import("./realtime-DEuDu9io.mjs").then((n) => n.r);
|
|
543
515
|
const opts = config.arcPlugins.realtime === true ? {} : config.arcPlugins.realtime;
|
|
544
516
|
await fastify.register(realtimePlugin, opts);
|
|
545
517
|
trackPlugin("arc-realtime", opts);
|
|
@@ -1466,48 +1438,4 @@ const ArcFactory = {
|
|
|
1466
1438
|
}
|
|
1467
1439
|
};
|
|
1468
1440
|
//#endregion
|
|
1469
|
-
|
|
1470
|
-
/**
|
|
1471
|
-
* Normalize an eager `import.meta.glob` result into a `ResourceLike[]`.
|
|
1472
|
-
*
|
|
1473
|
-
* Accepts either:
|
|
1474
|
-
* - `{ import: 'default' }` form: values are the resource directly
|
|
1475
|
-
* - default form: values are the full module — picks first export with `toPlugin()`
|
|
1476
|
-
*
|
|
1477
|
-
* Throws if any module doesn't yield a valid `ResourceLike`.
|
|
1478
|
-
*/
|
|
1479
|
-
function preloadResources(globResult) {
|
|
1480
|
-
const resources = [];
|
|
1481
|
-
for (const [path, value] of Object.entries(globResult)) {
|
|
1482
|
-
const resource = pickResource(value);
|
|
1483
|
-
if (!resource) throw new Error(`preloadResources: ${path} does not export a valid resource.\n Expected: a default export OR a named export with toPlugin().`);
|
|
1484
|
-
resources.push(resource);
|
|
1485
|
-
}
|
|
1486
|
-
return resources.sort((a, b) => (a.name ?? "").localeCompare(b.name ?? ""));
|
|
1487
|
-
}
|
|
1488
|
-
/**
|
|
1489
|
-
* Normalize a lazy `import.meta.glob` result into a `Promise<ResourceLike[]>`.
|
|
1490
|
-
*
|
|
1491
|
-
* Use this when resources depend on prior bootstrap (e.g., engine init) and
|
|
1492
|
-
* cannot be evaluated at import time of the preload file.
|
|
1493
|
-
*/
|
|
1494
|
-
async function preloadResourcesAsync(globResult) {
|
|
1495
|
-
return (await Promise.all(Object.entries(globResult).map(async ([path, loader]) => {
|
|
1496
|
-
const resource = pickResource(await loader());
|
|
1497
|
-
if (!resource) throw new Error(`preloadResourcesAsync: ${path} does not export a valid resource.\n Expected: a default export OR a named export with toPlugin().`);
|
|
1498
|
-
return resource;
|
|
1499
|
-
}))).sort((a, b) => (a.name ?? "").localeCompare(b.name ?? ""));
|
|
1500
|
-
}
|
|
1501
|
-
function pickResource(value) {
|
|
1502
|
-
if (!value || typeof value !== "object") return void 0;
|
|
1503
|
-
if (typeof value.toPlugin === "function") return value;
|
|
1504
|
-
const mod = value;
|
|
1505
|
-
const candidates = [
|
|
1506
|
-
mod.default,
|
|
1507
|
-
mod.resource,
|
|
1508
|
-
...Object.values(mod)
|
|
1509
|
-
];
|
|
1510
|
-
for (const c of candidates) if (c && typeof c === "object" && typeof c.toPlugin === "function") return c;
|
|
1511
|
-
}
|
|
1512
|
-
//#endregion
|
|
1513
|
-
export { collectModuleScheduledJobs as _, createApp_exports as a, resolveModule as b, getPreset as c, workerPreset as d, defineModule as f, collectModuleHealthChecks as g, subscribeModuleEventHandlers as h, createApp as i, productionPreset as l, orderModules as m, preloadResourcesAsync as n, developmentPreset as o, getModuleExports as p, ArcFactory as r, edgePreset as s, preloadResources as t, testingPreset as u, collectModuleWorkflows as v, resolveContribution as y };
|
|
1441
|
+
export { edgePreset as a, testingPreset as c, subscribeModuleEventHandlers as d, collectModuleHealthChecks as f, resolveModule as g, resolveContribution as h, developmentPreset as i, workerPreset as l, collectModuleWorkflows as m, createApp as n, getPreset as o, collectModuleScheduledJobs as p, createApp_exports as r, productionPreset as s, ArcFactory as t, orderModules as u };
|
|
@@ -3,15 +3,15 @@ import { arcLog } from "./logger/index.mjs";
|
|
|
3
3
|
import { r as ForbiddenError } from "./errors-DkO8Nk6-.mjs";
|
|
4
4
|
import { n as getUserRoles } from "./userHelpers-Cg_y3fBz.mjs";
|
|
5
5
|
import { b as isElevated, n as PUBLIC_SCOPE, x as isMember } from "./types-CSyCxl2V.mjs";
|
|
6
|
-
import { d as DEFAULT_FIELD_WRITE_DENIAL_POLICY, t as BaseController } from "./BaseController
|
|
6
|
+
import { d as DEFAULT_FIELD_WRITE_DENIAL_POLICY, t as BaseController } from "./BaseController-B65zbz9m.mjs";
|
|
7
7
|
import { a as buildRouteHooks, c as buildAuthMiddleware, i as selectPluginMw, n as buildRouteConfig, o as resolveRoutePreHandlers, r as resolveRouterPluginMw, s as routeHookOptions, t as buildRateLimitConfig, u as buildArcDecorator } from "./rateLimit-DrED-C4v.mjs";
|
|
8
|
-
import { a as applyFieldWritePermissions, s as resolveEffectiveRoles } from "./applyPermissionResult-
|
|
9
|
-
import { r as buildCrudPermissionMw, t as tryRegisterRoute } from "./registerRoute-
|
|
8
|
+
import { a as applyFieldWritePermissions, s as resolveEffectiveRoles } from "./applyPermissionResult-BnyVE9eD.mjs";
|
|
9
|
+
import { r as buildCrudPermissionMw, t as tryRegisterRoute } from "./registerRoute-DTp18Ah_.mjs";
|
|
10
10
|
import { c as getDefaultCrudSchemas } from "./utils-taUVD4p3.mjs";
|
|
11
11
|
import { n as convertRouteSchema, r as ensureSchemaConverter, t as convertOpenApiSchemas } from "./schemaConverter-DJvodiHj.mjs";
|
|
12
12
|
import { i as pipeUIMessageStreamToReply, r as isReadableStream } from "./streaming-BQcW6_Kq.mjs";
|
|
13
13
|
import { t as hasEvents } from "./typeGuards-C9KXUly6.mjs";
|
|
14
|
-
import { a as createFastifyHandler, i as createCrudHandlers, n as buildPipelineHandler, o as createRequestContext, r as resolvePipelineSteps, u as buildRequestScopeProjection } from "./pipeline-
|
|
14
|
+
import { a as createFastifyHandler, i as createCrudHandlers, n as buildPipelineHandler, o as createRequestContext, r as resolvePipelineSteps, u as buildRequestScopeProjection } from "./pipeline-BGRnV-To.mjs";
|
|
15
15
|
import { A as requireAuth } from "./permissions-BrtiBKGs.mjs";
|
|
16
16
|
import { r as getAvailablePresets, t as applyPresets } from "./presets-DwFDVKP0.mjs";
|
|
17
17
|
import { t as pluralize } from "./pluralize-BhrgwZb9.mjs";
|
|
@@ -55,52 +55,6 @@ function methodCarriesBody(method) {
|
|
|
55
55
|
return m === "POST" || m === "PUT" || m === "PATCH";
|
|
56
56
|
}
|
|
57
57
|
//#endregion
|
|
58
|
-
//#region src/core/aggregation/defineAggregation.ts
|
|
59
|
-
/**
|
|
60
|
-
* Declare a single resource aggregation. Exported configs flow into
|
|
61
|
-
* `defineResource({ aggregations: { ... } })` either as named keys or
|
|
62
|
-
* via auto-discovery patterns (loadAggregations — future).
|
|
63
|
-
*
|
|
64
|
-
* @example Inline
|
|
65
|
-
* ```ts
|
|
66
|
-
* defineResource({
|
|
67
|
-
* name: 'order',
|
|
68
|
-
* aggregations: {
|
|
69
|
-
* revenueByStatus: defineAggregation({
|
|
70
|
-
* groupBy: 'status',
|
|
71
|
-
* measures: { count: 'count', revenue: 'sum:totalPrice' },
|
|
72
|
-
* permissions: requireRoles(['admin']),
|
|
73
|
-
* }),
|
|
74
|
-
* },
|
|
75
|
-
* });
|
|
76
|
-
* ```
|
|
77
|
-
*
|
|
78
|
-
* @example Multi-file
|
|
79
|
-
* ```ts
|
|
80
|
-
* // orders/aggregations/revenue-by-status.ts
|
|
81
|
-
* import { defineAggregation } from '@classytic/arc';
|
|
82
|
-
*
|
|
83
|
-
* export const revenueByStatus = defineAggregation({
|
|
84
|
-
* groupBy: 'status',
|
|
85
|
-
* measures: { count: 'count', revenue: 'sum:totalPrice' },
|
|
86
|
-
* permissions: requireRoles(['admin']),
|
|
87
|
-
* timeout: 5000,
|
|
88
|
-
* cache: { staleTime: 60 },
|
|
89
|
-
* });
|
|
90
|
-
*
|
|
91
|
-
* // orders/order.resource.ts
|
|
92
|
-
* import * as aggregations from './aggregations/index.js';
|
|
93
|
-
*
|
|
94
|
-
* defineResource({
|
|
95
|
-
* name: 'order',
|
|
96
|
-
* aggregations, // 30+ entries flow in
|
|
97
|
-
* });
|
|
98
|
-
* ```
|
|
99
|
-
*/
|
|
100
|
-
function defineAggregation(config) {
|
|
101
|
-
return config;
|
|
102
|
-
}
|
|
103
|
-
//#endregion
|
|
104
58
|
//#region src/core/createCrudRouter.ts
|
|
105
59
|
/**
|
|
106
60
|
* Mount custom routes (from presets or user-defined `routes`) on Fastify.
|
|
@@ -340,80 +294,6 @@ function createPermissionMiddleware(permission, resourceName, action) {
|
|
|
340
294
|
return buildCrudPermissionMw(permission, resourceName, action);
|
|
341
295
|
}
|
|
342
296
|
//#endregion
|
|
343
|
-
//#region src/core/defineAction.ts
|
|
344
|
-
/**
|
|
345
|
-
* Build an `ActionDefinition` with a typed handler. The literal schema
|
|
346
|
-
* type captured here flows into `data`, so `defineAction({ schema:
|
|
347
|
-
* z.object({...}), handler })` produces fully-typed code with no
|
|
348
|
-
* `as MyShape` cast.
|
|
349
|
-
*
|
|
350
|
-
* Behaviorally identical to a bare `ActionDefinition` object — same
|
|
351
|
-
* validation path (AJV), same permission resolution, same MCP wiring.
|
|
352
|
-
* The runtime shape is unchanged; only the type-level inference is new.
|
|
353
|
-
*/
|
|
354
|
-
function defineAction(config) {
|
|
355
|
-
return {
|
|
356
|
-
handler: config.handler,
|
|
357
|
-
permissions: config.permissions,
|
|
358
|
-
schema: config.schema,
|
|
359
|
-
description: config.description,
|
|
360
|
-
id: config.id,
|
|
361
|
-
mcp: config.mcp
|
|
362
|
-
};
|
|
363
|
-
}
|
|
364
|
-
//#endregion
|
|
365
|
-
//#region src/core/defineResource/seams.ts
|
|
366
|
-
/** Plain object test — literals only; class instances must replace, not merge. */
|
|
367
|
-
function isPlainObject(value) {
|
|
368
|
-
if (value === null || typeof value !== "object" || Array.isArray(value)) return false;
|
|
369
|
-
const proto = Object.getPrototypeOf(value);
|
|
370
|
-
return proto === Object.prototype || proto === null;
|
|
371
|
-
}
|
|
372
|
-
/** Recursive plain-object merge; nested arrays REPLACE (they're value lists). */
|
|
373
|
-
function mergePlain(base, next) {
|
|
374
|
-
const out = { ...base };
|
|
375
|
-
for (const [key, value] of Object.entries(next)) {
|
|
376
|
-
if (value === void 0) continue;
|
|
377
|
-
const existing = out[key];
|
|
378
|
-
out[key] = isPlainObject(existing) && isPlainObject(value) ? mergePlain(existing, value) : value;
|
|
379
|
-
}
|
|
380
|
-
return out;
|
|
381
|
-
}
|
|
382
|
-
/**
|
|
383
|
-
* Canonical slot-aware merge of a base resource config with host seams.
|
|
384
|
-
* Later seams win over earlier ones; `undefined` values never clobber.
|
|
385
|
-
*
|
|
386
|
-
* Merge semantics (value-driven — no per-slot registry to drift):
|
|
387
|
-
*
|
|
388
|
-
* - **Top-level arrays CONCAT** — `routes`, `middlewares`, `presets`,
|
|
389
|
-
* `routeGuards`, `disabledRoutes`: additive collections, base first.
|
|
390
|
-
* - **Plain objects merge recursively** — `permissions`, `actions`,
|
|
391
|
-
* `schemaOptions` (incl. `fieldRules`), `aggregations`, `customSchemas`,
|
|
392
|
-
* `fields`, `cache`, `events`: per-key composition. Arrays NESTED inside
|
|
393
|
-
* them replace (they're value lists — enums, sort specs), not concat.
|
|
394
|
-
* - **Everything else last-wins** — class instances (`adapter`,
|
|
395
|
-
* `controller`, `queryParser`), functions, scalars.
|
|
396
|
-
*
|
|
397
|
-
* The single `as` at the return is the one documented erasure for the
|
|
398
|
-
* {@link AdapterLike}-widened seam slots — inside the helper so no module
|
|
399
|
-
* author ever writes one (mirrors mongokit's one-cast-at-the-boundary rule).
|
|
400
|
-
*/
|
|
401
|
-
function mergeResourceConfig(base, ...seams) {
|
|
402
|
-
const out = { ...base };
|
|
403
|
-
for (const seam of seams) {
|
|
404
|
-
if (!seam) continue;
|
|
405
|
-
for (const [key, value] of Object.entries(seam)) {
|
|
406
|
-
if (value === void 0) continue;
|
|
407
|
-
const existing = out[key];
|
|
408
|
-
if (Array.isArray(existing) && Array.isArray(value)) out[key] = [...existing, ...value];
|
|
409
|
-
else if (isPlainObject(existing) && isPlainObject(value)) out[key] = mergePlain(existing, value);
|
|
410
|
-
else if (Array.isArray(value)) out[key] = [...value];
|
|
411
|
-
else out[key] = value;
|
|
412
|
-
}
|
|
413
|
-
}
|
|
414
|
-
return out;
|
|
415
|
-
}
|
|
416
|
-
//#endregion
|
|
417
297
|
//#region src/core/defineResource/controller.ts
|
|
418
298
|
/**
|
|
419
299
|
* Phase 4 — pick (or auto-create) the resource's controller.
|
|
@@ -1398,7 +1278,7 @@ function buildResourcePlugin(resource) {
|
|
|
1398
1278
|
extensions: resource.extensions
|
|
1399
1279
|
});
|
|
1400
1280
|
if (resource.actions && Object.keys(resource.actions).length > 0) {
|
|
1401
|
-
const { createActionRouter } = await import("./createActionRouter-
|
|
1281
|
+
const { createActionRouter } = await import("./createActionRouter-C-N7z2EL.mjs").then((n) => n.n);
|
|
1402
1282
|
createActionRouter(typedInstance, {
|
|
1403
1283
|
...normalizeActionsToRouterConfig(resource.actions, resource.actionPermissions, resource.tag, resource.permissions, resource.name, typedInstance.log),
|
|
1404
1284
|
resourceName: resource.name,
|
|
@@ -2308,102 +2188,4 @@ function defineResource(config) {
|
|
|
2308
2188
|
return resource;
|
|
2309
2189
|
}
|
|
2310
2190
|
//#endregion
|
|
2311
|
-
|
|
2312
|
-
/**
|
|
2313
|
-
* Define multiple resources from a shared base config and per-variant overrides.
|
|
2314
|
-
*
|
|
2315
|
-
* Each variant is independently passed through `defineResource()` — the
|
|
2316
|
-
* returned `ResourceDefinition`s are real, fully-registered resources.
|
|
2317
|
-
* Register each one's plugin in your app:
|
|
2318
|
-
*
|
|
2319
|
-
* ```typescript
|
|
2320
|
-
* await app.register(articlePublic.toPlugin());
|
|
2321
|
-
* await app.register(articleAdmin.toPlugin());
|
|
2322
|
-
* ```
|
|
2323
|
-
*
|
|
2324
|
-
* @param base Shared config — adapter, queryParser, schemaOptions, hooks, etc.
|
|
2325
|
-
* Must NOT include `name` or `prefix` (those are per-variant).
|
|
2326
|
-
* @param variants Map of variant key → override. Each variant must declare
|
|
2327
|
-
* its own `name` and `prefix`. Other fields override the base.
|
|
2328
|
-
* @returns A record where each key from `variants` maps to a real
|
|
2329
|
-
* `ResourceDefinition` ready for `.toPlugin()` registration.
|
|
2330
|
-
*/
|
|
2331
|
-
function defineResourceVariants(base, variants) {
|
|
2332
|
-
const out = {};
|
|
2333
|
-
for (const key of Object.keys(variants)) {
|
|
2334
|
-
const override = variants[key];
|
|
2335
|
-
out[key] = defineResource({
|
|
2336
|
-
...base,
|
|
2337
|
-
...override
|
|
2338
|
-
});
|
|
2339
|
-
}
|
|
2340
|
-
return out;
|
|
2341
|
-
}
|
|
2342
|
-
//#endregion
|
|
2343
|
-
//#region src/core/entityHelpers.ts
|
|
2344
|
-
/**
|
|
2345
|
-
* Per-request entity helpers — read the resource binding (idField + the
|
|
2346
|
-
* URL `:id` value) off `req.arc`.
|
|
2347
|
-
*
|
|
2348
|
-
* Action handlers receive their `id` argument as the raw URL `:id` value.
|
|
2349
|
-
* When the resource declares a custom `idField` (`slug`, `reportId`, …)
|
|
2350
|
-
* that's NOT the document `_id`, a naive `Model.findById(id)` silently
|
|
2351
|
-
* returns null. The historical footgun was a `findById(id)` typo where
|
|
2352
|
-
* the handler author hadn't realised that `:id` resolves to the friendly
|
|
2353
|
-
* handle.
|
|
2354
|
-
*
|
|
2355
|
-
* `getEntityQuery(req)` produces the canonical filter shape so handlers
|
|
2356
|
-
* compose lookups with no resource-config recall:
|
|
2357
|
-
*
|
|
2358
|
-
* ```ts
|
|
2359
|
-
* actions: {
|
|
2360
|
-
* archive: {
|
|
2361
|
-
* handler: async (id, data, req) => {
|
|
2362
|
-
* const doc = await Model.findOne(getEntityQuery(req));
|
|
2363
|
-
* if (!doc) throw new NotFoundError("Order");
|
|
2364
|
-
* // ...
|
|
2365
|
-
* },
|
|
2366
|
-
* },
|
|
2367
|
-
* }
|
|
2368
|
-
* ```
|
|
2369
|
-
*
|
|
2370
|
-
* The router populates `req.arc.idField` and `req.arc.entityId` before
|
|
2371
|
-
* invoking the handler — these helpers are zero-cost reads.
|
|
2372
|
-
*/
|
|
2373
|
-
/**
|
|
2374
|
-
* Read the resource's configured `idField` for the current request.
|
|
2375
|
-
* Falls back to the framework default (`_id`) when the route hasn't
|
|
2376
|
-
* bound an idField — keeps handlers safe to author without checking
|
|
2377
|
-
* the resource config.
|
|
2378
|
-
*/
|
|
2379
|
-
function getEntityIdField(req) {
|
|
2380
|
-
return req.arc?.idField ?? "_id";
|
|
2381
|
-
}
|
|
2382
|
-
/**
|
|
2383
|
-
* Read the URL `:id` path param value as the resource handle.
|
|
2384
|
-
* Returns `undefined` when the route has no `:id` segment (collection
|
|
2385
|
-
* routes) — handlers that need the entity must be on row routes.
|
|
2386
|
-
*/
|
|
2387
|
-
function getEntityId(req) {
|
|
2388
|
-
if (req.arc?.entityId !== void 0) return req.arc.entityId;
|
|
2389
|
-
return req.params?.id;
|
|
2390
|
-
}
|
|
2391
|
-
/**
|
|
2392
|
-
* Compose a `findOne` filter that resolves the current request's
|
|
2393
|
-
* entity, regardless of whether the resource binds `_id` or a custom
|
|
2394
|
-
* field. Idiomatic shape for arc action handlers.
|
|
2395
|
-
*
|
|
2396
|
-
* ```ts
|
|
2397
|
-
* const doc = await Model.findOne(getEntityQuery(req));
|
|
2398
|
-
* ```
|
|
2399
|
-
*
|
|
2400
|
-
* Returns `{}` when the route has no entity context (collection routes
|
|
2401
|
-
* or tests bypassing the router) — caller decides what that means.
|
|
2402
|
-
*/
|
|
2403
|
-
function getEntityQuery(req) {
|
|
2404
|
-
const id = getEntityId(req);
|
|
2405
|
-
if (id === void 0) return {};
|
|
2406
|
-
return { [getEntityIdField(req)]: id };
|
|
2407
|
-
}
|
|
2408
|
-
//#endregion
|
|
2409
|
-
export { defineResource as a, validateResourceConfig as c, defineAction as d, createCrudRouter as f, defineResourceVariants as i, ResourceDefinition as l, defineAggregation as m, getEntityIdField as n, assertValidConfig as o, createPermissionMiddleware as p, getEntityQuery as r, formatValidationErrors as s, getEntityId as t, mergeResourceConfig as u };
|
|
2191
|
+
export { ResourceDefinition as a, validateResourceConfig as i, assertValidConfig as n, createCrudRouter as o, formatValidationErrors as r, createPermissionMiddleware as s, defineResource as t };
|
package/dist/docs/index.mjs
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { n as getUserRoles } from "../userHelpers-Cg_y3fBz.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-BLV4OMXb.mjs";
|
|
3
3
|
import fp from "fastify-plugin";
|
|
4
4
|
//#region src/docs/scalar.ts
|
|
5
5
|
const scalarPlugin = async (fastify, opts = {}) => {
|