@opensaas/stack-core 0.32.0 → 0.34.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.
Files changed (73) hide show
  1. package/.turbo/turbo-build.log +1 -1
  2. package/CHANGELOG.md +61 -0
  3. package/dist/access/access-filter.d.ts +76 -20
  4. package/dist/access/access-filter.d.ts.map +1 -1
  5. package/dist/access/access-filter.js +102 -70
  6. package/dist/access/access-filter.js.map +1 -1
  7. package/dist/access/access-filter.test.js +175 -10
  8. package/dist/access/access-filter.test.js.map +1 -1
  9. package/dist/access/depth-limits.d.ts +26 -0
  10. package/dist/access/depth-limits.d.ts.map +1 -0
  11. package/dist/access/depth-limits.js +26 -0
  12. package/dist/access/depth-limits.js.map +1 -0
  13. package/dist/access/errors.d.ts +43 -0
  14. package/dist/access/errors.d.ts.map +1 -0
  15. package/dist/access/errors.js +55 -0
  16. package/dist/access/errors.js.map +1 -0
  17. package/dist/access/field-visibility.d.ts.map +1 -1
  18. package/dist/access/field-visibility.js +82 -20
  19. package/dist/access/field-visibility.js.map +1 -1
  20. package/dist/access/index.d.ts +4 -1
  21. package/dist/access/index.d.ts.map +1 -1
  22. package/dist/access/index.js +5 -1
  23. package/dist/access/index.js.map +1 -1
  24. package/dist/access/multi-column-read-write.test.js +1 -1
  25. package/dist/access/multi-column-read-write.test.js.map +1 -1
  26. package/dist/access/relationship-count.test.js +1 -1
  27. package/dist/access/relationship-count.test.js.map +1 -1
  28. package/dist/access/relationship-label-filter.test.js +1 -1
  29. package/dist/access/relationship-label-filter.test.js.map +1 -1
  30. package/dist/access/types.d.ts +15 -7
  31. package/dist/access/types.d.ts.map +1 -1
  32. package/dist/context/index.d.ts.map +1 -1
  33. package/dist/context/index.js +15 -9
  34. package/dist/context/index.js.map +1 -1
  35. package/dist/context/nested-operations.d.ts +1 -1
  36. package/dist/context/nested-operations.d.ts.map +1 -1
  37. package/dist/context/nested-operations.js +1 -5
  38. package/dist/context/nested-operations.js.map +1 -1
  39. package/dist/context/transaction-boundary.d.ts.map +1 -1
  40. package/dist/context/transaction-boundary.js +43 -6
  41. package/dist/context/transaction-boundary.js.map +1 -1
  42. package/dist/context/write-pipeline.d.ts.map +1 -1
  43. package/dist/context/write-pipeline.js +5 -4
  44. package/dist/context/write-pipeline.js.map +1 -1
  45. package/dist/index.d.ts +2 -0
  46. package/dist/index.d.ts.map +1 -1
  47. package/dist/index.js +9 -0
  48. package/dist/index.js.map +1 -1
  49. package/package.json +1 -1
  50. package/src/access/access-filter.test.ts +258 -7
  51. package/src/access/access-filter.ts +146 -72
  52. package/src/access/depth-limits.ts +26 -0
  53. package/src/access/errors.ts +62 -0
  54. package/src/access/field-visibility.ts +97 -21
  55. package/src/access/index.ts +6 -0
  56. package/src/access/multi-column-read-write.test.ts +1 -1
  57. package/src/access/relationship-count.test.ts +1 -1
  58. package/src/access/relationship-label-filter.test.ts +1 -1
  59. package/src/access/types.ts +12 -5
  60. package/src/context/index.ts +15 -6
  61. package/src/context/nested-operations.ts +0 -7
  62. package/src/context/transaction-boundary.ts +48 -7
  63. package/src/context/write-pipeline.ts +5 -4
  64. package/src/index.ts +11 -0
  65. package/tests/access-relationships.test.ts +78 -64
  66. package/tests/context.test.ts +106 -24
  67. package/tests/default-value-create.test.ts +1 -1
  68. package/tests/hook-pipeline.test.ts +1 -1
  69. package/tests/nav-count.test.ts +2 -2
  70. package/tests/resolve-chain.test.ts +394 -0
  71. package/tests/transaction-boundary-hooks.test.ts +246 -1
  72. package/tests/write-pipeline.test.ts +1 -1
  73. package/tsconfig.tsbuildinfo +1 -1
@@ -1,4 +1,4 @@
1
1
 
2
- > @opensaas/stack-core@0.32.0 build /home/runner/work/stack/stack/packages/core
2
+ > @opensaas/stack-core@0.34.0 build /home/runner/work/stack/stack/packages/core
3
3
  > tsc
