@opensaas/stack-core 0.38.0 → 0.39.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 +123 -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/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/access/index.ts"],"names":[],"mappings":"AAaA,mEAAmE;AACnE,OAAO,EACL,WAAW,EACX,YAAY,EACZ,SAAS,EACT,cAAc,EACd,oBAAoB,GACrB,MAAM,aAAa,CAAA;AACpB,4EAA4E;AAC5E,OAAO,
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/access/index.ts"],"names":[],"mappings":"AAaA,mEAAmE;AACnE,OAAO,EACL,WAAW,EACX,YAAY,EACZ,SAAS,EACT,cAAc,EACd,oBAAoB,GACrB,MAAM,aAAa,CAAA;AACpB,4EAA4E;AAC5E,OAAO,EACL,gBAAgB,EAChB,oBAAoB,EACpB,2BAA2B,GAC5B,MAAM,mBAAmB,CAAA;AAC1B,6EAA6E;AAC7E,qCAAqC;AACrC,OAAO,EAAE,iBAAiB,EAAE,MAAM,uBAAuB,CAAA;AACzD,uEAAuE;AACvE,oEAAoE;AACpE,OAAO,EAAE,4BAA4B,EAAE,MAAM,uBAAuB,CAAA;AACpE,4DAA4D;AAC5D,OAAO,EACL,wBAAwB,EACxB,sBAAsB,EACtB,6BAA6B,GAC9B,MAAM,oBAAoB,CAAA;AAC3B,2EAA2E;AAC3E,OAAO,EAAE,oBAAoB,EAAE,MAAM,uBAAuB,CAAA;AAC5D,2EAA2E;AAC3E,yBAAyB;AACzB,OAAO,EACL,wBAAwB,EACxB,wBAAwB,EACxB,qBAAqB,GACtB,MAAM,4BAA4B,CAAA;AAEnC,mFAAmF;AACnF,OAAO,EAAE,6BAA6B,EAAE,MAAM,aAAa,CAAA;AAC3D,gFAAgF;AAChF,OAAO,EAAE,uBAAuB,EAAE,MAAM,aAAa,CAAA;AACrD,kFAAkF;AAClF,OAAO,EAAE,6BAA6B,EAAE,MAAM,aAAa,CAAA;AAC3D,oFAAoF;AACpF,OAAO,EAAE,+BAA+B,EAAE,MAAM,aAAa,CAAA"}
|
|
@@ -0,0 +1,121 @@
|
|
|
1
|
+
import type { ListConfig, OpenSaasConfig } from '../config/types.js';
|
|
2
|
+
import type { Session, AccessContext } from './types.js';
|
|
3
|
+
/**
|
|
4
|
+
* #912 — read-path key validation.
|
|
5
|
+
*
|
|
6
|
+
* The write path settled #564: an undeclared key in `data` throws, because the
|
|
7
|
+
* generated Prisma model has strictly more fields than the config declares (most
|
|
8
|
+
* notably back-relations — Prisma emits one for every inbound foreign key, whether
|
|
9
|
+
* or not the list config declares the reverse relationship). Reads had no
|
|
10
|
+
* equivalent — a caller's `where`/`orderBy` reached Prisma unchanged, so an
|
|
11
|
+
* anonymous caller could filter or order by a relation the config never exposed.
|
|
12
|
+
*
|
|
13
|
+
* This module is the read-path counterpart: every key named in a caller's `where`
|
|
14
|
+
* or `orderBy` is resolved against the list config before the query runs. A key
|
|
15
|
+
* with no entry in the config throws, naming the list and the key. `sudo` is the
|
|
16
|
+
* single trusted bypass, mirroring the write path.
|
|
17
|
+
*
|
|
18
|
+
* Deliberately NOT walked: the access filter produced by the list's own `query`
|
|
19
|
+
* access control. That filter is trusted config authored by the same person who
|
|
20
|
+
* declares the fields — walking it would make this an access-control decision
|
|
21
|
+
* (that's #915/#916), not the key-existence seam this ticket establishes.
|
|
22
|
+
*
|
|
23
|
+
* `validateQueryFieldReadAccess` below is that access-control decision for
|
|
24
|
+
* #915: it re-walks `where`/`orderBy` (reusing `resolveQueryField`) and checks
|
|
25
|
+
* each resolved field's `read` access via the canonical evaluator, so a field
|
|
26
|
+
* the session cannot read cannot be named in a predicate either. It runs
|
|
27
|
+
* strictly after this module's key-existence check — an undeclared key is
|
|
28
|
+
* #912's rejection, not a field-access decision — and stays scoped to the
|
|
29
|
+
* CURRENT list, deliberately not recursing into a related list's fields
|
|
30
|
+
* nested inside a relation filter itself (that recursion is #916's job, not
|
|
31
|
+
* this module's — see below).
|
|
32
|
+
*
|
|
33
|
+
* #916 — the relation-filter counterpart — scopes a relation filter itself
|
|
34
|
+
* (`some`/`every`/`none`/`is`/`isNot`) by the RELATED list's own `query`
|
|
35
|
+
* access, folding it into the nested clause exactly like
|
|
36
|
+
* `buildAccessScopedInclude` folds it into `include` (`access-filter.ts`).
|
|
37
|
+
* It reuses this module's shape-recognition (`resolveQueryField`,
|
|
38
|
+
* `LOGICAL_OPERATORS`, `RELATION_QUANTIFIERS`, exported below) and calls
|
|
39
|
+
* `walkWhereReadAccess` once per hop — against the RELATED list's own
|
|
40
|
+
* config — for the field-read half of the same job this module's
|
|
41
|
+
* `validateQueryFieldReadAccess` already does for the CURRENT list. There is
|
|
42
|
+
* deliberately no second copy of either the shape-recognition or the
|
|
43
|
+
* field-read check: `access-filter.ts` supplies the RELATED list at each
|
|
44
|
+
* hop and calls back into the same primitives this module already owns.
|
|
45
|
+
*/
|
|
46
|
+
export declare const LOGICAL_OPERATORS: Set<string>;
|
|
47
|
+
export declare const RELATION_QUANTIFIERS: Set<string>;
|
|
48
|
+
type FieldConfigMap = Record<string, any>;
|
|
49
|
+
export interface ResolvedQueryField {
|
|
50
|
+
fieldConfig: any;
|
|
51
|
+
isRelationship: boolean;
|
|
52
|
+
}
|
|
53
|
+
/**
|
|
54
|
+
* Resolve a `where`/`orderBy` key against a list's declared fields.
|
|
55
|
+
*
|
|
56
|
+
* A key is valid when it is:
|
|
57
|
+
* - a system field (`id`, `createdAt`, `updatedAt`) — always present, and never
|
|
58
|
+
* declared in `fields` (the write path excludes them from `fieldConfigs` the
|
|
59
|
+
* same way), or
|
|
60
|
+
* - a field declared directly in the list config, or
|
|
61
|
+
* - the foreign-key scalar a to-one `relationship` field implies (e.g. `authorId`
|
|
62
|
+
* for `author: relationship(...)`) — the config never names this column
|
|
63
|
+
* directly, but Prisma always generates it, and the write path
|
|
64
|
+
* (`filterWritableFields`) grants it the same pass, or
|
|
65
|
+
* - a raw per-part column a multi-column field's `splitColumns` contributes (e.g.
|
|
66
|
+
* storage `image()`/`file()` in Keystone-parity mode) — undeclared by design,
|
|
67
|
+
* mirroring the write path's `splitColumnOwners` allowance (#568/#789).
|
|
68
|
+
*
|
|
69
|
+
* Anything else — most importantly a Prisma-generated back-relation the config
|
|
70
|
+
* never declares — resolves to `undefined` and is rejected by the caller.
|
|
71
|
+
*/
|
|
72
|
+
export declare function resolveQueryField(key: string, fields: FieldConfigMap): ResolvedQueryField | undefined;
|
|
73
|
+
/**
|
|
74
|
+
* Validate a caller-supplied `where`/`orderBy` against the list config,
|
|
75
|
+
* recursing into logical operators (`AND`/`OR`/`NOT`) and relation filters.
|
|
76
|
+
* Throws a `ValidationError` naming the list and the offending key on the
|
|
77
|
+
* first undeclared key found. `isSudo` bypasses the check entirely, matching
|
|
78
|
+
* the write path's `sudo` escape hatch.
|
|
79
|
+
*/
|
|
80
|
+
export declare function validateQueryKeys(args: {
|
|
81
|
+
where?: unknown;
|
|
82
|
+
orderBy?: unknown;
|
|
83
|
+
listConfig: ListConfig<any>;
|
|
84
|
+
listName: string;
|
|
85
|
+
config: OpenSaasConfig;
|
|
86
|
+
isSudo: boolean;
|
|
87
|
+
}): void;
|
|
88
|
+
/**
|
|
89
|
+
* Check field-level `read` access for every key at ONE level of a `where`
|
|
90
|
+
* clause, recursing only into logical operators (`AND`/`OR`/`NOT`) — never
|
|
91
|
+
* into a relationship field's own nested value. Exported for `access-filter.ts`'s
|
|
92
|
+
* #916 reuse (see module doc comment above) — called once per hop against the
|
|
93
|
+
* RELATED list's own config.
|
|
94
|
+
*/
|
|
95
|
+
export declare function walkWhereReadAccess(where: unknown, listConfig: ListConfig<any>, listName: string, args: {
|
|
96
|
+
session: Session | null;
|
|
97
|
+
context: AccessContext & {
|
|
98
|
+
_isSudo?: boolean;
|
|
99
|
+
};
|
|
100
|
+
}): Promise<void>;
|
|
101
|
+
/**
|
|
102
|
+
* Validate a caller-supplied `where`/`orderBy` against field-level `read`
|
|
103
|
+
* access, recursing into logical operators (`AND`/`OR`/`NOT`) the same way
|
|
104
|
+
* `validateQueryKeys` does. Throws a `ValidationError` naming the list and
|
|
105
|
+
* the offending key on the first read-denied field found. `isSudo` bypasses
|
|
106
|
+
* the check entirely, matching `validateQueryKeys` and the write path's
|
|
107
|
+
* `sudo` escape hatch.
|
|
108
|
+
*/
|
|
109
|
+
export declare function validateQueryFieldReadAccess(args: {
|
|
110
|
+
where?: unknown;
|
|
111
|
+
orderBy?: unknown;
|
|
112
|
+
listConfig: ListConfig<any>;
|
|
113
|
+
listName: string;
|
|
114
|
+
session: Session | null;
|
|
115
|
+
context: AccessContext & {
|
|
116
|
+
_isSudo?: boolean;
|
|
117
|
+
};
|
|
118
|
+
isSudo: boolean;
|
|
119
|
+
}): Promise<void>;
|
|
120
|
+
export {};
|
|
121
|
+
//# sourceMappingURL=query-validation.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"query-validation.d.ts","sourceRoot":"","sources":["../../src/access/query-validation.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,UAAU,EAAE,cAAc,EAAE,MAAM,oBAAoB,CAAA;AACpE,OAAO,KAAK,EAAE,OAAO,EAAE,aAAa,EAAE,MAAM,YAAY,CAAA;AAKxD;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA0CG;AAGH,eAAO,MAAM,iBAAiB,aAAgC,CAAA;AAI9D,eAAO,MAAM,oBAAoB,aAAoD,CAAA;AAOrF,KAAK,cAAc,GAAG,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,CAAA;AAEzC,MAAM,WAAW,kBAAkB;IAEjC,WAAW,EAAE,GAAG,CAAA;IAChB,cAAc,EAAE,OAAO,CAAA;CACxB;AAED;;;;;;;;;;;;;;;;;;GAkBG;AACH,wBAAgB,iBAAiB,CAC/B,GAAG,EAAE,MAAM,EACX,MAAM,EAAE,cAAc,GACrB,kBAAkB,GAAG,SAAS,CA2BhC;AA4GD;;;;;;GAMG;AACH,wBAAgB,iBAAiB,CAAC,IAAI,EAAE;IACtC,KAAK,CAAC,EAAE,OAAO,CAAA;IACf,OAAO,CAAC,EAAE,OAAO,CAAA;IAEjB,UAAU,EAAE,UAAU,CAAC,GAAG,CAAC,CAAA;IAC3B,QAAQ,EAAE,MAAM,CAAA;IAChB,MAAM,EAAE,cAAc,CAAA;IACtB,MAAM,EAAE,OAAO,CAAA;CAChB,GAAG,IAAI,CAIP;AAmCD;;;;;;GAMG;AACH,wBAAsB,mBAAmB,CACvC,KAAK,EAAE,OAAO,EAEd,UAAU,EAAE,UAAU,CAAC,GAAG,CAAC,EAC3B,QAAQ,EAAE,MAAM,EAChB,IAAI,EAAE;IAAE,OAAO,EAAE,OAAO,GAAG,IAAI,CAAC;IAAC,OAAO,EAAE,aAAa,GAAG;QAAE,OAAO,CAAC,EAAE,OAAO,CAAA;KAAE,CAAA;CAAE,GAChF,OAAO,CAAC,IAAI,CAAC,CAoBf;AAqBD;;;;;;;GAOG;AACH,wBAAsB,4BAA4B,CAAC,IAAI,EAAE;IACvD,KAAK,CAAC,EAAE,OAAO,CAAA;IACf,OAAO,CAAC,EAAE,OAAO,CAAA;IAEjB,UAAU,EAAE,UAAU,CAAC,GAAG,CAAC,CAAA;IAC3B,QAAQ,EAAE,MAAM,CAAA;IAChB,OAAO,EAAE,OAAO,GAAG,IAAI,CAAA;IACvB,OAAO,EAAE,aAAa,GAAG;QAAE,OAAO,CAAC,EAAE,OAAO,CAAA;KAAE,CAAA;IAC9C,MAAM,EAAE,OAAO,CAAA;CAChB,GAAG,OAAO,CAAC,IAAI,CAAC,CAMhB"}
|
|
@@ -0,0 +1,292 @@
|
|
|
1
|
+
import { getRelatedListConfig } from './engine.js';
|
|
2
|
+
import { isFieldReadableForPredicate } from './field-access.js';
|
|
3
|
+
import { ValidationError } from '../hooks/index.js';
|
|
4
|
+
/**
|
|
5
|
+
* #912 — read-path key validation.
|
|
6
|
+
*
|
|
7
|
+
* The write path settled #564: an undeclared key in `data` throws, because the
|
|
8
|
+
* generated Prisma model has strictly more fields than the config declares (most
|
|
9
|
+
* notably back-relations — Prisma emits one for every inbound foreign key, whether
|
|
10
|
+
* or not the list config declares the reverse relationship). Reads had no
|
|
11
|
+
* equivalent — a caller's `where`/`orderBy` reached Prisma unchanged, so an
|
|
12
|
+
* anonymous caller could filter or order by a relation the config never exposed.
|
|
13
|
+
*
|
|
14
|
+
* This module is the read-path counterpart: every key named in a caller's `where`
|
|
15
|
+
* or `orderBy` is resolved against the list config before the query runs. A key
|
|
16
|
+
* with no entry in the config throws, naming the list and the key. `sudo` is the
|
|
17
|
+
* single trusted bypass, mirroring the write path.
|
|
18
|
+
*
|
|
19
|
+
* Deliberately NOT walked: the access filter produced by the list's own `query`
|
|
20
|
+
* access control. That filter is trusted config authored by the same person who
|
|
21
|
+
* declares the fields — walking it would make this an access-control decision
|
|
22
|
+
* (that's #915/#916), not the key-existence seam this ticket establishes.
|
|
23
|
+
*
|
|
24
|
+
* `validateQueryFieldReadAccess` below is that access-control decision for
|
|
25
|
+
* #915: it re-walks `where`/`orderBy` (reusing `resolveQueryField`) and checks
|
|
26
|
+
* each resolved field's `read` access via the canonical evaluator, so a field
|
|
27
|
+
* the session cannot read cannot be named in a predicate either. It runs
|
|
28
|
+
* strictly after this module's key-existence check — an undeclared key is
|
|
29
|
+
* #912's rejection, not a field-access decision — and stays scoped to the
|
|
30
|
+
* CURRENT list, deliberately not recursing into a related list's fields
|
|
31
|
+
* nested inside a relation filter itself (that recursion is #916's job, not
|
|
32
|
+
* this module's — see below).
|
|
33
|
+
*
|
|
34
|
+
* #916 — the relation-filter counterpart — scopes a relation filter itself
|
|
35
|
+
* (`some`/`every`/`none`/`is`/`isNot`) by the RELATED list's own `query`
|
|
36
|
+
* access, folding it into the nested clause exactly like
|
|
37
|
+
* `buildAccessScopedInclude` folds it into `include` (`access-filter.ts`).
|
|
38
|
+
* It reuses this module's shape-recognition (`resolveQueryField`,
|
|
39
|
+
* `LOGICAL_OPERATORS`, `RELATION_QUANTIFIERS`, exported below) and calls
|
|
40
|
+
* `walkWhereReadAccess` once per hop — against the RELATED list's own
|
|
41
|
+
* config — for the field-read half of the same job this module's
|
|
42
|
+
* `validateQueryFieldReadAccess` already does for the CURRENT list. There is
|
|
43
|
+
* deliberately no second copy of either the shape-recognition or the
|
|
44
|
+
* field-read check: `access-filter.ts` supplies the RELATED list at each
|
|
45
|
+
* hop and calls back into the same primitives this module already owns.
|
|
46
|
+
*/
|
|
47
|
+
// Prisma's logical combinators for a WHERE clause — never field names.
|
|
48
|
+
export const LOGICAL_OPERATORS = new Set(['AND', 'OR', 'NOT']);
|
|
49
|
+
// Prisma's relation quantifiers. The value nested under one of these is itself a
|
|
50
|
+
// WHERE clause for the RELATED list, and is walked against that list's fields.
|
|
51
|
+
export const RELATION_QUANTIFIERS = new Set(['some', 'every', 'none', 'is', 'isNot']);
|
|
52
|
+
// Always present, never declared in a list's `fields` — the write path
|
|
53
|
+
// (`filterWritableFields`) excludes the same three names from `fieldConfigs`.
|
|
54
|
+
const SYSTEM_FIELDS = new Set(['id', 'createdAt', 'updatedAt']);
|
|
55
|
+
/**
|
|
56
|
+
* Resolve a `where`/`orderBy` key against a list's declared fields.
|
|
57
|
+
*
|
|
58
|
+
* A key is valid when it is:
|
|
59
|
+
* - a system field (`id`, `createdAt`, `updatedAt`) — always present, and never
|
|
60
|
+
* declared in `fields` (the write path excludes them from `fieldConfigs` the
|
|
61
|
+
* same way), or
|
|
62
|
+
* - a field declared directly in the list config, or
|
|
63
|
+
* - the foreign-key scalar a to-one `relationship` field implies (e.g. `authorId`
|
|
64
|
+
* for `author: relationship(...)`) — the config never names this column
|
|
65
|
+
* directly, but Prisma always generates it, and the write path
|
|
66
|
+
* (`filterWritableFields`) grants it the same pass, or
|
|
67
|
+
* - a raw per-part column a multi-column field's `splitColumns` contributes (e.g.
|
|
68
|
+
* storage `image()`/`file()` in Keystone-parity mode) — undeclared by design,
|
|
69
|
+
* mirroring the write path's `splitColumnOwners` allowance (#568/#789).
|
|
70
|
+
*
|
|
71
|
+
* Anything else — most importantly a Prisma-generated back-relation the config
|
|
72
|
+
* never declares — resolves to `undefined` and is rejected by the caller.
|
|
73
|
+
*/
|
|
74
|
+
export function resolveQueryField(key, fields) {
|
|
75
|
+
if (SYSTEM_FIELDS.has(key)) {
|
|
76
|
+
return { fieldConfig: undefined, isRelationship: false };
|
|
77
|
+
}
|
|
78
|
+
const fieldConfig = fields[key];
|
|
79
|
+
if (fieldConfig) {
|
|
80
|
+
return { fieldConfig, isRelationship: fieldConfig.type === 'relationship' };
|
|
81
|
+
}
|
|
82
|
+
if (key.endsWith('Id')) {
|
|
83
|
+
const baseField = fields[key.slice(0, -2)];
|
|
84
|
+
if (baseField && baseField.type === 'relationship' && !baseField.many) {
|
|
85
|
+
return { fieldConfig: baseField, isRelationship: false };
|
|
86
|
+
}
|
|
87
|
+
}
|
|
88
|
+
for (const [ownerName, owner] of Object.entries(fields)) {
|
|
89
|
+
if (owner && typeof owner.getColumnNames === 'function') {
|
|
90
|
+
const columns = owner.getColumnNames(ownerName);
|
|
91
|
+
if (columns.includes(key)) {
|
|
92
|
+
return { fieldConfig: owner, isRelationship: false };
|
|
93
|
+
}
|
|
94
|
+
}
|
|
95
|
+
}
|
|
96
|
+
return undefined;
|
|
97
|
+
}
|
|
98
|
+
function rejectUndeclaredKey(listName, key, kind) {
|
|
99
|
+
throw new ValidationError([
|
|
100
|
+
`Cannot query "${listName}" — "${key}" is not a field of this list. ` +
|
|
101
|
+
`Undeclared ${kind} keys are rejected (use sudo to bypass).`,
|
|
102
|
+
]);
|
|
103
|
+
}
|
|
104
|
+
function walkWhere(where,
|
|
105
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any -- ListConfig must accept any TypeInfo
|
|
106
|
+
listConfig, listName, config, isSudo) {
|
|
107
|
+
if (where === null || typeof where !== 'object')
|
|
108
|
+
return;
|
|
109
|
+
if (Array.isArray(where)) {
|
|
110
|
+
for (const entry of where)
|
|
111
|
+
walkWhere(entry, listConfig, listName, config, isSudo);
|
|
112
|
+
return;
|
|
113
|
+
}
|
|
114
|
+
for (const [key, value] of Object.entries(where)) {
|
|
115
|
+
if (LOGICAL_OPERATORS.has(key)) {
|
|
116
|
+
walkWhere(value, listConfig, listName, config, isSudo);
|
|
117
|
+
continue;
|
|
118
|
+
}
|
|
119
|
+
const resolved = resolveQueryField(key, listConfig.fields);
|
|
120
|
+
if (!resolved) {
|
|
121
|
+
if (isSudo)
|
|
122
|
+
continue;
|
|
123
|
+
rejectUndeclaredKey(listName, key, 'where');
|
|
124
|
+
}
|
|
125
|
+
// Scalar field filters use Prisma's own operator vocabulary (`equals`,
|
|
126
|
+
// `contains`, `in`, …) and never nest another field name — trusted as-is,
|
|
127
|
+
// no further walk needed. Only a relationship field's filter nests a
|
|
128
|
+
// WHERE clause for another list.
|
|
129
|
+
if (resolved.isRelationship &&
|
|
130
|
+
value !== null &&
|
|
131
|
+
typeof value === 'object' &&
|
|
132
|
+
!Array.isArray(value)) {
|
|
133
|
+
const related = getRelatedListConfig(resolved.fieldConfig.ref, config);
|
|
134
|
+
if (!related)
|
|
135
|
+
continue;
|
|
136
|
+
const relationEntries = Object.entries(value);
|
|
137
|
+
const hasQuantifier = relationEntries.some(([k]) => RELATION_QUANTIFIERS.has(k));
|
|
138
|
+
if (hasQuantifier) {
|
|
139
|
+
// Wrapped form: `{ author: { is: {...} } }` / `{ posts: { some: {...} } }`.
|
|
140
|
+
// Only the quantifier's own value is a nested WHERE clause for the
|
|
141
|
+
// related list.
|
|
142
|
+
for (const [quantifier, quantifierValue] of relationEntries) {
|
|
143
|
+
if (RELATION_QUANTIFIERS.has(quantifier)) {
|
|
144
|
+
walkWhere(quantifierValue, related.listConfig, related.listName, config, isSudo);
|
|
145
|
+
}
|
|
146
|
+
}
|
|
147
|
+
}
|
|
148
|
+
else {
|
|
149
|
+
// Direct-nesting form: Prisma's documented default for a to-one
|
|
150
|
+
// relation filter nests the related list's own fields with no `is`
|
|
151
|
+
// wrapper at all (`{ author: { email: { contains: '...' } } }`). The
|
|
152
|
+
// whole value object IS the nested WHERE clause here — walk it
|
|
153
|
+
// directly, or an undeclared key reached exactly this way (one hop
|
|
154
|
+
// through a to-one relation) would pass through unchecked.
|
|
155
|
+
walkWhere(value, related.listConfig, related.listName, config, isSudo);
|
|
156
|
+
}
|
|
157
|
+
}
|
|
158
|
+
}
|
|
159
|
+
}
|
|
160
|
+
function walkOrderBy(orderBy,
|
|
161
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any -- ListConfig must accept any TypeInfo
|
|
162
|
+
listConfig, listName, config, isSudo) {
|
|
163
|
+
if (orderBy === null || typeof orderBy !== 'object')
|
|
164
|
+
return;
|
|
165
|
+
if (Array.isArray(orderBy)) {
|
|
166
|
+
for (const entry of orderBy)
|
|
167
|
+
walkOrderBy(entry, listConfig, listName, config, isSudo);
|
|
168
|
+
return;
|
|
169
|
+
}
|
|
170
|
+
for (const [key, value] of Object.entries(orderBy)) {
|
|
171
|
+
const resolved = resolveQueryField(key, listConfig.fields);
|
|
172
|
+
if (!resolved) {
|
|
173
|
+
if (isSudo)
|
|
174
|
+
continue;
|
|
175
|
+
rejectUndeclaredKey(listName, key, 'orderBy');
|
|
176
|
+
}
|
|
177
|
+
if (resolved.isRelationship && value !== null && typeof value === 'object') {
|
|
178
|
+
// `{ relation: { _count: 'asc' } }` orders by an aggregate — no nested
|
|
179
|
+
// field name to resolve. `{ relation: { name: 'asc' } }` orders by a
|
|
180
|
+
// field on a to-one related list — walk it against that list's fields.
|
|
181
|
+
if ('_count' in value)
|
|
182
|
+
continue;
|
|
183
|
+
const related = getRelatedListConfig(resolved.fieldConfig.ref, config);
|
|
184
|
+
if (related)
|
|
185
|
+
walkOrderBy(value, related.listConfig, related.listName, config, isSudo);
|
|
186
|
+
}
|
|
187
|
+
}
|
|
188
|
+
}
|
|
189
|
+
/**
|
|
190
|
+
* Validate a caller-supplied `where`/`orderBy` against the list config,
|
|
191
|
+
* recursing into logical operators (`AND`/`OR`/`NOT`) and relation filters.
|
|
192
|
+
* Throws a `ValidationError` naming the list and the offending key on the
|
|
193
|
+
* first undeclared key found. `isSudo` bypasses the check entirely, matching
|
|
194
|
+
* the write path's `sudo` escape hatch.
|
|
195
|
+
*/
|
|
196
|
+
export function validateQueryKeys(args) {
|
|
197
|
+
const { where, orderBy, listConfig, listName, config, isSudo } = args;
|
|
198
|
+
if (where !== undefined)
|
|
199
|
+
walkWhere(where, listConfig, listName, config, isSudo);
|
|
200
|
+
if (orderBy !== undefined)
|
|
201
|
+
walkOrderBy(orderBy, listConfig, listName, config, isSudo);
|
|
202
|
+
}
|
|
203
|
+
/**
|
|
204
|
+
* #915 — the predicate-time counterpart to `checkFieldAccess`'s post-query
|
|
205
|
+
* check: reject a `where`/`orderBy` key that names a field the session cannot
|
|
206
|
+
* read, BEFORE the query runs. See this module's top doc comment for how this
|
|
207
|
+
* relates to `validateQueryKeys` (#912) and `isFieldReadableForPredicate`'s
|
|
208
|
+
* doc (in `field-access.ts`) for how a row-dependent `read` rule is handled.
|
|
209
|
+
*
|
|
210
|
+
* A key `resolveQueryField` cannot resolve is skipped here — #912 has already
|
|
211
|
+
* rejected it (or, under `sudo`, deliberately let it through) by the time
|
|
212
|
+
* this runs. A system field (`id`/`createdAt`/`updatedAt`) resolves with no
|
|
213
|
+
* `fieldConfig` and carries no field-level access control, so it is always
|
|
214
|
+
* readable and skipped too.
|
|
215
|
+
*/
|
|
216
|
+
async function checkKeyReadableOrThrow(key,
|
|
217
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any -- ListConfig must accept any TypeInfo
|
|
218
|
+
listConfig, listName, args, kind) {
|
|
219
|
+
const resolved = resolveQueryField(key, listConfig.fields);
|
|
220
|
+
if (!resolved || resolved.fieldConfig === undefined)
|
|
221
|
+
return;
|
|
222
|
+
const readable = await isFieldReadableForPredicate(resolved.fieldConfig.access, args);
|
|
223
|
+
if (!readable) {
|
|
224
|
+
throw new ValidationError([
|
|
225
|
+
`Cannot query "${listName}" — "${key}" is denied by field-level read access. ` +
|
|
226
|
+
`A field the session cannot read cannot be named in a ${kind} (use sudo to bypass).`,
|
|
227
|
+
]);
|
|
228
|
+
}
|
|
229
|
+
}
|
|
230
|
+
/**
|
|
231
|
+
* Check field-level `read` access for every key at ONE level of a `where`
|
|
232
|
+
* clause, recursing only into logical operators (`AND`/`OR`/`NOT`) — never
|
|
233
|
+
* into a relationship field's own nested value. Exported for `access-filter.ts`'s
|
|
234
|
+
* #916 reuse (see module doc comment above) — called once per hop against the
|
|
235
|
+
* RELATED list's own config.
|
|
236
|
+
*/
|
|
237
|
+
export async function walkWhereReadAccess(where,
|
|
238
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any -- ListConfig must accept any TypeInfo
|
|
239
|
+
listConfig, listName, args) {
|
|
240
|
+
if (where === null || typeof where !== 'object')
|
|
241
|
+
return;
|
|
242
|
+
if (Array.isArray(where)) {
|
|
243
|
+
for (const entry of where)
|
|
244
|
+
await walkWhereReadAccess(entry, listConfig, listName, args);
|
|
245
|
+
return;
|
|
246
|
+
}
|
|
247
|
+
for (const [key, value] of Object.entries(where)) {
|
|
248
|
+
if (LOGICAL_OPERATORS.has(key)) {
|
|
249
|
+
await walkWhereReadAccess(value, listConfig, listName, args);
|
|
250
|
+
continue;
|
|
251
|
+
}
|
|
252
|
+
// Deliberately does not recurse into a relationship field's own nested
|
|
253
|
+
// value: it checks whether THIS list's relationship field may be named
|
|
254
|
+
// (its own `read` access) — a field on the RELATED list nested inside it
|
|
255
|
+
// is checked by the CALLER re-invoking this function against the related
|
|
256
|
+
// list's config (see #916 in the module doc comment above).
|
|
257
|
+
await checkKeyReadableOrThrow(key, listConfig, listName, args, 'where');
|
|
258
|
+
}
|
|
259
|
+
}
|
|
260
|
+
async function walkOrderByReadAccess(orderBy,
|
|
261
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any -- ListConfig must accept any TypeInfo
|
|
262
|
+
listConfig, listName, args) {
|
|
263
|
+
if (orderBy === null || typeof orderBy !== 'object')
|
|
264
|
+
return;
|
|
265
|
+
if (Array.isArray(orderBy)) {
|
|
266
|
+
for (const entry of orderBy)
|
|
267
|
+
await walkOrderByReadAccess(entry, listConfig, listName, args);
|
|
268
|
+
return;
|
|
269
|
+
}
|
|
270
|
+
for (const key of Object.keys(orderBy)) {
|
|
271
|
+
await checkKeyReadableOrThrow(key, listConfig, listName, args, 'orderBy');
|
|
272
|
+
}
|
|
273
|
+
}
|
|
274
|
+
/**
|
|
275
|
+
* Validate a caller-supplied `where`/`orderBy` against field-level `read`
|
|
276
|
+
* access, recursing into logical operators (`AND`/`OR`/`NOT`) the same way
|
|
277
|
+
* `validateQueryKeys` does. Throws a `ValidationError` naming the list and
|
|
278
|
+
* the offending key on the first read-denied field found. `isSudo` bypasses
|
|
279
|
+
* the check entirely, matching `validateQueryKeys` and the write path's
|
|
280
|
+
* `sudo` escape hatch.
|
|
281
|
+
*/
|
|
282
|
+
export async function validateQueryFieldReadAccess(args) {
|
|
283
|
+
const { where, orderBy, listConfig, listName, session, context, isSudo } = args;
|
|
284
|
+
if (isSudo)
|
|
285
|
+
return;
|
|
286
|
+
const evalArgs = { session, context };
|
|
287
|
+
if (where !== undefined)
|
|
288
|
+
await walkWhereReadAccess(where, listConfig, listName, evalArgs);
|
|
289
|
+
if (orderBy !== undefined)
|
|
290
|
+
await walkOrderByReadAccess(orderBy, listConfig, listName, evalArgs);
|
|
291
|
+
}
|
|
292
|
+
//# sourceMappingURL=query-validation.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"query-validation.js","sourceRoot":"","sources":["../../src/access/query-validation.ts"],"names":[],"mappings":"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"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"relationship-count.d.ts","sourceRoot":"","sources":["../../src/access/relationship-count.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,OAAO,EAAE,aAAa,EAAgB,MAAM,YAAY,CAAA;AACtE,OAAO,KAAK,EAAE,cAAc,EAAE,UAAU,EAAE,WAAW,EAAE,MAAM,oBAAoB,CAAA;AAMjF;;;;;;;;;;;;;;;;;;GAkBG;AAEH,KAAK,SAAS,GAAG;IACf,OAAO,EAAE,OAAO,GAAG,IAAI,CAAA;IACvB,OAAO,EAAE,aAAa,CAAA;CACvB,CAAA;AAED;;;GAGG;AACH,wBAAgB,yBAAyB,CAAC,KAAK,EAAE,WAAW,GAAG,SAAS,GAAG,OAAO,CASjF;AAkCD;;;;;;GAMG;AACH,wBAAsB,4BAA4B,CAEhD,UAAU,EAAE,UAAU,CAAC,GAAG,CAAC,EAC3B,IAAI,EAAE,SAAS,EACf,MAAM,EAAE,cAAc,GACrB,OAAO,CAAC,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,SAAS,CAAC,CAQ9C;
|
|
1
|
+
{"version":3,"file":"relationship-count.d.ts","sourceRoot":"","sources":["../../src/access/relationship-count.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,OAAO,EAAE,aAAa,EAAgB,MAAM,YAAY,CAAA;AACtE,OAAO,KAAK,EAAE,cAAc,EAAE,UAAU,EAAE,WAAW,EAAE,MAAM,oBAAoB,CAAA;AAMjF;;;;;;;;;;;;;;;;;;GAkBG;AAEH,KAAK,SAAS,GAAG;IACf,OAAO,EAAE,OAAO,GAAG,IAAI,CAAA;IACvB,OAAO,EAAE,aAAa,CAAA;CACvB,CAAA;AAED;;;GAGG;AACH,wBAAgB,yBAAyB,CAAC,KAAK,EAAE,WAAW,GAAG,SAAS,GAAG,OAAO,CASjF;AAkCD;;;;;;GAMG;AACH,wBAAsB,4BAA4B,CAEhD,UAAU,EAAE,UAAU,CAAC,GAAG,CAAC,EAC3B,IAAI,EAAE,SAAS,EACf,MAAM,EAAE,cAAc,GACrB,OAAO,CAAC,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,SAAS,CAAC,CAQ9C;AAuHD;;;;;;;;;GASG;AACH,wBAAgB,mBAAmB,CACjC,QAAQ,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EACjC,QAAQ,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAChC,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAIzB;AAED;;;;;;;GAOG;AACH,wBAAsB,+BAA+B,CACnD,KAAK,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,SAAS,EAE1C,UAAU,EAAE,UAAU,CAAC,GAAG,CAAC,EAC3B,OAAO,EAAE,MAAM,EACf,IAAI,EAAE,SAAS,EACf,MAAM,EAAE,cAAc,GACrB,OAAO,CAAC,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,SAAS,CAAC,CAkD9C"}
|
|
@@ -55,7 +55,6 @@ listConfig, args, config) {
|
|
|
55
55
|
}
|
|
56
56
|
return Object.keys(select).length > 0 ? select : undefined;
|
|
57
57
|
}
|
|
58
|
-
/** Read a to-many relationship's count off a fetched row's `_count` payload. */
|
|
59
58
|
function readRelationshipCount(row, fieldName) {
|
|
60
59
|
const counts = row._count;
|
|
61
60
|
if (counts && typeof counts === 'object') {
|
|
@@ -65,7 +64,6 @@ function readRelationshipCount(row, fieldName) {
|
|
|
65
64
|
}
|
|
66
65
|
return 0;
|
|
67
66
|
}
|
|
68
|
-
/** Whether a count satisfies a Filter operator/value comparison. */
|
|
69
67
|
function matchesCount(count, operator, value) {
|
|
70
68
|
switch (operator) {
|
|
71
69
|
case 'eq':
|
|
@@ -89,7 +87,6 @@ function asCountDelegate(value) {
|
|
|
89
87
|
}
|
|
90
88
|
return null;
|
|
91
89
|
}
|
|
92
|
-
/** Extract the `RelationshipCountFilterMarker` from a condition value, if present. */
|
|
93
90
|
function readCountMarker(value) {
|
|
94
91
|
if (!value || typeof value !== 'object')
|
|
95
92
|
return null;
|
|
@@ -212,13 +209,8 @@ listConfig, listKey, args, config) {
|
|
|
212
209
|
continue;
|
|
213
210
|
}
|
|
214
211
|
const resolved = await resolveOneCountFilter(listConfig, listKey, found.field, found.marker, args, config);
|
|
215
|
-
// Preserve any sibling conditions
|
|
216
|
-
//
|
|
217
|
-
// currently guarantees each AND-member (and the no-AND single object) carries
|
|
218
|
-
// exactly one field condition, so `siblings` is empty today and this equals the
|
|
219
|
-
// previous wholesale replacement — but if a future engine change ever merged
|
|
220
|
-
// multiple conditions into one member, spreading keeps the marker's siblings
|
|
221
|
-
// from being silently dropped.
|
|
212
|
+
// Preserve any sibling conditions rather than replacing the member
|
|
213
|
+
// wholesale — see `mergeResolvedMember` above for why.
|
|
222
214
|
const siblings = { ...member };
|
|
223
215
|
delete siblings[found.field];
|
|
224
216
|
resolvedMembers.push(mergeResolvedMember(siblings, resolved));
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"relationship-count.js","sourceRoot":"","sources":["../../src/access/relationship-count.ts"],"names":[],"mappings":"AAGA,OAAO,EAAE,6BAA6B,EAAE,MAAM,oBAAoB,CAAA;AAClE,OAAO,EAAE,WAAW,EAAE,oBAAoB,EAAE,MAAM,aAAa,CAAA;AAC/D,OAAO,EAAE,QAAQ,EAAE,MAAM,sBAAsB,CAAA;AA2B/C;;;GAGG;AACH,MAAM,UAAU,yBAAyB,CAAC,KAA8B;IACtE,OAAO,CACL,KAAK,EAAE,IAAI,KAAK,cAAc;QAC9B,MAAM,IAAI,KAAK;QACf,KAAK,CAAC,IAAI,KAAK,IAAI;QACnB,KAAK,IAAI,KAAK;QACd,OAAO,KAAK,CAAC,GAAG,KAAK,QAAQ;QAC7B,KAAK,CAAC,GAAG,CAAC,MAAM,GAAG,CAAC,CACrB,CAAA;AACH,CAAC;AAQD;;;;GAIG;AACH,KAAK,UAAU,4BAA4B,CACzC,KAA8B,EAC9B,IAAe,EACf,MAAsB;IAEtB,IAAI,CAAC,KAAK,IAAI,CAAC,yBAAyB,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC,KAAK,IAAI,KAAK,CAAC,EAAE,CAAC;QACrE,OAAO,IAAI,CAAA;IACb,CAAC;IACD,MAAM,GAAG,GAAG,KAAK,CAAC,GAAG,CAAA;IACrB,IAAI,OAAO,GAAG,KAAK,QAAQ;QAAE,OAAO,IAAI,CAAA;IACxC,MAAM,OAAO,GAAG,oBAAoB,CAAC,GAAG,EAAE,MAAM,CAAC,CAAA;IACjD,IAAI,CAAC,OAAO;QAAE,OAAO,IAAI,CAAA;IAEzB,MAAM,WAAW,GAAG,OAAO,CAAC,UAAU,CAAC,MAAM,EAAE,SAAS,EAAE,KAAK,CAAA;IAC/D,MAAM,MAAM,GAAG,MAAM,WAAW,CAAC,WAAW,EAAE,EAAE,OAAO,EAAE,IAAI,CAAC,OAAO,EAAE,OAAO,EAAE,IAAI,CAAC,OAAO,EAAE,CAAC,CAAA;IAE/F,IAAI,MAAM,KAAK,KAAK;QAAE,OAAO,EAAE,IAAI,EAAE,QAAQ,EAAE,CAAA;IAC/C,IAAI,OAAO,MAAM,KAAK,QAAQ;QAAE,OAAO,EAAE,IAAI,EAAE,QAAQ,EAAE,KAAK,EAAE,MAAM,EAAE,CAAA;IACxE,OAAO,EAAE,IAAI,EAAE,KAAK,EAAE,CAAA;AACxB,CAAC;AAED;;;;;;GAMG;AACH,MAAM,CAAC,KAAK,UAAU,4BAA4B;AAChD,qGAAqG;AACrG,UAA2B,EAC3B,IAAe,EACf,MAAsB;IAEtB,MAAM,MAAM,GAA4B,EAAE,CAAA;IAC1C,KAAK,MAAM,CAAC,SAAS,EAAE,KAAK,CAAC,IAAI,MAAM,CAAC,OAAO,CAAC,UAAU,CAAC,MAAM,CAAC,EAAE,CAAC;QACnE,MAAM,KAAK,GAAG,MAAM,4BAA4B,CAAC,KAAK,EAAE,IAAI,EAAE,MAAM,CAAC,CAAA;QACrE,IAAI,KAAK,KAAK,IAAI,IAAI,KAAK,CAAC,IAAI,KAAK,QAAQ;YAAE,SAAQ;QACvD,MAAM,CAAC,SAAS,CAAC,GAAG,KAAK,CAAC,IAAI,KAAK,QAAQ,CAAC,CAAC,CAAC,EAAE,KAAK,EAAE,KAAK,CAAC,KAAK,EAAE,CAAC,CAAC,CAAC,IAAI,CAAA;IAC7E,CAAC;IACD,OAAO,MAAM,CAAC,IAAI,CAAC,MAAM,CAAC,CAAC,MAAM,GAAG,CAAC,CAAC,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,SAAS,CAAA;AAC5D,CAAC;AAED,
|
|
1
|
+
{"version":3,"file":"relationship-count.js","sourceRoot":"","sources":["../../src/access/relationship-count.ts"],"names":[],"mappings":"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"}
|
|
@@ -2,25 +2,29 @@ import type { Session, AccessContext } from './types.js';
|
|
|
2
2
|
import type { OpenSaasConfig, ListConfig, FieldConfig } from '../config/types.js';
|
|
3
3
|
/**
|
|
4
4
|
* Access-scoped to-one relationship label filters for the admin list view
|
|
5
|
-
* (issue #749).
|
|
5
|
+
* (issue #749) — NOW A PASS-THROUGH.
|
|
6
6
|
*
|
|
7
7
|
* A to-one relationship's Filter spec (`author:Ada` → `{ author: { is: { name:
|
|
8
|
-
* { contains: 'Ada' } } } }`)
|
|
9
|
-
*
|
|
10
|
-
*
|
|
11
|
-
*
|
|
12
|
-
*
|
|
13
|
-
*
|
|
14
|
-
*
|
|
15
|
-
*
|
|
16
|
-
* against a `User` list it cannot query).
|
|
8
|
+
* { contains: 'Ada' } } } }`) produces exactly the `{ is: {...} }` shape a
|
|
9
|
+
* relation filter uses. Before #916, the engine did not scope relation
|
|
10
|
+
* filters in `where` at all, so this module was the only place the related
|
|
11
|
+
* list's `query` access was folded into that nested `is` clause (mirroring
|
|
12
|
+
* `relationship-count.ts`'s `_count` folding) — otherwise a session could
|
|
13
|
+
* distinguish parent rows by a related field it could not itself read (e.g.
|
|
14
|
+
* binary-searching `author:A`, `author:Ad`, `author:Ada` against a `User`
|
|
15
|
+
* list it cannot query).
|
|
17
16
|
*
|
|
18
|
-
*
|
|
19
|
-
*
|
|
20
|
-
*
|
|
21
|
-
*
|
|
22
|
-
*
|
|
23
|
-
*
|
|
17
|
+
* #916 closed that gap in the engine itself: `context.db.*.findMany`/`count`
|
|
18
|
+
* now scope every relation filter in `where` — including this exact `is`
|
|
19
|
+
* shape — via `buildAccessScopedWhere` (`access-filter.ts`), applied
|
|
20
|
+
* automatically to whatever `where` this module's caller (`ListView.tsx`)
|
|
21
|
+
* hands to the secured context. Folding the same access filter here as well
|
|
22
|
+
* would be redundant work producing an identical result (the engine ANDs the
|
|
23
|
+
* same filter in a second time), so this module's own fold was removed —
|
|
24
|
+
* `resolveRelationshipLabelFilters` now returns `where` unchanged. The
|
|
25
|
+
* exported functions are kept, unchanged in shape, only because they remain
|
|
26
|
+
* part of `@opensaas/stack-core`'s public surface; removing them outright
|
|
27
|
+
* would be a breaking change this fix does not need to make.
|
|
24
28
|
*/
|
|
25
29
|
type LabelFilterArgs = {
|
|
26
30
|
session: Session | null;
|
|
@@ -33,13 +37,9 @@ type LabelFilterArgs = {
|
|
|
33
37
|
*/
|
|
34
38
|
export declare function isToOneRelationshipField(field: FieldConfig | undefined): boolean;
|
|
35
39
|
/**
|
|
36
|
-
*
|
|
37
|
-
*
|
|
38
|
-
* top-level AND members (the relationship Filter spec does not declare
|
|
39
|
-
* `freeText`, so it never participates in the free-text OR), so this walks
|
|
40
|
-
* only the top level. Returns the `where` unchanged when it contains no such
|
|
41
|
-
* members, so lists without to-one relationship filters pay nothing.
|
|
40
|
+
* No longer folds access into label filters — see the module doc above.
|
|
41
|
+
* Returns `where` unchanged.
|
|
42
42
|
*/
|
|
43
|
-
export declare function resolveRelationshipLabelFilters(where: Record<string, unknown> | undefined,
|
|
43
|
+
export declare function resolveRelationshipLabelFilters(where: Record<string, unknown> | undefined, _listConfig: ListConfig<any>, _args: LabelFilterArgs, _config: OpenSaasConfig): Promise<Record<string, unknown> | undefined>;
|
|
44
44
|
export {};
|
|
45
45
|
//# sourceMappingURL=relationship-label-filter.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"relationship-label-filter.d.ts","sourceRoot":"","sources":["../../src/access/relationship-label-filter.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,OAAO,EAAE,aAAa,EAAE,MAAM,YAAY,CAAA;AACxD,OAAO,KAAK,EAAE,cAAc,EAAE,UAAU,EAAE,WAAW,EAAE,MAAM,oBAAoB,CAAA;
|
|
1
|
+
{"version":3,"file":"relationship-label-filter.d.ts","sourceRoot":"","sources":["../../src/access/relationship-label-filter.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,OAAO,EAAE,aAAa,EAAE,MAAM,YAAY,CAAA;AACxD,OAAO,KAAK,EAAE,cAAc,EAAE,UAAU,EAAE,WAAW,EAAE,MAAM,oBAAoB,CAAA;AAEjF;;;;;;;;;;;;;;;;;;;;;;;;;GAyBG;AAEH,KAAK,eAAe,GAAG;IACrB,OAAO,EAAE,OAAO,GAAG,IAAI,CAAA;IACvB,OAAO,EAAE,aAAa,CAAA;CACvB,CAAA;AAED;;;;GAIG;AACH,wBAAgB,wBAAwB,CAAC,KAAK,EAAE,WAAW,GAAG,SAAS,GAAG,OAAO,CAQhF;AAED;;;GAGG;AACH,wBAAsB,+BAA+B,CACnD,KAAK,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,SAAS,EAE1C,WAAW,EAAE,UAAU,CAAC,GAAG,CAAC,EAC5B,KAAK,EAAE,eAAe,EACtB,OAAO,EAAE,cAAc,GACtB,OAAO,CAAC,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,SAAS,CAAC,CAE9C"}
|