@opensaas/stack-cli 0.39.2 → 0.41.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/dist/commands/dev.d.ts.map +1 -1
- package/dist/commands/dev.js +0 -5
- package/dist/commands/dev.js.map +1 -1
- package/dist/commands/generate.d.ts +0 -6
- package/dist/commands/generate.d.ts.map +1 -1
- package/dist/commands/generate.js +4 -26
- package/dist/commands/generate.js.map +1 -1
- package/dist/commands/init.d.ts +1 -6
- package/dist/commands/init.d.ts.map +1 -1
- package/dist/commands/init.js +1 -7
- package/dist/commands/init.js.map +1 -1
- package/dist/commands/mcp.d.ts.map +1 -1
- package/dist/commands/mcp.js +0 -2
- package/dist/commands/mcp.js.map +1 -1
- package/dist/commands/migrate.d.ts +2 -17
- package/dist/commands/migrate.d.ts.map +1 -1
- package/dist/commands/migrate.js +3 -59
- package/dist/commands/migrate.js.map +1 -1
- package/dist/generator/context.d.ts +0 -12
- package/dist/generator/context.d.ts.map +1 -1
- package/dist/generator/context.js +5 -26
- package/dist/generator/context.js.map +1 -1
- package/dist/generator/extension.d.ts +0 -9
- package/dist/generator/extension.d.ts.map +1 -1
- package/dist/generator/extension.js +5 -37
- package/dist/generator/extension.js.map +1 -1
- package/dist/generator/lists.d.ts +2 -5
- package/dist/generator/lists.d.ts.map +1 -1
- package/dist/generator/lists.js +2 -12
- package/dist/generator/lists.js.map +1 -1
- package/dist/generator/output-paths.d.ts +2 -12
- package/dist/generator/output-paths.d.ts.map +1 -1
- package/dist/generator/output-paths.js +2 -12
- package/dist/generator/output-paths.js.map +1 -1
- package/dist/generator/plugin-types.d.ts +0 -6
- package/dist/generator/plugin-types.d.ts.map +1 -1
- package/dist/generator/plugin-types.js +3 -28
- package/dist/generator/plugin-types.js.map +1 -1
- package/dist/generator/prisma-config.d.ts +6 -25
- package/dist/generator/prisma-config.d.ts.map +1 -1
- package/dist/generator/prisma-config.js +6 -27
- package/dist/generator/prisma-config.js.map +1 -1
- package/dist/generator/prisma.d.ts +0 -8
- package/dist/generator/prisma.d.ts.map +1 -1
- package/dist/generator/prisma.js +24 -63
- package/dist/generator/prisma.js.map +1 -1
- package/dist/generator/types.d.ts +0 -6
- package/dist/generator/types.d.ts.map +1 -1
- package/dist/generator/types.js +25 -149
- package/dist/generator/types.js.map +1 -1
- package/dist/mcp/lib/documentation-provider.d.ts +0 -36
- package/dist/mcp/lib/documentation-provider.d.ts.map +1 -1
- package/dist/mcp/lib/documentation-provider.js +1 -49
- package/dist/mcp/lib/documentation-provider.js.map +1 -1
- package/dist/mcp/lib/features/catalog.d.ts +0 -15
- package/dist/mcp/lib/features/catalog.d.ts.map +1 -1
- package/dist/mcp/lib/features/catalog.js +0 -15
- package/dist/mcp/lib/features/catalog.js.map +1 -1
- package/dist/mcp/lib/generators/feature-generator.d.ts +3 -24
- package/dist/mcp/lib/generators/feature-generator.d.ts.map +1 -1
- package/dist/mcp/lib/generators/feature-generator.js +4 -38
- package/dist/mcp/lib/generators/feature-generator.js.map +1 -1
- package/dist/mcp/lib/types.d.ts +0 -3
- package/dist/mcp/lib/types.d.ts.map +1 -1
- package/dist/mcp/lib/types.js +0 -3
- package/dist/mcp/lib/types.js.map +1 -1
- package/dist/mcp/lib/wizards/migration-wizard.d.ts +0 -70
- package/dist/mcp/lib/wizards/migration-wizard.d.ts.map +1 -1
- package/dist/mcp/lib/wizards/migration-wizard.js +1 -87
- package/dist/mcp/lib/wizards/migration-wizard.js.map +1 -1
- package/dist/mcp/lib/wizards/wizard-engine.d.ts +0 -39
- package/dist/mcp/lib/wizards/wizard-engine.d.ts.map +1 -1
- package/dist/mcp/lib/wizards/wizard-engine.js +0 -49
- package/dist/mcp/lib/wizards/wizard-engine.js.map +1 -1
- package/dist/mcp/server/index.d.ts +0 -3
- package/dist/mcp/server/index.d.ts.map +1 -1
- package/dist/mcp/server/index.js +1 -9
- package/dist/mcp/server/index.js.map +1 -1
- package/dist/mcp/server/stack-mcp-server.d.ts +0 -42
- package/dist/mcp/server/stack-mcp-server.d.ts.map +1 -1
- package/dist/mcp/server/stack-mcp-server.js +1 -47
- package/dist/mcp/server/stack-mcp-server.js.map +1 -1
- package/dist/migration/generators/migration-generator.d.ts +0 -55
- package/dist/migration/generators/migration-generator.d.ts.map +1 -1
- package/dist/migration/generators/migration-generator.js +3 -97
- package/dist/migration/generators/migration-generator.js.map +1 -1
- package/dist/migration/introspectors/index.d.ts +0 -6
- package/dist/migration/introspectors/index.d.ts.map +1 -1
- package/dist/migration/introspectors/index.js +0 -6
- package/dist/migration/introspectors/index.js.map +1 -1
- package/dist/migration/introspectors/keystone-introspector.d.ts +0 -27
- package/dist/migration/introspectors/keystone-introspector.d.ts.map +1 -1
- package/dist/migration/introspectors/keystone-introspector.js +0 -40
- package/dist/migration/introspectors/keystone-introspector.js.map +1 -1
- package/dist/migration/introspectors/nextjs-introspector.d.ts +0 -36
- package/dist/migration/introspectors/nextjs-introspector.d.ts.map +1 -1
- package/dist/migration/introspectors/nextjs-introspector.js +0 -39
- package/dist/migration/introspectors/nextjs-introspector.js.map +1 -1
- package/dist/migration/introspectors/prisma-introspector.d.ts +0 -30
- package/dist/migration/introspectors/prisma-introspector.d.ts.map +1 -1
- package/dist/migration/introspectors/prisma-introspector.js +1 -42
- package/dist/migration/introspectors/prisma-introspector.js.map +1 -1
- package/dist/migration/types.d.ts +1 -5
- package/dist/migration/types.d.ts.map +1 -1
- package/dist/migration/types.js +0 -3
- package/dist/migration/types.js.map +1 -1
- package/package.json +2 -2
package/dist/generator/types.js
CHANGED
|
@@ -1,49 +1,34 @@
|
|
|
1
1
|
import * as fs from 'fs';
|
|
2
2
|
import * as path from 'path';
|
|
3
3
|
import { resolveListTimestamps } from './prisma.js';
|
|
4
|
-
/**
|
|
5
|
-
* Map OpenSaas field types to TypeScript types
|
|
6
|
-
*/
|
|
7
4
|
function mapFieldTypeToTypeScript(field) {
|
|
8
|
-
// Relationships are handled separately
|
|
9
5
|
if (field.type === 'relationship') {
|
|
10
6
|
return null;
|
|
11
7
|
}
|
|
12
|
-
// Use field's own TypeScript type generator if available
|
|
13
8
|
if (field.getTypeScriptType) {
|
|
14
9
|
const result = field.getTypeScriptType();
|
|
15
10
|
return result.type;
|
|
16
11
|
}
|
|
17
|
-
// Fallback for fields without generator methods
|
|
18
12
|
throw new Error(`Field type "${field.type}" does not implement getTypeScriptType method`);
|
|
19
13
|
}
|
|
20
|
-
/**
|
|
21
|
-
* Check if a field is optional in the type
|
|
22
|
-
*/
|
|
23
14
|
function isFieldOptional(field) {
|
|
24
15
|
// Relationships are always nullable
|
|
25
16
|
if (field.type === 'relationship') {
|
|
26
17
|
return true;
|
|
27
18
|
}
|
|
28
|
-
// Use field's own TypeScript type generator if available
|
|
29
19
|
if (field.getTypeScriptType) {
|
|
30
20
|
const result = field.getTypeScriptType();
|
|
31
21
|
return result.optional;
|
|
32
22
|
}
|
|
33
|
-
// Fallback: assume optional
|
|
34
23
|
return true;
|
|
35
24
|
}
|
|
36
|
-
/**
|
|
37
|
-
* Get names of virtual fields in a list
|
|
38
|
-
*/
|
|
39
25
|
function getVirtualFieldNames(fields) {
|
|
40
26
|
return Object.entries(fields)
|
|
41
27
|
.filter(([_, config]) => config.type === 'virtual')
|
|
42
28
|
.map(([name, _]) => name);
|
|
43
29
|
}
|
|
44
30
|
/**
|
|
45
|
-
*
|
|
46
|
-
* This is intersected with Prisma's GetPayload to add virtual fields to query results
|
|
31
|
+
* Intersected with Prisma's GetPayload to add virtual fields to query results.
|
|
47
32
|
*/
|
|
48
33
|
function generateVirtualFieldsType(listName, fields) {
|
|
49
34
|
const lines = [];
|
|
@@ -61,7 +46,6 @@ function generateVirtualFieldsType(listName, fields) {
|
|
|
61
46
|
const nullability = optional ? ' | null' : '';
|
|
62
47
|
lines.push(` ${fieldName}: ${tsType}${nullability}`);
|
|
63
48
|
}
|
|
64
|
-
// If no virtual fields, make it an empty object
|
|
65
49
|
if (virtualFields.length === 0) {
|
|
66
50
|
lines.push(' // No virtual fields defined');
|
|
67
51
|
}
|
|
@@ -69,8 +53,7 @@ function generateVirtualFieldsType(listName, fields) {
|
|
|
69
53
|
return lines.join('\n');
|
|
70
54
|
}
|
|
71
55
|
/**
|
|
72
|
-
*
|
|
73
|
-
* This replaces Prisma's base types with transformed types (e.g., string -> HashedPassword)
|
|
56
|
+
* Replaces Prisma's base types with transformed types (e.g., string -> HashedPassword).
|
|
74
57
|
*/
|
|
75
58
|
function generateTransformedFieldsType(listName, fields) {
|
|
76
59
|
const lines = [];
|
|
@@ -87,7 +70,6 @@ function generateTransformedFieldsType(listName, fields) {
|
|
|
87
70
|
lines.push(` ${fieldName}: ${fieldConfig.resultExtension.outputType}${nullability}`);
|
|
88
71
|
}
|
|
89
72
|
}
|
|
90
|
-
// If no transformed fields, make it an empty object
|
|
91
73
|
if (transformedFields.length === 0) {
|
|
92
74
|
lines.push(' // No transformed fields defined');
|
|
93
75
|
}
|
|
@@ -95,8 +77,8 @@ function generateTransformedFieldsType(listName, fields) {
|
|
|
95
77
|
return lines.join('\n');
|
|
96
78
|
}
|
|
97
79
|
/**
|
|
98
|
-
*
|
|
99
|
-
*
|
|
80
|
+
* Kept for backwards compatibility — `CustomDB` itself uses Prisma's
|
|
81
|
+
* GetPayload + VirtualFields instead.
|
|
100
82
|
*/
|
|
101
83
|
function generateModelOutputType(listName,
|
|
102
84
|
// ListConfig is generic over per-list TypeInfo; we only read `db`/`fields`, which are
|
|
@@ -110,7 +92,6 @@ listConfig, dbConfig, isSingleton) {
|
|
|
110
92
|
// Keystone 6, so the TypeScript id type is `number` rather than `string`.
|
|
111
93
|
lines.push(isSingleton ? ' id: number' : ' id: string');
|
|
112
94
|
for (const [fieldName, fieldConfig] of Object.entries(fields)) {
|
|
113
|
-
// Skip virtual fields - they're in VirtualFields type
|
|
114
95
|
if (fieldConfig.type === 'virtual')
|
|
115
96
|
continue;
|
|
116
97
|
if (fieldConfig.type === 'relationship') {
|
|
@@ -127,7 +108,7 @@ listConfig, dbConfig, isSingleton) {
|
|
|
127
108
|
else {
|
|
128
109
|
const tsType = mapFieldTypeToTypeScript(fieldConfig);
|
|
129
110
|
if (!tsType)
|
|
130
|
-
continue;
|
|
111
|
+
continue;
|
|
131
112
|
const optional = isFieldOptional(fieldConfig);
|
|
132
113
|
const nullability = optional ? ' | null' : '';
|
|
133
114
|
lines.push(` ${fieldName}: ${tsType}${nullability}`);
|
|
@@ -142,12 +123,9 @@ listConfig, dbConfig, isSingleton) {
|
|
|
142
123
|
if (timestamps.updatedAt) {
|
|
143
124
|
lines.push(' updatedAt: Date');
|
|
144
125
|
}
|
|
145
|
-
lines.push('} & ' + listName + 'VirtualFields');
|
|
126
|
+
lines.push('} & ' + listName + 'VirtualFields');
|
|
146
127
|
return lines.join('\n');
|
|
147
128
|
}
|
|
148
|
-
/**
|
|
149
|
-
* Generate convenience type alias (List = ListOutput)
|
|
150
|
-
*/
|
|
151
129
|
function generateModelTypeAlias(listName) {
|
|
152
130
|
return `export type ${listName} = ${listName}Output`;
|
|
153
131
|
}
|
|
@@ -194,15 +172,11 @@ function renderScalarInputMember(fieldName, fieldConfig, forCreate, indent) {
|
|
|
194
172
|
}
|
|
195
173
|
return `${indent}${fieldName}${optional}: ${tsType}${nullability}`;
|
|
196
174
|
}
|
|
197
|
-
/**
|
|
198
|
-
* Generate CreateInput type
|
|
199
|
-
*/
|
|
200
175
|
function generateCreateInputType(listName, fields) {
|
|
201
176
|
const lines = [];
|
|
202
177
|
lines.push(`export type ${listName}CreateInput = {`);
|
|
203
178
|
for (const [fieldName, fieldConfig] of Object.entries(fields)) {
|
|
204
|
-
//
|
|
205
|
-
// Virtual fields with resolveInput hooks handle side effects but don't store data
|
|
179
|
+
// Virtual fields with resolveInput hooks can handle side effects but don't store data.
|
|
206
180
|
if (fieldConfig.virtual) {
|
|
207
181
|
continue;
|
|
208
182
|
}
|
|
@@ -220,7 +194,7 @@ function generateCreateInputType(listName, fields) {
|
|
|
220
194
|
// optionality + `| null` nullability for nullable scalars.
|
|
221
195
|
const member = renderScalarInputMember(fieldName, fieldConfig, true, ' ');
|
|
222
196
|
if (!member)
|
|
223
|
-
continue;
|
|
197
|
+
continue;
|
|
224
198
|
lines.push(member);
|
|
225
199
|
}
|
|
226
200
|
}
|
|
@@ -358,9 +332,6 @@ function buildWriteInputOverride(prismaBase, fields, forCreate) {
|
|
|
358
332
|
}
|
|
359
333
|
return `${base} & {\n${members.join('\n')}\n }`;
|
|
360
334
|
}
|
|
361
|
-
/**
|
|
362
|
-
* Generate UpdateInput type
|
|
363
|
-
*/
|
|
364
335
|
function generateUpdateInputType(listName, fields) {
|
|
365
336
|
const lines = [];
|
|
366
337
|
lines.push(`export type ${listName}UpdateInput = {`);
|
|
@@ -384,25 +355,16 @@ function generateUpdateInputType(listName, fields) {
|
|
|
384
355
|
// update (always optional) with `| null` nullability for nullable scalars.
|
|
385
356
|
const member = renderScalarInputMember(fieldName, fieldConfig, false, ' ');
|
|
386
357
|
if (!member)
|
|
387
|
-
continue;
|
|
358
|
+
continue;
|
|
388
359
|
lines.push(member);
|
|
389
360
|
}
|
|
390
361
|
}
|
|
391
362
|
lines.push('}');
|
|
392
363
|
return lines.join('\n');
|
|
393
364
|
}
|
|
394
|
-
/**
|
|
395
|
-
* Generate WhereInput type by re-exporting Prisma's native WhereInput
|
|
396
|
-
* This ensures compatibility with all Prisma versions and includes all filter operators
|
|
397
|
-
*/
|
|
398
365
|
function generateWhereInputType(listName, _fields) {
|
|
399
|
-
// Simply re-export Prisma's generated WhereInput type
|
|
400
|
-
// Prisma already generates comprehensive WhereInput types with all filter operators
|
|
401
366
|
return `export type ${listName}WhereInput = Prisma.${listName}WhereInput`;
|
|
402
367
|
}
|
|
403
|
-
/**
|
|
404
|
-
* Generate hook types that reference Prisma input types
|
|
405
|
-
*/
|
|
406
368
|
function generateHookTypes(listName) {
|
|
407
369
|
const lines = [];
|
|
408
370
|
lines.push(`/**`);
|
|
@@ -446,10 +408,6 @@ function generateHookTypes(listName) {
|
|
|
446
408
|
lines.push(`}`);
|
|
447
409
|
return lines.join('\n');
|
|
448
410
|
}
|
|
449
|
-
/**
|
|
450
|
-
* Generate Select type that includes virtual fields
|
|
451
|
-
* Extends Prisma's Select type with virtual field selection support and nested relationship overrides
|
|
452
|
-
*/
|
|
453
411
|
function generateSelectType(listName, fields) {
|
|
454
412
|
const virtualFields = getVirtualFieldNames(fields);
|
|
455
413
|
const relationshipFields = Object.entries(fields)
|
|
@@ -459,7 +417,6 @@ function generateSelectType(listName, fields) {
|
|
|
459
417
|
targetList: config.ref.split('.')[0],
|
|
460
418
|
}));
|
|
461
419
|
if (virtualFields.length === 0 && relationshipFields.length === 0) {
|
|
462
|
-
// No virtual fields and no relationships - just re-export Prisma type
|
|
463
420
|
return `/**
|
|
464
421
|
* Select type for ${listName}
|
|
465
422
|
* No virtual fields defined, uses Prisma's Select type directly
|
|
@@ -467,13 +424,11 @@ function generateSelectType(listName, fields) {
|
|
|
467
424
|
export type ${listName}Select = Prisma.${listName}Select`;
|
|
468
425
|
}
|
|
469
426
|
const lines = [];
|
|
470
|
-
// Add virtual field properties
|
|
471
427
|
if (virtualFields.length > 0) {
|
|
472
428
|
virtualFields.forEach((name) => {
|
|
473
429
|
lines.push(` ${name}?: boolean`);
|
|
474
430
|
});
|
|
475
431
|
}
|
|
476
|
-
// Override relationship properties to use custom DefaultArgs
|
|
477
432
|
if (relationshipFields.length > 0) {
|
|
478
433
|
relationshipFields.forEach(({ name, targetList }) => {
|
|
479
434
|
lines.push(` ${name}?: boolean | ${targetList}DefaultArgs`);
|
|
@@ -508,38 +463,30 @@ ${lines.join('\n')}
|
|
|
508
463
|
}`;
|
|
509
464
|
}
|
|
510
465
|
/**
|
|
511
|
-
*
|
|
512
|
-
*
|
|
513
|
-
* Note: Only generates Include type if the list has relationship fields,
|
|
514
|
-
* since Prisma only generates Include types for models with relations
|
|
466
|
+
* Only generates an Include type if the list has relationship fields — Prisma
|
|
467
|
+
* itself only generates Include types for models with relations.
|
|
515
468
|
*/
|
|
516
469
|
function generateIncludeType(listName, fields) {
|
|
517
|
-
// Check if list has any relationship fields
|
|
518
470
|
const relationshipFields = Object.entries(fields)
|
|
519
471
|
.filter(([_, config]) => config.type === 'relationship')
|
|
520
472
|
.map(([name, config]) => ({
|
|
521
473
|
name,
|
|
522
474
|
targetList: config.ref.split('.')[0],
|
|
523
475
|
}));
|
|
524
|
-
// Prisma only generates Include types for models with relationships
|
|
525
|
-
// If there are no relationships, don't generate an Include type
|
|
526
476
|
if (relationshipFields.length === 0) {
|
|
527
477
|
return null;
|
|
528
478
|
}
|
|
529
479
|
const virtualFields = getVirtualFieldNames(fields);
|
|
530
480
|
const lines = [];
|
|
531
|
-
// Add virtual field properties
|
|
532
481
|
if (virtualFields.length > 0) {
|
|
533
482
|
virtualFields.forEach((name) => {
|
|
534
483
|
lines.push(` ${name}?: boolean`);
|
|
535
484
|
});
|
|
536
485
|
}
|
|
537
|
-
// Override relationship properties to use custom DefaultArgs
|
|
538
486
|
relationshipFields.forEach(({ name, targetList }) => {
|
|
539
487
|
lines.push(` ${name}?: boolean | ${targetList}DefaultArgs`);
|
|
540
488
|
});
|
|
541
489
|
if (lines.length === 0) {
|
|
542
|
-
// No virtual fields - just re-export Prisma type
|
|
543
490
|
return `/**
|
|
544
491
|
* Include type for ${listName}
|
|
545
492
|
* No virtual fields defined, uses Prisma's Include type directly
|
|
@@ -567,10 +514,9 @@ ${lines.join('\n')}
|
|
|
567
514
|
}`;
|
|
568
515
|
}
|
|
569
516
|
/**
|
|
570
|
-
*
|
|
571
|
-
*
|
|
572
|
-
*
|
|
573
|
-
* Even lists without virtual fields need this to support nested relations with virtual fields
|
|
517
|
+
* Always generated, even for lists without virtual fields — CustomDB type
|
|
518
|
+
* signatures stay consistent, and lists without their own virtual fields
|
|
519
|
+
* still need this to support nested relations that have them.
|
|
574
520
|
*/
|
|
575
521
|
function generateGetPayloadType(listName, fields) {
|
|
576
522
|
const virtualFields = getVirtualFieldNames(fields);
|
|
@@ -582,7 +528,6 @@ function generateGetPayloadType(listName, fields) {
|
|
|
582
528
|
// payload: only the assembled logical field (added back via TransformedFields)
|
|
583
529
|
// is exposed. Collect those raw column names for omission. See ADR-0006.
|
|
584
530
|
const multiColumnRawNames = Object.entries(fields).flatMap(([name, config]) => config.getColumnNames ? config.getColumnNames(name) : []);
|
|
585
|
-
// Get relationship fields to override with custom GetPayload types
|
|
586
531
|
const relationshipFields = Object.entries(fields)
|
|
587
532
|
.filter(([_, config]) => config.type === 'relationship')
|
|
588
533
|
.map(([name, config]) => ({
|
|
@@ -591,7 +536,6 @@ function generateGetPayloadType(listName, fields) {
|
|
|
591
536
|
many: !!config.many,
|
|
592
537
|
}));
|
|
593
538
|
const lines = [];
|
|
594
|
-
// Build documentation
|
|
595
539
|
lines.push(`/**`);
|
|
596
540
|
if (virtualFields.length > 0 || transformedFieldNames.length > 0) {
|
|
597
541
|
lines.push(` * GetPayload type for ${listName} with virtual and transformed field support`);
|
|
@@ -634,11 +578,9 @@ function generateGetPayloadType(listName, fields) {
|
|
|
634
578
|
else {
|
|
635
579
|
lines.push(` Prisma.${listName}GetPayload<${prismaT}> &`);
|
|
636
580
|
}
|
|
637
|
-
// Add transformed fields back
|
|
638
581
|
if (transformedFieldNames.length > 0) {
|
|
639
582
|
lines.push(` ${listName}TransformedFields &`);
|
|
640
583
|
}
|
|
641
|
-
// Add relationship fields back with custom GetPayload types
|
|
642
584
|
if (relationshipFields.length > 0) {
|
|
643
585
|
lines.push(` {`);
|
|
644
586
|
for (const rel of relationshipFields) {
|
|
@@ -669,7 +611,6 @@ function generateGetPayloadType(listName, fields) {
|
|
|
669
611
|
}
|
|
670
612
|
lines.push(` } &`);
|
|
671
613
|
}
|
|
672
|
-
// Build the virtual fields conditional type
|
|
673
614
|
if (virtualFields.length > 0) {
|
|
674
615
|
lines.push(` (`);
|
|
675
616
|
lines.push(` T extends { select: any }`);
|
|
@@ -696,15 +637,10 @@ function generateGetPayloadType(listName, fields) {
|
|
|
696
637
|
lines.push(` )`);
|
|
697
638
|
}
|
|
698
639
|
else {
|
|
699
|
-
// No virtual fields, just use empty object
|
|
700
640
|
lines.push(` {}`);
|
|
701
641
|
}
|
|
702
642
|
return lines.join('\n');
|
|
703
643
|
}
|
|
704
|
-
/**
|
|
705
|
-
* Generate DefaultArgs type for nested relationship selections
|
|
706
|
-
* This type is used when selecting relationships to enable custom Select/Include types
|
|
707
|
-
*/
|
|
708
644
|
function generateDefaultArgsType(listName, fields) {
|
|
709
645
|
const hasRelationships = Object.values(fields).some((field) => field.type === 'relationship');
|
|
710
646
|
if (hasRelationships) {
|
|
@@ -727,9 +663,6 @@ export type ${listName}DefaultArgs = {
|
|
|
727
663
|
}`;
|
|
728
664
|
}
|
|
729
665
|
}
|
|
730
|
-
/**
|
|
731
|
-
* Generate custom FindUniqueArgs type that uses our extended Select/Include
|
|
732
|
-
*/
|
|
733
666
|
function generateFindUniqueArgsType(listName, fields) {
|
|
734
667
|
const hasRelationships = Object.values(fields).some((field) => field.type === 'relationship');
|
|
735
668
|
if (hasRelationships) {
|
|
@@ -752,9 +685,6 @@ export type ${listName}FindUniqueArgs = Omit<Prisma.${listName}FindUniqueArgs, '
|
|
|
752
685
|
}`;
|
|
753
686
|
}
|
|
754
687
|
}
|
|
755
|
-
/**
|
|
756
|
-
* Generate custom FindManyArgs type that uses our extended Select/Include
|
|
757
|
-
*/
|
|
758
688
|
function generateFindManyArgsType(listName, fields) {
|
|
759
689
|
const hasRelationships = Object.values(fields).some((field) => field.type === 'relationship');
|
|
760
690
|
if (hasRelationships) {
|
|
@@ -777,9 +707,6 @@ export type ${listName}FindManyArgs = Omit<Prisma.${listName}FindManyArgs, 'sele
|
|
|
777
707
|
}`;
|
|
778
708
|
}
|
|
779
709
|
}
|
|
780
|
-
/**
|
|
781
|
-
* Generate custom FindFirstArgs type that uses our extended Select/Include
|
|
782
|
-
*/
|
|
783
710
|
function generateFindFirstArgsType(listName, fields) {
|
|
784
711
|
const hasRelationships = Object.values(fields).some((field) => field.type === 'relationship');
|
|
785
712
|
if (hasRelationships) {
|
|
@@ -803,9 +730,8 @@ export type ${listName}FindFirstArgs = Omit<Prisma.${listName}FindFirstArgs, 'se
|
|
|
803
730
|
}
|
|
804
731
|
}
|
|
805
732
|
/**
|
|
806
|
-
*
|
|
807
|
-
*
|
|
808
|
-
* unique selector; there is exactly one row).
|
|
733
|
+
* The same include/query narrowing as FindUniqueArgs, minus `where` (a
|
|
734
|
+
* singleton has no unique selector; there is exactly one row).
|
|
809
735
|
*/
|
|
810
736
|
function generateGetArgsType(listName, fields) {
|
|
811
737
|
const hasRelationships = Object.values(fields).some((field) => field.type === 'relationship');
|
|
@@ -829,9 +755,6 @@ export type ${listName}GetArgs = {
|
|
|
829
755
|
}`;
|
|
830
756
|
}
|
|
831
757
|
}
|
|
832
|
-
/**
|
|
833
|
-
* Generate custom CreateArgs type that uses our extended Select/Include
|
|
834
|
-
*/
|
|
835
758
|
function generateCreateArgsType(listName, fields) {
|
|
836
759
|
const hasRelationships = Object.values(fields).some((field) => field.type === 'relationship');
|
|
837
760
|
// Narrow scalar `data` members to OpenSaaS types so field-level narrowing
|
|
@@ -860,9 +783,6 @@ export type ${listName}CreateArgs = Omit<Prisma.${listName}CreateArgs, 'select'
|
|
|
860
783
|
}`;
|
|
861
784
|
}
|
|
862
785
|
}
|
|
863
|
-
/**
|
|
864
|
-
* Generate custom UpdateArgs type that uses our extended Select/Include
|
|
865
|
-
*/
|
|
866
786
|
function generateUpdateArgsType(listName, fields) {
|
|
867
787
|
const hasRelationships = Object.values(fields).some((field) => field.type === 'relationship');
|
|
868
788
|
// See generateCreateArgsType: narrow scalar `data` members (#599).
|
|
@@ -889,9 +809,6 @@ export type ${listName}UpdateArgs = Omit<Prisma.${listName}UpdateArgs, 'select'
|
|
|
889
809
|
}`;
|
|
890
810
|
}
|
|
891
811
|
}
|
|
892
|
-
/**
|
|
893
|
-
* Generate custom DeleteArgs type that uses our extended Select/Include
|
|
894
|
-
*/
|
|
895
812
|
function generateDeleteArgsType(listName, fields) {
|
|
896
813
|
const hasRelationships = Object.values(fields).some((field) => field.type === 'relationship');
|
|
897
814
|
if (hasRelationships) {
|
|
@@ -912,9 +829,6 @@ export type ${listName}DeleteArgs = Omit<Prisma.${listName}DeleteArgs, 'select'>
|
|
|
912
829
|
}`;
|
|
913
830
|
}
|
|
914
831
|
}
|
|
915
|
-
/**
|
|
916
|
-
* Generate custom CreateManyArgs type that uses our extended Select/Include
|
|
917
|
-
*/
|
|
918
832
|
function generateCreateManyArgsType(listName, fields) {
|
|
919
833
|
const hasRelationships = Object.values(fields).some((field) => field.type === 'relationship');
|
|
920
834
|
// createMany `data` is an array of scalar inputs; narrow each element (#599).
|
|
@@ -941,9 +855,6 @@ export type ${listName}CreateManyArgs = {
|
|
|
941
855
|
}`;
|
|
942
856
|
}
|
|
943
857
|
}
|
|
944
|
-
/**
|
|
945
|
-
* Generate custom UpdateManyArgs type that uses our extended Select/Include
|
|
946
|
-
*/
|
|
947
858
|
function generateUpdateManyArgsType(listName, fields) {
|
|
948
859
|
const hasRelationships = Object.values(fields).some((field) => field.type === 'relationship');
|
|
949
860
|
// updateMany `data` is a single partial scalar input; narrow it (#599).
|
|
@@ -980,6 +891,10 @@ export type ${listName}UpdateManyArgs = {
|
|
|
980
891
|
* intersection, combined with the self-referential `Context.sudo(): Context`
|
|
981
892
|
* return type, hit `TS2589: Type instantiation is excessively deep` once a
|
|
982
893
|
* schema grew past ~7-8 lists.
|
|
894
|
+
*
|
|
895
|
+
* Every method below is generic over its own `*Args` type, to preserve
|
|
896
|
+
* Prisma's conditional return type together with the virtual-field-aware
|
|
897
|
+
* `GetPayload` — `count` is the only exception, since it returns a plain number.
|
|
983
898
|
*/
|
|
984
899
|
function generateListCrudInterface(listName, isSingleton) {
|
|
985
900
|
const lines = [];
|
|
@@ -987,37 +902,28 @@ function generateListCrudInterface(listName, isSingleton) {
|
|
|
987
902
|
lines.push(` * Access-controlled CRUD methods for ${listName}, with virtual field support.`);
|
|
988
903
|
lines.push(` */`);
|
|
989
904
|
lines.push(`export interface ${listName}Crud {`);
|
|
990
|
-
// findUnique - generic to preserve Prisma's conditional return type with custom Args for virtual field support
|
|
991
905
|
lines.push(` findUnique: <T extends ${listName}FindUniqueArgs>(`);
|
|
992
906
|
lines.push(` args: Prisma.SelectSubset<T, ${listName}FindUniqueArgs>`);
|
|
993
907
|
lines.push(` ) => Promise<${listName}GetPayload<T> | null>`);
|
|
994
|
-
// findFirst - generic to preserve Prisma's conditional return type with custom Args for virtual field support
|
|
995
908
|
lines.push(` findFirst: <T extends ${listName}FindFirstArgs>(`);
|
|
996
909
|
lines.push(` args?: Prisma.SelectSubset<T, ${listName}FindFirstArgs>`);
|
|
997
910
|
lines.push(` ) => Promise<${listName}GetPayload<T> | null>`);
|
|
998
|
-
// findMany - generic to preserve Prisma's conditional return type with custom Args for virtual field support
|
|
999
911
|
lines.push(` findMany: <T extends ${listName}FindManyArgs>(`);
|
|
1000
912
|
lines.push(` args?: Prisma.SelectSubset<T, ${listName}FindManyArgs>`);
|
|
1001
913
|
lines.push(` ) => Promise<Array<${listName}GetPayload<T>>>`);
|
|
1002
|
-
// create - generic to preserve Prisma's conditional return type with custom Args for virtual field support
|
|
1003
914
|
lines.push(` create: <T extends ${listName}CreateArgs>(`);
|
|
1004
915
|
lines.push(` args: Prisma.SelectSubset<T, ${listName}CreateArgs>`);
|
|
1005
916
|
lines.push(` ) => Promise<${listName}GetPayload<T>>`);
|
|
1006
|
-
// update - generic to preserve Prisma's conditional return type with custom Args for virtual field support
|
|
1007
917
|
lines.push(` update: <T extends ${listName}UpdateArgs>(`);
|
|
1008
918
|
lines.push(` args: Prisma.SelectSubset<T, ${listName}UpdateArgs>`);
|
|
1009
919
|
lines.push(` ) => Promise<${listName}GetPayload<T> | null>`);
|
|
1010
|
-
// delete - generic to preserve Prisma's conditional return type with custom Args for virtual field support
|
|
1011
920
|
lines.push(` delete: <T extends ${listName}DeleteArgs>(`);
|
|
1012
921
|
lines.push(` args: Prisma.SelectSubset<T, ${listName}DeleteArgs>`);
|
|
1013
922
|
lines.push(` ) => Promise<${listName}GetPayload<T> | null>`);
|
|
1014
|
-
// count - no changes to return type
|
|
1015
923
|
lines.push(` count: (args?: Prisma.${listName}CountArgs) => Promise<number>`);
|
|
1016
|
-
// createMany - generic to preserve Prisma's conditional return type with custom Args for virtual field support
|
|
1017
924
|
lines.push(` createMany: <T extends ${listName}CreateManyArgs>(`);
|
|
1018
925
|
lines.push(` args: Prisma.SelectSubset<T, ${listName}CreateManyArgs>`);
|
|
1019
926
|
lines.push(` ) => Promise<Array<${listName}GetPayload<T>>>`);
|
|
1020
|
-
// updateMany - generic to preserve Prisma's conditional return type with custom Args for virtual field support
|
|
1021
927
|
lines.push(` updateMany: <T extends ${listName}UpdateManyArgs>(`);
|
|
1022
928
|
lines.push(` args: Prisma.SelectSubset<T, ${listName}UpdateManyArgs>`);
|
|
1023
929
|
lines.push(` ) => Promise<Array<${listName}GetPayload<T>>>`);
|
|
@@ -1043,7 +949,7 @@ function generateListCrudInterface(listName, isSingleton) {
|
|
|
1043
949
|
*/
|
|
1044
950
|
function generateCustomDBType(config) {
|
|
1045
951
|
const lines = [];
|
|
1046
|
-
//
|
|
952
|
+
// db keys to omit from AccessControlledDB before re-adding each list's own {List}Crud.
|
|
1047
953
|
const dbKeys = Object.keys(config.lists).map((listName) => {
|
|
1048
954
|
const dbKey = listName.charAt(0).toLowerCase() + listName.slice(1);
|
|
1049
955
|
return `'${dbKey}'`;
|
|
@@ -1082,7 +988,6 @@ function generateCustomDBType(config) {
|
|
|
1082
988
|
*/
|
|
1083
989
|
function generateContextType(_config) {
|
|
1084
990
|
const lines = [];
|
|
1085
|
-
// Generate BaseContext - minimal interface for services that only need db and session
|
|
1086
991
|
lines.push('/**');
|
|
1087
992
|
lines.push(' * Base context type for services that only need database and session access');
|
|
1088
993
|
lines.push(' * Compatible with both AccessContext (from hooks) and Context (from server actions)');
|
|
@@ -1093,41 +998,33 @@ function generateContextType(_config) {
|
|
|
1093
998
|
lines.push(' session: TSession');
|
|
1094
999
|
lines.push('}');
|
|
1095
1000
|
lines.push('');
|
|
1096
|
-
// Generate Context - extends BaseContext and adds server action capabilities
|
|
1097
1001
|
lines.push('/**');
|
|
1098
1002
|
lines.push(' * Full context type with server action capabilities and virtual field typing');
|
|
1099
|
-
lines.push(' * Extends BaseContext and adds serverAction and
|
|
1003
|
+
lines.push(' * Extends BaseContext and adds serverAction, sudo, and withSession methods');
|
|
1100
1004
|
lines.push(' * Use this type in server actions and components that need full context capabilities');
|
|
1101
1005
|
lines.push(' */');
|
|
1102
1006
|
lines.push('export interface Context<TSession extends OpensaasSession = OpensaasSession> extends BaseContext<TSession> {');
|
|
1103
1007
|
lines.push(' serverAction: (props: ServerActionProps) => Promise<unknown>');
|
|
1104
1008
|
lines.push(' sudo: () => Context<TSession>');
|
|
1009
|
+
lines.push(' withSession: (session: TSession | null) => Context<TSession>');
|
|
1105
1010
|
lines.push('}');
|
|
1106
1011
|
return lines.join('\n');
|
|
1107
1012
|
}
|
|
1108
|
-
/**
|
|
1109
|
-
* Collect TypeScript imports from field configurations
|
|
1110
|
-
*/
|
|
1111
1013
|
function collectFieldImports(config) {
|
|
1112
1014
|
const importsMap = new Map();
|
|
1113
|
-
// Iterate through all lists and fields
|
|
1114
1015
|
for (const listConfig of Object.values(config.lists)) {
|
|
1115
1016
|
for (const fieldConfig of Object.values(listConfig.fields)) {
|
|
1116
|
-
// Check if field provides imports
|
|
1117
1017
|
if (fieldConfig.getTypeScriptImports) {
|
|
1118
1018
|
const imports = fieldConfig.getTypeScriptImports();
|
|
1119
1019
|
for (const imp of imports) {
|
|
1120
1020
|
const existing = importsMap.get(imp.from);
|
|
1121
1021
|
if (existing) {
|
|
1122
|
-
// Merge names into existing import
|
|
1123
1022
|
imp.names.forEach((name) => existing.names.add(name));
|
|
1124
|
-
// If either import is not type-only, make the merged import not type-only
|
|
1125
1023
|
if (imp.typeOnly === false) {
|
|
1126
1024
|
existing.typeOnly = false;
|
|
1127
1025
|
}
|
|
1128
1026
|
}
|
|
1129
1027
|
else {
|
|
1130
|
-
// Add new import
|
|
1131
1028
|
importsMap.set(imp.from, {
|
|
1132
1029
|
names: new Set(imp.names),
|
|
1133
1030
|
typeOnly: imp.typeOnly ?? true,
|
|
@@ -1137,25 +1034,19 @@ function collectFieldImports(config) {
|
|
|
1137
1034
|
}
|
|
1138
1035
|
}
|
|
1139
1036
|
}
|
|
1140
|
-
// Convert map to array
|
|
1141
1037
|
return Array.from(importsMap.entries()).map(([from, { names, typeOnly }]) => ({
|
|
1142
1038
|
names: Array.from(names).sort(),
|
|
1143
1039
|
from,
|
|
1144
1040
|
typeOnly,
|
|
1145
1041
|
}));
|
|
1146
1042
|
}
|
|
1147
|
-
/**
|
|
1148
|
-
* Generate all TypeScript types from config
|
|
1149
|
-
*/
|
|
1150
1043
|
export function generateTypes(config) {
|
|
1151
1044
|
const lines = [];
|
|
1152
|
-
// Add header comment
|
|
1153
1045
|
lines.push('/**');
|
|
1154
1046
|
lines.push(' * Generated types from OpenSaas configuration');
|
|
1155
1047
|
lines.push(' * DO NOT EDIT - This file is automatically generated');
|
|
1156
1048
|
lines.push(' */');
|
|
1157
1049
|
lines.push('');
|
|
1158
|
-
// Add necessary imports
|
|
1159
1050
|
// Use alias for Session to avoid conflicts if user has a list named "Session".
|
|
1160
1051
|
// Session and AccessContext stay on the public root entry point (Session is the
|
|
1161
1052
|
// module-augmentation target); the rest are unstable runtime plumbing on /internal.
|
|
@@ -1166,7 +1057,6 @@ export function generateTypes(config) {
|
|
|
1166
1057
|
// `extensionAlias`. See ADR-0008.
|
|
1167
1058
|
lines.push("import type { PrismaClient, Prisma } from './prisma-client/client.ts'");
|
|
1168
1059
|
lines.push("import type { PluginServices } from './plugin-types.ts'");
|
|
1169
|
-
// Add field-specific imports
|
|
1170
1060
|
const fieldImports = collectFieldImports(config);
|
|
1171
1061
|
for (const imp of fieldImports) {
|
|
1172
1062
|
const typePrefix = imp.typeOnly ? 'type ' : '';
|
|
@@ -1174,8 +1064,6 @@ export function generateTypes(config) {
|
|
|
1174
1064
|
lines.push(`import ${typePrefix}{ ${names} } from '${imp.from}'`);
|
|
1175
1065
|
}
|
|
1176
1066
|
lines.push('');
|
|
1177
|
-
// Emit shared utility type used by GetPayload types to strip virtual field keys
|
|
1178
|
-
// from select/include before passing to Prisma's GetPayload (prevents `never` intersection)
|
|
1179
1067
|
lines.push('// Utility: strips virtual field keys from select/include so Prisma GetPayload never sees them');
|
|
1180
1068
|
lines.push('type StripVirtualFromArgs<T, VirtualKeys extends string> =');
|
|
1181
1069
|
lines.push(' T extends { select: infer S extends object }');
|
|
@@ -1184,12 +1072,11 @@ export function generateTypes(config) {
|
|
|
1184
1072
|
lines.push(" ? Omit<T, 'include'> & { include: Omit<I, VirtualKeys> }");
|
|
1185
1073
|
lines.push(' : T');
|
|
1186
1074
|
lines.push('');
|
|
1187
|
-
// Generate types for each list
|
|
1188
1075
|
for (const [listName, listConfig] of Object.entries(config.lists)) {
|
|
1189
|
-
//
|
|
1076
|
+
// VirtualFields first — needed by the Output type and CustomDB.
|
|
1190
1077
|
lines.push(generateVirtualFieldsType(listName, listConfig.fields));
|
|
1191
1078
|
lines.push('');
|
|
1192
|
-
//
|
|
1079
|
+
// TransformedFields — needed by CustomDB.
|
|
1193
1080
|
lines.push(generateTransformedFieldsType(listName, listConfig.fields));
|
|
1194
1081
|
lines.push('');
|
|
1195
1082
|
lines.push(generateModelOutputType(listName, listConfig, config.db, !!listConfig.isSingleton));
|
|
@@ -1204,22 +1091,17 @@ export function generateTypes(config) {
|
|
|
1204
1091
|
lines.push('');
|
|
1205
1092
|
lines.push(generateHookTypes(listName));
|
|
1206
1093
|
lines.push('');
|
|
1207
|
-
// Generate Select and Include types with virtual field support
|
|
1208
1094
|
lines.push(generateSelectType(listName, listConfig.fields));
|
|
1209
1095
|
lines.push('');
|
|
1210
|
-
// Only generate Include type if the list has relationships
|
|
1211
1096
|
const includeType = generateIncludeType(listName, listConfig.fields);
|
|
1212
1097
|
if (includeType) {
|
|
1213
1098
|
lines.push(includeType);
|
|
1214
1099
|
lines.push('');
|
|
1215
1100
|
}
|
|
1216
|
-
// Generate GetPayload helper type with virtual field support
|
|
1217
1101
|
lines.push(generateGetPayloadType(listName, listConfig.fields));
|
|
1218
1102
|
lines.push('');
|
|
1219
|
-
// Generate DefaultArgs type for nested relationship support
|
|
1220
1103
|
lines.push(generateDefaultArgsType(listName, listConfig.fields));
|
|
1221
1104
|
lines.push('');
|
|
1222
|
-
// Generate custom Args types with virtual field support
|
|
1223
1105
|
lines.push(generateFindUniqueArgsType(listName, listConfig.fields));
|
|
1224
1106
|
lines.push('');
|
|
1225
1107
|
lines.push(generateFindManyArgsType(listName, listConfig.fields));
|
|
@@ -1241,19 +1123,13 @@ export function generateTypes(config) {
|
|
|
1241
1123
|
lines.push(generateUpdateManyArgsType(listName, listConfig.fields));
|
|
1242
1124
|
lines.push('');
|
|
1243
1125
|
}
|
|
1244
|
-
// Generate CustomDB interface
|
|
1245
1126
|
lines.push(generateCustomDBType(config));
|
|
1246
1127
|
lines.push('');
|
|
1247
|
-
// Generate Context type
|
|
1248
1128
|
lines.push(generateContextType(config));
|
|
1249
1129
|
return lines.join('\n');
|
|
1250
1130
|
}
|
|
1251
|
-
/**
|
|
1252
|
-
* Write TypeScript types to file
|
|
1253
|
-
*/
|
|
1254
1131
|
export function writeTypes(config, outputPath) {
|
|
1255
1132
|
const types = generateTypes(config);
|
|
1256
|
-
// Ensure directory exists
|
|
1257
1133
|
const dir = path.dirname(outputPath);
|
|
1258
1134
|
if (!fs.existsSync(dir)) {
|
|
1259
1135
|
fs.mkdirSync(dir, { recursive: true });
|