prisma-generator-express 1.18.0 → 1.20.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.
- package/README.md +399 -194
- package/dist/bin.d.ts +2 -0
- package/dist/bin.js +1 -1
- package/dist/bin.js.map +1 -1
- package/dist/client/encodeQueryParams.d.ts +1 -0
- package/dist/client/encodeQueryParams.js +33 -0
- package/dist/client/encodeQueryParams.js.map +1 -0
- package/dist/constants.d.ts +1 -0
- package/dist/copy/misc.d.ts +5 -0
- package/dist/copy/misc.js +52 -0
- package/dist/copy/misc.js.map +1 -0
- package/dist/generators/generateImportPrismaStatement.d.ts +3 -0
- package/dist/generators/generateImportPrismaStatement.js +55 -0
- package/dist/generators/generateImportPrismaStatement.js.map +1 -0
- package/dist/generators/generateQueryBuilderHelper.d.ts +2 -0
- package/dist/generators/generateQueryBuilderHelper.js +139 -0
- package/dist/generators/generateQueryBuilderHelper.js.map +1 -0
- package/dist/generators/generateRouter.d.ts +6 -0
- package/dist/generators/generateRouter.js +340 -0
- package/dist/generators/generateRouter.js.map +1 -0
- package/dist/generators/generateUnifiedDocs.d.ts +1 -0
- package/dist/generators/generateUnifiedDocs.js +171 -0
- package/dist/generators/generateUnifiedDocs.js.map +1 -0
- package/dist/generators/generateUnifiedHandler.d.ts +6 -0
- package/dist/generators/generateUnifiedHandler.js +444 -0
- package/dist/generators/generateUnifiedHandler.js.map +1 -0
- package/dist/generators/generateUnifiedScalarUI.d.ts +5 -0
- package/dist/generators/generateUnifiedScalarUI.js +1390 -0
- package/dist/generators/generateUnifiedScalarUI.js.map +1 -0
- package/dist/index.d.ts +1 -0
- package/dist/index.js +80 -0
- package/dist/index.js.map +1 -0
- package/dist/utils/copyFiles.d.ts +6 -0
- package/dist/utils/copyFiles.js +123 -21
- package/dist/utils/copyFiles.js.map +1 -1
- package/dist/utils/strings.d.ts +2 -0
- package/dist/utils/writeFileSafely.d.ts +10 -0
- package/dist/utils/writeFileSafely.js +86 -14
- package/dist/utils/writeFileSafely.js.map +1 -1
- package/package.json +59 -28
- package/src/bin.ts +1 -1
- package/src/client/encodeQueryParams.ts +56 -0
- package/src/copy/buildModelOpenApi.ts +1569 -0
- package/src/copy/misc.ts +21 -0
- package/src/copy/operationDefinitions.ts +96 -0
- package/src/copy/parseQueryParams.ts +36 -21
- package/src/copy/routeConfig.ts +68 -28
- package/src/generators/generateImportPrismaStatement.ts +78 -0
- package/src/generators/generateQueryBuilderHelper.ts +138 -0
- package/src/generators/generateRouter.ts +352 -0
- package/src/generators/generateUnifiedDocs.ts +168 -0
- package/src/generators/generateUnifiedHandler.ts +469 -0
- package/src/generators/generateUnifiedScalarUI.ts +1409 -0
- package/src/index.ts +100 -0
- package/src/utils/copyFiles.ts +123 -16
- package/src/utils/writeFileSafely.ts +79 -25
- package/dist/generator.js +0 -47
- package/dist/generator.js.map +0 -1
- package/dist/helpers/generateImportPrismaStatement.js +0 -25
- package/dist/helpers/generateImportPrismaStatement.js.map +0 -1
- package/dist/helpers/generateOperation.js +0 -471
- package/dist/helpers/generateOperation.js.map +0 -1
- package/dist/helpers/generateRouteFile.js +0 -210
- package/dist/helpers/generateRouteFile.js.map +0 -1
- package/dist/utils/formatFile.js +0 -26
- package/dist/utils/formatFile.js.map +0 -1
- package/src/copy/encodeQueryParams.spec.ts +0 -303
- package/src/copy/encodeQueryParams.ts +0 -44
- package/src/copy/misc.spec.ts +0 -62
- package/src/copy/parseQueryParams.spec.ts +0 -187
- package/src/copy/transformZod.spec.ts +0 -763
- package/src/generator.ts +0 -54
- package/src/helpers/generateImportPrismaStatement.ts +0 -38
- package/src/helpers/generateOperation.ts +0 -515
- package/src/helpers/generateRouteFile.ts +0 -213
- package/src/utils/formatFile.ts +0 -22
package/src/copy/misc.ts
CHANGED
|
@@ -23,3 +23,24 @@ export function safeJSONparse<T>(
|
|
|
23
23
|
export const isObject = (value: unknown): value is Record<string, unknown> => {
|
|
24
24
|
return typeof value === 'object' && value !== null && !Array.isArray(value)
|
|
25
25
|
}
|
|
26
|
+
|
|
27
|
+
const UNSAFE_KEYS = new Set(['__proto__', 'constructor', 'prototype'])
|
|
28
|
+
|
|
29
|
+
export function isSafeKey(key: string): boolean {
|
|
30
|
+
return !UNSAFE_KEYS.has(key)
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
export function sanitizeKeys<T>(value: T): T {
|
|
34
|
+
if (Array.isArray(value)) {
|
|
35
|
+
return value.map(sanitizeKeys) as T
|
|
36
|
+
}
|
|
37
|
+
if (isObject(value)) {
|
|
38
|
+
const result: Record<string, unknown> = {}
|
|
39
|
+
for (const key of Object.keys(value)) {
|
|
40
|
+
if (!isSafeKey(key)) continue
|
|
41
|
+
result[key] = sanitizeKeys((value as Record<string, unknown>)[key])
|
|
42
|
+
}
|
|
43
|
+
return result as T
|
|
44
|
+
}
|
|
45
|
+
return value
|
|
46
|
+
}
|
|
@@ -0,0 +1,96 @@
|
|
|
1
|
+
export type HttpMethod = 'get' | 'post' | 'put' | 'patch' | 'delete'
|
|
2
|
+
|
|
3
|
+
export interface OperationDef {
|
|
4
|
+
name: string
|
|
5
|
+
method: HttpMethod
|
|
6
|
+
pathSuffix: string
|
|
7
|
+
configKey: string
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
export const OPERATION_DEFS: OperationDef[] = [
|
|
11
|
+
{ name: 'findMany', method: 'get', pathSuffix: '', configKey: 'findMany' },
|
|
12
|
+
{
|
|
13
|
+
name: 'findUnique',
|
|
14
|
+
method: 'get',
|
|
15
|
+
pathSuffix: '/unique',
|
|
16
|
+
configKey: 'findUnique',
|
|
17
|
+
},
|
|
18
|
+
{
|
|
19
|
+
name: 'findUniqueOrThrow',
|
|
20
|
+
method: 'get',
|
|
21
|
+
pathSuffix: '/unique/strict',
|
|
22
|
+
configKey: 'findUniqueOrThrow',
|
|
23
|
+
},
|
|
24
|
+
{
|
|
25
|
+
name: 'findFirst',
|
|
26
|
+
method: 'get',
|
|
27
|
+
pathSuffix: '/first',
|
|
28
|
+
configKey: 'findFirst',
|
|
29
|
+
},
|
|
30
|
+
{
|
|
31
|
+
name: 'findFirstOrThrow',
|
|
32
|
+
method: 'get',
|
|
33
|
+
pathSuffix: '/first/strict',
|
|
34
|
+
configKey: 'findFirstOrThrow',
|
|
35
|
+
},
|
|
36
|
+
{
|
|
37
|
+
name: 'findManyPaginated',
|
|
38
|
+
method: 'get',
|
|
39
|
+
pathSuffix: '/paginated',
|
|
40
|
+
configKey: 'findManyPaginated',
|
|
41
|
+
},
|
|
42
|
+
{ name: 'create', method: 'post', pathSuffix: '', configKey: 'create' },
|
|
43
|
+
{
|
|
44
|
+
name: 'createMany',
|
|
45
|
+
method: 'post',
|
|
46
|
+
pathSuffix: '/many',
|
|
47
|
+
configKey: 'createMany',
|
|
48
|
+
},
|
|
49
|
+
{
|
|
50
|
+
name: 'createManyAndReturn',
|
|
51
|
+
method: 'post',
|
|
52
|
+
pathSuffix: '/many/return',
|
|
53
|
+
configKey: 'createManyAndReturn',
|
|
54
|
+
},
|
|
55
|
+
{ name: 'update', method: 'put', pathSuffix: '', configKey: 'update' },
|
|
56
|
+
{
|
|
57
|
+
name: 'updateMany',
|
|
58
|
+
method: 'put',
|
|
59
|
+
pathSuffix: '/many',
|
|
60
|
+
configKey: 'updateMany',
|
|
61
|
+
},
|
|
62
|
+
{
|
|
63
|
+
name: 'updateManyAndReturn',
|
|
64
|
+
method: 'put',
|
|
65
|
+
pathSuffix: '/many/return',
|
|
66
|
+
configKey: 'updateManyAndReturn',
|
|
67
|
+
},
|
|
68
|
+
{ name: 'upsert', method: 'patch', pathSuffix: '', configKey: 'upsert' },
|
|
69
|
+
{ name: 'delete', method: 'delete', pathSuffix: '', configKey: 'delete' },
|
|
70
|
+
{
|
|
71
|
+
name: 'deleteMany',
|
|
72
|
+
method: 'delete',
|
|
73
|
+
pathSuffix: '/many',
|
|
74
|
+
configKey: 'deleteMany',
|
|
75
|
+
},
|
|
76
|
+
{ name: 'count', method: 'get', pathSuffix: '/count', configKey: 'count' },
|
|
77
|
+
{
|
|
78
|
+
name: 'aggregate',
|
|
79
|
+
method: 'get',
|
|
80
|
+
pathSuffix: '/aggregate',
|
|
81
|
+
configKey: 'aggregate',
|
|
82
|
+
},
|
|
83
|
+
{
|
|
84
|
+
name: 'groupBy',
|
|
85
|
+
method: 'get',
|
|
86
|
+
pathSuffix: '/groupby',
|
|
87
|
+
configKey: 'groupBy',
|
|
88
|
+
},
|
|
89
|
+
]
|
|
90
|
+
|
|
91
|
+
export function isOperationEnabled(
|
|
92
|
+
config: Record<string, any>,
|
|
93
|
+
def: OperationDef,
|
|
94
|
+
): boolean {
|
|
95
|
+
return !!(config.enableAll || config[def.configKey])
|
|
96
|
+
}
|
|
@@ -1,29 +1,38 @@
|
|
|
1
|
-
import { isObject } from './misc'
|
|
2
|
-
import { ParsedQs } from 'qs'
|
|
1
|
+
import { isObject, isSafeKey, sanitizeKeys } from './misc'
|
|
3
2
|
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
3
|
+
type QueryParams =
|
|
4
|
+
| string
|
|
5
|
+
| Record<string, unknown>
|
|
6
|
+
| string[]
|
|
7
|
+
| Record<string, unknown>[]
|
|
8
|
+
| undefined
|
|
8
9
|
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
const
|
|
10
|
+
const NUMERIC_KEYS = new Set(['take', 'skip'])
|
|
11
|
+
|
|
12
|
+
const parseQueryValue = (value: string, key?: string): unknown => {
|
|
13
|
+
if (value.startsWith('{') || value.startsWith('[') || value.startsWith('"')) {
|
|
14
|
+
try {
|
|
15
|
+
const parsed = JSON.parse(value)
|
|
16
|
+
return sanitizeKeys(parsed)
|
|
17
|
+
} catch {
|
|
18
|
+
// fall through
|
|
19
|
+
}
|
|
20
|
+
}
|
|
15
21
|
if (value === 'true') return true
|
|
16
22
|
if (value === 'false') return false
|
|
17
23
|
if (value === 'null') return null
|
|
18
|
-
if (
|
|
24
|
+
if (
|
|
25
|
+
key &&
|
|
26
|
+
NUMERIC_KEYS.has(key) &&
|
|
27
|
+
value !== '' &&
|
|
28
|
+
!isNaN(Number(value)) &&
|
|
29
|
+
isFinite(Number(value))
|
|
30
|
+
) {
|
|
31
|
+
return Number(value)
|
|
32
|
+
}
|
|
19
33
|
return value
|
|
20
34
|
}
|
|
21
35
|
|
|
22
|
-
/**
|
|
23
|
-
* Recursively parses query parameters to convert strings to their respective types.
|
|
24
|
-
* @param {QueryParams} params - The query parameters to parse.
|
|
25
|
-
* @returns {unknown} The parsed query parameters.
|
|
26
|
-
*/
|
|
27
36
|
export const parseQueryParams = (params: QueryParams): unknown => {
|
|
28
37
|
if (typeof params === 'string') {
|
|
29
38
|
return parseQueryValue(params)
|
|
@@ -33,10 +42,16 @@ export const parseQueryParams = (params: QueryParams): unknown => {
|
|
|
33
42
|
}
|
|
34
43
|
if (isObject(params)) {
|
|
35
44
|
const parsedParams: Record<string, unknown> = {}
|
|
36
|
-
for (const key
|
|
37
|
-
|
|
45
|
+
for (const key of Object.keys(params)) {
|
|
46
|
+
if (!isSafeKey(key)) continue
|
|
47
|
+
const raw = params[key]
|
|
48
|
+
if (typeof raw === 'string') {
|
|
49
|
+
parsedParams[key] = parseQueryValue(raw, key)
|
|
50
|
+
} else {
|
|
51
|
+
parsedParams[key] = raw
|
|
52
|
+
}
|
|
38
53
|
}
|
|
39
54
|
return parsedParams
|
|
40
55
|
}
|
|
41
56
|
return params
|
|
42
|
-
}
|
|
57
|
+
}
|
package/src/copy/routeConfig.ts
CHANGED
|
@@ -1,36 +1,76 @@
|
|
|
1
|
-
import { RequestHandler } from 'express'
|
|
2
|
-
import { ZodType } from 'zod'
|
|
1
|
+
import { RequestHandler, Request } from 'express'
|
|
3
2
|
|
|
4
|
-
export interface
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
3
|
+
export interface OperationConfig {
|
|
4
|
+
before?: RequestHandler[]
|
|
5
|
+
after?: RequestHandler[]
|
|
6
|
+
shape?: Record<string, any>
|
|
8
7
|
}
|
|
9
8
|
|
|
10
|
-
interface
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
9
|
+
export interface QueryBuilderConfig {
|
|
10
|
+
enabled?: boolean
|
|
11
|
+
port?: number
|
|
12
|
+
host?: string
|
|
13
|
+
schemaPath?: string
|
|
14
|
+
databaseUrl?: string
|
|
15
15
|
}
|
|
16
16
|
|
|
17
|
-
export interface
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
groupBy?: MiddlewareConfig<M>
|
|
33
|
-
addModelPrefix?: boolean
|
|
17
|
+
export interface OpenApiServerConfig {
|
|
18
|
+
url: string
|
|
19
|
+
description?: string
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
export interface OpenApiSecuritySchemeConfig {
|
|
23
|
+
type: string
|
|
24
|
+
scheme?: string
|
|
25
|
+
bearerFormat?: string
|
|
26
|
+
name?: string
|
|
27
|
+
in?: string
|
|
28
|
+
description?: string
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
export interface RouteConfig {
|
|
34
32
|
enableAll?: boolean
|
|
33
|
+
addModelPrefix?: boolean
|
|
35
34
|
customUrlPrefix?: string
|
|
35
|
+
specBasePath?: string
|
|
36
|
+
disableOpenApi?: boolean
|
|
37
|
+
scalarCdnUrl?: string
|
|
38
|
+
|
|
39
|
+
openApiTitle?: string
|
|
40
|
+
openApiDescription?: string
|
|
41
|
+
openApiVersion?: string
|
|
42
|
+
openApiServers?: OpenApiServerConfig[]
|
|
43
|
+
openApiSecuritySchemes?: Record<string, OpenApiSecuritySchemeConfig>
|
|
44
|
+
openApiSecurity?: Record<string, string[]>[]
|
|
45
|
+
|
|
46
|
+
guard?: {
|
|
47
|
+
resolveVariant?: (req: Request) => string | undefined
|
|
48
|
+
variantHeader?: string
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
queryBuilder?: QueryBuilderConfig | false
|
|
52
|
+
|
|
53
|
+
pagination?: {
|
|
54
|
+
defaultLimit?: number
|
|
55
|
+
maxLimit?: number
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
findUnique?: OperationConfig
|
|
59
|
+
findUniqueOrThrow?: OperationConfig
|
|
60
|
+
findFirst?: OperationConfig
|
|
61
|
+
findFirstOrThrow?: OperationConfig
|
|
62
|
+
findMany?: OperationConfig
|
|
63
|
+
findManyPaginated?: OperationConfig
|
|
64
|
+
create?: OperationConfig
|
|
65
|
+
createMany?: OperationConfig
|
|
66
|
+
createManyAndReturn?: OperationConfig
|
|
67
|
+
update?: OperationConfig
|
|
68
|
+
updateMany?: OperationConfig
|
|
69
|
+
updateManyAndReturn?: OperationConfig
|
|
70
|
+
upsert?: OperationConfig
|
|
71
|
+
delete?: OperationConfig
|
|
72
|
+
deleteMany?: OperationConfig
|
|
73
|
+
aggregate?: OperationConfig
|
|
74
|
+
count?: OperationConfig
|
|
75
|
+
groupBy?: OperationConfig
|
|
36
76
|
}
|
|
@@ -0,0 +1,78 @@
|
|
|
1
|
+
import { GeneratorOptions } from '@prisma/generator-helper'
|
|
2
|
+
import path from 'path'
|
|
3
|
+
|
|
4
|
+
function findClientGenerator(options: GeneratorOptions) {
|
|
5
|
+
const byName = options.otherGenerators.find((gen) => gen.name === 'client')
|
|
6
|
+
if (byName) return byName
|
|
7
|
+
|
|
8
|
+
const byProvider = options.otherGenerators.find(
|
|
9
|
+
(gen) =>
|
|
10
|
+
gen.provider.value === 'prisma-client-js' ||
|
|
11
|
+
gen.provider.value === '@prisma/client' ||
|
|
12
|
+
gen.provider.value === 'prisma-client',
|
|
13
|
+
)
|
|
14
|
+
if (byProvider) return byProvider
|
|
15
|
+
|
|
16
|
+
const withOutput = options.otherGenerators.find(
|
|
17
|
+
(gen) =>
|
|
18
|
+
gen.output?.value?.includes('prisma') ||
|
|
19
|
+
gen.output?.value?.includes('client'),
|
|
20
|
+
)
|
|
21
|
+
return withOutput || null
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
function getRelativeImportPath(
|
|
25
|
+
fromDir: string,
|
|
26
|
+
clientOutputPath: string,
|
|
27
|
+
): string {
|
|
28
|
+
let relativeImportPath = path.relative(fromDir, clientOutputPath)
|
|
29
|
+
relativeImportPath = relativeImportPath.split(path.sep).join(path.posix.sep)
|
|
30
|
+
if (!relativeImportPath.startsWith('.')) {
|
|
31
|
+
relativeImportPath = './' + relativeImportPath
|
|
32
|
+
}
|
|
33
|
+
return relativeImportPath
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
export function generateImportPrismaStatement(
|
|
37
|
+
generatorOptions: GeneratorOptions,
|
|
38
|
+
): string {
|
|
39
|
+
const clientGenerator = findClientGenerator(generatorOptions)
|
|
40
|
+
|
|
41
|
+
if (!clientGenerator || !clientGenerator.output?.value) {
|
|
42
|
+
throw new Error(
|
|
43
|
+
'Prisma client generator not found. Ensure a generator with provider "prisma-client-js" exists in your schema.',
|
|
44
|
+
)
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
const outputValue = generatorOptions.generator.output?.value
|
|
48
|
+
if (!outputValue) {
|
|
49
|
+
throw new Error('Generator output path not defined.')
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
const subDir = path.join(outputValue, '_relative')
|
|
53
|
+
const outputPath = getRelativeImportPath(subDir, clientGenerator.output.value)
|
|
54
|
+
|
|
55
|
+
return `import { Prisma, PrismaClient } from '${outputPath}';\n`
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
export function getRelativeClientPath(
|
|
59
|
+
generatorOptions: GeneratorOptions,
|
|
60
|
+
modelName: string,
|
|
61
|
+
): string {
|
|
62
|
+
const clientGenerator = findClientGenerator(generatorOptions)
|
|
63
|
+
|
|
64
|
+
if (!clientGenerator || !clientGenerator.output?.value) {
|
|
65
|
+
throw new Error(
|
|
66
|
+
'Prisma client generator not found. Ensure a generator with provider "prisma-client-js" exists in your schema.',
|
|
67
|
+
)
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
const outputValue = generatorOptions.generator.output?.value
|
|
71
|
+
if (!outputValue) {
|
|
72
|
+
throw new Error('Generator output path not defined.')
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
const routerDirPath = path.join(outputValue, modelName)
|
|
76
|
+
|
|
77
|
+
return getRelativeImportPath(routerDirPath, clientGenerator.output.value)
|
|
78
|
+
}
|
|
@@ -0,0 +1,138 @@
|
|
|
1
|
+
import { GeneratorOptions } from '@prisma/generator-helper'
|
|
2
|
+
|
|
3
|
+
export function generateQueryBuilderHelper(options: GeneratorOptions): string {
|
|
4
|
+
const schemaPath = options.schemaPath
|
|
5
|
+
? JSON.stringify(options.schemaPath)
|
|
6
|
+
: "require('path').resolve(process.cwd(), 'prisma/schema.prisma')"
|
|
7
|
+
|
|
8
|
+
return `import { spawn } from 'child_process'
|
|
9
|
+
import { resolve, join, dirname } from 'path'
|
|
10
|
+
import { existsSync, readFileSync } from 'fs'
|
|
11
|
+
import { createRequire } from 'module'
|
|
12
|
+
import type { ChildProcess } from 'child_process'
|
|
13
|
+
|
|
14
|
+
let _process: ChildProcess | null = null
|
|
15
|
+
let _stopping = false
|
|
16
|
+
let _cleanupRegistered = false
|
|
17
|
+
|
|
18
|
+
export interface QueryBuilderOptions {
|
|
19
|
+
port?: number
|
|
20
|
+
host?: string
|
|
21
|
+
schemaPath?: string
|
|
22
|
+
databaseUrl?: string
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
function findCliPath(): string | null {
|
|
26
|
+
try {
|
|
27
|
+
const req = createRequire(resolve(process.cwd(), 'package.json'))
|
|
28
|
+
const pkgJsonPath = req.resolve('prisma-query-builder-ui/package.json')
|
|
29
|
+
const pkgDir = dirname(pkgJsonPath)
|
|
30
|
+
const cliPath = join(pkgDir, 'bin', 'cli.js')
|
|
31
|
+
if (existsSync(cliPath)) return cliPath
|
|
32
|
+
} catch {}
|
|
33
|
+
|
|
34
|
+
let dir = process.cwd()
|
|
35
|
+
const root = resolve(dir, '/')
|
|
36
|
+
while (dir !== root) {
|
|
37
|
+
const candidate = join(dir, 'node_modules', 'prisma-query-builder-ui', 'bin', 'cli.js')
|
|
38
|
+
if (existsSync(candidate)) return candidate
|
|
39
|
+
dir = dirname(dir)
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
return null
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
export function startQueryBuilder(options: QueryBuilderOptions = {}): void {
|
|
46
|
+
if (_process) return
|
|
47
|
+
|
|
48
|
+
const env = typeof process !== 'undefined' && process.env ? process.env : {} as Record<string, string | undefined>
|
|
49
|
+
|
|
50
|
+
if (env.NODE_ENV === 'production') return
|
|
51
|
+
|
|
52
|
+
const cliPath = findCliPath()
|
|
53
|
+
if (!cliPath) {
|
|
54
|
+
console.warn('[query-builder] prisma-query-builder-ui not found. Install: npm install prisma-query-builder-ui')
|
|
55
|
+
return
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
const port = options.port || 5173
|
|
59
|
+
const host = options.host || 'localhost'
|
|
60
|
+
const schemaPath = options.schemaPath || ${schemaPath}
|
|
61
|
+
const databaseUrl = options.databaseUrl || env.DATABASE_URL || ''
|
|
62
|
+
|
|
63
|
+
if (!existsSync(schemaPath)) {
|
|
64
|
+
console.error('[query-builder] Schema file not found: ' + schemaPath)
|
|
65
|
+
return
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
let schemaContent: string
|
|
69
|
+
try {
|
|
70
|
+
schemaContent = readFileSync(schemaPath, 'utf-8')
|
|
71
|
+
} catch (err) {
|
|
72
|
+
console.error('[query-builder] Failed to read schema:', err)
|
|
73
|
+
return
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
const schemaCwd = dirname(resolve(schemaPath))
|
|
77
|
+
|
|
78
|
+
_process = spawn(process.execPath, [cliPath], {
|
|
79
|
+
stdio: 'inherit',
|
|
80
|
+
env: {
|
|
81
|
+
...env,
|
|
82
|
+
PORT: String(port),
|
|
83
|
+
HOST: host,
|
|
84
|
+
PRISMA_QUERY_BUILDER_MODE: 'embedded',
|
|
85
|
+
DISABLE_PERSISTENCE: 'true',
|
|
86
|
+
PRISMA_QUERY_BUILDER_SCHEMA_CONTENT: schemaContent,
|
|
87
|
+
PRISMA_QUERY_BUILDER_CWD: schemaCwd,
|
|
88
|
+
DATABASE_URL: databaseUrl,
|
|
89
|
+
},
|
|
90
|
+
})
|
|
91
|
+
|
|
92
|
+
_process.on('error', (err) => {
|
|
93
|
+
console.error('[query-builder] Failed to start:', err.message)
|
|
94
|
+
_process = null
|
|
95
|
+
})
|
|
96
|
+
|
|
97
|
+
_process.on('exit', (code) => {
|
|
98
|
+
const wasStopping = _stopping
|
|
99
|
+
_stopping = false
|
|
100
|
+
_process = null
|
|
101
|
+
if (!wasStopping && code !== 0) {
|
|
102
|
+
console.warn('[query-builder] Process exited with code ' + code)
|
|
103
|
+
}
|
|
104
|
+
})
|
|
105
|
+
|
|
106
|
+
if (!_cleanupRegistered) {
|
|
107
|
+
_cleanupRegistered = true
|
|
108
|
+
|
|
109
|
+
process.on('exit', () => {
|
|
110
|
+
stopQueryBuilder()
|
|
111
|
+
})
|
|
112
|
+
|
|
113
|
+
const handleSigint = () => {
|
|
114
|
+
stopQueryBuilder()
|
|
115
|
+
process.removeListener('SIGINT', handleSigint)
|
|
116
|
+
process.kill(process.pid, 'SIGINT')
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
const handleSigterm = () => {
|
|
120
|
+
stopQueryBuilder()
|
|
121
|
+
process.removeListener('SIGTERM', handleSigterm)
|
|
122
|
+
process.kill(process.pid, 'SIGTERM')
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
process.on('SIGINT', handleSigint)
|
|
126
|
+
process.on('SIGTERM', handleSigterm)
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
console.log('[query-builder] Starting on http://' + host + ':' + port)
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
export function stopQueryBuilder(): void {
|
|
133
|
+
if (!_process || _process.killed) return
|
|
134
|
+
_stopping = true
|
|
135
|
+
_process.kill()
|
|
136
|
+
}
|
|
137
|
+
`
|
|
138
|
+
}
|