@opensaas/stack-cli 0.1.4 → 0.1.6
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 +18 -0
- package/dist/commands/generate.d.ts.map +1 -1
- package/dist/commands/generate.js +69 -4
- package/dist/commands/generate.js.map +1 -1
- package/dist/generator/context.d.ts.map +1 -1
- package/dist/generator/context.js +53 -11
- package/dist/generator/context.js.map +1 -1
- package/dist/generator/types.d.ts.map +1 -1
- package/dist/generator/types.js +46 -0
- package/dist/generator/types.js.map +1 -1
- package/package.json +11 -2
- package/src/commands/__snapshots__/generate.test.ts.snap +47 -10
- package/src/commands/generate.ts +83 -5
- package/src/generator/__snapshots__/context.test.ts.snap +199 -20
- package/src/generator/context.test.ts +3 -16
- package/src/generator/context.ts +53 -11
- package/src/generator/types.ts +54 -0
- package/tsconfig.tsbuildinfo +1 -1
|
@@ -1,5 +1,110 @@
|
|
|
1
1
|
// Vitest Snapshot v1, https://vitest.dev/guide/snapshot.html
|
|
2
2
|
|
|
3
|
+
exports[`Context Generator > generateContext > should export rawOpensaasContext 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, OpenSaasConfig } from '@opensaas/stack-core'
|
|
15
|
+
import { PrismaClient } from './prisma-client'
|
|
16
|
+
import type { Context } from './types'
|
|
17
|
+
import configOrPromise from '../opensaas.config'
|
|
18
|
+
|
|
19
|
+
// Resolve config if it's a Promise (when plugins are present)
|
|
20
|
+
const configPromise = Promise.resolve(configOrPromise)
|
|
21
|
+
let resolvedConfig: OpenSaasConfig | null = null
|
|
22
|
+
|
|
23
|
+
// Internal Prisma singleton - managed automatically
|
|
24
|
+
const globalForPrisma = globalThis as unknown as { prisma: PrismaClient | undefined }
|
|
25
|
+
let prisma: PrismaClient | null = null
|
|
26
|
+
|
|
27
|
+
async function getPrisma() {
|
|
28
|
+
if (!prisma) {
|
|
29
|
+
if (!resolvedConfig) {
|
|
30
|
+
resolvedConfig = await configPromise
|
|
31
|
+
}
|
|
32
|
+
prisma = globalForPrisma.prisma ?? new PrismaClient()
|
|
33
|
+
if (process.env.NODE_ENV !== 'production') globalForPrisma.prisma = prisma
|
|
34
|
+
}
|
|
35
|
+
return prisma
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
async function getConfig() {
|
|
39
|
+
if (!resolvedConfig) {
|
|
40
|
+
resolvedConfig = await configPromise
|
|
41
|
+
}
|
|
42
|
+
return resolvedConfig
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
/**
|
|
46
|
+
* Storage utilities (not configured)
|
|
47
|
+
*/
|
|
48
|
+
const storage = {
|
|
49
|
+
uploadFile: async () => {
|
|
50
|
+
throw new Error('Storage is not configured. Add storage providers to your opensaas.config.ts')
|
|
51
|
+
},
|
|
52
|
+
uploadImage: async () => {
|
|
53
|
+
throw new Error('Storage is not configured. Add storage providers to your opensaas.config.ts')
|
|
54
|
+
},
|
|
55
|
+
deleteFile: async () => {
|
|
56
|
+
throw new Error('Storage is not configured. Add storage providers to your opensaas.config.ts')
|
|
57
|
+
},
|
|
58
|
+
deleteImage: async () => {
|
|
59
|
+
throw new Error('Storage is not configured. Add storage providers to your opensaas.config.ts')
|
|
60
|
+
},
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
/**
|
|
64
|
+
* Get OpenSaas context with optional session
|
|
65
|
+
*
|
|
66
|
+
* @param session - Optional session object (structure defined by your application)
|
|
67
|
+
*
|
|
68
|
+
* @example
|
|
69
|
+
* \`\`\`typescript
|
|
70
|
+
* // Anonymous access
|
|
71
|
+
* const context = await getContext()
|
|
72
|
+
* const posts = await context.db.post.findMany()
|
|
73
|
+
*
|
|
74
|
+
* // Authenticated access
|
|
75
|
+
* const context = await getContext({ userId: 'user-123' })
|
|
76
|
+
* const myPosts = await context.db.post.findMany()
|
|
77
|
+
*
|
|
78
|
+
* // With custom session type
|
|
79
|
+
* type CustomSession = { userId: string; email: string; role: string } | null
|
|
80
|
+
* const context = await getContext<CustomSession>({ userId: '123', email: 'user@example.com', role: 'admin' })
|
|
81
|
+
* // context.session is now typed as CustomSession
|
|
82
|
+
* \`\`\`
|
|
83
|
+
*/
|
|
84
|
+
export async function getContext<TSession extends OpensaasSession = OpensaasSession>(session?: TSession): Promise<Context<TSession>> {
|
|
85
|
+
const config = await getConfig()
|
|
86
|
+
const prismaClient = await getPrisma()
|
|
87
|
+
return getOpensaasContext(config, prismaClient, session ?? null, storage) as Context<TSession>
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
/**
|
|
91
|
+
* Raw context for synchronous initialization (e.g., Better-auth setup)
|
|
92
|
+
* This is only available after config is resolved, use with caution
|
|
93
|
+
*/
|
|
94
|
+
export const rawOpensaasContext = (async () => {
|
|
95
|
+
const config = await getConfig()
|
|
96
|
+
const prismaClient = await getPrisma()
|
|
97
|
+
return getOpensaasContext(config, prismaClient, null, storage)
|
|
98
|
+
})()
|
|
99
|
+
|
|
100
|
+
/**
|
|
101
|
+
* Re-export resolved config for use in admin pages and server actions
|
|
102
|
+
* This is a promise that resolves to the config
|
|
103
|
+
*/
|
|
104
|
+
export const config = getConfig()
|
|
105
|
+
"
|
|
106
|
+
`;
|
|
107
|
+
|
|
3
108
|
exports[`Context Generator > generateContext > should generate context factory with custom Prisma client constructor 1`] = `
|
|
4
109
|
"/**
|
|
5
110
|
* Auto-generated context factory
|
|
@@ -11,15 +116,36 @@ exports[`Context Generator > generateContext > should generate context factory w
|
|
|
11
116
|
*/
|
|
12
117
|
|
|
13
118
|
import { getContext as getOpensaasContext } from '@opensaas/stack-core'
|
|
14
|
-
import type { Session as OpensaasSession } from '@opensaas/stack-core'
|
|
119
|
+
import type { Session as OpensaasSession, OpenSaasConfig } from '@opensaas/stack-core'
|
|
15
120
|
import { PrismaClient } from './prisma-client'
|
|
16
121
|
import type { Context } from './types'
|
|
17
|
-
import
|
|
122
|
+
import configOrPromise from '../opensaas.config'
|
|
123
|
+
|
|
124
|
+
// Resolve config if it's a Promise (when plugins are present)
|
|
125
|
+
const configPromise = Promise.resolve(configOrPromise)
|
|
126
|
+
let resolvedConfig: OpenSaasConfig | null = null
|
|
18
127
|
|
|
19
128
|
// Internal Prisma singleton - managed automatically
|
|
20
129
|
const globalForPrisma = globalThis as unknown as { prisma: PrismaClient | undefined }
|
|
21
|
-
|
|
22
|
-
|
|
130
|
+
let prisma: PrismaClient | null = null
|
|
131
|
+
|
|
132
|
+
async function getPrisma() {
|
|
133
|
+
if (!prisma) {
|
|
134
|
+
if (!resolvedConfig) {
|
|
135
|
+
resolvedConfig = await configPromise
|
|
136
|
+
}
|
|
137
|
+
prisma = globalForPrisma.prisma ?? resolvedConfig.db.prismaClientConstructor!(PrismaClient)
|
|
138
|
+
if (process.env.NODE_ENV !== 'production') globalForPrisma.prisma = prisma
|
|
139
|
+
}
|
|
140
|
+
return prisma
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
async function getConfig() {
|
|
144
|
+
if (!resolvedConfig) {
|
|
145
|
+
resolvedConfig = await configPromise
|
|
146
|
+
}
|
|
147
|
+
return resolvedConfig
|
|
148
|
+
}
|
|
23
149
|
|
|
24
150
|
/**
|
|
25
151
|
* Storage utilities (not configured)
|
|
@@ -47,24 +173,40 @@ const storage = {
|
|
|
47
173
|
* @example
|
|
48
174
|
* \`\`\`typescript
|
|
49
175
|
* // Anonymous access
|
|
50
|
-
* const context = getContext()
|
|
176
|
+
* const context = await getContext()
|
|
51
177
|
* const posts = await context.db.post.findMany()
|
|
52
178
|
*
|
|
53
179
|
* // Authenticated access
|
|
54
|
-
* const context = getContext({ userId: 'user-123' })
|
|
180
|
+
* const context = await getContext({ userId: 'user-123' })
|
|
55
181
|
* const myPosts = await context.db.post.findMany()
|
|
56
182
|
*
|
|
57
183
|
* // With custom session type
|
|
58
184
|
* type CustomSession = { userId: string; email: string; role: string } | null
|
|
59
|
-
* const context = getContext<CustomSession>({ userId: '123', email: 'user@example.com', role: 'admin' })
|
|
185
|
+
* const context = await getContext<CustomSession>({ userId: '123', email: 'user@example.com', role: 'admin' })
|
|
60
186
|
* // context.session is now typed as CustomSession
|
|
61
187
|
* \`\`\`
|
|
62
188
|
*/
|
|
63
|
-
export function getContext<TSession extends OpensaasSession = OpensaasSession>(session?: TSession): Context<TSession
|
|
64
|
-
|
|
189
|
+
export async function getContext<TSession extends OpensaasSession = OpensaasSession>(session?: TSession): Promise<Context<TSession>> {
|
|
190
|
+
const config = await getConfig()
|
|
191
|
+
const prismaClient = await getPrisma()
|
|
192
|
+
return getOpensaasContext(config, prismaClient, session ?? null, storage) as Context<TSession>
|
|
65
193
|
}
|
|
66
194
|
|
|
67
|
-
|
|
195
|
+
/**
|
|
196
|
+
* Raw context for synchronous initialization (e.g., Better-auth setup)
|
|
197
|
+
* This is only available after config is resolved, use with caution
|
|
198
|
+
*/
|
|
199
|
+
export const rawOpensaasContext = (async () => {
|
|
200
|
+
const config = await getConfig()
|
|
201
|
+
const prismaClient = await getPrisma()
|
|
202
|
+
return getOpensaasContext(config, prismaClient, null, storage)
|
|
203
|
+
})()
|
|
204
|
+
|
|
205
|
+
/**
|
|
206
|
+
* Re-export resolved config for use in admin pages and server actions
|
|
207
|
+
* This is a promise that resolves to the config
|
|
208
|
+
*/
|
|
209
|
+
export const config = getConfig()
|
|
68
210
|
"
|
|
69
211
|
`;
|
|
70
212
|
|
|
@@ -79,15 +221,36 @@ exports[`Context Generator > generateContext > should generate context factory w
|
|
|
79
221
|
*/
|
|
80
222
|
|
|
81
223
|
import { getContext as getOpensaasContext } from '@opensaas/stack-core'
|
|
82
|
-
import type { Session as OpensaasSession } from '@opensaas/stack-core'
|
|
224
|
+
import type { Session as OpensaasSession, OpenSaasConfig } from '@opensaas/stack-core'
|
|
83
225
|
import { PrismaClient } from './prisma-client'
|
|
84
226
|
import type { Context } from './types'
|
|
85
|
-
import
|
|
227
|
+
import configOrPromise from '../opensaas.config'
|
|
228
|
+
|
|
229
|
+
// Resolve config if it's a Promise (when plugins are present)
|
|
230
|
+
const configPromise = Promise.resolve(configOrPromise)
|
|
231
|
+
let resolvedConfig: OpenSaasConfig | null = null
|
|
86
232
|
|
|
87
233
|
// Internal Prisma singleton - managed automatically
|
|
88
234
|
const globalForPrisma = globalThis as unknown as { prisma: PrismaClient | undefined }
|
|
89
|
-
|
|
90
|
-
|
|
235
|
+
let prisma: PrismaClient | null = null
|
|
236
|
+
|
|
237
|
+
async function getPrisma() {
|
|
238
|
+
if (!prisma) {
|
|
239
|
+
if (!resolvedConfig) {
|
|
240
|
+
resolvedConfig = await configPromise
|
|
241
|
+
}
|
|
242
|
+
prisma = globalForPrisma.prisma ?? new PrismaClient()
|
|
243
|
+
if (process.env.NODE_ENV !== 'production') globalForPrisma.prisma = prisma
|
|
244
|
+
}
|
|
245
|
+
return prisma
|
|
246
|
+
}
|
|
247
|
+
|
|
248
|
+
async function getConfig() {
|
|
249
|
+
if (!resolvedConfig) {
|
|
250
|
+
resolvedConfig = await configPromise
|
|
251
|
+
}
|
|
252
|
+
return resolvedConfig
|
|
253
|
+
}
|
|
91
254
|
|
|
92
255
|
/**
|
|
93
256
|
* Storage utilities (not configured)
|
|
@@ -115,23 +278,39 @@ const storage = {
|
|
|
115
278
|
* @example
|
|
116
279
|
* \`\`\`typescript
|
|
117
280
|
* // Anonymous access
|
|
118
|
-
* const context = getContext()
|
|
281
|
+
* const context = await getContext()
|
|
119
282
|
* const posts = await context.db.post.findMany()
|
|
120
283
|
*
|
|
121
284
|
* // Authenticated access
|
|
122
|
-
* const context = getContext({ userId: 'user-123' })
|
|
285
|
+
* const context = await getContext({ userId: 'user-123' })
|
|
123
286
|
* const myPosts = await context.db.post.findMany()
|
|
124
287
|
*
|
|
125
288
|
* // With custom session type
|
|
126
289
|
* type CustomSession = { userId: string; email: string; role: string } | null
|
|
127
|
-
* const context = getContext<CustomSession>({ userId: '123', email: 'user@example.com', role: 'admin' })
|
|
290
|
+
* const context = await getContext<CustomSession>({ userId: '123', email: 'user@example.com', role: 'admin' })
|
|
128
291
|
* // context.session is now typed as CustomSession
|
|
129
292
|
* \`\`\`
|
|
130
293
|
*/
|
|
131
|
-
export function getContext<TSession extends OpensaasSession = OpensaasSession>(session?: TSession): Context<TSession
|
|
132
|
-
|
|
294
|
+
export async function getContext<TSession extends OpensaasSession = OpensaasSession>(session?: TSession): Promise<Context<TSession>> {
|
|
295
|
+
const config = await getConfig()
|
|
296
|
+
const prismaClient = await getPrisma()
|
|
297
|
+
return getOpensaasContext(config, prismaClient, session ?? null, storage) as Context<TSession>
|
|
133
298
|
}
|
|
134
299
|
|
|
135
|
-
|
|
300
|
+
/**
|
|
301
|
+
* Raw context for synchronous initialization (e.g., Better-auth setup)
|
|
302
|
+
* This is only available after config is resolved, use with caution
|
|
303
|
+
*/
|
|
304
|
+
export const rawOpensaasContext = (async () => {
|
|
305
|
+
const config = await getConfig()
|
|
306
|
+
const prismaClient = await getPrisma()
|
|
307
|
+
return getOpensaasContext(config, prismaClient, null, storage)
|
|
308
|
+
})()
|
|
309
|
+
|
|
310
|
+
/**
|
|
311
|
+
* Re-export resolved config for use in admin pages and server actions
|
|
312
|
+
* This is a promise that resolves to the config
|
|
313
|
+
*/
|
|
314
|
+
export const config = getConfig()
|
|
136
315
|
"
|
|
137
316
|
`;
|
|
@@ -95,9 +95,9 @@ describe('Context Generator', () => {
|
|
|
95
95
|
const context = generateContext(config)
|
|
96
96
|
|
|
97
97
|
expect(context).toContain('// Anonymous access')
|
|
98
|
-
expect(context).toContain('const context = getContext()')
|
|
98
|
+
expect(context).toContain('const context = await getContext()')
|
|
99
99
|
expect(context).toContain('// Authenticated access')
|
|
100
|
-
expect(context).toContain("const context = getContext({ userId: 'user-123' })")
|
|
100
|
+
expect(context).toContain("const context = await getContext({ userId: 'user-123' })")
|
|
101
101
|
})
|
|
102
102
|
|
|
103
103
|
it('should export rawOpensaasContext', () => {
|
|
@@ -111,7 +111,7 @@ describe('Context Generator', () => {
|
|
|
111
111
|
|
|
112
112
|
const context = generateContext(config)
|
|
113
113
|
|
|
114
|
-
expect(context).
|
|
114
|
+
expect(context).toMatchSnapshot()
|
|
115
115
|
})
|
|
116
116
|
|
|
117
117
|
it('should type session parameter correctly', () => {
|
|
@@ -128,18 +128,5 @@ describe('Context Generator', () => {
|
|
|
128
128
|
expect(context).toContain('session?: TSession')
|
|
129
129
|
expect(context).toContain('<TSession extends OpensaasSession = OpensaasSession>')
|
|
130
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
131
|
})
|
|
145
132
|
})
|
package/src/generator/context.ts
CHANGED
|
@@ -17,7 +17,7 @@ export function generateContext(config: OpenSaasConfig): string {
|
|
|
17
17
|
|
|
18
18
|
// Generate the Prisma client instantiation code
|
|
19
19
|
const prismaInstantiation = hasCustomConstructor
|
|
20
|
-
? `
|
|
20
|
+
? `resolvedConfig.db.prismaClientConstructor!(PrismaClient)`
|
|
21
21
|
: `new PrismaClient()`
|
|
22
22
|
|
|
23
23
|
// Generate storage utilities if storage is configured
|
|
@@ -47,21 +47,25 @@ async function getStorageRuntime() {
|
|
|
47
47
|
*/
|
|
48
48
|
const storage = {
|
|
49
49
|
uploadFile: async (providerName: string, file: File, buffer: Buffer, options?: unknown) => {
|
|
50
|
+
const config = await getConfig()
|
|
50
51
|
const runtime = await getStorageRuntime()
|
|
51
52
|
return runtime.uploadFile(config, providerName, { file, buffer }, options as any)
|
|
52
53
|
},
|
|
53
54
|
|
|
54
55
|
uploadImage: async (providerName: string, file: File, buffer: Buffer, options?: unknown) => {
|
|
56
|
+
const config = await getConfig()
|
|
55
57
|
const runtime = await getStorageRuntime()
|
|
56
58
|
return runtime.uploadImage(config, providerName, { file, buffer }, options as any)
|
|
57
59
|
},
|
|
58
60
|
|
|
59
61
|
deleteFile: async (providerName: string, filename: string) => {
|
|
62
|
+
const config = await getConfig()
|
|
60
63
|
const runtime = await getStorageRuntime()
|
|
61
64
|
return runtime.deleteFile(config, providerName, filename)
|
|
62
65
|
},
|
|
63
66
|
|
|
64
67
|
deleteImage: async (metadata: unknown) => {
|
|
68
|
+
const config = await getConfig()
|
|
65
69
|
const runtime = await getStorageRuntime()
|
|
66
70
|
return runtime.deleteImage(config, metadata as any)
|
|
67
71
|
},
|
|
@@ -87,6 +91,7 @@ const storage = {
|
|
|
87
91
|
}
|
|
88
92
|
`
|
|
89
93
|
|
|
94
|
+
// Always use async version for consistency
|
|
90
95
|
return `/**
|
|
91
96
|
* Auto-generated context factory
|
|
92
97
|
*
|
|
@@ -97,15 +102,36 @@ const storage = {
|
|
|
97
102
|
*/
|
|
98
103
|
|
|
99
104
|
import { getContext as getOpensaasContext } from '@opensaas/stack-core'
|
|
100
|
-
import type { Session as OpensaasSession } from '@opensaas/stack-core'
|
|
105
|
+
import type { Session as OpensaasSession, OpenSaasConfig } from '@opensaas/stack-core'
|
|
101
106
|
import { PrismaClient } from './prisma-client'
|
|
102
107
|
import type { Context } from './types'
|
|
103
|
-
import
|
|
108
|
+
import configOrPromise from '../opensaas.config'
|
|
109
|
+
|
|
110
|
+
// Resolve config if it's a Promise (when plugins are present)
|
|
111
|
+
const configPromise = Promise.resolve(configOrPromise)
|
|
112
|
+
let resolvedConfig: OpenSaasConfig | null = null
|
|
104
113
|
|
|
105
114
|
// Internal Prisma singleton - managed automatically
|
|
106
115
|
const globalForPrisma = globalThis as unknown as { prisma: PrismaClient | undefined }
|
|
107
|
-
|
|
108
|
-
|
|
116
|
+
let prisma: PrismaClient | null = null
|
|
117
|
+
|
|
118
|
+
async function getPrisma() {
|
|
119
|
+
if (!prisma) {
|
|
120
|
+
if (!resolvedConfig) {
|
|
121
|
+
resolvedConfig = await configPromise
|
|
122
|
+
}
|
|
123
|
+
prisma = globalForPrisma.prisma ?? ${prismaInstantiation}
|
|
124
|
+
if (process.env.NODE_ENV !== 'production') globalForPrisma.prisma = prisma
|
|
125
|
+
}
|
|
126
|
+
return prisma
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
async function getConfig() {
|
|
130
|
+
if (!resolvedConfig) {
|
|
131
|
+
resolvedConfig = await configPromise
|
|
132
|
+
}
|
|
133
|
+
return resolvedConfig
|
|
134
|
+
}
|
|
109
135
|
${storageUtilities}
|
|
110
136
|
/**
|
|
111
137
|
* Get OpenSaas context with optional session
|
|
@@ -115,24 +141,40 @@ ${storageUtilities}
|
|
|
115
141
|
* @example
|
|
116
142
|
* \`\`\`typescript
|
|
117
143
|
* // Anonymous access
|
|
118
|
-
* const context = getContext()
|
|
144
|
+
* const context = await getContext()
|
|
119
145
|
* const posts = await context.db.post.findMany()
|
|
120
146
|
*
|
|
121
147
|
* // Authenticated access
|
|
122
|
-
* const context = getContext({ userId: 'user-123' })
|
|
148
|
+
* const context = await getContext({ userId: 'user-123' })
|
|
123
149
|
* const myPosts = await context.db.post.findMany()
|
|
124
150
|
*
|
|
125
151
|
* // With custom session type
|
|
126
152
|
* type CustomSession = { userId: string; email: string; role: string } | null
|
|
127
|
-
* const context = getContext<CustomSession>({ userId: '123', email: 'user@example.com', role: 'admin' })
|
|
153
|
+
* const context = await getContext<CustomSession>({ userId: '123', email: 'user@example.com', role: 'admin' })
|
|
128
154
|
* // context.session is now typed as CustomSession
|
|
129
155
|
* \`\`\`
|
|
130
156
|
*/
|
|
131
|
-
export function getContext<TSession extends OpensaasSession = OpensaasSession>(session?: TSession): Context<TSession
|
|
132
|
-
|
|
157
|
+
export async function getContext<TSession extends OpensaasSession = OpensaasSession>(session?: TSession): Promise<Context<TSession>> {
|
|
158
|
+
const config = await getConfig()
|
|
159
|
+
const prismaClient = await getPrisma()
|
|
160
|
+
return getOpensaasContext(config, prismaClient, session ?? null, storage) as Context<TSession>
|
|
133
161
|
}
|
|
134
162
|
|
|
135
|
-
|
|
163
|
+
/**
|
|
164
|
+
* Raw context for synchronous initialization (e.g., Better-auth setup)
|
|
165
|
+
* This is only available after config is resolved, use with caution
|
|
166
|
+
*/
|
|
167
|
+
export const rawOpensaasContext = (async () => {
|
|
168
|
+
const config = await getConfig()
|
|
169
|
+
const prismaClient = await getPrisma()
|
|
170
|
+
return getOpensaasContext(config, prismaClient, null, storage)
|
|
171
|
+
})()
|
|
172
|
+
|
|
173
|
+
/**
|
|
174
|
+
* Re-export resolved config for use in admin pages and server actions
|
|
175
|
+
* This is a promise that resolves to the config
|
|
176
|
+
*/
|
|
177
|
+
export const config = getConfig()
|
|
136
178
|
`
|
|
137
179
|
}
|
|
138
180
|
|
package/src/generator/types.ts
CHANGED
|
@@ -186,6 +186,51 @@ function generateContextType(): string {
|
|
|
186
186
|
return lines.join('\n')
|
|
187
187
|
}
|
|
188
188
|
|
|
189
|
+
/**
|
|
190
|
+
* Collect TypeScript imports from field configurations
|
|
191
|
+
*/
|
|
192
|
+
function collectFieldImports(config: OpenSaasConfig): Array<{
|
|
193
|
+
names: string[]
|
|
194
|
+
from: string
|
|
195
|
+
typeOnly: boolean
|
|
196
|
+
}> {
|
|
197
|
+
const importsMap = new Map<string, { names: Set<string>; typeOnly: boolean }>()
|
|
198
|
+
|
|
199
|
+
// Iterate through all lists and fields
|
|
200
|
+
for (const listConfig of Object.values(config.lists)) {
|
|
201
|
+
for (const fieldConfig of Object.values(listConfig.fields)) {
|
|
202
|
+
// Check if field provides imports
|
|
203
|
+
if (fieldConfig.getTypeScriptImports) {
|
|
204
|
+
const imports = fieldConfig.getTypeScriptImports()
|
|
205
|
+
for (const imp of imports) {
|
|
206
|
+
const existing = importsMap.get(imp.from)
|
|
207
|
+
if (existing) {
|
|
208
|
+
// Merge names into existing import
|
|
209
|
+
imp.names.forEach((name) => existing.names.add(name))
|
|
210
|
+
// If either import is not type-only, make the merged import not type-only
|
|
211
|
+
if (imp.typeOnly === false) {
|
|
212
|
+
existing.typeOnly = false
|
|
213
|
+
}
|
|
214
|
+
} else {
|
|
215
|
+
// Add new import
|
|
216
|
+
importsMap.set(imp.from, {
|
|
217
|
+
names: new Set(imp.names),
|
|
218
|
+
typeOnly: imp.typeOnly ?? true,
|
|
219
|
+
})
|
|
220
|
+
}
|
|
221
|
+
}
|
|
222
|
+
}
|
|
223
|
+
}
|
|
224
|
+
}
|
|
225
|
+
|
|
226
|
+
// Convert map to array
|
|
227
|
+
return Array.from(importsMap.entries()).map(([from, { names, typeOnly }]) => ({
|
|
228
|
+
names: Array.from(names).sort(),
|
|
229
|
+
from,
|
|
230
|
+
typeOnly,
|
|
231
|
+
}))
|
|
232
|
+
}
|
|
233
|
+
|
|
189
234
|
/**
|
|
190
235
|
* Generate all TypeScript types from config
|
|
191
236
|
*/
|
|
@@ -205,6 +250,15 @@ export function generateTypes(config: OpenSaasConfig): string {
|
|
|
205
250
|
"import type { Session as OpensaasSession, StorageUtils, ServerActionProps, AccessControlledDB } from '@opensaas/stack-core'",
|
|
206
251
|
)
|
|
207
252
|
lines.push("import type { PrismaClient } from './prisma-client'")
|
|
253
|
+
|
|
254
|
+
// Add field-specific imports
|
|
255
|
+
const fieldImports = collectFieldImports(config)
|
|
256
|
+
for (const imp of fieldImports) {
|
|
257
|
+
const typePrefix = imp.typeOnly ? 'type ' : ''
|
|
258
|
+
const names = imp.names.join(', ')
|
|
259
|
+
lines.push(`import ${typePrefix}{ ${names} } from '${imp.from}'`)
|
|
260
|
+
}
|
|
261
|
+
|
|
208
262
|
lines.push('')
|
|
209
263
|
|
|
210
264
|
// Generate types for each list
|