@opensaas/stack-core 0.20.1 → 0.22.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 +334 -0
- package/CLAUDE.md +29 -11
- package/dist/access/access-filter.d.ts +29 -0
- package/dist/access/access-filter.d.ts.map +1 -0
- package/dist/access/access-filter.js +68 -0
- package/dist/access/access-filter.js.map +1 -0
- package/dist/access/engine.d.ts +15 -48
- package/dist/access/engine.d.ts.map +1 -1
- package/dist/access/engine.js +14 -280
- package/dist/access/engine.js.map +1 -1
- package/dist/access/field-access.d.ts +44 -0
- package/dist/access/field-access.d.ts.map +1 -0
- package/dist/access/field-access.js +123 -0
- package/dist/access/field-access.js.map +1 -0
- package/dist/access/field-access.test.d.ts +2 -0
- package/dist/access/field-access.test.d.ts.map +1 -0
- package/dist/access/{engine.test.js → field-access.test.js} +2 -2
- package/dist/access/field-access.test.js.map +1 -0
- package/dist/access/field-visibility.d.ts +13 -0
- package/dist/access/field-visibility.d.ts.map +1 -0
- package/dist/access/field-visibility.js +178 -0
- package/dist/access/field-visibility.js.map +1 -0
- package/dist/access/index.d.ts +4 -1
- package/dist/access/index.d.ts.map +1 -1
- package/dist/access/index.js +8 -1
- package/dist/access/index.js.map +1 -1
- package/dist/access/multi-column-read-write.test.d.ts +2 -0
- package/dist/access/multi-column-read-write.test.d.ts.map +1 -0
- package/dist/access/multi-column-read-write.test.js +149 -0
- package/dist/access/multi-column-read-write.test.js.map +1 -0
- package/dist/config/index.d.ts +1 -1
- package/dist/config/index.d.ts.map +1 -1
- package/dist/config/types.d.ts +334 -5
- package/dist/config/types.d.ts.map +1 -1
- package/dist/context/hook-pipeline.d.ts +49 -0
- package/dist/context/hook-pipeline.d.ts.map +1 -0
- package/dist/context/hook-pipeline.js +75 -0
- package/dist/context/hook-pipeline.js.map +1 -0
- package/dist/context/index.d.ts.map +1 -1
- package/dist/context/index.js +30 -462
- package/dist/context/index.js.map +1 -1
- package/dist/context/nested-operations.d.ts.map +1 -1
- package/dist/context/nested-operations.js +72 -68
- package/dist/context/nested-operations.js.map +1 -1
- package/dist/context/write-pipeline.d.ts +158 -0
- package/dist/context/write-pipeline.d.ts.map +1 -0
- package/dist/context/write-pipeline.js +306 -0
- package/dist/context/write-pipeline.js.map +1 -0
- package/dist/extend.d.ts +3 -0
- package/dist/extend.d.ts.map +1 -0
- package/dist/extend.js +10 -0
- package/dist/extend.js.map +1 -0
- package/dist/fields/format-prisma-default.d.ts +35 -0
- package/dist/fields/format-prisma-default.d.ts.map +1 -0
- package/dist/fields/format-prisma-default.js +52 -0
- package/dist/fields/format-prisma-default.js.map +1 -0
- package/dist/fields/format-prisma-default.test.d.ts +2 -0
- package/dist/fields/format-prisma-default.test.d.ts.map +1 -0
- package/dist/fields/format-prisma-default.test.js +54 -0
- package/dist/fields/format-prisma-default.test.js.map +1 -0
- package/dist/fields/index.d.ts +1 -0
- package/dist/fields/index.d.ts.map +1 -1
- package/dist/fields/index.js +267 -18
- package/dist/fields/index.js.map +1 -1
- package/dist/fields/select.test.js +85 -0
- package/dist/fields/select.test.js.map +1 -1
- package/dist/fields/text-keystone-compat.test.d.ts +2 -0
- package/dist/fields/text-keystone-compat.test.d.ts.map +1 -0
- package/dist/fields/text-keystone-compat.test.js +93 -0
- package/dist/fields/text-keystone-compat.test.js.map +1 -0
- package/dist/hooks/index.d.ts +20 -0
- package/dist/hooks/index.d.ts.map +1 -1
- package/dist/hooks/index.js +246 -0
- package/dist/hooks/index.js.map +1 -1
- package/dist/index.d.ts +6 -8
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +25 -9
- package/dist/index.js.map +1 -1
- package/dist/index.test.d.ts +2 -0
- package/dist/index.test.d.ts.map +1 -0
- package/dist/index.test.js +33 -0
- package/dist/index.test.js.map +1 -0
- package/dist/internal.d.ts +8 -0
- package/dist/internal.d.ts.map +1 -0
- package/dist/internal.js +16 -0
- package/dist/internal.js.map +1 -0
- package/dist/mcp/handler.js +0 -1
- package/dist/mcp/handler.js.map +1 -1
- package/dist/validation/field-config.d.ts +55 -0
- package/dist/validation/field-config.d.ts.map +1 -0
- package/dist/validation/field-config.js +100 -0
- package/dist/validation/field-config.js.map +1 -0
- package/dist/validation/field-config.test.d.ts +2 -0
- package/dist/validation/field-config.test.d.ts.map +1 -0
- package/dist/validation/field-config.test.js +159 -0
- package/dist/validation/field-config.test.js.map +1 -0
- package/package.json +11 -3
- package/src/access/access-filter.ts +97 -0
- package/src/access/engine.ts +13 -396
- package/src/access/{engine.test.ts → field-access.test.ts} +1 -1
- package/src/access/field-access.ts +159 -0
- package/src/access/field-visibility.ts +269 -0
- package/src/access/index.ts +7 -4
- package/src/access/multi-column-read-write.test.ts +255 -0
- package/src/config/index.ts +3 -0
- package/src/config/types.ts +342 -4
- package/src/context/hook-pipeline.ts +160 -0
- package/src/context/index.ts +29 -667
- package/src/context/nested-operations.ts +142 -111
- package/src/context/write-pipeline.ts +543 -0
- package/src/extend.ts +19 -0
- package/src/fields/format-prisma-default.test.ts +64 -0
- package/src/fields/format-prisma-default.ts +67 -0
- package/src/fields/index.ts +375 -20
- package/src/fields/select.test.ts +99 -0
- package/src/fields/text-keystone-compat.test.ts +126 -0
- package/src/hooks/index.ts +270 -0
- package/src/index.test.ts +50 -0
- package/src/index.ts +35 -82
- package/src/internal.ts +49 -0
- package/src/mcp/handler.ts +0 -2
- package/src/validation/field-config.test.ts +199 -0
- package/src/validation/field-config.ts +145 -0
- package/tests/access-relationships.test.ts +4 -4
- package/tests/access.test.ts +1 -1
- package/tests/field-hooks.test.ts +410 -0
- package/tests/field-types.test.ts +1 -1
- package/tests/hook-pipeline.test.ts +233 -0
- package/tests/nested-operation-registry.test.ts +206 -0
- package/tests/write-pipeline.test.ts +588 -0
- package/tsconfig.tsbuildinfo +1 -1
- package/vitest.config.ts +43 -1
- package/dist/access/engine.test.d.ts +0 -2
- package/dist/access/engine.test.d.ts.map +0 -1
- package/dist/access/engine.test.js.map +0 -1
|
@@ -0,0 +1,178 @@
|
|
|
1
|
+
import { getRelatedListConfig } from './engine.js';
|
|
2
|
+
import { checkFieldAccess } from './field-access.js';
|
|
3
|
+
/**
|
|
4
|
+
* The core Field Visibility step for a single field: check read access and, if
|
|
5
|
+
* granted, produce the output value by running any `resolveOutput` hook.
|
|
6
|
+
*
|
|
7
|
+
* This is the single place the "check read access → skip if denied →
|
|
8
|
+
* resolveOutput" sequence lives. Both the regular-field branch and the
|
|
9
|
+
* virtual-field branch of `filterReadableFields` call it, so the sequence is
|
|
10
|
+
* never duplicated. Returns `{ readable: false }` when the field must be omitted
|
|
11
|
+
* from the result.
|
|
12
|
+
*
|
|
13
|
+
* `accessItem` is the row used to evaluate field access; `hookItem` is the
|
|
14
|
+
* object passed to the hook as `item` (these differ for virtual fields, which
|
|
15
|
+
* see the already-filtered output so they can read sibling fields).
|
|
16
|
+
*/
|
|
17
|
+
async function resolveReadableFieldValue(params) {
|
|
18
|
+
const { fieldConfig, fieldName, value, accessItem, hookItem, listKey, args } = params;
|
|
19
|
+
// Check field access (checkFieldAccess already handles sudo mode)
|
|
20
|
+
const canRead = await checkFieldAccess(fieldConfig?.access, 'read', {
|
|
21
|
+
...args,
|
|
22
|
+
item: accessItem,
|
|
23
|
+
});
|
|
24
|
+
if (!canRead) {
|
|
25
|
+
return { readable: false };
|
|
26
|
+
}
|
|
27
|
+
// Apply resolveOutput hook if present
|
|
28
|
+
if (fieldConfig?.hooks?.resolveOutput && listKey) {
|
|
29
|
+
// Cast to runtime type for generic execution
|
|
30
|
+
// At runtime, the hook will receive the correct value type for the field
|
|
31
|
+
const hook = fieldConfig.hooks.resolveOutput;
|
|
32
|
+
// Increment depth counter to prevent infinite loops from hooks making DB queries
|
|
33
|
+
// that include relationships back to the same entity
|
|
34
|
+
args.context._resolveOutputCounter.depth++;
|
|
35
|
+
try {
|
|
36
|
+
// Use Promise.resolve() to handle both sync and async hooks
|
|
37
|
+
const resolved = await Promise.resolve(hook({
|
|
38
|
+
value,
|
|
39
|
+
operation: 'query',
|
|
40
|
+
fieldName,
|
|
41
|
+
listKey,
|
|
42
|
+
item: hookItem,
|
|
43
|
+
context: args.context,
|
|
44
|
+
}));
|
|
45
|
+
return { readable: true, value: resolved };
|
|
46
|
+
}
|
|
47
|
+
finally {
|
|
48
|
+
args.context._resolveOutputCounter.depth--;
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
return { readable: true, value };
|
|
52
|
+
}
|
|
53
|
+
/**
|
|
54
|
+
* Filter fields from an object based on read access
|
|
55
|
+
* Recursively applies access control to nested relationships
|
|
56
|
+
*/
|
|
57
|
+
export async function filterReadableFields(item, fieldConfigs, args, config, depth = 0, listKey) {
|
|
58
|
+
const filtered = {};
|
|
59
|
+
const MAX_DEPTH = 5; // Prevent infinite recursion
|
|
60
|
+
// Multi-column fields (e.g. storage image()/file() in Keystone-parity mode)
|
|
61
|
+
// back several physical columns rather than one. Before the per-field pass,
|
|
62
|
+
// assemble each such field's logical value from its raw columns and remove the
|
|
63
|
+
// raw columns from the working row, so only the assembled value is exposed
|
|
64
|
+
// (the raw per-part columns never leak). The assembled value then flows
|
|
65
|
+
// through the normal read-access + resolveOutput path under the field's own
|
|
66
|
+
// key. See ADR-0006.
|
|
67
|
+
const workingItem = { ...item };
|
|
68
|
+
for (const [fieldName, fieldConfig] of Object.entries(fieldConfigs)) {
|
|
69
|
+
if (!fieldConfig.assembleColumns || !fieldConfig.getColumnNames)
|
|
70
|
+
continue;
|
|
71
|
+
const columnNames = fieldConfig.getColumnNames(fieldName);
|
|
72
|
+
// Only assemble when the raw columns are present in the row (i.e. they were
|
|
73
|
+
// selected); otherwise leave the field absent from the result.
|
|
74
|
+
const hasAnyColumn = columnNames.some((name) => name in workingItem);
|
|
75
|
+
if (!hasAnyColumn)
|
|
76
|
+
continue;
|
|
77
|
+
const assembled = fieldConfig.assembleColumns(fieldName, workingItem);
|
|
78
|
+
for (const name of columnNames) {
|
|
79
|
+
delete workingItem[name];
|
|
80
|
+
}
|
|
81
|
+
workingItem[fieldName] = assembled;
|
|
82
|
+
}
|
|
83
|
+
// Process existing fields from the database result
|
|
84
|
+
for (const [fieldName, value] of Object.entries(workingItem)) {
|
|
85
|
+
const fieldConfig = fieldConfigs[fieldName];
|
|
86
|
+
// Always include id, createdAt, updatedAt
|
|
87
|
+
if (['id', 'createdAt', 'updatedAt'].includes(fieldName)) {
|
|
88
|
+
filtered[fieldName] = value;
|
|
89
|
+
continue;
|
|
90
|
+
}
|
|
91
|
+
// Handle relationship fields - recursively filter fields within related items
|
|
92
|
+
// Note: Access control filtering is now done at database level via buildIncludeWithAccessControl
|
|
93
|
+
// This only handles field-level access (hiding sensitive fields)
|
|
94
|
+
if (config &&
|
|
95
|
+
fieldConfig?.type === 'relationship' &&
|
|
96
|
+
'ref' in fieldConfig &&
|
|
97
|
+
fieldConfig.ref &&
|
|
98
|
+
value !== null &&
|
|
99
|
+
value !== undefined &&
|
|
100
|
+
depth < MAX_DEPTH) {
|
|
101
|
+
// Gate the relationship on read access before recursing.
|
|
102
|
+
const canRead = await checkFieldAccess(fieldConfig?.access, 'read', {
|
|
103
|
+
...args,
|
|
104
|
+
item: workingItem,
|
|
105
|
+
});
|
|
106
|
+
if (!canRead) {
|
|
107
|
+
continue;
|
|
108
|
+
}
|
|
109
|
+
const relatedConfig = getRelatedListConfig(fieldConfig.ref, config);
|
|
110
|
+
if (relatedConfig) {
|
|
111
|
+
// For many relationships (arrays) - recursively filter fields in each item
|
|
112
|
+
// The recursive call already handles applying resolveOutput hooks
|
|
113
|
+
if (Array.isArray(value)) {
|
|
114
|
+
filtered[fieldName] = await Promise.all(value.map((relatedItem) => filterReadableFields(relatedItem, relatedConfig.listConfig.fields, args, config, depth + 1, relatedConfig.listName)));
|
|
115
|
+
}
|
|
116
|
+
// For single relationships (objects) - recursively filter fields
|
|
117
|
+
// The recursive call already handles applying resolveOutput hooks
|
|
118
|
+
else if (typeof value === 'object') {
|
|
119
|
+
filtered[fieldName] = await filterReadableFields(value, relatedConfig.listConfig.fields, args, config, depth + 1, relatedConfig.listName);
|
|
120
|
+
}
|
|
121
|
+
}
|
|
122
|
+
else {
|
|
123
|
+
// Related config not found, include the value as-is
|
|
124
|
+
filtered[fieldName] = value;
|
|
125
|
+
}
|
|
126
|
+
continue;
|
|
127
|
+
}
|
|
128
|
+
// Non-relationship field (or relationship without an includable value):
|
|
129
|
+
// check read access and apply resolveOutput via the shared helper.
|
|
130
|
+
const result = await resolveReadableFieldValue({
|
|
131
|
+
fieldConfig,
|
|
132
|
+
fieldName,
|
|
133
|
+
value,
|
|
134
|
+
accessItem: workingItem,
|
|
135
|
+
hookItem: workingItem,
|
|
136
|
+
listKey,
|
|
137
|
+
args,
|
|
138
|
+
});
|
|
139
|
+
if (result.readable) {
|
|
140
|
+
filtered[fieldName] = result.value;
|
|
141
|
+
}
|
|
142
|
+
}
|
|
143
|
+
// Process virtual fields - compute values from other fields
|
|
144
|
+
// Virtual fields don't exist in the database result, so we need to compute them separately
|
|
145
|
+
for (const [fieldName, fieldConfig] of Object.entries(fieldConfigs)) {
|
|
146
|
+
// Skip if already processed (from database result)
|
|
147
|
+
if (fieldName in filtered) {
|
|
148
|
+
continue;
|
|
149
|
+
}
|
|
150
|
+
// Only process virtual fields
|
|
151
|
+
if (!fieldConfig.virtual) {
|
|
152
|
+
continue;
|
|
153
|
+
}
|
|
154
|
+
// Virtual fields must have a resolveOutput hook to compute their value;
|
|
155
|
+
// without one there is nothing to add to the result.
|
|
156
|
+
if (!(fieldConfig.hooks?.resolveOutput && listKey)) {
|
|
157
|
+
// Still evaluate read access to preserve any access-fn side effects.
|
|
158
|
+
await checkFieldAccess(fieldConfig.access, 'read', { ...args, item: workingItem });
|
|
159
|
+
continue;
|
|
160
|
+
}
|
|
161
|
+
// Check read access and compute the value via the shared helper. Virtual
|
|
162
|
+
// fields see the already-filtered item so they can read sibling fields.
|
|
163
|
+
const result = await resolveReadableFieldValue({
|
|
164
|
+
fieldConfig,
|
|
165
|
+
fieldName,
|
|
166
|
+
value: undefined, // Virtual fields don't have a database value
|
|
167
|
+
accessItem: workingItem,
|
|
168
|
+
hookItem: filtered,
|
|
169
|
+
listKey,
|
|
170
|
+
args,
|
|
171
|
+
});
|
|
172
|
+
if (result.readable) {
|
|
173
|
+
filtered[fieldName] = result.value;
|
|
174
|
+
}
|
|
175
|
+
}
|
|
176
|
+
return filtered;
|
|
177
|
+
}
|
|
178
|
+
//# sourceMappingURL=field-visibility.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"field-visibility.js","sourceRoot":"","sources":["../../src/access/field-visibility.ts"],"names":[],"mappings":"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"}
|
package/dist/access/index.d.ts
CHANGED
|
@@ -1,3 +1,6 @@
|
|
|
1
1
|
export type { AccessControl, FieldAccess, Session, AccessContext, PrismaFilter, AccessControlledDB, PrismaClientLike, StorageUtils, AugmentedFindMany, AugmentedFindUnique, FindManyQueryArgs, } from './types.js';
|
|
2
|
-
export { checkAccess, mergeFilters,
|
|
2
|
+
export { checkAccess, mergeFilters, isBoolean, isPrismaFilter, getRelatedListConfig, } from './engine.js';
|
|
3
|
+
export { checkFieldAccess, filterWritableFields } from './field-access.js';
|
|
4
|
+
export { buildIncludeWithAccessControl } from './access-filter.js';
|
|
5
|
+
export { filterReadableFields } from './field-visibility.js';
|
|
3
6
|
//# sourceMappingURL=index.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/access/index.ts"],"names":[],"mappings":"AAAA,YAAY,EACV,aAAa,EACb,WAAW,EACX,OAAO,EACP,aAAa,EACb,YAAY,EACZ,kBAAkB,EAClB,gBAAgB,EAChB,YAAY,EACZ,iBAAiB,EACjB,mBAAmB,EACnB,iBAAiB,GAClB,MAAM,YAAY,CAAA;
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/access/index.ts"],"names":[],"mappings":"AAAA,YAAY,EACV,aAAa,EACb,WAAW,EACX,OAAO,EACP,aAAa,EACb,YAAY,EACZ,kBAAkB,EAClB,gBAAgB,EAChB,YAAY,EACZ,iBAAiB,EACjB,mBAAmB,EACnB,iBAAiB,GAClB,MAAM,YAAY,CAAA;AAEnB,OAAO,EACL,WAAW,EACX,YAAY,EACZ,SAAS,EACT,cAAc,EACd,oBAAoB,GACrB,MAAM,aAAa,CAAA;AAEpB,OAAO,EAAE,gBAAgB,EAAE,oBAAoB,EAAE,MAAM,mBAAmB,CAAA;AAE1E,OAAO,EAAE,6BAA6B,EAAE,MAAM,oBAAoB,CAAA;AAElE,OAAO,EAAE,oBAAoB,EAAE,MAAM,uBAAuB,CAAA"}
|
package/dist/access/index.js
CHANGED
|
@@ -1,2 +1,9 @@
|
|
|
1
|
-
|
|
1
|
+
// Operation-level access primitives and shared ref-parsing helper.
|
|
2
|
+
export { checkAccess, mergeFilters, isBoolean, isPrismaFilter, getRelatedListConfig, } from './engine.js';
|
|
3
|
+
// Canonical field-level access evaluation (shared by read and write paths).
|
|
4
|
+
export { checkFieldAccess, filterWritableFields } from './field-access.js';
|
|
5
|
+
// Phase 1 — Access Filter (pre-query row/relation scoping).
|
|
6
|
+
export { buildIncludeWithAccessControl } from './access-filter.js';
|
|
7
|
+
// Phase 2 — Field Visibility (post-query field stripping + resolveOutput).
|
|
8
|
+
export { filterReadableFields } from './field-visibility.js';
|
|
2
9
|
//# sourceMappingURL=index.js.map
|
package/dist/access/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/access/index.ts"],"names":[],"mappings":"AAaA,OAAO,EACL,WAAW,EACX,YAAY,EACZ,
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/access/index.ts"],"names":[],"mappings":"AAaA,mEAAmE;AACnE,OAAO,EACL,WAAW,EACX,YAAY,EACZ,SAAS,EACT,cAAc,EACd,oBAAoB,GACrB,MAAM,aAAa,CAAA;AACpB,4EAA4E;AAC5E,OAAO,EAAE,gBAAgB,EAAE,oBAAoB,EAAE,MAAM,mBAAmB,CAAA;AAC1E,4DAA4D;AAC5D,OAAO,EAAE,6BAA6B,EAAE,MAAM,oBAAoB,CAAA;AAClE,2EAA2E;AAC3E,OAAO,EAAE,oBAAoB,EAAE,MAAM,uBAAuB,CAAA"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"multi-column-read-write.test.d.ts","sourceRoot":"","sources":["../../src/access/multi-column-read-write.test.ts"],"names":[],"mappings":""}
|
|
@@ -0,0 +1,149 @@
|
|
|
1
|
+
import { describe, it, expect } from 'vitest';
|
|
2
|
+
import { filterReadableFields } from './field-visibility.js';
|
|
3
|
+
import { executeFieldResolveInputHooks } from '../hooks/index.js';
|
|
4
|
+
/**
|
|
5
|
+
* Generic core wiring for multi-column fields (the contract storage
|
|
6
|
+
* image()/file() use in Keystone-parity mode — see ADR-0006). These tests are
|
|
7
|
+
* field-agnostic: they assert that ANY field implementing
|
|
8
|
+
* getColumnNames/assembleColumns/splitColumns is assembled on read (raw columns
|
|
9
|
+
* stripped) and split on write.
|
|
10
|
+
*/
|
|
11
|
+
// A minimal multi-column field: two physical columns `m_url` and `m_size`
|
|
12
|
+
// assembled into `{ url, size }` and split back. Optionally carries field-level
|
|
13
|
+
// access so we can lock the write-access gate around the split.
|
|
14
|
+
function multiColumnField(access) {
|
|
15
|
+
const COLUMNS = ['m_url', 'm_size'];
|
|
16
|
+
return {
|
|
17
|
+
type: 'multiColumn',
|
|
18
|
+
access,
|
|
19
|
+
getColumnNames: () => COLUMNS,
|
|
20
|
+
assembleColumns: (_fieldName, row) => {
|
|
21
|
+
const url = row.m_url;
|
|
22
|
+
if (url === null || url === undefined || url === '')
|
|
23
|
+
return null;
|
|
24
|
+
return { url, size: row.m_size ?? 0 };
|
|
25
|
+
},
|
|
26
|
+
splitColumns: (_fieldName, value) => {
|
|
27
|
+
if (value === null || value === undefined) {
|
|
28
|
+
return { m_url: null, m_size: null };
|
|
29
|
+
}
|
|
30
|
+
const v = value;
|
|
31
|
+
return { m_url: v.url ?? null, m_size: v.size ?? null };
|
|
32
|
+
},
|
|
33
|
+
// Field's own resolveInput is identity here (the value is authoritative).
|
|
34
|
+
hooks: {
|
|
35
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any -- generic test hook
|
|
36
|
+
resolveInput: async ({ resolvedData, fieldKey }) => resolvedData?.[fieldKey],
|
|
37
|
+
},
|
|
38
|
+
};
|
|
39
|
+
}
|
|
40
|
+
function makeContext(overrides = {}) {
|
|
41
|
+
return {
|
|
42
|
+
session: null,
|
|
43
|
+
_isSudo: overrides.isSudo ?? false,
|
|
44
|
+
_resolveOutputCounter: { depth: 0 },
|
|
45
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any -- minimal context for unit test
|
|
46
|
+
};
|
|
47
|
+
}
|
|
48
|
+
describe('multi-column read assembly (filterReadableFields)', () => {
|
|
49
|
+
const fields = { media: multiColumnField() };
|
|
50
|
+
it('assembles the per-part columns into the logical field and strips the raw columns', async () => {
|
|
51
|
+
const row = { id: 'a', m_url: 'https://x/y.jpg', m_size: 99, title: 'hi' };
|
|
52
|
+
const result = await filterReadableFields(row,
|
|
53
|
+
// title is a plain field
|
|
54
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any -- inline field configs
|
|
55
|
+
{ ...fields, title: { type: 'text' } }, { session: null, context: makeContext() }, undefined, 0, 'Post');
|
|
56
|
+
expect(result).toEqual({ id: 'a', title: 'hi', media: { url: 'https://x/y.jpg', size: 99 } });
|
|
57
|
+
// Raw columns must NOT leak.
|
|
58
|
+
expect('m_url' in result).toBe(false);
|
|
59
|
+
expect('m_size' in result).toBe(false);
|
|
60
|
+
});
|
|
61
|
+
it('assembles a partially-populated row (only m_url present)', async () => {
|
|
62
|
+
const row = { id: 'b', m_url: 'https://x/only.jpg' };
|
|
63
|
+
const result = await filterReadableFields(row, fields, { session: null, context: makeContext() }, undefined, 0, 'Post');
|
|
64
|
+
expect(result).toEqual({ id: 'b', media: { url: 'https://x/only.jpg', size: 0 } });
|
|
65
|
+
});
|
|
66
|
+
it('yields a null logical value when the columns are empty', async () => {
|
|
67
|
+
const row = { id: 'c', m_url: null, m_size: null };
|
|
68
|
+
const result = await filterReadableFields(row, fields, { session: null, context: makeContext() }, undefined, 0, 'Post');
|
|
69
|
+
expect(result).toEqual({ id: 'c', media: null });
|
|
70
|
+
});
|
|
71
|
+
it('leaves the field absent when its columns were not selected', async () => {
|
|
72
|
+
const row = { id: 'd', title: 'no media columns' };
|
|
73
|
+
const result = await filterReadableFields(row,
|
|
74
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any -- inline field configs
|
|
75
|
+
{ ...fields, title: { type: 'text' } }, { session: null, context: makeContext() }, undefined, 0, 'Post');
|
|
76
|
+
expect(result).toEqual({ id: 'd', title: 'no media columns' });
|
|
77
|
+
expect('media' in result).toBe(false);
|
|
78
|
+
});
|
|
79
|
+
});
|
|
80
|
+
describe('multi-column write split (executeFieldResolveInputHooks)', () => {
|
|
81
|
+
const fields = { media: multiColumnField() };
|
|
82
|
+
it('splits the logical value into per-part columns and removes the logical key', async () => {
|
|
83
|
+
const inputData = { media: { url: 'https://x/y.jpg', size: 99 } };
|
|
84
|
+
const result = await executeFieldResolveInputHooks(inputData, { ...inputData }, fields, 'create', makeContext(), 'Post');
|
|
85
|
+
expect(result).toEqual({ m_url: 'https://x/y.jpg', m_size: 99 });
|
|
86
|
+
expect('media' in result).toBe(false);
|
|
87
|
+
});
|
|
88
|
+
it('splitting null clears all per-part columns', async () => {
|
|
89
|
+
const inputData = { media: null };
|
|
90
|
+
const result = await executeFieldResolveInputHooks(inputData, { ...inputData }, fields, 'update', makeContext(), 'Post');
|
|
91
|
+
expect(result).toEqual({ m_url: null, m_size: null });
|
|
92
|
+
});
|
|
93
|
+
it('does not touch the columns when the logical field is absent from the write', async () => {
|
|
94
|
+
const inputData = { title: 'no media in payload' };
|
|
95
|
+
const result = await executeFieldResolveInputHooks(inputData, { ...inputData },
|
|
96
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any -- inline field configs
|
|
97
|
+
{ ...fields, title: { type: 'text' } }, 'update', makeContext(), 'Post');
|
|
98
|
+
expect(result).toEqual({ title: 'no media in payload' });
|
|
99
|
+
expect('m_url' in result).toBe(false);
|
|
100
|
+
});
|
|
101
|
+
});
|
|
102
|
+
describe('multi-column write split respects field-level write access', () => {
|
|
103
|
+
it('does NOT write any per-part columns when update access is denied', async () => {
|
|
104
|
+
const fields = { media: multiColumnField({ update: () => false }) };
|
|
105
|
+
const inputData = { media: { url: 'https://x/y.jpg', size: 99 } };
|
|
106
|
+
const result = await executeFieldResolveInputHooks(inputData, { ...inputData }, fields, 'update', makeContext(), 'Post');
|
|
107
|
+
// The logical key is dropped (it is not a real column) AND none of its
|
|
108
|
+
// per-part columns are written — identical to how filterWritableFields
|
|
109
|
+
// drops a denied single-column field.
|
|
110
|
+
expect(result).toEqual({});
|
|
111
|
+
expect('media' in result).toBe(false);
|
|
112
|
+
expect('m_url' in result).toBe(false);
|
|
113
|
+
expect('m_size' in result).toBe(false);
|
|
114
|
+
});
|
|
115
|
+
it('does NOT write any per-part columns when create access is denied', async () => {
|
|
116
|
+
const fields = { media: multiColumnField({ create: () => false }) };
|
|
117
|
+
const inputData = { media: { url: 'https://x/y.jpg', size: 99 } };
|
|
118
|
+
const result = await executeFieldResolveInputHooks(inputData, { ...inputData }, fields, 'create', makeContext(), 'Post');
|
|
119
|
+
expect(result).toEqual({});
|
|
120
|
+
expect('m_url' in result).toBe(false);
|
|
121
|
+
});
|
|
122
|
+
it('still splits/writes the columns when write access is granted', async () => {
|
|
123
|
+
const fields = { media: multiColumnField({ update: () => true, create: () => true }) };
|
|
124
|
+
const inputData = { media: { url: 'https://x/y.jpg', size: 99 } };
|
|
125
|
+
const result = await executeFieldResolveInputHooks(inputData, { ...inputData }, fields, 'update', makeContext(), 'Post');
|
|
126
|
+
expect(result).toEqual({ m_url: 'https://x/y.jpg', m_size: 99 });
|
|
127
|
+
expect('media' in result).toBe(false);
|
|
128
|
+
});
|
|
129
|
+
it('denying the OTHER operation does not block the write (update field, create op)', async () => {
|
|
130
|
+
// A field that denies `update` must still be writable on `create`.
|
|
131
|
+
const fields = { media: multiColumnField({ update: () => false }) };
|
|
132
|
+
const inputData = { media: { url: 'https://x/y.jpg', size: 99 } };
|
|
133
|
+
const result = await executeFieldResolveInputHooks(inputData, { ...inputData }, fields, 'create', makeContext(), 'Post');
|
|
134
|
+
expect(result).toEqual({ m_url: 'https://x/y.jpg', m_size: 99 });
|
|
135
|
+
});
|
|
136
|
+
it('sudo bypasses the field-access gate and still splits', async () => {
|
|
137
|
+
const fields = { media: multiColumnField({ update: () => false }) };
|
|
138
|
+
const inputData = { media: { url: 'https://x/y.jpg', size: 99 } };
|
|
139
|
+
const result = await executeFieldResolveInputHooks(inputData, { ...inputData }, fields, 'update', makeContext({ isSudo: true }), 'Post');
|
|
140
|
+
expect(result).toEqual({ m_url: 'https://x/y.jpg', m_size: 99 });
|
|
141
|
+
});
|
|
142
|
+
it('a multi-column field WITHOUT field-level access splits exactly as before', async () => {
|
|
143
|
+
const fields = { media: multiColumnField() };
|
|
144
|
+
const inputData = { media: { url: 'https://x/y.jpg', size: 99 } };
|
|
145
|
+
const result = await executeFieldResolveInputHooks(inputData, { ...inputData }, fields, 'update', makeContext(), 'Post');
|
|
146
|
+
expect(result).toEqual({ m_url: 'https://x/y.jpg', m_size: 99 });
|
|
147
|
+
});
|
|
148
|
+
});
|
|
149
|
+
//# sourceMappingURL=multi-column-read-write.test.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"multi-column-read-write.test.js","sourceRoot":"","sources":["../../src/access/multi-column-read-write.test.ts"],"names":[],"mappings":"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"}
|
package/dist/config/index.d.ts
CHANGED
|
@@ -62,5 +62,5 @@ export declare function config(userConfig: OpenSaasConfig): OpenSaasConfig | Pro
|
|
|
62
62
|
* ```
|
|
63
63
|
*/
|
|
64
64
|
export declare function list<TTypeInfo extends import('./types.js').TypeInfo>(config: ListConfigInput<TTypeInfo>): ListConfig<TTypeInfo>;
|
|
65
|
-
export type { OpenSaasConfig, ListConfig, ListConfigInput, ListAccessControl, FieldConfig, BaseFieldConfig, TextField, IntegerField, CheckboxField, TimestampField, PasswordField, SelectField, RelationshipField, JsonField, VirtualField, TypeDescriptor, TypeInfo, OperationAccess, Hooks, FieldHooks, FieldsWithTypeInfo, DatabaseConfig, SessionConfig, UIConfig, ThemeConfig, ThemePreset, ThemeColors, McpConfig, McpToolsConfig, McpAuthConfig, ListMcpConfig, McpCustomTool, FileMetadata, ImageMetadata, ImageTransformationResult, Plugin, PluginContext, GeneratedFiles, ResolveInputHookArgs, ValidateHookArgs, BeforeOperationHookArgs, AfterOperationHookArgs, FieldResolveInputHookArgs, FieldValidateHookArgs, FieldBeforeOperationHookArgs, FieldAfterOperationHookArgs, FieldResolveOutputHookArgs, } from './types.js';
|
|
65
|
+
export type { OpenSaasConfig, OutputConfig, ListConfig, ListConfigInput, ListAccessControl, FieldConfig, BaseFieldConfig, TextField, IntegerField, CheckboxField, TimestampField, PasswordField, SelectField, RelationshipField, PrismaRelationResult, MultiColumnPrismaResult, JsonField, VirtualField, TypeDescriptor, TypeInfo, OperationAccess, Hooks, FieldHooks, FieldsWithTypeInfo, DatabaseConfig, SessionConfig, UIConfig, ThemeConfig, ThemePreset, ThemeColors, McpConfig, McpToolsConfig, McpAuthConfig, ListMcpConfig, McpCustomTool, FileMetadata, ImageMetadata, ImageTransformationResult, Plugin, PluginContext, GeneratedFiles, ResolveInputHookArgs, ValidateHookArgs, BeforeOperationHookArgs, AfterOperationHookArgs, FieldResolveInputHookArgs, FieldValidateHookArgs, FieldBeforeOperationHookArgs, FieldAfterOperationHookArgs, FieldResolveOutputHookArgs, } from './types.js';
|
|
66
66
|
//# sourceMappingURL=index.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/config/index.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EACV,cAAc,EACd,UAAU,EACV,eAAe,EAGhB,MAAM,YAAY,CAAA;AA8BnB;;;;;;GAMG;AACH,wBAAgB,MAAM,CAAC,UAAU,EAAE,cAAc,GAAG,cAAc,GAAG,OAAO,CAAC,cAAc,CAAC,CAQ3F;AAED;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAqDG;AACH,wBAAgB,IAAI,CAAC,SAAS,SAAS,OAAO,YAAY,EAAE,QAAQ,EAClE,MAAM,EAAE,eAAe,CAAC,SAAS,CAAC,GACjC,UAAU,CAAC,SAAS,CAAC,CAUvB;AAGD,YAAY,EACV,cAAc,EACd,UAAU,EACV,eAAe,EACf,iBAAiB,EACjB,WAAW,EACX,eAAe,EACf,SAAS,EACT,YAAY,EACZ,aAAa,EACb,cAAc,EACd,aAAa,EACb,WAAW,EACX,iBAAiB,EACjB,SAAS,EACT,YAAY,EACZ,cAAc,EACd,QAAQ,EACR,eAAe,EACf,KAAK,EACL,UAAU,EACV,kBAAkB,EAClB,cAAc,EACd,aAAa,EACb,QAAQ,EACR,WAAW,EACX,WAAW,EACX,WAAW,EACX,SAAS,EACT,cAAc,EACd,aAAa,EACb,aAAa,EACb,aAAa,EACb,YAAY,EACZ,aAAa,EACb,yBAAyB,EAEzB,MAAM,EACN,aAAa,EACb,cAAc,EAEd,oBAAoB,EACpB,gBAAgB,EAChB,uBAAuB,EACvB,sBAAsB,EAEtB,yBAAyB,EACzB,qBAAqB,EACrB,4BAA4B,EAC5B,2BAA2B,EAC3B,0BAA0B,GAC3B,MAAM,YAAY,CAAA"}
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/config/index.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EACV,cAAc,EACd,UAAU,EACV,eAAe,EAGhB,MAAM,YAAY,CAAA;AA8BnB;;;;;;GAMG;AACH,wBAAgB,MAAM,CAAC,UAAU,EAAE,cAAc,GAAG,cAAc,GAAG,OAAO,CAAC,cAAc,CAAC,CAQ3F;AAED;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAqDG;AACH,wBAAgB,IAAI,CAAC,SAAS,SAAS,OAAO,YAAY,EAAE,QAAQ,EAClE,MAAM,EAAE,eAAe,CAAC,SAAS,CAAC,GACjC,UAAU,CAAC,SAAS,CAAC,CAUvB;AAGD,YAAY,EACV,cAAc,EACd,YAAY,EACZ,UAAU,EACV,eAAe,EACf,iBAAiB,EACjB,WAAW,EACX,eAAe,EACf,SAAS,EACT,YAAY,EACZ,aAAa,EACb,cAAc,EACd,aAAa,EACb,WAAW,EACX,iBAAiB,EACjB,oBAAoB,EACpB,uBAAuB,EACvB,SAAS,EACT,YAAY,EACZ,cAAc,EACd,QAAQ,EACR,eAAe,EACf,KAAK,EACL,UAAU,EACV,kBAAkB,EAClB,cAAc,EACd,aAAa,EACb,QAAQ,EACR,WAAW,EACX,WAAW,EACX,WAAW,EACX,SAAS,EACT,cAAc,EACd,aAAa,EACb,aAAa,EACb,aAAa,EACb,YAAY,EACZ,aAAa,EACb,yBAAyB,EAEzB,MAAM,EACN,aAAa,EACb,cAAc,EAEd,oBAAoB,EACpB,gBAAgB,EAChB,uBAAuB,EACvB,sBAAsB,EAEtB,yBAAyB,EACzB,qBAAqB,EACrB,4BAA4B,EAC5B,2BAA2B,EAC3B,0BAA0B,GAC3B,MAAM,YAAY,CAAA"}
|