Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
42 commits
Select commit Hold shift + click to select a range
9d60949
feat(config): section-tagged config diagnostics and defineConfig vers…
wmadden-electric Aug 9, 2026
6bf68c1
feat(cli): fixture-backed ControlClient test double at control-api/te…
wmadden-electric Aug 9, 2026
f345024
Merge remote-tracking branch 'origin/main' into config-diagnostics-ma…
wmadden-electric Aug 11, 2026
0bb2acb
fix(cli): keep the ControlClient double and marker error family-blind
wmadden-electric Aug 11, 2026
7e2f02f
fix(scripts): stamp regen configs with defineConfig
wmadden-electric Aug 11, 2026
5c9790d
test(config-loader): cover the load failure paths and section composi…
wmadden-electric Aug 11, 2026
3d03d74
fix(config-loader): drop the legacy product name from a test fixture
wmadden-electric Aug 11, 2026
2cf3700
feat(cli): add the orm config section on the pinned cli-engine
wmadden-electric Aug 11, 2026
c781862
feat(cli): convert raised errors at the handler boundary
wmadden-electric Aug 11, 2026
486f38c
feat(cli): build the engine's LoadedConfig from prisma-next.config.ts
wmadden-electric Aug 11, 2026
47ab290
feat(cli): stand up the prisma-next bin on the engine
wmadden-electric Aug 11, 2026
7514a2b
feat(cli): port migration list onto the engine
wmadden-electric Aug 11, 2026
d8f2d17
test(integration): run journey steps through the engine harness
wmadden-electric Aug 11, 2026
1f23370
refactor(cli): give control-API operations their config and cwd
wmadden-electric Aug 11, 2026
a689df1
refactor(cli): drop the prisma-next telemetry commands, keep the repo…
wmadden-electric Aug 11, 2026
fba8852
feat(cli): give the CLI's own error factories typed next actions
wmadden-electric Aug 11, 2026
3dfb208
feat(cli): publish migration check failures with typed next actions
wmadden-electric Aug 11, 2026
bd03d1a
feat(cli): port migration show onto the engine
wmadden-electric Aug 11, 2026
6e351e3
feat(cli): port migration log onto the engine
wmadden-electric Aug 11, 2026
9f03f13
feat(cli): port migration graph onto the engine
wmadden-electric Aug 11, 2026
098e4b6
test(integration): run the ported migration read commands on the engine
wmadden-electric Aug 11, 2026
014bb60
test(cli): make the migration list tests load a real migration
wmadden-electric Aug 11, 2026
e5dc5d2
fix(config): narrow config sections with isObject instead of bare casts
wmadden-electric Aug 11, 2026
21c9f28
fix(config-loader): classify load failures by cause, not message text
wmadden-electric Aug 11, 2026
fe47ee8
fix(cli): declare every config section each command actually reads
wmadden-electric Aug 11, 2026
f21fc63
fix(cli): hold the fixture ControlClient to the real connection lifec…
wmadden-electric Aug 11, 2026
8a4114b
fix(language-server): block a project only on the sections it resolve…
wmadden-electric Aug 11, 2026
49c927c
fix(cli): let the fixture client connect from an operation option
wmadden-electric Aug 11, 2026
766b956
test(config-loader): exclude unreachable defensive arms from coverage
wmadden-electric Aug 11, 2026
a1f22ba
Merge branch 'config-diagnostics-marker-and-control-client-double' in…
wmadden-electric Aug 11, 2026
9eac8b1
Merge remote-tracking branch 'bot/main' into s5-orm-cli-port
wmadden-electric Aug 11, 2026
efc9a08
Merge branch 's5-orm-cli-port' into s5-orm-pr2
wmadden-electric Aug 11, 2026
023636c
fix(cli): clear the three CI checks this port left red
wmadden-electric Aug 11, 2026
e33f9b1
Merge branch 's5-orm-cli-port' into s5-orm-pr2
wmadden-electric Aug 11, 2026
7018399
chore(cli): keep the framework layer's wording family-blind
wmadden-electric Aug 11, 2026
646183d
Merge branch 's5-orm-cli-port' into s5-orm-pr2
wmadden-electric Aug 11, 2026
e4cddfd
chore(cli): keep the typed next actions on the same prose as the fix …
wmadden-electric Aug 11, 2026
7080919
fix(cli): close the review findings on the ORM CLI port
wmadden-electric Aug 11, 2026
de5b48f
Merge branch 's5-orm-cli-port' into s5-orm-pr2
wmadden-electric Aug 11, 2026
b45096d
fix(cli): stop a failed close replacing the command's result
wmadden-electric Aug 11, 2026
f4377f1
Merge remote-tracking branch 'bot/main' into s5-orm-pr2
wmadden-electric Aug 11, 2026
ec753a5
test(cli): bind the migration log mock regardless of file order
wmadden-electric Aug 11, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ import {
} from '../utils/cli-errors';
import type { MigrationCommandOptions } from '../utils/command-helpers';
import {
closeQuietly,
resolveMigrationPaths,
sanitizeErrorMessage,
setCommandDescriptions,
Expand Down Expand Up @@ -237,7 +238,7 @@ async function executeDbInitCommand(
}),
);
} finally {
await client.close();
await closeQuietly(client);
}
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ import {
} from '../utils/cli-errors';
import {
addGlobalOptions,
closeQuietly,
maskConnectionUrl,
resolveContractPath,
resolveMigrationPaths,
Expand Down Expand Up @@ -230,7 +231,7 @@ async function executeDbSignCommand(
}),
);
} finally {
await client.close();
await closeQuietly(client);
}
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ import {
} from '../utils/cli-errors';
import type { MigrationCommandOptions } from '../utils/command-helpers';
import {
closeQuietly,
resolveMigrationPaths,
sanitizeErrorMessage,
setCommandDescriptions,
Expand Down Expand Up @@ -229,7 +230,7 @@ async function executeDbUpdateCommand(
}),
);
} finally {
await client.close();
await closeQuietly(client);
}
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@ import {
import { type CombinedVerifyResult, combineVerifyResults } from '../utils/combine-verify-results';
import {
addGlobalOptions,
closeQuietly,
maskConnectionUrl,
resolveContractPath,
resolveMigrationPaths,
Expand Down Expand Up @@ -480,7 +481,7 @@ async function executeDbVerifyCommand(
} catch (error) {
return wrapVerifyError(error, contractPathAbsolute, 'db verify');
} finally {
await client.close();
await closeQuietly(client);
}
}

Expand Down Expand Up @@ -529,7 +530,7 @@ async function executeDbSchemaOnlyVerifyCommand(
} catch (error) {
return wrapVerifyError(error, contractPathAbsolute, 'db verify --schema-only');
} finally {
await client.close();
await closeQuietly(client);
}
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ import {
errorDriverRequired,
errorUnexpected,
} from '../utils/cli-errors';
import { maskConnectionUrl, sanitizeErrorMessage } from '../utils/command-helpers';
import { closeQuietly, maskConnectionUrl, sanitizeErrorMessage } from '../utils/command-helpers';
import { formatStyledHeader } from '../utils/formatters/styled';
import type { CommonCommandOptions, GlobalFlags } from '../utils/global-flags';
import { createProgressAdapter } from '../utils/progress-adapter';
Expand Down Expand Up @@ -178,6 +178,6 @@ export async function inspectLiveSchema(
}),
);
} finally {
await client.close();
await closeQuietly(client);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -176,12 +176,28 @@ export const migrationShowResultSchema = successEnvelopeBaseSchema.and(

export type MigrationShowResult = typeof migrationShowResultSchema.infer;

/**
* The engine's `NextAction`, restated as the published shape of this CLI's own
* `--json` documents. It is not imported from the engine because these schemas
* ARE the wire contract — a change here is a change users see. The engine's
* plural `commands` form is absent because nothing here emits one.
*/
export const nextActionSchema = type({
kind: '"run-command" | "open-url" | "user-choice" | "edit-file" | "done"',
label: 'string',
'command?': 'string',
'url?': 'string',
'reason?': 'string',
});

export type NextActionJson = typeof nextActionSchema.infer;

export const checkFailureSchema = type({
space: 'string',
code: 'string',
where: 'string',
why: 'string',
fix: 'string',
nextActions: nextActionSchema.array(),
});

export type CheckFailure = typeof checkFailureSchema.infer;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,7 @@ import {
} from '../utils/cli-errors';
import {
addGlobalOptions,
closeQuietly,
maskConnectionUrl,
resolveContractPath,
resolveMigrationPaths,
Expand Down Expand Up @@ -631,7 +632,7 @@ async function executeMigrateCommand(
}),
);
} finally {
await client.close();
await closeQuietly(client);
}
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -87,6 +87,7 @@ async function executeMigrationCheckCommand(
...(options.space !== undefined ? { spaceFilter: options.space } : {}),
appMigrationsDir,
appMigrationsRelative,
cwd: process.cwd(),
});
}

