@prisma-next/cli 0.10.0-dev.21 → 0.10.0-dev.23
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/dist/cli.mjs +4 -4
- package/dist/{client-Brv4qlfB.mjs → client-BbsiOKZN.mjs} +3 -2
- package/dist/client-BbsiOKZN.mjs.map +1 -0
- package/dist/commands/contract-infer.mjs +1 -1
- package/dist/commands/db-init.d.mts.map +1 -1
- package/dist/commands/db-init.mjs +13 -10
- package/dist/commands/db-init.mjs.map +1 -1
- package/dist/commands/db-schema.mjs +1 -1
- package/dist/commands/db-sign.mjs +1 -1
- package/dist/commands/db-update.d.mts.map +1 -1
- package/dist/commands/db-update.mjs +10 -7
- package/dist/commands/db-update.mjs.map +1 -1
- package/dist/commands/db-verify.mjs +1 -1
- package/dist/commands/migrate.mjs +1 -1
- package/dist/commands/migration-log.d.mts.map +1 -1
- package/dist/commands/migration-log.mjs +3 -2
- package/dist/commands/migration-log.mjs.map +1 -1
- package/dist/commands/migration-show.mjs +1 -1
- package/dist/commands/migration-status.d.mts.map +1 -1
- package/dist/commands/migration-status.mjs +29 -3
- package/dist/commands/migration-status.mjs.map +1 -1
- package/dist/{contract-infer-Dm8pBZMR.mjs → contract-infer-DsgNf-d7.mjs} +2 -2
- package/dist/{contract-infer-Dm8pBZMR.mjs.map → contract-infer-DsgNf-d7.mjs.map} +1 -1
- package/dist/{db-verify-CW8DR5Ei.mjs → db-verify-BfsmBkd4.mjs} +3 -3
- package/dist/db-verify-BfsmBkd4.mjs.map +1 -0
- package/dist/exports/control-api.mjs +1 -1
- package/dist/{init-n7JaaCgD.mjs → init-CHaTgMQg.mjs} +65 -1
- package/dist/init-CHaTgMQg.mjs.map +1 -0
- package/dist/{inspect-live-schema-iETRZ_59.mjs → inspect-live-schema-DyQVuLw6.mjs} +2 -2
- package/dist/{inspect-live-schema-iETRZ_59.mjs.map → inspect-live-schema-DyQVuLw6.mjs.map} +1 -1
- package/dist/{migration-command-scaffold-BlgVj_Pn.mjs → migration-command-scaffold--ZwMgOD_.mjs} +2 -2
- package/dist/{migration-command-scaffold-BlgVj_Pn.mjs.map → migration-command-scaffold--ZwMgOD_.mjs.map} +1 -1
- package/dist/readme-mongo.md +35 -0
- package/dist/readme-postgres.md +34 -0
- package/package.json +18 -18
- package/src/commands/db-init.ts +9 -1
- package/src/commands/db-update.ts +9 -1
- package/src/commands/db-verify.ts +1 -1
- package/src/commands/init/detect-package-manager.ts +15 -0
- package/src/commands/init/init.ts +21 -0
- package/src/commands/init/templates/readme-mongo.md +35 -0
- package/src/commands/init/templates/readme-postgres.md +34 -0
- package/src/commands/init/templates/readme.ts +62 -0
- package/src/commands/migration-log.ts +2 -1
- package/src/commands/migration-status.ts +45 -2
- package/src/control-api/operations/apply-aggregate.ts +1 -0
- package/dist/client-Brv4qlfB.mjs.map +0 -1
- package/dist/db-verify-CW8DR5Ei.mjs.map +0 -1
- package/dist/init-n7JaaCgD.mjs.map +0 -1
|
@@ -2,7 +2,7 @@ import { t as loadConfig } from "./config-loader-B6sJjXTv.mjs";
|
|
|
2
2
|
import { _ as errorUnexpected, c as errorDriverRequired, o as errorDatabaseConnectionRequired, t as CliStructuredError } from "./cli-errors-CF60g2cG.mjs";
|
|
3
3
|
import { b as formatStyledHeader, o as maskConnectionUrl, u as sanitizeErrorMessage } from "./command-helpers-Dvgul7UA.mjs";
|
|
4
4
|
import { t as createProgressAdapter } from "./progress-adapter-DFfvZcYL.mjs";
|
|
5
|
-
import { t as createControlClient } from "./client-
|
|
5
|
+
import { t as createControlClient } from "./client-BbsiOKZN.mjs";
|
|
6
6
|
import { notOk, ok } from "@prisma-next/utils/result";
|
|
7
7
|
import { relative, resolve } from "pathe";
|
|
8
8
|
//#region src/commands/inspect-live-schema.ts
|
|
@@ -87,4 +87,4 @@ async function inspectLiveSchema(options, flags, ui, startTime, context) {
|
|
|
87
87
|
//#endregion
|
|
88
88
|
export { inspectLiveSchema as t };
|
|
89
89
|
|
|
90
|
-
//# sourceMappingURL=inspect-live-schema-
|
|
90
|
+
//# sourceMappingURL=inspect-live-schema-DyQVuLw6.mjs.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"inspect-live-schema-
|
|
1
|
+
{"version":3,"file":"inspect-live-schema-DyQVuLw6.mjs","names":[],"sources":["../src/commands/inspect-live-schema.ts"],"sourcesContent":["import type { CoreSchemaView } from '@prisma-next/framework-components/control';\nimport type { PslDocumentAst } from '@prisma-next/framework-components/psl-ast';\nimport { notOk, ok, type Result } from '@prisma-next/utils/result';\nimport { relative, resolve } from 'pathe';\nimport { loadConfig } from '../config-loader';\nimport { createControlClient } from '../control-api/client';\nimport {\n CliStructuredError,\n errorDatabaseConnectionRequired,\n errorDriverRequired,\n errorUnexpected,\n} from '../utils/cli-errors';\nimport { maskConnectionUrl, sanitizeErrorMessage } from '../utils/command-helpers';\nimport { formatStyledHeader } from '../utils/formatters/styled';\nimport type { CommonCommandOptions, GlobalFlags } from '../utils/global-flags';\nimport { createProgressAdapter } from '../utils/progress-adapter';\nimport type { TerminalUI } from '../utils/terminal-ui';\n\nexport interface InspectLiveSchemaOptions extends CommonCommandOptions {\n readonly db?: string;\n readonly config?: string;\n}\n\ninterface InspectLiveSchemaContext {\n readonly commandName: string;\n readonly description: string;\n readonly url: string;\n}\n\ntype LoadedCliConfig = Awaited<ReturnType<typeof loadConfig>>;\n\nexport interface InspectLiveSchemaResult {\n readonly config: LoadedCliConfig;\n readonly schema: unknown;\n readonly schemaView: CoreSchemaView | undefined;\n /**\n * PSL AST inferred from the introspected schema, when the configured family\n * implements `PslContractInferCapable`. `undefined` for families that do not\n * support inference (e.g. Mongo today).\n */\n readonly pslContractAst: PslDocumentAst | undefined;\n readonly target: {\n readonly familyId: string;\n readonly id: string;\n };\n readonly meta: {\n readonly configPath?: string;\n readonly dbUrl?: string;\n };\n readonly timings: {\n readonly total: number;\n };\n}\n\nexport async function inspectLiveSchema(\n options: InspectLiveSchemaOptions,\n flags: GlobalFlags,\n ui: TerminalUI,\n startTime: number,\n context: InspectLiveSchemaContext,\n): Promise<Result<InspectLiveSchemaResult, CliStructuredError>> {\n let config: LoadedCliConfig;\n try {\n config = await loadConfig(options.config);\n } catch (error) {\n if (CliStructuredError.is(error)) {\n return notOk(error);\n }\n\n return notOk(\n errorUnexpected(error instanceof Error ? error.message : String(error), {\n why: 'Failed to load config',\n }),\n );\n }\n\n const configPath = options.config\n ? relative(process.cwd(), resolve(options.config))\n : 'prisma-next.config.ts';\n\n if (!flags.json && !flags.quiet) {\n const details: Array<{ label: string; value: string }> = [\n { label: 'config', value: configPath },\n ];\n\n if (options.db) {\n details.push({ label: 'database', value: maskConnectionUrl(options.db) });\n } else if (config.db?.connection && typeof config.db.connection === 'string') {\n details.push({ label: 'database', value: maskConnectionUrl(config.db.connection) });\n }\n\n ui.stderr(\n formatStyledHeader({\n command: context.commandName,\n description: context.description,\n url: context.url,\n details,\n flags,\n }),\n );\n }\n\n const dbConnection = options.db ?? config.db?.connection;\n if (!dbConnection) {\n return notOk(\n errorDatabaseConnectionRequired({\n why: `Database connection is required for ${context.commandName} (set db.connection in ${configPath}, or pass --db <url>)`,\n commandName: context.commandName,\n }),\n );\n }\n\n if (!config.driver) {\n return notOk(\n errorDriverRequired({\n why: `Config.driver is required for ${context.commandName}`,\n }),\n );\n }\n\n const client = createControlClient({\n family: config.family,\n target: config.target,\n adapter: config.adapter,\n driver: config.driver,\n extensionPacks: config.extensionPacks ?? [],\n });\n const onProgress = createProgressAdapter({ ui, flags });\n\n try {\n const schema = await client.introspect({\n connection: dbConnection,\n onProgress,\n });\n const schemaView = client.toSchemaView(schema);\n const pslContractAst = client.inferPslContract(schema);\n\n const dbUrl = typeof dbConnection === 'string' ? maskConnectionUrl(dbConnection) : undefined;\n\n return ok({\n config,\n schema,\n schemaView,\n pslContractAst,\n target: {\n familyId: config.family.familyId,\n id: config.target.targetId,\n },\n meta: {\n configPath,\n ...(dbUrl ? { dbUrl } : {}),\n },\n timings: {\n total: Date.now() - startTime,\n },\n });\n } catch (error) {\n if (CliStructuredError.is(error)) {\n return notOk(error);\n }\n\n const rawMessage = error instanceof Error ? error.message : String(error);\n const safeMessage = sanitizeErrorMessage(\n rawMessage,\n typeof dbConnection === 'string' ? dbConnection : undefined,\n );\n return notOk(\n errorUnexpected(safeMessage, {\n why: `Unexpected error during ${context.commandName}: ${safeMessage}`,\n }),\n );\n } finally {\n await client.close();\n }\n}\n"],"mappings":";;;;;;;;AAsDA,eAAsB,kBACpB,SACA,OACA,IACA,WACA,SAC8D;CAC9D,IAAI;CACJ,IAAI;EACF,SAAS,MAAM,WAAW,QAAQ,OAAO;UAClC,OAAO;EACd,IAAI,mBAAmB,GAAG,MAAM,EAC9B,OAAO,MAAM,MAAM;EAGrB,OAAO,MACL,gBAAgB,iBAAiB,QAAQ,MAAM,UAAU,OAAO,MAAM,EAAE,EACtE,KAAK,yBACN,CAAC,CACH;;CAGH,MAAM,aAAa,QAAQ,SACvB,SAAS,QAAQ,KAAK,EAAE,QAAQ,QAAQ,OAAO,CAAC,GAChD;CAEJ,IAAI,CAAC,MAAM,QAAQ,CAAC,MAAM,OAAO;EAC/B,MAAM,UAAmD,CACvD;GAAE,OAAO;GAAU,OAAO;GAAY,CACvC;EAED,IAAI,QAAQ,IACV,QAAQ,KAAK;GAAE,OAAO;GAAY,OAAO,kBAAkB,QAAQ,GAAG;GAAE,CAAC;OACpE,IAAI,OAAO,IAAI,cAAc,OAAO,OAAO,GAAG,eAAe,UAClE,QAAQ,KAAK;GAAE,OAAO;GAAY,OAAO,kBAAkB,OAAO,GAAG,WAAW;GAAE,CAAC;EAGrF,GAAG,OACD,mBAAmB;GACjB,SAAS,QAAQ;GACjB,aAAa,QAAQ;GACrB,KAAK,QAAQ;GACb;GACA;GACD,CAAC,CACH;;CAGH,MAAM,eAAe,QAAQ,MAAM,OAAO,IAAI;CAC9C,IAAI,CAAC,cACH,OAAO,MACL,gCAAgC;EAC9B,KAAK,uCAAuC,QAAQ,YAAY,yBAAyB,WAAW;EACpG,aAAa,QAAQ;EACtB,CAAC,CACH;CAGH,IAAI,CAAC,OAAO,QACV,OAAO,MACL,oBAAoB,EAClB,KAAK,iCAAiC,QAAQ,eAC/C,CAAC,CACH;CAGH,MAAM,SAAS,oBAAoB;EACjC,QAAQ,OAAO;EACf,QAAQ,OAAO;EACf,SAAS,OAAO;EAChB,QAAQ,OAAO;EACf,gBAAgB,OAAO,kBAAkB,EAAE;EAC5C,CAAC;CACF,MAAM,aAAa,sBAAsB;EAAE;EAAI;EAAO,CAAC;CAEvD,IAAI;EACF,MAAM,SAAS,MAAM,OAAO,WAAW;GACrC,YAAY;GACZ;GACD,CAAC;EACF,MAAM,aAAa,OAAO,aAAa,OAAO;EAC9C,MAAM,iBAAiB,OAAO,iBAAiB,OAAO;EAEtD,MAAM,QAAQ,OAAO,iBAAiB,WAAW,kBAAkB,aAAa,GAAG,KAAA;EAEnF,OAAO,GAAG;GACR;GACA;GACA;GACA;GACA,QAAQ;IACN,UAAU,OAAO,OAAO;IACxB,IAAI,OAAO,OAAO;IACnB;GACD,MAAM;IACJ;IACA,GAAI,QAAQ,EAAE,OAAO,GAAG,EAAE;IAC3B;GACD,SAAS,EACP,OAAO,KAAK,KAAK,GAAG,WACrB;GACF,CAAC;UACK,OAAO;EACd,IAAI,mBAAmB,GAAG,MAAM,EAC9B,OAAO,MAAM,MAAM;EAIrB,MAAM,cAAc,qBADD,iBAAiB,QAAQ,MAAM,UAAU,OAAO,MAAM,EAGvE,OAAO,iBAAiB,WAAW,eAAe,KAAA,EACnD;EACD,OAAO,MACL,gBAAgB,aAAa,EAC3B,KAAK,2BAA2B,QAAQ,YAAY,IAAI,eACzD,CAAC,CACH;WACO;EACR,MAAM,OAAO,OAAO"}
|
package/dist/{migration-command-scaffold-BlgVj_Pn.mjs → migration-command-scaffold--ZwMgOD_.mjs}
RENAMED
|
@@ -2,7 +2,7 @@ import { t as loadConfig } from "./config-loader-B6sJjXTv.mjs";
|
|
|
2
2
|
import { _ as errorUnexpected, a as errorContractValidationFailed, c as errorDriverRequired, h as errorTargetMigrationNotSupported, l as errorFileNotFound, o as errorDatabaseConnectionRequired } from "./cli-errors-CF60g2cG.mjs";
|
|
3
3
|
import { b as formatStyledHeader, c as resolveContractPath, o as maskConnectionUrl, t as addGlobalOptions } from "./command-helpers-Dvgul7UA.mjs";
|
|
4
4
|
import { t as createProgressAdapter } from "./progress-adapter-DFfvZcYL.mjs";
|
|
5
|
-
import { t as createControlClient } from "./client-
|
|
5
|
+
import { t as createControlClient } from "./client-BbsiOKZN.mjs";
|
|
6
6
|
import { notOk, ok } from "@prisma-next/utils/result";
|
|
7
7
|
import { readFile } from "node:fs/promises";
|
|
8
8
|
import { hasMigrations } from "@prisma-next/framework-components/control";
|
|
@@ -101,4 +101,4 @@ function addMigrationCommandOptions(command) {
|
|
|
101
101
|
//#endregion
|
|
102
102
|
export { prepareMigrationContext as n, addMigrationCommandOptions as t };
|
|
103
103
|
|
|
104
|
-
//# sourceMappingURL=migration-command-scaffold
|
|
104
|
+
//# sourceMappingURL=migration-command-scaffold--ZwMgOD_.mjs.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"migration-command-scaffold
|
|
1
|
+
{"version":3,"file":"migration-command-scaffold--ZwMgOD_.mjs","names":[],"sources":["../src/utils/migration-command-scaffold.ts"],"sourcesContent":["import { readFile } from 'node:fs/promises';\nimport { relative, resolve } from 'node:path';\nimport { hasMigrations } from '@prisma-next/framework-components/control';\nimport { notOk, ok, type Result } from '@prisma-next/utils/result';\nimport type { Command } from 'commander';\nimport { loadConfig } from '../config-loader';\nimport { createControlClient } from '../control-api/client';\nimport type { ControlClient } from '../control-api/types';\nimport {\n type CliStructuredError,\n errorContractValidationFailed,\n errorDatabaseConnectionRequired,\n errorDriverRequired,\n errorFileNotFound,\n errorTargetMigrationNotSupported,\n errorUnexpected,\n} from './cli-errors';\nimport { addGlobalOptions, maskConnectionUrl, resolveContractPath } from './command-helpers';\nimport { formatStyledHeader } from './formatters/styled';\nimport type { GlobalFlags } from './global-flags';\nimport { createProgressAdapter } from './progress-adapter';\nimport type { TerminalUI } from './terminal-ui';\n\n/**\n * Resolved context for a migration command.\n * Contains everything needed to invoke a control-api operation.\n */\nexport interface MigrationContext {\n readonly client: ControlClient;\n readonly contractJson: Record<string, unknown>;\n readonly dbConnection: unknown;\n readonly onProgress: ReturnType<typeof createProgressAdapter>;\n readonly configPath: string;\n readonly contractPath: string;\n readonly contractPathAbsolute: string;\n readonly config: Awaited<ReturnType<typeof loadConfig>>;\n}\n\n/**\n * Command-specific configuration for the shared scaffold.\n */\nexport interface MigrationCommandDescriptor {\n readonly commandName: string;\n readonly description: string;\n readonly url: string;\n}\n\n/**\n * Prepares the shared context for migration commands (db init, db update).\n *\n * Handles: config loading, contract file reading, JSON parsing, connection resolution,\n * driver/migration-support validation, client creation, and header output.\n *\n * Returns a Result with either the resolved context or a structured error.\n */\nexport async function prepareMigrationContext(\n options: { readonly db?: string; readonly config?: string; readonly dryRun?: boolean },\n flags: GlobalFlags,\n ui: TerminalUI,\n descriptor: MigrationCommandDescriptor,\n): Promise<Result<MigrationContext, CliStructuredError>> {\n // Load config\n const config = await loadConfig(options.config);\n const configPath = options.config\n ? relative(process.cwd(), resolve(options.config))\n : 'prisma-next.config.ts';\n const contractPathAbsolute = resolveContractPath(config);\n const contractPath = relative(process.cwd(), contractPathAbsolute);\n\n // Output header to stderr (decoration)\n if (!flags.json && !flags.quiet) {\n const details: Array<{ label: string; value: string }> = [\n { label: 'config', value: configPath },\n { label: 'contract', value: contractPath },\n ];\n if (options.db) {\n details.push({ label: 'database', value: maskConnectionUrl(options.db) });\n }\n if (options.dryRun) {\n details.push({ label: 'mode', value: 'dry run' });\n }\n const header = formatStyledHeader({\n command: descriptor.commandName,\n description: descriptor.description,\n url: descriptor.url,\n details,\n flags,\n });\n ui.stderr(header);\n }\n\n // Load contract file\n let contractJsonContent: string;\n try {\n contractJsonContent = await readFile(contractPathAbsolute, 'utf-8');\n } catch (error) {\n if (error instanceof Error && (error as { code?: string }).code === 'ENOENT') {\n return notOk(\n errorFileNotFound(contractPathAbsolute, {\n why: `Contract file not found at ${contractPathAbsolute}`,\n fix: `Run \\`prisma-next contract emit\\` to generate ${contractPath}, or update \\`config.contract.output\\` in ${configPath}`,\n }),\n );\n }\n return notOk(\n errorUnexpected(error instanceof Error ? error.message : String(error), {\n why: `Failed to read contract file: ${error instanceof Error ? error.message : String(error)}`,\n }),\n );\n }\n\n // Parse contract JSON\n let contractJson: Record<string, unknown>;\n try {\n contractJson = JSON.parse(contractJsonContent) as Record<string, unknown>;\n } catch (error) {\n return notOk(\n errorContractValidationFailed(\n `Contract JSON is invalid: ${error instanceof Error ? error.message : String(error)}`,\n { where: { path: contractPathAbsolute } },\n ),\n );\n }\n\n // Resolve database connection (--db flag or config.db.connection)\n const dbConnection = options.db ?? config.db?.connection;\n if (!dbConnection) {\n return notOk(\n errorDatabaseConnectionRequired({\n why: `Database connection is required for ${descriptor.commandName} (set db.connection in ${configPath}, or pass --db <url>)`,\n commandName: descriptor.commandName,\n }),\n );\n }\n\n // Check for driver\n if (!config.driver) {\n return notOk(\n errorDriverRequired({ why: `Config.driver is required for ${descriptor.commandName}` }),\n );\n }\n\n if (!hasMigrations(config.target)) {\n return notOk(\n errorTargetMigrationNotSupported({\n why: `Target \"${config.target.id}\" does not support migrations`,\n }),\n );\n }\n\n // Create control client\n const client = createControlClient({\n family: config.family,\n target: config.target,\n adapter: config.adapter,\n driver: config.driver,\n extensionPacks: config.extensionPacks ?? [],\n });\n\n // Create progress adapter\n const onProgress = createProgressAdapter({ ui, flags });\n\n return ok({\n client,\n contractJson,\n dbConnection,\n onProgress,\n configPath,\n contractPath,\n contractPathAbsolute,\n config,\n });\n}\n\n/**\n * Registers the shared CLI options for migration commands (db init, db update).\n */\nexport function addMigrationCommandOptions(command: Command): Command {\n addGlobalOptions(command);\n return command\n .option('--db <url>', 'Database connection string')\n .option('--config <path>', 'Path to prisma-next.config.ts')\n .option('--dry-run', 'Preview planned operations without applying', false);\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;AAuDA,eAAsB,wBACpB,SACA,OACA,IACA,YACuD;CAEvD,MAAM,SAAS,MAAM,WAAW,QAAQ,OAAO;CAC/C,MAAM,aAAa,QAAQ,SACvB,SAAS,QAAQ,KAAK,EAAE,QAAQ,QAAQ,OAAO,CAAC,GAChD;CACJ,MAAM,uBAAuB,oBAAoB,OAAO;CACxD,MAAM,eAAe,SAAS,QAAQ,KAAK,EAAE,qBAAqB;CAGlE,IAAI,CAAC,MAAM,QAAQ,CAAC,MAAM,OAAO;EAC/B,MAAM,UAAmD,CACvD;GAAE,OAAO;GAAU,OAAO;GAAY,EACtC;GAAE,OAAO;GAAY,OAAO;GAAc,CAC3C;EACD,IAAI,QAAQ,IACV,QAAQ,KAAK;GAAE,OAAO;GAAY,OAAO,kBAAkB,QAAQ,GAAG;GAAE,CAAC;EAE3E,IAAI,QAAQ,QACV,QAAQ,KAAK;GAAE,OAAO;GAAQ,OAAO;GAAW,CAAC;EAEnD,MAAM,SAAS,mBAAmB;GAChC,SAAS,WAAW;GACpB,aAAa,WAAW;GACxB,KAAK,WAAW;GAChB;GACA;GACD,CAAC;EACF,GAAG,OAAO,OAAO;;CAInB,IAAI;CACJ,IAAI;EACF,sBAAsB,MAAM,SAAS,sBAAsB,QAAQ;UAC5D,OAAO;EACd,IAAI,iBAAiB,SAAU,MAA4B,SAAS,UAClE,OAAO,MACL,kBAAkB,sBAAsB;GACtC,KAAK,8BAA8B;GACnC,KAAK,iDAAiD,aAAa,4CAA4C;GAChH,CAAC,CACH;EAEH,OAAO,MACL,gBAAgB,iBAAiB,QAAQ,MAAM,UAAU,OAAO,MAAM,EAAE,EACtE,KAAK,iCAAiC,iBAAiB,QAAQ,MAAM,UAAU,OAAO,MAAM,IAC7F,CAAC,CACH;;CAIH,IAAI;CACJ,IAAI;EACF,eAAe,KAAK,MAAM,oBAAoB;UACvC,OAAO;EACd,OAAO,MACL,8BACE,6BAA6B,iBAAiB,QAAQ,MAAM,UAAU,OAAO,MAAM,IACnF,EAAE,OAAO,EAAE,MAAM,sBAAsB,EAAE,CAC1C,CACF;;CAIH,MAAM,eAAe,QAAQ,MAAM,OAAO,IAAI;CAC9C,IAAI,CAAC,cACH,OAAO,MACL,gCAAgC;EAC9B,KAAK,uCAAuC,WAAW,YAAY,yBAAyB,WAAW;EACvG,aAAa,WAAW;EACzB,CAAC,CACH;CAIH,IAAI,CAAC,OAAO,QACV,OAAO,MACL,oBAAoB,EAAE,KAAK,iCAAiC,WAAW,eAAe,CAAC,CACxF;CAGH,IAAI,CAAC,cAAc,OAAO,OAAO,EAC/B,OAAO,MACL,iCAAiC,EAC/B,KAAK,WAAW,OAAO,OAAO,GAAG,gCAClC,CAAC,CACH;CAIH,MAAM,SAAS,oBAAoB;EACjC,QAAQ,OAAO;EACf,QAAQ,OAAO;EACf,SAAS,OAAO;EAChB,QAAQ,OAAO;EACf,gBAAgB,OAAO,kBAAkB,EAAE;EAC5C,CAAC;CAGF,MAAM,aAAa,sBAAsB;EAAE;EAAI;EAAO,CAAC;CAEvD,OAAO,GAAG;EACR;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACD,CAAC;;;;;AAMJ,SAAgB,2BAA2B,SAA2B;CACpE,iBAAiB,QAAQ;CACzB,OAAO,QACJ,OAAO,cAAc,6BAA6B,CAClD,OAAO,mBAAmB,gCAAgC,CAC1D,OAAO,aAAa,+CAA+C,MAAM"}
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
# {{projectName}}
|
|
2
|
+
|
|
3
|
+
Generated by `prisma-next init` with the Minimal template.
|
|
4
|
+
|
|
5
|
+
## First run
|
|
6
|
+
|
|
7
|
+
1. `{{runDbUp}}` — start the local MongoDB replica set with `mongodb-memory-server`. Data persists in `.mongo-data/` across restarts.
|
|
8
|
+
2. `{{runDev}}` — runs `src/index.ts`, which writes one row and reads it back.
|
|
9
|
+
|
|
10
|
+
## Available scripts
|
|
11
|
+
|
|
12
|
+
- `{{runDev}}` — run the Prisma Next sample script
|
|
13
|
+
|
|
14
|
+
### Database and migrations
|
|
15
|
+
|
|
16
|
+
- `{{runContractEmit}}` — emit contract artifacts after contract changes
|
|
17
|
+
- `{{runDbUp}}` — start the local MongoDB replica set with `mongodb-memory-server`. Data persists in `.mongo-data/` across restarts.
|
|
18
|
+
- `{{runDbDown}}` — stop the local MongoDB process (data preserved)
|
|
19
|
+
- `{{runDbReset}}` — stop and wipe `.mongo-data/` for a clean slate
|
|
20
|
+
- `{{runMigrationPlan}}` — create a MongoDB migration plan
|
|
21
|
+
- `{{runMigrate}}` — apply the planned MongoDB migration
|
|
22
|
+
- `{{runDbSeed}}` — insert sample users manually
|
|
23
|
+
|
|
24
|
+
## Prisma Next
|
|
25
|
+
|
|
26
|
+
Prisma Next setup is scaffolded in:
|
|
27
|
+
|
|
28
|
+
- `{{contractPath}}`
|
|
29
|
+
- `prisma-next.config.ts`
|
|
30
|
+
- `prisma/db.ts`
|
|
31
|
+
- `src/lib/prisma.ts`
|
|
32
|
+
|
|
33
|
+
For provider-specific Prisma Next reference docs, see `prisma-next.md`. Prisma Next skills live in the upstream `skills/` directory: https://github.com/prisma/prisma-next/tree/main/skills.
|
|
34
|
+
|
|
35
|
+
Node-based Prisma Next projects expect Node.js 24 LTS or newer.
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
# {{projectName}}
|
|
2
|
+
|
|
3
|
+
Generated by `prisma-next init` with the Minimal template.
|
|
4
|
+
|
|
5
|
+
## First run
|
|
6
|
+
|
|
7
|
+
1. `{{runDbInit}}` — applies your contract to the database (creates tables, signs the marker).
|
|
8
|
+
2. `{{runDev}}` — runs `src/index.ts`, which writes one row and reads it back.
|
|
9
|
+
|
|
10
|
+
## Available scripts
|
|
11
|
+
|
|
12
|
+
- `{{runDev}}` — run the Prisma Next sample script
|
|
13
|
+
|
|
14
|
+
### Database and migrations
|
|
15
|
+
|
|
16
|
+
- `{{runContractEmit}}` — emit contract artifacts after contract changes
|
|
17
|
+
- `{{runDbInit}}` — initialize database state manually
|
|
18
|
+
- `{{runDbUpdate}}` — update database state manually
|
|
19
|
+
- `{{runMigrationPlan}}` — create a migration plan
|
|
20
|
+
- `{{runMigrate}}` — apply a planned migration
|
|
21
|
+
- `{{runDbSeed}}` — insert sample users manually (run after `db:init`)
|
|
22
|
+
|
|
23
|
+
## Prisma Next
|
|
24
|
+
|
|
25
|
+
Prisma Next setup is scaffolded in:
|
|
26
|
+
|
|
27
|
+
- `{{contractPath}}`
|
|
28
|
+
- `prisma-next.config.ts`
|
|
29
|
+
- `prisma/db.ts`
|
|
30
|
+
- `src/lib/prisma.ts`
|
|
31
|
+
|
|
32
|
+
For provider-specific Prisma Next reference docs, see `prisma-next.md`. Prisma Next skills live in the upstream `skills/` directory: https://github.com/prisma/prisma-next/tree/main/skills.
|
|
33
|
+
|
|
34
|
+
Node-based Prisma Next projects expect Node.js 24 LTS or newer.
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@prisma-next/cli",
|
|
3
|
-
"version": "0.10.0-dev.
|
|
3
|
+
"version": "0.10.0-dev.23",
|
|
4
4
|
"license": "Apache-2.0",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"sideEffects": false,
|
|
@@ -14,15 +14,15 @@
|
|
|
14
14
|
"dependencies": {
|
|
15
15
|
"@clack/prompts": "^1.3.0",
|
|
16
16
|
"@dagrejs/dagre": "^3.0.0",
|
|
17
|
-
"@prisma-next/config": "0.10.0-dev.
|
|
18
|
-
"@prisma-next/contract": "0.10.0-dev.
|
|
19
|
-
"@prisma-next/emitter": "0.10.0-dev.
|
|
20
|
-
"@prisma-next/errors": "0.10.0-dev.
|
|
21
|
-
"@prisma-next/framework-components": "0.10.0-dev.
|
|
22
|
-
"@prisma-next/migration-tools": "0.10.0-dev.
|
|
23
|
-
"@prisma-next/psl-printer": "0.10.0-dev.
|
|
24
|
-
"@prisma-next/cli-telemetry": "0.10.0-dev.
|
|
25
|
-
"@prisma-next/utils": "0.10.0-dev.
|
|
17
|
+
"@prisma-next/config": "0.10.0-dev.23",
|
|
18
|
+
"@prisma-next/contract": "0.10.0-dev.23",
|
|
19
|
+
"@prisma-next/emitter": "0.10.0-dev.23",
|
|
20
|
+
"@prisma-next/errors": "0.10.0-dev.23",
|
|
21
|
+
"@prisma-next/framework-components": "0.10.0-dev.23",
|
|
22
|
+
"@prisma-next/migration-tools": "0.10.0-dev.23",
|
|
23
|
+
"@prisma-next/psl-printer": "0.10.0-dev.23",
|
|
24
|
+
"@prisma-next/cli-telemetry": "0.10.0-dev.23",
|
|
25
|
+
"@prisma-next/utils": "0.10.0-dev.23",
|
|
26
26
|
"arktype": "^2.2.0",
|
|
27
27
|
"c12": "^3.3.4",
|
|
28
28
|
"ci-info": "^4.3.1",
|
|
@@ -39,14 +39,14 @@
|
|
|
39
39
|
"wrap-ansi": "^10.0.0"
|
|
40
40
|
},
|
|
41
41
|
"devDependencies": {
|
|
42
|
-
"@prisma-next/sql-contract": "0.10.0-dev.
|
|
43
|
-
"@prisma-next/sql-contract-emitter": "0.10.0-dev.
|
|
44
|
-
"@prisma-next/sql-contract-ts": "0.10.0-dev.
|
|
45
|
-
"@prisma-next/sql-operations": "0.10.0-dev.
|
|
46
|
-
"@prisma-next/sql-runtime": "0.10.0-dev.
|
|
47
|
-
"@prisma-next/test-utils": "0.10.0-dev.
|
|
48
|
-
"@prisma-next/tsconfig": "0.10.0-dev.
|
|
49
|
-
"@prisma-next/tsdown": "0.10.0-dev.
|
|
42
|
+
"@prisma-next/sql-contract": "0.10.0-dev.23",
|
|
43
|
+
"@prisma-next/sql-contract-emitter": "0.10.0-dev.23",
|
|
44
|
+
"@prisma-next/sql-contract-ts": "0.10.0-dev.23",
|
|
45
|
+
"@prisma-next/sql-operations": "0.10.0-dev.23",
|
|
46
|
+
"@prisma-next/sql-runtime": "0.10.0-dev.23",
|
|
47
|
+
"@prisma-next/test-utils": "0.10.0-dev.23",
|
|
48
|
+
"@prisma-next/tsconfig": "0.10.0-dev.23",
|
|
49
|
+
"@prisma-next/tsdown": "0.10.0-dev.23",
|
|
50
50
|
"@types/node": "24.10.4",
|
|
51
51
|
"tsdown": "0.22.0",
|
|
52
52
|
"typescript": "5.9.3",
|
package/src/commands/db-init.ts
CHANGED
|
@@ -80,9 +80,17 @@ function mapDbInitFailure(failure: DbInitFailure): CliStructuredError {
|
|
|
80
80
|
}
|
|
81
81
|
|
|
82
82
|
if (failure.code === 'RUNNER_FAILED') {
|
|
83
|
+
const runnerCode =
|
|
84
|
+
typeof failure.meta?.['runnerErrorCode'] === 'string'
|
|
85
|
+
? failure.meta['runnerErrorCode']
|
|
86
|
+
: undefined;
|
|
87
|
+
const fix =
|
|
88
|
+
runnerCode === 'LEGACY_MARKER_SHAPE'
|
|
89
|
+
? 'Legacy marker-table shape detected. Drop `prisma_contract.marker` (Postgres) or `_prisma_marker` (SQLite) and re-run `prisma-next db init` to recreate it with the current per-space schema.'
|
|
90
|
+
: 'Fix the schema mismatch (db init is additive-only), or drop/reset the database and re-run `prisma-next db init`';
|
|
83
91
|
return errorRunnerFailed(failure.summary, {
|
|
84
92
|
why: failure.why ?? 'Migration runner failed',
|
|
85
|
-
fix
|
|
93
|
+
fix,
|
|
86
94
|
...(failure.meta
|
|
87
95
|
? { meta: { code: 'RUNNER_FAILED', ...failure.meta } }
|
|
88
96
|
: { meta: { code: 'RUNNER_FAILED' } }),
|
|
@@ -54,9 +54,17 @@ function mapDbUpdateFailure(failure: DbUpdateFailure): CliStructuredError {
|
|
|
54
54
|
}
|
|
55
55
|
|
|
56
56
|
if (failure.code === 'RUNNER_FAILED') {
|
|
57
|
+
const runnerCode =
|
|
58
|
+
typeof failure.meta?.['runnerErrorCode'] === 'string'
|
|
59
|
+
? failure.meta['runnerErrorCode']
|
|
60
|
+
: undefined;
|
|
61
|
+
const fix =
|
|
62
|
+
runnerCode === 'LEGACY_MARKER_SHAPE'
|
|
63
|
+
? 'Legacy marker-table shape detected. Drop `prisma_contract.marker` (Postgres) or `_prisma_marker` (SQLite) and re-run `prisma-next db init` to recreate it with the current per-space schema.'
|
|
64
|
+
: 'Inspect the reported conflict, reconcile schema drift if needed, then re-run `prisma-next db update`';
|
|
57
65
|
return errorRunnerFailed(failure.summary, {
|
|
58
66
|
why: failure.why ?? 'Migration runner failed',
|
|
59
|
-
fix
|
|
67
|
+
fix,
|
|
60
68
|
...ifDefined('meta', failure.meta),
|
|
61
69
|
});
|
|
62
70
|
}
|
|
@@ -331,7 +331,7 @@ function wrapVerifyError(
|
|
|
331
331
|
contractPathAbsolute: string,
|
|
332
332
|
modeLabel: string,
|
|
333
333
|
): Result<never, CliStructuredError> {
|
|
334
|
-
if (error
|
|
334
|
+
if (CliStructuredError.is(error)) {
|
|
335
335
|
return notOk(error);
|
|
336
336
|
}
|
|
337
337
|
if (error instanceof ContractValidationError) {
|
|
@@ -56,6 +56,21 @@ export function formatRunCommand(pm: PackageManager, bin: string, args: string):
|
|
|
56
56
|
return `${pm} ${bin} ${args}`;
|
|
57
57
|
}
|
|
58
58
|
|
|
59
|
+
export function formatRunScriptCommand(pm: PackageManager, scriptName: string): string {
|
|
60
|
+
switch (pm) {
|
|
61
|
+
case 'deno':
|
|
62
|
+
return `deno task ${scriptName}`;
|
|
63
|
+
case 'bun':
|
|
64
|
+
return `bun run ${scriptName}`;
|
|
65
|
+
case 'pnpm':
|
|
66
|
+
return `pnpm run ${scriptName}`;
|
|
67
|
+
case 'yarn':
|
|
68
|
+
return `yarn run ${scriptName}`;
|
|
69
|
+
default:
|
|
70
|
+
return `npm run ${scriptName}`;
|
|
71
|
+
}
|
|
72
|
+
}
|
|
73
|
+
|
|
59
74
|
export function formatAddArgs(pm: PackageManager, packages: string[]): string[] {
|
|
60
75
|
if (pm === 'deno') {
|
|
61
76
|
return ['add', ...packages.map((p) => `npm:${p}`)];
|
|
@@ -59,6 +59,7 @@ import {
|
|
|
59
59
|
import { configFile, dbFile, starterSchema, targetPackageName } from './templates/code-templates';
|
|
60
60
|
import { envExampleContent, envFileContent, MIN_SERVER_VERSION } from './templates/env';
|
|
61
61
|
import { quickReferenceMd } from './templates/quick-reference';
|
|
62
|
+
import { minimalProjectReadmeMd } from './templates/readme';
|
|
62
63
|
import { defaultTsConfig, mergeTsConfig, TsConfigParseError } from './templates/tsconfig';
|
|
63
64
|
|
|
64
65
|
interface FileEntry {
|
|
@@ -349,6 +350,26 @@ export async function runInit(
|
|
|
349
350
|
}
|
|
350
351
|
}
|
|
351
352
|
|
|
353
|
+
if (existsSync(join(baseDir, 'src/index.ts'))) {
|
|
354
|
+
if (!existsSync(join(baseDir, 'README.md'))) {
|
|
355
|
+
const rawName =
|
|
356
|
+
parsedPackageJson !== null && typeof parsedPackageJson['name'] === 'string'
|
|
357
|
+
? parsedPackageJson['name']
|
|
358
|
+
: basename(baseDir);
|
|
359
|
+
filesToWrite.push({
|
|
360
|
+
path: 'README.md',
|
|
361
|
+
content: minimalProjectReadmeMd(
|
|
362
|
+
inputs.target,
|
|
363
|
+
inputs.schemaPath,
|
|
364
|
+
sanitisePackageName(rawName),
|
|
365
|
+
pm,
|
|
366
|
+
),
|
|
367
|
+
});
|
|
368
|
+
} else {
|
|
369
|
+
warnings.push('README.md already exists; leaving it untouched.');
|
|
370
|
+
}
|
|
371
|
+
}
|
|
372
|
+
|
|
352
373
|
// -----------------------------------------------------------------
|
|
353
374
|
// Write phase — every input has been parsed and every merged file is
|
|
354
375
|
// staged. From here on, failures are only possible at the
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
# {{projectName}}
|
|
2
|
+
|
|
3
|
+
Generated by `prisma-next init` with the Minimal template.
|
|
4
|
+
|
|
5
|
+
## First run
|
|
6
|
+
|
|
7
|
+
1. `{{runDbUp}}` — start the local MongoDB replica set with `mongodb-memory-server`. Data persists in `.mongo-data/` across restarts.
|
|
8
|
+
2. `{{runDev}}` — runs `src/index.ts`, which writes one row and reads it back.
|
|
9
|
+
|
|
10
|
+
## Available scripts
|
|
11
|
+
|
|
12
|
+
- `{{runDev}}` — run the Prisma Next sample script
|
|
13
|
+
|
|
14
|
+
### Database and migrations
|
|
15
|
+
|
|
16
|
+
- `{{runContractEmit}}` — emit contract artifacts after contract changes
|
|
17
|
+
- `{{runDbUp}}` — start the local MongoDB replica set with `mongodb-memory-server`. Data persists in `.mongo-data/` across restarts.
|
|
18
|
+
- `{{runDbDown}}` — stop the local MongoDB process (data preserved)
|
|
19
|
+
- `{{runDbReset}}` — stop and wipe `.mongo-data/` for a clean slate
|
|
20
|
+
- `{{runMigrationPlan}}` — create a MongoDB migration plan
|
|
21
|
+
- `{{runMigrate}}` — apply the planned MongoDB migration
|
|
22
|
+
- `{{runDbSeed}}` — insert sample users manually
|
|
23
|
+
|
|
24
|
+
## Prisma Next
|
|
25
|
+
|
|
26
|
+
Prisma Next setup is scaffolded in:
|
|
27
|
+
|
|
28
|
+
- `{{contractPath}}`
|
|
29
|
+
- `prisma-next.config.ts`
|
|
30
|
+
- `prisma/db.ts`
|
|
31
|
+
- `src/lib/prisma.ts`
|
|
32
|
+
|
|
33
|
+
For provider-specific Prisma Next reference docs, see `prisma-next.md`. Prisma Next skills live in the upstream `skills/` directory: https://github.com/prisma/prisma-next/tree/main/skills.
|
|
34
|
+
|
|
35
|
+
Node-based Prisma Next projects expect Node.js 24 LTS or newer.
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
# {{projectName}}
|
|
2
|
+
|
|
3
|
+
Generated by `prisma-next init` with the Minimal template.
|
|
4
|
+
|
|
5
|
+
## First run
|
|
6
|
+
|
|
7
|
+
1. `{{runDbInit}}` — applies your contract to the database (creates tables, signs the marker).
|
|
8
|
+
2. `{{runDev}}` — runs `src/index.ts`, which writes one row and reads it back.
|
|
9
|
+
|
|
10
|
+
## Available scripts
|
|
11
|
+
|
|
12
|
+
- `{{runDev}}` — run the Prisma Next sample script
|
|
13
|
+
|
|
14
|
+
### Database and migrations
|
|
15
|
+
|
|
16
|
+
- `{{runContractEmit}}` — emit contract artifacts after contract changes
|
|
17
|
+
- `{{runDbInit}}` — initialize database state manually
|
|
18
|
+
- `{{runDbUpdate}}` — update database state manually
|
|
19
|
+
- `{{runMigrationPlan}}` — create a migration plan
|
|
20
|
+
- `{{runMigrate}}` — apply a planned migration
|
|
21
|
+
- `{{runDbSeed}}` — insert sample users manually (run after `db:init`)
|
|
22
|
+
|
|
23
|
+
## Prisma Next
|
|
24
|
+
|
|
25
|
+
Prisma Next setup is scaffolded in:
|
|
26
|
+
|
|
27
|
+
- `{{contractPath}}`
|
|
28
|
+
- `prisma-next.config.ts`
|
|
29
|
+
- `prisma/db.ts`
|
|
30
|
+
- `src/lib/prisma.ts`
|
|
31
|
+
|
|
32
|
+
For provider-specific Prisma Next reference docs, see `prisma-next.md`. Prisma Next skills live in the upstream `skills/` directory: https://github.com/prisma/prisma-next/tree/main/skills.
|
|
33
|
+
|
|
34
|
+
Node-based Prisma Next projects expect Node.js 24 LTS or newer.
|
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
import { formatRunScriptCommand, type PackageManager } from '../detect-package-manager';
|
|
2
|
+
import type { TargetId } from './code-templates';
|
|
3
|
+
import { renderTemplate } from './render';
|
|
4
|
+
|
|
5
|
+
const sharedVariables = ['projectName', 'contractPath', 'runDev', 'runContractEmit'] as const;
|
|
6
|
+
|
|
7
|
+
export const postgresVariables = [
|
|
8
|
+
...sharedVariables,
|
|
9
|
+
'runDbInit',
|
|
10
|
+
'runDbUpdate',
|
|
11
|
+
'runMigrationPlan',
|
|
12
|
+
'runMigrate',
|
|
13
|
+
'runDbSeed',
|
|
14
|
+
] as const;
|
|
15
|
+
|
|
16
|
+
export const mongoVariables = [
|
|
17
|
+
...sharedVariables,
|
|
18
|
+
'runDbUp',
|
|
19
|
+
'runDbDown',
|
|
20
|
+
'runDbReset',
|
|
21
|
+
'runMigrationPlan',
|
|
22
|
+
'runMigrate',
|
|
23
|
+
'runDbSeed',
|
|
24
|
+
] as const;
|
|
25
|
+
|
|
26
|
+
type PostgresVars = Record<(typeof postgresVariables)[number], string>;
|
|
27
|
+
type MongoVars = Record<(typeof mongoVariables)[number], string>;
|
|
28
|
+
|
|
29
|
+
export function minimalProjectReadmeMd(
|
|
30
|
+
target: TargetId,
|
|
31
|
+
schemaPath: string,
|
|
32
|
+
projectName: string,
|
|
33
|
+
pm: PackageManager,
|
|
34
|
+
): string {
|
|
35
|
+
const run = (script: string): string => formatRunScriptCommand(pm, script);
|
|
36
|
+
const shared = {
|
|
37
|
+
projectName,
|
|
38
|
+
contractPath: schemaPath,
|
|
39
|
+
runDev: run('dev'),
|
|
40
|
+
runContractEmit: run('contract:emit'),
|
|
41
|
+
runMigrationPlan: run('migration:plan'),
|
|
42
|
+
runMigrate: run('migrate'),
|
|
43
|
+
runDbSeed: run('db:seed'),
|
|
44
|
+
};
|
|
45
|
+
|
|
46
|
+
if (target === 'mongo') {
|
|
47
|
+
const vars: MongoVars = {
|
|
48
|
+
...shared,
|
|
49
|
+
runDbUp: run('db:up'),
|
|
50
|
+
runDbDown: run('db:down'),
|
|
51
|
+
runDbReset: run('db:reset'),
|
|
52
|
+
};
|
|
53
|
+
return renderTemplate('readme-mongo.md', mongoVariables, vars);
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
const vars: PostgresVars = {
|
|
57
|
+
...shared,
|
|
58
|
+
runDbInit: run('db:init'),
|
|
59
|
+
runDbUpdate: run('db:update'),
|
|
60
|
+
};
|
|
61
|
+
return renderTemplate('readme-postgres.md', postgresVariables, vars);
|
|
62
|
+
}
|
|
@@ -8,7 +8,7 @@ import { Command } from 'commander';
|
|
|
8
8
|
import { loadConfig } from '../config-loader';
|
|
9
9
|
import { createControlClient } from '../control-api/client';
|
|
10
10
|
import {
|
|
11
|
-
|
|
11
|
+
CliStructuredError,
|
|
12
12
|
errorDatabaseConnectionRequired,
|
|
13
13
|
errorDriverRequired,
|
|
14
14
|
errorUnexpected,
|
|
@@ -159,6 +159,7 @@ async function executeMigrationLogCommand(
|
|
|
159
159
|
summary: `${entries.length} migration(s) applied`,
|
|
160
160
|
});
|
|
161
161
|
} catch (error) {
|
|
162
|
+
if (CliStructuredError.is(error)) return notOk(error);
|
|
162
163
|
if (MigrationToolsError.is(error)) return notOk(mapMigrationToolsError(error));
|
|
163
164
|
return notOk(
|
|
164
165
|
errorUnexpected(error instanceof Error ? error.message : String(error), {
|
|
@@ -30,7 +30,7 @@ import { Command } from 'commander';
|
|
|
30
30
|
import { loadConfig } from '../config-loader';
|
|
31
31
|
import { createControlClient } from '../control-api/client';
|
|
32
32
|
import {
|
|
33
|
-
|
|
33
|
+
CliStructuredError,
|
|
34
34
|
errorRuntime,
|
|
35
35
|
errorUnexpected,
|
|
36
36
|
mapMigrationToolsError,
|
|
@@ -540,6 +540,40 @@ async function loadContractRawSafely(config: {
|
|
|
540
540
|
}
|
|
541
541
|
}
|
|
542
542
|
|
|
543
|
+
async function validateOnlineMarkerRead(
|
|
544
|
+
config: Awaited<ReturnType<typeof loadConfig>>,
|
|
545
|
+
dbConnection: unknown,
|
|
546
|
+
): Promise<Result<void, CliStructuredError>> {
|
|
547
|
+
const driver = config.driver;
|
|
548
|
+
if (!driver) {
|
|
549
|
+
return ok(undefined);
|
|
550
|
+
}
|
|
551
|
+
|
|
552
|
+
const client = createControlClient({
|
|
553
|
+
family: config.family,
|
|
554
|
+
target: config.target,
|
|
555
|
+
adapter: config.adapter,
|
|
556
|
+
driver,
|
|
557
|
+
extensionPacks: config.extensionPacks ?? [],
|
|
558
|
+
});
|
|
559
|
+
try {
|
|
560
|
+
await client.connect(dbConnection);
|
|
561
|
+
await client.readMarker();
|
|
562
|
+
return ok(undefined);
|
|
563
|
+
} catch (error) {
|
|
564
|
+
if (CliStructuredError.is(error)) {
|
|
565
|
+
return notOk(error);
|
|
566
|
+
}
|
|
567
|
+
return notOk(
|
|
568
|
+
errorUnexpected(error instanceof Error ? error.message : String(error), {
|
|
569
|
+
why: `Failed to read database marker: ${error instanceof Error ? error.message : String(error)}`,
|
|
570
|
+
}),
|
|
571
|
+
);
|
|
572
|
+
} finally {
|
|
573
|
+
await client.close();
|
|
574
|
+
}
|
|
575
|
+
}
|
|
576
|
+
|
|
543
577
|
async function executeMigrationStatusCommand(
|
|
544
578
|
options: MigrationStatusOptions,
|
|
545
579
|
flags: GlobalFlags,
|
|
@@ -666,6 +700,12 @@ async function executeMigrationStatusCommand(
|
|
|
666
700
|
}
|
|
667
701
|
|
|
668
702
|
if (bundles.length === 0) {
|
|
703
|
+
if (dbConnection && hasDriver) {
|
|
704
|
+
const markerProbe = await validateOnlineMarkerRead(config, dbConnection);
|
|
705
|
+
if (!markerProbe.ok) {
|
|
706
|
+
return markerProbe;
|
|
707
|
+
}
|
|
708
|
+
}
|
|
669
709
|
if (contractHash !== EMPTY_CONTRACT_HASH) {
|
|
670
710
|
diagnostics.push({
|
|
671
711
|
code: 'CONTRACT.AHEAD',
|
|
@@ -750,7 +790,10 @@ async function executeMigrationStatusCommand(
|
|
|
750
790
|
// space has no marker", which is a different condition).
|
|
751
791
|
allMarkers = null;
|
|
752
792
|
}
|
|
753
|
-
} catch {
|
|
793
|
+
} catch (error) {
|
|
794
|
+
if (CliStructuredError.is(error)) {
|
|
795
|
+
return notOk(error);
|
|
796
|
+
}
|
|
754
797
|
if (!flags.json && !flags.quiet) {
|
|
755
798
|
ui.warn('Could not connect to database — showing offline status');
|
|
756
799
|
}
|
|
@@ -178,6 +178,7 @@ export async function applyAggregate<TFamilyId extends string, TTargetId extends
|
|
|
178
178
|
meta: {
|
|
179
179
|
...(runnerResult.failure.meta ?? {}),
|
|
180
180
|
failingSpace: runnerResult.failure.failingSpace,
|
|
181
|
+
runnerErrorCode: runnerResult.failure.code,
|
|
181
182
|
},
|
|
182
183
|
});
|
|
183
184
|
}
|