@opensaas/stack-cli 0.4.0 → 0.6.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 (90) hide show
  1. package/README.md +76 -0
  2. package/dist/commands/migrate.d.ts +9 -0
  3. package/dist/commands/migrate.d.ts.map +1 -0
  4. package/dist/commands/migrate.js +299 -0
  5. package/dist/commands/migrate.js.map +1 -0
  6. package/dist/index.js +3 -0
  7. package/dist/index.js.map +1 -1
  8. package/dist/mcp/lib/documentation-provider.d.ts +23 -0
  9. package/dist/mcp/lib/documentation-provider.d.ts.map +1 -1
  10. package/dist/mcp/lib/documentation-provider.js +471 -0
  11. package/dist/mcp/lib/documentation-provider.js.map +1 -1
  12. package/dist/mcp/lib/wizards/migration-wizard.d.ts +80 -0
  13. package/dist/mcp/lib/wizards/migration-wizard.d.ts.map +1 -0
  14. package/dist/mcp/lib/wizards/migration-wizard.js +499 -0
  15. package/dist/mcp/lib/wizards/migration-wizard.js.map +1 -0
  16. package/dist/mcp/server/index.d.ts.map +1 -1
  17. package/dist/mcp/server/index.js +103 -0
  18. package/dist/mcp/server/index.js.map +1 -1
  19. package/dist/mcp/server/stack-mcp-server.d.ts +85 -0
  20. package/dist/mcp/server/stack-mcp-server.d.ts.map +1 -1
  21. package/dist/mcp/server/stack-mcp-server.js +219 -0
  22. package/dist/mcp/server/stack-mcp-server.js.map +1 -1
  23. package/dist/migration/generators/migration-generator.d.ts +60 -0
  24. package/dist/migration/generators/migration-generator.d.ts.map +1 -0
  25. package/dist/migration/generators/migration-generator.js +510 -0
  26. package/dist/migration/generators/migration-generator.js.map +1 -0
  27. package/dist/migration/introspectors/index.d.ts +12 -0
  28. package/dist/migration/introspectors/index.d.ts.map +1 -0
  29. package/dist/migration/introspectors/index.js +10 -0
  30. package/dist/migration/introspectors/index.js.map +1 -0
  31. package/dist/migration/introspectors/keystone-introspector.d.ts +59 -0
  32. package/dist/migration/introspectors/keystone-introspector.d.ts.map +1 -0
  33. package/dist/migration/introspectors/keystone-introspector.js +229 -0
  34. package/dist/migration/introspectors/keystone-introspector.js.map +1 -0
  35. package/dist/migration/introspectors/nextjs-introspector.d.ts +59 -0
  36. package/dist/migration/introspectors/nextjs-introspector.d.ts.map +1 -0
  37. package/dist/migration/introspectors/nextjs-introspector.js +159 -0
  38. package/dist/migration/introspectors/nextjs-introspector.js.map +1 -0
  39. package/dist/migration/introspectors/prisma-introspector.d.ts +45 -0
  40. package/dist/migration/introspectors/prisma-introspector.d.ts.map +1 -0
  41. package/dist/migration/introspectors/prisma-introspector.js +190 -0
  42. package/dist/migration/introspectors/prisma-introspector.js.map +1 -0
  43. package/dist/migration/types.d.ts +86 -0
  44. package/dist/migration/types.d.ts.map +1 -0
  45. package/dist/migration/types.js +5 -0
  46. package/dist/migration/types.js.map +1 -0
  47. package/package.json +10 -2
  48. package/plugin/.claude-plugin/plugin.json +15 -0
  49. package/plugin/README.md +112 -0
  50. package/plugin/agents/migration-assistant.md +150 -0
  51. package/plugin/commands/analyze-schema.md +34 -0
  52. package/plugin/commands/generate-config.md +33 -0
  53. package/plugin/commands/validate-migration.md +34 -0
  54. package/plugin/skills/opensaas-migration/SKILL.md +192 -0
  55. package/.turbo/turbo-build.log +0 -4
  56. package/CHANGELOG.md +0 -410
  57. package/CLAUDE.md +0 -298
  58. package/src/commands/__snapshots__/generate.test.ts.snap +0 -413
  59. package/src/commands/dev.test.ts +0 -215
  60. package/src/commands/dev.ts +0 -48
  61. package/src/commands/generate.test.ts +0 -282
  62. package/src/commands/generate.ts +0 -182
  63. package/src/commands/init.ts +0 -34
  64. package/src/commands/mcp.ts +0 -135
  65. package/src/generator/__snapshots__/context.test.ts.snap +0 -361
  66. package/src/generator/__snapshots__/prisma.test.ts.snap +0 -174
  67. package/src/generator/__snapshots__/types.test.ts.snap +0 -1702
  68. package/src/generator/context.test.ts +0 -139
  69. package/src/generator/context.ts +0 -227
  70. package/src/generator/index.ts +0 -7
  71. package/src/generator/lists.test.ts +0 -335
  72. package/src/generator/lists.ts +0 -140
  73. package/src/generator/plugin-types.ts +0 -147
  74. package/src/generator/prisma-config.ts +0 -46
  75. package/src/generator/prisma-extensions.ts +0 -159
  76. package/src/generator/prisma.test.ts +0 -211
  77. package/src/generator/prisma.ts +0 -161
  78. package/src/generator/types.test.ts +0 -268
  79. package/src/generator/types.ts +0 -537
  80. package/src/index.ts +0 -42
  81. package/src/mcp/lib/documentation-provider.ts +0 -203
  82. package/src/mcp/lib/features/catalog.ts +0 -301
  83. package/src/mcp/lib/generators/feature-generator.ts +0 -598
  84. package/src/mcp/lib/types.ts +0 -89
  85. package/src/mcp/lib/wizards/wizard-engine.ts +0 -427
  86. package/src/mcp/server/index.ts +0 -240
  87. package/src/mcp/server/stack-mcp-server.ts +0 -301
  88. package/tsconfig.json +0 -13
  89. package/tsconfig.tsbuildinfo +0 -1
  90. 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
- })