Expand Down Expand Up @@ -195,7 +196,10 @@ export function createMigrationCheckCommand(): Command {
} else {
for (const f of result.failures) {
ui.log(`✗ [${f.code}] ${f.where}: ${f.why}`);
ui.log(` fix: ${f.fix}`);
for (const action of f.nextActions) {
const command = action.command === undefined ? '' : `: ${action.command}`;
ui.log(` next: ${action.label}${command}`);
}
}
ui.log(`\n${result.summary}`);
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,6 @@ import { Command } from 'commander';
import { buildReadAggregate } from '../control-api/operations/contract-space-aggregate-loader';
import { buildMigrationSpaceGraphEntries } from '../control-api/operations/migration-graph';
import {
listRefsByContractHash,
migrationSpaceListEntriesFromAggregate,
runMigrationList,
} from '../control-api/operations/migration-list';
Expand All @@ -20,11 +19,11 @@ import {
} from '../utils/command-helpers';
import { renderMigrationGraphLegend } from '../utils/formatters/migration-graph-labels';
import {
computeGlobalMaxDirNameWidth,
computeGlobalMaxEdgeTreePrefixWidth,
indentMigrationGraphTreeBlock,
renderMigrationGraphSpaceTree,
} from '../utils/formatters/migration-graph-space-render';
buildMigrationGraphTreeSections,
type MigrationGraphTreeSection,
renderMigrationGraphDot,
renderMigrationGraphSections,
} from '../utils/formatters/migration-graph-sections';
import { formatStyledHeader } from '../utils/formatters/styled';
import type { CommonCommandOptions } from '../utils/global-flags';
import { type GlobalFlags, parseGlobalFlagsOrExit } from '../utils/global-flags';
Expand All @@ -41,12 +40,6 @@ interface MigrationGraphOptions extends CommonCommandOptions {
readonly legend?: boolean;
}

