@opensaas/stack-cli 0.1.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.
Files changed (51) hide show
  1. package/.turbo/turbo-build.log +4 -0
  2. package/README.md +328 -0
  3. package/bin/opensaas.js +3 -0
  4. package/dist/commands/dev.d.ts +2 -0
  5. package/dist/commands/dev.d.ts.map +1 -0
  6. package/dist/commands/dev.js +40 -0
  7. package/dist/commands/dev.js.map +1 -0
  8. package/dist/commands/generate.d.ts +2 -0
  9. package/dist/commands/generate.d.ts.map +1 -0
  10. package/dist/commands/generate.js +90 -0
  11. package/dist/commands/generate.js.map +1 -0
  12. package/dist/commands/init.d.ts +2 -0
  13. package/dist/commands/init.d.ts.map +1 -0
  14. package/dist/commands/init.js +343 -0
  15. package/dist/commands/init.js.map +1 -0
  16. package/dist/generator/context.d.ts +13 -0
  17. package/dist/generator/context.d.ts.map +1 -0
  18. package/dist/generator/context.js +69 -0
  19. package/dist/generator/context.js.map +1 -0
  20. package/dist/generator/index.d.ts +5 -0
  21. package/dist/generator/index.d.ts.map +1 -0
  22. package/dist/generator/index.js +5 -0
  23. package/dist/generator/index.js.map +1 -0
  24. package/dist/generator/prisma.d.ts +10 -0
  25. package/dist/generator/prisma.d.ts.map +1 -0
  26. package/dist/generator/prisma.js +129 -0
  27. package/dist/generator/prisma.js.map +1 -0
  28. package/dist/generator/type-patcher.d.ts +13 -0
  29. package/dist/generator/type-patcher.d.ts.map +1 -0
  30. package/dist/generator/type-patcher.js +68 -0
  31. package/dist/generator/type-patcher.js.map +1 -0
  32. package/dist/generator/types.d.ts +10 -0
  33. package/dist/generator/types.d.ts.map +1 -0
  34. package/dist/generator/types.js +225 -0
  35. package/dist/generator/types.js.map +1 -0
  36. package/dist/index.d.ts +3 -0
  37. package/dist/index.d.ts.map +1 -0
  38. package/dist/index.js +28 -0
  39. package/dist/index.js.map +1 -0
  40. package/package.json +48 -0
  41. package/src/commands/dev.ts +48 -0
  42. package/src/commands/generate.ts +103 -0
  43. package/src/commands/init.ts +367 -0
  44. package/src/generator/context.ts +75 -0
  45. package/src/generator/index.ts +4 -0
  46. package/src/generator/prisma.ts +157 -0
  47. package/src/generator/type-patcher.ts +93 -0
  48. package/src/generator/types.ts +263 -0
  49. package/src/index.ts +34 -0
  50. package/tsconfig.json +13 -0
  51. package/tsconfig.tsbuildinfo +1 -0
