@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
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import type { FieldConfig, OpenSaasConfig } from '../config/types.js';
|
|
2
|
+
import type { FieldSelectionScope } from '../query/index.js';
|
|
2
3
|
/**
|
|
3
4
|
* Declared Dependencies — folding a computed field's `needs` into a read's
|
|
4
5
|
* `include` without widening what the caller receives (ADR-0025).
|
|
@@ -6,10 +7,9 @@ import type { FieldConfig, OpenSaasConfig } from '../config/types.js';
|
|
|
6
7
|
* A field's `needs` declares immediate sibling relations its `resolveOutput`
|
|
7
8
|
* hook cannot compute without. This module folds those relations into
|
|
8
9
|
* whatever `include` a read is already building (caller-supplied, fragment-
|
|
9
|
-
* derived, or none at all) BEFORE it reaches the
|
|
10
|
-
*
|
|
11
|
-
*
|
|
12
|
-
* caller-named one, never a bypass.
|
|
10
|
+
* derived, or none at all) BEFORE it reaches the access-scoping pipeline
|
|
11
|
+
* (`buildAccessScopedInclude` in `access-filter.ts`) — a declared relation is
|
|
12
|
+
* scoped exactly like a caller-named one, never a bypass.
|
|
13
13
|
*
|
|
14
14
|
* The fold also tracks provenance: which relation keys, at which nesting
|
|
15
15
|
* level, were added ONLY to satisfy a declaration (as opposed to being named
|
|
@@ -17,25 +17,44 @@ import type { FieldConfig, OpenSaasConfig } from '../config/types.js';
|
|
|
17
17
|
* from the result after `resolveOutput` hooks have had a chance to read them
|
|
18
18
|
* — a declared dependency is private plumbing, not an implicit `include`.
|
|
19
19
|
*
|
|
20
|
-
*
|
|
21
|
-
* added
|
|
22
|
-
*
|
|
23
|
-
*
|
|
24
|
-
*
|
|
25
|
-
*
|
|
26
|
-
*
|
|
27
|
-
*
|
|
28
|
-
*
|
|
29
|
-
* `buildIncludeWithAccessControl`'s existing `visitedLists` cycle guard
|
|
30
|
-
* rather than through any recursion of this module's own (see ADR-0026's
|
|
31
|
-
* note that this guard's remaining job, after that ADR lands, is defending
|
|
32
|
-
* exactly this fold).
|
|
20
|
+
* **Every relation this module reaches is folded recursively**, whether it
|
|
21
|
+
* was added purely to satisfy a declaration or is already present for another
|
|
22
|
+
* reason (caller/fragment-named, bare or not). Since ADR-0026 removed the
|
|
23
|
+
* auto-expansion that used to carry a chain of declarations "for free" —
|
|
24
|
+
* naming a relation now fetches its own columns and stops — a field computed
|
|
25
|
+
* one hop down would otherwise lose its own declared dependencies the moment
|
|
26
|
+
* its list is reached any way OTHER than an explicit nested caller `include`.
|
|
27
|
+
* Declarations fold in "at every level a field is computed" (ADR-0025), not
|
|
28
|
+
* only where the caller happened to write one out.
|
|
33
29
|
*
|
|
34
|
-
*
|
|
35
|
-
*
|
|
36
|
-
*
|
|
37
|
-
*
|
|
38
|
-
*
|
|
30
|
+
* A branch added purely by this fold (`declaredOnly.keys`) is stripped
|
|
31
|
+
* wholesale by `filterReadableFields` regardless of what's nested inside it,
|
|
32
|
+
* so its own nested declared-only keys need no individual tracking — only a
|
|
33
|
+
* branch present for another reason (caller/fragment-named) needs the
|
|
34
|
+
* fine-grained `declaredOnly.nested` tracking, so a declaration folded
|
|
35
|
+
* beneath IT can be stripped without removing the caller's own data.
|
|
36
|
+
*
|
|
37
|
+
* **Cycle guard, re-pointed (ADR-0026).** A caller-supplied `include` is
|
|
38
|
+
* always a finite literal and cannot cycle. Recursively following declared
|
|
39
|
+
* dependencies across lists CAN — list A declaring `needs` on a relation to
|
|
40
|
+
* list B, whose own field declares `needs` back to A — so `visitedLists`
|
|
41
|
+
* carries the list names already on this fold's own path and stops rather
|
|
42
|
+
* than recursing without bound. `pnpm generate`'s `validateNeedsClosureDepth`
|
|
43
|
+
* (`needs-closure.ts`) is the primary backstop — a cyclic `needs` closure
|
|
44
|
+
* fails generation and should never reach this code at runtime — this guard
|
|
45
|
+
* is defense in depth, not the mechanism relied on for correctness.
|
|
46
|
+
*
|
|
47
|
+
* The guard applies to DECLARATION-ADDED edges only, which is exactly where
|
|
48
|
+
* the unbounded recursion can come from: a branch added by this fold carries
|
|
49
|
+
* no caller include of its own, so everything beneath it is declaration-added
|
|
50
|
+
* too, and each such edge either reaches a list not yet on the path or stops
|
|
51
|
+
* — bounding that suffix by the number of lists. An edge the request itself
|
|
52
|
+
* named is bounded by the request's own finite literal instead. Applying the
|
|
53
|
+
* guard to those as well would stop the fold at a path that merely revisits a
|
|
54
|
+
* list (`Post → author → posts`, or a self-referential `parent`), silently
|
|
55
|
+
* leaving the revisited list's own declared dependencies unsatisfied — the
|
|
56
|
+
* `undefined`-compute ADR-0025 exists to prevent, at every level a field is
|
|
57
|
+
* computed.
|
|
39
58
|
*/
|
|
40
59
|
/** Which relation keys, at which nesting level, exist only to satisfy a `needs` declaration. */
|
|
41
60
|
export type DeclaredOnlyTree = {
|
|
@@ -50,18 +69,39 @@ export declare function emptyDeclaredOnlyTree(): DeclaredOnlyTree;
|
|
|
50
69
|
* list that have a `resolveOutput` hook. A `needs` entry on a field without
|
|
51
70
|
* one is inert — there is no hook to feed it to — so it contributes nothing
|
|
52
71
|
* to fetch.
|
|
72
|
+
*
|
|
73
|
+
* `selectedFields`, when given, restricts the union to fields the read is
|
|
74
|
+
* actually going to return (ADR-0027) — a field a fragment did not select is
|
|
75
|
+
* never computed, so its declared relation is never fetched for it either.
|
|
76
|
+
* `undefined` means unrestricted: every field with a hook contributes,
|
|
77
|
+
* matching a bare or `include`-based read, which always returns every
|
|
78
|
+
* computed field on the list.
|
|
53
79
|
*/
|
|
54
|
-
export declare function getDeclaredRelationNames(fieldConfigs: Record<string, FieldConfig>): string[];
|
|
80
|
+
export declare function getDeclaredRelationNames(fieldConfigs: Record<string, FieldConfig>, selectedFields?: ReadonlySet<string>): string[];
|
|
55
81
|
/**
|
|
56
82
|
* Fold this list's declared dependencies into `rawInclude`, recursing into
|
|
57
|
-
*
|
|
58
|
-
*
|
|
83
|
+
* EVERY relation reached from here — whether it was just added to satisfy a
|
|
84
|
+
* declaration or was already present for another reason — so that list's own
|
|
85
|
+
* declared dependencies are satisfied too, wherever it's reached (see module
|
|
86
|
+
* doc comment).
|
|
59
87
|
*
|
|
60
88
|
* Returns `rawInclude` itself (same reference) when there is nothing to
|
|
61
89
|
* fold, so a list with no `needs` fields and no caller include stays on the
|
|
62
90
|
* exact bare-read path (ADR-0024) — untouched, not merely equivalent.
|
|
91
|
+
*
|
|
92
|
+
* `listKey` seeds the cycle guard at the root of a read; recursive calls
|
|
93
|
+
* extend it with each related list reached along THIS fold's own path.
|
|
94
|
+
*
|
|
95
|
+
* `selection`, when given, is the fragment scope this level was reached
|
|
96
|
+
* under (ADR-0027) — only fields it names contribute their `needs` (see
|
|
97
|
+
* `getDeclaredRelationNames`). It is `undefined` for a bare/`include`-based
|
|
98
|
+
* read (unrestricted: every field's `needs` folds in, unchanged from before
|
|
99
|
+
* ADR-0027) and for any branch reached only to satisfy a declaration — a
|
|
100
|
+
* relation added purely by this fold has no fragment scope of its own, so
|
|
101
|
+
* its own list folds unrestricted, exactly as it did before selectivity
|
|
102
|
+
* existed.
|
|
63
103
|
*/
|
|
64
|
-
export declare function foldDeclaredDependencies(rawInclude: Record<string, unknown> | undefined, fieldConfigs: Record<string, FieldConfig>, config: OpenSaasConfig): {
|
|
104
|
+
export declare function foldDeclaredDependencies(rawInclude: Record<string, unknown> | undefined, fieldConfigs: Record<string, FieldConfig>, config: OpenSaasConfig, listKey: string, visitedLists?: readonly string[], selection?: FieldSelectionScope): {
|
|
65
105
|
include: Record<string, unknown> | undefined;
|
|
66
106
|
declaredOnly: DeclaredOnlyTree;
|
|
67
107
|
};
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"declared-dependencies.d.ts","sourceRoot":"","sources":["../../src/access/declared-dependencies.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,WAAW,EAAE,cAAc,EAAE,MAAM,oBAAoB,CAAA;
|
|
1
|
+
{"version":3,"file":"declared-dependencies.d.ts","sourceRoot":"","sources":["../../src/access/declared-dependencies.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,WAAW,EAAE,cAAc,EAAE,MAAM,oBAAoB,CAAA;AAErE,OAAO,KAAK,EAAE,mBAAmB,EAAE,MAAM,mBAAmB,CAAA;AAE5D;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAuDG;AAEH,gGAAgG;AAChG,MAAM,MAAM,gBAAgB,GAAG;IAC7B,2EAA2E;IAC3E,IAAI,EAAE,GAAG,CAAC,MAAM,CAAC,CAAA;IACjB,6JAA6J;IAC7J,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,gBAAgB,CAAC,CAAA;CACzC,CAAA;AAED,wBAAgB,qBAAqB,IAAI,gBAAgB,CAExD;AA6BD;;;;;;;;;;;;GAYG;AACH,wBAAgB,wBAAwB,CACtC,YAAY,EAAE,MAAM,CAAC,MAAM,EAAE,WAAW,CAAC,EACzC,cAAc,CAAC,EAAE,WAAW,CAAC,MAAM,CAAC,GACnC,MAAM,EAAE,CAUV;AAED;;;;;;;;;;;;;;;;;;;;;;GAsBG;AACH,wBAAgB,wBAAwB,CACtC,UAAU,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,SAAS,EAC/C,YAAY,EAAE,MAAM,CAAC,MAAM,EAAE,WAAW,CAAC,EACzC,MAAM,EAAE,cAAc,EACtB,OAAO,EAAE,MAAM,EACf,YAAY,GAAE,SAAS,MAAM,EAAc,EAC3C,SAAS,CAAC,EAAE,mBAAmB,GAC9B;IAAE,OAAO,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,SAAS,CAAC;IAAC,YAAY,EAAE,gBAAgB,CAAA;CAAE,CAkElF"}
|
|
@@ -11,17 +11,37 @@ function isRelationshipFieldConfig(fieldConfig) {
|
|
|
11
11
|
'ref' in fieldConfig &&
|
|
12
12
|
!!fieldConfig.ref);
|
|
13
13
|
}
|
|
14
|
+
/** A plain object — excludes `null` and arrays, which `typeof x === 'object'` alone would admit. */
|
|
15
|
+
function isPlainObject(value) {
|
|
16
|
+
return typeof value === 'object' && value !== null && !Array.isArray(value);
|
|
17
|
+
}
|
|
18
|
+
/** The explicit nested `include` on an include entry, if the entry is a structured object naming one. */
|
|
19
|
+
function getExplicitInclude(value) {
|
|
20
|
+
if (!isPlainObject(value))
|
|
21
|
+
return undefined;
|
|
22
|
+
const { include } = value;
|
|
23
|
+
return isPlainObject(include) ? include : undefined;
|
|
24
|
+
}
|
|
14
25
|
/**
|
|
15
26
|
* The deduped set of relation names declared via `needs` by fields on this
|
|
16
27
|
* list that have a `resolveOutput` hook. A `needs` entry on a field without
|
|
17
28
|
* one is inert — there is no hook to feed it to — so it contributes nothing
|
|
18
29
|
* to fetch.
|
|
30
|
+
*
|
|
31
|
+
* `selectedFields`, when given, restricts the union to fields the read is
|
|
32
|
+
* actually going to return (ADR-0027) — a field a fragment did not select is
|
|
33
|
+
* never computed, so its declared relation is never fetched for it either.
|
|
34
|
+
* `undefined` means unrestricted: every field with a hook contributes,
|
|
35
|
+
* matching a bare or `include`-based read, which always returns every
|
|
36
|
+
* computed field on the list.
|
|
19
37
|
*/
|
|
20
|
-
export function getDeclaredRelationNames(fieldConfigs) {
|
|
38
|
+
export function getDeclaredRelationNames(fieldConfigs, selectedFields) {
|
|
21
39
|
const names = new Set();
|
|
22
|
-
for (const fieldConfig of Object.
|
|
40
|
+
for (const [fieldName, fieldConfig] of Object.entries(fieldConfigs)) {
|
|
23
41
|
if (!fieldConfig?.hooks?.resolveOutput)
|
|
24
42
|
continue;
|
|
43
|
+
if (selectedFields && !selectedFields.has(fieldName))
|
|
44
|
+
continue;
|
|
25
45
|
for (const name of fieldConfig.needs ?? []) {
|
|
26
46
|
names.add(name);
|
|
27
47
|
}
|
|
@@ -30,15 +50,29 @@ export function getDeclaredRelationNames(fieldConfigs) {
|
|
|
30
50
|
}
|
|
31
51
|
/**
|
|
32
52
|
* Fold this list's declared dependencies into `rawInclude`, recursing into
|
|
33
|
-
*
|
|
34
|
-
*
|
|
53
|
+
* EVERY relation reached from here — whether it was just added to satisfy a
|
|
54
|
+
* declaration or was already present for another reason — so that list's own
|
|
55
|
+
* declared dependencies are satisfied too, wherever it's reached (see module
|
|
56
|
+
* doc comment).
|
|
35
57
|
*
|
|
36
58
|
* Returns `rawInclude` itself (same reference) when there is nothing to
|
|
37
59
|
* fold, so a list with no `needs` fields and no caller include stays on the
|
|
38
60
|
* exact bare-read path (ADR-0024) — untouched, not merely equivalent.
|
|
61
|
+
*
|
|
62
|
+
* `listKey` seeds the cycle guard at the root of a read; recursive calls
|
|
63
|
+
* extend it with each related list reached along THIS fold's own path.
|
|
64
|
+
*
|
|
65
|
+
* `selection`, when given, is the fragment scope this level was reached
|
|
66
|
+
* under (ADR-0027) — only fields it names contribute their `needs` (see
|
|
67
|
+
* `getDeclaredRelationNames`). It is `undefined` for a bare/`include`-based
|
|
68
|
+
* read (unrestricted: every field's `needs` folds in, unchanged from before
|
|
69
|
+
* ADR-0027) and for any branch reached only to satisfy a declaration — a
|
|
70
|
+
* relation added purely by this fold has no fragment scope of its own, so
|
|
71
|
+
* its own list folds unrestricted, exactly as it did before selectivity
|
|
72
|
+
* existed.
|
|
39
73
|
*/
|
|
40
|
-
export function foldDeclaredDependencies(rawInclude, fieldConfigs, config) {
|
|
41
|
-
const declaredNames = getDeclaredRelationNames(fieldConfigs);
|
|
74
|
+
export function foldDeclaredDependencies(rawInclude, fieldConfigs, config, listKey, visitedLists = [listKey], selection) {
|
|
75
|
+
const declaredNames = getDeclaredRelationNames(fieldConfigs, selection?.fields);
|
|
42
76
|
if (declaredNames.length === 0 && !rawInclude) {
|
|
43
77
|
return { include: rawInclude, declaredOnly: emptyDeclaredOnlyTree() };
|
|
44
78
|
}
|
|
@@ -56,21 +90,37 @@ export function foldDeclaredDependencies(rawInclude, fieldConfigs, config) {
|
|
|
56
90
|
const fieldConfig = fieldConfigs[key];
|
|
57
91
|
if (!isRelationshipFieldConfig(fieldConfig))
|
|
58
92
|
continue;
|
|
59
|
-
// A whole branch we just added is bare `true` — its own subtree auto-expands
|
|
60
|
-
// (see module doc comment), so there is no explicit nested include to recurse into.
|
|
61
|
-
if (declaredOnly.keys.has(key))
|
|
62
|
-
continue;
|
|
63
|
-
const entry = value;
|
|
64
|
-
if (!entry || typeof entry !== 'object' || !entry.include)
|
|
65
|
-
continue;
|
|
66
93
|
const relatedConfig = getRelatedListConfig(fieldConfig.ref, config);
|
|
67
94
|
if (!relatedConfig)
|
|
68
95
|
continue;
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
96
|
+
// Defensive cycle guard (see module doc comment) — a DECLARATION-ADDED
|
|
97
|
+
// edge into a list already on this path stops here rather than recursing
|
|
98
|
+
// without bound. The value at `key` is left exactly as-is. The guard is
|
|
99
|
+
// deliberately not applied to an edge the request itself named: that
|
|
100
|
+
// recursion is bounded by the request's own finite literal and cannot
|
|
101
|
+
// loop, so stopping it would silently drop the folds beneath a request
|
|
102
|
+
// that merely revisits a list (e.g. `Post → author → posts`).
|
|
103
|
+
if (declaredOnly.keys.has(key) && visitedLists.includes(relatedConfig.listName))
|
|
104
|
+
continue;
|
|
105
|
+
// A branch added purely by the fold has no fragment scope of its own —
|
|
106
|
+
// it folds unrestricted, as before ADR-0027. A branch the request itself
|
|
107
|
+
// named (caller include or fragment) carries that name's own nested
|
|
108
|
+
// scope, if the fragment gave it one (a bare `true` selector leaves it
|
|
109
|
+
// `undefined` — also unrestricted, since the caller asked for
|
|
110
|
+
// "everything" there).
|
|
111
|
+
const nestedSelection = declaredOnly.keys.has(key) ? undefined : selection?.nested[key];
|
|
112
|
+
const explicitNested = getExplicitInclude(value);
|
|
113
|
+
const nested = foldDeclaredDependencies(explicitNested, relatedConfig.listConfig.fields, config, relatedConfig.listName, [...visitedLists, relatedConfig.listName], nestedSelection);
|
|
114
|
+
if (nested.include) {
|
|
115
|
+
merged[key] = {
|
|
116
|
+
...(typeof value === 'object' && value ? value : {}),
|
|
117
|
+
include: nested.include,
|
|
118
|
+
};
|
|
72
119
|
}
|
|
73
|
-
|
|
120
|
+
// A whole branch added purely by the fold above is stripped wholesale by
|
|
121
|
+
// field-visibility regardless of what's nested inside it, so it needs no
|
|
122
|
+
// individual nested-key tracking of its own.
|
|
123
|
+
if (!declaredOnly.keys.has(key) && !isDeclaredOnlyTreeEmpty(nested.declaredOnly)) {
|
|
74
124
|
declaredOnly.nested[key] = nested.declaredOnly;
|
|
75
125
|
}
|
|
76
126
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"declared-dependencies.js","sourceRoot":"","sources":["../../src/access/declared-dependencies.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,oBAAoB,EAAE,MAAM,aAAa,CAAA;
|
|
1
|
+
{"version":3,"file":"declared-dependencies.js","sourceRoot":"","sources":["../../src/access/declared-dependencies.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"declared-dependencies.test.d.ts","sourceRoot":"","sources":["../../src/access/declared-dependencies.test.ts"],"names":[],"mappings":""}
|
|
@@ -0,0 +1,226 @@
|
|
|
1
|
+
import { describe, it, expect } from 'vitest';
|
|
2
|
+
import { foldDeclaredDependencies, emptyDeclaredOnlyTree } from './declared-dependencies.js';
|
|
3
|
+
/**
|
|
4
|
+
* Unit coverage for `foldDeclaredDependencies` (ADR-0025), focused on the
|
|
5
|
+
* behaviour ADR-0026 requires of it once the read pipeline stopped
|
|
6
|
+
* auto-expanding a named relation's own subtree: a declared dependency no
|
|
7
|
+
* longer rides that expansion "for free" and must be folded recursively —
|
|
8
|
+
* at every level a field is computed, however that level was reached
|
|
9
|
+
* (declaration-added, caller-named bare, or caller-named with its own nested
|
|
10
|
+
* include).
|
|
11
|
+
*
|
|
12
|
+
* End-to-end coverage of the fold feeding an actual read (mocked Prisma, real
|
|
13
|
+
* `resolveOutput` hooks) lives in `tests/needs-declared-dependencies.test.ts`;
|
|
14
|
+
* these tests exercise the fold function directly so a regression here fails
|
|
15
|
+
* fast with a small, precise reproduction.
|
|
16
|
+
*/
|
|
17
|
+
function relField(ref, many = false) {
|
|
18
|
+
return { type: 'relationship', ref, many };
|
|
19
|
+
}
|
|
20
|
+
function virtualNeeds(needs) {
|
|
21
|
+
return {
|
|
22
|
+
type: 'virtual',
|
|
23
|
+
needs,
|
|
24
|
+
hooks: { resolveOutput: () => 'x' },
|
|
25
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any -- minimal field config for unit test
|
|
26
|
+
};
|
|
27
|
+
}
|
|
28
|
+
describe('foldDeclaredDependencies', () => {
|
|
29
|
+
it('returns the exact same reference when there is nothing to fold', () => {
|
|
30
|
+
const fields = { name: { type: 'text' } };
|
|
31
|
+
const config = { db: { provider: 'sqlite' }, lists: {} };
|
|
32
|
+
const result = foldDeclaredDependencies(undefined, fields, config, 'List');
|
|
33
|
+
expect(result.include).toBeUndefined();
|
|
34
|
+
expect(result.declaredOnly).toEqual(emptyDeclaredOnlyTree());
|
|
35
|
+
});
|
|
36
|
+
it('folds a declaration-added bare relation whose own related list has further needs', () => {
|
|
37
|
+
// Order.total needs lineItems (declaration-added, no caller include at
|
|
38
|
+
// all). LineItem.summary needs product. Without recursing into the
|
|
39
|
+
// declaration-added branch, `product` would never be fetched, and
|
|
40
|
+
// LineItem.summary would silently compute over `undefined`.
|
|
41
|
+
const config = {
|
|
42
|
+
db: { provider: 'sqlite' },
|
|
43
|
+
lists: {
|
|
44
|
+
Order: {
|
|
45
|
+
fields: {
|
|
46
|
+
lineItems: relField('LineItem.order', true),
|
|
47
|
+
total: virtualNeeds(['lineItems']),
|
|
48
|
+
},
|
|
49
|
+
},
|
|
50
|
+
LineItem: {
|
|
51
|
+
fields: { product: relField('Product'), summary: virtualNeeds(['product']) },
|
|
52
|
+
},
|
|
53
|
+
Product: { fields: { name: { type: 'text' } } },
|
|
54
|
+
},
|
|
55
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any -- minimal config for unit test
|
|
56
|
+
};
|
|
57
|
+
const result = foldDeclaredDependencies(undefined, config.lists.Order.fields, config, 'Order');
|
|
58
|
+
expect(result.include).toEqual({ lineItems: { include: { product: true } } });
|
|
59
|
+
// The whole `lineItems` branch is declaration-only at Order's level, so it
|
|
60
|
+
// needs no fine-grained nested tracking — the entire branch is stripped
|
|
61
|
+
// regardless of what's nested inside it.
|
|
62
|
+
expect(result.declaredOnly.keys.has('lineItems')).toBe(true);
|
|
63
|
+
expect(result.declaredOnly.nested.lineItems).toBeUndefined();
|
|
64
|
+
});
|
|
65
|
+
it('folds a caller-named BARE relation whose related list has its own needs, tracking the addition for fine-grained stripping', () => {
|
|
66
|
+
// The caller explicitly asked for `lineItems` (bare) — NOT declaration-only
|
|
67
|
+
// at Order's level — but LineItem's own `product` still has to be folded
|
|
68
|
+
// in for LineItem.summary's hook. Because the caller DID ask for
|
|
69
|
+
// `lineItems`, `product` must be tracked as declaration-only ONE LEVEL
|
|
70
|
+
// DOWN so it (and only it) gets stripped from each returned line item.
|
|
71
|
+
const config = {
|
|
72
|
+
db: { provider: 'sqlite' },
|
|
73
|
+
lists: {
|
|
74
|
+
Order: {
|
|
75
|
+
fields: {
|
|
76
|
+
lineItems: relField('LineItem.order', true),
|
|
77
|
+
total: virtualNeeds(['lineItems']),
|
|
78
|
+
},
|
|
79
|
+
},
|
|
80
|
+
LineItem: {
|
|
81
|
+
fields: {
|
|
82
|
+
product: relField('Product'),
|
|
83
|
+
tag: relField('Tag'),
|
|
84
|
+
summary: virtualNeeds(['product']),
|
|
85
|
+
},
|
|
86
|
+
},
|
|
87
|
+
Product: { fields: { name: { type: 'text' } } },
|
|
88
|
+
Tag: { fields: { name: { type: 'text' } } },
|
|
89
|
+
},
|
|
90
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any -- minimal config for unit test
|
|
91
|
+
};
|
|
92
|
+
const result = foldDeclaredDependencies({ lineItems: true }, config.lists.Order.fields, config, 'Order');
|
|
93
|
+
expect(result.include).toEqual({ lineItems: { include: { product: true } } });
|
|
94
|
+
// `lineItems` itself is caller-named, not declaration-only...
|
|
95
|
+
expect(result.declaredOnly.keys.has('lineItems')).toBe(false);
|
|
96
|
+
// ...but `product`, nested beneath it, IS — so it gets stripped from each
|
|
97
|
+
// line item while `lineItems` (and its other fields, e.g. `tag`) survive.
|
|
98
|
+
expect(result.declaredOnly.nested.lineItems?.keys.has('product')).toBe(true);
|
|
99
|
+
});
|
|
100
|
+
it("recurses into an explicit caller-nested include so the nested list's own needs are satisfied there too", () => {
|
|
101
|
+
const config = {
|
|
102
|
+
db: { provider: 'sqlite' },
|
|
103
|
+
lists: {
|
|
104
|
+
Order: { fields: { lineItems: relField('LineItem.order', true) } },
|
|
105
|
+
LineItem: {
|
|
106
|
+
fields: {
|
|
107
|
+
product: relField('Product'),
|
|
108
|
+
tag: relField('Tag'),
|
|
109
|
+
summary: virtualNeeds(['product']),
|
|
110
|
+
},
|
|
111
|
+
},
|
|
112
|
+
Product: { fields: { name: { type: 'text' } } },
|
|
113
|
+
Tag: { fields: { name: { type: 'text' } } },
|
|
114
|
+
},
|
|
115
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any -- minimal config for unit test
|
|
116
|
+
};
|
|
117
|
+
const result = foldDeclaredDependencies({ lineItems: { include: { tag: true } } }, config.lists.Order.fields, config, 'Order');
|
|
118
|
+
expect(result.include).toEqual({ lineItems: { include: { tag: true, product: true } } });
|
|
119
|
+
expect(result.declaredOnly.nested.lineItems?.keys.has('product')).toBe(true);
|
|
120
|
+
expect(result.declaredOnly.nested.lineItems?.keys.has('tag')).toBe(false);
|
|
121
|
+
});
|
|
122
|
+
it('does not fold anything beneath a relation whose related list has no needs of its own', () => {
|
|
123
|
+
const config = {
|
|
124
|
+
db: { provider: 'sqlite' },
|
|
125
|
+
lists: {
|
|
126
|
+
Order: { fields: { customer: relField('Customer') } },
|
|
127
|
+
Customer: { fields: { name: { type: 'text' } } },
|
|
128
|
+
},
|
|
129
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any -- minimal config for unit test
|
|
130
|
+
};
|
|
131
|
+
const result = foldDeclaredDependencies({ customer: true }, config.lists.Order.fields, config, 'Order');
|
|
132
|
+
// Untouched — no needs anywhere in reach, so the bare relation stays bare.
|
|
133
|
+
expect(result.include).toEqual({ customer: true });
|
|
134
|
+
expect(result.declaredOnly).toEqual(emptyDeclaredOnlyTree());
|
|
135
|
+
});
|
|
136
|
+
it("folds a revisited list's own needs beneath a relation the request named", () => {
|
|
137
|
+
// The request names Post → author → posts, revisiting Post. Nothing here
|
|
138
|
+
// can loop — the include is a finite literal the caller wrote out — so the
|
|
139
|
+
// fold must keep going and satisfy Post.blurb's `needs` at THAT level too.
|
|
140
|
+
// Guarding a caller-named edge on list identity would stop the fold at
|
|
141
|
+
// `posts` and leave `blurb` computing over an undefined `tags`.
|
|
142
|
+
const config = {
|
|
143
|
+
db: { provider: 'sqlite' },
|
|
144
|
+
lists: {
|
|
145
|
+
Post: {
|
|
146
|
+
fields: {
|
|
147
|
+
author: relField('User.posts'),
|
|
148
|
+
comments: relField('Comment', true),
|
|
149
|
+
tags: relField('Tag', true),
|
|
150
|
+
blurb: virtualNeeds(['tags']),
|
|
151
|
+
},
|
|
152
|
+
},
|
|
153
|
+
User: { fields: { posts: relField('Post.author', true) } },
|
|
154
|
+
Comment: { fields: { body: { type: 'text' } } },
|
|
155
|
+
Tag: { fields: { label: { type: 'text' } } },
|
|
156
|
+
},
|
|
157
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any -- minimal config for unit test
|
|
158
|
+
};
|
|
159
|
+
const result = foldDeclaredDependencies({ author: { include: { posts: { include: { comments: true } } } } }, config.lists.Post.fields, config, 'Post');
|
|
160
|
+
expect(result.include).toEqual({
|
|
161
|
+
tags: true,
|
|
162
|
+
author: { include: { posts: { include: { comments: true, tags: true } } } },
|
|
163
|
+
});
|
|
164
|
+
// Only the nested `tags` needs fine-grained tracking: the root-level one is
|
|
165
|
+
// declaration-only at this level (stripped wholesale), while the nested one
|
|
166
|
+
// sits inside branches the caller named and must be stripped individually.
|
|
167
|
+
expect(result.declaredOnly.keys.has('tags')).toBe(true);
|
|
168
|
+
expect(result.declaredOnly.nested.author?.nested.posts?.keys.has('tags')).toBe(true);
|
|
169
|
+
expect(result.declaredOnly.nested.author?.nested.posts?.keys.has('comments')).toBe(false);
|
|
170
|
+
});
|
|
171
|
+
it("folds a self-referential relation's own needs when the request names it", () => {
|
|
172
|
+
// `parent` points back at Category itself. The request named it, so the
|
|
173
|
+
// fold must satisfy Category's own `needs` beneath it — `depth` is computed
|
|
174
|
+
// on the parent row too.
|
|
175
|
+
const config = {
|
|
176
|
+
db: { provider: 'sqlite' },
|
|
177
|
+
lists: {
|
|
178
|
+
Category: {
|
|
179
|
+
fields: {
|
|
180
|
+
parent: relField('Category.children'),
|
|
181
|
+
children: relField('Category.parent', true),
|
|
182
|
+
depth: virtualNeeds(['children']),
|
|
183
|
+
},
|
|
184
|
+
},
|
|
185
|
+
},
|
|
186
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any -- minimal config for unit test
|
|
187
|
+
};
|
|
188
|
+
const result = foldDeclaredDependencies({ parent: true }, config.lists.Category.fields, config, 'Category');
|
|
189
|
+
// `children` folds in at the root AND beneath the caller-named `parent`.
|
|
190
|
+
// The fold beneath `parent` stops there: `children` is declaration-added,
|
|
191
|
+
// so the guard applies to its own onward edge back into Category.
|
|
192
|
+
expect(result.include).toEqual({
|
|
193
|
+
parent: { include: { children: true } },
|
|
194
|
+
children: true,
|
|
195
|
+
});
|
|
196
|
+
expect(result.declaredOnly.keys.has('children')).toBe(true);
|
|
197
|
+
expect(result.declaredOnly.nested.parent?.keys.has('children')).toBe(true);
|
|
198
|
+
});
|
|
199
|
+
it('defensively terminates a two-list mutual `needs` cycle instead of recursing without bound', () => {
|
|
200
|
+
// Order.total needs lineItems; LineItem.orderRef needs order — a cycle
|
|
201
|
+
// `validateNeedsClosureDepth` (needs-closure.ts) rejects at generate time.
|
|
202
|
+
// This constructs it directly to exercise the fold's OWN defensive guard,
|
|
203
|
+
// independent of that generate-time backstop.
|
|
204
|
+
const config = {
|
|
205
|
+
db: { provider: 'sqlite' },
|
|
206
|
+
lists: {
|
|
207
|
+
Order: {
|
|
208
|
+
fields: {
|
|
209
|
+
lineItems: relField('LineItem.order', true),
|
|
210
|
+
total: virtualNeeds(['lineItems']),
|
|
211
|
+
},
|
|
212
|
+
},
|
|
213
|
+
LineItem: {
|
|
214
|
+
fields: { order: relField('Order.lineItems'), orderRef: virtualNeeds(['order']) },
|
|
215
|
+
},
|
|
216
|
+
},
|
|
217
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any -- minimal config for unit test
|
|
218
|
+
};
|
|
219
|
+
const result = foldDeclaredDependencies(undefined, config.lists.Order.fields, config, 'Order');
|
|
220
|
+
// Terminates (would hang/stack-overflow without the guard) and still
|
|
221
|
+
// produces a usable, finite include: `lineItems` folds in, but `order`
|
|
222
|
+
// beneath it stops at a flat fetch rather than looping back to `lineItems` again.
|
|
223
|
+
expect(result.include).toEqual({ lineItems: { include: { order: true } } });
|
|
224
|
+
});
|
|
225
|
+
});
|
|
226
|
+
//# sourceMappingURL=declared-dependencies.test.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"declared-dependencies.test.js","sourceRoot":"","sources":["../../src/access/declared-dependencies.test.ts"],"names":[],"mappings":"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"}
|
|
@@ -1,12 +1,13 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* Maximum nesting depth of relation `include`s
|
|
3
|
-
* (`
|
|
2
|
+
* Maximum nesting depth of relation `include`s the Access Filter
|
|
3
|
+
* (`buildAccessScopedInclude`) will scope on a read.
|
|
4
4
|
*
|
|
5
|
-
*
|
|
6
|
-
*
|
|
7
|
-
*
|
|
8
|
-
*
|
|
9
|
-
*
|
|
5
|
+
* Since ADR-0026 made the read pipeline caller-directed, this is a COST
|
|
6
|
+
* limit, not an access-control boundary: nothing walks the relationship
|
|
7
|
+
* graph unprompted anymore, so there is no unscoped tree to fail open on. A
|
|
8
|
+
* request naming a relation at or beyond this depth still throws
|
|
9
|
+
* `AccessScopeDepthExceededError` (ADR-0022, issue #830) — the engine
|
|
10
|
+
* declines to serve a tree this expensive, not because it cannot scope one.
|
|
10
11
|
*/
|
|
11
12
|
export declare const READ_INCLUDE_MAX_DEPTH = 5;
|
|
12
13
|
/**
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"depth-limits.d.ts","sourceRoot":"","sources":["../../src/access/depth-limits.ts"],"names":[],"mappings":"AAAA
|
|
1
|
+
{"version":3,"file":"depth-limits.d.ts","sourceRoot":"","sources":["../../src/access/depth-limits.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;GAUG;AACH,eAAO,MAAM,sBAAsB,IAAI,CAAA;AAEvC;;;;;;;;;;;;GAYG;AACH,eAAO,MAAM,wBAAwB,IAAI,CAAA"}
|
|
@@ -1,12 +1,13 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* Maximum nesting depth of relation `include`s
|
|
3
|
-
* (`
|
|
2
|
+
* Maximum nesting depth of relation `include`s the Access Filter
|
|
3
|
+
* (`buildAccessScopedInclude`) will scope on a read.
|
|
4
4
|
*
|
|
5
|
-
*
|
|
6
|
-
*
|
|
7
|
-
*
|
|
8
|
-
*
|
|
9
|
-
*
|
|
5
|
+
* Since ADR-0026 made the read pipeline caller-directed, this is a COST
|
|
6
|
+
* limit, not an access-control boundary: nothing walks the relationship
|
|
7
|
+
* graph unprompted anymore, so there is no unscoped tree to fail open on. A
|
|
8
|
+
* request naming a relation at or beyond this depth still throws
|
|
9
|
+
* `AccessScopeDepthExceededError` (ADR-0022, issue #830) — the engine
|
|
10
|
+
* declines to serve a tree this expensive, not because it cannot scope one.
|
|
10
11
|
*/
|
|
11
12
|
export const READ_INCLUDE_MAX_DEPTH = 5;
|
|
12
13
|
/**
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"depth-limits.js","sourceRoot":"","sources":["../../src/access/depth-limits.ts"],"names":[],"mappings":"AAAA
|
|
1
|
+
{"version":3,"file":"depth-limits.js","sourceRoot":"","sources":["../../src/access/depth-limits.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;GAUG;AACH,MAAM,CAAC,MAAM,sBAAsB,GAAG,CAAC,CAAA;AAEvC;;;;;;;;;;;;GAYG;AACH,MAAM,CAAC,MAAM,wBAAwB,GAAG,CAAC,CAAA"}
|
package/dist/access/errors.d.ts
CHANGED
|
@@ -1,14 +1,18 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* Thrown when a caller-supplied `include` names a relation nested deeper than
|
|
3
|
-
*
|
|
4
|
-
*
|
|
5
|
-
*
|
|
6
|
-
*
|
|
7
|
-
* swallow this.
|
|
3
|
+
* `READ_INCLUDE_MAX_DEPTH`. Deliberately distinct from `ValidationError`: this
|
|
4
|
+
* is not bad user input, it is the engine declining to serve a tree this
|
|
5
|
+
* expensive. Code that catches `ValidationError` to report form errors must
|
|
6
|
+
* not silently swallow this.
|
|
8
7
|
*
|
|
9
|
-
* Only an explicit
|
|
10
|
-
*
|
|
11
|
-
*
|
|
8
|
+
* Only an explicit request naming something at or past the cap triggers this
|
|
9
|
+
* — a read that simply doesn't reach this deep never throws. Before
|
|
10
|
+
* ADR-0026 made the read pipeline caller-directed, this cap was the engine's
|
|
11
|
+
* last line of defense against returning a relation it could not prove was
|
|
12
|
+
* row/field scoped (ADR-0022, issue #830); a request naming anything at this
|
|
13
|
+
* depth is now scoped exactly like every other named relation; the cap
|
|
14
|
+
* exists solely to bound how deep a request may cost the engine to serve. See
|
|
15
|
+
* ADR-0026 and `docs/adr/0022-access-control-fails-closed-when-it-cannot-scope.md`.
|
|
12
16
|
*/
|
|
13
17
|
export declare class AccessScopeDepthExceededError extends Error {
|
|
14
18
|
listKey: string;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"errors.d.ts","sourceRoot":"","sources":["../../src/access/errors.ts"],"names":[],"mappings":"AAEA
|
|
1
|
+
{"version":3,"file":"errors.d.ts","sourceRoot":"","sources":["../../src/access/errors.ts"],"names":[],"mappings":"AAEA;;;;;;;;;;;;;;;GAeG;AACH,qBAAa,6BAA8B,SAAQ,KAAK;IAC/C,OAAO,EAAE,MAAM,CAAA;IACf,QAAQ,EAAE,MAAM,CAAA;IAChB,KAAK,EAAE,MAAM,CAAA;IAEpB,YAAY,OAAO,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,EAW3D;CACF;AAED;;;;;;;;;;;;;GAaG;AACH,qBAAa,uBAAwB,SAAQ,KAAK;IACzC,KAAK,EAAE,SAAS;QAAE,OAAO,EAAE,MAAM,CAAC;QAAC,QAAQ,EAAE,MAAM,CAAA;KAAE,EAAE,CAAA;IAE9D,YAAY,KAAK,EAAE,SAAS;QAAE,OAAO,EAAE,MAAM,CAAC;QAAC,QAAQ,EAAE,MAAM,CAAA;KAAE,EAAE,EAUlE;CACF"}
|