@opensaas/stack-cli 0.1.0 → 0.1.2

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 (49) hide show
  1. package/.turbo/turbo-build.log +1 -1
  2. package/CHANGELOG.md +21 -0
  3. package/CLAUDE.md +249 -0
  4. package/LICENSE +21 -0
  5. package/README.md +65 -5
  6. package/dist/commands/generate.d.ts.map +1 -1
  7. package/dist/commands/generate.js +6 -1
  8. package/dist/commands/generate.js.map +1 -1
  9. package/dist/commands/init.d.ts +9 -1
  10. package/dist/commands/init.d.ts.map +1 -1
  11. package/dist/commands/init.js +27 -338
  12. package/dist/commands/init.js.map +1 -1
  13. package/dist/generator/context.d.ts.map +1 -1
  14. package/dist/generator/context.js +78 -3
  15. package/dist/generator/context.js.map +1 -1
  16. package/dist/generator/index.d.ts +1 -0
  17. package/dist/generator/index.d.ts.map +1 -1
  18. package/dist/generator/index.js +1 -0
  19. package/dist/generator/index.js.map +1 -1
  20. package/dist/generator/mcp.d.ts +14 -0
  21. package/dist/generator/mcp.d.ts.map +1 -0
  22. package/dist/generator/mcp.js +193 -0
  23. package/dist/generator/mcp.js.map +1 -0
  24. package/dist/generator/types.d.ts.map +1 -1
  25. package/dist/generator/types.js +11 -33
  26. package/dist/generator/types.js.map +1 -1
  27. package/dist/index.js +10 -4
  28. package/dist/index.js.map +1 -1
  29. package/package.json +9 -3
  30. package/src/commands/__snapshots__/generate.test.ts.snap +265 -0
  31. package/src/commands/dev.test.ts +216 -0
  32. package/src/commands/generate.test.ts +272 -0
  33. package/src/commands/generate.ts +7 -0
  34. package/src/commands/init.ts +28 -361
  35. package/src/generator/__snapshots__/context.test.ts.snap +137 -0
  36. package/src/generator/__snapshots__/prisma.test.ts.snap +182 -0
  37. package/src/generator/__snapshots__/types.test.ts.snap +512 -0
  38. package/src/generator/context.test.ts +145 -0
  39. package/src/generator/context.ts +80 -3
  40. package/src/generator/index.ts +1 -0
  41. package/src/generator/mcp.test.ts +393 -0
  42. package/src/generator/mcp.ts +221 -0
  43. package/src/generator/prisma.test.ts +221 -0
  44. package/src/generator/types.test.ts +280 -0
  45. package/src/generator/types.ts +14 -36
  46. package/src/index.ts +8 -4
  47. package/tsconfig.json +1 -1
  48. package/tsconfig.tsbuildinfo +1 -1
  49. package/vitest.config.ts +26 -0
