@opensaas/stack-core 0.38.0 → 0.39.1
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 +125 -0
- package/CLAUDE.md +21 -0
- package/dist/access/access-filter.d.ts +46 -1
- package/dist/access/access-filter.d.ts.map +1 -1
- package/dist/access/access-filter.js +113 -4
- package/dist/access/access-filter.js.map +1 -1
- package/dist/access/declared-dependencies.d.ts.map +1 -1
- package/dist/access/declared-dependencies.js +4 -9
- package/dist/access/declared-dependencies.js.map +1 -1
- package/dist/access/engine.d.ts +0 -20
- package/dist/access/engine.d.ts.map +1 -1
- package/dist/access/engine.js +0 -25
- package/dist/access/engine.js.map +1 -1
- package/dist/access/errors.d.ts +49 -0
- package/dist/access/errors.d.ts.map +1 -1
- package/dist/access/errors.js +75 -0
- package/dist/access/errors.js.map +1 -1
- package/dist/access/field-access.d.ts +39 -1
- package/dist/access/field-access.d.ts.map +1 -1
- package/dist/access/field-access.js +108 -42
- package/dist/access/field-access.js.map +1 -1
- package/dist/access/field-access.test.js +229 -2
- package/dist/access/field-access.test.js.map +1 -1
- package/dist/access/field-transforms.d.ts +0 -27
- package/dist/access/field-transforms.d.ts.map +1 -1
- package/dist/access/field-visibility.d.ts +0 -4
- package/dist/access/field-visibility.d.ts.map +1 -1
- package/dist/access/field-visibility.js +13 -39
- package/dist/access/field-visibility.js.map +1 -1
- package/dist/access/index.d.ts +6 -2
- package/dist/access/index.d.ts.map +1 -1
- package/dist/access/index.js +12 -2
- package/dist/access/index.js.map +1 -1
- package/dist/access/query-validation.d.ts +121 -0
- package/dist/access/query-validation.d.ts.map +1 -0
- package/dist/access/query-validation.js +292 -0
- package/dist/access/query-validation.js.map +1 -0
- package/dist/access/relationship-count.d.ts.map +1 -1
- package/dist/access/relationship-count.js +2 -10
- package/dist/access/relationship-count.js.map +1 -1
- package/dist/access/relationship-label-filter.d.ts +23 -23
- package/dist/access/relationship-label-filter.d.ts.map +1 -1
- package/dist/access/relationship-label-filter.js +5 -76
- package/dist/access/relationship-label-filter.js.map +1 -1
- package/dist/access/relationship-label-filter.test.js +19 -56
- package/dist/access/relationship-label-filter.test.js.map +1 -1
- package/dist/access/transaction-registry.d.ts +45 -0
- package/dist/access/transaction-registry.d.ts.map +1 -0
- package/dist/access/transaction-registry.js +37 -0
- package/dist/access/transaction-registry.js.map +1 -0
- package/dist/access/types.d.ts +55 -59
- package/dist/access/types.d.ts.map +1 -1
- package/dist/config/index.d.ts +9 -7
- package/dist/config/index.d.ts.map +1 -1
- package/dist/config/index.js +10 -18
- package/dist/config/index.js.map +1 -1
- package/dist/config/label.d.ts +6 -13
- package/dist/config/label.d.ts.map +1 -1
- package/dist/config/label.js +6 -13
- package/dist/config/label.js.map +1 -1
- package/dist/config/nav-count.d.ts +11 -19
- package/dist/config/nav-count.d.ts.map +1 -1
- package/dist/config/nav-count.js +11 -19
- package/dist/config/nav-count.js.map +1 -1
- package/dist/config/plugin-engine.d.ts +0 -10
- package/dist/config/plugin-engine.d.ts.map +1 -1
- package/dist/config/plugin-engine.js +1 -37
- package/dist/config/plugin-engine.js.map +1 -1
- package/dist/config/types.d.ts +94 -109
- package/dist/config/types.d.ts.map +1 -1
- package/dist/context/apply-defaults.d.ts +8 -31
- package/dist/context/apply-defaults.d.ts.map +1 -1
- package/dist/context/apply-defaults.js +12 -41
- package/dist/context/apply-defaults.js.map +1 -1
- package/dist/context/hook-pipeline.d.ts +4 -29
- package/dist/context/hook-pipeline.d.ts.map +1 -1
- package/dist/context/hook-pipeline.js +8 -41
- package/dist/context/hook-pipeline.js.map +1 -1
- package/dist/context/index.d.ts +4 -15
- package/dist/context/index.d.ts.map +1 -1
- package/dist/context/index.js +190 -193
- package/dist/context/index.js.map +1 -1
- package/dist/context/nested-operations.d.ts +7 -26
- package/dist/context/nested-operations.d.ts.map +1 -1
- package/dist/context/nested-operations.js +73 -171
- package/dist/context/nested-operations.js.map +1 -1
- package/dist/context/transaction-boundary.d.ts +36 -47
- package/dist/context/transaction-boundary.d.ts.map +1 -1
- package/dist/context/transaction-boundary.js +92 -79
- package/dist/context/transaction-boundary.js.map +1 -1
- package/dist/context/write-pipeline.d.ts +25 -64
- package/dist/context/write-pipeline.d.ts.map +1 -1
- package/dist/context/write-pipeline.js +81 -111
- package/dist/context/write-pipeline.js.map +1 -1
- package/dist/fields/format-prisma-default.d.ts.map +1 -1
- package/dist/fields/format-prisma-default.js +4 -7
- package/dist/fields/format-prisma-default.js.map +1 -1
- package/dist/fields/index.d.ts +17 -2
- package/dist/fields/index.d.ts.map +1 -1
- package/dist/fields/index.js +136 -147
- package/dist/fields/index.js.map +1 -1
- package/dist/filter/collect.d.ts +26 -3
- package/dist/filter/collect.d.ts.map +1 -1
- package/dist/filter/collect.js +30 -12
- package/dist/filter/collect.js.map +1 -1
- package/dist/filter/filter.test.js +98 -15
- package/dist/filter/filter.test.js.map +1 -1
- package/dist/filter/index.d.ts +1 -0
- package/dist/filter/index.d.ts.map +1 -1
- package/dist/filter/index.js.map +1 -1
- package/dist/filter/map.d.ts.map +1 -1
- package/dist/filter/map.js +0 -5
- package/dist/filter/map.js.map +1 -1
- package/dist/filter/parse.d.ts.map +1 -1
- package/dist/filter/parse.js +2 -9
- package/dist/filter/parse.js.map +1 -1
- package/dist/filter/serialize.d.ts.map +1 -1
- package/dist/filter/serialize.js +2 -4
- package/dist/filter/serialize.js.map +1 -1
- package/dist/hooks/index.d.ts +4 -42
- package/dist/hooks/index.d.ts.map +1 -1
- package/dist/hooks/index.js +5 -59
- package/dist/hooks/index.js.map +1 -1
- package/dist/index.d.ts +3 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +20 -4
- package/dist/index.js.map +1 -1
- package/dist/internal.d.ts +1 -0
- package/dist/internal.d.ts.map +1 -1
- package/dist/internal.js +4 -0
- package/dist/internal.js.map +1 -1
- package/dist/lib/case-utils.d.ts +0 -32
- package/dist/lib/case-utils.d.ts.map +1 -1
- package/dist/lib/case-utils.js +0 -32
- package/dist/lib/case-utils.js.map +1 -1
- package/dist/mcp/handler.d.ts +0 -4
- package/dist/mcp/handler.d.ts.map +1 -1
- package/dist/mcp/handler.js +12 -69
- package/dist/mcp/handler.js.map +1 -1
- package/dist/query/index.d.ts +26 -227
- package/dist/query/index.d.ts.map +1 -1
- package/dist/query/index.js +14 -117
- package/dist/query/index.js.map +1 -1
- package/dist/utils/password.d.ts +0 -68
- package/dist/utils/password.d.ts.map +1 -1
- package/dist/utils/password.js +7 -76
- package/dist/utils/password.js.map +1 -1
- package/dist/validation/field-config.d.ts.map +1 -1
- package/dist/validation/field-config.js +0 -6
- package/dist/validation/field-config.js.map +1 -1
- package/dist/validation/schema.d.ts +0 -7
- package/dist/validation/schema.d.ts.map +1 -1
- package/dist/validation/schema.js +0 -10
- package/dist/validation/schema.js.map +1 -1
- package/package.json +1 -1
- package/src/access/access-filter.ts +152 -5
- package/src/access/declared-dependencies.ts +4 -9
- package/src/access/engine.ts +0 -25
- package/src/access/errors.ts +81 -0
- package/src/access/field-access.test.ts +312 -2
- package/src/access/field-access.ts +118 -42
- package/src/access/field-transforms.ts +8 -39
- package/src/access/field-visibility.ts +16 -47
- package/src/access/index.ts +20 -2
- package/src/access/query-validation.ts +359 -0
- package/src/access/relationship-count.ts +2 -10
- package/src/access/relationship-label-filter.test.ts +19 -68
- package/src/access/relationship-label-filter.ts +27 -104
- package/src/access/transaction-registry.ts +58 -0
- package/src/access/types.ts +83 -86
- package/src/config/index.ts +10 -22
- package/src/config/label.ts +6 -13
- package/src/config/nav-count.ts +11 -19
- package/src/config/plugin-engine.ts +1 -37
- package/src/config/types.ts +95 -110
- package/src/context/apply-defaults.ts +12 -44
- package/src/context/hook-pipeline.ts +12 -67
- package/src/context/index.ts +263 -247
- package/src/context/nested-operations.ts +94 -226
- package/src/context/transaction-boundary.ts +113 -109
- package/src/context/write-pipeline.ts +99 -155
- package/src/fields/format-prisma-default.ts +4 -7
- package/src/fields/index.ts +155 -147
- package/src/filter/collect.ts +48 -12
- package/src/filter/filter.test.ts +153 -15
- package/src/filter/index.ts +1 -0
- package/src/filter/map.ts +0 -5
- package/src/filter/parse.ts +2 -9
- package/src/filter/serialize.ts +2 -4
- package/src/hooks/index.ts +5 -59
- package/src/index.ts +23 -4
- package/src/internal.ts +5 -0
- package/src/lib/case-utils.ts +0 -32
- package/src/mcp/handler.ts +13 -71
- package/src/query/index.ts +29 -235
- package/src/utils/password.ts +7 -76
- package/src/validation/field-config.ts +0 -6
- package/src/validation/schema.ts +0 -10
- package/tests/context.test.ts +700 -1
- package/tests/field-types.test.ts +222 -0
- package/tests/mcp-handler.test.ts +34 -0
- package/tests/transaction-boundary-outermost.test.ts +530 -0
- package/tsconfig.tsbuildinfo +1 -1
package/dist/access/errors.d.ts
CHANGED
|
@@ -44,4 +44,53 @@ export declare class ResolveOutputCycleError extends Error {
|
|
|
44
44
|
fieldKey: string;
|
|
45
45
|
}[]);
|
|
46
46
|
}
|
|
47
|
+
/**
|
|
48
|
+
* Thrown when a relation filter in a caller's `where` (`some`/`every`/`none`/
|
|
49
|
+
* `is`/`isNot`) names a relationship whose related list denies operation-level
|
|
50
|
+
* `query` access outright (`=== false`). Deliberately a loud failure, not a
|
|
51
|
+
* silently narrowed `{ id: { in: [] } }` or a pass-through: ADR-0022 requires
|
|
52
|
+
* an engine that cannot compute a scope to deny, never pass through, and here
|
|
53
|
+
* the engine CAN compute the scope (denied) — passing that through as a
|
|
54
|
+
* silently-empty match would itself be a distinguishable signal for a caller
|
|
55
|
+
* probing which relations they may filter on. `include`'s equivalent case
|
|
56
|
+
* (`buildAccessScopedInclude`) drops the relation silently instead, because a
|
|
57
|
+
* `null`/missing key in a response is indistinguishable from "not requested";
|
|
58
|
+
* a `where` predicate has no such neutral outcome, so a relation filter on a
|
|
59
|
+
* fully denied relation is refused instead. `sudo` bypasses this check
|
|
60
|
+
* entirely, matching every other access-control escape hatch. See #916 and
|
|
61
|
+
* `docs/adr/0022-access-control-fails-closed-when-it-cannot-scope.md`.
|
|
62
|
+
*/
|
|
63
|
+
export declare class RelationFilterAccessDeniedError extends Error {
|
|
64
|
+
listKey: string;
|
|
65
|
+
fieldKey: string;
|
|
66
|
+
relatedListKey: string;
|
|
67
|
+
constructor(listKey: string, fieldKey: string, relatedListKey: string);
|
|
68
|
+
}
|
|
69
|
+
/**
|
|
70
|
+
* Thrown when a field-level access control function returns anything other
|
|
71
|
+
* than a strict `boolean`. `FieldAccessControl` is typed to return `boolean`
|
|
72
|
+
* only: field access is a single per-field visibility decision, not a row
|
|
73
|
+
* filter, and a denied field is removed rather than used to scope rows (see
|
|
74
|
+
* the "Field-level access" glossary entry in `CONTEXT.md`, ADR-0001, and
|
|
75
|
+
* ADR-0030).
|
|
76
|
+
*
|
|
77
|
+
* A rule that type-checks can never reach this — the only way here is a
|
|
78
|
+
* caller that bypasses the type (an untyped JS config, or a value forced past
|
|
79
|
+
* the checker), most notably a Prisma filter, which is the shape
|
|
80
|
+
* operation-level `AccessControl` accepts but `FieldAccessControl` does not.
|
|
81
|
+
* Before #913 this fell through to an unconditional `return true`, silently
|
|
82
|
+
* granting the field blanket access; it now fails loudly and closed instead,
|
|
83
|
+
* for `read`, `create`, and `update` alike.
|
|
84
|
+
*
|
|
85
|
+
* Deliberately does not expose the offending result as a public field: unlike
|
|
86
|
+
* `AccessScopeDepthExceededError`/`ResolveOutputCycleError`'s fields, there is
|
|
87
|
+
* no concretely-typed shape to give it (the whole problem is that it isn't
|
|
88
|
+
* the `boolean` the caller's rule promised), and the root CLAUDE.md forbids
|
|
89
|
+
* exposing `unknown`/`any` as part of a package's external API. A description
|
|
90
|
+
* of what was returned instead is folded into the message text.
|
|
91
|
+
*/
|
|
92
|
+
export declare class InvalidFieldAccessResultError extends Error {
|
|
93
|
+
operation: 'read' | 'create' | 'update';
|
|
94
|
+
constructor(operation: 'read' | 'create' | 'update', result: unknown);
|
|
95
|
+
}
|
|
47
96
|
//# sourceMappingURL=errors.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
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"}
|
|
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;AAED;;;;;;;;;;;;;;;GAeG;AACH,qBAAa,+BAAgC,SAAQ,KAAK;IACjD,OAAO,EAAE,MAAM,CAAA;IACf,QAAQ,EAAE,MAAM,CAAA;IAChB,cAAc,EAAE,MAAM,CAAA;IAE7B,YAAY,OAAO,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,EAAE,cAAc,EAAE,MAAM,EAUpE;CACF;AASD;;;;;;;;;;;;;;;;;;;;;;GAsBG;AACH,qBAAa,6BAA8B,SAAQ,KAAK;IAC/C,SAAS,EAAE,MAAM,GAAG,QAAQ,GAAG,QAAQ,CAAA;IAE9C,YAAY,SAAS,EAAE,MAAM,GAAG,QAAQ,GAAG,QAAQ,EAAE,MAAM,EAAE,OAAO,EAWnE;CACF"}
|
package/dist/access/errors.js
CHANGED
|
@@ -56,4 +56,79 @@ export class ResolveOutputCycleError extends Error {
|
|
|
56
56
|
this.chain = chain;
|
|
57
57
|
}
|
|
58
58
|
}
|
|
59
|
+
/**
|
|
60
|
+
* Thrown when a relation filter in a caller's `where` (`some`/`every`/`none`/
|
|
61
|
+
* `is`/`isNot`) names a relationship whose related list denies operation-level
|
|
62
|
+
* `query` access outright (`=== false`). Deliberately a loud failure, not a
|
|
63
|
+
* silently narrowed `{ id: { in: [] } }` or a pass-through: ADR-0022 requires
|
|
64
|
+
* an engine that cannot compute a scope to deny, never pass through, and here
|
|
65
|
+
* the engine CAN compute the scope (denied) — passing that through as a
|
|
66
|
+
* silently-empty match would itself be a distinguishable signal for a caller
|
|
67
|
+
* probing which relations they may filter on. `include`'s equivalent case
|
|
68
|
+
* (`buildAccessScopedInclude`) drops the relation silently instead, because a
|
|
69
|
+
* `null`/missing key in a response is indistinguishable from "not requested";
|
|
70
|
+
* a `where` predicate has no such neutral outcome, so a relation filter on a
|
|
71
|
+
* fully denied relation is refused instead. `sudo` bypasses this check
|
|
72
|
+
* entirely, matching every other access-control escape hatch. See #916 and
|
|
73
|
+
* `docs/adr/0022-access-control-fails-closed-when-it-cannot-scope.md`.
|
|
74
|
+
*/
|
|
75
|
+
export class RelationFilterAccessDeniedError extends Error {
|
|
76
|
+
listKey;
|
|
77
|
+
fieldKey;
|
|
78
|
+
relatedListKey;
|
|
79
|
+
constructor(listKey, fieldKey, relatedListKey) {
|
|
80
|
+
super(`Cannot filter "${listKey}.${fieldKey}" — the related list "${relatedListKey}" denies ` +
|
|
81
|
+
`query access to this session, so this relation filter cannot be scoped. Denial is loud ` +
|
|
82
|
+
`rather than silently narrowed to match nothing (ADR-0022). Use sudo to bypass.`);
|
|
83
|
+
this.name = 'RelationFilterAccessDeniedError';
|
|
84
|
+
this.listKey = listKey;
|
|
85
|
+
this.fieldKey = fieldKey;
|
|
86
|
+
this.relatedListKey = relatedListKey;
|
|
87
|
+
}
|
|
88
|
+
}
|
|
89
|
+
function describeFieldAccessResult(result) {
|
|
90
|
+
if (result === null)
|
|
91
|
+
return 'null';
|
|
92
|
+
if (result === undefined)
|
|
93
|
+
return 'undefined';
|
|
94
|
+
if (typeof result === 'object')
|
|
95
|
+
return 'an object (e.g. a Prisma filter)';
|
|
96
|
+
return `a ${typeof result}`;
|
|
97
|
+
}
|
|
98
|
+
/**
|
|
99
|
+
* Thrown when a field-level access control function returns anything other
|
|
100
|
+
* than a strict `boolean`. `FieldAccessControl` is typed to return `boolean`
|
|
101
|
+
* only: field access is a single per-field visibility decision, not a row
|
|
102
|
+
* filter, and a denied field is removed rather than used to scope rows (see
|
|
103
|
+
* the "Field-level access" glossary entry in `CONTEXT.md`, ADR-0001, and
|
|
104
|
+
* ADR-0030).
|
|
105
|
+
*
|
|
106
|
+
* A rule that type-checks can never reach this — the only way here is a
|
|
107
|
+
* caller that bypasses the type (an untyped JS config, or a value forced past
|
|
108
|
+
* the checker), most notably a Prisma filter, which is the shape
|
|
109
|
+
* operation-level `AccessControl` accepts but `FieldAccessControl` does not.
|
|
110
|
+
* Before #913 this fell through to an unconditional `return true`, silently
|
|
111
|
+
* granting the field blanket access; it now fails loudly and closed instead,
|
|
112
|
+
* for `read`, `create`, and `update` alike.
|
|
113
|
+
*
|
|
114
|
+
* Deliberately does not expose the offending result as a public field: unlike
|
|
115
|
+
* `AccessScopeDepthExceededError`/`ResolveOutputCycleError`'s fields, there is
|
|
116
|
+
* no concretely-typed shape to give it (the whole problem is that it isn't
|
|
117
|
+
* the `boolean` the caller's rule promised), and the root CLAUDE.md forbids
|
|
118
|
+
* exposing `unknown`/`any` as part of a package's external API. A description
|
|
119
|
+
* of what was returned instead is folded into the message text.
|
|
120
|
+
*/
|
|
121
|
+
export class InvalidFieldAccessResultError extends Error {
|
|
122
|
+
operation;
|
|
123
|
+
constructor(operation, result) {
|
|
124
|
+
super(`Field-level access control for operation "${operation}" returned ` +
|
|
125
|
+
`${describeFieldAccessResult(result)}, not a boolean. Field access is a per-field ` +
|
|
126
|
+
`visibility decision — it must return true or false, and (unlike operation-level access) ` +
|
|
127
|
+
`cannot scope which rows are affected. If you meant to restrict access based on the row or ` +
|
|
128
|
+
`the write payload, evaluate the condition yourself and return a boolean, e.g. ` +
|
|
129
|
+
`\`({ item, session }) => item?.ownerId === session?.userId\`.`);
|
|
130
|
+
this.name = 'InvalidFieldAccessResultError';
|
|
131
|
+
this.operation = operation;
|
|
132
|
+
}
|
|
133
|
+
}
|
|
59
134
|
//# sourceMappingURL=errors.js.map
|
|
@@ -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;;;;;;;;;;;;;;;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
|
+
{"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;AAED;;;;;;;;;;;;;;;GAeG;AACH,MAAM,OAAO,+BAAgC,SAAQ,KAAK;IACjD,OAAO,CAAQ;IACf,QAAQ,CAAQ;IAChB,cAAc,CAAQ;IAE7B,YAAY,OAAe,EAAE,QAAgB,EAAE,cAAsB;QACnE,KAAK,CACH,kBAAkB,OAAO,IAAI,QAAQ,yBAAyB,cAAc,WAAW;YACrF,yFAAyF;YACzF,gFAAgF,CACnF,CAAA;QACD,IAAI,CAAC,IAAI,GAAG,iCAAiC,CAAA;QAC7C,IAAI,CAAC,OAAO,GAAG,OAAO,CAAA;QACtB,IAAI,CAAC,QAAQ,GAAG,QAAQ,CAAA;QACxB,IAAI,CAAC,cAAc,GAAG,cAAc,CAAA;IACtC,CAAC;CACF;AAED,SAAS,yBAAyB,CAAC,MAAe;IAChD,IAAI,MAAM,KAAK,IAAI;QAAE,OAAO,MAAM,CAAA;IAClC,IAAI,MAAM,KAAK,SAAS;QAAE,OAAO,WAAW,CAAA;IAC5C,IAAI,OAAO,MAAM,KAAK,QAAQ;QAAE,OAAO,kCAAkC,CAAA;IACzE,OAAO,KAAK,OAAO,MAAM,EAAE,CAAA;AAC7B,CAAC;AAED;;;;;;;;;;;;;;;;;;;;;;GAsBG;AACH,MAAM,OAAO,6BAA8B,SAAQ,KAAK;IAC/C,SAAS,CAA8B;IAE9C,YAAY,SAAuC,EAAE,MAAe;QAClE,KAAK,CACH,6CAA6C,SAAS,aAAa;YACjE,GAAG,yBAAyB,CAAC,MAAM,CAAC,+CAA+C;YACnF,0FAA0F;YAC1F,4FAA4F;YAC5F,gFAAgF;YAChF,+DAA+D,CAClE,CAAA;QACD,IAAI,CAAC,IAAI,GAAG,+BAA+B,CAAA;QAC3C,IAAI,CAAC,SAAS,GAAG,SAAS,CAAA;IAC5B,CAAC;CACF"}
|
|
@@ -18,6 +18,10 @@ import type { FieldAccess } from './types.js';
|
|
|
18
18
|
* (read/update/delete), and on the `inputData` being written (create/update),
|
|
19
19
|
* so all of those are accepted. Do not introduce a parallel evaluator with a
|
|
20
20
|
* narrower signature.
|
|
21
|
+
*
|
|
22
|
+
* Returns a strict boolean, never a filter: a rule that returns anything else
|
|
23
|
+
* throws `InvalidFieldAccessResultError` rather than defaulting to allow (see
|
|
24
|
+
* that error's doc, ADR-0001, and ADR-0030).
|
|
21
25
|
*/
|
|
22
26
|
export declare function checkFieldAccess(fieldAccess: FieldAccess | undefined, operation: 'read' | 'create' | 'update', args: {
|
|
23
27
|
session: Session | null;
|
|
@@ -28,8 +32,42 @@ export declare function checkFieldAccess(fieldAccess: FieldAccess | undefined, o
|
|
|
28
32
|
inputData?: Record<string, unknown>;
|
|
29
33
|
}): Promise<boolean>;
|
|
30
34
|
/**
|
|
31
|
-
*
|
|
35
|
+
* Whether a field's `read` access allows it to be NAMED in a `where`/`orderBy`
|
|
36
|
+
* predicate, evaluated BEFORE the query runs (#915).
|
|
37
|
+
*
|
|
38
|
+
* Field-level `read` access has always been a post-query check (Field
|
|
39
|
+
* Visibility, `field-visibility.ts`): it strips a denied key from a row that
|
|
40
|
+
* has already been fetched. That leaves the predicate itself unconstrained —
|
|
41
|
+
* a caller can still filter or sort by a field whose value they could never
|
|
42
|
+
* read, and recover it (or its relative order) by probing. This function is
|
|
43
|
+
* the pre-query counterpart, called from `query-validation.ts`'s `where`/
|
|
44
|
+
* `orderBy` walk for every key that resolves to a declared field.
|
|
45
|
+
*
|
|
46
|
+
* It delegates to the same evaluator Field Visibility uses (`checkFieldAccess`),
|
|
47
|
+
* per this module's canonical-evaluator rule above. The one difference is the
|
|
48
|
+
* `item` it hands the rule: there is no fetched row
|
|
49
|
+
* yet, so a rule that depends on one (the shape `FieldAccess['read']`
|
|
50
|
+
* documents as the norm, e.g. `item?.ownerId === session?.userId`) cannot be
|
|
51
|
+
* answered here. Rather than skip the check for such a rule — which would
|
|
52
|
+
* reopen exactly the hole this closes for the fields most likely to be
|
|
53
|
+
* sensitive — it is handed a poisoned `item` that throws on any property
|
|
54
|
+
* read, and that throw is caught and resolved to `false`: a row-dependent
|
|
55
|
+
* `read` rule always denies at predicate time, documented and deliberate
|
|
56
|
+
* (see docs/adr/0031). A rule that never touches `item` (checking only
|
|
57
|
+
* `session`, the common case for a field meant to be filterable/sortable at
|
|
58
|
+
* all) evaluates normally and returns its real answer.
|
|
59
|
+
*
|
|
60
|
+
* A rule that returns a non-boolean is a distinct, louder failure (#913,
|
|
61
|
+
* ADR-0030) — `InvalidFieldAccessResultError` — and is deliberately NOT
|
|
62
|
+
* folded into the `false` here; it propagates so the config bug it signals is
|
|
63
|
+
* never mistaken for an ordinary field-level denial.
|
|
32
64
|
*/
|
|
65
|
+
export declare function isFieldReadableForPredicate(fieldAccess: FieldAccess | undefined, args: {
|
|
66
|
+
session: Session | null;
|
|
67
|
+
context: AccessContext & {
|
|
68
|
+
_isSudo?: boolean;
|
|
69
|
+
};
|
|
70
|
+
}): Promise<boolean>;
|
|
33
71
|
export declare function filterWritableFields<T extends Record<string, unknown>>(data: T, fieldConfigs: Record<string, {
|
|
34
72
|
access?: FieldAccess;
|
|
35
73
|
type?: string;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"field-access.d.ts","sourceRoot":"","sources":["../../src/access/field-access.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,OAAO,EAAE,aAAa,EAAE,MAAM,YAAY,CAAA;AACxD,OAAO,KAAK,EAAE,WAAW,
|
|
1
|
+
{"version":3,"file":"field-access.d.ts","sourceRoot":"","sources":["../../src/access/field-access.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,OAAO,EAAE,aAAa,EAAE,MAAM,YAAY,CAAA;AACxD,OAAO,KAAK,EAAE,WAAW,EAAsB,MAAM,YAAY,CAAA;AA6CjE;;;;;;;;;GASG;AAEH;;;;;;;;;;;;GAYG;AACH,wBAAsB,gBAAgB,CACpC,WAAW,EAAE,WAAW,GAAG,SAAS,EACpC,SAAS,EAAE,MAAM,GAAG,QAAQ,GAAG,QAAQ,EACvC,IAAI,EAAE;IACJ,OAAO,EAAE,OAAO,GAAG,IAAI,CAAA;IACvB,IAAI,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAA;IAC9B,OAAO,EAAE,aAAa,GAAG;QAAE,OAAO,CAAC,EAAE,OAAO,CAAA;KAAE,CAAA;IAC9C,SAAS,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAA;CACpC,GACA,OAAO,CAAC,OAAO,CAAC,CA+ClB;AAED;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA8BG;AACH,wBAAsB,2BAA2B,CAC/C,WAAW,EAAE,WAAW,GAAG,SAAS,EACpC,IAAI,EAAE;IACJ,OAAO,EAAE,OAAO,GAAG,IAAI,CAAA;IACvB,OAAO,EAAE,aAAa,GAAG;QAAE,OAAO,CAAC,EAAE,OAAO,CAAA;KAAE,CAAA;CAC/C,GACA,OAAO,CAAC,OAAO,CAAC,CAelB;AAED,wBAAsB,oBAAoB,CAAC,CAAC,SAAS,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EAC1E,IAAI,EAAE,CAAC,EACP,YAAY,EAAE,MAAM,CAClB,MAAM,EACN;IACE,MAAM,CAAC,EAAE,WAAW,CAAA;IACpB,IAAI,CAAC,EAAE,MAAM,CAAA;IACb,cAAc,CAAC,EAAE,CAAC,SAAS,EAAE,MAAM,KAAK,MAAM,EAAE,CAAA;CACjD,CACF,EACD,SAAS,EAAE,QAAQ,GAAG,QAAQ,EAC9B,IAAI,EAAE;IACJ,OAAO,EAAE,OAAO,GAAG,IAAI,CAAA;IACvB,IAAI,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAA;IAC9B,OAAO,EAAE,aAAa,GAAG;QAAE,OAAO,CAAC,EAAE,OAAO,CAAA;KAAE,CAAA;IAC9C,SAAS,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAA;CACpC,GACA,OAAO,CAAC,OAAO,CAAC,CAAC,CAAC,CAAC,CAyHrB"}
|
|
@@ -2,6 +2,41 @@
|
|
|
2
2
|
// at module-evaluation time, so the field-access ⇄ hooks import cycle is safe
|
|
3
3
|
// under ESM live bindings.
|
|
4
4
|
import { ValidationError } from '../hooks/index.js';
|
|
5
|
+
import { InvalidFieldAccessResultError } from './errors.js';
|
|
6
|
+
/**
|
|
7
|
+
* Marks a throw caused by touching {@link createPoisonedItem}'s `item`, as
|
|
8
|
+
* opposed to some other error a field rule legitimately raises. Not exported
|
|
9
|
+
* — callers only ever see its effect (a `false` from
|
|
10
|
+
* `isFieldReadableForPredicate`), never the class itself.
|
|
11
|
+
*/
|
|
12
|
+
class PredicateTimeItemAccessError extends Error {
|
|
13
|
+
}
|
|
14
|
+
/**
|
|
15
|
+
* An `item` that throws {@link PredicateTimeItemAccessError} on ANY attempt to
|
|
16
|
+
* read a property off it — including via optional chaining (`item?.x`),
|
|
17
|
+
* since the Proxy itself is a truthy object and optional chaining still
|
|
18
|
+
* performs the property read once its base is non-nullish. Used by
|
|
19
|
+
* `isFieldReadableForPredicate` to detect a read rule that depends on the
|
|
20
|
+
* fetched row: there is no row yet at predicate-evaluation time (see that
|
|
21
|
+
* function's doc), so any rule that reaches into `item` at all cannot be
|
|
22
|
+
* answered here.
|
|
23
|
+
*/
|
|
24
|
+
function createPoisonedItem() {
|
|
25
|
+
return new Proxy({}, {
|
|
26
|
+
get(_target, prop) {
|
|
27
|
+
throw new PredicateTimeItemAccessError(String(prop));
|
|
28
|
+
},
|
|
29
|
+
has(_target, prop) {
|
|
30
|
+
throw new PredicateTimeItemAccessError(String(prop));
|
|
31
|
+
},
|
|
32
|
+
ownKeys() {
|
|
33
|
+
throw new PredicateTimeItemAccessError('ownKeys');
|
|
34
|
+
},
|
|
35
|
+
getOwnPropertyDescriptor(_target, prop) {
|
|
36
|
+
throw new PredicateTimeItemAccessError(String(prop));
|
|
37
|
+
},
|
|
38
|
+
});
|
|
39
|
+
}
|
|
5
40
|
/**
|
|
6
41
|
* Shared field-level access evaluation.
|
|
7
42
|
*
|
|
@@ -20,18 +55,29 @@ import { ValidationError } from '../hooks/index.js';
|
|
|
20
55
|
* (read/update/delete), and on the `inputData` being written (create/update),
|
|
21
56
|
* so all of those are accepted. Do not introduce a parallel evaluator with a
|
|
22
57
|
* narrower signature.
|
|
58
|
+
*
|
|
59
|
+
* Returns a strict boolean, never a filter: a rule that returns anything else
|
|
60
|
+
* throws `InvalidFieldAccessResultError` rather than defaulting to allow (see
|
|
61
|
+
* that error's doc, ADR-0001, and ADR-0030).
|
|
23
62
|
*/
|
|
24
63
|
export async function checkFieldAccess(fieldAccess, operation, args) {
|
|
25
|
-
// Skip access check in sudo mode
|
|
26
64
|
if (args.context._isSudo) {
|
|
27
65
|
return true;
|
|
28
66
|
}
|
|
29
67
|
if (!fieldAccess) {
|
|
30
|
-
return true;
|
|
68
|
+
return true;
|
|
31
69
|
}
|
|
70
|
+
// `FieldAccess['read']` is narrower than `FieldAccess['create'/'update']` (it
|
|
71
|
+
// only accepts the single `operation: 'read'` call shape, see `types.ts`),
|
|
72
|
+
// so indexing by a not-yet-narrowed `operation` union produces a callable
|
|
73
|
+
// whose effective parameter type collapses to an intersection TypeScript
|
|
74
|
+
// can't satisfy generically here. Widen back to the general
|
|
75
|
+
// `FieldAccessControl` — the same shape this function has always built and
|
|
76
|
+
// passed below — since by construction the args object always matches
|
|
77
|
+
// whichever operation is actually requested.
|
|
32
78
|
const accessControl = fieldAccess[operation];
|
|
33
79
|
if (!accessControl) {
|
|
34
|
-
return true;
|
|
80
|
+
return true;
|
|
35
81
|
}
|
|
36
82
|
const result = await accessControl({
|
|
37
83
|
session: args.session,
|
|
@@ -40,53 +86,74 @@ export async function checkFieldAccess(fieldAccess, operation, args) {
|
|
|
40
86
|
inputData: args.inputData,
|
|
41
87
|
operation,
|
|
42
88
|
});
|
|
43
|
-
// If result is false, deny access
|
|
44
89
|
if (result === false) {
|
|
45
90
|
return false;
|
|
46
91
|
}
|
|
47
|
-
// If result is true, allow access
|
|
48
92
|
if (result === true) {
|
|
49
93
|
return true;
|
|
50
94
|
}
|
|
51
|
-
//
|
|
52
|
-
|
|
95
|
+
// `FieldAccessControl` is typed to return `boolean` only — field access is a
|
|
96
|
+
// per-field visibility decision, not a row filter (ADR-0001, ADR-0030). A
|
|
97
|
+
// well-typed rule can never reach this line; getting here means a caller
|
|
98
|
+
// bypassed the type (most notably by returning a Prisma filter, the shape
|
|
99
|
+
// operation-level `AccessControl` accepts but this does not). Fail loudly
|
|
100
|
+
// and closed — this operation applies identically whether or not `item` is
|
|
101
|
+
// available, so `create` (which has no `item` to evaluate a filter against)
|
|
102
|
+
// needs no separate answer: neither operation ever honours a non-boolean.
|
|
103
|
+
throw new InvalidFieldAccessResultError(operation, result);
|
|
53
104
|
}
|
|
54
105
|
/**
|
|
55
|
-
*
|
|
56
|
-
*
|
|
106
|
+
* Whether a field's `read` access allows it to be NAMED in a `where`/`orderBy`
|
|
107
|
+
* predicate, evaluated BEFORE the query runs (#915).
|
|
108
|
+
*
|
|
109
|
+
* Field-level `read` access has always been a post-query check (Field
|
|
110
|
+
* Visibility, `field-visibility.ts`): it strips a denied key from a row that
|
|
111
|
+
* has already been fetched. That leaves the predicate itself unconstrained —
|
|
112
|
+
* a caller can still filter or sort by a field whose value they could never
|
|
113
|
+
* read, and recover it (or its relative order) by probing. This function is
|
|
114
|
+
* the pre-query counterpart, called from `query-validation.ts`'s `where`/
|
|
115
|
+
* `orderBy` walk for every key that resolves to a declared field.
|
|
116
|
+
*
|
|
117
|
+
* It delegates to the same evaluator Field Visibility uses (`checkFieldAccess`),
|
|
118
|
+
* per this module's canonical-evaluator rule above. The one difference is the
|
|
119
|
+
* `item` it hands the rule: there is no fetched row
|
|
120
|
+
* yet, so a rule that depends on one (the shape `FieldAccess['read']`
|
|
121
|
+
* documents as the norm, e.g. `item?.ownerId === session?.userId`) cannot be
|
|
122
|
+
* answered here. Rather than skip the check for such a rule — which would
|
|
123
|
+
* reopen exactly the hole this closes for the fields most likely to be
|
|
124
|
+
* sensitive — it is handed a poisoned `item` that throws on any property
|
|
125
|
+
* read, and that throw is caught and resolved to `false`: a row-dependent
|
|
126
|
+
* `read` rule always denies at predicate time, documented and deliberate
|
|
127
|
+
* (see docs/adr/0031). A rule that never touches `item` (checking only
|
|
128
|
+
* `session`, the common case for a field meant to be filterable/sortable at
|
|
129
|
+
* all) evaluates normally and returns its real answer.
|
|
130
|
+
*
|
|
131
|
+
* A rule that returns a non-boolean is a distinct, louder failure (#913,
|
|
132
|
+
* ADR-0030) — `InvalidFieldAccessResultError` — and is deliberately NOT
|
|
133
|
+
* folded into the `false` here; it propagates so the config bug it signals is
|
|
134
|
+
* never mistaken for an ordinary field-level denial.
|
|
57
135
|
*/
|
|
58
|
-
function
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
else {
|
|
75
|
-
// Direct equality check
|
|
76
|
-
if (item[key] !== condition) {
|
|
77
|
-
return false;
|
|
78
|
-
}
|
|
79
|
-
}
|
|
136
|
+
export async function isFieldReadableForPredicate(fieldAccess, args) {
|
|
137
|
+
try {
|
|
138
|
+
return await checkFieldAccess(fieldAccess, 'read', {
|
|
139
|
+
session: args.session,
|
|
140
|
+
context: args.context,
|
|
141
|
+
item: createPoisonedItem(),
|
|
142
|
+
});
|
|
143
|
+
}
|
|
144
|
+
catch (err) {
|
|
145
|
+
// Only the poisoned-item signal means "row-dependent, deny". Anything
|
|
146
|
+
// else — including `InvalidFieldAccessResultError` and a genuine bug in
|
|
147
|
+
// the rule itself — propagates unchanged rather than being silently
|
|
148
|
+
// folded into an ordinary denial (found in review of #925).
|
|
149
|
+
if (err instanceof PredicateTimeItemAccessError)
|
|
150
|
+
return false;
|
|
151
|
+
throw err;
|
|
80
152
|
}
|
|
81
|
-
return true;
|
|
82
153
|
}
|
|
83
|
-
/**
|
|
84
|
-
* Filter fields from input data based on write access (create/update)
|
|
85
|
-
*/
|
|
86
154
|
export async function filterWritableFields(data, fieldConfigs, operation, args) {
|
|
87
155
|
const filtered = {};
|
|
88
|
-
//
|
|
89
|
-
// Foreign keys should not be in the data when using Prisma's relation syntax
|
|
156
|
+
// Foreign keys must not appear in `data` when using Prisma's relation syntax.
|
|
90
157
|
const foreignKeyFields = new Set();
|
|
91
158
|
// Map each raw per-part column name contributed by a multi-column field
|
|
92
159
|
// (e.g. storage image()/file() in Keystone-parity mode) back to its OWNING
|
|
@@ -122,17 +189,16 @@ export async function filterWritableFields(data, fieldConfigs, operation, args)
|
|
|
122
189
|
const isSudo = args.context._isSudo === true;
|
|
123
190
|
for (const [fieldName, value] of Object.entries(data)) {
|
|
124
191
|
const fieldConfig = fieldConfigs[fieldName];
|
|
125
|
-
// Skip system fields
|
|
126
192
|
if (['id', 'createdAt', 'updatedAt'].includes(fieldName)) {
|
|
127
193
|
continue;
|
|
128
194
|
}
|
|
129
|
-
//
|
|
130
|
-
//
|
|
195
|
+
// Virtual fields don't store in the database — skipped here, but their
|
|
196
|
+
// resolveInput hooks still run as a separate side-effect step.
|
|
131
197
|
if (fieldConfig && 'virtual' in fieldConfig && fieldConfig.virtual) {
|
|
132
198
|
continue;
|
|
133
199
|
}
|
|
134
|
-
//
|
|
135
|
-
//
|
|
200
|
+
// Prevents conflicts with Prisma's relation syntax (e.g.,
|
|
201
|
+
// `author: { connect: { id } }`).
|
|
136
202
|
if (foreignKeyFields.has(fieldName)) {
|
|
137
203
|
continue;
|
|
138
204
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"field-access.js","sourceRoot":"","sources":["../../src/access/field-access.ts"],"names":[],"mappings":"AAEA,iFAAiF;AACjF,8EAA8E;AAC9E,2BAA2B;AAC3B,OAAO,EAAE,eAAe,EAAE,MAAM,mBAAmB,CAAA;AAEnD;;;;;;;;;GASG;AAEH
|
|
1
|
+
{"version":3,"file":"field-access.js","sourceRoot":"","sources":["../../src/access/field-access.ts"],"names":[],"mappings":"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"}
|