@opensaas/stack-core 0.27.1 → 0.29.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 +58 -0
- package/CLAUDE.md +2 -0
- package/dist/config/index.d.ts +1 -1
- package/dist/config/index.d.ts.map +1 -1
- package/dist/config/plugin-engine.d.ts.map +1 -1
- package/dist/config/plugin-engine.js +10 -21
- package/dist/config/plugin-engine.js.map +1 -1
- package/dist/config/types.d.ts +74 -10
- package/dist/config/types.d.ts.map +1 -1
- package/dist/context/index.d.ts.map +1 -1
- package/dist/context/index.js +3 -1
- package/dist/context/index.js.map +1 -1
- package/dist/internal.d.ts +1 -1
- package/dist/internal.d.ts.map +1 -1
- package/dist/query/relationship-options.d.ts.map +1 -1
- package/dist/query/relationship-options.js +7 -1
- package/dist/query/relationship-options.js.map +1 -1
- package/dist/query/relationship-options.test.js +29 -0
- package/dist/query/relationship-options.test.js.map +1 -1
- package/package.json +2 -2
- package/src/config/index.ts +2 -0
- package/src/config/plugin-engine.ts +13 -28
- package/src/config/types.ts +79 -10
- package/src/context/index.ts +6 -1
- package/src/internal.ts +2 -0
- package/src/query/relationship-options.test.ts +37 -0
- package/src/query/relationship-options.ts +10 -2
- package/tests/plugin-engine.test.ts +66 -13
- package/tests/sudo.test.ts +49 -0
- package/tsconfig.tsbuildinfo +1 -1
package/dist/internal.d.ts
CHANGED
|
@@ -4,5 +4,5 @@ export type { Fragment, FieldSelection } from './query/index.js';
|
|
|
4
4
|
export { hashPassword, comparePassword, isHashedPassword, HashedPassword, } from './utils/password.js';
|
|
5
5
|
export { pascalToCamel, pascalToKebab, kebabToPascal, kebabToCamel } from './lib/case-utils.js';
|
|
6
6
|
export { validateWithZod, generateZodSchema } from './validation/schema.js';
|
|
7
|
-
export type { DatabaseConfig, SessionConfig, ThemeConfig, ThemePreset, ThemeColors, FileMetadata, ImageMetadata, ImageTransformationResult, } from './config/index.js';
|
|
7
|
+
export type { DatabaseConfig, SessionConfig, ThemeConfig, ThemePreset, ThemeColors, ThemeFonts, ThemeShadows, FileMetadata, ImageMetadata, ImageTransformationResult, } from './config/index.js';
|
|
8
8
|
//# sourceMappingURL=internal.d.ts.map
|
package/dist/internal.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"internal.d.ts","sourceRoot":"","sources":["../src/internal.ts"],"names":[],"mappings":"AAWA,YAAY,EAAE,iBAAiB,EAAE,MAAM,oBAAoB,CAAA;AAC3D,YAAY,EACV,gBAAgB,EAChB,kBAAkB,EAClB,YAAY,EACZ,iBAAiB,EACjB,mBAAmB,EACnB,iBAAiB,GAClB,MAAM,mBAAmB,CAAA;AAG1B,YAAY,EAAE,QAAQ,EAAE,cAAc,EAAE,MAAM,kBAAkB,CAAA;AAGhE,OAAO,EACL,YAAY,EACZ,eAAe,EACf,gBAAgB,EAChB,cAAc,GACf,MAAM,qBAAqB,CAAA;AAG5B,OAAO,EAAE,aAAa,EAAE,aAAa,EAAE,aAAa,EAAE,YAAY,EAAE,MAAM,qBAAqB,CAAA;AAG/F,OAAO,EAAE,eAAe,EAAE,iBAAiB,EAAE,MAAM,wBAAwB,CAAA;AAG3E,YAAY,EACV,cAAc,EACd,aAAa,EACb,WAAW,EACX,WAAW,EACX,WAAW,EACX,YAAY,EACZ,aAAa,EACb,yBAAyB,GAC1B,MAAM,mBAAmB,CAAA"}
|
|
1
|
+
{"version":3,"file":"internal.d.ts","sourceRoot":"","sources":["../src/internal.ts"],"names":[],"mappings":"AAWA,YAAY,EAAE,iBAAiB,EAAE,MAAM,oBAAoB,CAAA;AAC3D,YAAY,EACV,gBAAgB,EAChB,kBAAkB,EAClB,YAAY,EACZ,iBAAiB,EACjB,mBAAmB,EACnB,iBAAiB,GAClB,MAAM,mBAAmB,CAAA;AAG1B,YAAY,EAAE,QAAQ,EAAE,cAAc,EAAE,MAAM,kBAAkB,CAAA;AAGhE,OAAO,EACL,YAAY,EACZ,eAAe,EACf,gBAAgB,EAChB,cAAc,GACf,MAAM,qBAAqB,CAAA;AAG5B,OAAO,EAAE,aAAa,EAAE,aAAa,EAAE,aAAa,EAAE,YAAY,EAAE,MAAM,qBAAqB,CAAA;AAG/F,OAAO,EAAE,eAAe,EAAE,iBAAiB,EAAE,MAAM,wBAAwB,CAAA;AAG3E,YAAY,EACV,cAAc,EACd,aAAa,EACb,WAAW,EACX,WAAW,EACX,WAAW,EACX,UAAU,EACV,YAAY,EACZ,YAAY,EACZ,aAAa,EACb,yBAAyB,GAC1B,MAAM,mBAAmB,CAAA"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"relationship-options.d.ts","sourceRoot":"","sources":["../../src/query/relationship-options.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,oBAAoB,CAAA;AAExD,OAAO,EAA4B,KAAK,kBAAkB,EAAE,MAAM,YAAY,CAAA;AAI9E,MAAM,WAAW,kBAAkB;IACjC,EAAE,EAAE,MAAM,CAAA;IACV,KAAK,EAAE,MAAM,CAAA;CACd;AAED,MAAM,WAAW,uBAAuB;IACtC,sEAAsE;IACtE,MAAM,CAAC,EAAE,MAAM,CAAA;IACf,6DAA6D;IAC7D,IAAI,CAAC,EAAE,MAAM,CAAA;IACb,2EAA2E;IAC3E,WAAW,CAAC,EAAE,MAAM,EAAE,CAAA;CACvB;AAED;;;;;;;;;GASG;AACH,wBAAsB,sBAAsB,CAC1C,OAAO,EAAE,kBAAkB,EAC3B,MAAM,EAAE,cAAc,EACtB,cAAc,EAAE,MAAM,EACtB,IAAI,GAAE,uBAA4B,GACjC,OAAO,CAAC,kBAAkB,EAAE,CAAC,
|
|
1
|
+
{"version":3,"file":"relationship-options.d.ts","sourceRoot":"","sources":["../../src/query/relationship-options.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,oBAAoB,CAAA;AAExD,OAAO,EAA4B,KAAK,kBAAkB,EAAE,MAAM,YAAY,CAAA;AAI9E,MAAM,WAAW,kBAAkB;IACjC,EAAE,EAAE,MAAM,CAAA;IACV,KAAK,EAAE,MAAM,CAAA;CACd;AAED,MAAM,WAAW,uBAAuB;IACtC,sEAAsE;IACtE,MAAM,CAAC,EAAE,MAAM,CAAA;IACf,6DAA6D;IAC7D,IAAI,CAAC,EAAE,MAAM,CAAA;IACb,2EAA2E;IAC3E,WAAW,CAAC,EAAE,MAAM,EAAE,CAAA;CACvB;AAED;;;;;;;;;GASG;AACH,wBAAsB,sBAAsB,CAC1C,OAAO,EAAE,kBAAkB,EAC3B,MAAM,EAAE,cAAc,EACtB,cAAc,EAAE,MAAM,EACtB,IAAI,GAAE,uBAA4B,GACjC,OAAO,CAAC,kBAAkB,EAAE,CAAC,CA0C/B"}
|
|
@@ -23,9 +23,15 @@ export async function getRelationshipOptions(context, config, relatedListKey, ar
|
|
|
23
23
|
const { search, take = DEFAULT_TAKE, selectedIds = [] } = args;
|
|
24
24
|
const labelFieldConfig = relatedListConfig.fields[labelField];
|
|
25
25
|
const where = search && labelFieldConfig?.type === 'text' ? { [labelField]: { contains: search } } : undefined;
|
|
26
|
+
// Virtual/computed label fields (resolved at read time via `resolveOutput`)
|
|
27
|
+
// have no backing database column, so passing them into `orderBy` fails
|
|
28
|
+
// Prisma validation and 500s the request. Fall back to ordering by `id` —
|
|
29
|
+
// always a real, orderable column — whenever the label field is virtual.
|
|
30
|
+
const isVirtualLabel = labelFieldConfig?.type === 'virtual' || labelFieldConfig?.virtual === true;
|
|
31
|
+
const orderBy = isVirtualLabel ? { id: 'asc' } : { [labelField]: 'asc' };
|
|
26
32
|
const primary = await runQuery(context, relatedListKey, fragment, {
|
|
27
33
|
where,
|
|
28
|
-
orderBy
|
|
34
|
+
orderBy,
|
|
29
35
|
take,
|
|
30
36
|
});
|
|
31
37
|
const seenIds = new Set(primary.map((item) => item.id));
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"relationship-options.js","sourceRoot":"","sources":["../../src/query/relationship-options.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,iBAAiB,EAAE,YAAY,EAAE,MAAM,oBAAoB,CAAA;AACpE,OAAO,EAAE,cAAc,EAAE,QAAQ,EAA2B,MAAM,YAAY,CAAA;AAE9E,MAAM,YAAY,GAAG,EAAE,CAAA;AAgBvB;;;;;;;;;GASG;AACH,MAAM,CAAC,KAAK,UAAU,sBAAsB,CAC1C,OAA2B,EAC3B,MAAsB,EACtB,cAAsB,EACtB,IAAI,GAA4B,EAAE;IAElC,MAAM,iBAAiB,GAAG,MAAM,CAAC,KAAK,CAAC,cAAc,CAAC,CAAA;IACtD,IAAI,CAAC,iBAAiB;QAAE,OAAO,EAAE,CAAA;IAEjC,MAAM,UAAU,GAAG,iBAAiB,CAAC,iBAAiB,CAAC,CAAA;IACvD,MAAM,QAAQ,GAAG,cAAc,EAA2B,CAAC;QACzD,EAAE,EAAE,IAAI;QACR,CAAC,UAAU,CAAC,EAAE,IAAI;KACnB,CAAC,CAAA;IAEF,MAAM,EAAE,MAAM,EAAE,IAAI,GAAG,YAAY,EAAE,WAAW,GAAG,EAAE,EAAE,GAAG,IAAI,CAAA;IAC9D,MAAM,gBAAgB,GAAG,iBAAiB,CAAC,MAAM,CAAC,UAAU,
|
|
1
|
+
{"version":3,"file":"relationship-options.js","sourceRoot":"","sources":["../../src/query/relationship-options.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,iBAAiB,EAAE,YAAY,EAAE,MAAM,oBAAoB,CAAA;AACpE,OAAO,EAAE,cAAc,EAAE,QAAQ,EAA2B,MAAM,YAAY,CAAA;AAE9E,MAAM,YAAY,GAAG,EAAE,CAAA;AAgBvB;;;;;;;;;GASG;AACH,MAAM,CAAC,KAAK,UAAU,sBAAsB,CAC1C,OAA2B,EAC3B,MAAsB,EACtB,cAAsB,EACtB,IAAI,GAA4B,EAAE;IAElC,MAAM,iBAAiB,GAAG,MAAM,CAAC,KAAK,CAAC,cAAc,CAAC,CAAA;IACtD,IAAI,CAAC,iBAAiB;QAAE,OAAO,EAAE,CAAA;IAEjC,MAAM,UAAU,GAAG,iBAAiB,CAAC,iBAAiB,CAAC,CAAA;IACvD,MAAM,QAAQ,GAAG,cAAc,EAA2B,CAAC;QACzD,EAAE,EAAE,IAAI;QACR,CAAC,UAAU,CAAC,EAAE,IAAI;KACnB,CAAC,CAAA;IAEF,MAAM,EAAE,MAAM,EAAE,IAAI,GAAG,YAAY,EAAE,WAAW,GAAG,EAAE,EAAE,GAAG,IAAI,CAAA;IAC9D,MAAM,gBAAgB,GAAG,iBAAiB,CAAC,MAAM,CAAC,UAAU,CACV,CAAA;IAClD,MAAM,KAAK,GACT,MAAM,IAAI,gBAAgB,EAAE,IAAI,KAAK,MAAM,CAAC,CAAC,CAAC,EAAE,CAAC,UAAU,CAAC,EAAE,EAAE,QAAQ,EAAE,MAAM,EAAE,EAAE,CAAC,CAAC,CAAC,SAAS,CAAA;IAElG,4EAA4E;IAC5E,wEAAwE;IACxE,0EAA0E;IAC1E,yEAAyE;IACzE,MAAM,cAAc,GAAG,gBAAgB,EAAE,IAAI,KAAK,SAAS,IAAI,gBAAgB,EAAE,OAAO,KAAK,IAAI,CAAA;IACjG,MAAM,OAAO,GAA0B,cAAc,CAAC,CAAC,CAAC,EAAE,EAAE,EAAE,KAAK,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC,UAAU,CAAC,EAAE,KAAK,EAAE,CAAA;IAE/F,MAAM,OAAO,GAAG,MAAM,QAAQ,CAAC,OAAO,EAAE,cAAc,EAAE,QAAQ,EAAE;QAChE,KAAK;QACL,OAAO;QACP,IAAI;KACL,CAAC,CAAA;IAEF,MAAM,OAAO,GAAG,IAAI,GAAG,CAAC,OAAO,CAAC,GAAG,CAAC,CAAC,IAAI,EAAE,EAAE,CAAE,IAAuB,CAAC,EAAE,CAAC,CAAC,CAAA;IAC3E,MAAM,kBAAkB,GAAG,WAAW,CAAC,MAAM,CAAC,CAAC,EAAE,EAAE,EAAE,CAAC,CAAC,OAAO,CAAC,GAAG,CAAC,EAAE,CAAC,CAAC,CAAA;IAEvE,MAAM,QAAQ,GAAG,kBAAkB,CAAC,MAAM;QACxC,CAAC,CAAC,MAAM,QAAQ,CAAC,OAAO,EAAE,cAAc,EAAE,QAAQ,EAAE;YAChD,KAAK,EAAE,EAAE,EAAE,EAAE,EAAE,EAAE,EAAE,kBAAkB,EAAE,EAAE;SAC1C,CAAC;QACJ,CAAC,CAAC,EAAE,CAAA;IAEN,OAAO,CAAC,GAAG,OAAO,EAAE,GAAG,QAAQ,CAAC,CAAC,GAAG,CAAC,CAAC,IAAI,EAAE,EAAE,CAAC,CAAC;QAC9C,EAAE,EAAG,IAAuB,CAAC,EAAE;QAC/B,KAAK,EAAE,YAAY,CAAC,iBAAiB,EAAE,IAA+B,CAAC;KACxE,CAAC,CAAC,CAAA;AACL,CAAC"}
|
|
@@ -31,6 +31,13 @@ function makeConfig() {
|
|
|
31
31
|
ui: { labelField: 'rank' },
|
|
32
32
|
access: { operation: { query: () => true } },
|
|
33
33
|
},
|
|
34
|
+
VirtualLabel: {
|
|
35
|
+
fields: {
|
|
36
|
+
displayName: { type: 'virtual', virtual: true },
|
|
37
|
+
},
|
|
38
|
+
ui: { labelField: 'displayName' },
|
|
39
|
+
access: { operation: { query: () => true } },
|
|
40
|
+
},
|
|
34
41
|
},
|
|
35
42
|
};
|
|
36
43
|
}
|
|
@@ -75,6 +82,28 @@ describe('getRelationshipOptions', () => {
|
|
|
75
82
|
expect(call.where).toBeUndefined();
|
|
76
83
|
expect(call.orderBy).toEqual({ rank: 'asc' });
|
|
77
84
|
});
|
|
85
|
+
it('falls back to ordering by id when the label field is virtual (no backing column)', async () => {
|
|
86
|
+
const rows = [{ id: 'v1', displayName: 'Computed One' }];
|
|
87
|
+
const delegate = makeDelegate(rows);
|
|
88
|
+
const context = makeContext({ virtualLabel: delegate });
|
|
89
|
+
const config = makeConfig();
|
|
90
|
+
const result = await getRelationshipOptions(context, config, 'VirtualLabel', { search: 'One' });
|
|
91
|
+
const call = delegate.findMany.mock.calls[0][0];
|
|
92
|
+
// Virtual label fields have no backing column, so ordering by them would
|
|
93
|
+
// 500 in Prisma — order by id instead, and skip the text `contains` filter.
|
|
94
|
+
expect(call.orderBy).toEqual({ id: 'asc' });
|
|
95
|
+
expect(call.where).toBeUndefined();
|
|
96
|
+
expect(result).toEqual([{ id: 'v1', label: 'Computed One' }]);
|
|
97
|
+
});
|
|
98
|
+
it('treats a field flagged virtual via type alone as non-orderable (orders by id)', async () => {
|
|
99
|
+
const config = makeConfig();
|
|
100
|
+
config.lists.VirtualLabel.fields.displayName.virtual = undefined;
|
|
101
|
+
const delegate = makeDelegate([{ id: 'v1', displayName: 'Computed One' }]);
|
|
102
|
+
const context = makeContext({ virtualLabel: delegate });
|
|
103
|
+
await getRelationshipOptions(context, config, 'VirtualLabel', { search: 'One' });
|
|
104
|
+
const call = delegate.findMany.mock.calls[0][0];
|
|
105
|
+
expect(call.orderBy).toEqual({ id: 'asc' });
|
|
106
|
+
});
|
|
78
107
|
it('unions currently-selected ids even when beyond take / not matching search', async () => {
|
|
79
108
|
// The bounded/search-scoped query only returns a1 (mimicking take:1 + search).
|
|
80
109
|
const primaryDelegate = makeDelegate([authors[0]]);
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"relationship-options.test.js","sourceRoot":"","sources":["../../src/query/relationship-options.test.ts"],"names":[],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"relationship-options.test.js","sourceRoot":"","sources":["../../src/query/relationship-options.test.ts"],"names":[],"mappings":"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"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@opensaas/stack-core",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.29.0",
|
|
4
4
|
"description": "Core stack for OpenSaas - schema definition, access control, and runtime utilities",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "./dist/index.js",
|
|
@@ -53,7 +53,7 @@
|
|
|
53
53
|
},
|
|
54
54
|
"dependencies": {
|
|
55
55
|
"bcryptjs": "^3.0.3",
|
|
56
|
-
"zod": "^4.3
|
|
56
|
+
"zod": "^4.4.3"
|
|
57
57
|
},
|
|
58
58
|
"devDependencies": {
|
|
59
59
|
"@prisma/client": "^7.8.0",
|
package/src/config/index.ts
CHANGED
|
@@ -4,7 +4,6 @@ import type {
|
|
|
4
4
|
PluginContext,
|
|
5
5
|
ListConfig,
|
|
6
6
|
Hooks,
|
|
7
|
-
OperationAccess,
|
|
8
7
|
McpCustomTool,
|
|
9
8
|
BaseFieldConfig,
|
|
10
9
|
} from './types.js'
|
|
@@ -125,29 +124,6 @@ function mergeHooks(existing: Hooks | undefined, extension: Hooks | undefined):
|
|
|
125
124
|
return Object.keys(merged).length > 0 ? (merged as Hooks) : undefined
|
|
126
125
|
}
|
|
127
126
|
|
|
128
|
-
/**
|
|
129
|
-
* Merge access control from extension into existing access
|
|
130
|
-
*/
|
|
131
|
-
function mergeAccess(
|
|
132
|
-
existing: { operation?: OperationAccess } | undefined,
|
|
133
|
-
extension: { operation?: OperationAccess } | undefined,
|
|
134
|
-
): { operation?: OperationAccess } | undefined {
|
|
135
|
-
if (!extension) return existing
|
|
136
|
-
if (!existing) return extension
|
|
137
|
-
|
|
138
|
-
const merged: { operation?: OperationAccess } = {}
|
|
139
|
-
|
|
140
|
-
// Merge operation access (use extension if provided, otherwise keep existing)
|
|
141
|
-
if (existing.operation || extension.operation) {
|
|
142
|
-
merged.operation = {
|
|
143
|
-
...existing.operation,
|
|
144
|
-
...extension.operation,
|
|
145
|
-
}
|
|
146
|
-
}
|
|
147
|
-
|
|
148
|
-
return merged
|
|
149
|
-
}
|
|
150
|
-
|
|
151
127
|
/**
|
|
152
128
|
* Execute plugins and transform config
|
|
153
129
|
* Returns modified config with plugin data attached
|
|
@@ -197,6 +173,19 @@ export async function executePlugins(config: OpenSaasConfig): Promise<OpenSaasCo
|
|
|
197
173
|
)
|
|
198
174
|
}
|
|
199
175
|
|
|
176
|
+
// Operation-level access belongs to whoever owns the list (the
|
|
177
|
+
// application, or an earlier plugin that created it via addList).
|
|
178
|
+
// An extension of a pre-existing list must never define or override
|
|
179
|
+
// that access — see ADR-0013. Fields/hooks/relationships/mcp may
|
|
180
|
+
// still be merged in below.
|
|
181
|
+
if (extension.access?.operation) {
|
|
182
|
+
throw new Error(
|
|
183
|
+
`Plugin "${plugin.name}" tried to set operation-level access while extending list "${name}", ` +
|
|
184
|
+
`but access control belongs to the application (or whichever party created the list), never a ` +
|
|
185
|
+
`plugin extending it. Remove "access" from this extension — see ADR-0013.`,
|
|
186
|
+
)
|
|
187
|
+
}
|
|
188
|
+
|
|
200
189
|
// Deep merge fields
|
|
201
190
|
const mergedFields = {
|
|
202
191
|
...existing.fields,
|
|
@@ -206,9 +195,6 @@ export async function executePlugins(config: OpenSaasConfig): Promise<OpenSaasCo
|
|
|
206
195
|
// Merge hooks
|
|
207
196
|
const mergedHooks = mergeHooks(existing.hooks, extension.hooks)
|
|
208
197
|
|
|
209
|
-
// Merge access control
|
|
210
|
-
const mergedAccess = mergeAccess(existing.access, extension.access)
|
|
211
|
-
|
|
212
198
|
// Merge MCP config
|
|
213
199
|
const mergedMcp = extension.mcp
|
|
214
200
|
? {
|
|
@@ -221,7 +207,6 @@ export async function executePlugins(config: OpenSaasConfig): Promise<OpenSaasCo
|
|
|
221
207
|
...existing,
|
|
222
208
|
fields: mergedFields,
|
|
223
209
|
hooks: mergedHooks,
|
|
224
|
-
access: mergedAccess,
|
|
225
210
|
mcp: mergedMcp,
|
|
226
211
|
}
|
|
227
212
|
},
|
package/src/config/types.ts
CHANGED
|
@@ -593,6 +593,21 @@ export type BaseFieldConfig<TTypeInfo extends TypeInfo> = {
|
|
|
593
593
|
* e.g., "color" to use a globally registered ColorPickerField
|
|
594
594
|
*/
|
|
595
595
|
fieldType?: string
|
|
596
|
+
/**
|
|
597
|
+
* Help / description text rendered beneath the field control in the admin UI.
|
|
598
|
+
*
|
|
599
|
+
* `FieldRenderer` surfaces this to the rendered field component as its
|
|
600
|
+
* `helpText` prop, so it appears via the shared field-shell `FieldHelp`
|
|
601
|
+
* rhythm (data-slot="field-help") for admin-rendered fields.
|
|
602
|
+
*
|
|
603
|
+
* @example
|
|
604
|
+
* ```typescript
|
|
605
|
+
* fields: {
|
|
606
|
+
* slug: text({ ui: { description: 'URL-friendly identifier, lowercase only.' } })
|
|
607
|
+
* }
|
|
608
|
+
* ```
|
|
609
|
+
*/
|
|
610
|
+
description?: string
|
|
596
611
|
/**
|
|
597
612
|
* Transform field value before sending to client (browser)
|
|
598
613
|
* Useful for sensitive fields (e.g., passwords) or complex data structures
|
|
@@ -2029,8 +2044,13 @@ export type SessionConfig = {
|
|
|
2029
2044
|
export type ThemePreset = 'modern' | 'classic' | 'neon'
|
|
2030
2045
|
|
|
2031
2046
|
/**
|
|
2032
|
-
* Custom theme colors
|
|
2033
|
-
*
|
|
2047
|
+
* Custom theme colors.
|
|
2048
|
+
*
|
|
2049
|
+
* Each value is passed through verbatim to the corresponding CSS custom
|
|
2050
|
+
* property, so any valid CSS color string works: `oklch(…)`, `#hex`, `rgb(…)`,
|
|
2051
|
+
* or a wrapped `hsl(…)`. Bare HSL triplets (`"220 20% 97%"`) are a clean break —
|
|
2052
|
+
* they are no longer accepted and trigger a dev-mode warning. See
|
|
2053
|
+
* `specs/THEMING.md` and ADR-0015.
|
|
2034
2054
|
*/
|
|
2035
2055
|
export type ThemeColors = {
|
|
2036
2056
|
background?: string
|
|
@@ -2049,6 +2069,10 @@ export type ThemeColors = {
|
|
|
2049
2069
|
accentForeground?: string
|
|
2050
2070
|
destructive?: string
|
|
2051
2071
|
destructiveForeground?: string
|
|
2072
|
+
success?: string
|
|
2073
|
+
successForeground?: string
|
|
2074
|
+
warning?: string
|
|
2075
|
+
warningForeground?: string
|
|
2052
2076
|
border?: string
|
|
2053
2077
|
input?: string
|
|
2054
2078
|
ring?: string
|
|
@@ -2057,27 +2081,59 @@ export type ThemeColors = {
|
|
|
2057
2081
|
}
|
|
2058
2082
|
|
|
2059
2083
|
/**
|
|
2060
|
-
*
|
|
2084
|
+
* Font family tokens. Each value is a CSS `font-family` string, designed to
|
|
2085
|
+
* compose with `next/font`: set the value to the font's CSS variable, e.g.
|
|
2086
|
+
* `sans: 'var(--font-inter), system-ui, sans-serif'`. `heading` defaults to the
|
|
2087
|
+
* `sans` value when omitted.
|
|
2088
|
+
*/
|
|
2089
|
+
export type ThemeFonts = {
|
|
2090
|
+
sans?: string
|
|
2091
|
+
mono?: string
|
|
2092
|
+
heading?: string
|
|
2093
|
+
}
|
|
2094
|
+
|
|
2095
|
+
/**
|
|
2096
|
+
* Elevation shadow tokens. Each value is a CSS `box-shadow` string. Set them to
|
|
2097
|
+
* `'none'` for a fully flat theme without forking components.
|
|
2098
|
+
*/
|
|
2099
|
+
export type ThemeShadows = {
|
|
2100
|
+
sm?: string
|
|
2101
|
+
md?: string
|
|
2102
|
+
lg?: string
|
|
2103
|
+
}
|
|
2104
|
+
|
|
2105
|
+
/**
|
|
2106
|
+
* Theme configuration. Compiles to token overrides written onto the same CSS
|
|
2107
|
+
* custom properties the UI package stylesheet declares — the config layer and
|
|
2108
|
+
* the stylesheet write to one token set so they can never drift (ADR-0015).
|
|
2061
2109
|
*/
|
|
2062
2110
|
export type ThemeConfig = {
|
|
2063
2111
|
/**
|
|
2064
|
-
* Preset theme to
|
|
2112
|
+
* Preset theme to start from. Individual tokens can be overridden on top.
|
|
2065
2113
|
* @default "modern"
|
|
2066
2114
|
*/
|
|
2067
2115
|
preset?: ThemePreset
|
|
2068
2116
|
/**
|
|
2069
|
-
* Custom color overrides for light mode
|
|
2117
|
+
* Custom color overrides for light mode.
|
|
2070
2118
|
*/
|
|
2071
2119
|
colors?: ThemeColors
|
|
2072
2120
|
/**
|
|
2073
|
-
* Custom color overrides for dark mode
|
|
2121
|
+
* Custom color overrides for dark mode.
|
|
2074
2122
|
*/
|
|
2075
2123
|
darkColors?: ThemeColors
|
|
2076
2124
|
/**
|
|
2077
|
-
*
|
|
2078
|
-
|
|
2125
|
+
* Font family overrides (`--font-sans`, `--font-mono`, `--font-heading`).
|
|
2126
|
+
*/
|
|
2127
|
+
fonts?: ThemeFonts
|
|
2128
|
+
/**
|
|
2129
|
+
* Base border radius in rem. Derived sm/md/lg radii are computed from it.
|
|
2130
|
+
* @default 0.625
|
|
2079
2131
|
*/
|
|
2080
2132
|
radius?: number
|
|
2133
|
+
/**
|
|
2134
|
+
* Elevation shadow overrides (`--shadow-sm`, `--shadow-md`, `--shadow-lg`).
|
|
2135
|
+
*/
|
|
2136
|
+
shadows?: ThemeShadows
|
|
2081
2137
|
}
|
|
2082
2138
|
|
|
2083
2139
|
/**
|
|
@@ -2437,8 +2493,21 @@ export type Plugin = {
|
|
|
2437
2493
|
* Optional: Provide runtime services
|
|
2438
2494
|
* Called when creating context to provide plugin-specific services
|
|
2439
2495
|
* Return value is stored in context.plugins[pluginName]
|
|
2440
|
-
|
|
2441
|
-
|
|
2496
|
+
*
|
|
2497
|
+
* `sudo` returns an access-bypassing (but still hook-firing) `AccessContext`
|
|
2498
|
+
* for the same request — use `sudo().db` for reads/writes that must not
|
|
2499
|
+
* depend on the caller's own list access policy (e.g. an identity lookup
|
|
2500
|
+
* like "who is this session"). Deliberately NOT a method on `AccessContext`
|
|
2501
|
+
* itself — a self-referential `sudo(): AccessContext` field on that shared,
|
|
2502
|
+
* widely-instantiated interface tripped up TypeScript's structural checking
|
|
2503
|
+
* of unrelated generated Prisma types elsewhere (nullable JSON `CreateInput`
|
|
2504
|
+
* fields) in a downstream app; passing it as a plain second argument avoids
|
|
2505
|
+
* that recursion entirely.
|
|
2506
|
+
*/
|
|
2507
|
+
runtime?: (
|
|
2508
|
+
context: import('../access/types.js').AccessContext,
|
|
2509
|
+
sudo: () => import('../access/types.js').AccessContext,
|
|
2510
|
+
) => unknown
|
|
2442
2511
|
|
|
2443
2512
|
/**
|
|
2444
2513
|
* Optional: Type metadata for runtime services
|
package/src/context/index.ts
CHANGED
|
@@ -393,7 +393,12 @@ export function getContext<
|
|
|
393
393
|
for (const plugin of pluginsToExecute) {
|
|
394
394
|
if (plugin.runtime) {
|
|
395
395
|
try {
|
|
396
|
-
|
|
396
|
+
// Passed as a plain second argument rather than a method on
|
|
397
|
+
// `context` itself — see the `sudo` param doc on `Plugin['runtime']`.
|
|
398
|
+
context.plugins[plugin.name] = plugin.runtime(
|
|
399
|
+
context,
|
|
400
|
+
() => sudo() as unknown as AccessContext<TPrisma>,
|
|
401
|
+
)
|
|
397
402
|
} catch (error) {
|
|
398
403
|
console.error(`Error executing runtime for plugin "${plugin.name}":`, error)
|
|
399
404
|
// Continue with other plugins even if one fails
|
package/src/internal.ts
CHANGED
|
@@ -39,6 +39,13 @@ function makeConfig(): OpenSaasConfig {
|
|
|
39
39
|
ui: { labelField: 'rank' },
|
|
40
40
|
access: { operation: { query: () => true } },
|
|
41
41
|
},
|
|
42
|
+
VirtualLabel: {
|
|
43
|
+
fields: {
|
|
44
|
+
displayName: { type: 'virtual', virtual: true },
|
|
45
|
+
},
|
|
46
|
+
ui: { labelField: 'displayName' },
|
|
47
|
+
access: { operation: { query: () => true } },
|
|
48
|
+
},
|
|
42
49
|
},
|
|
43
50
|
} as unknown as OpenSaasConfig
|
|
44
51
|
}
|
|
@@ -99,6 +106,36 @@ describe('getRelationshipOptions', () => {
|
|
|
99
106
|
expect(call.orderBy).toEqual({ rank: 'asc' })
|
|
100
107
|
})
|
|
101
108
|
|
|
109
|
+
it('falls back to ordering by id when the label field is virtual (no backing column)', async () => {
|
|
110
|
+
const rows = [{ id: 'v1', displayName: 'Computed One' }]
|
|
111
|
+
const delegate = makeDelegate(rows)
|
|
112
|
+
const context = makeContext({ virtualLabel: delegate })
|
|
113
|
+
const config = makeConfig()
|
|
114
|
+
|
|
115
|
+
const result = await getRelationshipOptions(context, config, 'VirtualLabel', { search: 'One' })
|
|
116
|
+
|
|
117
|
+
const call = delegate.findMany.mock.calls[0][0] as Record<string, unknown>
|
|
118
|
+
// Virtual label fields have no backing column, so ordering by them would
|
|
119
|
+
// 500 in Prisma — order by id instead, and skip the text `contains` filter.
|
|
120
|
+
expect(call.orderBy).toEqual({ id: 'asc' })
|
|
121
|
+
expect(call.where).toBeUndefined()
|
|
122
|
+
expect(result).toEqual([{ id: 'v1', label: 'Computed One' }])
|
|
123
|
+
})
|
|
124
|
+
|
|
125
|
+
it('treats a field flagged virtual via type alone as non-orderable (orders by id)', async () => {
|
|
126
|
+
const config = makeConfig()
|
|
127
|
+
// Only `type: 'virtual'` is set here (no `virtual: true`) to lock in the
|
|
128
|
+
// discriminator against future refactors.
|
|
129
|
+
;(config.lists.VirtualLabel.fields.displayName as { virtual?: boolean }).virtual = undefined
|
|
130
|
+
const delegate = makeDelegate([{ id: 'v1', displayName: 'Computed One' }])
|
|
131
|
+
const context = makeContext({ virtualLabel: delegate })
|
|
132
|
+
|
|
133
|
+
await getRelationshipOptions(context, config, 'VirtualLabel', { search: 'One' })
|
|
134
|
+
|
|
135
|
+
const call = delegate.findMany.mock.calls[0][0] as Record<string, unknown>
|
|
136
|
+
expect(call.orderBy).toEqual({ id: 'asc' })
|
|
137
|
+
})
|
|
138
|
+
|
|
102
139
|
it('unions currently-selected ids even when beyond take / not matching search', async () => {
|
|
103
140
|
// The bounded/search-scoped query only returns a1 (mimicking take:1 + search).
|
|
104
141
|
const primaryDelegate = makeDelegate([authors[0]])
|
|
@@ -44,13 +44,21 @@ export async function getRelationshipOptions(
|
|
|
44
44
|
})
|
|
45
45
|
|
|
46
46
|
const { search, take = DEFAULT_TAKE, selectedIds = [] } = args
|
|
47
|
-
const labelFieldConfig = relatedListConfig.fields[labelField] as
|
|
47
|
+
const labelFieldConfig = relatedListConfig.fields[labelField] as
|
|
48
|
+
{ type?: string; virtual?: boolean } | undefined
|
|
48
49
|
const where =
|
|
49
50
|
search && labelFieldConfig?.type === 'text' ? { [labelField]: { contains: search } } : undefined
|
|
50
51
|
|
|
52
|
+
// Virtual/computed label fields (resolved at read time via `resolveOutput`)
|
|
53
|
+
// have no backing database column, so passing them into `orderBy` fails
|
|
54
|
+
// Prisma validation and 500s the request. Fall back to ordering by `id` —
|
|
55
|
+
// always a real, orderable column — whenever the label field is virtual.
|
|
56
|
+
const isVirtualLabel = labelFieldConfig?.type === 'virtual' || labelFieldConfig?.virtual === true
|
|
57
|
+
const orderBy: Record<string, 'asc'> = isVirtualLabel ? { id: 'asc' } : { [labelField]: 'asc' }
|
|
58
|
+
|
|
51
59
|
const primary = await runQuery(context, relatedListKey, fragment, {
|
|
52
60
|
where,
|
|
53
|
-
orderBy
|
|
61
|
+
orderBy,
|
|
54
62
|
take,
|
|
55
63
|
})
|
|
56
64
|
|
|
@@ -681,8 +681,8 @@ describe('Plugin Engine', () => {
|
|
|
681
681
|
})
|
|
682
682
|
})
|
|
683
683
|
|
|
684
|
-
describe('access control
|
|
685
|
-
test('
|
|
684
|
+
describe('access control guardrail (ADR-0013)', () => {
|
|
685
|
+
test('throws when an extension carries operation access for a pre-existing list', async () => {
|
|
686
686
|
const plugin: Plugin = {
|
|
687
687
|
name: 'test-plugin',
|
|
688
688
|
init: async (context) => {
|
|
@@ -711,23 +711,19 @@ describe('Plugin Engine', () => {
|
|
|
711
711
|
plugins: [plugin],
|
|
712
712
|
}
|
|
713
713
|
|
|
714
|
-
|
|
715
|
-
|
|
716
|
-
|
|
717
|
-
expect(result.lists.Post.access?.operation?.create).toBeDefined()
|
|
718
|
-
expect(result.lists.Post.access?.operation?.update).toBeDefined()
|
|
714
|
+
await expect(executePlugins(config)).rejects.toThrow(
|
|
715
|
+
'Plugin "test-plugin" tried to set operation-level access while extending list "Post"',
|
|
716
|
+
)
|
|
719
717
|
})
|
|
720
718
|
|
|
721
|
-
test('
|
|
722
|
-
const pluginQuery = vi.fn(() => false)
|
|
723
|
-
|
|
719
|
+
test('extension attempting to override existing access throws instead of silently winning', async () => {
|
|
724
720
|
const plugin: Plugin = {
|
|
725
721
|
name: 'test-plugin',
|
|
726
722
|
init: async (context) => {
|
|
727
723
|
context.extendList('Post', {
|
|
728
724
|
access: {
|
|
729
725
|
operation: {
|
|
730
|
-
query:
|
|
726
|
+
query: () => false,
|
|
731
727
|
},
|
|
732
728
|
},
|
|
733
729
|
})
|
|
@@ -750,10 +746,67 @@ describe('Plugin Engine', () => {
|
|
|
750
746
|
plugins: [plugin],
|
|
751
747
|
}
|
|
752
748
|
|
|
749
|
+
await expect(executePlugins(config)).rejects.toThrow('Post')
|
|
750
|
+
|
|
751
|
+
// The host's access is untouched — the throw happens before any mutation.
|
|
752
|
+
expect(config.lists.Post.access?.operation?.query).toBe(originalQuery)
|
|
753
|
+
})
|
|
754
|
+
|
|
755
|
+
test('extension providing only fields/hooks for a pre-existing list leaves its access untouched', async () => {
|
|
756
|
+
const originalQuery = vi.fn(() => true)
|
|
757
|
+
|
|
758
|
+
const plugin: Plugin = {
|
|
759
|
+
name: 'test-plugin',
|
|
760
|
+
init: async (context) => {
|
|
761
|
+
context.extendList('Post', {
|
|
762
|
+
fields: { views: integer() },
|
|
763
|
+
})
|
|
764
|
+
},
|
|
765
|
+
}
|
|
766
|
+
|
|
767
|
+
const config: OpenSaasConfig = {
|
|
768
|
+
lists: {
|
|
769
|
+
Post: {
|
|
770
|
+
fields: { title: text() },
|
|
771
|
+
access: {
|
|
772
|
+
operation: {
|
|
773
|
+
query: originalQuery,
|
|
774
|
+
},
|
|
775
|
+
},
|
|
776
|
+
},
|
|
777
|
+
},
|
|
778
|
+
plugins: [plugin],
|
|
779
|
+
}
|
|
780
|
+
|
|
781
|
+
const result = await executePlugins(config)
|
|
782
|
+
|
|
783
|
+
expect(result.lists.Post.fields.views).toBeDefined()
|
|
784
|
+
expect(result.lists.Post.access?.operation?.query).toBe(originalQuery)
|
|
785
|
+
})
|
|
786
|
+
|
|
787
|
+
test('a plugin creating a new list may still set its access via addList', async () => {
|
|
788
|
+
const plugin: Plugin = {
|
|
789
|
+
name: 'test-plugin',
|
|
790
|
+
init: async (context) => {
|
|
791
|
+
context.addList('Widget', {
|
|
792
|
+
fields: { name: text() },
|
|
793
|
+
access: {
|
|
794
|
+
operation: {
|
|
795
|
+
query: () => true,
|
|
796
|
+
},
|
|
797
|
+
},
|
|
798
|
+
})
|
|
799
|
+
},
|
|
800
|
+
}
|
|
801
|
+
|
|
802
|
+
const config: OpenSaasConfig = {
|
|
803
|
+
lists: {},
|
|
804
|
+
plugins: [plugin],
|
|
805
|
+
}
|
|
806
|
+
|
|
753
807
|
const result = await executePlugins(config)
|
|
754
808
|
|
|
755
|
-
|
|
756
|
-
expect(result.lists.Post.access?.operation?.query).toBe(pluginQuery)
|
|
809
|
+
expect(result.lists.Widget.access?.operation?.query).toBeDefined()
|
|
757
810
|
})
|
|
758
811
|
})
|
|
759
812
|
|
package/tests/sudo.test.ts
CHANGED
|
@@ -3,6 +3,8 @@ import { getContext } from '../src/context/index.js'
|
|
|
3
3
|
import { config, list } from '../src/config/index.js'
|
|
4
4
|
import { text, integer, relationship } from '../src/fields/index.js'
|
|
5
5
|
import type { PrismaClient } from '@prisma/client'
|
|
6
|
+
import type { Plugin } from '../src/config/types.js'
|
|
7
|
+
import type { AccessContext } from '../src/access/types.js'
|
|
6
8
|
|
|
7
9
|
describe('Sudo Context', () => {
|
|
8
10
|
// Mock Prisma client
|
|
@@ -624,4 +626,51 @@ describe('Sudo Context', () => {
|
|
|
624
626
|
).rejects.toThrow('Access denied')
|
|
625
627
|
})
|
|
626
628
|
})
|
|
629
|
+
|
|
630
|
+
describe('Plugin runtime sudo access', () => {
|
|
631
|
+
// A plugin's `runtime(context, sudo)` factory receives a `sudo` helper as a
|
|
632
|
+
// plain second argument — NOT a method on `AccessContext` itself. A
|
|
633
|
+
// self-referential `sudo(): AccessContext` field on that shared, widely
|
|
634
|
+
// instantiated interface was found to break TypeScript's structural
|
|
635
|
+
// checking of unrelated generated Prisma types in a downstream app
|
|
636
|
+
// (nullable JSON `CreateInput` fields); passing it as a separate argument
|
|
637
|
+
// avoids that recursion while still giving plugins (e.g. the auth
|
|
638
|
+
// plugin's getUser/getCurrentUser, see ADR-0013) an access-bypassing
|
|
639
|
+
// identity-lookup path.
|
|
640
|
+
it('passes a working sudo() as the second argument to plugin.runtime()', async () => {
|
|
641
|
+
let capturedSudo: (() => AccessContext<typeof mockPrisma>) | undefined
|
|
642
|
+
|
|
643
|
+
const plugin: Plugin = {
|
|
644
|
+
name: 'test-plugin',
|
|
645
|
+
init: async () => {},
|
|
646
|
+
runtime: (_context, sudo) => {
|
|
647
|
+
capturedSudo = sudo as () => AccessContext<typeof mockPrisma>
|
|
648
|
+
return {}
|
|
649
|
+
},
|
|
650
|
+
}
|
|
651
|
+
|
|
652
|
+
const pluginConfig = await config({
|
|
653
|
+
db: { provider: 'sqlite' },
|
|
654
|
+
plugins: [plugin],
|
|
655
|
+
lists: {
|
|
656
|
+
Post: list({
|
|
657
|
+
fields: { title: text({ validation: { isRequired: true } }) },
|
|
658
|
+
// Closed list: only sudo() should be able to read from it.
|
|
659
|
+
access: { operation: { query: () => false } },
|
|
660
|
+
}),
|
|
661
|
+
},
|
|
662
|
+
})
|
|
663
|
+
|
|
664
|
+
mockPrisma.post.findMany.mockResolvedValue([{ id: '1', title: 'Test Post' }])
|
|
665
|
+
|
|
666
|
+
getContext(pluginConfig, mockPrisma, null)
|
|
667
|
+
|
|
668
|
+
expect(capturedSudo).toBeTypeOf('function')
|
|
669
|
+
|
|
670
|
+
const sudoContext = capturedSudo!()
|
|
671
|
+
const sudoResult = await sudoContext.db.post.findMany()
|
|
672
|
+
expect(sudoResult).toHaveLength(1)
|
|
673
|
+
expect(sudoResult[0].title).toBe('Test Post')
|
|
674
|
+
})
|
|
675
|
+
})
|
|
627
676
|
})
|