@zenstackhq/cli 3.0.0-alpha.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.
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../package.json","../src/index.ts","../src/actions/db.ts","../src/utils/exec-utils.ts","../src/actions/action-utils.ts","../src/cli-error.ts","../src/actions/generate.ts","../src/actions/info.ts","../src/actions/init.ts","../src/actions/templates.ts","../src/actions/migrate.ts","../src/utils/version-utils.ts"],"sourcesContent":["{\n \"name\": \"@zenstackhq/cli\",\n \"publisher\": \"zenstack\",\n \"displayName\": \"ZenStack CLI\",\n \"description\": \"FullStack database toolkit with built-in access control and automatic API generation.\",\n \"version\": \"3.0.0-alpha.0\",\n \"type\": \"module\",\n \"author\": {\n \"name\": \"ZenStack Team\"\n },\n \"homepage\": \"https://zenstack.dev\",\n \"license\": \"MIT\",\n \"keywords\": [\n \"orm\",\n \"fullstack\",\n \"react\",\n \"typescript\",\n \"data modeling\"\n ],\n \"bin\": {\n \"zenstack\": \"bin/cli\"\n },\n \"scripts\": {\n \"build\": \"tsup-node\",\n \"watch\": \"tsup-node --watch\",\n \"lint\": \"eslint src --ext ts\",\n \"test\": \"vitest\",\n \"pack\": \"pnpm pack\"\n },\n \"dependencies\": {\n \"@types/node\": \"^20.0.0\",\n \"@zenstackhq/language\": \"workspace:*\",\n \"@zenstackhq/sdk\": \"workspace:*\",\n \"async-exit-hook\": \"^2.0.1\",\n \"colors\": \"1.4.0\",\n \"commander\": \"^8.3.0\",\n \"langium\": \"~3.3.0\",\n \"ora\": \"^5.4.1\",\n \"package-manager-detector\": \"^1.3.0\",\n \"tiny-invariant\": \"^1.3.3\",\n \"ts-pattern\": \"^4.3.0\"\n },\n \"peerDependencies\": {\n \"prisma\": \"^6.0.0\",\n \"typescript\": \"^5.0.0\"\n },\n \"devDependencies\": {\n \"@types/async-exit-hook\": \"^2.0.0\",\n \"@types/better-sqlite3\": \"^7.6.13\",\n \"@types/semver\": \"^7.3.13\",\n \"@types/tmp\": \"^0.2.6\",\n \"@zenstackhq/runtime\": \"workspace:*\",\n \"@zenstackhq/testtools\": \"workspace:*\",\n \"better-sqlite3\": \"^11.8.1\",\n \"tmp\": \"^0.2.3\"\n }\n}\n","import { ZModelLanguageMetaData } from '@zenstackhq/language';\nimport colors from 'colors';\nimport { Command, Option } from 'commander';\nimport * as actions from './actions';\nimport { getVersion } from './utils/version-utils';\n\nconst generateAction = async (\n options: Parameters<typeof actions.generate>[0]\n): Promise<void> => {\n await actions.generate(options);\n};\n\nconst migrateAction = async (command: string, options: any): Promise<void> => {\n await actions.migrate(command, options);\n};\n\nconst dbAction = async (command: string, options: any): Promise<void> => {\n await actions.db(command, options);\n};\n\nconst infoAction = async (projectPath: string): Promise<void> => {\n await actions.info(projectPath);\n};\n\nconst initAction = async (projectPath: string): Promise<void> => {\n await actions.init(projectPath);\n};\n\nexport function createProgram() {\n const program = new Command('zenstack');\n\n program.version(getVersion()!, '-v --version', 'display CLI version');\n\n const schemaExtensions = ZModelLanguageMetaData.fileExtensions.join(', ');\n\n program\n .description(\n `${colors.bold.blue(\n 'ζ'\n )} ZenStack is a Prisma power pack for building full-stack apps.\\n\\nDocumentation: https://zenstack.dev.`\n )\n .showHelpAfterError()\n .showSuggestionAfterError();\n\n const schemaOption = new Option(\n '--schema <file>',\n `schema file (with extension ${schemaExtensions}). Defaults to \"schema.zmodel\" unless specified in package.json.`\n );\n\n program\n .command('generate')\n .description('Run code generation.')\n .addOption(schemaOption)\n .addOption(\n new Option(\n '-o, --output <path>',\n 'default output directory for core plugins'\n )\n )\n .action(generateAction);\n\n const migrateCommand = program\n .command('migrate')\n .description('Update the database schema with migrations.');\n\n migrateCommand\n .command('dev')\n .addOption(schemaOption)\n .addOption(new Option('-n, --name <name>', 'migration name'))\n .addOption(\n new Option('--create-only', 'only create migration, do not apply')\n )\n .description(\n 'Create a migration from changes in schema and apply it to the database.'\n )\n .action((options) => migrateAction('dev', options));\n\n migrateCommand\n .command('reset')\n .addOption(schemaOption)\n .addOption(new Option('--force', 'skip the confirmation prompt'))\n .description(\n 'Reset your database and apply all migrations, all data will be lost.'\n )\n .action((options) => migrateAction('reset', options));\n\n migrateCommand\n .command('deploy')\n .addOption(schemaOption)\n .description(\n 'Deploy your pending migrations to your production/staging database.'\n )\n .action((options) => migrateAction('deploy', options));\n\n migrateCommand\n .command('status')\n .addOption(schemaOption)\n .description('check the status of your database migrations.')\n .action((options) => migrateAction('status', options));\n\n const dbCommand = program\n .command('db')\n .description('Manage your database schema during development.');\n\n dbCommand\n .command('push')\n .description('Push the state from your schema to your database')\n .addOption(schemaOption)\n .addOption(\n new Option('--accept-data-loss', 'ignore data loss warnings')\n )\n .addOption(\n new Option(\n '--force-reset',\n 'force a reset of the database before push'\n )\n )\n .action((options) => dbAction('push', options));\n\n program\n .command('info')\n .description(\n 'Get information of installed ZenStack and related packages.'\n )\n .argument('[path]', 'project path', '.')\n .action(infoAction);\n\n program\n .command('init')\n .description('Initialize an existing project for ZenStack.')\n .argument('[path]', 'project path', '.')\n .action(initAction);\n\n return program;\n}\n\nconst program = createProgram();\nprogram.parse(process.argv);\n","import path from 'node:path';\nimport { execPackage } from '../utils/exec-utils';\nimport { getSchemaFile, handleSubProcessError } from './action-utils';\nimport { run as runGenerate } from './generate';\n\ntype CommonOptions = {\n schema?: string;\n name?: string;\n};\n\n/**\n * CLI action for db related commands\n */\nexport async function run(command: string, options: CommonOptions) {\n const schemaFile = getSchemaFile(options.schema);\n\n // run generate first\n await runGenerate({\n schema: schemaFile,\n silent: true,\n });\n\n const prismaSchemaFile = path.join(\n path.dirname(schemaFile),\n 'schema.prisma'\n );\n\n switch (command) {\n case 'push':\n await runPush(prismaSchemaFile, options);\n break;\n }\n}\n\nasync function runPush(prismaSchemaFile: string, options: any) {\n const cmd = `prisma db push --schema \"${prismaSchemaFile}\"${\n options.acceptDataLoss ? ' --accept-data-loss' : ''\n }${options.forceReset ? ' --force-reset' : ''} --skip-generate`;\n try {\n await execPackage(cmd, {\n stdio: 'inherit',\n });\n } catch (err) {\n handleSubProcessError(err);\n }\n}\n","import { execSync as _exec, type ExecSyncOptions } from 'child_process';\n\n/**\n * Utility for executing command synchronously and prints outputs on current console\n */\nexport function execSync(\n cmd: string,\n options?: Omit<ExecSyncOptions, 'env'> & { env?: Record<string, string> }\n): void {\n const { env, ...restOptions } = options ?? {};\n const mergedEnv = env ? { ...process.env, ...env } : undefined;\n _exec(cmd, {\n encoding: 'utf-8',\n stdio: options?.stdio ?? 'inherit',\n env: mergedEnv,\n ...restOptions,\n });\n}\n\n/**\n * Utility for running package commands through npx/bunx\n */\nexport function execPackage(\n cmd: string,\n options?: Omit<ExecSyncOptions, 'env'> & { env?: Record<string, string> }\n): void {\n const packageManager = process?.versions?.['bun'] ? 'bunx' : 'npx';\n execSync(`${packageManager} ${cmd}`, options);\n}\n","import fs from 'node:fs';\nimport { CliError } from '../cli-error';\nimport { loadDocument } from '@zenstackhq/language';\nimport colors from 'colors';\n\nexport function getSchemaFile(file?: string) {\n if (file) {\n if (!fs.existsSync(file)) {\n throw new CliError(`Schema file not found: ${file}`);\n }\n return file;\n }\n\n if (fs.existsSync('./zenstack/schema.zmodel')) {\n return './zenstack/schema.zmodel';\n } else if (fs.existsSync('./schema.zmodel')) {\n return './schema.zmodel';\n } else {\n throw new CliError(\n 'Schema file not found in default locations (\"./zenstack/schema.zmodel\" or \"./schema.zmodel\").'\n );\n }\n}\n\nexport async function loadSchemaDocument(schemaFile: string) {\n const loadResult = await loadDocument(schemaFile);\n if (!loadResult.success) {\n console.error(colors.red('Error loading schema:'));\n loadResult.errors.forEach((err) => {\n console.error(colors.red(err));\n });\n throw new CliError('Failed to load schema');\n }\n return loadResult.model;\n}\n\nexport function handleSubProcessError(err: unknown) {\n if (\n err instanceof Error &&\n 'status' in err &&\n typeof err.status === 'number'\n ) {\n process.exit(err.status);\n } else {\n process.exit(1);\n }\n}\n","/**\n * Indicating an error during CLI execution\n */\nexport class CliError extends Error {}\n","import { isPlugin, LiteralExpr, type Model } from '@zenstackhq/language/ast';\nimport type { CliGenerator } from '@zenstackhq/runtime/client';\nimport { PrismaSchemaGenerator, TsSchemaGenerator } from '@zenstackhq/sdk';\nimport colors from 'colors';\nimport fs from 'node:fs';\nimport path from 'node:path';\nimport invariant from 'tiny-invariant';\nimport { getSchemaFile, loadSchemaDocument } from './action-utils';\n\ntype Options = {\n schema?: string;\n output?: string;\n silent?: boolean;\n};\n\n/**\n * CLI action for generating code from schema\n */\nexport async function run(options: Options) {\n const schemaFile = getSchemaFile(options.schema);\n\n const model = await loadSchemaDocument(schemaFile);\n const outputPath = options.output ?? path.dirname(schemaFile);\n\n // generate TS schema\n const tsSchemaFile = path.join(outputPath, 'schema.ts');\n await new TsSchemaGenerator().generate(schemaFile, [], tsSchemaFile);\n\n await runPlugins(model, outputPath, tsSchemaFile);\n\n // generate Prisma schema\n const prismaSchema = await new PrismaSchemaGenerator(model).generate();\n fs.writeFileSync(path.join(outputPath, 'schema.prisma'), prismaSchema);\n\n if (!options.silent) {\n console.log(colors.green('Generation completed successfully.'));\n console.log(`You can now create a ZenStack client with it.\n\n\\`\\`\\`\nimport { ZenStackClient } from '@zenstackhq/runtime';\nimport { schema } from '${outputPath}/schema';\n\nconst client = new ZenStackClient(schema);\n\\`\\`\\`\n`);\n }\n}\n\nasync function runPlugins(\n model: Model,\n outputPath: string,\n tsSchemaFile: string\n) {\n const plugins = model.declarations.filter(isPlugin);\n for (const plugin of plugins) {\n const providerField = plugin.fields.find((f) => f.name === 'provider');\n invariant(\n providerField,\n `Plugin ${plugin.name} does not have a provider field`\n );\n const provider = (providerField.value as LiteralExpr).value as string;\n let useProvider = provider;\n if (useProvider.startsWith('@core/')) {\n useProvider = `@zenstackhq/runtime/plugins/${useProvider.slice(6)}`;\n }\n const generator = (await import(useProvider)).default as CliGenerator;\n console.log('Running generator:', provider);\n await generator({ model, outputPath, tsSchemaFile });\n }\n}\n","import colors from 'colors';\nimport path from 'node:path';\n\n/**\n * CLI action for getting information about installed ZenStack packages\n */\nexport async function run(projectPath: string) {\n const packages = await getZenStackPackages(projectPath);\n if (!packages) {\n console.error(\n 'Unable to locate package.json. Are you in a valid project directory?'\n );\n return;\n }\n\n console.log('Installed ZenStack Packages:');\n const versions = new Set<string>();\n for (const { pkg, version } of packages) {\n if (version) {\n versions.add(version);\n }\n console.log(` ${colors.green(pkg.padEnd(20))}\\t${version}`);\n }\n\n if (versions.size > 1) {\n console.warn(\n colors.yellow(\n 'WARNING: Multiple versions of Zenstack packages detected. This may cause issues.'\n )\n );\n }\n}\n\nasync function getZenStackPackages(\n projectPath: string\n): Promise<Array<{ pkg: string; version: string | undefined }>> {\n let pkgJson: {\n dependencies: Record<string, unknown>;\n devDependencies: Record<string, unknown>;\n };\n const resolvedPath = path.resolve(projectPath);\n try {\n pkgJson = (\n await import(path.join(resolvedPath, 'package.json'), {\n with: { type: 'json' },\n })\n ).default;\n } catch (err) {\n return [];\n }\n\n const packages = Array.from(\n new Set(\n [\n ...Object.keys(pkgJson.dependencies ?? {}),\n ...Object.keys(pkgJson.devDependencies ?? {}),\n ].filter((p) => p.startsWith('@zenstackhq/') || p === 'zenstack')\n )\n ).sort();\n\n const result = await Promise.all(\n packages.map(async (pkg) => {\n try {\n const depPkgJson = (\n await import(`${pkg}/package.json`, {\n with: { type: 'json' },\n })\n ).default;\n return { pkg, version: depPkgJson.version as string };\n } catch {\n return { pkg, version: undefined };\n }\n })\n );\n\n return result;\n}\n","import colors from 'colors';\nimport fs from 'node:fs';\nimport path from 'node:path';\nimport ora from 'ora';\nimport { detect, resolveCommand } from 'package-manager-detector';\nimport { CliError } from '../cli-error';\nimport { execSync } from '../utils/exec-utils';\nimport { STARTER_ZMODEL } from './templates';\n\n/**\n * CLI action for getting information about installed ZenStack packages\n */\nexport async function run(projectPath: string) {\n const packages = [\n { name: '@zenstackhq/cli', dev: true },\n { name: '@zenstackhq/runtime', dev: false },\n ];\n let pm = await detect();\n if (!pm) {\n pm = { agent: 'npm', name: 'npm' };\n }\n\n console.log(colors.gray(`Using package manager: ${pm.agent}`));\n\n for (const pkg of packages) {\n const resolved = resolveCommand(pm.agent, 'install', [\n pkg.name,\n ...(pkg.dev ? [pm.agent === 'yarn' ? '--dev' : '--save-dev'] : []),\n ]);\n if (!resolved) {\n throw new CliError(\n `Unable to determine how to install package \"${pkg.name}\". Please install it manually.`\n );\n }\n\n const spinner = ora(`Installing \"${pkg.name}\"`).start();\n try {\n execSync(`${resolved.command} ${resolved.args.join(' ')}`, {\n cwd: projectPath,\n });\n spinner.succeed();\n } catch (e) {\n spinner.fail();\n throw e;\n }\n }\n\n const generationFolder = 'zenstack';\n\n if (!fs.existsSync(path.join(projectPath, generationFolder))) {\n fs.mkdirSync(path.join(projectPath, generationFolder));\n }\n\n if (\n !fs.existsSync(\n path.join(projectPath, generationFolder, 'schema.zmodel')\n )\n ) {\n fs.writeFileSync(\n path.join(projectPath, generationFolder, 'schema.zmodel'),\n STARTER_ZMODEL\n );\n } else {\n console.log(\n colors.yellow(\n 'Schema file already exists. Skipping generation of sample.'\n )\n );\n }\n\n console.log(colors.green('ZenStack project initialized successfully!'));\n console.log(\n colors.gray(\n `See \"${generationFolder}/schema.zmodel\" for your database schema.`\n )\n );\n console.log(\n colors.gray(\n 'Run `zenstack generate` to compile the the schema into a TypeScript file.'\n )\n );\n}\n","export const STARTER_ZMODEL = `// This is a sample model to get you started.\n\n/// A sample data source using local sqlite db.\ndatasource db {\n provider = 'sqlite'\n url = 'file:./dev.db'\n}\n\n/// User model\nmodel User {\n id String @id @default(cuid())\n email String @unique @email @length(6, 32)\n posts Post[]\n}\n\n/// Post model\nmodel Post {\n id String @id @default(cuid())\n createdAt DateTime @default(now())\n updatedAt DateTime @updatedAt\n title String @length(1, 256)\n content String\n published Boolean @default(false)\n author User @relation(fields: [authorId], references: [id])\n authorId String\n}\n`;\n\nexport const STARTER_MAIN_TS = `import { ZenStackClient } from '@zenstackhq/runtime';\nimport { schema } from './zenstack/schema';\n\nasync function main() {\n const client = new ZenStackClient(schema);\n const user = await client.user.create({\n data: {\n email: 'test@zenstack.dev',\n posts: {\n create: [\n {\n title: 'Hello World',\n content: 'This is a test post',\n },\n ],\n },\n },\n include: { posts: true }\n });\n console.log('User created:', user);\n}\n\nmain();\n`;\n","import path from 'node:path';\nimport { execPackage } from '../utils/exec-utils';\nimport { getSchemaFile } from './action-utils';\nimport { run as runGenerate } from './generate';\n\ntype CommonOptions = {\n schema?: string;\n name?: string;\n};\n\n/**\n * CLI action for migration-related commands\n */\nexport async function run(command: string, options: CommonOptions) {\n const schemaFile = getSchemaFile(options.schema);\n\n // run generate first\n await runGenerate({\n schema: schemaFile,\n silent: true,\n });\n\n const prismaSchemaFile = path.join(\n path.dirname(schemaFile),\n 'schema.prisma'\n );\n\n switch (command) {\n case 'dev':\n await runDev(prismaSchemaFile, options);\n break;\n\n case 'reset':\n await runReset(prismaSchemaFile, options as any);\n break;\n\n case 'deploy':\n await runDeploy(prismaSchemaFile, options);\n break;\n\n case 'status':\n await runStatus(prismaSchemaFile, options);\n break;\n }\n}\n\nasync function runDev(prismaSchemaFile: string, _options: unknown) {\n try {\n await execPackage(\n `prisma migrate dev --schema \"${prismaSchemaFile}\" --skip-generate`,\n {\n stdio: 'inherit',\n }\n );\n } catch (err) {\n handleSubProcessError(err);\n }\n}\n\nasync function runReset(prismaSchemaFile: string, options: { force: boolean }) {\n try {\n await execPackage(\n `prisma migrate reset --schema \"${prismaSchemaFile}\"${\n options.force ? ' --force' : ''\n }`,\n {\n stdio: 'inherit',\n }\n );\n } catch (err) {\n handleSubProcessError(err);\n }\n}\n\nasync function runDeploy(prismaSchemaFile: string, _options: unknown) {\n try {\n await execPackage(\n `prisma migrate deploy --schema \"${prismaSchemaFile}\"`,\n {\n stdio: 'inherit',\n }\n );\n } catch (err) {\n handleSubProcessError(err);\n }\n}\n\nasync function runStatus(prismaSchemaFile: string, _options: unknown) {\n try {\n await execPackage(\n `prisma migrate status --schema \"${prismaSchemaFile}\"`,\n {\n stdio: 'inherit',\n }\n );\n } catch (err) {\n handleSubProcessError(err);\n }\n}\n\nfunction handleSubProcessError(err: unknown) {\n if (\n err instanceof Error &&\n 'status' in err &&\n typeof err.status === 'number'\n ) {\n process.exit(err.status);\n } else {\n process.exit(1);\n }\n}\n","/* eslint-disable @typescript-eslint/no-var-requires */\nexport function getVersion(): string | undefined {\n try {\n return require('../package.json').version;\n } catch {\n try {\n // dev environment\n return require('../../package.json').version;\n } catch {\n return undefined;\n }\n }\n}\n"],"mappings":";;;;;;;;;;;;;;AAAA;AAAA;AAAA;AAAA,MACI,MAAQ;AAAA,MACR,WAAa;AAAA,MACb,aAAe;AAAA,MACf,aAAe;AAAA,MACf,SAAW;AAAA,MACX,MAAQ;AAAA,MACR,QAAU;AAAA,QACN,MAAQ;AAAA,MACZ;AAAA,MACA,UAAY;AAAA,MACZ,SAAW;AAAA,MACX,UAAY;AAAA,QACR;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,MACJ;AAAA,MACA,KAAO;AAAA,QACH,UAAY;AAAA,MAChB;AAAA,MACA,SAAW;AAAA,QACP,OAAS;AAAA,QACT,OAAS;AAAA,QACT,MAAQ;AAAA,QACR,MAAQ;AAAA,QACR,MAAQ;AAAA,MACZ;AAAA,MACA,cAAgB;AAAA,QACZ,eAAe;AAAA,QACf,wBAAwB;AAAA,QACxB,mBAAmB;AAAA,QACnB,mBAAmB;AAAA,QACnB,QAAU;AAAA,QACV,WAAa;AAAA,QACb,SAAW;AAAA,QACX,KAAO;AAAA,QACP,4BAA4B;AAAA,QAC5B,kBAAkB;AAAA,QAClB,cAAc;AAAA,MAClB;AAAA,MACA,kBAAoB;AAAA,QAChB,QAAU;AAAA,QACV,YAAc;AAAA,MAClB;AAAA,MACA,iBAAmB;AAAA,QACf,0BAA0B;AAAA,QAC1B,yBAAyB;AAAA,QACzB,iBAAiB;AAAA,QACjB,cAAc;AAAA,QACd,uBAAuB;AAAA,QACvB,yBAAyB;AAAA,QACzB,kBAAkB;AAAA,QAClB,KAAO;AAAA,MACX;AAAA,IACJ;AAAA;AAAA;;;ACxDA,SAASA,8BAA8B;AACvC,OAAOC,aAAY;AACnB,SAASC,SAASC,cAAc;;;ACFhC,OAAOC,WAAU;;;ACAjB,SAASC,YAAYC,aAAmC;AAKjD,SAASC,SACZC,KACAC,SAAyE;AAEzE,QAAM,EAAEC,KAAK,GAAGC,YAAAA,IAAgBF,WAAW,CAAC;AAC5C,QAAMG,YAAYF,MAAM;IAAE,GAAGG,QAAQH;IAAK,GAAGA;EAAI,IAAII;AACrDC,QAAMP,KAAK;IACPQ,UAAU;IACVC,OAAOR,SAASQ,SAAS;IACzBP,KAAKE;IACL,GAAGD;EACP,CAAA;AACJ;AAZgBJ;AAiBT,SAASW,YACZV,KACAC,SAAyE;AAEzE,QAAMU,iBAAiBN,SAASO,WAAW,KAAA,IAAS,SAAS;AAC7Db,WAAS,GAAGY,cAAAA,IAAkBX,GAAAA,IAAOC,OAAAA;AACzC;AANgBS;;;ACtBhB,OAAOG,QAAQ;;;ACGR,IAAMC,WAAN,cAAuBC,MAAAA;EAH9B,OAG8BA;;;AAAO;;;ADDrC,SAASC,oBAAoB;AAC7B,OAAOC,YAAY;AAEZ,SAASC,cAAcC,MAAa;AACvC,MAAIA,MAAM;AACN,QAAI,CAACC,GAAGC,WAAWF,IAAAA,GAAO;AACtB,YAAM,IAAIG,SAAS,0BAA0BH,IAAAA,EAAM;IACvD;AACA,WAAOA;EACX;AAEA,MAAIC,GAAGC,WAAW,0BAAA,GAA6B;AAC3C,WAAO;EACX,WAAWD,GAAGC,WAAW,iBAAA,GAAoB;AACzC,WAAO;EACX,OAAO;AACH,UAAM,IAAIC,SACN,+FAAA;EAER;AACJ;AAjBgBJ;AAmBhB,eAAsBK,mBAAmBC,YAAkB;AACvD,QAAMC,aAAa,MAAMC,aAAaF,UAAAA;AACtC,MAAI,CAACC,WAAWE,SAAS;AACrBC,YAAQC,MAAMC,OAAOC,IAAI,uBAAA,CAAA;AACzBN,eAAWO,OAAOC,QAAQ,CAACC,QAAAA;AACvBN,cAAQC,MAAMC,OAAOC,IAAIG,GAAAA,CAAAA;IAC7B,CAAA;AACA,UAAM,IAAIZ,SAAS,uBAAA;EACvB;AACA,SAAOG,WAAWU;AACtB;AAVsBZ;AAYf,SAASa,sBAAsBF,KAAY;AAC9C,MACIA,eAAeG,SACf,YAAYH,OACZ,OAAOA,IAAII,WAAW,UACxB;AACEC,YAAQC,KAAKN,IAAII,MAAM;EAC3B,OAAO;AACHC,YAAQC,KAAK,CAAA;EACjB;AACJ;AAVgBJ;;;AEpChB,SAASK,gBAAyC;AAElD,SAASC,uBAAuBC,yBAAyB;AACzD,OAAOC,aAAY;AACnB,OAAOC,SAAQ;AACf,OAAOC,UAAU;AACjB,OAAOC,eAAe;AAYtB,eAAsBC,IAAIC,SAAgB;AACtC,QAAMC,aAAaC,cAAcF,QAAQG,MAAM;AAE/C,QAAMC,QAAQ,MAAMC,mBAAmBJ,UAAAA;AACvC,QAAMK,aAAaN,QAAQO,UAAUC,KAAKC,QAAQR,UAAAA;AAGlD,QAAMS,eAAeF,KAAKG,KAAKL,YAAY,WAAA;AAC3C,QAAM,IAAIM,kBAAAA,EAAoBC,SAASZ,YAAY,CAAA,GAAIS,YAAAA;AAEvD,QAAMI,WAAWV,OAAOE,YAAYI,YAAAA;AAGpC,QAAMK,eAAe,MAAM,IAAIC,sBAAsBZ,KAAAA,EAAOS,SAAQ;AACpEI,EAAAA,IAAGC,cAAcV,KAAKG,KAAKL,YAAY,eAAA,GAAkBS,YAAAA;AAEzD,MAAI,CAACf,QAAQmB,QAAQ;AACjBC,YAAQC,IAAIC,QAAOC,MAAM,oCAAA,CAAA;AACzBH,YAAQC,IAAI;;;;0BAIMf,UAAAA;;;;CAIzB;EACG;AACJ;AA5BsBP;AA8BtB,eAAee,WACXV,OACAE,YACAI,cAAoB;AAEpB,QAAMc,UAAUpB,MAAMqB,aAAaC,OAAOC,QAAAA;AAC1C,aAAWC,UAAUJ,SAAS;AAC1B,UAAMK,gBAAgBD,OAAOE,OAAOC,KAAK,CAACC,MAAMA,EAAEC,SAAS,UAAA;AAC3DC,cACIL,eACA,UAAUD,OAAOK,IAAI,iCAAiC;AAE1D,UAAME,WAAYN,cAAcO,MAAsBA;AACtD,QAAIC,cAAcF;AAClB,QAAIE,YAAYC,WAAW,QAAA,GAAW;AAClCD,oBAAc,+BAA+BA,YAAYE,MAAM,CAAA,CAAA;IACnE;AACA,UAAMC,aAAa,MAAM,OAAOH,cAAcI;AAC9CrB,YAAQC,IAAI,sBAAsBc,QAAAA;AAClC,UAAMK,UAAU;MAAEpC;MAAOE;MAAYI;IAAa,CAAA;EACtD;AACJ;AArBeI;;;AJnCf,eAAsB4B,KAAIC,SAAiBC,SAAsB;AAC7D,QAAMC,aAAaC,cAAcF,QAAQG,MAAM;AAG/C,QAAMC,IAAY;IACdD,QAAQF;IACRI,QAAQ;EACZ,CAAA;AAEA,QAAMC,mBAAmBC,MAAKC,KAC1BD,MAAKE,QAAQR,UAAAA,GACb,eAAA;AAGJ,UAAQF,SAAAA;IACJ,KAAK;AACD,YAAMW,QAAQJ,kBAAkBN,OAAAA;AAChC;EACR;AACJ;AAnBsBF,OAAAA,MAAAA;AAqBtB,eAAeY,QAAQJ,kBAA0BN,SAAY;AACzD,QAAMW,MAAM,4BAA4BL,gBAAAA,IACpCN,QAAQY,iBAAiB,wBAAwB,EAAA,GAClDZ,QAAQa,aAAa,mBAAmB,EAAA;AAC3C,MAAI;AACA,UAAMC,YAAYH,KAAK;MACnBI,OAAO;IACX,CAAA;EACJ,SAASC,KAAK;AACVC,0BAAsBD,GAAAA;EAC1B;AACJ;AAXeN;;;AKlCf,OAAOQ,aAAY;AACnB,OAAOC,WAAU;AAKjB,eAAsBC,KAAIC,aAAmB;AACzC,QAAMC,WAAW,MAAMC,oBAAoBF,WAAAA;AAC3C,MAAI,CAACC,UAAU;AACXE,YAAQC,MACJ,sEAAA;AAEJ;EACJ;AAEAD,UAAQE,IAAI,8BAAA;AACZ,QAAMC,WAAW,oBAAIC,IAAAA;AACrB,aAAW,EAAEC,KAAKC,QAAO,KAAMR,UAAU;AACrC,QAAIQ,SAAS;AACTH,eAASI,IAAID,OAAAA;IACjB;AACAN,YAAQE,IAAI,OAAOM,QAAOC,MAAMJ,IAAIK,OAAO,EAAA,CAAA,CAAA,IAASJ,OAAAA,EAAS;EACjE;AAEA,MAAIH,SAASQ,OAAO,GAAG;AACnBX,YAAQY,KACJJ,QAAOK,OACH,kFAAA,CAAA;EAGZ;AACJ;AAzBsBjB,OAAAA,MAAAA;AA2BtB,eAAeG,oBACXF,aAAmB;AAEnB,MAAIiB;AAIJ,QAAMC,eAAeC,MAAKC,QAAQpB,WAAAA;AAClC,MAAI;AACAiB,eACI,MAAM,OAAOE,MAAKE,KAAKH,cAAc,cAAA,GAAiB;MAClDI,MAAM;QAAEC,MAAM;MAAO;IACzB,IACFC;EACN,SAASC,KAAK;AACV,WAAO,CAAA;EACX;AAEA,QAAMxB,WAAWyB,MAAMC,KACnB,IAAIpB,IACA;OACOqB,OAAOC,KAAKZ,QAAQa,gBAAgB,CAAC,CAAA;OACrCF,OAAOC,KAAKZ,QAAQc,mBAAmB,CAAC,CAAA;IAC7CC,OAAO,CAACC,MAAMA,EAAEC,WAAW,cAAA,KAAmBD,MAAM,UAAA,CAAA,CAAA,EAE5DE,KAAI;AAEN,QAAMC,SAAS,MAAMC,QAAQC,IACzBrC,SAASsC,IAAI,OAAO/B,QAAAA;AAChB,QAAI;AACA,YAAMgC,cACF,MAAM,OAAO,GAAGhC,GAAAA,iBAAoB;QAChCc,MAAM;UAAEC,MAAM;QAAO;MACzB,IACFC;AACF,aAAO;QAAEhB;QAAKC,SAAS+B,WAAW/B;MAAkB;IACxD,QAAQ;AACJ,aAAO;QAAED;QAAKC,SAASgC;MAAU;IACrC;EACJ,CAAA,CAAA;AAGJ,SAAOL;AACX;AA3CelC;;;ACjCf,OAAOwC,aAAY;AACnB,OAAOC,SAAQ;AACf,OAAOC,WAAU;AACjB,OAAOC,SAAS;AAChB,SAASC,QAAQC,sBAAsB;;;ACJhC,IAAMC,iBAAiB;;;;;;;;;;;;;;;;;;;;;;;;;;;;;ADY9B,eAAsBC,KAAIC,aAAmB;AACzC,QAAMC,WAAW;IACb;MAAEC,MAAM;MAAmBC,KAAK;IAAK;IACrC;MAAED,MAAM;MAAuBC,KAAK;IAAM;;AAE9C,MAAIC,KAAK,MAAMC,OAAAA;AACf,MAAI,CAACD,IAAI;AACLA,SAAK;MAAEE,OAAO;MAAOJ,MAAM;IAAM;EACrC;AAEAK,UAAQC,IAAIC,QAAOC,KAAK,0BAA0BN,GAAGE,KAAK,EAAE,CAAA;AAE5D,aAAWK,OAAOV,UAAU;AACxB,UAAMW,WAAWC,eAAeT,GAAGE,OAAO,WAAW;MACjDK,IAAIT;SACAS,IAAIR,MAAM;QAACC,GAAGE,UAAU,SAAS,UAAU;UAAgB,CAAA;KAClE;AACD,QAAI,CAACM,UAAU;AACX,YAAM,IAAIE,SACN,+CAA+CH,IAAIT,IAAI,gCAAgC;IAE/F;AAEA,UAAMa,UAAUC,IAAI,eAAeL,IAAIT,IAAI,GAAG,EAAEe,MAAK;AACrD,QAAI;AACAC,eAAS,GAAGN,SAASO,OAAO,IAAIP,SAASQ,KAAKC,KAAK,GAAA,CAAA,IAAQ;QACvDC,KAAKtB;MACT,CAAA;AACAe,cAAQQ,QAAO;IACnB,SAASC,GAAG;AACRT,cAAQU,KAAI;AACZ,YAAMD;IACV;EACJ;AAEA,QAAME,mBAAmB;AAEzB,MAAI,CAACC,IAAGC,WAAWC,MAAKR,KAAKrB,aAAa0B,gBAAAA,CAAAA,GAAoB;AAC1DC,IAAAA,IAAGG,UAAUD,MAAKR,KAAKrB,aAAa0B,gBAAAA,CAAAA;EACxC;AAEA,MACI,CAACC,IAAGC,WACAC,MAAKR,KAAKrB,aAAa0B,kBAAkB,eAAA,CAAA,GAE/C;AACEC,IAAAA,IAAGI,cACCF,MAAKR,KAAKrB,aAAa0B,kBAAkB,eAAA,GACzCM,cAAAA;EAER,OAAO;AACHzB,YAAQC,IACJC,QAAOwB,OACH,4DAAA,CAAA;EAGZ;AAEA1B,UAAQC,IAAIC,QAAOyB,MAAM,4CAAA,CAAA;AACzB3B,UAAQC,IACJC,QAAOC,KACH,QAAQgB,gBAAAA,2CAA2D,CAAA;AAG3EnB,UAAQC,IACJC,QAAOC,KACH,2EAAA,CAAA;AAGZ;AArEsBX,OAAAA,MAAAA;;;AEZtB,OAAOoC,WAAU;AAajB,eAAsBC,KAAIC,SAAiBC,SAAsB;AAC7D,QAAMC,aAAaC,cAAcF,QAAQG,MAAM;AAG/C,QAAMC,IAAY;IACdD,QAAQF;IACRI,QAAQ;EACZ,CAAA;AAEA,QAAMC,mBAAmBC,MAAKC,KAC1BD,MAAKE,QAAQR,UAAAA,GACb,eAAA;AAGJ,UAAQF,SAAAA;IACJ,KAAK;AACD,YAAMW,OAAOJ,kBAAkBN,OAAAA;AAC/B;IAEJ,KAAK;AACD,YAAMW,SAASL,kBAAkBN,OAAAA;AACjC;IAEJ,KAAK;AACD,YAAMY,UAAUN,kBAAkBN,OAAAA;AAClC;IAEJ,KAAK;AACD,YAAMa,UAAUP,kBAAkBN,OAAAA;AAClC;EACR;AACJ;AA/BsBF,OAAAA,MAAAA;AAiCtB,eAAeY,OAAOJ,kBAA0BQ,UAAiB;AAC7D,MAAI;AACA,UAAMC,YACF,gCAAgCT,gBAAAA,qBAChC;MACIU,OAAO;IACX,CAAA;EAER,SAASC,KAAK;AACVC,IAAAA,uBAAsBD,GAAAA;EAC1B;AACJ;AAXeP;AAaf,eAAeC,SAASL,kBAA0BN,SAA2B;AACzE,MAAI;AACA,UAAMe,YACF,kCAAkCT,gBAAAA,IAC9BN,QAAQmB,QAAQ,aAAa,EAAA,IAEjC;MACIH,OAAO;IACX,CAAA;EAER,SAASC,KAAK;AACVC,IAAAA,uBAAsBD,GAAAA;EAC1B;AACJ;AAbeN;AAef,eAAeC,UAAUN,kBAA0BQ,UAAiB;AAChE,MAAI;AACA,UAAMC,YACF,mCAAmCT,gBAAAA,KACnC;MACIU,OAAO;IACX,CAAA;EAER,SAASC,KAAK;AACVC,IAAAA,uBAAsBD,GAAAA;EAC1B;AACJ;AAXeL;AAaf,eAAeC,UAAUP,kBAA0BQ,UAAiB;AAChE,MAAI;AACA,UAAMC,YACF,mCAAmCT,gBAAAA,KACnC;MACIU,OAAO;IACX,CAAA;EAER,SAASC,KAAK;AACVC,IAAAA,uBAAsBD,GAAAA;EAC1B;AACJ;AAXeJ;AAaf,SAASK,uBAAsBD,KAAY;AACvC,MACIA,eAAeG,SACf,YAAYH,OACZ,OAAOA,IAAII,WAAW,UACxB;AACEC,YAAQC,KAAKN,IAAII,MAAM;EAC3B,OAAO;AACHC,YAAQC,KAAK,CAAA;EACjB;AACJ;AAVSL,OAAAA,wBAAAA;;;ACnGF,SAASM,aAAAA;AACZ,MAAI;AACA,WAAOC,UAAQ,iBAAA,EAAmBC;EACtC,QAAQ;AACJ,QAAI;AAEA,aAAOD,kBAA8BC;IACzC,QAAQ;AACJ,aAAOC;IACX;EACJ;AACJ;AAXgBH;;;AVKhB,IAAMI,iBAAiB,8BACnBC,YAAAA;AAEA,QAAcC,IAASD,OAAAA;AAC3B,GAJuB;AAMvB,IAAME,gBAAgB,8BAAOC,SAAiBH,YAAAA;AAC1C,QAAcI,KAAQD,SAASH,OAAAA;AACnC,GAFsB;AAItB,IAAMK,WAAW,8BAAOF,SAAiBH,YAAAA;AACrC,QAAcM,KAAGH,SAASH,OAAAA;AAC9B,GAFiB;AAIjB,IAAMO,aAAa,8BAAOC,gBAAAA;AACtB,QAAcC,KAAKD,WAAAA;AACvB,GAFmB;AAInB,IAAME,aAAa,8BAAOF,gBAAAA;AACtB,QAAcG,KAAKH,WAAAA;AACvB,GAFmB;AAIZ,SAASI,gBAAAA;AACZ,QAAMC,WAAU,IAAIC,QAAQ,UAAA;AAE5BD,EAAAA,SAAQE,QAAQC,WAAAA,GAAe,gBAAgB,qBAAA;AAE/C,QAAMC,mBAAmBC,uBAAuBC,eAAeC,KAAK,IAAA;AAEpEP,EAAAA,SACKQ,YACG,GAAGC,QAAOC,KAAKC,KACX,QAAA,CAAA;;qCACqG,EAE5GC,mBAAkB,EAClBC,yBAAwB;AAE7B,QAAMC,eAAe,IAAIC,OACrB,mBACA,+BAA+BX,gBAAAA,kEAAkF;AAGrHJ,EAAAA,SACKV,QAAQ,UAAA,EACRkB,YAAY,sBAAA,EACZQ,UAAUF,YAAAA,EACVE,UACG,IAAID,OACA,uBACA,2CAAA,CAAA,EAGPE,OAAO/B,cAAAA;AAEZ,QAAMgC,iBAAiBlB,SAClBV,QAAQ,SAAA,EACRkB,YAAY,6CAAA;AAEjBU,iBACK5B,QAAQ,KAAA,EACR0B,UAAUF,YAAAA,EACVE,UAAU,IAAID,OAAO,qBAAqB,gBAAA,CAAA,EAC1CC,UACG,IAAID,OAAO,iBAAiB,qCAAA,CAAA,EAE/BP,YACG,yEAAA,EAEHS,OAAO,CAAC9B,YAAYE,cAAc,OAAOF,OAAAA,CAAAA;AAE9C+B,iBACK5B,QAAQ,OAAA,EACR0B,UAAUF,YAAAA,EACVE,UAAU,IAAID,OAAO,WAAW,8BAAA,CAAA,EAChCP,YACG,sEAAA,EAEHS,OAAO,CAAC9B,YAAYE,cAAc,SAASF,OAAAA,CAAAA;AAEhD+B,iBACK5B,QAAQ,QAAA,EACR0B,UAAUF,YAAAA,EACVN,YACG,qEAAA,EAEHS,OAAO,CAAC9B,YAAYE,cAAc,UAAUF,OAAAA,CAAAA;AAEjD+B,iBACK5B,QAAQ,QAAA,EACR0B,UAAUF,YAAAA,EACVN,YAAY,+CAAA,EACZS,OAAO,CAAC9B,YAAYE,cAAc,UAAUF,OAAAA,CAAAA;AAEjD,QAAMgC,YAAYnB,SACbV,QAAQ,IAAA,EACRkB,YAAY,iDAAA;AAEjBW,YACK7B,QAAQ,MAAA,EACRkB,YAAY,kDAAA,EACZQ,UAAUF,YAAAA,EACVE,UACG,IAAID,OAAO,sBAAsB,2BAAA,CAAA,EAEpCC,UACG,IAAID,OACA,iBACA,2CAAA,CAAA,EAGPE,OAAO,CAAC9B,YAAYK,SAAS,QAAQL,OAAAA,CAAAA;AAE1Ca,EAAAA,SACKV,QAAQ,MAAA,EACRkB,YACG,6DAAA,EAEHY,SAAS,UAAU,gBAAgB,GAAA,EACnCH,OAAOvB,UAAAA;AAEZM,EAAAA,SACKV,QAAQ,MAAA,EACRkB,YAAY,8CAAA,EACZY,SAAS,UAAU,gBAAgB,GAAA,EACnCH,OAAOpB,UAAAA;AAEZ,SAAOG;AACX;AA1GgBD;AA4GhB,IAAMC,UAAUD,cAAAA;AAChBC,QAAQqB,MAAMC,QAAQC,IAAI;","names":["ZModelLanguageMetaData","colors","Command","Option","path","execSync","_exec","execSync","cmd","options","env","restOptions","mergedEnv","process","undefined","_exec","encoding","stdio","execPackage","packageManager","versions","fs","CliError","Error","loadDocument","colors","getSchemaFile","file","fs","existsSync","CliError","loadSchemaDocument","schemaFile","loadResult","loadDocument","success","console","error","colors","red","errors","forEach","err","model","handleSubProcessError","Error","status","process","exit","isPlugin","PrismaSchemaGenerator","TsSchemaGenerator","colors","fs","path","invariant","run","options","schemaFile","getSchemaFile","schema","model","loadSchemaDocument","outputPath","output","path","dirname","tsSchemaFile","join","TsSchemaGenerator","generate","runPlugins","prismaSchema","PrismaSchemaGenerator","fs","writeFileSync","silent","console","log","colors","green","plugins","declarations","filter","isPlugin","plugin","providerField","fields","find","f","name","invariant","provider","value","useProvider","startsWith","slice","generator","default","run","command","options","schemaFile","getSchemaFile","schema","runGenerate","silent","prismaSchemaFile","path","join","dirname","runPush","cmd","acceptDataLoss","forceReset","execPackage","stdio","err","handleSubProcessError","colors","path","run","projectPath","packages","getZenStackPackages","console","error","log","versions","Set","pkg","version","add","colors","green","padEnd","size","warn","yellow","pkgJson","resolvedPath","path","resolve","join","with","type","default","err","Array","from","Object","keys","dependencies","devDependencies","filter","p","startsWith","sort","result","Promise","all","map","depPkgJson","undefined","colors","fs","path","ora","detect","resolveCommand","STARTER_ZMODEL","run","projectPath","packages","name","dev","pm","detect","agent","console","log","colors","gray","pkg","resolved","resolveCommand","CliError","spinner","ora","start","execSync","command","args","join","cwd","succeed","e","fail","generationFolder","fs","existsSync","path","mkdirSync","writeFileSync","STARTER_ZMODEL","yellow","green","path","run","command","options","schemaFile","getSchemaFile","schema","runGenerate","silent","prismaSchemaFile","path","join","dirname","runDev","runReset","runDeploy","runStatus","_options","execPackage","stdio","err","handleSubProcessError","force","Error","status","process","exit","getVersion","require","version","undefined","generateAction","options","generate","migrateAction","command","migrate","dbAction","db","infoAction","projectPath","info","initAction","init","createProgram","program","Command","version","getVersion","schemaExtensions","ZModelLanguageMetaData","fileExtensions","join","description","colors","bold","blue","showHelpAfterError","showSuggestionAfterError","schemaOption","Option","addOption","action","migrateCommand","dbCommand","argument","parse","process","argv"]}
package/package.json ADDED
@@ -0,0 +1,57 @@
1
+ {
2
+ "name": "@zenstackhq/cli",
3
+ "publisher": "zenstack",
4
+ "displayName": "ZenStack CLI",
5
+ "description": "FullStack database toolkit with built-in access control and automatic API generation.",
6
+ "version": "3.0.0-alpha.0",
7
+ "type": "module",
8
+ "author": {
9
+ "name": "ZenStack Team"
10
+ },
11
+ "homepage": "https://zenstack.dev",
12
+ "license": "MIT",
13
+ "keywords": [
14
+ "orm",
15
+ "fullstack",
16
+ "react",
17
+ "typescript",
18
+ "data modeling"
19
+ ],
20
+ "bin": {
21
+ "zenstack": "bin/cli"
22
+ },
23
+ "dependencies": {
24
+ "@types/node": "^20.0.0",
25
+ "async-exit-hook": "^2.0.1",
26
+ "colors": "1.4.0",
27
+ "commander": "^8.3.0",
28
+ "langium": "~3.3.0",
29
+ "ora": "^5.4.1",
30
+ "package-manager-detector": "^1.3.0",
31
+ "tiny-invariant": "^1.3.3",
32
+ "ts-pattern": "^4.3.0",
33
+ "@zenstackhq/language": "3.0.0-alpha.0",
34
+ "@zenstackhq/sdk": "3.0.0-alpha.0"
35
+ },
36
+ "peerDependencies": {
37
+ "prisma": "^6.0.0",
38
+ "typescript": "^5.0.0"
39
+ },
40
+ "devDependencies": {
41
+ "@types/async-exit-hook": "^2.0.0",
42
+ "@types/better-sqlite3": "^7.6.13",
43
+ "@types/semver": "^7.3.13",
44
+ "@types/tmp": "^0.2.6",
45
+ "better-sqlite3": "^11.8.1",
46
+ "tmp": "^0.2.3",
47
+ "@zenstackhq/testtools": "3.0.0-alpha.0",
48
+ "@zenstackhq/runtime": "3.0.0-alpha.0"
49
+ },
50
+ "scripts": {
51
+ "build": "tsup-node",
52
+ "watch": "tsup-node --watch",
53
+ "lint": "eslint src --ext ts",
54
+ "test": "vitest",
55
+ "pack": "pnpm pack"
56
+ }
57
+ }
@@ -0,0 +1,47 @@
1
+ import fs from 'node:fs';
2
+ import { CliError } from '../cli-error';
3
+ import { loadDocument } from '@zenstackhq/language';
4
+ import colors from 'colors';
5
+
6
+ export function getSchemaFile(file?: string) {
7
+ if (file) {
8
+ if (!fs.existsSync(file)) {
9
+ throw new CliError(`Schema file not found: ${file}`);
10
+ }
11
+ return file;
12
+ }
13
+
14
+ if (fs.existsSync('./zenstack/schema.zmodel')) {
15
+ return './zenstack/schema.zmodel';
16
+ } else if (fs.existsSync('./schema.zmodel')) {
17
+ return './schema.zmodel';
18
+ } else {
19
+ throw new CliError(
20
+ 'Schema file not found in default locations ("./zenstack/schema.zmodel" or "./schema.zmodel").'
21
+ );
22
+ }
23
+ }
24
+
25
+ export async function loadSchemaDocument(schemaFile: string) {
26
+ const loadResult = await loadDocument(schemaFile);
27
+ if (!loadResult.success) {
28
+ console.error(colors.red('Error loading schema:'));
29
+ loadResult.errors.forEach((err) => {
30
+ console.error(colors.red(err));
31
+ });
32
+ throw new CliError('Failed to load schema');
33
+ }
34
+ return loadResult.model;
35
+ }
36
+
37
+ export function handleSubProcessError(err: unknown) {
38
+ if (
39
+ err instanceof Error &&
40
+ 'status' in err &&
41
+ typeof err.status === 'number'
42
+ ) {
43
+ process.exit(err.status);
44
+ } else {
45
+ process.exit(1);
46
+ }
47
+ }
@@ -0,0 +1,46 @@
1
+ import path from 'node:path';
2
+ import { execPackage } from '../utils/exec-utils';
3
+ import { getSchemaFile, handleSubProcessError } from './action-utils';
4
+ import { run as runGenerate } from './generate';
5
+
6
+ type CommonOptions = {
7
+ schema?: string;
8
+ name?: string;
9
+ };
10
+
11
+ /**
12
+ * CLI action for db related commands
13
+ */
14
+ export async function run(command: string, options: CommonOptions) {
15
+ const schemaFile = getSchemaFile(options.schema);
16
+
17
+ // run generate first
18
+ await runGenerate({
19
+ schema: schemaFile,
20
+ silent: true,
21
+ });
22
+
23
+ const prismaSchemaFile = path.join(
24
+ path.dirname(schemaFile),
25
+ 'schema.prisma'
26
+ );
27
+
28
+ switch (command) {
29
+ case 'push':
30
+ await runPush(prismaSchemaFile, options);
31
+ break;
32
+ }
33
+ }
34
+
35
+ async function runPush(prismaSchemaFile: string, options: any) {
36
+ const cmd = `prisma db push --schema "${prismaSchemaFile}"${
37
+ options.acceptDataLoss ? ' --accept-data-loss' : ''
38
+ }${options.forceReset ? ' --force-reset' : ''} --skip-generate`;
39
+ try {
40
+ await execPackage(cmd, {
41
+ stdio: 'inherit',
42
+ });
43
+ } catch (err) {
44
+ handleSubProcessError(err);
45
+ }
46
+ }
@@ -0,0 +1,70 @@
1
+ import { isPlugin, LiteralExpr, type Model } from '@zenstackhq/language/ast';
2
+ import type { CliGenerator } from '@zenstackhq/runtime/client';
3
+ import { PrismaSchemaGenerator, TsSchemaGenerator } from '@zenstackhq/sdk';
4
+ import colors from 'colors';
5
+ import fs from 'node:fs';
6
+ import path from 'node:path';
7
+ import invariant from 'tiny-invariant';
8
+ import { getSchemaFile, loadSchemaDocument } from './action-utils';
9
+
10
+ type Options = {
11
+ schema?: string;
12
+ output?: string;
13
+ silent?: boolean;
14
+ };
15
+
16
+ /**
17
+ * CLI action for generating code from schema
18
+ */
19
+ export async function run(options: Options) {
20
+ const schemaFile = getSchemaFile(options.schema);
21
+
22
+ const model = await loadSchemaDocument(schemaFile);
23
+ const outputPath = options.output ?? path.dirname(schemaFile);
24
+
25
+ // generate TS schema
26
+ const tsSchemaFile = path.join(outputPath, 'schema.ts');
27
+ await new TsSchemaGenerator().generate(schemaFile, [], tsSchemaFile);
28
+
29
+ await runPlugins(model, outputPath, tsSchemaFile);
30
+
31
+ // generate Prisma schema
32
+ const prismaSchema = await new PrismaSchemaGenerator(model).generate();
33
+ fs.writeFileSync(path.join(outputPath, 'schema.prisma'), prismaSchema);
34
+
35
+ if (!options.silent) {
36
+ console.log(colors.green('Generation completed successfully.'));
37
+ console.log(`You can now create a ZenStack client with it.
38
+
39
+ \`\`\`
40
+ import { ZenStackClient } from '@zenstackhq/runtime';
41
+ import { schema } from '${outputPath}/schema';
42
+
43
+ const client = new ZenStackClient(schema);
44
+ \`\`\`
45
+ `);
46
+ }
47
+ }
48
+
49
+ async function runPlugins(
50
+ model: Model,
51
+ outputPath: string,
52
+ tsSchemaFile: string
53
+ ) {
54
+ const plugins = model.declarations.filter(isPlugin);
55
+ for (const plugin of plugins) {
56
+ const providerField = plugin.fields.find((f) => f.name === 'provider');
57
+ invariant(
58
+ providerField,
59
+ `Plugin ${plugin.name} does not have a provider field`
60
+ );
61
+ const provider = (providerField.value as LiteralExpr).value as string;
62
+ let useProvider = provider;
63
+ if (useProvider.startsWith('@core/')) {
64
+ useProvider = `@zenstackhq/runtime/plugins/${useProvider.slice(6)}`;
65
+ }
66
+ const generator = (await import(useProvider)).default as CliGenerator;
67
+ console.log('Running generator:', provider);
68
+ await generator({ model, outputPath, tsSchemaFile });
69
+ }
70
+ }
@@ -0,0 +1,7 @@
1
+ import { run as db } from './db';
2
+ import { run as generate } from './generate';
3
+ import { run as info } from './info';
4
+ import { run as init } from './init';
5
+ import { run as migrate } from './migrate';
6
+
7
+ export { db, generate, info, init, migrate };
@@ -0,0 +1,77 @@
1
+ import colors from 'colors';
2
+ import path from 'node:path';
3
+
4
+ /**
5
+ * CLI action for getting information about installed ZenStack packages
6
+ */
7
+ export async function run(projectPath: string) {
8
+ const packages = await getZenStackPackages(projectPath);
9
+ if (!packages) {
10
+ console.error(
11
+ 'Unable to locate package.json. Are you in a valid project directory?'
12
+ );
13
+ return;
14
+ }
15
+
16
+ console.log('Installed ZenStack Packages:');
17
+ const versions = new Set<string>();
18
+ for (const { pkg, version } of packages) {
19
+ if (version) {
20
+ versions.add(version);
21
+ }
22
+ console.log(` ${colors.green(pkg.padEnd(20))}\t${version}`);
23
+ }
24
+
25
+ if (versions.size > 1) {
26
+ console.warn(
27
+ colors.yellow(
28
+ 'WARNING: Multiple versions of Zenstack packages detected. This may cause issues.'
29
+ )
30
+ );
31
+ }
32
+ }
33
+
34
+ async function getZenStackPackages(
35
+ projectPath: string
36
+ ): Promise<Array<{ pkg: string; version: string | undefined }>> {
37
+ let pkgJson: {
38
+ dependencies: Record<string, unknown>;
39
+ devDependencies: Record<string, unknown>;
40
+ };
41
+ const resolvedPath = path.resolve(projectPath);
42
+ try {
43
+ pkgJson = (
44
+ await import(path.join(resolvedPath, 'package.json'), {
45
+ with: { type: 'json' },
46
+ })
47
+ ).default;
48
+ } catch (err) {
49
+ return [];
50
+ }
51
+
52
+ const packages = Array.from(
53
+ new Set(
54
+ [
55
+ ...Object.keys(pkgJson.dependencies ?? {}),
56
+ ...Object.keys(pkgJson.devDependencies ?? {}),
57
+ ].filter((p) => p.startsWith('@zenstackhq/') || p === 'zenstack')
58
+ )
59
+ ).sort();
60
+
61
+ const result = await Promise.all(
62
+ packages.map(async (pkg) => {
63
+ try {
64
+ const depPkgJson = (
65
+ await import(`${pkg}/package.json`, {
66
+ with: { type: 'json' },
67
+ })
68
+ ).default;
69
+ return { pkg, version: depPkgJson.version as string };
70
+ } catch {
71
+ return { pkg, version: undefined };
72
+ }
73
+ })
74
+ );
75
+
76
+ return result;
77
+ }
@@ -0,0 +1,82 @@
1
+ import colors from 'colors';
2
+ import fs from 'node:fs';
3
+ import path from 'node:path';
4
+ import ora from 'ora';
5
+ import { detect, resolveCommand } from 'package-manager-detector';
6
+ import { CliError } from '../cli-error';
7
+ import { execSync } from '../utils/exec-utils';
8
+ import { STARTER_ZMODEL } from './templates';
9
+
10
+ /**
11
+ * CLI action for getting information about installed ZenStack packages
12
+ */
13
+ export async function run(projectPath: string) {
14
+ const packages = [
15
+ { name: '@zenstackhq/cli', dev: true },
16
+ { name: '@zenstackhq/runtime', dev: false },
17
+ ];
18
+ let pm = await detect();
19
+ if (!pm) {
20
+ pm = { agent: 'npm', name: 'npm' };
21
+ }
22
+
23
+ console.log(colors.gray(`Using package manager: ${pm.agent}`));
24
+
25
+ for (const pkg of packages) {
26
+ const resolved = resolveCommand(pm.agent, 'install', [
27
+ pkg.name,
28
+ ...(pkg.dev ? [pm.agent === 'yarn' ? '--dev' : '--save-dev'] : []),
29
+ ]);
30
+ if (!resolved) {
31
+ throw new CliError(
32
+ `Unable to determine how to install package "${pkg.name}". Please install it manually.`
33
+ );
34
+ }
35
+
36
+ const spinner = ora(`Installing "${pkg.name}"`).start();
37
+ try {
38
+ execSync(`${resolved.command} ${resolved.args.join(' ')}`, {
39
+ cwd: projectPath,
40
+ });
41
+ spinner.succeed();
42
+ } catch (e) {
43
+ spinner.fail();
44
+ throw e;
45
+ }
46
+ }
47
+
48
+ const generationFolder = 'zenstack';
49
+
50
+ if (!fs.existsSync(path.join(projectPath, generationFolder))) {
51
+ fs.mkdirSync(path.join(projectPath, generationFolder));
52
+ }
53
+
54
+ if (
55
+ !fs.existsSync(
56
+ path.join(projectPath, generationFolder, 'schema.zmodel')
57
+ )
58
+ ) {
59
+ fs.writeFileSync(
60
+ path.join(projectPath, generationFolder, 'schema.zmodel'),
61
+ STARTER_ZMODEL
62
+ );
63
+ } else {
64
+ console.log(
65
+ colors.yellow(
66
+ 'Schema file already exists. Skipping generation of sample.'
67
+ )
68
+ );
69
+ }
70
+
71
+ console.log(colors.green('ZenStack project initialized successfully!'));
72
+ console.log(
73
+ colors.gray(
74
+ `See "${generationFolder}/schema.zmodel" for your database schema.`
75
+ )
76
+ );
77
+ console.log(
78
+ colors.gray(
79
+ 'Run `zenstack generate` to compile the the schema into a TypeScript file.'
80
+ )
81
+ );
82
+ }
@@ -0,0 +1,111 @@
1
+ import path from 'node:path';
2
+ import { execPackage } from '../utils/exec-utils';
3
+ import { getSchemaFile } from './action-utils';
4
+ import { run as runGenerate } from './generate';
5
+
6
+ type CommonOptions = {
7
+ schema?: string;
8
+ name?: string;
9
+ };
10
+
11
+ /**
12
+ * CLI action for migration-related commands
13
+ */
14
+ export async function run(command: string, options: CommonOptions) {
15
+ const schemaFile = getSchemaFile(options.schema);
16
+
17
+ // run generate first
18
+ await runGenerate({
19
+ schema: schemaFile,
20
+ silent: true,
21
+ });
22
+
23
+ const prismaSchemaFile = path.join(
24
+ path.dirname(schemaFile),
25
+ 'schema.prisma'
26
+ );
27
+
28
+ switch (command) {
29
+ case 'dev':
30
+ await runDev(prismaSchemaFile, options);
31
+ break;
32
+
33
+ case 'reset':
34
+ await runReset(prismaSchemaFile, options as any);
35
+ break;
36
+
37
+ case 'deploy':
38
+ await runDeploy(prismaSchemaFile, options);
39
+ break;
40
+
41
+ case 'status':
42
+ await runStatus(prismaSchemaFile, options);
43
+ break;
44
+ }
45
+ }
46
+
47
+ async function runDev(prismaSchemaFile: string, _options: unknown) {
48
+ try {
49
+ await execPackage(
50
+ `prisma migrate dev --schema "${prismaSchemaFile}" --skip-generate`,
51
+ {
52
+ stdio: 'inherit',
53
+ }
54
+ );
55
+ } catch (err) {
56
+ handleSubProcessError(err);
57
+ }
58
+ }
59
+
60
+ async function runReset(prismaSchemaFile: string, options: { force: boolean }) {
61
+ try {
62
+ await execPackage(
63
+ `prisma migrate reset --schema "${prismaSchemaFile}"${
64
+ options.force ? ' --force' : ''
65
+ }`,
66
+ {
67
+ stdio: 'inherit',
68
+ }
69
+ );
70
+ } catch (err) {
71
+ handleSubProcessError(err);
72
+ }
73
+ }
74
+
75
+ async function runDeploy(prismaSchemaFile: string, _options: unknown) {
76
+ try {
77
+ await execPackage(
78
+ `prisma migrate deploy --schema "${prismaSchemaFile}"`,
79
+ {
80
+ stdio: 'inherit',
81
+ }
82
+ );
83
+ } catch (err) {
84
+ handleSubProcessError(err);
85
+ }
86
+ }
87
+
88
+ async function runStatus(prismaSchemaFile: string, _options: unknown) {
89
+ try {
90
+ await execPackage(
91
+ `prisma migrate status --schema "${prismaSchemaFile}"`,
92
+ {
93
+ stdio: 'inherit',
94
+ }
95
+ );
96
+ } catch (err) {
97
+ handleSubProcessError(err);
98
+ }
99
+ }
100
+
101
+ function handleSubProcessError(err: unknown) {
102
+ if (
103
+ err instanceof Error &&
104
+ 'status' in err &&
105
+ typeof err.status === 'number'
106
+ ) {
107
+ process.exit(err.status);
108
+ } else {
109
+ process.exit(1);
110
+ }
111
+ }
@@ -0,0 +1,52 @@
1
+ export const STARTER_ZMODEL = `// This is a sample model to get you started.
2
+
3
+ /// A sample data source using local sqlite db.
4
+ datasource db {
5
+ provider = 'sqlite'
6
+ url = 'file:./dev.db'
7
+ }
8
+
9
+ /// User model
10
+ model User {
11
+ id String @id @default(cuid())
12
+ email String @unique @email @length(6, 32)
13
+ posts Post[]
14
+ }
15
+
16
+ /// Post model
17
+ model Post {
18
+ id String @id @default(cuid())
19
+ createdAt DateTime @default(now())
20
+ updatedAt DateTime @updatedAt
21
+ title String @length(1, 256)
22
+ content String
23
+ published Boolean @default(false)
24
+ author User @relation(fields: [authorId], references: [id])
25
+ authorId String
26
+ }
27
+ `;
28
+
29
+ export const STARTER_MAIN_TS = `import { ZenStackClient } from '@zenstackhq/runtime';
30
+ import { schema } from './zenstack/schema';
31
+
32
+ async function main() {
33
+ const client = new ZenStackClient(schema);
34
+ const user = await client.user.create({
35
+ data: {
36
+ email: 'test@zenstack.dev',
37
+ posts: {
38
+ create: [
39
+ {
40
+ title: 'Hello World',
41
+ content: 'This is a test post',
42
+ },
43
+ ],
44
+ },
45
+ },
46
+ include: { posts: true }
47
+ });
48
+ console.log('User created:', user);
49
+ }
50
+
51
+ main();
52
+ `;
@@ -0,0 +1,4 @@
1
+ /**
2
+ * Indicating an error during CLI execution
3
+ */
4
+ export class CliError extends Error {}