@@ -0,0 +1,512 @@
1
+ // Vitest Snapshot v1, https://vitest.dev/guide/snapshot.html
2
+
3
+ exports[`Types Generator > generateTypes > should generate Context type with all operations 1`] = `
4
+ "/**
5
+ * Generated types from OpenSaas configuration
6
+ * DO NOT EDIT - This file is automatically generated
7
+ */
8
+
9
+ import type { Session as OpensaasSession, StorageUtils, ServerActionProps, AccessControlledDB } from '@opensaas/stack-core'
10
+ import type { PrismaClient } from './prisma-client'
11
+
12
+ export type User = {
13
+ id: string
14
+ name: string | null
15
+ createdAt: Date
16
+ updatedAt: Date
17
+ }
18
+
19
+ export type UserCreateInput = {
20
+ name?: string
21
+ }
22
+
23
+ export type UserUpdateInput = {
24
+ name?: string
25
+ }
26
+
27
+ export type UserWhereInput = {
28
+ id?: string
29
+ AND?: Array<UserWhereInput>
30
+ OR?: Array<UserWhereInput>
31
+ NOT?: UserWhereInput
32
+ name?: { equals?: string, not?: string }
33
+ }
34
+
35
+ export type Context<TSession extends OpensaasSession = OpensaasSession> = {
36
+ db: AccessControlledDB<PrismaClient>
37
+ session: TSession
38
+ prisma: PrismaClient
39
+ storage: StorageUtils
40
+ serverAction: (props: ServerActionProps) => Promise<unknown>
41
+ }"
42
+ `;
43
+
44
+ exports[`Types Generator > generateTypes > should generate CreateInput type 1`] = `
45
+ "/**
46
+ * Generated types from OpenSaas configuration
47
+ * DO NOT EDIT - This file is automatically generated
48
+ */
49
+
50
+ import type { Session as OpensaasSession, StorageUtils, ServerActionProps, AccessControlledDB } from '@opensaas/stack-core'
51
+ import type { PrismaClient } from './prisma-client'
52
+
53
+ export type Post = {
54
+ id: string
55
+ title: string
56
+ content: string | null
57
+ createdAt: Date
58
+ updatedAt: Date
59
+ }
60
+
61
+ export type PostCreateInput = {
62
+ title: string
63
+ content?: string
64
+ }
65
+
66
+ export type PostUpdateInput = {
67
+ title?: string
68
+ content?: string
69
+ }
70
+
71
+ export type PostWhereInput = {
72
+ id?: string
73
+ AND?: Array<PostWhereInput>
74
+ OR?: Array<PostWhereInput>
75
+ NOT?: PostWhereInput
76
+ title?: { equals?: string, not?: string }
77
+ content?: { equals?: string, not?: string }
78
+ }
79
+
80
+ export type Context<TSession extends OpensaasSession = OpensaasSession> = {
81
+ db: AccessControlledDB<PrismaClient>
82
+ session: TSession
83
+ prisma: PrismaClient
84
+ storage: StorageUtils
85
+ serverAction: (props: ServerActionProps) => Promise<unknown>
86
+ }"
87
+ `;
88
+
89
+ exports[`Types Generator > generateTypes > should generate UpdateInput type 1`] = `
90
+ "/**
91
+ * Generated types from OpenSaas configuration
92
+ * DO NOT EDIT - This file is automatically generated
93
+ */
94
+
95
+ import type { Session as OpensaasSession, StorageUtils, ServerActionProps, AccessControlledDB } from '@opensaas/stack-core'
96
+ import type { PrismaClient } from './prisma-client'
97
+
98
+ export type Post = {
99
+ id: string
100
+ title: string
101
+ content: string | null
102
+ createdAt: Date
103
+ updatedAt: Date
104
+ }
105
+
106
+ export type PostCreateInput = {
107
+ title: string
108
+ content?: string
109
+ }
110
+
111
+ export type PostUpdateInput = {
112
+ title?: string
113
+ content?: string
114
+ }
115
+
116
+ export type PostWhereInput = {
117
+ id?: string
118
+ AND?: Array<PostWhereInput>
119
+ OR?: Array<PostWhereInput>
120
+ NOT?: PostWhereInput
121
+ title?: { equals?: string, not?: string }
122
+ content?: { equals?: string, not?: string }
123
+ }
124
+
125
+ export type Context<TSession extends OpensaasSession = OpensaasSession> = {
126
+ db: AccessControlledDB<PrismaClient>
127
+ session: TSession
128
+ prisma: PrismaClient
129
+ storage: StorageUtils
130
+ serverAction: (props: ServerActionProps) => Promise<unknown>
131
+ }"
132
+ `;
133
+
134
+ exports[`Types Generator > generateTypes > should generate WhereInput type 1`] = `
135
+ "/**
136
+ * Generated types from OpenSaas configuration
137
+ * DO NOT EDIT - This file is automatically generated
138
+ */
139
+
140
+ import type { Session as OpensaasSession, StorageUtils, ServerActionProps, AccessControlledDB } from '@opensaas/stack-core'
141
+ import type { PrismaClient } from './prisma-client'
142
+
143
+ export type User = {
144
+ id: string
145
+ name: string | null
146
+ createdAt: Date
147
+ updatedAt: Date
148
+ }
149
+
150
+ export type UserCreateInput = {
151
+ name?: string
152
+ }
153
+
154
+ export type UserUpdateInput = {
155
+ name?: string
156
+ }
157
+
158
+ export type UserWhereInput = {
159
+ id?: string
160
+ AND?: Array<UserWhereInput>
161
+ OR?: Array<UserWhereInput>
162
+ NOT?: UserWhereInput
163
+ name?: { equals?: string, not?: string }
164
+ }
165
+
166
+ export type Context<TSession extends OpensaasSession = OpensaasSession> = {
167
+ db: AccessControlledDB<PrismaClient>
168
+ session: TSession
169
+ prisma: PrismaClient
170
+ storage: StorageUtils
171
+ serverAction: (props: ServerActionProps) => Promise<unknown>
172
+ }"
173
+ `;
174
+
175
+ exports[`Types Generator > generateTypes > should generate type definitions for basic model 1`] = `
176
+ "/**
177
+ * Generated types from OpenSaas configuration
178
+ * DO NOT EDIT - This file is automatically generated
179
+ */
180
+
181
+ import type { Session as OpensaasSession, StorageUtils, ServerActionProps, AccessControlledDB } from '@opensaas/stack-core'
182
+ import type { PrismaClient } from './prisma-client'
183
+
184
+ export type User = {
185
+ id: string
186
+ name: string
187
+ email: string
188
+ createdAt: Date
189
+ updatedAt: Date
190
+ }
191
+
192
+ export type UserCreateInput = {
193
+ name: string
194
+ email: string
195
+ }
196
+
197
+ export type UserUpdateInput = {
198
+ name?: string
199
+ email?: string
200
+ }
201
+
202
+ export type UserWhereInput = {
203
+ id?: string
204
+ AND?: Array<UserWhereInput>
205
+ OR?: Array<UserWhereInput>
206
+ NOT?: UserWhereInput
207
+ name?: { equals?: string, not?: string }
208
+ email?: { equals?: string, not?: string }
209
+ }
210
+
211
+ export type Context<TSession extends OpensaasSession = OpensaasSession> = {
212
+ db: AccessControlledDB<PrismaClient>
213
+ session: TSession
214
+ prisma: PrismaClient
215
+ storage: StorageUtils
216
+ serverAction: (props: ServerActionProps) => Promise<unknown>
217
+ }"
218
+ `;
219
+
220
+ exports[`Types Generator > generateTypes > should generate types for multiple lists 1`] = `
221
+ "/**
222
+ * Generated types from OpenSaas configuration
223
+ * DO NOT EDIT - This file is automatically generated
224
+ */
225
+
226
+ import type { Session as OpensaasSession, StorageUtils, ServerActionProps, AccessControlledDB } from '@opensaas/stack-core'
227
+ import type { PrismaClient } from './prisma-client'
228
+
229
+ export type User = {
230
+ id: string
231
+ name: string | null
232
+ createdAt: Date
233
+ updatedAt: Date
234
+ }
235
+
236
+ export type UserCreateInput = {
237
+ name?: string
238
+ }
239
+
240
+ export type UserUpdateInput = {
241
+ name?: string
242
+ }
243
+
244
+ export type UserWhereInput = {
245
+ id?: string
246
+ AND?: Array<UserWhereInput>
247
+ OR?: Array<UserWhereInput>
248
+ NOT?: UserWhereInput
249
+ name?: { equals?: string, not?: string }
250
+ }
251
+
252
+ export type Post = {
253
+ id: string
254
+ title: string | null
255
+ createdAt: Date
256
+ updatedAt: Date
257
+ }
258
+
259
+ export type PostCreateInput = {
260
+ title?: string
261
+ }
262
+
263
+ export type PostUpdateInput = {
264
+ title?: string
265
+ }
266
+
267
+ export type PostWhereInput = {
268
+ id?: string
269
+ AND?: Array<PostWhereInput>
270
+ OR?: Array<PostWhereInput>
271
+ NOT?: PostWhereInput
272
+ title?: { equals?: string, not?: string }
273
+ }
274
+
275
+ export type Comment = {
276
+ id: string
277
+ content: string | null
278
+ createdAt: Date
279
+ updatedAt: Date
280
+ }
281
+
282
+ export type CommentCreateInput = {
283
+ content?: string
284
+ }
285
+
286
+ export type CommentUpdateInput = {
287
+ content?: string
288
+ }
289
+
290
+ export type CommentWhereInput = {
291
+ id?: string
292
+ AND?: Array<CommentWhereInput>
293
+ OR?: Array<CommentWhereInput>
294
+ NOT?: CommentWhereInput
295
+ content?: { equals?: string, not?: string }
296
+ }
297
+
298
+ export type Context<TSession extends OpensaasSession = OpensaasSession> = {
299
+ db: AccessControlledDB<PrismaClient>
300
+ session: TSession
301
+ prisma: PrismaClient
302
+ storage: StorageUtils
303
+ serverAction: (props: ServerActionProps) => Promise<unknown>
304
+ }"
305
+ `;
306
+
307
+ exports[`Types Generator > generateTypes > should handle relationship fields in CreateInput 1`] = `
308
+ "/**
309
+ * Generated types from OpenSaas configuration
310
+ * DO NOT EDIT - This file is automatically generated
311
+ */
312
+
313
+ import type { Session as OpensaasSession, StorageUtils, ServerActionProps, AccessControlledDB } from '@opensaas/stack-core'
314
+ import type { PrismaClient } from './prisma-client'
315
+
316
+ export type Post = {
317
+ id: string
318
+ title: string | null
319
+ authorId: string | null
320
+ author: User | null
321
+ createdAt: Date
322
+ updatedAt: Date
323
+ }
324
+
325
+ export type PostCreateInput = {
326
+ title?: string
327
+ author?: { connect: { id: string } }
328
+ }
329
+
330
+ export type PostUpdateInput = {
331
+ title?: string
332
+ author?: { connect: { id: string } } | { disconnect: true }
333
+ }
334
+
335
+ export type PostWhereInput = {
336
+ id?: string
337
+ AND?: Array<PostWhereInput>
338
+ OR?: Array<PostWhereInput>
339
+ NOT?: PostWhereInput
340
+ title?: { equals?: string, not?: string }
341
+ }
342
+
343
+ export type User = {
344
+ id: string
345
+ name: string | null
346
+ createdAt: Date
347
+ updatedAt: Date
348
+ }
349
+
350
+ export type UserCreateInput = {
351
+ name?: string
352
+ }
353
+
354
+ export type UserUpdateInput = {
355
+ name?: string
356
+ }
357
+
358
+ export type UserWhereInput = {
359
+ id?: string
360
+ AND?: Array<UserWhereInput>
361
+ OR?: Array<UserWhereInput>
362
+ NOT?: UserWhereInput
363
+ name?: { equals?: string, not?: string }
364
+ }
365
+
366
+ export type Context<TSession extends OpensaasSession = OpensaasSession> = {
367
+ db: AccessControlledDB<PrismaClient>
368
+ session: TSession
369
+ prisma: PrismaClient
370
+ storage: StorageUtils
371
+ serverAction: (props: ServerActionProps) => Promise<unknown>
372
+ }"
373
+ `;
374
+
375
+ exports[`Types Generator > generateTypes > should handle relationship fields in UpdateInput 1`] = `
376
+ "/**
377
+ * Generated types from OpenSaas configuration
378
+ * DO NOT EDIT - This file is automatically generated
379
+ */
380
+
381
+ import type { Session as OpensaasSession, StorageUtils, ServerActionProps, AccessControlledDB } from '@opensaas/stack-core'
382
+ import type { PrismaClient } from './prisma-client'
383
+
384
+ export type Post = {
385
+ id: string
386
+ title: string | null
387
+ authorId: string | null
388
+ author: User | null
389
+ createdAt: Date
390
+ updatedAt: Date
391
+ }
392
+
393
+ export type PostCreateInput = {
394
+ title?: string
395
+ author?: { connect: { id: string } }
396
+ }
397
+
398
+ export type PostUpdateInput = {
399
+ title?: string
400
+ author?: { connect: { id: string } } | { disconnect: true }
401
+ }
402
+
403
+ export type PostWhereInput = {
404
+ id?: string
405
+ AND?: Array<PostWhereInput>
406
+ OR?: Array<PostWhereInput>
407
+ NOT?: PostWhereInput
408
+ title?: { equals?: string, not?: string }
409
+ }
410
+
411
+ export type User = {
412
+ id: string
413
+ name: string | null
414
+ createdAt: Date
415
+ updatedAt: Date
416
+ }
417
+
418
+ export type UserCreateInput = {
419
+ name?: string
420
+ }
421
+
422
+ export type UserUpdateInput = {
423
+ name?: string
424
+ }
425
+
426
+ export type UserWhereInput = {
427
+ id?: string
428
+ AND?: Array<UserWhereInput>
429
+ OR?: Array<UserWhereInput>
430
+ NOT?: UserWhereInput
431
+ name?: { equals?: string, not?: string }
432
+ }
433
+
434
+ export type Context<TSession extends OpensaasSession = OpensaasSession> = {
435
+ db: AccessControlledDB<PrismaClient>
436
+ session: TSession
437
+ prisma: PrismaClient
438
+ storage: StorageUtils
439
+ serverAction: (props: ServerActionProps) => Promise<unknown>
440
+ }"
441
+ `;
442
+
443
+ exports[`Types Generator > generateTypes > should handle relationship fields in types 1`] = `
444
+ "/**
445
+ * Generated types from OpenSaas configuration
446
+ * DO NOT EDIT - This file is automatically generated
447
+ */
448
+
449
+ import type { Session as OpensaasSession, StorageUtils, ServerActionProps, AccessControlledDB } from '@opensaas/stack-core'
450
+ import type { PrismaClient } from './prisma-client'
451
+
452
+ export type User = {
453
+ id: string
454
+ name: string | null
455
+ posts: Post[]
456
+ createdAt: Date
457
+ updatedAt: Date
458
+ }
459
+
460
+ export type UserCreateInput = {
461
+ name?: string
462
+ posts?: { connect: Array<{ id: string }> }
463
+ }
464
+
465
+ export type UserUpdateInput = {
466
+ name?: string
467
+ posts?: { connect: Array<{ id: string }>, disconnect: Array<{ id: string }> }
468
+ }
469
+
470
+ export type UserWhereInput = {
471
+ id?: string
472
+ AND?: Array<UserWhereInput>
473
+ OR?: Array<UserWhereInput>
474
+ NOT?: UserWhereInput
475
+ name?: { equals?: string, not?: string }
476
+ }
477
+
478
+ export type Post = {
479
+ id: string
480
+ title: string | null
481
+ authorId: string | null
482
+ author: User | null
483
+ createdAt: Date
484
+ updatedAt: Date
485
+ }
486
+
487
+ export type PostCreateInput = {
488
+ title?: string
489
+ author?: { connect: { id: string } }
490
+ }
491
+
492
+ export type PostUpdateInput = {
493
+ title?: string
494
+ author?: { connect: { id: string } } | { disconnect: true }
495
+ }
496
+
497
+ export type PostWhereInput = {
498
+ id?: string
499
+ AND?: Array<PostWhereInput>
500
+ OR?: Array<PostWhereInput>
501
+ NOT?: PostWhereInput
502
+ title?: { equals?: string, not?: string }
503
+ }
504
+
505
+ export type Context<TSession extends OpensaasSession = OpensaasSession> = {
506
+ db: AccessControlledDB<PrismaClient>
507
+ session: TSession
508
+ prisma: PrismaClient
509
+ storage: StorageUtils
510
+ serverAction: (props: ServerActionProps) => Promise<unknown>
511
+ }"
512
+ `;
@@ -0,0 +1,145 @@
1
+ import { describe, it, expect } from 'vitest'
2
+ import { generateContext } from './context.js'
3
+ import type { OpenSaasConfig } from '@opensaas/stack-core'
4
+ import { text } from '@opensaas/stack-core/fields'
5
+
6
+ describe('Context Generator', () => {
7
+ describe('generateContext', () => {
8
+ it('should generate context factory with default Prisma client', () => {
9
+ const config: OpenSaasConfig = {
10
+ db: {
11
+ provider: 'sqlite',
12
+ url: 'file:./dev.db',
13
+ },
14
+ lists: {
15
+ User: {
16
+ fields: {
17
+ name: text(),
18
+ },
19
+ },
20
+ },
21
+ }
22
+
23
+ const context = generateContext(config)
24
+
25
+ expect(context).toMatchSnapshot()
26
+ })
27
+
28
+ it('should generate context factory with custom Prisma client constructor', () => {
29
+ const config: OpenSaasConfig = {
30
+ db: {
31
+ provider: 'postgresql',
32
+ url: process.env.DATABASE_URL || 'postgresql://localhost:5432/db',
33
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
34
+ prismaClientConstructor: ((PrismaClient: any) => new PrismaClient()) as any,
35
+ },
36
+ lists: {
37
+ User: {
38
+ fields: {
39
+ name: text(),
40
+ },
41
+ },
42
+ },
43
+ }
44
+
45
+ const context = generateContext(config)
46
+
47
+ expect(context).toMatchSnapshot()
48
+ })
49
+
50
+ it('should include singleton pattern for Prisma client', () => {
51
+ const config: OpenSaasConfig = {
52
+ db: {
53
+ provider: 'sqlite',
54
+ url: 'file:./dev.db',
55
+ },
56
+ lists: {},
57
+ }
58
+
59
+ const context = generateContext(config)
60
+
61
+ expect(context).toContain('const globalForPrisma')
62
+ expect(context).toContain('globalThis as unknown as { prisma: PrismaClient | undefined }')
63
+ expect(context).toContain('globalForPrisma.prisma')
64
+ expect(context).toContain("if (process.env.NODE_ENV !== 'production')")
65
+ })
66
+
67
+ it('should include JSDoc comments', () => {
68
+ const config: OpenSaasConfig = {
69
+ db: {
70
+ provider: 'sqlite',
71
+ url: 'file:./dev.db',
72
+ },
73
+ lists: {},
74
+ }
75
+
76
+ const context = generateContext(config)
77
+
78
+ expect(context).toContain('/**')
79
+ expect(context).toContain('Auto-generated context factory')
80
+ expect(context).toContain('DO NOT EDIT')
81
+ expect(context).toContain('Get OpenSaas context with optional session')
82
+ expect(context).toContain('@param session')
83
+ expect(context).toContain('@example')
84
+ })
85
+
86
+ it('should include usage examples in comments', () => {
87
+ const config: OpenSaasConfig = {
88
+ db: {
89
+ provider: 'sqlite',
90
+ url: 'file:./dev.db',
91
+ },
92
+ lists: {},
93
+ }
94
+
95
+ const context = generateContext(config)
96
+
97
+ expect(context).toContain('// Anonymous access')
98
+ expect(context).toContain('const context = getContext()')
99
+ expect(context).toContain('// Authenticated access')
100
+ expect(context).toContain("const context = getContext({ userId: 'user-123' })")
101
+ })
102
+
103
+ it('should export rawOpensaasContext', () => {
104
+ const config: OpenSaasConfig = {
105
+ db: {
106
+ provider: 'sqlite',
107
+ url: 'file:./dev.db',
108
+ },
109
+ lists: {},
110
+ }
111
+
112
+ const context = generateContext(config)
113
+
114
+ expect(context).toContain('export const rawOpensaasContext = getContext()')
115
+ })
116
+
117
+ it('should type session parameter correctly', () => {
118
+ const config: OpenSaasConfig = {
119
+ db: {
120
+ provider: 'sqlite',
121
+ url: 'file:./dev.db',
122
+ },
123
+ lists: {},
124
+ }
125
+
126
+ const context = generateContext(config)
127
+
128
+ expect(context).toContain('session?: TSession')
129
+ expect(context).toContain('<TSession extends OpensaasSession = OpensaasSession>')
130
+ })
131
+
132
+ it('should call getOpensaasContext with correct arguments', () => {
133
+ const config: OpenSaasConfig = {
134
+ db: {
135
+ provider: 'sqlite',
136
+ url: 'file:./dev.db',
137
+ },
138
+ lists: {},
139
+ }
140
+
141
+ const context = generateContext(config)
142
+ expect(context).toContain('getOpensaasContext(config, prisma, session ?? null, storage)')
143
+ })
144
+ })
145
+ })