@@ -0,0 +1,68 @@
1
+ import * as fs from 'fs';
2
+ import * as path from 'path';
3
+ /**
4
+ * Patches Prisma's generated types based on field-level type patch configurations
5
+ *
6
+ * This reads Prisma's generated index.d.ts and replaces field types according to
7
+ * the `typePatch` configuration on each field. Fields can specify custom result types
8
+ * that will replace the original Prisma types in query results.
9
+ *
10
+ * The patched types are written to `.opensaas/prisma-client.d.ts` so users can
11
+ * import from there to get the transformed types.
12
+ */
13
+ export function patchPrismaTypes(config, projectRoot) {
14
+ const opensaasPath = config.opensaasPath || '.opensaas';
15
+ // Prisma generates to opensaasPath/prisma-client
16
+ const prismaClientDir = path.join(projectRoot, opensaasPath, 'prisma-client');
17
+ const prismaIndexPath = path.join(prismaClientDir, 'index.d.ts');
18
+ // Check if Prisma types exist
19
+ if (!fs.existsSync(prismaIndexPath)) {
20
+ console.warn('āš ļø Prisma types not found. Run `npx prisma generate` first to generate Prisma Client.');
21
+ return;
22
+ }
23
+ // Read original Prisma types
24
+ const originalTypes = fs.readFileSync(prismaIndexPath, 'utf-8');
25
+ const fieldPatches = [];
26
+ for (const listConfig of Object.values(config.lists)) {
27
+ for (const [fieldName, fieldConfig] of Object.entries(listConfig.fields)) {
28
+ if (fieldConfig.typePatch) {
29
+ fieldPatches.push({
30
+ fieldName,
31
+ resultType: fieldConfig.typePatch.resultType,
32
+ patchScope: fieldConfig.typePatch.patchScope || 'scalars-only',
33
+ });
34
+ }
35
+ }
36
+ }
37
+ if (fieldPatches.length === 0) {
38
+ // No fields need patching
39
+ return;
40
+ }
41
+ // Patch the types
42
+ let patchedTypes = originalTypes;
43
+ // For each field that needs patching, replace its type
44
+ for (const { fieldName, resultType, patchScope } of fieldPatches) {
45
+ if (patchScope === 'scalars-only') {
46
+ // Pattern matches: fieldName: <type> ONLY inside scalars: $Extensions.GetPayloadResult<{...}>
47
+ // This ensures we don't patch Input types (UserCreateInput, etc.)
48
+ // Example match:
49
+ // scalars: $Extensions.GetPayloadResult<{
50
+ // id: string
51
+ // password: string ← Replace this
52
+ // ...
53
+ const pattern = new RegExp(`(scalars:\\s*\\$Extensions\\.GetPayloadResult<\\{[^}]*?\\b${fieldName}:\\s*)[^,\\n}]+`, 'g');
54
+ patchedTypes = patchedTypes.replace(pattern, `$1${resultType}`);
55
+ }
56
+ else {
57
+ // patchScope === 'all' - patch everywhere the field appears
58
+ // This is more aggressive and will patch input types too
59
+ const pattern = new RegExp(`(\\b${fieldName}:\\s*)[^,\\n}]+`, 'g');
60
+ patchedTypes = patchedTypes.replace(pattern, `$1${resultType}`);
61
+ }
62
+ }
63
+ // Write patched types back to Prisma's index.d.ts
64
+ // This directly modifies the Prisma-generated file with our type patches
65
+ fs.writeFileSync(prismaIndexPath, patchedTypes, 'utf-8');
66
+ console.log(`āœ… Patched Prisma types (${fieldPatches.length} field${fieldPatches.length === 1 ? '' : 's'})`);
67
+ }
68
+ //# sourceMappingURL=type-patcher.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"type-patcher.js","sourceRoot":"","sources":["../../src/generator/type-patcher.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,MAAM,IAAI,CAAA;AACxB,OAAO,KAAK,IAAI,MAAM,MAAM,CAAA;AAE5B;;;;;;;;;GASG;AACH,MAAM,UAAU,gBAAgB,CAAC,MAAsB,EAAE,WAAmB;IAC1E,MAAM,YAAY,GAAG,MAAM,CAAC,YAAY,IAAI,WAAW,CAAA;IAEvD,iDAAiD;IACjD,MAAM,eAAe,GAAG,IAAI,CAAC,IAAI,CAAC,WAAW,EAAE,YAAY,EAAE,eAAe,CAAC,CAAA;IAC7E,MAAM,eAAe,GAAG,IAAI,CAAC,IAAI,CAAC,eAAe,EAAE,YAAY,CAAC,CAAA;IAEhE,8BAA8B;IAC9B,IAAI,CAAC,EAAE,CAAC,UAAU,CAAC,eAAe,CAAC,EAAE,CAAC;QACpC,OAAO,CAAC,IAAI,CACV,wFAAwF,CACzF,CAAA;QACD,OAAM;IACR,CAAC;IAED,6BAA6B;IAC7B,MAAM,aAAa,GAAG,EAAE,CAAC,YAAY,CAAC,eAAe,EAAE,OAAO,CAAC,CAAA;IAS/D,MAAM,YAAY,GAAiB,EAAE,CAAA;IAErC,KAAK,MAAM,UAAU,IAAI,MAAM,CAAC,MAAM,CAAC,MAAM,CAAC,KAAK,CAAC,EAAE,CAAC;QACrD,KAAK,MAAM,CAAC,SAAS,EAAE,WAAW,CAAC,IAAI,MAAM,CAAC,OAAO,CAAC,UAAU,CAAC,MAAM,CAAC,EAAE,CAAC;YACzE,IAAI,WAAW,CAAC,SAAS,EAAE,CAAC;gBAC1B,YAAY,CAAC,IAAI,CAAC;oBAChB,SAAS;oBACT,UAAU,EAAE,WAAW,CAAC,SAAS,CAAC,UAAU;oBAC5C,UAAU,EAAE,WAAW,CAAC,SAAS,CAAC,UAAU,IAAI,cAAc;iBAC/D,CAAC,CAAA;YACJ,CAAC;QACH,CAAC;IACH,CAAC;IAED,IAAI,YAAY,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;QAC9B,0BAA0B;QAC1B,OAAM;IACR,CAAC;IAED,kBAAkB;IAClB,IAAI,YAAY,GAAG,aAAa,CAAA;IAEhC,uDAAuD;IACvD,KAAK,MAAM,EAAE,SAAS,EAAE,UAAU,EAAE,UAAU,EAAE,IAAI,YAAY,EAAE,CAAC;QACjE,IAAI,UAAU,KAAK,cAAc,EAAE,CAAC;YAClC,8FAA8F;YAC9F,kEAAkE;YAClE,iBAAiB;YACjB,0CAA0C;YAC1C,eAAe;YACf,qCAAqC;YACrC,QAAQ;YACR,MAAM,OAAO,GAAG,IAAI,MAAM,CACxB,6DAA6D,SAAS,iBAAiB,EACvF,GAAG,CACJ,CAAA;YAED,YAAY,GAAG,YAAY,CAAC,OAAO,CAAC,OAAO,EAAE,KAAK,UAAU,EAAE,CAAC,CAAA;QACjE,CAAC;aAAM,CAAC;YACN,4DAA4D;YAC5D,yDAAyD;YACzD,MAAM,OAAO,GAAG,IAAI,MAAM,CAAC,OAAO,SAAS,iBAAiB,EAAE,GAAG,CAAC,CAAA;YAClE,YAAY,GAAG,YAAY,CAAC,OAAO,CAAC,OAAO,EAAE,KAAK,UAAU,EAAE,CAAC,CAAA;QACjE,CAAC;IACH,CAAC;IAED,kDAAkD;IAClD,yEAAyE;IACzE,EAAE,CAAC,aAAa,CAAC,eAAe,EAAE,YAAY,EAAE,OAAO,CAAC,CAAA;IAExD,OAAO,CAAC,GAAG,CACT,2BAA2B,YAAY,CAAC,MAAM,SAAS,YAAY,CAAC,MAAM,KAAK,CAAC,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,GAAG,GAAG,CAC/F,CAAA;AACH,CAAC"}
@@ -0,0 +1,10 @@
1
+ import type { OpenSaasConfig } from '@opensaas/stack-core';
2
+ /**
3
+ * Generate all TypeScript types from config
4
+ */
5
+ export declare function generateTypes(config: OpenSaasConfig): string;
6
+ /**
7
+ * Write TypeScript types to file
8
+ */
9
+ export declare function writeTypes(config: OpenSaasConfig, outputPath: string): void;
10
+ //# sourceMappingURL=types.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../../src/generator/types.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,cAAc,EAAkC,MAAM,sBAAsB,CAAA;AA0N1F;;GAEG;AACH,wBAAgB,aAAa,CAAC,MAAM,EAAE,cAAc,GAAG,MAAM,CA0B5D;AAED;;GAEG;AACH,wBAAgB,UAAU,CAAC,MAAM,EAAE,cAAc,EAAE,UAAU,EAAE,MAAM,GAAG,IAAI,CAU3E"}
@@ -0,0 +1,225 @@
1
+ import * as fs from 'fs';
2
+ import * as path from 'path';
3
+ /**
4
+ * Map OpenSaas field types to TypeScript types
5
+ */
6
+ function mapFieldTypeToTypeScript(field) {
7
+ // Relationships are handled separately
8
+ if (field.type === 'relationship') {
9
+ return null;
10
+ }
11
+ // Use field's own TypeScript type generator if available
12
+ if (field.getTypeScriptType) {
13
+ const result = field.getTypeScriptType();
14
+ return result.type;
15
+ }
16
+ // Fallback for fields without generator methods
17
+ throw new Error(`Field type "${field.type}" does not implement getTypeScriptType method`);
18
+ }
19
+ /**
20
+ * Check if a field is optional in the type
21
+ */
22
+ function isFieldOptional(field) {
23
+ // Relationships are always nullable
24
+ if (field.type === 'relationship') {
25
+ return true;
26
+ }
27
+ // Use field's own TypeScript type generator if available
28
+ if (field.getTypeScriptType) {
29
+ const result = field.getTypeScriptType();
30
+ return result.optional;
31
+ }
32
+ // Fallback: assume optional
33
+ return true;
34
+ }
35
+ /**
36
+ * Generate TypeScript interface for a model
37
+ */
38
+ function generateModelType(listName, fields) {
39
+ const lines = [];
40
+ lines.push(`export type ${listName} = {`);
41
+ lines.push(' id: string');
42
+ for (const [fieldName, fieldConfig] of Object.entries(fields)) {
43
+ if (fieldConfig.type === 'relationship') {
44
+ const relField = fieldConfig;
45
+ const [targetList] = relField.ref.split('.');
46
+ if (relField.many) {
47
+ lines.push(` ${fieldName}: ${targetList}[]`);
48
+ }
49
+ else {
50
+ lines.push(` ${fieldName}Id: string | null`);
51
+ lines.push(` ${fieldName}: ${targetList} | null`);
52
+ }
53
+ }
54
+ else {
55
+ const tsType = mapFieldTypeToTypeScript(fieldConfig);
56
+ if (!tsType)
57
+ continue; // Skip if no type returned
58
+ const optional = isFieldOptional(fieldConfig);
59
+ const nullability = optional ? ' | null' : '';
60
+ lines.push(` ${fieldName}: ${tsType}${nullability}`);
61
+ }
62
+ }
63
+ lines.push(' createdAt: Date');
64
+ lines.push(' updatedAt: Date');
65
+ lines.push('}');
66
+ return lines.join('\n');
67
+ }
68
+ /**
69
+ * Generate CreateInput type
70
+ */
71
+ function generateCreateInputType(listName, fields) {
72
+ const lines = [];
73
+ lines.push(`export type ${listName}CreateInput = {`);
74
+ for (const [fieldName, fieldConfig] of Object.entries(fields)) {
75
+ if (fieldConfig.type === 'relationship') {
76
+ const relField = fieldConfig;
77
+ if (relField.many) {
78
+ lines.push(` ${fieldName}?: { connect: Array<{ id: string }> }`);
79
+ }
80
+ else {
81
+ lines.push(` ${fieldName}?: { connect: { id: string } }`);
82
+ }
83
+ }
84
+ else {
85
+ const tsType = mapFieldTypeToTypeScript(fieldConfig);
86
+ if (!tsType)
87
+ continue; // Skip if no type returned
88
+ const required = !isFieldOptional(fieldConfig) && !fieldConfig.defaultValue;
89
+ const optional = required ? '' : '?';
90
+ lines.push(` ${fieldName}${optional}: ${tsType}`);
91
+ }
92
+ }
93
+ lines.push('}');
94
+ return lines.join('\n');
95
+ }
96
+ /**
97
+ * Generate UpdateInput type
98
+ */
99
+ function generateUpdateInputType(listName, fields) {
100
+ const lines = [];
101
+ lines.push(`export type ${listName}UpdateInput = {`);
102
+ for (const [fieldName, fieldConfig] of Object.entries(fields)) {
103
+ if (fieldConfig.type === 'relationship') {
104
+ const relField = fieldConfig;
105
+ if (relField.many) {
106
+ lines.push(` ${fieldName}?: { connect: Array<{ id: string }>, disconnect: Array<{ id: string }> }`);
107
+ }
108
+ else {
109
+ lines.push(` ${fieldName}?: { connect: { id: string } } | { disconnect: true }`);
110
+ }
111
+ }
112
+ else {
113
+ const tsType = mapFieldTypeToTypeScript(fieldConfig);
114
+ if (!tsType)
115
+ continue; // Skip if no type returned
116
+ lines.push(` ${fieldName}?: ${tsType}`);
117
+ }
118
+ }
119
+ lines.push('}');
120
+ return lines.join('\n');
121
+ }
122
+ /**
123
+ * Generate WhereInput type (simplified)
124
+ */
125
+ function generateWhereInputType(listName, fields) {
126
+ const lines = [];
127
+ lines.push(`export type ${listName}WhereInput = {`);
128
+ lines.push(' id?: string');
129
+ lines.push(' AND?: Array<' + listName + 'WhereInput>');
130
+ lines.push(' OR?: Array<' + listName + 'WhereInput>');
131
+ lines.push(' NOT?: ' + listName + 'WhereInput');
132
+ for (const [fieldName, fieldConfig] of Object.entries(fields)) {
133
+ if (fieldConfig.type === 'relationship') {
134
+ continue; // Skip for now
135
+ }
136
+ else {
137
+ const tsType = mapFieldTypeToTypeScript(fieldConfig);
138
+ if (!tsType)
139
+ continue; // Skip if no type returned
140
+ lines.push(` ${fieldName}?: { equals?: ${tsType}, not?: ${tsType} }`);
141
+ }
142
+ }
143
+ lines.push('}');
144
+ return lines.join('\n');
145
+ }
146
+ /**
147
+ * Generate Context type with all operations
148
+ */
149
+ function generateContextType(config) {
150
+ const lines = [];
151
+ lines.push('export type Context = {');
152
+ lines.push(' db: {');
153
+ for (const listName of Object.keys(config.lists)) {
154
+ const lowerName = listName.toLowerCase();
155
+ lines.push(` ${lowerName}: {`);
156
+ lines.push(` findUnique: (args: {`);
157
+ lines.push(` where: { id: string }`);
158
+ lines.push(` include?: any`);
159
+ lines.push(` }) => Promise<${listName} | null>`);
160
+ lines.push(` findMany: (args?: {`);
161
+ lines.push(` where?: ${listName}WhereInput`);
162
+ lines.push(` take?: number`);
163
+ lines.push(` skip?: number`);
164
+ lines.push(` include?: any`);
165
+ lines.push(` }) => Promise<${listName}[]>`);
166
+ lines.push(` create: (args: {`);
167
+ lines.push(` data: ${listName}CreateInput`);
168
+ lines.push(` }) => Promise<${listName} | null>`);
169
+ lines.push(` update: (args: {`);
170
+ lines.push(` where: { id: string }`);
171
+ lines.push(` data: ${listName}UpdateInput`);
172
+ lines.push(` }) => Promise<${listName} | null>`);
173
+ lines.push(` delete: (args: {`);
174
+ lines.push(` where: { id: string }`);
175
+ lines.push(` }) => Promise<${listName} | null>`);
176
+ lines.push(` count: (args?: {`);
177
+ lines.push(` where?: ${listName}WhereInput`);
178
+ lines.push(` }) => Promise<number>`);
179
+ lines.push(` }`);
180
+ }
181
+ lines.push(' }');
182
+ lines.push(' session: any');
183
+ lines.push(' prisma: any // Your PrismaClient instance');
184
+ lines.push('}');
185
+ return lines.join('\n');
186
+ }
187
+ /**
188
+ * Generate all TypeScript types from config
189
+ */
190
+ export function generateTypes(config) {
191
+ const lines = [];
192
+ // Add header comment
193
+ lines.push('/**');
194
+ lines.push(' * Generated types from OpenSaas configuration');
195
+ lines.push(' * DO NOT EDIT - This file is automatically generated');
196
+ lines.push(' */');
197
+ lines.push('');
198
+ // Generate types for each list
199
+ for (const [listName, listConfig] of Object.entries(config.lists)) {
200
+ lines.push(generateModelType(listName, listConfig.fields));
201
+ lines.push('');
202
+ lines.push(generateCreateInputType(listName, listConfig.fields));
203
+ lines.push('');
204
+ lines.push(generateUpdateInputType(listName, listConfig.fields));
205
+ lines.push('');
206
+ lines.push(generateWhereInputType(listName, listConfig.fields));
207
+ lines.push('');
208
+ }
209
+ // Generate Context type
210
+ lines.push(generateContextType(config));
211
+ return lines.join('\n');
212
+ }
213
+ /**
214
+ * Write TypeScript types to file
215
+ */
216
+ export function writeTypes(config, outputPath) {
217
+ const types = generateTypes(config);
218
+ // Ensure directory exists
219
+ const dir = path.dirname(outputPath);
220
+ if (!fs.existsSync(dir)) {
221
+ fs.mkdirSync(dir, { recursive: true });
222
+ }
223
+ fs.writeFileSync(outputPath, types, 'utf-8');
224
+ }
225
+ //# sourceMappingURL=types.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"types.js","sourceRoot":"","sources":["../../src/generator/types.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,3 @@
1
+ #!/usr/bin/env node
2
+ export {};
3
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":""}
package/dist/index.js ADDED
@@ -0,0 +1,28 @@
1
+ #!/usr/bin/env node
2
+ import { Command } from 'commander';
3
+ import { generateCommand } from './commands/generate.js';
4
+ import { initCommand } from './commands/init.js';
5
+ import { devCommand } from './commands/dev.js';
6
+ const program = new Command();
7
+ program.name('opensaas').description('OpenSaas Stack CLI').version('0.1.0');
8
+ program
9
+ .command('generate')
10
+ .description('Generate Prisma schema and TypeScript types from opensaas.config.ts')
11
+ .action(async () => {
12
+ await generateCommand();
13
+ });
14
+ program
15
+ .command('init [project-name]')
16
+ .description('Create a new OpenSaas project')
17
+ .option('-t, --template <template>', 'Project template to use')
18
+ .action(async (projectName) => {
19
+ await initCommand(projectName);
20
+ });
21
+ program
22
+ .command('dev')
23
+ .description('Watch opensaas.config.ts and regenerate on changes')
24
+ .action(async () => {
25
+ await devCommand();
26
+ });
27
+ program.parse();
28
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":";AAEA,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAA;AACnC,OAAO,EAAE,eAAe,EAAE,MAAM,wBAAwB,CAAA;AACxD,OAAO,EAAE,WAAW,EAAE,MAAM,oBAAoB,CAAA;AAChD,OAAO,EAAE,UAAU,EAAE,MAAM,mBAAmB,CAAA;AAE9C,MAAM,OAAO,GAAG,IAAI,OAAO,EAAE,CAAA;AAE7B,OAAO,CAAC,IAAI,CAAC,UAAU,CAAC,CAAC,WAAW,CAAC,oBAAoB,CAAC,CAAC,OAAO,CAAC,OAAO,CAAC,CAAA;AAE3E,OAAO;KACJ,OAAO,CAAC,UAAU,CAAC;KACnB,WAAW,CAAC,qEAAqE,CAAC;KAClF,MAAM,CAAC,KAAK,IAAI,EAAE;IACjB,MAAM,eAAe,EAAE,CAAA;AACzB,CAAC,CAAC,CAAA;AAEJ,OAAO;KACJ,OAAO,CAAC,qBAAqB,CAAC;KAC9B,WAAW,CAAC,+BAA+B,CAAC;KAC5C,MAAM,CAAC,2BAA2B,EAAE,yBAAyB,CAAC;KAC9D,MAAM,CAAC,KAAK,EAAE,WAAW,EAAE,EAAE;IAC5B,MAAM,WAAW,CAAC,WAAW,CAAC,CAAA;AAChC,CAAC,CAAC,CAAA;AAEJ,OAAO;KACJ,OAAO,CAAC,KAAK,CAAC;KACd,WAAW,CAAC,oDAAoD,CAAC;KACjE,MAAM,CAAC,KAAK,IAAI,EAAE;IACjB,MAAM,UAAU,EAAE,CAAA;AACpB,CAAC,CAAC,CAAA;AAEJ,OAAO,CAAC,KAAK,EAAE,CAAA"}
package/package.json ADDED
@@ -0,0 +1,48 @@
1
+ {
2
+ "name": "@opensaas/stack-cli",
3
+ "version": "0.1.0",
4
+ "description": "CLI tools for OpenSaas Stack",
5
+ "type": "module",
6
+ "main": "./dist/index.js",
7
+ "types": "./dist/index.d.ts",
8
+ "exports": {
9
+ ".": {
10
+ "types": "./dist/index.d.ts",
11
+ "default": "./dist/index.js"
12
+ },
13
+ "./generator": {
14
+ "types": "./dist/generator/index.d.ts",
15
+ "default": "./dist/generator/index.js"
16
+ }
17
+ },
18
+ "bin": {
19
+ "opensaas": "./bin/opensaas.js"
20
+ },
21
+ "keywords": [
22
+ "opensaas",
23
+ "cli",
24
+ "nextjs",
25
+ "prisma"
26
+ ],
27
+ "author": "",
28
+ "license": "MIT",
29
+ "dependencies": {
30
+ "commander": "^14.0.1",
31
+ "chalk": "^5.6.2",
32
+ "ora": "^9.0.0",
33
+ "prompts": "^2.4.2",
34
+ "chokidar": "^4.0.3",
35
+ "jiti": "^2.6.1",
36
+ "@opensaas/stack-core": "0.1.0"
37
+ },
38
+ "devDependencies": {
39
+ "@types/node": "^24.7.2",
40
+ "@types/prompts": "^2.4.9",
41
+ "typescript": "^5.9.3"
42
+ },
43
+ "scripts": {
44
+ "build": "tsc",
45
+ "dev": "tsc --watch",
46
+ "clean": "rm -rf .turbo dist tsconfig.tsbuildinfo"
47
+ }
48
+ }
@@ -0,0 +1,48 @@
1
+ import * as path from 'path'
2
+ import * as fs from 'fs'
3
+ import chalk from 'chalk'
4
+ import chokidar from 'chokidar'
5
+ import { generateCommand } from './generate.js'
6
+
7
+ export async function devCommand() {
8
+ const cwd = process.cwd()
9
+ const configPath = path.join(cwd, 'opensaas.config.ts')
10
+
11
+ // Check if config exists
12
+ if (!fs.existsSync(configPath)) {
13
+ console.error(chalk.red('Error: opensaas.config.ts not found in current directory'))
14
+ console.error(chalk.gray(' Please run this command from your project root'))
15
+ process.exit(1)
16
+ }
17
+
18
+ console.log(chalk.bold.cyan('\nOpenSaas Dev Mode\n'))
19
+ console.log(chalk.gray('Watching for changes to opensaas.config.ts...\n'))
20
+
21
+ // Run initial generation
22
+ await generateCommand()
23
+
24
+ // Watch for changes
25
+ const watcher = chokidar.watch(configPath, {
26
+ persistent: true,
27
+ ignoreInitial: true,
28
+ })
29
+
30
+ watcher.on('change', async () => {
31
+ console.log(chalk.yellow('\nConfig changed, regenerating...\n'))
32
+ await generateCommand()
33
+ })
34
+
35
+ watcher.on('error', (error) => {
36
+ console.error(chalk.red('\nWatcher error:'), error)
37
+ })
38
+
39
+ // Keep the process running
40
+ console.log(chalk.gray('Press Ctrl+C to stop watching\n'))
41
+
42
+ // Handle graceful shutdown
43
+ process.on('SIGINT', () => {
44
+ console.log(chalk.yellow('\n\nStopping dev mode...'))
45
+ watcher.close()
46
+ process.exit(0)
47
+ })
48
+ }
@@ -0,0 +1,103 @@
1
+ import * as path from 'path'
2
+ import * as fs from 'fs'
3
+ import { execSync } from 'child_process'
4
+ import chalk from 'chalk'
5
+ import ora from 'ora'
6
+ import { createJiti } from 'jiti'
7
+ import {
8
+ writePrismaSchema,
9
+ writeTypes,
10
+ writeContext,
11
+ patchPrismaTypes,
12
+ } from '../generator/index.js'
13
+ import type { OpenSaasConfig } from '@opensaas/stack-core'
14
+
15
+ export async function generateCommand() {
16
+ console.log(chalk.bold('\nšŸš€ OpenSaas Generator\n'))
17
+
18
+ const cwd = process.cwd()
19
+ const configPath = path.join(cwd, 'opensaas.config.ts')
20
+
21
+ // Check if config exists
22
+ if (!fs.existsSync(configPath)) {
23
+ console.error(chalk.red('āŒ Error: opensaas.config.ts not found in current directory'))
24
+ console.error(chalk.gray(' Please run this command from your project root'))
25
+ process.exit(1)
26
+ }
27
+
28
+ const spinner = ora('Loading configuration...').start()
29
+
30
+ try {
31
+ // Load config using jiti (supports TypeScript)
32
+ const jiti = createJiti(cwd, {
33
+ interopDefault: true,
34
+ })
35
+
36
+ const config = (await jiti.import(configPath)) as OpenSaasConfig
37
+
38
+ spinner.succeed(chalk.green('Configuration loaded'))
39
+
40
+ // Generate Prisma schema, types, and context
41
+ const generatorSpinner = ora('Generating schema and types...').start()
42
+ try {
43
+ writePrismaSchema(config, path.join(cwd, 'prisma', 'schema.prisma'))
44
+ writeTypes(config, path.join(cwd, '.opensaas', 'types.ts'))
45
+ writeContext(config, path.join(cwd, '.opensaas', 'context.ts'))
46
+
47
+ generatorSpinner.succeed(chalk.green('Schema generation complete'))
48
+ console.log(chalk.green('āœ… Prisma schema generated'))
49
+ console.log(chalk.green('āœ… TypeScript types generated'))
50
+ console.log(chalk.green('āœ… Context factory generated'))
51
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
52
+ } catch (err: any) {
53
+ generatorSpinner.fail(chalk.red('Failed to generate'))
54
+ console.error(chalk.red('\nāŒ Error:'), err.message)
55
+ if (err.stack) {
56
+ console.error(chalk.gray('\n' + err.stack))
57
+ }
58
+ process.exit(1)
59
+ }
60
+
61
+ // Run Prisma generate to create the Prisma client
62
+ const prismaSpinner = ora('Generating Prisma client...').start()
63
+ try {
64
+ execSync('npx prisma generate', {
65
+ cwd,
66
+ encoding: 'utf-8',
67
+ stdio: 'pipe',
68
+ })
69
+ prismaSpinner.succeed(chalk.green('Prisma client generated'))
70
+ console.log(chalk.green('āœ… Prisma client generated'))
71
+ } catch (err) {
72
+ prismaSpinner.fail(chalk.red('Failed to generate Prisma client'))
73
+ const message = err instanceof Error ? err.message : String(err)
74
+ console.error(chalk.red('\nāŒ Error:'), message)
75
+ process.exit(1)
76
+ }
77
+
78
+ // Patch Prisma types with field transformations
79
+ const patchSpinner = ora('Patching Prisma types...').start()
80
+ try {
81
+ patchPrismaTypes(config, cwd)
82
+ patchSpinner.succeed(chalk.green('Type patching complete'))
83
+ } catch (err) {
84
+ patchSpinner.fail(chalk.red('Failed to patch types'))
85
+ const message = err instanceof Error ? err.message : String(err)
86
+ console.error(chalk.red('\nāŒ Error:'), message)
87
+ process.exit(1)
88
+ }
89
+
90
+ console.log(chalk.bold('\n✨ Generation complete!\n'))
91
+ console.log(chalk.gray('Next steps:'))
92
+ console.log(chalk.gray(' 1. Run: npx prisma db push'))
93
+ console.log(chalk.gray(' 2. Start using your generated types!\n'))
94
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
95
+ } catch (error: any) {
96
+ spinner.fail(chalk.red('Generation failed'))
97
+ console.error(chalk.red('\nāŒ Error:'), error.message)
98
+ if (error.stack) {
99
+ console.error(chalk.gray('\n' + error.stack))
100
+ }
101
+ process.exit(1)
102
+ }
103
+ }