export interface MigrationGraphTreeSection {
readonly space: string;
readonly tree: string;
readonly showHeading: boolean;
}

export interface MigrationGraphResult {
readonly ok: true;
/** App-space graph for the `--dot` Graphviz output. */
Expand All @@ -64,20 +57,7 @@ function computeGraphSummary(spaces: readonly MigrationSpaceGraphEntry[]): strin
}

export function formatMigrationGraphHumanOutput(result: MigrationGraphResult): string {
const sections: string[] = [];
for (const section of result.treeSections) {
if (section.showHeading) {
sections.push(`${section.space}:`);
}
if (section.tree.length > 0) {
sections.push(section.tree);
} else {
sections.push('(no migrations)');
}
sections.push('');
}
sections.push(result.summary);
return sections.join('\n').trimEnd();
return renderMigrationGraphSections(result.treeSections, result.summary);
}

export async function executeMigrationGraphCommand(
Expand Down Expand Up @@ -144,56 +124,14 @@ export async function executeMigrationGraphCommand(
}

const scopedSpaces = listResult.value.spaces;
const showSpaceHeadings = scopedSpaces.length > 1;
const glyphMode = ui.resolveGlyphMode(options.ascii === true);
const colorize = flags.color !== false;

const globalLayoutInputs = showSpaceHeadings
? scopedSpaces
.filter((spaceEntry) => spaceEntry.migrations.length > 0)
.map((spaceEntry) => ({
graph: aggregate.space(spaceEntry.space)!.graph(),
liveContractHash,
}))
: [];
const globalMaxEdgeTreePrefixWidth =
globalLayoutInputs.length > 0
? computeGlobalMaxEdgeTreePrefixWidth(globalLayoutInputs)
: undefined;
const globalMaxDirNameWidth =
globalLayoutInputs.length > 0 ? computeGlobalMaxDirNameWidth(globalLayoutInputs) : undefined;
const treeSections = buildMigrationGraphTreeSections({
aggregate,
scopedSpaces,
liveContractHash,
glyphMode: ui.resolveGlyphMode(options.ascii === true),
colorize: flags.color !== false,
});

const treeSections: MigrationGraphTreeSection[] = [];
for (const spaceEntry of scopedSpaces) {
const space = aggregate.space(spaceEntry.space);
if (space === undefined) {
continue;
}
const graph = space.graph();
const isAppSpace = spaceEntry.space === aggregate.app.spaceId;
const refsByHash = listRefsByContractHash(space);
const tree =
spaceEntry.migrations.length === 0
? ''
: renderMigrationGraphSpaceTree({
graph,
migrations: spaceEntry.migrations,
liveContractHash,
glyphMode,
colorize,
isAppSpace,
refsByHash,
...(globalMaxEdgeTreePrefixWidth !== undefined ? { globalMaxEdgeTreePrefixWidth } : {}),
...(globalMaxDirNameWidth !== undefined ? { globalMaxDirNameWidth } : {}),
});
const displayTree =
showSpaceHeadings && tree.length > 0 ? indentMigrationGraphTreeBlock(tree, ' ') : tree;
treeSections.push({
space: spaceEntry.space,
tree: displayTree,
showHeading: showSpaceHeadings,
});
}
const spaces: MigrationSpaceGraphEntry[] = [
...buildMigrationSpaceGraphEntries({ aggregate, scopedSpaces }),
];
Expand Down Expand Up @@ -248,14 +186,7 @@ export function createMigrationGraphCommand(): Command {
const result = await executeMigrationGraphCommand(options, flags, ui);
const exitCode = handleResult(result, flags, ui, (graphResult) => {
if (options.dot) {
const lines = ['digraph migrations {'];
for (const edge of graphResult.graph.migrationByHash.values()) {
const from = edge.from.slice(0, 12);
const to = edge.to.slice(0, 12);
lines.push(` "${from}" -> "${to}" [label="${edge.dirName}"];`);
}
lines.push('}');
ui.output(lines.join('\n'));
ui.output(renderMigrationGraphDot(graphResult.graph));
} else if (flags.json) {
const jsonResult: MigrationGraphJsonResult = {
ok: true,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ import { mapCaughtMigrationError } from '../control-api/operations/caught-errors
import { type CliStructuredError, errorUnexpected, requireLiveDatabase } from '../utils/cli-errors';
import {
addGlobalOptions,
closeQuietly,
maskConnectionUrl,
resolveMigrationPaths,
setCommandDescriptions,
Expand Down Expand Up @@ -107,7 +108,7 @@ export async function executeMigrationLogCommand(
}),
);
} finally {
await client.close();
await closeQuietly(client);
}
}

Expand Down
31 changes: 15 additions & 16 deletions packages/1-framework/3-tooling/cli/src/commands/migration-show.ts
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,8 @@ import {
type CliStructuredError,
errorContractValidationFailed,
errorFileNotFound,
errorRuntime,
errorMigrationPackageNotFound,
errorNoMigrations,
errorUnexpected,
} from '../utils/cli-errors';
import {
Expand Down Expand Up @@ -187,26 +188,25 @@ async function executeMigrationShowCommand(

let appPkg: OnDiskMigrationPackage;
if (looksLikePath(target)) {
const resolved = resolveAppTargetPath(target, appMigrationsDir, appMigrationsRelative);
const resolved = resolveAppTargetPath(
process.cwd(),
target,
appMigrationsDir,
appMigrationsRelative,
);
if (!resolved.ok) return resolved;
const matched = findPackageByDirPath(packages, resolved.value);
if (!matched) {
return notOk(
errorRuntime('MIGRATION.PACKAGE_NOT_FOUND', 'Migration package not found', {
why: `No loaded migration package at ${relative(process.cwd(), resolved.value)}`,
fix: 'Pass a directory name, hash prefix, or path to an on-disk app-space migration package.',
}),
errorMigrationPackageNotFound(
`No loaded migration package at ${relative(process.cwd(), resolved.value)}`,
),
);
}
appPkg = matched;
} else {
if (packages.length === 0) {
return notOk(
errorRuntime('MIGRATION.NO_MIGRATIONS', 'No migrations found', {
why: `No migration packages found in ${appMigrationsRelative}`,
fix: 'Run `prisma-next migration plan` to create a migration first.',
}),
);
return notOk(errorNoMigrations(appMigrationsRelative));
}
const migResult = resolveMigrationRef(target, { graph, refs });
if (!migResult.ok) {
Expand All @@ -217,10 +217,9 @@ async function executeMigrationShowCommand(
);
if (!matchedPkg) {
return notOk(
errorRuntime('MIGRATION.PACKAGE_NOT_FOUND', 'Migration package not found', {
why: `Resolved migration "${migResult.value.dirName}" but the package was not loaded`,
fix: 'The migrations directory may be corrupted. Inspect the migration.json files.',
}),
errorMigrationPackageNotFound(
`Resolved migration "${migResult.value.dirName}" but the package was not loaded`,
),
);
}
appPkg = matchedPkg;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,7 @@ import { readMigrationRefs } from '../control-api/operations/refs';
import { CliStructuredError, errorUnexpected, requireLiveDatabase } from '../utils/cli-errors';
import {
addGlobalOptions,
closeQuietly,
maskConnectionUrl,
readContractEnvelope,
resolveMigrationPaths,
Expand Down Expand Up @@ -441,7 +442,7 @@ export async function executeMigrationStatusCommand(
}),
);
} finally {
await client.close();
await closeQuietly(client);
}
}

Expand Down
Loading
Loading