4
4
 
package/CHANGELOG.md CHANGED
@@ -1,5 +1,66 @@
1
1
  # @opensaas/stack-core
2
2
 
3
+ ## 0.34.0
4
+
5
+ ### Minor Changes
6
+
7
+ - [#846](https://github.com/OpenSaasAU/stack/pull/846) [`fedc858`](https://github.com/OpenSaasAU/stack/commit/fedc858f41bf5cacf001f64e7b710112f2fce20b) Thanks [@borisno2](https://github.com/borisno2)! - Fix unbounded recursion when a `resolveOutput` hook issues its own read ([#844](https://github.com/OpenSaasAU/stack/issues/844)): a hook's read could return rows whose own `resolveOutput` hooks issued further reads with no bound, driving the process to the V8 heap limit on a cyclic readable-relationship graph.
8
+
9
+ Reads are now tracked with a resolve chain — the ordered `(list, field)` pairs a read has entered via `resolveOutput` hooks. A hook that would re-enter a pair already on its own chain throws the new `ResolveOutputCycleError` naming the full chain, instead of recursing forever:
10
+
11
+ ```ts
12
+ import { ResolveOutputCycleError } from '@opensaas/stack-core'
13
+
14
+ try {
15
+ await context.db.user.findMany({})
16
+ } catch (err) {
17
+ if (err instanceof ResolveOutputCycleError) {
18
+ // err.chain: readonly { listKey: string; fieldKey: string }[]
19
+ }
20
+ }
21
+ ```
22
+
23
+ An acyclic chain that runs deeper than `RESOLVE_CHAIN_MAX_LENGTH` (a cost limit, not a correctness guard) omits the field and logs a single `console.warn` instead of throwing — a legitimately terminating hook chain (e.g. a virtual field reading another virtual field several hops deep) is never denied.
24
+
25
+ This also fixes a related bug where a plain top-level read running concurrently with an unrelated in-flight `resolveOutput` hook could have its own nested auto-include silently collapsed, because the previous implementation tracked "am I inside a hook?" on one mutable counter shared by the whole request.
26
+
27
+ **Breaking (internal plumbing only):** `AccessContext`'s underscore-prefixed `_resolveOutputCounter: { depth: number }` is replaced by `_resolveOutputChain: readonly { listKey: string; fieldKey: string }[]`. Application code never reads this field. Hand-built `AccessContext` mocks in tests need a one-line update:
28
+
29
+ ```ts
30
+ // Before
31
+ _resolveOutputCounter: {
32
+ depth: 0
33
+ }
34
+ // After
35
+ _resolveOutputChain: []
36
+ ```
37
+
38
+ ## 0.33.0
39
+
40
+ ### Minor Changes
41
+
42
+ - [#838](https://github.com/OpenSaasAU/stack/pull/838) [`0caf680`](https://github.com/OpenSaasAU/stack/commit/0caf68007e41b69f1a5d5f74fb15df2548a559dc) Thanks [@borisno2](https://github.com/borisno2)! - **Behavior change:** reads that previously returned deeply-nested relation data unscoped now throw instead. A caller-supplied `include` nested past the Access Filter's read-include depth cap (`READ_INCLUDE_MAX_DEPTH`, 5) used to fail OPEN — the relation was fetched with no row filter and its fields were never access-checked or `resolveOutput`-processed. It now fails CLOSED: the read throws a new `AccessScopeDepthExceededError` (exported from `@opensaas/stack-core`) naming the list, relation field, and depth reached, instead of returning unscoped data.
43
+
44
+ ```typescript
45
+ import { AccessScopeDepthExceededError } from '@opensaas/stack-core'
46
+
47
+ try {
48
+ await context.db.post.findMany({
49
+ include: { author: { include: {/* … nested past the depth cap */} } },
50
+ })
51
+ } catch (err) {
52
+ if (err instanceof AccessScopeDepthExceededError) {
53
+ // err.listKey / err.fieldKey / err.depth — restructure into shallower reads.
54
+ }
55
+ }
56
+ ```
57
+
58
+ An ordinary read with no caller `include`, or one within the depth limit, is unaffected — the auto-include still stops silently at the cap, matching prior behavior. A read issued from inside a `resolveOutput`/virtual-field hook now also row-scopes its immediate relations (previously it skipped scoping entirely at that point). Field-level read access and `resolveOutput` hooks are now applied at every nesting depth on the returned rows, with no independent cap of their own. See ADR-0022 and issue [#830](https://github.com/OpenSaasAU/stack/issues/830).
59
+
60
+ ### Patch Changes
61
+
62
+ - [#839](https://github.com/OpenSaasAU/stack/pull/839) [`1a3f51d`](https://github.com/OpenSaasAU/stack/commit/1a3f51d5837d6e5244ccf04c3d14c41c264701c3) Thanks [@borisno2](https://github.com/borisno2)! - Fix `beforeTransaction`/`afterTransaction` hooks not firing for lists reachable only past the involved-list enumeration's old fixed depth cap. These hooks now fire for every list a write touches regardless of nesting depth, so compensation logic that previously never ran will start running — that was a bug, not a contract.
63
+
3
64
  ## 0.32.0
4
65
 
5
66
  ## 0.31.1
@@ -15,17 +15,69 @@ import type { OpenSaasConfig, FieldConfig } from '../config/types.js';
15
15
  * `docs/adr/0001-access-control-is-a-two-phase-read.md` and the access-control
16
16
  * glossary in `CONTEXT.md`.
17
17
  */
18
+ /** A single relation entry in a Prisma `include` object (see below). */
19
+ type IncludeEntry = boolean | {
20
+ where?: PrismaFilter;
21
+ include?: IncludeObject;
22
+ take?: number;
23
+ };
24
+ type IncludeObject = Record<string, IncludeEntry>;
25
+ /**
26
+ * The result of trying to compute an access-controlled include for a list's
27
+ * fields. `buildIncludeWithAccessControl` used to collapse three unrelated
28
+ * outcomes into a single overloaded `undefined`: "inside a resolveOutput
29
+ * context", "hit the depth cap", and "no relationships to scope" all looked
30
+ * identical to callers, which is what let a depth-capped relation pass
31
+ * through unscoped (issue #830). This discriminated result keeps them
32
+ * distinguishable all the way to `mergeIncludeWithAccessControl`, which is the
33
+ * only place that knows whether a caller actually asked for the part that
34
+ * couldn't be scoped.
35
+ *
36
+ * - `scoped`: relationships were found and (to the extent depth allows)
37
+ * access-controlled; `include` is the resulting tree.
38
+ * - `nothing-to-scope`: the list genuinely has no relationships to scope, OR
39
+ * we are inside a resolveOutput/virtual-field context and deliberately did
40
+ * not descend into a relation's own nested relations. Passing the caller's
41
+ * include through unchanged here is correct, not a leak.
42
+ * - `depth-exceeded`: we could not evaluate this level at all because it sits
43
+ * at or past `READ_INCLUDE_MAX_DEPTH`. This is a denial: a caller `include`
44
+ * that reaches here must be rejected, not passed through.
45
+ */
46
+ export type AccessIncludeResult = {
47
+ kind: 'scoped';
48
+ include: RichIncludeObject;
49
+ } | {
50
+ kind: 'nothing-to-scope';
51
+ } | {
52
+ kind: 'depth-exceeded';
53
+ };
54
+ /** A relation entry in the rich, provenance-carrying tree `buildIncludeWithAccessControl` builds internally. */
55
+ type RichIncludeEntry = {
56
+ where?: PrismaFilter;
57
+ nested: AccessIncludeResult;
58
+ };
59
+ type RichIncludeObject = Record<string, RichIncludeEntry>;
18
60
  /**
19
- * Build Prisma include object with access control filters
20
- * This allows us to filter relationships at the database level instead of in memory
61
+ * Collapse an `AccessIncludeResult` to the plain Prisma `include` shape used
62
+ * when there is no caller-supplied include to merge against (the direct
63
+ * auto-include path). `nothing-to-scope` and `depth-exceeded` both become
64
+ * `undefined` here — at this call site nothing was explicitly requested past
65
+ * either boundary, so there is nothing to deny.
66
+ */
67
+ export declare function toPrismaInclude(result: AccessIncludeResult): IncludeObject | undefined;
68
+ /**
69
+ * Build the access-controlled include for a list's fields.
70
+ *
71
+ * This allows us to filter relationships at the database level instead of in
72
+ * memory. Returns an {@link AccessIncludeResult} rather than a plain include
73
+ * object so that `mergeIncludeWithAccessControl` can tell a genuine "nothing
74
+ * to scope" apart from "the engine hit its depth cap" (see that type's doc
75
+ * comment and ADR-0022).
21
76
  */
22
77
  export declare function buildIncludeWithAccessControl(fieldConfigs: Record<string, FieldConfig>, args: {
23
78
  session: Session | null;
24
79
  context: AccessContext;
25
- }, config: OpenSaasConfig, depth?: number, visitedLists?: readonly string[]): Promise<Record<string, boolean | {
26
- where?: PrismaFilter;
27
- include?: Record<string, boolean | /*elided*/ any>;
28
- }> | undefined>;
80
+ }, config: OpenSaasConfig, depth?: number, visitedLists?: readonly string[]): Promise<AccessIncludeResult>;
29
81
  /**
30
82
  * Merge a caller-supplied `include` with the access-controlled include — phase-1
31
83
  * row/relation scoping for explicit caller selections.
@@ -49,22 +101,25 @@ export declare function buildIncludeWithAccessControl(fieldConfigs: Record<strin
49
101
  * - If the caller names a key that is NOT a config-declared relationship, it is
50
102
  * passed through unchanged (access control does not govern it).
51
103
  *
52
- * The access-controlled include is recursive to `MAX_DEPTH` (see
53
- * `buildIncludeWithAccessControl`); beyond that depth no auto-include exists, so
54
- * deeper caller selections pass through unscoped consistent with the existing
55
- * auto-include behaviour.
104
+ * `accessControlledInclude` is the {@link AccessIncludeResult} for THIS level:
105
+ * - `nothing-to-scope` nothing to merge against (the list has no
106
+ * relationships, or we're inside a resolveOutput context where the caller
107
+ * include is irrelevant to begin with). Pass the caller's include through
108
+ * unchanged — this is a non-denial outcome, not "every relation denied".
109
+ * - `depth-exceeded` → the engine could not compute a scope for THIS level at
110
+ * all because it sits at or past `READ_INCLUDE_MAX_DEPTH`. If the caller
111
+ * named anything here, that is exactly the case that used to pass through
112
+ * unscoped (issue #830): throw `AccessScopeDepthExceededError` instead. An
113
+ * empty caller include at this level (nothing further requested) is not an
114
+ * error — there's simply nothing to do.
115
+ * - `scoped` → the normal per-relation merge below: a declared relationship
116
+ * ABSENT from the access include was denied (drop it); one PRESENT is used
117
+ * as the base, AND-combining `where`s and recursing into nested includes.
56
118
  *
57
- * `accessControlledInclude` being `undefined` is NOT "every relation denied". It
58
- * means no access-controlled include was computed at all — a non-denial outcome
59
- * that `buildIncludeWithAccessControl` returns when inside a `resolveOutput`/
60
- * virtual-field context, at `MAX_DEPTH`, or when the list has no relationships.
61
- * In every one of those cases there is nothing to merge against, so the caller's
62
- * `include` is passed through unchanged (matching the prior `args.include || …`
63
- * fallback). This is distinct from an `undefined` ENTRY inside a defined access
64
- * include, which DOES mean the relation was denied and must be dropped (see the
65
- * per-relation loop below). Only the whole-object `undefined` is a passthrough.
119
+ * `listKey` and `depth` are carried only to build a useful
120
+ * `AccessScopeDepthExceededError` message; they do not affect merge behaviour.
66
121
  */
67
- export declare function mergeIncludeWithAccessControl(callerInclude: Record<string, unknown>, accessControlledInclude: Record<string, unknown> | undefined, fieldConfigs: Record<string, FieldConfig>, config: OpenSaasConfig): Record<string, unknown>;
122
+ export declare function mergeIncludeWithAccessControl(callerInclude: Record<string, unknown>, accessControlledInclude: AccessIncludeResult, fieldConfigs: Record<string, FieldConfig>, config: OpenSaasConfig, listKey: string, depth?: number): Record<string, unknown>;
68
123
  /**
69
124
  * Remove keys that correspond to `virtual` fields from a Prisma `include`
70
125
  * object, recursing into nested relationship includes using the related
@@ -82,4 +137,5 @@ export declare function mergeIncludeWithAccessControl(callerInclude: Record<stri
82
137
  * no effect on whether the value appears in the result (#628).
83
138
  */
84
139
  export declare function stripVirtualFieldsFromInclude(include: Record<string, unknown> | undefined, fieldConfigs: Record<string, FieldConfig>, config: OpenSaasConfig): Record<string, unknown> | undefined;
140
+ export {};
85
141
  //# sourceMappingURL=access-filter.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"access-filter.d.ts","sourceRoot":"","sources":["../../src/access/access-filter.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,OAAO,EAAE,aAAa,EAAE,YAAY,EAAE,MAAM,YAAY,CAAA;AACtE,OAAO,KAAK,EAAE,cAAc,EAAE,WAAW,EAAE,MAAM,oBAAoB,CAAA;AAGrE;;;;;;;;;;;;;;GAcG;AAEH;;;GAGG;AACH,wBAAsB,6BAA6B,CACjD,YAAY,EAAE,MAAM,CAAC,MAAM,EAAE,WAAW,CAAC,EACzC,IAAI,EAAE;IACJ,OAAO,EAAE,OAAO,GAAG,IAAI,CAAA;IACvB,OAAO,EAAE,aAAa,CAAA;CACvB,EACD,MAAM,EAAE,cAAc,EACtB,KAAK,GAAE,MAAU,EAIjB,YAAY,GAAE,SAAS,MAAM,EAAO;YAeI,YAAY;cAAY,MAAM,CAAC,MAAM,2BAAe;gBAiE7F;AAyDD;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAqCG;AACH,wBAAgB,6BAA6B,CAC3C,aAAa,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EACtC,uBAAuB,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,SAAS,EAC5D,YAAY,EAAE,MAAM,CAAC,MAAM,EAAE,WAAW,CAAC,EACzC,MAAM,EAAE,cAAc,GACrB,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAmEzB;AAED;;;;;;;;;;;;;;;GAeG;AACH,wBAAgB,6BAA6B,CAC3C,OAAO,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,SAAS,EAC5C,YAAY,EAAE,MAAM,CAAC,MAAM,EAAE,WAAW,CAAC,EACzC,MAAM,EAAE,cAAc,GACrB,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,SAAS,CAgCrC"}
1
+ {"version":3,"file":"access-filter.d.ts","sourceRoot":"","sources":["../../src/access/access-filter.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,OAAO,EAAE,aAAa,EAAE,YAAY,EAAE,MAAM,YAAY,CAAA;AACtE,OAAO,KAAK,EAAE,cAAc,EAAE,WAAW,EAAE,MAAM,oBAAoB,CAAA;AAKrE;;;;;;;;;;;;;;GAcG;AAEH,wEAAwE;AACxE,KAAK,YAAY,GAAG,OAAO,GAAG;IAAE,KAAK,CAAC,EAAE,YAAY,CAAC;IAAC,OAAO,CAAC,EAAE,aAAa,CAAC;IAAC,IAAI,CAAC,EAAE,MAAM,CAAA;CAAE,CAAA;AAC9F,KAAK,aAAa,GAAG,MAAM,CAAC,MAAM,EAAE,YAAY,CAAC,CAAA;AAEjD;;;;;;;;;;;;;;;;;;;;GAoBG;AACH,MAAM,MAAM,mBAAmB,GAC3B;IAAE,IAAI,EAAE,QAAQ,CAAC;IAAC,OAAO,EAAE,iBAAiB,CAAA;CAAE,GAC9C;IAAE,IAAI,EAAE,kBAAkB,CAAA;CAAE,GAC5B;IAAE,IAAI,EAAE,gBAAgB,CAAA;CAAE,CAAA;AAE9B,gHAAgH;AAChH,KAAK,gBAAgB,GAAG;IAAE,KAAK,CAAC,EAAE,YAAY,CAAC;IAAC,MAAM,EAAE,mBAAmB,CAAA;CAAE,CAAA;AAC7E,KAAK,iBAAiB,GAAG,MAAM,CAAC,MAAM,EAAE,gBAAgB,CAAC,CAAA;AAqBzD;;;;;;GAMG;AACH,wBAAgB,eAAe,CAAC,MAAM,EAAE,mBAAmB,GAAG,aAAa,GAAG,SAAS,CAOtF;AAED;;;;;;;;GAQG;AACH,wBAAsB,6BAA6B,CACjD,YAAY,EAAE,MAAM,CAAC,MAAM,EAAE,WAAW,CAAC,EACzC,IAAI,EAAE;IACJ,OAAO,EAAE,OAAO,GAAG,IAAI,CAAA;IACvB,OAAO,EAAE,aAAa,CAAA;CACvB,EACD,MAAM,EAAE,cAAc,EACtB,KAAK,GAAE,MAAU,EAIjB,YAAY,GAAE,SAAS,MAAM,EAAO,GACnC,OAAO,CAAC,mBAAmB,CAAC,CAwE9B;AAiDD;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAwCG;AACH,wBAAgB,6BAA6B,CAC3C,aAAa,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EACtC,uBAAuB,EAAE,mBAAmB,EAC5C,YAAY,EAAE,MAAM,CAAC,MAAM,EAAE,WAAW,CAAC,EACzC,MAAM,EAAE,cAAc,EACtB,OAAO,EAAE,MAAM,EACf,KAAK,GAAE,MAAU,GAChB,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CA2EzB;AAED;;;;;;;;;;;;;;;GAeG;AACH,wBAAgB,6BAA6B,CAC3C,OAAO,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,SAAS,EAC5C,YAAY,EAAE,MAAM,CAAC,MAAM,EAAE,WAAW,CAAC,EACzC,MAAM,EAAE,cAAc,GACrB,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,SAAS,CAgCrC"}
@@ -1,39 +1,72 @@
1
1
  import { checkAccess, getRelatedListConfig } from './engine.js';
2
+ import { READ_INCLUDE_MAX_DEPTH } from './depth-limits.js';
3
+ import { AccessScopeDepthExceededError } from './errors.js';
2
4
  /**
3
- * Access Filter phase 1 of the two-phase read (pre-query).
4
- *
5
- * This module scopes which rows and relationships the database is allowed to
6
- * return, before the query runs. It evaluates *operation-level* `query` access
7
- * on related lists and turns the results into a Prisma `include`/`where` clause,
8
- * so denied rows and relations never leave the database.
9
- *
10
- * Phase 2 (post-query field stripping + `resolveOutput` + virtual computation)
11
- * lives in `field-visibility.ts`. The two phases cannot be merged: virtual
12
- * fields are computed in JavaScript and post-query field access can depend on
13
- * the fetched row, neither of which is expressible in SQL. See
14
- * `docs/adr/0001-access-control-is-a-two-phase-read.md` and the access-control
15
- * glossary in `CONTEXT.md`.
5
+ * Collapse a rich, provenance-carrying include entry down to the plain
6
+ * Prisma-shaped form. A `nested` result that is `depth-exceeded` or
7
+ * `nothing-to-scope` contributes no `include` key this is the AUTO-include
8
+ * silently stopping, which is correct when no caller asked for anything past
9
+ * this point (see `AccessIncludeResult` doc comment).
16
10
  */
11
+ function toPrismaEntry(entry) {
12
+ const result = {};
13
+ if (entry.where)
14
+ result.where = entry.where;
15
+ if (entry.nested.kind === 'scoped') {
16
+ const nestedInclude = toPrismaInclude(entry.nested);
17
+ if (nestedInclude && Object.keys(nestedInclude).length > 0) {
18
+ result.include = nestedInclude;
19
+ }
20
+ }
21
+ return Object.keys(result).length > 0 ? result : true;
22
+ }
23
+ /**
24
+ * Collapse an `AccessIncludeResult` to the plain Prisma `include` shape used
25
+ * when there is no caller-supplied include to merge against (the direct
26
+ * auto-include path). `nothing-to-scope` and `depth-exceeded` both become
27
+ * `undefined` here — at this call site nothing was explicitly requested past
28
+ * either boundary, so there is nothing to deny.
29
+ */
30
+ export function toPrismaInclude(result) {
31
+ if (result.kind !== 'scoped')
32
+ return undefined;
33
+ const out = {};
34
+ for (const [key, entry] of Object.entries(result.include)) {
35
+ out[key] = toPrismaEntry(entry);
36
+ }
37
+ return out;
38
+ }
17
39
  /**
18
- * Build Prisma include object with access control filters
19
- * This allows us to filter relationships at the database level instead of in memory
40
+ * Build the access-controlled include for a list's fields.
41
+ *
42
+ * This allows us to filter relationships at the database level instead of in
43
+ * memory. Returns an {@link AccessIncludeResult} rather than a plain include
44
+ * object so that `mergeIncludeWithAccessControl` can tell a genuine "nothing
45
+ * to scope" apart from "the engine hit its depth cap" (see that type's doc
46
+ * comment and ADR-0022).
20
47
  */
21
48
  export async function buildIncludeWithAccessControl(fieldConfigs, args, config, depth = 0,
22
49
  // List names already on the path from the root to here. Used to detect
23
50
  // relationship cycles (A → B → … → A) and stop the auto-include from
24
51
  // re-descending them. Seed it with the root list name at the call site.
25
52
  visitedLists = []) {
26
- const MAX_DEPTH = 5;
27
- if (depth >= MAX_DEPTH) {
28
- return undefined;
29
- }
30
- // Skip auto-including relationships when inside a resolveOutput hook
31
- // This prevents infinite loops when hooks make DB queries that include
32
- // relationships back to the same entity (e.g., User virtual field queries Posts
33
- // which includes author back to User, triggering the virtual field again)
34
- if (args.context._resolveOutputCounter.depth > 0) {
35
- return undefined;
53
+ if (depth >= READ_INCLUDE_MAX_DEPTH) {
54
+ return { kind: 'depth-exceeded' };
36
55
  }
56
+ // Inside a resolveOutput/virtual-field context we still scope each immediate
57
+ // relation (its own access `where`), but do not auto-expand INTO its nested
58
+ // relations — this keeps a single hook-issued read's include from growing
59
+ // depth-first. Correction (ADR-0023): this does NOT by itself terminate a
60
+ // cycle, despite what an earlier version of this comment claimed. A
61
+ // self-referential relation read from inside a hook is a NEW top-level
62
+ // read, invisible to this function's own recursion — the loop runs through
63
+ // the hook↔read boundary, not through repeated calls to
64
+ // `buildIncludeWithAccessControl`. Loop termination is the resolve chain's
65
+ // job: `field-visibility.ts`'s `resolveReadableFieldValue` refuses to
66
+ // re-enter a `(list, field)` pair already on the chain. What this check
67
+ // still does correctly is row-scope the immediate relation rather than
68
+ // skipping scoping altogether (issue #830's second trigger).
69
+ const insideResolveOutput = args.context._resolveOutputChain.length > 0;
37
70
  const include = {};
38
71
  let hasRelationships = false;
39
72
  for (const [fieldName, fieldConfig] of Object.entries(fieldConfigs)) {
@@ -51,12 +84,7 @@ visitedLists = []) {
51
84
  if (accessResult === false) {
52
85
  continue;
53
86
  }
54
- // Build the include entry
55
- const includeEntry = {};
56
- // If access returns a filter, add it to the where clause
57
- if (typeof accessResult === 'object') {
58
- includeEntry.where = accessResult;
59
- }
87
+ const where = typeof accessResult === 'object' ? accessResult : undefined;
60
88
  // Cycle guard: if the related list already appears on the path from the
61
89
  // root, DO NOT auto-include its relationships again. On a cyclic
62
90
  // readable-relationship graph (A → B → … → A) the depth-first walk would
@@ -68,20 +96,16 @@ visitedLists = []) {
68
96
  // genuine single-level fetch, not a re-expansion of the full auto-include.
69
97
  // The relation itself is still included (as a FLAT fetch of its own
70
98
  // columns); only its onward relationships are pruned at the back-edge.
99
+ let nested = { kind: 'nothing-to-scope' };
71
100
  const relatedListName = relatedConfig.listName;
72
- if (!visitedLists.includes(relatedListName)) {
73
- // Recursively build nested includes
74
- const nestedInclude = await buildIncludeWithAccessControl(relatedConfig.listConfig.fields, args, config, depth + 1, [...visitedLists, relatedListName]);
75
- if (nestedInclude && Object.keys(nestedInclude).length > 0) {
76
- includeEntry.include = nestedInclude;
77
- }
101
+ if (!insideResolveOutput && !visitedLists.includes(relatedListName)) {
102
+ nested = await buildIncludeWithAccessControl(relatedConfig.listConfig.fields, args, config, depth + 1, [...visitedLists, relatedListName]);
78
103
  }
79
- // Add to include object
80
- include[fieldName] = Object.keys(includeEntry).length > 0 ? includeEntry : true;
104
+ include[fieldName] = { where, nested };
81
105
  }
82
106
  }
83
107
  }
84
- return hasRelationships ? include : undefined;
108
+ return hasRelationships ? { kind: 'scoped', include } : { kind: 'nothing-to-scope' };
85
109
  }
86
110
  /**
87
111
  * Narrow an unknown include value to the structured object form (vs bare `true`
@@ -148,32 +172,37 @@ function andWhere(accessWhere, callerWhere) {
148
172
  * - If the caller names a key that is NOT a config-declared relationship, it is
149
173
  * passed through unchanged (access control does not govern it).
150
174
  *
151
- * The access-controlled include is recursive to `MAX_DEPTH` (see
152
- * `buildIncludeWithAccessControl`); beyond that depth no auto-include exists, so
153
- * deeper caller selections pass through unscoped consistent with the existing
154
- * auto-include behaviour.
175
+ * `accessControlledInclude` is the {@link AccessIncludeResult} for THIS level:
176
+ * - `nothing-to-scope` nothing to merge against (the list has no
177
+ * relationships, or we're inside a resolveOutput context where the caller
178
+ * include is irrelevant to begin with). Pass the caller's include through
179
+ * unchanged — this is a non-denial outcome, not "every relation denied".
180
+ * - `depth-exceeded` → the engine could not compute a scope for THIS level at
181
+ * all because it sits at or past `READ_INCLUDE_MAX_DEPTH`. If the caller
182
+ * named anything here, that is exactly the case that used to pass through
183
+ * unscoped (issue #830): throw `AccessScopeDepthExceededError` instead. An
184
+ * empty caller include at this level (nothing further requested) is not an
185
+ * error — there's simply nothing to do.
186
+ * - `scoped` → the normal per-relation merge below: a declared relationship
187
+ * ABSENT from the access include was denied (drop it); one PRESENT is used
188
+ * as the base, AND-combining `where`s and recursing into nested includes.
155
189
  *
156
- * `accessControlledInclude` being `undefined` is NOT "every relation denied". It
157
- * means no access-controlled include was computed at all — a non-denial outcome
158
- * that `buildIncludeWithAccessControl` returns when inside a `resolveOutput`/
159
- * virtual-field context, at `MAX_DEPTH`, or when the list has no relationships.
160
- * In every one of those cases there is nothing to merge against, so the caller's
161
- * `include` is passed through unchanged (matching the prior `args.include || …`
162
- * fallback). This is distinct from an `undefined` ENTRY inside a defined access
163
- * include, which DOES mean the relation was denied and must be dropped (see the
164
- * per-relation loop below). Only the whole-object `undefined` is a passthrough.
190
+ * `listKey` and `depth` are carried only to build a useful
191
+ * `AccessScopeDepthExceededError` message; they do not affect merge behaviour.
165
192
  */
166
- export function mergeIncludeWithAccessControl(callerInclude, accessControlledInclude, fieldConfigs, config) {
167
- // No access-controlled include was computed (resolveOutput/virtual context,
168
- // MAX_DEPTH, or a list with no relationships) → nothing to scope against, so
169
- // pass the caller's include through unchanged. Dropping relations here would be
170
- // fail-closed data loss, not a denial. Denied relations are dropped only when a
171
- // defined access include OMITS them (handled per-relation below).
172
- if (accessControlledInclude === undefined) {
193
+ export function mergeIncludeWithAccessControl(callerInclude, accessControlledInclude, fieldConfigs, config, listKey, depth = 0) {
194
+ if (accessControlledInclude.kind === 'nothing-to-scope') {
173
195
  return callerInclude;
174
196
  }
197
+ if (accessControlledInclude.kind === 'depth-exceeded') {
198
+ const [firstRelationName] = Object.keys(callerInclude);
199
+ if (firstRelationName !== undefined) {
200
+ throw new AccessScopeDepthExceededError(listKey, firstRelationName, depth);
201
+ }
202
+ return {};
203
+ }
175
204
  const merged = {};
176
- const accessInclude = accessControlledInclude;
205
+ const accessInclude = accessControlledInclude.include;
177
206
  for (const [relationName, callerValue] of Object.entries(callerInclude)) {
178
207
  const fieldConfig = fieldConfigs[relationName];
179
208
  const isDeclaredRelationship = fieldConfig?.type === 'relationship' && 'ref' in fieldConfig && !!fieldConfig.ref;
@@ -182,27 +211,30 @@ export function mergeIncludeWithAccessControl(callerInclude, accessControlledInc
182
211
  merged[relationName] = callerValue;
183
212
  continue;
184
213
  }
185
- const accessValue = accessInclude[relationName];
214
+ const accessEntry = accessInclude[relationName];
186
215
  // Declared relationship absent from the access include → query access denied → drop it.
187
- if (accessValue === undefined) {
216
+ if (accessEntry === undefined) {
188
217
  continue;
189
218
  }
190
- const accessEntry = asEntryObject(accessValue);
191
219
  const callerEntry = asEntryObject(callerValue);
192
220
  // Resolve the related list's field configs so nested includes merge recursively.
193
221
  const relatedConfig = getRelatedListConfig(fieldConfig.ref, config);
194
222
  const relatedFields = relatedConfig?.listConfig.fields;
195
- const mergedWhere = andWhere(accessEntry?.where, callerEntry?.where);
223
+ const mergedWhere = andWhere(accessEntry.where, callerEntry?.where);
196
224
  let mergedNested;
197
- if (callerEntry?.include && relatedFields) {
198
- // Recurse: scope the caller's nested selection against the nested access include.
199
- mergedNested = mergeIncludeWithAccessControl(callerEntry.include, accessEntry?.include, relatedFields, config);
225
+ if (callerEntry?.include && relatedFields && relatedConfig) {
226
+ // Recurse: scope the caller's nested selection against the nested access
227
+ // result. If that result is 'depth-exceeded', the recursive call itself
228
+ // throws — the caller named something the engine cannot scope.
229
+ mergedNested = mergeIncludeWithAccessControl(callerEntry.include, accessEntry.nested, relatedFields, config, relatedConfig.listName, depth + 1);
200
230
  }
201
- else if (accessEntry?.include) {
231
+ else if (accessEntry.nested.kind === 'scoped') {
202
232
  // Caller selected the relation bare (no nested include); keep the
203
233
  // access-controlled nested include so deeper relations stay filtered.
204
- mergedNested = accessEntry.include;
234
+ mergedNested = toPrismaInclude(accessEntry.nested);
205
235
  }
236
+ // If the caller's entry has no nested include, a 'depth-exceeded' nested
237
+ // result is silent here too — nothing was asked for past this point.
206
238
  const entry = {};
207
239
  if (mergedWhere)
208
240
  entry.where = mergedWhere;
@@ -1 +1 @@
1
- {"version":3,"file":"access-filter.js","sourceRoot":"","sources":["../../src/access/access-filter.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"access-filter.js","sourceRoot":"","sources":["../../src/access/access-filter.ts"],"names":[],"mappings":"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"}