@opensaas/stack-cli 0.5.0 → 0.6.1

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 (54) hide show
  1. package/README.md +76 -0
  2. package/dist/commands/migrate.d.ts.map +1 -1
  3. package/dist/commands/migrate.js +94 -268
  4. package/dist/commands/migrate.js.map +1 -1
  5. package/package.json +7 -2
  6. package/.turbo/turbo-build.log +0 -4
  7. package/CHANGELOG.md +0 -462
  8. package/CLAUDE.md +0 -298
  9. package/src/commands/__snapshots__/generate.test.ts.snap +0 -413
  10. package/src/commands/dev.test.ts +0 -215
  11. package/src/commands/dev.ts +0 -48
  12. package/src/commands/generate.test.ts +0 -282
  13. package/src/commands/generate.ts +0 -182
  14. package/src/commands/init.ts +0 -34
  15. package/src/commands/mcp.ts +0 -135
  16. package/src/commands/migrate.ts +0 -534
  17. package/src/generator/__snapshots__/context.test.ts.snap +0 -361
  18. package/src/generator/__snapshots__/prisma.test.ts.snap +0 -174
  19. package/src/generator/__snapshots__/types.test.ts.snap +0 -1702
  20. package/src/generator/context.test.ts +0 -139
  21. package/src/generator/context.ts +0 -227
  22. package/src/generator/index.ts +0 -7
  23. package/src/generator/lists.test.ts +0 -335
  24. package/src/generator/lists.ts +0 -140
  25. package/src/generator/plugin-types.ts +0 -147
  26. package/src/generator/prisma-config.ts +0 -46
  27. package/src/generator/prisma-extensions.ts +0 -159
  28. package/src/generator/prisma.test.ts +0 -211
  29. package/src/generator/prisma.ts +0 -161
  30. package/src/generator/types.test.ts +0 -268
  31. package/src/generator/types.ts +0 -537
  32. package/src/index.ts +0 -46
  33. package/src/mcp/lib/documentation-provider.ts +0 -710
  34. package/src/mcp/lib/features/catalog.ts +0 -301
  35. package/src/mcp/lib/generators/feature-generator.ts +0 -598
  36. package/src/mcp/lib/types.ts +0 -89
  37. package/src/mcp/lib/wizards/migration-wizard.ts +0 -584
  38. package/src/mcp/lib/wizards/wizard-engine.ts +0 -427
  39. package/src/mcp/server/index.ts +0 -361
  40. package/src/mcp/server/stack-mcp-server.ts +0 -544
  41. package/src/migration/generators/migration-generator.ts +0 -675
  42. package/src/migration/introspectors/index.ts +0 -12
  43. package/src/migration/introspectors/keystone-introspector.ts +0 -296
  44. package/src/migration/introspectors/nextjs-introspector.ts +0 -209
  45. package/src/migration/introspectors/prisma-introspector.ts +0 -233
  46. package/src/migration/types.ts +0 -92
  47. package/tests/introspectors/keystone-introspector.test.ts +0 -255
  48. package/tests/introspectors/nextjs-introspector.test.ts +0 -302
  49. package/tests/introspectors/prisma-introspector.test.ts +0 -268
  50. package/tests/migration-generator.test.ts +0 -592
  51. package/tests/migration-wizard.test.ts +0 -442
  52. package/tsconfig.json +0 -13
  53. package/tsconfig.tsbuildinfo +0 -1
  54. package/vitest.config.ts +0 -26
