@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.
- package/.turbo/turbo-build.log +1 -1
- package/CHANGELOG.md +21 -0
- package/CLAUDE.md +249 -0
- package/LICENSE +21 -0
- package/README.md +65 -5
- package/dist/commands/generate.d.ts.map +1 -1
- package/dist/commands/generate.js +6 -1
- package/dist/commands/generate.js.map +1 -1
- package/dist/commands/init.d.ts +9 -1
- package/dist/commands/init.d.ts.map +1 -1
- package/dist/commands/init.js +27 -338
- package/dist/commands/init.js.map +1 -1
- package/dist/generator/context.d.ts.map +1 -1
- package/dist/generator/context.js +78 -3
- package/dist/generator/context.js.map +1 -1
- package/dist/generator/index.d.ts +1 -0
- package/dist/generator/index.d.ts.map +1 -1
- package/dist/generator/index.js +1 -0
- package/dist/generator/index.js.map +1 -1
- package/dist/generator/mcp.d.ts +14 -0
- package/dist/generator/mcp.d.ts.map +1 -0
- package/dist/generator/mcp.js +193 -0
- package/dist/generator/mcp.js.map +1 -0
- package/dist/generator/types.d.ts.map +1 -1
- package/dist/generator/types.js +11 -33
- package/dist/generator/types.js.map +1 -1
- package/dist/index.js +10 -4
- package/dist/index.js.map +1 -1
- package/package.json +9 -3
- package/src/commands/__snapshots__/generate.test.ts.snap +265 -0
- package/src/commands/dev.test.ts +216 -0
- package/src/commands/generate.test.ts +272 -0
- package/src/commands/generate.ts +7 -0
- package/src/commands/init.ts +28 -361
- package/src/generator/__snapshots__/context.test.ts.snap +137 -0
- package/src/generator/__snapshots__/prisma.test.ts.snap +182 -0
- package/src/generator/__snapshots__/types.test.ts.snap +512 -0
- package/src/generator/context.test.ts +145 -0
- package/src/generator/context.ts +80 -3
- package/src/generator/index.ts +1 -0
- package/src/generator/mcp.test.ts +393 -0
- package/src/generator/mcp.ts +221 -0
- package/src/generator/prisma.test.ts +221 -0
- package/src/generator/types.test.ts +280 -0
- package/src/generator/types.ts +14 -36
- package/src/index.ts +8 -4
- package/tsconfig.json +1 -1
- package/tsconfig.tsbuildinfo +1 -1
- package/vitest.config.ts +26 -0
package/src/commands/init.ts
CHANGED
|
@@ -1,367 +1,34 @@
|
|
|
1
|
-
import
|
|
2
|
-
import * as fs from 'fs'
|
|
1
|
+
import { spawn } from 'child_process'
|
|
3
2
|
import chalk from 'chalk'
|
|
4
|
-
import ora from 'ora'
|
|
5
|
-
import prompts from 'prompts'
|
|
6
|
-
|
|
7
|
-
export async function initCommand(projectName: string | undefined) {
|
|
8
|
-
console.log(chalk.bold.cyan('\nš Create OpenSaas Project\n'))
|
|
9
|
-
|
|
10
|
-
// Prompt for project name if not provided
|
|
11
|
-
if (!projectName) {
|
|
12
|
-
const response = await prompts({
|
|
13
|
-
type: 'text',
|
|
14
|
-
name: 'name',
|
|
15
|
-
message: 'Project name:',
|
|
16
|
-
initial: 'my-opensaas-app',
|
|
17
|
-
validate: (value) => {
|
|
18
|
-
if (!value) return 'Project name is required'
|
|
19
|
-
if (!/^[a-z0-9-]+$/.test(value)) {
|
|
20
|
-
return 'Project name must contain only lowercase letters, numbers, and hyphens'
|
|
21
|
-
}
|
|
22
|
-
return true
|
|
23
|
-
},
|
|
24
|
-
})
|
|
25
|
-
|
|
26
|
-
if (!response.name) {
|
|
27
|
-
console.log(chalk.yellow('\nā Cancelled'))
|
|
28
|
-
process.exit(0)
|
|
29
|
-
}
|
|
30
|
-
|
|
31
|
-
projectName = response.name
|
|
32
|
-
}
|
|
33
|
-
|
|
34
|
-
// Type guard to ensure projectName is defined
|
|
35
|
-
if (!projectName) {
|
|
36
|
-
console.error(chalk.red('\nā Project name is required'))
|
|
37
|
-
process.exit(1)
|
|
38
|
-
}
|
|
39
|
-
|
|
40
|
-
const projectPath = path.join(process.cwd(), projectName)
|
|
41
|
-
|
|
42
|
-
// Check if directory already exists
|
|
43
|
-
if (fs.existsSync(projectPath)) {
|
|
44
|
-
console.error(chalk.red(`\nā Directory "${projectName}" already exists`))
|
|
45
|
-
process.exit(1)
|
|
46
|
-
}
|
|
47
|
-
|
|
48
|
-
const spinner = ora('Creating project structure...').start()
|
|
49
|
-
|
|
50
|
-
try {
|
|
51
|
-
// Create project directory
|
|
52
|
-
fs.mkdirSync(projectPath, { recursive: true })
|
|
53
|
-
|
|
54
|
-
// Create basic structure
|
|
55
|
-
fs.mkdirSync(path.join(projectPath, 'app'), { recursive: true })
|
|
56
|
-
fs.mkdirSync(path.join(projectPath, 'lib'), { recursive: true })
|
|
57
|
-
fs.mkdirSync(path.join(projectPath, 'prisma'), { recursive: true })
|
|
58
|
-
|
|
59
|
-
spinner.text = 'Writing configuration files...'
|
|
60
|
-
|
|
61
|
-
// Create package.json
|
|
62
|
-
const packageJson = {
|
|
63
|
-
name: projectName,
|
|
64
|
-
version: '0.1.0',
|
|
65
|
-
private: true,
|
|
66
|
-
scripts: {
|
|
67
|
-
dev: 'next dev',
|
|
68
|
-
build: 'next build',
|
|
69
|
-
start: 'next start',
|
|
70
|
-
generate: 'opensaas generate',
|
|
71
|
-
'db:push': 'prisma db push',
|
|
72
|
-
'db:studio': 'prisma studio',
|
|
73
|
-
},
|
|
74
|
-
dependencies: {
|
|
75
|
-
'@opensaas/stack-core': '^0.1.0',
|
|
76
|
-
'@prisma/client': '^5.7.1',
|
|
77
|
-
next: '^14.0.4',
|
|
78
|
-
react: '^18.2.0',
|
|
79
|
-
'react-dom': '^18.2.0',
|
|
80
|
-
},
|
|
81
|
-
devDependencies: {
|
|
82
|
-
'@opensaas/stack-cli': '^0.1.0',
|
|
83
|
-
'@types/node': '^20.10.0',
|
|
84
|
-
'@types/react': '^18.2.45',
|
|
85
|
-
'@types/react-dom': '^18.2.18',
|
|
86
|
-
prisma: '^5.7.1',
|
|
87
|
-
tsx: '^4.7.0',
|
|
88
|
-
typescript: '^5.3.3',
|
|
89
|
-
},
|
|
90
|
-
}
|
|
91
|
-
|
|
92
|
-
fs.writeFileSync(path.join(projectPath, 'package.json'), JSON.stringify(packageJson, null, 2))
|
|
93
|
-
|
|
94
|
-
// Create tsconfig.json
|
|
95
|
-
const tsConfig = {
|
|
96
|
-
compilerOptions: {
|
|
97
|
-
target: 'ES2022',
|
|
98
|
-
lib: ['dom', 'dom.iterable', 'esnext'],
|
|
99
|
-
allowJs: true,
|
|
100
|
-
skipLibCheck: true,
|
|
101
|
-
strict: true,
|
|
102
|
-
noEmit: true,
|
|
103
|
-
esModuleInterop: true,
|
|
104
|
-
module: 'esnext',
|
|
105
|
-
moduleResolution: 'bundler',
|
|
106
|
-
resolveJsonModule: true,
|
|
107
|
-
isolatedModules: true,
|
|
108
|
-
jsx: 'preserve',
|
|
109
|
-
incremental: true,
|
|
110
|
-
plugins: [{ name: 'next' }],
|
|
111
|
-
paths: {
|
|
112
|
-
'@/*': ['./*'],
|
|
113
|
-
},
|
|
114
|
-
},
|
|
115
|
-
include: ['next-env.d.ts', '**/*.ts', '**/*.tsx', '.next/types/**/*.ts'],
|
|
116
|
-
exclude: ['node_modules'],
|
|
117
|
-
}
|
|
118
|
-
|
|
119
|
-
fs.writeFileSync(path.join(projectPath, 'tsconfig.json'), JSON.stringify(tsConfig, null, 2))
|
|
120
|
-
|
|
121
|
-
// Create next.config.js
|
|
122
|
-
const nextConfig = `/** @type {import('next').NextConfig} */
|
|
123
|
-
const nextConfig = {
|
|
124
|
-
experimental: {
|
|
125
|
-
serverComponentsExternalPackages: ['@prisma/client', '@opensaas/stack-core'],
|
|
126
|
-
},
|
|
127
|
-
}
|
|
128
|
-
|
|
129
|
-
module.exports = nextConfig
|
|
130
|
-
`
|
|
131
|
-
fs.writeFileSync(path.join(projectPath, 'next.config.js'), nextConfig)
|
|
132
|
-
|
|
133
|
-
// Create .env
|
|
134
|
-
const env = `DATABASE_URL="file:./dev.db"
|
|
135
|
-
`
|
|
136
|
-
fs.writeFileSync(path.join(projectPath, '.env'), env)
|
|
137
|
-
|
|
138
|
-
// Create .gitignore
|
|
139
|
-
const gitignore = `# Dependencies
|
|
140
|
-
node_modules
|
|
141
|
-
.pnp
|
|
142
|
-
.pnp.js
|
|
143
|
-
|
|
144
|
-
# Testing
|
|
145
|
-
coverage
|
|
146
|
-
|
|
147
|
-
# Next.js
|
|
148
|
-
.next
|
|
149
|
-
out
|
|
150
|
-
|
|
151
|
-
# Production
|
|
152
|
-
build
|
|
153
|
-
dist
|
|
154
|
-
|
|
155
|
-
# Misc
|
|
156
|
-
.DS_Store
|
|
157
|
-
*.pem
|
|
158
|
-
|
|
159
|
-
# Debug
|
|
160
|
-
npm-debug.log*
|
|
161
|
-
yarn-debug.log*
|
|
162
|
-
yarn-error.log*
|
|
163
|
-
|
|
164
|
-
# Local env files
|
|
165
|
-
.env
|
|
166
|
-
.env.local
|
|
167
|
-
.env.development.local
|
|
168
|
-
.env.test.local
|
|
169
|
-
.env.production.local
|
|
170
|
-
|
|
171
|
-
# Vercel
|
|
172
|
-
.vercel
|
|
173
|
-
|
|
174
|
-
# TypeScript
|
|
175
|
-
*.tsbuildinfo
|
|
176
|
-
|
|
177
|
-
# OpenSaas generated
|
|
178
|
-
.opensaas
|
|
179
|
-
|
|
180
|
-
# Prisma
|
|
181
|
-
prisma/dev.db
|
|
182
|
-
prisma/dev.db-journal
|
|
183
|
-
`
|
|
184
|
-
fs.writeFileSync(path.join(projectPath, '.gitignore'), gitignore)
|
|
185
|
-
|
|
186
|
-
// Create opensaas.config.ts
|
|
187
|
-
const config = `import { config, list } from '@opensaas/stack-core'
|
|
188
|
-
import { text, relationship, password } from '@opensaas/stack-core/fields'
|
|
189
|
-
import type { AccessControl } from '@opensaas/stack-core'
|
|
190
|
-
|
|
191
|
-
// Access control helpers
|
|
192
|
-
const isSignedIn: AccessControl = ({ session }) => {
|
|
193
|
-
return !!session
|
|
194
|
-
}
|
|
195
|
-
|
|
196
|
-
export default config({
|
|
197
|
-
db: {
|
|
198
|
-
provider: 'sqlite',
|
|
199
|
-
url: process.env.DATABASE_URL || 'file:./dev.db',
|
|
200
|
-
},
|
|
201
|
-
|
|
202
|
-
lists: {
|
|
203
|
-
User: list({
|
|
204
|
-
fields: {
|
|
205
|
-
name: text({ validation: { isRequired: true } }),
|
|
206
|
-
email: text({
|
|
207
|
-
validation: { isRequired: true },
|
|
208
|
-
isIndexed: 'unique',
|
|
209
|
-
}),
|
|
210
|
-
password: password({ validation: { isRequired: true } }),
|
|
211
|
-
},
|
|
212
|
-
}),
|
|
213
|
-
},
|
|
214
|
-
|
|
215
|
-
session: {
|
|
216
|
-
getSession: async () => {
|
|
217
|
-
// TODO: Integrate with your auth system
|
|
218
|
-
return null
|
|
219
|
-
}
|
|
220
|
-
},
|
|
221
|
-
|
|
222
|
-
ui: {
|
|
223
|
-
basePath: '/admin',
|
|
224
|
-
}
|
|
225
|
-
})
|
|
226
|
-
`
|
|
227
|
-
fs.writeFileSync(path.join(projectPath, 'opensaas.config.ts'), config)
|
|
228
|
-
|
|
229
|
-
// Create lib/context.ts
|
|
230
|
-
const contextFile = `import { PrismaClient } from '@prisma/client'
|
|
231
|
-
import { getContext as createContext } from '@opensaas/stack-core'
|
|
232
|
-
import config from '../opensaas.config'
|
|
233
|
-
import type { Context } from '../.opensaas/types'
|
|
234
|
-
|
|
235
|
-
// Singleton Prisma client
|
|
236
|
-
const globalForPrisma = globalThis as unknown as {
|
|
237
|
-
prisma: PrismaClient | undefined
|
|
238
|
-
}
|
|
239
|
-
|
|
240
|
-
export const prisma = globalForPrisma.prisma ?? new PrismaClient()
|
|
241
|
-
|
|
242
|
-
if (process.env.NODE_ENV !== 'production') {
|
|
243
|
-
globalForPrisma.prisma = prisma
|
|
244
|
-
}
|
|
245
3
|
|
|
246
4
|
/**
|
|
247
|
-
*
|
|
5
|
+
* Initialize a new OpenSaas Stack project.
|
|
6
|
+
*
|
|
7
|
+
* This command delegates to create-opensaas-app for the actual scaffolding.
|
|
8
|
+
* It's kept here for backwards compatibility with `opensaas init`.
|
|
9
|
+
*
|
|
10
|
+
* @param args - Command line arguments (project name and flags)
|
|
248
11
|
*/
|
|
249
|
-
export async function
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
<ol className="text-sm text-gray-600 list-decimal list-inside space-y-1">
|
|
267
|
-
<li>Run <code className="bg-gray-100 px-2 py-1 rounded">npm run generate</code></li>
|
|
268
|
-
<li>Run <code className="bg-gray-100 px-2 py-1 rounded">npm run db:push</code></li>
|
|
269
|
-
<li>Edit <code className="bg-gray-100 px-2 py-1 rounded">opensaas.config.ts</code> to define your schema</li>
|
|
270
|
-
</ol>
|
|
271
|
-
</div>
|
|
272
|
-
</main>
|
|
273
|
-
)
|
|
274
|
-
}
|
|
275
|
-
`
|
|
276
|
-
fs.writeFileSync(path.join(projectPath, 'app', 'page.tsx'), page)
|
|
277
|
-
|
|
278
|
-
// Create app/layout.tsx
|
|
279
|
-
const layout = `export const metadata = {
|
|
280
|
-
title: '${projectName}',
|
|
281
|
-
description: 'Built with OpenSaas',
|
|
282
|
-
}
|
|
283
|
-
|
|
284
|
-
export default function RootLayout({
|
|
285
|
-
children,
|
|
286
|
-
}: {
|
|
287
|
-
children: React.ReactNode
|
|
288
|
-
}) {
|
|
289
|
-
return (
|
|
290
|
-
<html lang="en">
|
|
291
|
-
<body>{children}</body>
|
|
292
|
-
</html>
|
|
293
|
-
)
|
|
294
|
-
}
|
|
295
|
-
`
|
|
296
|
-
fs.writeFileSync(path.join(projectPath, 'app', 'layout.tsx'), layout)
|
|
297
|
-
|
|
298
|
-
// Create README.md
|
|
299
|
-
const readme = `# ${projectName}
|
|
300
|
-
|
|
301
|
-
Built with [OpenSaas Stack](https://github.com/your-org/opensaas-stack)
|
|
302
|
-
|
|
303
|
-
## Getting Started
|
|
304
|
-
|
|
305
|
-
1. Install dependencies:
|
|
306
|
-
\`\`\`bash
|
|
307
|
-
npm install
|
|
308
|
-
# or
|
|
309
|
-
pnpm install
|
|
310
|
-
\`\`\`
|
|
311
|
-
|
|
312
|
-
2. Generate Prisma schema and types:
|
|
313
|
-
\`\`\`bash
|
|
314
|
-
npm run generate
|
|
315
|
-
\`\`\`
|
|
316
|
-
|
|
317
|
-
3. Push schema to database:
|
|
318
|
-
\`\`\`bash
|
|
319
|
-
npm run db:push
|
|
320
|
-
\`\`\`
|
|
321
|
-
|
|
322
|
-
4. Run the development server:
|
|
323
|
-
\`\`\`bash
|
|
324
|
-
npm run dev
|
|
325
|
-
\`\`\`
|
|
326
|
-
|
|
327
|
-
Open [http://localhost:3000](http://localhost:3000) to see your app.
|
|
328
|
-
|
|
329
|
-
## Project Structure
|
|
330
|
-
|
|
331
|
-
- \`opensaas.config.ts\` - Your schema definition with access control
|
|
332
|
-
- \`lib/context.ts\` - Database context with access control
|
|
333
|
-
- \`app/\` - Next.js app router pages
|
|
334
|
-
- \`prisma/\` - Generated Prisma schema
|
|
335
|
-
- \`.opensaas/\` - Generated TypeScript types
|
|
336
|
-
|
|
337
|
-
## Learn More
|
|
338
|
-
|
|
339
|
-
- [OpenSaas Documentation](https://github.com/your-org/opensaas-stack)
|
|
340
|
-
- [Next.js Documentation](https://nextjs.org/docs)
|
|
341
|
-
- [Prisma Documentation](https://www.prisma.io/docs)
|
|
342
|
-
`
|
|
343
|
-
fs.writeFileSync(path.join(projectPath, 'README.md'), readme)
|
|
344
|
-
|
|
345
|
-
spinner.succeed(chalk.green('Project created successfully!'))
|
|
346
|
-
|
|
347
|
-
console.log(chalk.bold.green(`\n⨠Created ${projectName}\n`))
|
|
348
|
-
console.log(chalk.gray('Next steps:\n'))
|
|
349
|
-
console.log(chalk.cyan(` cd ${projectName}`))
|
|
350
|
-
console.log(chalk.cyan(' npm install'))
|
|
351
|
-
console.log(chalk.cyan(' npm run generate'))
|
|
352
|
-
console.log(chalk.cyan(' npm run db:push'))
|
|
353
|
-
console.log(chalk.cyan(' npm run dev'))
|
|
354
|
-
console.log()
|
|
355
|
-
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
356
|
-
} catch (error: any) {
|
|
357
|
-
spinner.fail(chalk.red('Failed to create project'))
|
|
358
|
-
console.error(chalk.red('\nā Error:'), error.message)
|
|
359
|
-
|
|
360
|
-
// Cleanup on failure
|
|
361
|
-
if (fs.existsSync(projectPath)) {
|
|
362
|
-
fs.rmSync(projectPath, { recursive: true, force: true })
|
|
363
|
-
}
|
|
12
|
+
export async function initCommand(args: string[]) {
|
|
13
|
+
console.log(chalk.dim('Delegating to create-opensaas-app...\n'))
|
|
14
|
+
|
|
15
|
+
// Forward all arguments to create-opensaas-app
|
|
16
|
+
const child = spawn('npx', ['create-opensaas-app@latest', ...args], {
|
|
17
|
+
stdio: 'inherit',
|
|
18
|
+
shell: true,
|
|
19
|
+
})
|
|
20
|
+
|
|
21
|
+
return new Promise<void>((resolve, reject) => {
|
|
22
|
+
child.on('close', (code) => {
|
|
23
|
+
if (code !== 0) {
|
|
24
|
+
reject(new Error(`create-opensaas-app exited with code ${code}`))
|
|
25
|
+
} else {
|
|
26
|
+
resolve()
|
|
27
|
+
}
|
|
28
|
+
})
|
|
364
29
|
|
|
365
|
-
|
|
366
|
-
|
|
30
|
+
child.on('error', (err) => {
|
|
31
|
+
reject(err)
|
|
32
|
+
})
|
|
33
|
+
})
|
|
367
34
|
}
|
|
@@ -0,0 +1,137 @@
|
|
|
1
|
+
// Vitest Snapshot v1, https://vitest.dev/guide/snapshot.html
|
|
2
|
+
|
|
3
|
+
exports[`Context Generator > generateContext > should generate context factory with custom Prisma client constructor 1`] = `
|
|
4
|
+
"/**
|
|
5
|
+
* Auto-generated context factory
|
|
6
|
+
*
|
|
7
|
+
* This module provides a simple API for creating OpenSaas contexts.
|
|
8
|
+
* It abstracts away Prisma client management and configuration.
|
|
9
|
+
*
|
|
10
|
+
* DO NOT EDIT - This file is automatically generated by 'pnpm generate'
|
|
11
|
+
*/
|
|
12
|
+
|
|
13
|
+
import { getContext as getOpensaasContext } from '@opensaas/stack-core'
|
|
14
|
+
import type { Session as OpensaasSession } from '@opensaas/stack-core'
|
|
15
|
+
import { PrismaClient } from './prisma-client'
|
|
16
|
+
import type { Context } from './types'
|
|
17
|
+
import config from '../opensaas.config'
|
|
18
|
+
|
|
19
|
+
// Internal Prisma singleton - managed automatically
|
|
20
|
+
const globalForPrisma = globalThis as unknown as { prisma: PrismaClient | undefined }
|
|
21
|
+
const prisma = globalForPrisma.prisma ?? config.db.prismaClientConstructor!(PrismaClient)
|
|
22
|
+
if (process.env.NODE_ENV !== 'production') globalForPrisma.prisma = prisma
|
|
23
|
+
|
|
24
|
+
/**
|
|
25
|
+
* Storage utilities (not configured)
|
|
26
|
+
*/
|
|
27
|
+
const storage = {
|
|
28
|
+
uploadFile: async () => {
|
|
29
|
+
throw new Error('Storage is not configured. Add storage providers to your opensaas.config.ts')
|
|
30
|
+
},
|
|
31
|
+
uploadImage: async () => {
|
|
32
|
+
throw new Error('Storage is not configured. Add storage providers to your opensaas.config.ts')
|
|
33
|
+
},
|
|
34
|
+
deleteFile: async () => {
|
|
35
|
+
throw new Error('Storage is not configured. Add storage providers to your opensaas.config.ts')
|
|
36
|
+
},
|
|
37
|
+
deleteImage: async () => {
|
|
38
|
+
throw new Error('Storage is not configured. Add storage providers to your opensaas.config.ts')
|
|
39
|
+
},
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
/**
|
|
43
|
+
* Get OpenSaas context with optional session
|
|
44
|
+
*
|
|
45
|
+
* @param session - Optional session object (structure defined by your application)
|
|
46
|
+
*
|
|
47
|
+
* @example
|
|
48
|
+
* \`\`\`typescript
|
|
49
|
+
* // Anonymous access
|
|
50
|
+
* const context = getContext()
|
|
51
|
+
* const posts = await context.db.post.findMany()
|
|
52
|
+
*
|
|
53
|
+
* // Authenticated access
|
|
54
|
+
* const context = getContext({ userId: 'user-123' })
|
|
55
|
+
* const myPosts = await context.db.post.findMany()
|
|
56
|
+
*
|
|
57
|
+
* // With custom session type
|
|
58
|
+
* type CustomSession = { userId: string; email: string; role: string } | null
|
|
59
|
+
* const context = getContext<CustomSession>({ userId: '123', email: 'user@example.com', role: 'admin' })
|
|
60
|
+
* // context.session is now typed as CustomSession
|
|
61
|
+
* \`\`\`
|
|
62
|
+
*/
|
|
63
|
+
export function getContext<TSession extends OpensaasSession = OpensaasSession>(session?: TSession): Context<TSession> {
|
|
64
|
+
return getOpensaasContext(config, prisma, session ?? null, storage) as Context<TSession>
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
export const rawOpensaasContext = getContext()
|
|
68
|
+
"
|
|
69
|
+
`;
|
|
70
|
+
|
|
71
|
+
exports[`Context Generator > generateContext > should generate context factory with default Prisma client 1`] = `
|
|
72
|
+
"/**
|
|
73
|
+
* Auto-generated context factory
|
|
74
|
+
*
|
|
75
|
+
* This module provides a simple API for creating OpenSaas contexts.
|
|
76
|
+
* It abstracts away Prisma client management and configuration.
|
|
77
|
+
*
|
|
78
|
+
* DO NOT EDIT - This file is automatically generated by 'pnpm generate'
|
|
79
|
+
*/
|
|
80
|
+
|
|
81
|
+
import { getContext as getOpensaasContext } from '@opensaas/stack-core'
|
|
82
|
+
import type { Session as OpensaasSession } from '@opensaas/stack-core'
|
|
83
|
+
import { PrismaClient } from './prisma-client'
|
|
84
|
+
import type { Context } from './types'
|
|
85
|
+
import config from '../opensaas.config'
|
|
86
|
+
|
|
87
|
+
// Internal Prisma singleton - managed automatically
|
|
88
|
+
const globalForPrisma = globalThis as unknown as { prisma: PrismaClient | undefined }
|
|
89
|
+
const prisma = globalForPrisma.prisma ?? new PrismaClient()
|
|
90
|
+
if (process.env.NODE_ENV !== 'production') globalForPrisma.prisma = prisma
|
|
91
|
+
|
|
92
|
+
/**
|
|
93
|
+
* Storage utilities (not configured)
|
|
94
|
+
*/
|
|
95
|
+
const storage = {
|
|
96
|
+
uploadFile: async () => {
|
|
97
|
+
throw new Error('Storage is not configured. Add storage providers to your opensaas.config.ts')
|
|
98
|
+
},
|
|
99
|
+
uploadImage: async () => {
|
|
100
|
+
throw new Error('Storage is not configured. Add storage providers to your opensaas.config.ts')
|
|
101
|
+
},
|
|
102
|
+
deleteFile: async () => {
|
|
103
|
+
throw new Error('Storage is not configured. Add storage providers to your opensaas.config.ts')
|
|
104
|
+
},
|
|
105
|
+
deleteImage: async () => {
|
|
106
|
+
throw new Error('Storage is not configured. Add storage providers to your opensaas.config.ts')
|
|
107
|
+
},
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
/**
|
|
111
|
+
* Get OpenSaas context with optional session
|
|
112
|
+
*
|
|
113
|
+
* @param session - Optional session object (structure defined by your application)
|
|
114
|
+
*
|
|
115
|
+
* @example
|
|
116
|
+
* \`\`\`typescript
|
|
117
|
+
* // Anonymous access
|
|
118
|
+
* const context = getContext()
|
|
119
|
+
* const posts = await context.db.post.findMany()
|
|
120
|
+
*
|
|
121
|
+
* // Authenticated access
|
|
122
|
+
* const context = getContext({ userId: 'user-123' })
|
|
123
|
+
* const myPosts = await context.db.post.findMany()
|
|
124
|
+
*
|
|
125
|
+
* // With custom session type
|
|
126
|
+
* type CustomSession = { userId: string; email: string; role: string } | null
|
|
127
|
+
* const context = getContext<CustomSession>({ userId: '123', email: 'user@example.com', role: 'admin' })
|
|
128
|
+
* // context.session is now typed as CustomSession
|
|
129
|
+
* \`\`\`
|
|
130
|
+
*/
|
|
131
|
+
export function getContext<TSession extends OpensaasSession = OpensaasSession>(session?: TSession): Context<TSession> {
|
|
132
|
+
return getOpensaasContext(config, prisma, session ?? null, storage) as Context<TSession>
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
export const rawOpensaasContext = getContext()
|
|
136
|
+
"
|
|
137
|
+
`;
|
|
@@ -0,0 +1,182 @@
|
|
|
1
|
+
// Vitest Snapshot v1, https://vitest.dev/guide/snapshot.html
|
|
2
|
+
|
|
3
|
+
exports[`Prisma Schema Generator > generatePrismaSchema > should generate basic schema with datasource and generator 1`] = `
|
|
4
|
+
"generator client {
|
|
5
|
+
provider = "prisma-client-js"
|
|
6
|
+
output = "../.opensaas/prisma-client"
|
|
7
|
+
}
|
|
8
|
+
|
|
9
|
+
datasource db {
|
|
10
|
+
provider = "sqlite"
|
|
11
|
+
url = env("DATABASE_URL")
|
|
12
|
+
}
|
|
13
|
+
"
|
|
14
|
+
`;
|
|
15
|
+
|
|
16
|
+
exports[`Prisma Schema Generator > generatePrismaSchema > should generate many-to-one relationship 1`] = `
|
|
17
|
+
"generator client {
|
|
18
|
+
provider = "prisma-client-js"
|
|
19
|
+
output = "../.opensaas/prisma-client"
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
datasource db {
|
|
23
|
+
provider = "sqlite"
|
|
24
|
+
url = env("DATABASE_URL")
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
model User {
|
|
28
|
+
id String @id @default(cuid())
|
|
29
|
+
name String?
|
|
30
|
+
createdAt DateTime @default(now())
|
|
31
|
+
updatedAt DateTime @updatedAt
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
model Post {
|
|
35
|
+
id String @id @default(cuid())
|
|
36
|
+
title String?
|
|
37
|
+
authorId String?
|
|
38
|
+
author User? @relation(fields: [authorId], references: [id])
|
|
39
|
+
createdAt DateTime @default(now())
|
|
40
|
+
updatedAt DateTime @updatedAt
|
|
41
|
+
}
|
|
42
|
+
"
|
|
43
|
+
`;
|
|
44
|
+
|
|
45
|
+
exports[`Prisma Schema Generator > generatePrismaSchema > should generate model with basic fields 1`] = `
|
|
46
|
+
"generator client {
|
|
47
|
+
provider = "prisma-client-js"
|
|
48
|
+
output = "../.opensaas/prisma-client"
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
datasource db {
|
|
52
|
+
provider = "sqlite"
|
|
53
|
+
url = env("DATABASE_URL")
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
model User {
|
|
57
|
+
id String @id @default(cuid())
|
|
58
|
+
name String
|
|
59
|
+
email String
|
|
60
|
+
age Int?
|
|
61
|
+
createdAt DateTime @default(now())
|
|
62
|
+
updatedAt DateTime @updatedAt
|
|
63
|
+
}
|
|
64
|
+
"
|
|
65
|
+
`;
|
|
66
|
+
|
|
67
|
+
exports[`Prisma Schema Generator > generatePrismaSchema > should generate model with checkbox field 1`] = `
|
|
68
|
+
"generator client {
|
|
69
|
+
provider = "prisma-client-js"
|
|
70
|
+
output = "../.opensaas/prisma-client"
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
datasource db {
|
|
74
|
+
provider = "sqlite"
|
|
75
|
+
url = env("DATABASE_URL")
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
model Post {
|
|
79
|
+
id String @id @default(cuid())
|
|
80
|
+
title String?
|
|
81
|
+
isPublished Boolean @default(false)
|
|
82
|
+
createdAt DateTime @default(now())
|
|
83
|
+
updatedAt DateTime @updatedAt
|
|
84
|
+
}
|
|
85
|
+
"
|
|
86
|
+
`;
|
|
87
|
+
|
|
88
|
+
exports[`Prisma Schema Generator > generatePrismaSchema > should generate model with timestamp field 1`] = `
|
|
89
|
+
"generator client {
|
|
90
|
+
provider = "prisma-client-js"
|
|
91
|
+
output = "../.opensaas/prisma-client"
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
datasource db {
|
|
95
|
+
provider = "sqlite"
|
|
96
|
+
url = env("DATABASE_URL")
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
model Post {
|
|
100
|
+
id String @id @default(cuid())
|
|
101
|
+
title String?
|
|
102
|
+
publishedAt DateTime?
|
|
103
|
+
createdAt DateTime @default(now())
|
|
104
|
+
updatedAt DateTime @updatedAt
|
|
105
|
+
}
|
|
106
|
+
"
|
|
107
|
+
`;
|
|
108
|
+
|
|
109
|
+
exports[`Prisma Schema Generator > generatePrismaSchema > should generate multiple models 1`] = `
|
|
110
|
+
"generator client {
|
|
111
|
+
provider = "prisma-client-js"
|
|
112
|
+
output = "../.opensaas/prisma-client"
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
datasource db {
|
|
116
|
+
provider = "postgresql"
|
|
117
|
+
url = env("DATABASE_URL")
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
model User {
|
|
121
|
+
id String @id @default(cuid())
|
|
122
|
+
name String?
|
|
123
|
+
createdAt DateTime @default(now())
|
|
124
|
+
updatedAt DateTime @updatedAt
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
model Post {
|
|
128
|
+
id String @id @default(cuid())
|
|
129
|
+
title String?
|
|
130
|
+
createdAt DateTime @default(now())
|
|
131
|
+
updatedAt DateTime @updatedAt
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
model Comment {
|
|
135
|
+
id String @id @default(cuid())
|
|
136
|
+
content String?
|
|
137
|
+
createdAt DateTime @default(now())
|
|
138
|
+
updatedAt DateTime @updatedAt
|
|
139
|
+
}
|
|
140
|
+
"
|
|
141
|
+
`;
|
|
142
|
+
|
|
143
|
+
exports[`Prisma Schema Generator > generatePrismaSchema > should generate one-to-many relationship 1`] = `
|
|
144
|
+
"generator client {
|
|
145
|
+
provider = "prisma-client-js"
|
|
146
|
+
output = "../.opensaas/prisma-client"
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
datasource db {
|
|
150
|
+
provider = "sqlite"
|
|
151
|
+
url = env("DATABASE_URL")
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
model User {
|
|
155
|
+
id String @id @default(cuid())
|
|
156
|
+
name String?
|
|
157
|
+
posts Post[]
|
|
158
|
+
createdAt DateTime @default(now())
|
|
159
|
+
updatedAt DateTime @updatedAt
|
|
160
|
+
}
|
|
161
|
+
|
|
162
|
+
model Post {
|
|
163
|
+
id String @id @default(cuid())
|
|
164
|
+
title String?
|
|
165
|
+
createdAt DateTime @default(now())
|
|
166
|
+
updatedAt DateTime @updatedAt
|
|
167
|
+
}
|
|
168
|
+
"
|
|
169
|
+
`;
|
|
170
|
+
|
|
171
|
+
exports[`Prisma Schema Generator > generatePrismaSchema > should use custom opensaasPath for generator output 1`] = `
|
|
172
|
+
"generator client {
|
|
173
|
+
provider = "prisma-client-js"
|
|
174
|
+
output = "../.custom-path/prisma-client"
|
|
175
|
+
}
|
|
176
|
+
|
|
177
|
+
datasource db {
|
|
178
|
+
provider = "sqlite"
|
|
179
|
+
url = env("DATABASE_URL")
|
|
180
|
+
}
|
|
181
|
+
"
|
|
182
|
+
`;
|