@opensaas/stack-core 0.36.0 → 0.38.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/.turbo/turbo-build.log +1 -1
- package/CHANGELOG.md +121 -0
- package/CLAUDE.md +21 -3
- package/dist/access/access-filter.d.ts +30 -118
- package/dist/access/access-filter.d.ts.map +1 -1
- package/dist/access/access-filter.js +70 -206
- package/dist/access/access-filter.js.map +1 -1
- package/dist/access/access-filter.test.js +148 -188
- package/dist/access/access-filter.test.js.map +1 -1
- package/dist/access/declared-dependencies.d.ts +66 -26
- package/dist/access/declared-dependencies.d.ts.map +1 -1
- package/dist/access/declared-dependencies.js +67 -17
- package/dist/access/declared-dependencies.js.map +1 -1
- package/dist/access/declared-dependencies.test.d.ts +2 -0
- package/dist/access/declared-dependencies.test.d.ts.map +1 -0
- package/dist/access/declared-dependencies.test.js +226 -0
- package/dist/access/declared-dependencies.test.js.map +1 -0
- package/dist/access/depth-limits.d.ts +8 -7
- package/dist/access/depth-limits.d.ts.map +1 -1
- package/dist/access/depth-limits.js +8 -7
- package/dist/access/depth-limits.js.map +1 -1
- package/dist/access/errors.d.ts +12 -8
- package/dist/access/errors.d.ts.map +1 -1
- package/dist/access/errors.js +16 -12
- package/dist/access/errors.js.map +1 -1
- package/dist/access/field-visibility.d.ts +2 -1
- package/dist/access/field-visibility.d.ts.map +1 -1
- package/dist/access/field-visibility.js +91 -17
- package/dist/access/field-visibility.js.map +1 -1
- package/dist/access/index.d.ts +1 -2
- package/dist/access/index.d.ts.map +1 -1
- package/dist/access/index.js +1 -1
- package/dist/access/index.js.map +1 -1
- package/dist/access/relationship-count.d.ts +1 -1
- package/dist/config/index.d.ts +1 -1
- package/dist/config/index.d.ts.map +1 -1
- package/dist/config/types.d.ts +126 -0
- package/dist/config/types.d.ts.map +1 -1
- package/dist/context/index.d.ts.map +1 -1
- package/dist/context/index.js +38 -27
- package/dist/context/index.js.map +1 -1
- package/dist/fields/index.d.ts.map +1 -1
- package/dist/fields/index.js +28 -5
- package/dist/fields/index.js.map +1 -1
- package/dist/index.d.ts +1 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js.map +1 -1
- package/dist/query/index.d.ts +29 -0
- package/dist/query/index.d.ts.map +1 -1
- package/dist/query/index.js +27 -0
- package/dist/query/index.js.map +1 -1
- package/dist/query/relationship-options.d.ts +1 -1
- package/dist/query/relationship-options.js +1 -1
- package/package.json +1 -1
- package/src/access/access-filter.test.ts +205 -275
- package/src/access/access-filter.ts +84 -267
- package/src/access/declared-dependencies.test.ts +277 -0
- package/src/access/declared-dependencies.ts +122 -37
- package/src/access/depth-limits.ts +8 -7
- package/src/access/errors.ts +16 -12
- package/src/access/field-visibility.ts +99 -14
- package/src/access/index.ts +1 -7
- package/src/access/relationship-count.ts +1 -1
- package/src/config/index.ts +2 -0
- package/src/config/types.ts +130 -0
- package/src/context/index.ts +52 -33
- package/src/fields/index.ts +35 -5
- package/src/index.ts +2 -0
- package/src/query/index.ts +53 -0
- package/src/query/relationship-options.ts +1 -1
- package/tests/access-relationships.test.ts +18 -16
- package/tests/computed-field-selective-evaluation.test.ts +418 -0
- package/tests/context.test.ts +27 -0
- package/tests/field-types.test.ts +12 -0
- package/tests/needs-declared-dependencies.test.ts +7 -4
- package/tests/resolve-chain.test.ts +11 -11
- package/tsconfig.tsbuildinfo +1 -1
package/dist/access/errors.js
CHANGED
|
@@ -1,25 +1,29 @@
|
|
|
1
1
|
import { READ_INCLUDE_MAX_DEPTH } from './depth-limits.js';
|
|
2
2
|
/**
|
|
3
3
|
* Thrown when a caller-supplied `include` names a relation nested deeper than
|
|
4
|
-
*
|
|
5
|
-
*
|
|
6
|
-
*
|
|
7
|
-
*
|
|
8
|
-
* swallow this.
|
|
4
|
+
* `READ_INCLUDE_MAX_DEPTH`. Deliberately distinct from `ValidationError`: this
|
|
5
|
+
* is not bad user input, it is the engine declining to serve a tree this
|
|
6
|
+
* expensive. Code that catches `ValidationError` to report form errors must
|
|
7
|
+
* not silently swallow this.
|
|
9
8
|
*
|
|
10
|
-
* Only an explicit
|
|
11
|
-
*
|
|
12
|
-
*
|
|
9
|
+
* Only an explicit request naming something at or past the cap triggers this
|
|
10
|
+
* — a read that simply doesn't reach this deep never throws. Before
|
|
11
|
+
* ADR-0026 made the read pipeline caller-directed, this cap was the engine's
|
|
12
|
+
* last line of defense against returning a relation it could not prove was
|
|
13
|
+
* row/field scoped (ADR-0022, issue #830); a request naming anything at this
|
|
14
|
+
* depth is now scoped exactly like every other named relation; the cap
|
|
15
|
+
* exists solely to bound how deep a request may cost the engine to serve. See
|
|
16
|
+
* ADR-0026 and `docs/adr/0022-access-control-fails-closed-when-it-cannot-scope.md`.
|
|
13
17
|
*/
|
|
14
18
|
export class AccessScopeDepthExceededError extends Error {
|
|
15
19
|
listKey;
|
|
16
20
|
fieldKey;
|
|
17
21
|
depth;
|
|
18
22
|
constructor(listKey, fieldKey, depth) {
|
|
19
|
-
super(`Cannot
|
|
20
|
-
`
|
|
21
|
-
`
|
|
22
|
-
`
|
|
23
|
+
super(`Cannot include "${listKey}.${fieldKey}" at include depth ${depth}: this exceeds the read ` +
|
|
24
|
+
`pipeline's maximum include depth (${READ_INCLUDE_MAX_DEPTH}). This is a cost limit, not an ` +
|
|
25
|
+
`inability to scope — the engine declines to serve a tree this deep rather than returning ` +
|
|
26
|
+
`it. Restructure the query to fetch this relation separately.`);
|
|
23
27
|
this.name = 'AccessScopeDepthExceededError';
|
|
24
28
|
this.listKey = listKey;
|
|
25
29
|
this.fieldKey = fieldKey;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"errors.js","sourceRoot":"","sources":["../../src/access/errors.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,sBAAsB,EAAE,MAAM,mBAAmB,CAAA;AAE1D
|
|
1
|
+
{"version":3,"file":"errors.js","sourceRoot":"","sources":["../../src/access/errors.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,sBAAsB,EAAE,MAAM,mBAAmB,CAAA;AAE1D;;;;;;;;;;;;;;;GAeG;AACH,MAAM,OAAO,6BAA8B,SAAQ,KAAK;IAC/C,OAAO,CAAQ;IACf,QAAQ,CAAQ;IAChB,KAAK,CAAQ;IAEpB,YAAY,OAAe,EAAE,QAAgB,EAAE,KAAa;QAC1D,KAAK,CACH,mBAAmB,OAAO,IAAI,QAAQ,sBAAsB,KAAK,0BAA0B;YACzF,qCAAqC,sBAAsB,kCAAkC;YAC7F,2FAA2F;YAC3F,8DAA8D,CACjE,CAAA;QACD,IAAI,CAAC,IAAI,GAAG,+BAA+B,CAAA;QAC3C,IAAI,CAAC,OAAO,GAAG,OAAO,CAAA;QACtB,IAAI,CAAC,QAAQ,GAAG,QAAQ,CAAA;QACxB,IAAI,CAAC,KAAK,GAAG,KAAK,CAAA;IACpB,CAAC;CACF;AAED;;;;;;;;;;;;;GAaG;AACH,MAAM,OAAO,uBAAwB,SAAQ,KAAK;IACzC,KAAK,CAAkD;IAE9D,YAAY,KAAuD;QACjE,MAAM,IAAI,GAAG,KAAK,CAAC,GAAG,CAAC,CAAC,IAAI,EAAE,EAAE,CAAC,GAAG,IAAI,CAAC,OAAO,IAAI,IAAI,CAAC,QAAQ,EAAE,CAAC,CAAC,IAAI,CAAC,KAAK,CAAC,CAAA;QAChF,KAAK,CACH,iCAAiC,IAAI,+CAA+C;YAClF,wFAAwF;YACxF,0FAA0F;YAC1F,iCAAiC,CACpC,CAAA;QACD,IAAI,CAAC,IAAI,GAAG,yBAAyB,CAAA;QACrC,IAAI,CAAC,KAAK,GAAG,KAAK,CAAA;IACpB,CAAC;CACF"}
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import type { Session, AccessContext } from './types.js';
|
|
2
2
|
import type { OpenSaasConfig, FieldConfig } from '../config/types.js';
|
|
3
3
|
import type { DeclaredOnlyTree } from './declared-dependencies.js';
|
|
4
|
+
import type { FieldSelectionScope } from '../query/index.js';
|
|
4
5
|
/**
|
|
5
6
|
* Filter fields from an object based on read access
|
|
6
7
|
* Recursively applies access control to nested relationships
|
|
@@ -10,5 +11,5 @@ export declare function filterReadableFields<T extends Record<string, unknown>>(
|
|
|
10
11
|
context: AccessContext & {
|
|
11
12
|
_isSudo?: boolean;
|
|
12
13
|
};
|
|
13
|
-
}, config?: OpenSaasConfig, depth?: number, listKey?: string, declaredOnly?: DeclaredOnlyTree): Promise<Partial<T>>;
|
|
14
|
+
}, config?: OpenSaasConfig, depth?: number, listKey?: string, declaredOnly?: DeclaredOnlyTree, selection?: FieldSelectionScope): Promise<Partial<T>>;
|
|
14
15
|
//# sourceMappingURL=field-visibility.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"field-visibility.d.ts","sourceRoot":"","sources":["../../src/access/field-visibility.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,OAAO,EAAE,aAAa,EAAE,MAAM,YAAY,CAAA;AACxD,OAAO,KAAK,EAAE,cAAc,EAAE,WAAW,EAAE,MAAM,oBAAoB,CAAA;AAKrE,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,4BAA4B,CAAA;
|
|
1
|
+
{"version":3,"file":"field-visibility.d.ts","sourceRoot":"","sources":["../../src/access/field-visibility.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,OAAO,EAAE,aAAa,EAAE,MAAM,YAAY,CAAA;AACxD,OAAO,KAAK,EAAE,cAAc,EAAE,WAAW,EAAE,MAAM,oBAAoB,CAAA;AAKrE,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,4BAA4B,CAAA;AAElE,OAAO,KAAK,EAAE,mBAAmB,EAAE,MAAM,mBAAmB,CAAA;AA+K5D;;;GAGG;AACH,wBAAsB,oBAAoB,CAAC,CAAC,SAAS,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EAC1E,IAAI,EAAE,CAAC,EACP,YAAY,EAAE,MAAM,CAAC,MAAM,EAAE,WAAW,CAAC,EACzC,IAAI,EAAE;IACJ,OAAO,EAAE,OAAO,GAAG,IAAI,CAAA;IACvB,OAAO,EAAE,aAAa,GAAG;QAAE,OAAO,CAAC,EAAE,OAAO,CAAA;KAAE,CAAA;CAC/C,EACD,MAAM,CAAC,EAAE,cAAc,EACvB,KAAK,GAAE,MAAU,EACjB,OAAO,CAAC,EAAE,MAAM,EAMhB,YAAY,GAAE,gBAA0C,EAOxD,SAAS,CAAC,EAAE,mBAAmB,GAC9B,OAAO,CAAC,OAAO,CAAC,CAAC,CAAC,CAAC,CAmPrB"}
|
|
@@ -54,8 +54,11 @@ function deriveResolveOutputContext(context, link, config) {
|
|
|
54
54
|
* from the result.
|
|
55
55
|
*
|
|
56
56
|
* `accessItem` is the row used to evaluate field access; `hookItem` is the
|
|
57
|
-
* object passed to the hook as `item
|
|
58
|
-
*
|
|
57
|
+
* object passed to the hook as `item`. For a stored field, both are
|
|
58
|
+
* `workingItem` (the row's own stored/fetched columns). For a virtual field,
|
|
59
|
+
* `hookItem` is `computedFieldItem` instead — the same stored columns with
|
|
60
|
+
* every skipped-or-denied key removed — so it never sees another computed
|
|
61
|
+
* field's resolved value (ADR-0027).
|
|
59
62
|
*/
|
|
60
63
|
async function resolveReadableFieldValue(params) {
|
|
61
64
|
const { fieldConfig, fieldName, value, accessItem, hookItem, listKey, args, config } = params;
|
|
@@ -114,7 +117,14 @@ export async function filterReadableFields(item, fieldConfigs, args, config, dep
|
|
|
114
117
|
// caller-named branch. Stripped from `filtered` right before it is
|
|
115
118
|
// returned — after resolveOutput has had a chance to read them — so a
|
|
116
119
|
// declared dependency never widens what the caller receives.
|
|
117
|
-
declaredOnly = emptyDeclaredOnlyTree()
|
|
120
|
+
declaredOnly = emptyDeclaredOnlyTree(),
|
|
121
|
+
// The fragment scope this level was reached under (ADR-0027), and the same
|
|
122
|
+
// tree one level down for each nested relation. `undefined` — the default,
|
|
123
|
+
// and what a bare/`include`-based read passes at every level — means
|
|
124
|
+
// unrestricted: every field on the list is computed, unchanged from
|
|
125
|
+
// before ADR-0027. Only a `query` fragment's own field selection ever
|
|
126
|
+
// restricts a level.
|
|
127
|
+
selection) {
|
|
118
128
|
const filtered = {};
|
|
119
129
|
// Multi-column fields (e.g. storage image()/file() in Keystone-parity mode)
|
|
120
130
|
// back several physical columns rather than one. Before the per-field pass,
|
|
@@ -139,6 +149,14 @@ declaredOnly = emptyDeclaredOnlyTree()) {
|
|
|
139
149
|
}
|
|
140
150
|
workingItem[fieldName] = assembled;
|
|
141
151
|
}
|
|
152
|
+
// Keys denied by field-level read access during the pass below — as opposed
|
|
153
|
+
// to a key merely skipped by `selection` or held back only for
|
|
154
|
+
// `declaredOnly` stripping. Tracked separately because a denied key must
|
|
155
|
+
// stay invisible to a computed field's hook (below), while a declared-only
|
|
156
|
+
// key must stay VISIBLE to one — that is the entire point of declaring it
|
|
157
|
+
// (ADR-0025) — even though `selection` above skipped adding it to
|
|
158
|
+
// `filtered` because the caller's fragment never asked for it.
|
|
159
|
+
const accessDeniedKeys = new Set();
|
|
142
160
|
// Process existing fields from the database result
|
|
143
161
|
for (const [fieldName, value] of Object.entries(workingItem)) {
|
|
144
162
|
const fieldConfig = fieldConfigs[fieldName];
|
|
@@ -147,8 +165,18 @@ declaredOnly = emptyDeclaredOnlyTree()) {
|
|
|
147
165
|
filtered[fieldName] = value;
|
|
148
166
|
continue;
|
|
149
167
|
}
|
|
168
|
+
// Projection-aware skip (ADR-0027): a fragment read that does not select
|
|
169
|
+
// this field does no work for it at all — no field-level read-access
|
|
170
|
+
// check, no resolveOutput, no recursion into a relation — because the
|
|
171
|
+
// read is never going to return it. `selection` is only ever restricted
|
|
172
|
+
// by a fragment's own field selection; a bare/`include`-based read, and a
|
|
173
|
+
// relation reached only to satisfy another field's `needs`, pass no
|
|
174
|
+
// selection at all and compute every field here, unchanged.
|
|
175
|
+
if (selection?.fields && !selection.fields.has(fieldName)) {
|
|
176
|
+
continue;
|
|
177
|
+
}
|
|
150
178
|
// Handle relationship fields - recursively filter fields within related items
|
|
151
|
-
// Note: Access control filtering is now done at database level via
|
|
179
|
+
// Note: Access control filtering is now done at database level via buildAccessScopedInclude
|
|
152
180
|
// This only handles field-level access (hiding sensitive fields)
|
|
153
181
|
//
|
|
154
182
|
// Deliberately uncapped: the row/relation scoping in access-filter.ts bounds
|
|
@@ -171,6 +199,7 @@ declaredOnly = emptyDeclaredOnlyTree()) {
|
|
|
171
199
|
item: workingItem,
|
|
172
200
|
});
|
|
173
201
|
if (!canRead) {
|
|
202
|
+
accessDeniedKeys.add(fieldName);
|
|
174
203
|
continue;
|
|
175
204
|
}
|
|
176
205
|
const relatedConfig = getRelatedListConfig(fieldConfig.ref, config);
|
|
@@ -179,16 +208,22 @@ declaredOnly = emptyDeclaredOnlyTree()) {
|
|
|
179
208
|
// back to an empty tree when this relation isn't declaration-related at
|
|
180
209
|
// all — the common case.
|
|
181
210
|
const nestedDeclaredOnly = declaredOnly.nested[fieldName] ?? emptyDeclaredOnlyTree();
|
|
211
|
+
// This relation's own fragment scope, if the caller's fragment named it
|
|
212
|
+
// with a nested Fragment/RelationSelector. `undefined` (a bare `true`
|
|
213
|
+
// selector, or no `selection` at all) means the nested list computes
|
|
214
|
+
// unrestricted — matching what naming a relation without narrowing it
|
|
215
|
+
// further has always meant.
|
|
216
|
+
const nestedSelection = selection?.nested[fieldName];
|
|
182
217
|
if (relatedConfig) {
|
|
183
218
|
// For many relationships (arrays) - recursively filter fields in each item
|
|
184
219
|
// The recursive call already handles applying resolveOutput hooks
|
|
185
220
|
if (Array.isArray(value)) {
|
|
186
|
-
filtered[fieldName] = await Promise.all(value.map((relatedItem) => filterReadableFields(relatedItem, relatedConfig.listConfig.fields, args, config, depth + 1, relatedConfig.listName, nestedDeclaredOnly)));
|
|
221
|
+
filtered[fieldName] = await Promise.all(value.map((relatedItem) => filterReadableFields(relatedItem, relatedConfig.listConfig.fields, args, config, depth + 1, relatedConfig.listName, nestedDeclaredOnly, nestedSelection)));
|
|
187
222
|
}
|
|
188
223
|
// For single relationships (objects) - recursively filter fields
|
|
189
224
|
// The recursive call already handles applying resolveOutput hooks
|
|
190
225
|
else if (typeof value === 'object') {
|
|
191
|
-
filtered[fieldName] = await filterReadableFields(value, relatedConfig.listConfig.fields, args, config, depth + 1, relatedConfig.listName, nestedDeclaredOnly);
|
|
226
|
+
filtered[fieldName] = await filterReadableFields(value, relatedConfig.listConfig.fields, args, config, depth + 1, relatedConfig.listName, nestedDeclaredOnly, nestedSelection);
|
|
192
227
|
}
|
|
193
228
|
}
|
|
194
229
|
else {
|
|
@@ -212,6 +247,38 @@ declaredOnly = emptyDeclaredOnlyTree()) {
|
|
|
212
247
|
if (result.readable) {
|
|
213
248
|
filtered[fieldName] = result.value;
|
|
214
249
|
}
|
|
250
|
+
else {
|
|
251
|
+
accessDeniedKeys.add(fieldName);
|
|
252
|
+
}
|
|
253
|
+
}
|
|
254
|
+
// The item a virtual field's hook sees: stored columns and fetched
|
|
255
|
+
// relations (from `workingItem`, never a resolved value — no hook's output
|
|
256
|
+
// is ever written back into `workingItem`). A key is visible here if it
|
|
257
|
+
// either survived into `filtered` (selected and allowed) OR exists only to
|
|
258
|
+
// satisfy a `needs` declaration (`declaredOnly` — that IS the point of
|
|
259
|
+
// declaring it: fetched for a hook, never for the caller, ADR-0025).
|
|
260
|
+
// Everything else — field-level denied, or skipped by `selection` and
|
|
261
|
+
// declared by no one — is deleted. A computed field reaches for exactly its
|
|
262
|
+
// own declared dependencies and nothing another field's hook produced
|
|
263
|
+
// (ADR-0027): reaching for a sibling that was denied or skipped-and-
|
|
264
|
+
// undeclared finds nothing there, the same as reaching for one never
|
|
265
|
+
// declared at all, and reaching for a sibling that DID survive finds its
|
|
266
|
+
// raw stored form, never another hook's resolved value — a virtual field
|
|
267
|
+
// computed earlier in declaration order is exactly as invisible as one
|
|
268
|
+
// computed later.
|
|
269
|
+
const computedFieldItem = { ...workingItem };
|
|
270
|
+
for (const key of Object.keys(workingItem)) {
|
|
271
|
+
if (['id', 'createdAt', 'updatedAt'].includes(key))
|
|
272
|
+
continue;
|
|
273
|
+
if (accessDeniedKeys.has(key)) {
|
|
274
|
+
delete computedFieldItem[key];
|
|
275
|
+
continue;
|
|
276
|
+
}
|
|
277
|
+
if (key in filtered)
|
|
278
|
+
continue;
|
|
279
|
+
if (declaredOnly.keys.has(key))
|
|
280
|
+
continue;
|
|
281
|
+
delete computedFieldItem[key];
|
|
215
282
|
}
|
|
216
283
|
// Process virtual fields - compute values from other fields
|
|
217
284
|
// Virtual fields don't exist in the database result, so we need to compute them separately
|
|
@@ -224,21 +291,27 @@ declaredOnly = emptyDeclaredOnlyTree()) {
|
|
|
224
291
|
if (!fieldConfig.virtual) {
|
|
225
292
|
continue;
|
|
226
293
|
}
|
|
227
|
-
//
|
|
228
|
-
//
|
|
294
|
+
// Projection-aware skip (ADR-0027): same rule as the stored-field pass
|
|
295
|
+
// above — a fragment that does not select this virtual field does no
|
|
296
|
+
// work for it at all.
|
|
297
|
+
if (selection?.fields && !selection.fields.has(fieldName)) {
|
|
298
|
+
continue;
|
|
299
|
+
}
|
|
300
|
+
// A virtual field with no resolveOutput hook can never produce a value
|
|
301
|
+
// on ANY read — there is nothing to compute, so there is nothing to do,
|
|
302
|
+
// including evaluating its read access (ADR-0027 reconciles the
|
|
303
|
+
// access-only evaluation this branch used to preserve: a field that
|
|
304
|
+
// never has output has no side effect worth preserving access for).
|
|
229
305
|
if (!(fieldConfig.hooks?.resolveOutput && listKey)) {
|
|
230
|
-
// Still evaluate read access to preserve any access-fn side effects.
|
|
231
|
-
await checkFieldAccess(fieldConfig.access, 'read', { ...args, item: workingItem });
|
|
232
306
|
continue;
|
|
233
307
|
}
|
|
234
|
-
// Check read access and compute the value via the shared helper.
|
|
235
|
-
// fields see the already-filtered item so they can read sibling fields.
|
|
308
|
+
// Check read access and compute the value via the shared helper.
|
|
236
309
|
const result = await resolveReadableFieldValue({
|
|
237
310
|
fieldConfig,
|
|
238
311
|
fieldName,
|
|
239
312
|
value: undefined, // Virtual fields don't have a database value
|
|
240
313
|
accessItem: workingItem,
|
|
241
|
-
hookItem:
|
|
314
|
+
hookItem: computedFieldItem,
|
|
242
315
|
listKey,
|
|
243
316
|
args,
|
|
244
317
|
config,
|
|
@@ -248,10 +321,11 @@ declaredOnly = emptyDeclaredOnlyTree()) {
|
|
|
248
321
|
}
|
|
249
322
|
}
|
|
250
323
|
// Strip relations that were fetched ONLY to satisfy a `needs` declaration
|
|
251
|
-
// (ADR-0025), now that every resolveOutput hook at this level
|
|
252
|
-
//
|
|
253
|
-
//
|
|
254
|
-
//
|
|
324
|
+
// (ADR-0025), now that every resolveOutput hook at this level has had the
|
|
325
|
+
// chance to see them (via `computedFieldItem`, never `filtered` itself — a
|
|
326
|
+
// declared dependency is read from stored columns, not from another
|
|
327
|
+
// field's resolved output). A declared dependency is private plumbing, not
|
|
328
|
+
// an implicit `include`: it never widens what the caller receives.
|
|
255
329
|
for (const key of declaredOnly.keys) {
|
|
256
330
|
delete filtered[key];
|
|
257
331
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"field-visibility.js","sourceRoot":"","sources":["../../src/access/field-visibility.ts"],"names":[],"mappings":"AAEA,OAAO,EAAE,oBAAoB,EAAE,MAAM,aAAa,CAAA;AAClD,OAAO,EAAE,gBAAgB,EAAE,MAAM,mBAAmB,CAAA;AACpD,OAAO,EAAE,wBAAwB,EAAE,MAAM,mBAAmB,CAAA;AAC5D,OAAO,EAAE,uBAAuB,EAAE,MAAM,aAAa,CAAA;AAErD,OAAO,EAAE,qBAAqB,EAAE,MAAM,4BAA4B,CAAA;
|
|
1
|
+
{"version":3,"file":"field-visibility.js","sourceRoot":"","sources":["../../src/access/field-visibility.ts"],"names":[],"mappings":"AAEA,OAAO,EAAE,oBAAoB,EAAE,MAAM,aAAa,CAAA;AAClD,OAAO,EAAE,gBAAgB,EAAE,MAAM,mBAAmB,CAAA;AACpD,OAAO,EAAE,wBAAwB,EAAE,MAAM,mBAAmB,CAAA;AAC5D,OAAO,EAAE,uBAAuB,EAAE,MAAM,aAAa,CAAA;AAErD,OAAO,EAAE,qBAAqB,EAAE,MAAM,4BAA4B,CAAA;AAElE,2EAA2E;AAC3E,qEAAqE;AACrE,8EAA8E;AAC9E,wEAAwE;AACxE,6EAA6E;AAC7E,oDAAoD;AACpD,OAAO,EAAE,eAAe,EAAE,MAAM,qBAAqB,CAAA;AA2CrD;;;;;;;;;;;;;;;;;;;;;;GAsBG;AACH,SAAS,0BAA0B,CACjC,OAA8C,EAC9C,IAA2C,EAC3C,MAAkC;IAElC,MAAM,OAAO,GAA0C;QACrD,GAAG,OAAO;QACV,mBAAmB,EAAE,CAAC,GAAG,OAAO,CAAC,mBAAmB,EAAE,IAAI,CAAC;KAC5D,CAAA;IACD,IAAI,MAAM,EAAE,CAAC;QACX,OAAO,CAAC,EAAE,GAAG,eAAe,CAAC,MAAM,EAAE,OAAO,CAAC,MAAM,EAAE,OAAO,CAAC,CAAA;IAC/D,CAAC;IACD,OAAO,OAAO,CAAA;AAChB,CAAC;AAED;;;;;;;;;;;;;;;;GAgBG;AACH,KAAK,UAAU,yBAAyB,CAAC,MASxC;IACC,MAAM,EAAE,WAAW,EAAE,SAAS,EAAE,KAAK,EAAE,UAAU,EAAE,QAAQ,EAAE,OAAO,EAAE,IAAI,EAAE,MAAM,EAAE,GAAG,MAAM,CAAA;IAE7F,kEAAkE;IAClE,MAAM,OAAO,GAAG,MAAM,gBAAgB,CAAC,WAAW,EAAE,MAAM,EAAE,MAAM,EAAE;QAClE,GAAG,IAAI;QACP,IAAI,EAAE,UAAU;KACjB,CAAC,CAAA;IAEF,IAAI,CAAC,OAAO,EAAE,CAAC;QACb,OAAO,EAAE,QAAQ,EAAE,KAAK,EAAE,CAAA;IAC5B,CAAC;IAED,sCAAsC;IACtC,IAAI,WAAW,EAAE,KAAK,EAAE,aAAa,IAAI,OAAO,EAAE,CAAC;QACjD,6CAA6C;QAC7C,yEAAyE;QACzE,MAAM,IAAI,GAAG,WAAW,CAAC,KAAK,CAAC,aAAoD,CAAA;QACnF,MAAM,IAAI,GAAG,EAAE,OAAO,EAAE,QAAQ,EAAE,SAAS,EAAE,CAAA;QAC7C,MAAM,KAAK,GAAG,IAAI,CAAC,OAAO,CAAC,mBAAmB,CAAA;QAE9C,0EAA0E;QAC1E,qEAAqE;QACrE,+DAA+D;QAC/D,MAAM,cAAc,GAAG,KAAK,CAAC,IAAI,CAC/B,CAAC,KAAK,EAAE,EAAE,CAAC,KAAK,CAAC,OAAO,KAAK,IAAI,CAAC,OAAO,IAAI,KAAK,CAAC,QAAQ,KAAK,IAAI,CAAC,QAAQ,CAC9E,CAAA;QACD,IAAI,cAAc,EAAE,CAAC;YACnB,MAAM,IAAI,uBAAuB,CAAC,CAAC,GAAG,KAAK,EAAE,IAAI,CAAC,CAAC,CAAA;QACrD,CAAC;QAED,uEAAuE;QACvE,uEAAuE;QACvE,2DAA2D;QAC3D,IAAI,KAAK,CAAC,MAAM,IAAI,wBAAwB,EAAE,CAAC;YAC7C,MAAM,IAAI,GAAG,CAAC,GAAG,KAAK,EAAE,IAAI,CAAC,CAAC,GAAG,CAAC,CAAC,KAAK,EAAE,EAAE,CAAC,GAAG,KAAK,CAAC,OAAO,IAAI,KAAK,CAAC,QAAQ,EAAE,CAAC,CAAC,IAAI,CAAC,KAAK,CAAC,CAAA;YAC9F,OAAO,CAAC,IAAI,CACV,4BAA4B,OAAO,IAAI,SAAS,iCAAiC;gBAC/E,6BAA6B,wBAAwB,MAAM,IAAI,0BAA0B;gBACzF,uBAAuB,CAC1B,CAAA;YACD,OAAO,EAAE,QAAQ,EAAE,KAAK,EAAE,CAAA;QAC5B,CAAC;QAED,4DAA4D;QAC5D,MAAM,QAAQ,GAAG,MAAM,OAAO,CAAC,OAAO,CACpC,IAAI,CAAC;YACH,KAAK;YACL,SAAS,EAAE,OAAO;YAClB,SAAS;YACT,OAAO;YACP,IAAI,EAAE,QAAQ;YACd,OAAO,EAAE,0BAA0B,CAAC,IAAI,CAAC,OAAO,EAAE,IAAI,EAAE,MAAM,CAAC;SAChE,CAAC,CACH,CAAA;QACD,OAAO,EAAE,QAAQ,EAAE,IAAI,EAAE,KAAK,EAAE,QAAQ,EAAE,CAAA;IAC5C,CAAC;IAED,OAAO,EAAE,QAAQ,EAAE,IAAI,EAAE,KAAK,EAAE,CAAA;AAClC,CAAC;AAED;;;GAGG;AACH,MAAM,CAAC,KAAK,UAAU,oBAAoB,CACxC,IAAO,EACP,YAAyC,EACzC,IAGC,EACD,MAAuB,EACvB,KAAK,GAAW,CAAC,EACjB,OAAgB;AAChB,wEAAwE;AACxE,uEAAuE;AACvE,mEAAmE;AACnE,sEAAsE;AACtE,6DAA6D;AAC7D,YAAY,GAAqB,qBAAqB,EAAE;AACxD,2EAA2E;AAC3E,2EAA2E;AAC3E,qEAAqE;AACrE,oEAAoE;AACpE,sEAAsE;AACtE,qBAAqB;AACrB,SAA+B;IAE/B,MAAM,QAAQ,GAA4B,EAAE,CAAA;IAE5C,4EAA4E;IAC5E,4EAA4E;IAC5E,+EAA+E;IAC/E,2EAA2E;IAC3E,wEAAwE;IACxE,4EAA4E;IAC5E,qBAAqB;IACrB,MAAM,WAAW,GAA4B,EAAE,GAAG,IAAI,EAAE,CAAA;IACxD,KAAK,MAAM,CAAC,SAAS,EAAE,WAAW,CAAC,IAAI,MAAM,CAAC,OAAO,CAAC,YAAY,CAAC,EAAE,CAAC;QACpE,IAAI,CAAC,WAAW,CAAC,eAAe,IAAI,CAAC,WAAW,CAAC,cAAc;YAAE,SAAQ;QACzE,MAAM,WAAW,GAAG,WAAW,CAAC,cAAc,CAAC,SAAS,CAAC,CAAA;QACzD,4EAA4E;QAC5E,+DAA+D;QAC/D,MAAM,YAAY,GAAG,WAAW,CAAC,IAAI,CAAC,CAAC,IAAI,EAAE,EAAE,CAAC,IAAI,IAAI,WAAW,CAAC,CAAA;QACpE,IAAI,CAAC,YAAY;YAAE,SAAQ;QAC3B,MAAM,SAAS,GAAG,WAAW,CAAC,eAAe,CAAC,SAAS,EAAE,WAAW,CAAC,CAAA;QACrE,KAAK,MAAM,IAAI,IAAI,WAAW,EAAE,CAAC;YAC/B,OAAO,WAAW,CAAC,IAAI,CAAC,CAAA;QAC1B,CAAC;QACD,WAAW,CAAC,SAAS,CAAC,GAAG,SAAS,CAAA;IACpC,CAAC;IAED,4EAA4E;IAC5E,+DAA+D;IAC/D,yEAAyE;IACzE,2EAA2E;IAC3E,0EAA0E;IAC1E,kEAAkE;IAClE,+DAA+D;IAC/D,MAAM,gBAAgB,GAAG,IAAI,GAAG,EAAU,CAAA;IAE1C,mDAAmD;IACnD,KAAK,MAAM,CAAC,SAAS,EAAE,KAAK,CAAC,IAAI,MAAM,CAAC,OAAO,CAAC,WAAW,CAAC,EAAE,CAAC;QAC7D,MAAM,WAAW,GAAG,YAAY,CAAC,SAAS,CAAC,CAAA;QAE3C,0CAA0C;QAC1C,IAAI,CAAC,IAAI,EAAE,WAAW,EAAE,WAAW,CAAC,CAAC,QAAQ,CAAC,SAAS,CAAC,EAAE,CAAC;YACzD,QAAQ,CAAC,SAAS,CAAC,GAAG,KAAK,CAAA;YAC3B,SAAQ;QACV,CAAC;QAED,yEAAyE;QACzE,qEAAqE;QACrE,sEAAsE;QACtE,wEAAwE;QACxE,0EAA0E;QAC1E,oEAAoE;QACpE,4DAA4D;QAC5D,IAAI,SAAS,EAAE,MAAM,IAAI,CAAC,SAAS,CAAC,MAAM,CAAC,GAAG,CAAC,SAAS,CAAC,EAAE,CAAC;YAC1D,SAAQ;QACV,CAAC;QAED,8EAA8E;QAC9E,4FAA4F;QAC5F,iEAAiE;QACjE,EAAE;QACF,6EAA6E;QAC7E,0EAA0E;QAC1E,0EAA0E;QAC1E,2EAA2E;QAC3E,0EAA0E;QAC1E,uEAAuE;QACvE,mEAAmE;QACnE,yCAAyC;QACzC,IACE,MAAM;YACN,WAAW,EAAE,IAAI,KAAK,cAAc;YACpC,KAAK,IAAI,WAAW;YACpB,WAAW,CAAC,GAAG;YACf,KAAK,KAAK,IAAI;YACd,KAAK,KAAK,SAAS,EACnB,CAAC;YACD,yDAAyD;YACzD,MAAM,OAAO,GAAG,MAAM,gBAAgB,CAAC,WAAW,EAAE,MAAM,EAAE,MAAM,EAAE;gBAClE,GAAG,IAAI;gBACP,IAAI,EAAE,WAAW;aAClB,CAAC,CAAA;YAEF,IAAI,CAAC,OAAO,EAAE,CAAC;gBACb,gBAAgB,CAAC,GAAG,CAAC,SAAS,CAAC,CAAA;gBAC/B,SAAQ;YACV,CAAC;YAED,MAAM,aAAa,GAAG,oBAAoB,CAAC,WAAW,CAAC,GAAa,EAAE,MAAM,CAAC,CAAA;YAC7E,yEAAyE;YACzE,wEAAwE;YACxE,wEAAwE;YACxE,yBAAyB;YACzB,MAAM,kBAAkB,GAAG,YAAY,CAAC,MAAM,CAAC,SAAS,CAAC,IAAI,qBAAqB,EAAE,CAAA;YACpF,wEAAwE;YACxE,sEAAsE;YACtE,qEAAqE;YACrE,sEAAsE;YACtE,4BAA4B;YAC5B,MAAM,eAAe,GAAG,SAAS,EAAE,MAAM,CAAC,SAAS,CAAC,CAAA;YAEpD,IAAI,aAAa,EAAE,CAAC;gBAClB,2EAA2E;gBAC3E,kEAAkE;gBAClE,IAAI,KAAK,CAAC,OAAO,CAAC,KAAK,CAAC,EAAE,CAAC;oBACzB,QAAQ,CAAC,SAAS,CAAC,GAAG,MAAM,OAAO,CAAC,GAAG,CACrC,KAAK,CAAC,GAAG,CAAC,CAAC,WAAW,EAAE,EAAE,CACxB,oBAAoB,CAClB,WAAW,EACX,aAAa,CAAC,UAAU,CAAC,MAAM,EAC/B,IAAI,EACJ,MAAM,EACN,KAAK,GAAG,CAAC,EACT,aAAa,CAAC,QAAQ,EACtB,kBAAkB,EAClB,eAAe,CAChB,CACF,CACF,CAAA;gBACH,CAAC;gBACD,iEAAiE;gBACjE,kEAAkE;qBAC7D,IAAI,OAAO,KAAK,KAAK,QAAQ,EAAE,CAAC;oBACnC,QAAQ,CAAC,SAAS,CAAC,GAAG,MAAM,oBAAoB,CAC9C,KAAgC,EAChC,aAAa,CAAC,UAAU,CAAC,MAAM,EAC/B,IAAI,EACJ,MAAM,EACN,KAAK,GAAG,CAAC,EACT,aAAa,CAAC,QAAQ,EACtB,kBAAkB,EAClB,eAAe,CAChB,CAAA;gBACH,CAAC;YACH,CAAC;iBAAM,CAAC;gBACN,oDAAoD;gBACpD,QAAQ,CAAC,SAAS,CAAC,GAAG,KAAK,CAAA;YAC7B,CAAC;YACD,SAAQ;QACV,CAAC;QAED,wEAAwE;QACxE,mEAAmE;QACnE,MAAM,MAAM,GAAG,MAAM,yBAAyB,CAAC;YAC7C,WAAW;YACX,SAAS;YACT,KAAK;YACL,UAAU,EAAE,WAAW;YACvB,QAAQ,EAAE,WAAW;YACrB,OAAO;YACP,IAAI;YACJ,MAAM;SACP,CAAC,CAAA;QAEF,IAAI,MAAM,CAAC,QAAQ,EAAE,CAAC;YACpB,QAAQ,CAAC,SAAS,CAAC,GAAG,MAAM,CAAC,KAAK,CAAA;QACpC,CAAC;aAAM,CAAC;YACN,gBAAgB,CAAC,GAAG,CAAC,SAAS,CAAC,CAAA;QACjC,CAAC;IACH,CAAC;IAED,mEAAmE;IACnE,2EAA2E;IAC3E,wEAAwE;IACxE,2EAA2E;IAC3E,uEAAuE;IACvE,qEAAqE;IACrE,sEAAsE;IACtE,4EAA4E;IAC5E,sEAAsE;IACtE,qEAAqE;IACrE,qEAAqE;IACrE,yEAAyE;IACzE,yEAAyE;IACzE,uEAAuE;IACvE,kBAAkB;IAClB,MAAM,iBAAiB,GAA4B,EAAE,GAAG,WAAW,EAAE,CAAA;IACrE,KAAK,MAAM,GAAG,IAAI,MAAM,CAAC,IAAI,CAAC,WAAW,CAAC,EAAE,CAAC;QAC3C,IAAI,CAAC,IAAI,EAAE,WAAW,EAAE,WAAW,CAAC,CAAC,QAAQ,CAAC,GAAG,CAAC;YAAE,SAAQ;QAC5D,IAAI,gBAAgB,CAAC,GAAG,CAAC,GAAG,CAAC,EAAE,CAAC;YAC9B,OAAO,iBAAiB,CAAC,GAAG,CAAC,CAAA;YAC7B,SAAQ;QACV,CAAC;QACD,IAAI,GAAG,IAAI,QAAQ;YAAE,SAAQ;QAC7B,IAAI,YAAY,CAAC,IAAI,CAAC,GAAG,CAAC,GAAG,CAAC;YAAE,SAAQ;QACxC,OAAO,iBAAiB,CAAC,GAAG,CAAC,CAAA;IAC/B,CAAC;IAED,4DAA4D;IAC5D,2FAA2F;IAC3F,KAAK,MAAM,CAAC,SAAS,EAAE,WAAW,CAAC,IAAI,MAAM,CAAC,OAAO,CAAC,YAAY,CAAC,EAAE,CAAC;QACpE,mDAAmD;QACnD,IAAI,SAAS,IAAI,QAAQ,EAAE,CAAC;YAC1B,SAAQ;QACV,CAAC;QAED,8BAA8B;QAC9B,IAAI,CAAC,WAAW,CAAC,OAAO,EAAE,CAAC;YACzB,SAAQ;QACV,CAAC;QAED,uEAAuE;QACvE,qEAAqE;QACrE,sBAAsB;QACtB,IAAI,SAAS,EAAE,MAAM,IAAI,CAAC,SAAS,CAAC,MAAM,CAAC,GAAG,CAAC,SAAS,CAAC,EAAE,CAAC;YAC1D,SAAQ;QACV,CAAC;QAED,uEAAuE;QACvE,wEAAwE;QACxE,gEAAgE;QAChE,oEAAoE;QACpE,oEAAoE;QACpE,IAAI,CAAC,CAAC,WAAW,CAAC,KAAK,EAAE,aAAa,IAAI,OAAO,CAAC,EAAE,CAAC;YACnD,SAAQ;QACV,CAAC;QAED,iEAAiE;QACjE,MAAM,MAAM,GAAG,MAAM,yBAAyB,CAAC;YAC7C,WAAW;YACX,SAAS;YACT,KAAK,EAAE,SAAS,EAAE,6CAA6C;YAC/D,UAAU,EAAE,WAAW;YACvB,QAAQ,EAAE,iBAAiB;YAC3B,OAAO;YACP,IAAI;YACJ,MAAM;SACP,CAAC,CAAA;QAEF,IAAI,MAAM,CAAC,QAAQ,EAAE,CAAC;YACpB,QAAQ,CAAC,SAAS,CAAC,GAAG,MAAM,CAAC,KAAK,CAAA;QACpC,CAAC;IACH,CAAC;IAED,0EAA0E;IAC1E,0EAA0E;IAC1E,2EAA2E;IAC3E,oEAAoE;IACpE,2EAA2E;IAC3E,mEAAmE;IACnE,KAAK,MAAM,GAAG,IAAI,YAAY,CAAC,IAAI,EAAE,CAAC;QACpC,OAAO,QAAQ,CAAC,GAAG,CAAC,CAAA;IACtB,CAAC;IAED,OAAO,QAAsB,CAAA;AAC/B,CAAC"}
|
package/dist/access/index.d.ts
CHANGED
|
@@ -1,8 +1,7 @@
|
|
|
1
1
|
export type { AccessControl, FieldAccess, Session, AccessContext, PrismaFilter, AccessControlledDB, PrismaClientLike, StorageUtils, AugmentedFindMany, AugmentedFindUnique, FindManyQueryArgs, } from './types.js';
|
|
2
2
|
export { checkAccess, mergeFilters, isBoolean, isPrismaFilter, getRelatedListConfig, } from './engine.js';
|
|
3
3
|
export { checkFieldAccess, filterWritableFields } from './field-access.js';
|
|
4
|
-
export {
|
|
5
|
-
export type { AccessIncludeResult } from './access-filter.js';
|
|
4
|
+
export { buildAccessScopedInclude, stripVirtualFieldsFromInclude } from './access-filter.js';
|
|
6
5
|
export { filterReadableFields } from './field-visibility.js';
|
|
7
6
|
export { foldDeclaredDependencies, getDeclaredRelationNames, emptyDeclaredOnlyTree, } from './declared-dependencies.js';
|
|
8
7
|
export type { DeclaredOnlyTree } from './declared-dependencies.js';
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/access/index.ts"],"names":[],"mappings":"AAAA,YAAY,EACV,aAAa,EACb,WAAW,EACX,OAAO,EACP,aAAa,EACb,YAAY,EACZ,kBAAkB,EAClB,gBAAgB,EAChB,YAAY,EACZ,iBAAiB,EACjB,mBAAmB,EACnB,iBAAiB,GAClB,MAAM,YAAY,CAAA;AAEnB,OAAO,EACL,WAAW,EACX,YAAY,EACZ,SAAS,EACT,cAAc,EACd,oBAAoB,GACrB,MAAM,aAAa,CAAA;AAEpB,OAAO,EAAE,gBAAgB,EAAE,oBAAoB,EAAE,MAAM,mBAAmB,CAAA;AAE1E,OAAO,
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/access/index.ts"],"names":[],"mappings":"AAAA,YAAY,EACV,aAAa,EACb,WAAW,EACX,OAAO,EACP,aAAa,EACb,YAAY,EACZ,kBAAkB,EAClB,gBAAgB,EAChB,YAAY,EACZ,iBAAiB,EACjB,mBAAmB,EACnB,iBAAiB,GAClB,MAAM,YAAY,CAAA;AAEnB,OAAO,EACL,WAAW,EACX,YAAY,EACZ,SAAS,EACT,cAAc,EACd,oBAAoB,GACrB,MAAM,aAAa,CAAA;AAEpB,OAAO,EAAE,gBAAgB,EAAE,oBAAoB,EAAE,MAAM,mBAAmB,CAAA;AAE1E,OAAO,EAAE,wBAAwB,EAAE,6BAA6B,EAAE,MAAM,oBAAoB,CAAA;AAE5F,OAAO,EAAE,oBAAoB,EAAE,MAAM,uBAAuB,CAAA;AAG5D,OAAO,EACL,wBAAwB,EACxB,wBAAwB,EACxB,qBAAqB,GACtB,MAAM,4BAA4B,CAAA;AACnC,YAAY,EAAE,gBAAgB,EAAE,MAAM,4BAA4B,CAAA;AAElE,OAAO,EAAE,6BAA6B,EAAE,MAAM,aAAa,CAAA;AAE3D,OAAO,EAAE,uBAAuB,EAAE,MAAM,aAAa,CAAA"}
|
package/dist/access/index.js
CHANGED
|
@@ -3,7 +3,7 @@ export { checkAccess, mergeFilters, isBoolean, isPrismaFilter, getRelatedListCon
|
|
|
3
3
|
// Canonical field-level access evaluation (shared by read and write paths).
|
|
4
4
|
export { checkFieldAccess, filterWritableFields } from './field-access.js';
|
|
5
5
|
// Phase 1 — Access Filter (pre-query row/relation scoping).
|
|
6
|
-
export {
|
|
6
|
+
export { buildAccessScopedInclude, stripVirtualFieldsFromInclude } from './access-filter.js';
|
|
7
7
|
// Phase 2 — Field Visibility (post-query field stripping + resolveOutput).
|
|
8
8
|
export { filterReadableFields } from './field-visibility.js';
|
|
9
9
|
// Declared Dependencies — folding `needs` into an include without widening
|
package/dist/access/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/access/index.ts"],"names":[],"mappings":"AAaA,mEAAmE;AACnE,OAAO,EACL,WAAW,EACX,YAAY,EACZ,SAAS,EACT,cAAc,EACd,oBAAoB,GACrB,MAAM,aAAa,CAAA;AACpB,4EAA4E;AAC5E,OAAO,EAAE,gBAAgB,EAAE,oBAAoB,EAAE,MAAM,mBAAmB,CAAA;AAC1E,4DAA4D;AAC5D,OAAO,
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/access/index.ts"],"names":[],"mappings":"AAaA,mEAAmE;AACnE,OAAO,EACL,WAAW,EACX,YAAY,EACZ,SAAS,EACT,cAAc,EACd,oBAAoB,GACrB,MAAM,aAAa,CAAA;AACpB,4EAA4E;AAC5E,OAAO,EAAE,gBAAgB,EAAE,oBAAoB,EAAE,MAAM,mBAAmB,CAAA;AAC1E,4DAA4D;AAC5D,OAAO,EAAE,wBAAwB,EAAE,6BAA6B,EAAE,MAAM,oBAAoB,CAAA;AAC5F,2EAA2E;AAC3E,OAAO,EAAE,oBAAoB,EAAE,MAAM,uBAAuB,CAAA;AAC5D,2EAA2E;AAC3E,yBAAyB;AACzB,OAAO,EACL,wBAAwB,EACxB,wBAAwB,EACxB,qBAAqB,GACtB,MAAM,4BAA4B,CAAA;AAEnC,mFAAmF;AACnF,OAAO,EAAE,6BAA6B,EAAE,MAAM,aAAa,CAAA;AAC3D,gFAAgF;AAChF,OAAO,EAAE,uBAAuB,EAAE,MAAM,aAAa,CAAA"}
|
|
@@ -10,7 +10,7 @@ import type { OpenSaasConfig, ListConfig, FieldConfig } from '../config/types.js
|
|
|
10
10
|
* per-row query is issued and the count can never include rows the session
|
|
11
11
|
* cannot read. This module is the single place the related list's
|
|
12
12
|
* operation-level `query` access is folded into that `_count`, mirroring how
|
|
13
|
-
* `
|
|
13
|
+
* `buildAccessScopedInclude` folds it into relation includes.
|
|
14
14
|
*
|
|
15
15
|
* It also resolves the count Filter spec's markers: Prisma cannot compare a
|
|
16
16
|
* relation count in a `where`, so a to-many relationship's Filter spec emits a
|
package/dist/config/index.d.ts
CHANGED
|
@@ -62,5 +62,5 @@ export declare function config(userConfig: OpenSaasConfig): OpenSaasConfig | Pro
|
|
|
62
62
|
* ```
|
|
63
63
|
*/
|
|
64
64
|
export declare function list<TTypeInfo extends import('./types.js').TypeInfo>(config: ListConfigInput<TTypeInfo>): ListConfig<TTypeInfo>;
|
|
65
|
-
export type { OpenSaasConfig, OutputConfig, ListConfig, ListConfigInput, ListAccessControl, FieldConfig, BaseFieldConfig, TextField, IntegerField, CheckboxField, TimestampField, PasswordField, SelectField, RelationshipField, PrismaRelationResult, MultiColumnPrismaResult, JsonField, VirtualField, TypeDescriptor, TypeInfo, OperationAccess, Hooks, FieldHooks, FieldsWithTypeInfo, DatabaseConfig, SessionConfig, UIConfig, ListUIConfig, ListViewUIConfig, BulkAction, BulkActionContext, BulkActionResult, BulkActionVariant, ItemViewUIConfig, RelationshipItemViewConfig, ThemeConfig, ThemePreset, ThemeColors, ThemeFonts, ThemeShadows, McpConfig, McpToolsConfig, McpAuthConfig, ListMcpConfig, McpCustomTool, FileMetadata, ImageMetadata, ImageTransformationResult, Plugin, PluginContext, GeneratedFiles, ResolveInputHookArgs, ValidateHookArgs, BeforeOperationHookArgs, AfterOperationHookArgs, FieldResolveInputHookArgs, FieldValidateHookArgs, FieldBeforeOperationHookArgs, FieldAfterOperationHookArgs, FieldResolveOutputHookArgs, } from './types.js';
|
|
65
|
+
export type { OpenSaasConfig, OutputConfig, ListConfig, ListConfigInput, ListAccessControl, FieldConfig, BaseFieldConfig, TextField, IntegerField, CheckboxField, TimestampField, PasswordField, SelectField, RelationshipField, PrismaRelationResult, MultiColumnPrismaResult, JsonField, VirtualField, TypeDescriptor, TypeInfo, OperationAccess, Hooks, FieldHooks, FieldsWithTypeInfo, DatabaseConfig, ListIndex, ListIndexFieldRef, SessionConfig, UIConfig, ListUIConfig, ListViewUIConfig, BulkAction, BulkActionContext, BulkActionResult, BulkActionVariant, ItemViewUIConfig, RelationshipItemViewConfig, ThemeConfig, ThemePreset, ThemeColors, ThemeFonts, ThemeShadows, McpConfig, McpToolsConfig, McpAuthConfig, ListMcpConfig, McpCustomTool, FileMetadata, ImageMetadata, ImageTransformationResult, Plugin, PluginContext, GeneratedFiles, ResolveInputHookArgs, ValidateHookArgs, BeforeOperationHookArgs, AfterOperationHookArgs, FieldResolveInputHookArgs, FieldValidateHookArgs, FieldBeforeOperationHookArgs, FieldAfterOperationHookArgs, FieldResolveOutputHookArgs, } from './types.js';
|
|
66
66
|
//# sourceMappingURL=index.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/config/index.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EACV,cAAc,EACd,UAAU,EACV,eAAe,EAGhB,MAAM,YAAY,CAAA;AA8BnB;;;;;;GAMG;AACH,wBAAgB,MAAM,CAAC,UAAU,EAAE,cAAc,GAAG,cAAc,GAAG,OAAO,CAAC,cAAc,CAAC,CAQ3F;AAED;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAqDG;AACH,wBAAgB,IAAI,CAAC,SAAS,SAAS,OAAO,YAAY,EAAE,QAAQ,EAClE,MAAM,EAAE,eAAe,CAAC,SAAS,CAAC,GACjC,UAAU,CAAC,SAAS,CAAC,CAUvB;AAGD,YAAY,EACV,cAAc,EACd,YAAY,EACZ,UAAU,EACV,eAAe,EACf,iBAAiB,EACjB,WAAW,EACX,eAAe,EACf,SAAS,EACT,YAAY,EACZ,aAAa,EACb,cAAc,EACd,aAAa,EACb,WAAW,EACX,iBAAiB,EACjB,oBAAoB,EACpB,uBAAuB,EACvB,SAAS,EACT,YAAY,EACZ,cAAc,EACd,QAAQ,EACR,eAAe,EACf,KAAK,EACL,UAAU,EACV,kBAAkB,EAClB,cAAc,EACd,aAAa,EACb,QAAQ,EACR,YAAY,EACZ,gBAAgB,EAChB,UAAU,EACV,iBAAiB,EACjB,gBAAgB,EAChB,iBAAiB,EACjB,gBAAgB,EAChB,0BAA0B,EAC1B,WAAW,EACX,WAAW,EACX,WAAW,EACX,UAAU,EACV,YAAY,EACZ,SAAS,EACT,cAAc,EACd,aAAa,EACb,aAAa,EACb,aAAa,EACb,YAAY,EACZ,aAAa,EACb,yBAAyB,EAEzB,MAAM,EACN,aAAa,EACb,cAAc,EAEd,oBAAoB,EACpB,gBAAgB,EAChB,uBAAuB,EACvB,sBAAsB,EAEtB,yBAAyB,EACzB,qBAAqB,EACrB,4BAA4B,EAC5B,2BAA2B,EAC3B,0BAA0B,GAC3B,MAAM,YAAY,CAAA"}
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/config/index.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EACV,cAAc,EACd,UAAU,EACV,eAAe,EAGhB,MAAM,YAAY,CAAA;AA8BnB;;;;;;GAMG;AACH,wBAAgB,MAAM,CAAC,UAAU,EAAE,cAAc,GAAG,cAAc,GAAG,OAAO,CAAC,cAAc,CAAC,CAQ3F;AAED;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAqDG;AACH,wBAAgB,IAAI,CAAC,SAAS,SAAS,OAAO,YAAY,EAAE,QAAQ,EAClE,MAAM,EAAE,eAAe,CAAC,SAAS,CAAC,GACjC,UAAU,CAAC,SAAS,CAAC,CAUvB;AAGD,YAAY,EACV,cAAc,EACd,YAAY,EACZ,UAAU,EACV,eAAe,EACf,iBAAiB,EACjB,WAAW,EACX,eAAe,EACf,SAAS,EACT,YAAY,EACZ,aAAa,EACb,cAAc,EACd,aAAa,EACb,WAAW,EACX,iBAAiB,EACjB,oBAAoB,EACpB,uBAAuB,EACvB,SAAS,EACT,YAAY,EACZ,cAAc,EACd,QAAQ,EACR,eAAe,EACf,KAAK,EACL,UAAU,EACV,kBAAkB,EAClB,cAAc,EACd,SAAS,EACT,iBAAiB,EACjB,aAAa,EACb,QAAQ,EACR,YAAY,EACZ,gBAAgB,EAChB,UAAU,EACV,iBAAiB,EACjB,gBAAgB,EAChB,iBAAiB,EACjB,gBAAgB,EAChB,0BAA0B,EAC1B,WAAW,EACX,WAAW,EACX,WAAW,EACX,UAAU,EACV,YAAY,EACZ,SAAS,EACT,cAAc,EACd,aAAa,EACb,aAAa,EACb,aAAa,EACb,YAAY,EACZ,aAAa,EACb,yBAAyB,EAEzB,MAAM,EACN,aAAa,EACb,cAAc,EAEd,oBAAoB,EACpB,gBAAgB,EAChB,uBAAuB,EACvB,sBAAsB,EAEtB,yBAAyB,EACzB,qBAAqB,EACrB,4BAA4B,EAC5B,2BAA2B,EAC3B,0BAA0B,GAC3B,MAAM,YAAY,CAAA"}
|
package/dist/config/types.d.ts
CHANGED
|
@@ -1004,6 +1004,28 @@ export type RelationshipField<TTypeInfo extends TypeInfo = TypeInfo> = BaseField
|
|
|
1004
1004
|
*/
|
|
1005
1005
|
isIndexed?: boolean | 'unique';
|
|
1006
1006
|
db?: {
|
|
1007
|
+
/**
|
|
1008
|
+
* Controls DB-level nullability of the foreign key column (and its
|
|
1009
|
+
* relation field) independently of the many side's own shape. Only
|
|
1010
|
+
* meaningful on the FK-owning (single) side of a relationship — the
|
|
1011
|
+
* many side has no column of its own to make non-nullable and rejects
|
|
1012
|
+
* this option.
|
|
1013
|
+
*
|
|
1014
|
+
* @default true (nullable, matching every relationship generated before
|
|
1015
|
+
* this option existed)
|
|
1016
|
+
*
|
|
1017
|
+
* @example
|
|
1018
|
+
* ```typescript
|
|
1019
|
+
* // Every session genuinely belongs to a user — make the FK required
|
|
1020
|
+
* user: relationship({
|
|
1021
|
+
* ref: 'User.sessions',
|
|
1022
|
+
* db: { isNullable: false },
|
|
1023
|
+
* })
|
|
1024
|
+
* // Generates: userId String (was String?)
|
|
1025
|
+
* // user User @relation(...) (was User?)
|
|
1026
|
+
* ```
|
|
1027
|
+
*/
|
|
1028
|
+
isNullable?: boolean;
|
|
1007
1029
|
/**
|
|
1008
1030
|
* Controls foreign key placement and column name for bidirectional relationships
|
|
1009
1031
|
* Can be a boolean or an object with a map property
|
|
@@ -1137,6 +1159,16 @@ export type PrismaRelationResult = {
|
|
|
1137
1159
|
* for the many side or the non-FK side it is `[relationLine]`.
|
|
1138
1160
|
*/
|
|
1139
1161
|
modelLines: string[];
|
|
1162
|
+
/**
|
|
1163
|
+
* The Prisma-level foreign key field name this side owns (e.g. `authorId`),
|
|
1164
|
+
* regardless of whether it is indexed. `undefined` when this side doesn't
|
|
1165
|
+
* own a foreign key column at all (the many side, or the non-FK side of a
|
|
1166
|
+
* one-to-one). Lets other generator passes resolve a relationship field
|
|
1167
|
+
* name to its physical column — e.g. a model-level composite index
|
|
1168
|
+
* ({@link ListIndex}) naming a relationship field — without duplicating
|
|
1169
|
+
* {@link foreignKeyIndex}'s narrower, indexing-conditional presence.
|
|
1170
|
+
*/
|
|
1171
|
+
foreignKeyField?: string;
|
|
1140
1172
|
/**
|
|
1141
1173
|
* Foreign key index to add to the owning model, if this side owns an
|
|
1142
1174
|
* indexed foreign key.
|
|
@@ -1579,6 +1611,39 @@ export type Hooks<TOutput = Record<string, unknown>, TCreateInput = Record<strin
|
|
|
1579
1611
|
*/
|
|
1580
1612
|
validateInput?: (args: ValidateHookArgs<TOutput, TCreateInput, TUpdateInput>) => Promise<void>;
|
|
1581
1613
|
};
|
|
1614
|
+
/**
|
|
1615
|
+
* A single field reference within a model-level {@link ListIndex}. Either
|
|
1616
|
+
* just the OpenSaaS field name, or an object naming it alongside a sort
|
|
1617
|
+
* direction.
|
|
1618
|
+
*/
|
|
1619
|
+
export type ListIndexFieldRef = string | {
|
|
1620
|
+
field: string;
|
|
1621
|
+
/** Sort direction for this column within the index/constraint. */
|
|
1622
|
+
sort?: 'asc' | 'desc';
|
|
1623
|
+
};
|
|
1624
|
+
/**
|
|
1625
|
+
* A model-level composite `@@unique`/`@@index` constraint spanning two or
|
|
1626
|
+
* more of a list's own fields. See {@link ListConfig.db}'s `indexes` for the
|
|
1627
|
+
* full explanation and examples (#864).
|
|
1628
|
+
*/
|
|
1629
|
+
export type ListIndex = {
|
|
1630
|
+
/**
|
|
1631
|
+
* The fields participating in this index/constraint, in declaration order.
|
|
1632
|
+
* OpenSaaS field names, not raw database column names.
|
|
1633
|
+
*/
|
|
1634
|
+
fields: ListIndexFieldRef[];
|
|
1635
|
+
/**
|
|
1636
|
+
* Emit `@@unique([...])` instead of `@@index([...])`.
|
|
1637
|
+
* @default false
|
|
1638
|
+
*/
|
|
1639
|
+
unique?: boolean;
|
|
1640
|
+
/**
|
|
1641
|
+
* Constraint/index name, emitted as Prisma's `map:` argument — lets an
|
|
1642
|
+
* existing live constraint be adopted under its current name rather than
|
|
1643
|
+
* renamed.
|
|
1644
|
+
*/
|
|
1645
|
+
name?: string;
|
|
1646
|
+
};
|
|
1582
1647
|
/**
|
|
1583
1648
|
* Internal list configuration type (after normalization by list() function)
|
|
1584
1649
|
* Access control is always in object form internally.
|
|
@@ -1652,6 +1717,67 @@ export type ListConfig<TTypeInfo extends TypeInfo> = {
|
|
|
1652
1717
|
* ```
|
|
1653
1718
|
*/
|
|
1654
1719
|
timestamps?: boolean;
|
|
1720
|
+
/**
|
|
1721
|
+
* Model-level composite `@@unique`/`@@index` constraints spanning two or
|
|
1722
|
+
* more of this list's own fields (#864).
|
|
1723
|
+
*
|
|
1724
|
+
* Field-level `isIndexed` (on a scalar or relationship field) can only
|
|
1725
|
+
* ever produce a single-column index — it has no way to express a
|
|
1726
|
+
* constraint or index that spans more than one column. `db.indexes` is
|
|
1727
|
+
* that multi-column case: each entry names two or more of the list's own
|
|
1728
|
+
* OpenSaaS field names, not raw database column names. The generator
|
|
1729
|
+
* resolves each to its underlying Prisma column — a scalar field's own
|
|
1730
|
+
* name (its Prisma field name is unaffected by `db.map`), or a
|
|
1731
|
+
* relationship field's foreign key column (`<field>Id`) when this side
|
|
1732
|
+
* owns it.
|
|
1733
|
+
*
|
|
1734
|
+
* A composite **unique** is the load-bearing case: it's the
|
|
1735
|
+
* database-level backstop for a business rule two concurrent writes could
|
|
1736
|
+
* otherwise both slip past (e.g. "one booking per student per
|
|
1737
|
+
* production") — something a hook's existence check cannot close on its
|
|
1738
|
+
* own, and can't be retrofitted once duplicate rows exist. A composite
|
|
1739
|
+
* **index** (non-unique) is the equivalent performance-only case (a hot
|
|
1740
|
+
* multi-column lookup path).
|
|
1741
|
+
*
|
|
1742
|
+
* An entry naming a field the list doesn't have, a virtual field, a
|
|
1743
|
+
* to-many relationship, or the non-FK side of a one-to-one relationship
|
|
1744
|
+
* fails `pnpm generate` with an error naming the list, the entry, and the
|
|
1745
|
+
* bad field — it is never silently dropped or emitted as invalid Prisma.
|
|
1746
|
+
*
|
|
1747
|
+
* @example One audition per student per production (composite unique)
|
|
1748
|
+
* ```typescript
|
|
1749
|
+
* Audition: list({
|
|
1750
|
+
* fields: {
|
|
1751
|
+
* student: relationship({ ref: 'Student.auditions' }),
|
|
1752
|
+
* production: relationship({ ref: 'Production.auditions' }),
|
|
1753
|
+
* },
|
|
1754
|
+
* db: {
|
|
1755
|
+
* indexes: [{ fields: ['student', 'production'], unique: true }],
|
|
1756
|
+
* },
|
|
1757
|
+
* })
|
|
1758
|
+
* // Generates: @@unique([studentId, productionId])
|
|
1759
|
+
* ```
|
|
1760
|
+
*
|
|
1761
|
+
* @example Hot lookup path (composite index) with a sort direction and an adopted constraint name
|
|
1762
|
+
* ```typescript
|
|
1763
|
+
* AuthVerification: list({
|
|
1764
|
+
* fields: {
|
|
1765
|
+
* identifier: text(),
|
|
1766
|
+
* createdAt: timestamp(),
|
|
1767
|
+
* },
|
|
1768
|
+
* db: {
|
|
1769
|
+
* indexes: [
|
|
1770
|
+
* {
|
|
1771
|
+
* fields: ['identifier', { field: 'createdAt', sort: 'desc' }],
|
|
1772
|
+
* name: 'AuthVerification_identifier_createdAt_idx',
|
|
1773
|
+
* },
|
|
1774
|
+
* ],
|
|
1775
|
+
* },
|
|
1776
|
+
* })
|
|
1777
|
+
* // Generates: @@index([identifier, createdAt(sort: Desc)], map: "AuthVerification_identifier_createdAt_idx")
|
|
1778
|
+
* ```
|
|
1779
|
+
*/
|
|
1780
|
+
indexes?: ListIndex[];
|
|
1655
1781
|
};
|
|
1656
1782
|
/**
|
|
1657
1783
|
* MCP server configuration for this list
|