@@ -1,161 +0,0 @@
1
- import type { OpenSaasConfig, FieldConfig, RelationshipField } from '@opensaas/stack-core'
2
- import * as fs from 'fs'
3
- import * as path from 'path'
4
-
5
- /**
6
- * Map OpenSaas field types to Prisma field types
7
- */
8
- function mapFieldTypeToPrisma(fieldName: string, field: FieldConfig): string | null {
9
- // Relationships are handled separately
10
- if (field.type === 'relationship') {
11
- return null
12
- }
13
-
14
- // Use field's own Prisma type generator if available
15
- if (field.getPrismaType) {
16
- const result = field.getPrismaType(fieldName)
17
- return result.type
18
- }
19
-
20
- // Fallback for fields without generator methods
21
- throw new Error(`Field type "${field.type}" does not implement getPrismaType method`)
22
- }
23
-
24
- /**
25
- * Get field modifiers (?, @default, @unique, etc.)
26
- */
27
- function getFieldModifiers(fieldName: string, field: FieldConfig): string {
28
- // Handle relationships separately
29
- if (field.type === 'relationship') {
30
- const relField = field as RelationshipField
31
- if (relField.many) {
32
- return '[]'
33
- } else {
34
- return '?'
35
- }
36
- }
37
-
38
- // Use field's own Prisma type generator if available
39
- if (field.getPrismaType) {
40
- const result = field.getPrismaType(fieldName)
41
- return result.modifiers || ''
42
- }
43
-
44
- // Fallback for fields without generator methods
45
- return ''
46
- }
47
-
48
- /**
49
- * Parse relationship ref to get target list and field
50
- */
51
- function parseRelationshipRef(ref: string): { list: string; field: string } {
52
- const [list, field] = ref.split('.')
53
- if (!list || !field) {
54
- throw new Error(`Invalid relationship ref: ${ref}`)
55
- }
56
- return { list, field }
57
- }
58
-
59
- /**
60
- * Generate Prisma schema from OpenSaas config
61
- */
62
- export function generatePrismaSchema(config: OpenSaasConfig): string {
63
- const lines: string[] = []
64
-
65
- const opensaasPath = config.opensaasPath || '.opensaas'
66
-
67
- // Generator and datasource
68
- lines.push('generator client {')
69
- lines.push(' provider = "prisma-client"')
70
- lines.push(` output = "../${opensaasPath}/prisma-client"`)
71
- lines.push('}')
72
- lines.push('')
73
- lines.push('datasource db {')
74
- lines.push(` provider = "${config.db.provider}"`)
75
- lines.push('}')
76
- lines.push('')
77
-
78
- // Generate models for each list
79
- for (const [listName, listConfig] of Object.entries(config.lists)) {
80
- lines.push(`model ${listName} {`)
81
-
82
- // Always add id field
83
- lines.push(' id String @id @default(cuid())')
84
-
85
- // Track relationship fields for later processing
86
- const relationshipFields: Array<{
87
- name: string
88
- field: RelationshipField
89
- }> = []
90
-
91
- // Add regular fields
92
- for (const [fieldName, fieldConfig] of Object.entries(listConfig.fields)) {
93
- // Skip virtual fields - they don't create database columns
94
- if (fieldConfig.virtual) {
95
- continue
96
- }
97
-
98
- if (fieldConfig.type === 'relationship') {
99
- relationshipFields.push({
100
- name: fieldName,
101
- field: fieldConfig as RelationshipField,
102
- })
103
- continue
104
- }
105
-
106
- const prismaType = mapFieldTypeToPrisma(fieldName, fieldConfig)
107
- if (!prismaType) continue // Skip if no type returned
108
-
109
- const modifiers = getFieldModifiers(fieldName, fieldConfig)
110
-
111
- // Format with proper spacing
112
- const paddedName = fieldName.padEnd(12)
113
- lines.push(` ${paddedName} ${prismaType}${modifiers}`)
114
- }
115
-
116
- // Add relationship fields
117
- for (const { name: fieldName, field: relField } of relationshipFields) {
118
- const { list: targetList } = parseRelationshipRef(relField.ref)
119
- const _modifiers = getFieldModifiers(fieldName, relField)
120
- const paddedName = fieldName.padEnd(12)
121
-
122
- if (relField.many) {
123
- // One-to-many relationship
124
- lines.push(` ${paddedName} ${targetList}[]`)
125
- } else {
126
- // Many-to-one relationship (add foreign key field)
127
- const foreignKeyField = `${fieldName}Id`
128
- const fkPaddedName = foreignKeyField.padEnd(12)
129
-
130
- lines.push(` ${fkPaddedName} String?`)
131
- lines.push(
132
- ` ${paddedName} ${targetList}? @relation(fields: [${foreignKeyField}], references: [id])`,
133
- )
134
- }
135
- }
136
-
137
- // Always add timestamps
138
- lines.push(' createdAt DateTime @default(now())')
139
- lines.push(' updatedAt DateTime @updatedAt')
140
-
141
- lines.push('}')
142
- lines.push('')
143
- }
144
-
145
- return lines.join('\n')
146
- }
147
-
148
- /**
149
- * Write Prisma schema to file
150
- */
151
- export function writePrismaSchema(config: OpenSaasConfig, outputPath: string): void {
152
- const schema = generatePrismaSchema(config)
153
-
154
- // Ensure directory exists
155
- const dir = path.dirname(outputPath)
156
- if (!fs.existsSync(dir)) {
157
- fs.mkdirSync(dir, { recursive: true })
158
- }
159
-
160
- fs.writeFileSync(outputPath, schema, 'utf-8')
161
- }
@@ -1,268 +0,0 @@
1
- import { describe, it, expect } from 'vitest'
2
- import { generateTypes } from './types.js'
3
- import type { OpenSaasConfig } from '@opensaas/stack-core'
4
- import { text, integer, relationship, checkbox } from '@opensaas/stack-core/fields'
5
-
6
- describe('Types Generator', () => {
7
- describe('generateTypes', () => {
8
- it('should generate type definitions for basic model', () => {
9
- const config: OpenSaasConfig = {
10
- db: {
11
- provider: 'sqlite',
12
- },
13
- lists: {
14
- User: {
15
- fields: {
16
- name: text({ validation: { isRequired: true } }),
17
- email: text({ validation: { isRequired: true } }),
18
- },
19
- },
20
- },
21
- }
22
-
23
- const types = generateTypes(config)
24
-
25
- expect(types).toMatchSnapshot()
26
- })
27
-
28
- it('should generate CreateInput type', () => {
29
- const config: OpenSaasConfig = {
30
- db: {
31
- provider: 'sqlite',
32
- },
33
- lists: {
34
- Post: {
35
- fields: {
36
- title: text({ validation: { isRequired: true } }),
37
- content: text(),
38
- },
39
- },
40
- },
41
- }
42
-
43
- const types = generateTypes(config)
44
-
45
- expect(types).toMatchSnapshot()
46
- })
47
-
48
- it('should generate UpdateInput type', () => {
49
- const config: OpenSaasConfig = {
50
- db: {
51
- provider: 'sqlite',
52
- },
53
- lists: {
54
- Post: {
55
- fields: {
56
- title: text({ validation: { isRequired: true } }),
57
- content: text(),
58
- },
59
- },
60
- },
61
- }
62
-
63
- const types = generateTypes(config)
64
-
65
- expect(types).toMatchSnapshot()
66
- })
67
-
68
- it('should generate WhereInput type', () => {
69
- const config: OpenSaasConfig = {
70
- db: {
71
- provider: 'sqlite',
72
- },
73
- lists: {
74
- User: {
75
- fields: {
76
- name: text(),
77
- },
78
- },
79
- },
80
- }
81
-
82
- const types = generateTypes(config)
83
-
84
- expect(types).toMatchSnapshot()
85
- })
86
-
87
- it('should generate Context type with all operations', () => {
88
- const config: OpenSaasConfig = {
89
- db: {
90
- provider: 'sqlite',
91
- },
92
- lists: {
93
- User: {
94
- fields: {
95
- name: text(),
96
- },
97
- },
98
- },
99
- }
100
-
101
- const types = generateTypes(config)
102
-
103
- expect(types).toMatchSnapshot()
104
- })
105
-
106
- it('should handle relationship fields in types', () => {
107
- const config: OpenSaasConfig = {
108
- db: {
109
- provider: 'sqlite',
110
- },
111
- lists: {
112
- User: {
113
- fields: {
114
- name: text(),
115
- posts: relationship({ ref: 'Post.author', many: true }),
116
- },
117
- },
118
- Post: {
119
- fields: {
120
- title: text(),
121
- author: relationship({ ref: 'User.posts' }),
122
- },
123
- },
124
- },
125
- }
126
-
127
- const types = generateTypes(config)
128
-
129
- expect(types).toMatchSnapshot()
130
- })
131
-
132
- it('should handle relationship fields in CreateInput', () => {
133
- const config: OpenSaasConfig = {
134
- db: {
135
- provider: 'sqlite',
136
- },
137
- lists: {
138
- Post: {
139
- fields: {
140
- title: text(),
141
- author: relationship({ ref: 'User.posts' }),
142
- },
143
- },
144
- User: {
145
- fields: {
146
- name: text(),
147
- },
148
- },
149
- },
150
- }
151
-
152
- const types = generateTypes(config)
153
-
154
- expect(types).toMatchSnapshot()
155
- })
156
-
157
- it('should handle relationship fields in UpdateInput', () => {
158
- const config: OpenSaasConfig = {
159
- db: {
160
- provider: 'sqlite',
161
- },
162
- lists: {
163
- Post: {
164
- fields: {
165
- title: text(),
166
- author: relationship({ ref: 'User.posts' }),
167
- },
168
- },
169
- User: {
170
- fields: {
171
- name: text(),
172
- },
173
- },
174
- },
175
- }
176
-
177
- const types = generateTypes(config)
178
-
179
- expect(types).toMatchSnapshot()
180
- })
181
-
182
- it('should generate types for multiple lists', () => {
183
- const config: OpenSaasConfig = {
184
- db: {
185
- provider: 'sqlite',
186
- },
187
- lists: {
188
- User: {
189
- fields: {
190
- name: text(),
191
- },
192
- },
193
- Post: {
194
- fields: {
195
- title: text(),
196
- },
197
- },
198
- Comment: {
199
- fields: {
200
- content: text(),
201
- },
202
- },
203
- },
204
- }
205
-
206
- const types = generateTypes(config)
207
-
208
- expect(types).toMatchSnapshot()
209
- })
210
-
211
- it('should handle integer fields correctly', () => {
212
- const config: OpenSaasConfig = {
213
- db: {
214
- provider: 'sqlite',
215
- },
216
- lists: {
217
- Product: {
218
- fields: {
219
- name: text(),
220
- price: integer(),
221
- },
222
- },
223
- },
224
- }
225
-
226
- const types = generateTypes(config)
227
-
228
- expect(types).toContain('price:')
229
- expect(types).toContain('number')
230
- })
231
-
232
- it('should handle checkbox fields correctly', () => {
233
- const config: OpenSaasConfig = {
234
- db: {
235
- provider: 'sqlite',
236
- },
237
- lists: {
238
- Post: {
239
- fields: {
240
- title: text(),
241
- isPublished: checkbox(),
242
- },
243
- },
244
- },
245
- }
246
-
247
- const types = generateTypes(config)
248
-
249
- expect(types).toContain('isPublished:')
250
- expect(types).toContain('boolean')
251
- })
252
-
253
- it('should include header comment', () => {
254
- const config: OpenSaasConfig = {
255
- db: {
256
- provider: 'sqlite',
257
- },
258
- lists: {},
259
- }
260
-
261
- const types = generateTypes(config)
262
-
263
- expect(types).toContain('/**')
264
- expect(types).toContain('Generated types from OpenSaas configuration')
265
- expect(types).toContain('DO NOT EDIT')
266
- })
267
- })
268
- })