@prisma-next/cli 0.14.0-dev.2 → 0.14.0-dev.20

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.
Files changed (133) hide show
  1. package/README.md +2 -2
  2. package/dist/cli.mjs +29 -12
  3. package/dist/cli.mjs.map +1 -1
  4. package/dist/{client-CJzuo5wX.mjs → client-Dm_Efs9g.mjs} +5 -4
  5. package/dist/client-Dm_Efs9g.mjs.map +1 -0
  6. package/dist/{command-helpers-DGMvGBeX.mjs → command-helpers-6cNJZ863.mjs} +3 -3
  7. package/dist/{command-helpers-DGMvGBeX.mjs.map → command-helpers-6cNJZ863.mjs.map} +1 -1
  8. package/dist/commands/contract-emit.mjs +1 -1
  9. package/dist/commands/contract-infer.mjs +1 -1
  10. package/dist/commands/db-init.mjs +3 -3
  11. package/dist/commands/db-schema.mjs +3 -3
  12. package/dist/commands/db-sign.mjs +5 -5
  13. package/dist/commands/db-sign.mjs.map +1 -1
  14. package/dist/commands/db-update.mjs +4 -4
  15. package/dist/commands/db-verify.mjs +1 -1
  16. package/dist/commands/migrate.d.mts +1 -1
  17. package/dist/commands/migrate.mjs +6 -6
  18. package/dist/commands/migrate.mjs.map +1 -1
  19. package/dist/commands/migration-check.d.mts +1 -1
  20. package/dist/commands/migration-check.mjs +1 -1
  21. package/dist/commands/migration-graph.d.mts +1 -1
  22. package/dist/commands/migration-graph.mjs +4 -4
  23. package/dist/commands/migration-graph.mjs.map +1 -1
  24. package/dist/commands/migration-list.d.mts +1 -1
  25. package/dist/commands/migration-list.mjs +1 -1
  26. package/dist/commands/migration-log.d.mts +1 -1
  27. package/dist/commands/migration-log.mjs +1 -1
  28. package/dist/commands/migration-new.mjs +4 -4
  29. package/dist/commands/migration-new.mjs.map +1 -1
  30. package/dist/commands/migration-plan.mjs +1 -1
  31. package/dist/commands/migration-show.mjs +4 -4
  32. package/dist/commands/migration-show.mjs.map +1 -1
  33. package/dist/commands/migration-status.d.mts +1 -1
  34. package/dist/commands/migration-status.mjs +1 -1
  35. package/dist/commands/ref.d.mts +1 -1
  36. package/dist/commands/ref.mjs +3 -3
  37. package/dist/commands/ref.mjs.map +1 -1
  38. package/dist/commands/telemetry/index.mjs +1 -1
  39. package/dist/{contract-at-errors-CFXsstzm.mjs → contract-at-errors-DdxWRjMc.mjs} +2 -2
  40. package/dist/{contract-at-errors-CFXsstzm.mjs.map → contract-at-errors-DdxWRjMc.mjs.map} +1 -1
  41. package/dist/{contract-emit-B_qriF8B.mjs → contract-emit-D4xDDFkO.mjs} +6 -6
  42. package/dist/contract-emit-D4xDDFkO.mjs.map +1 -0
  43. package/dist/{contract-emit-C8HmtboH.mjs → contract-emit-fz6pDR2n.mjs} +4 -4
  44. package/dist/contract-emit-fz6pDR2n.mjs.map +1 -0
  45. package/dist/{contract-infer-BYT_ra_U.mjs → contract-infer-BMaib2U-.mjs} +3 -3
  46. package/dist/{contract-infer-BYT_ra_U.mjs.map → contract-infer-BMaib2U-.mjs.map} +1 -1
  47. package/dist/{contract-space-aggregate-loader-ClI1KN6d.mjs → contract-space-aggregate-loader-Bu2fjSWV.mjs} +2 -2
  48. package/dist/{contract-space-aggregate-loader-ClI1KN6d.mjs.map → contract-space-aggregate-loader-Bu2fjSWV.mjs.map} +1 -1
  49. package/dist/{db-verify-C24FKhb7.mjs → db-verify-PPxMkghu.mjs} +8 -5
  50. package/dist/db-verify-PPxMkghu.mjs.map +1 -0
  51. package/dist/exports/control-api.d.mts +1 -1
  52. package/dist/exports/control-api.mjs +2 -2
  53. package/dist/exports/index.d.mts +5 -1
  54. package/dist/exports/index.d.mts.map +1 -1
  55. package/dist/exports/index.mjs +4 -3
  56. package/dist/exports/index.mjs.map +1 -1
  57. package/dist/format-C3XdaDuA.mjs +102 -0
  58. package/dist/format-C3XdaDuA.mjs.map +1 -0
  59. package/dist/{framework-components-YVQHhPH7.mjs → framework-components-5RljWT7Q.mjs} +2 -2
  60. package/dist/{framework-components-YVQHhPH7.mjs.map → framework-components-5RljWT7Q.mjs.map} +1 -1
  61. package/dist/{init-0HwB-Vh8.mjs → init-C79Yb97u.mjs} +12 -8
  62. package/dist/init-C79Yb97u.mjs.map +1 -0
  63. package/dist/{inspect-live-schema-DF6IwcDl.mjs → inspect-live-schema-BmgdUJcj.mjs} +4 -4
  64. package/dist/inspect-live-schema-BmgdUJcj.mjs.map +1 -0
  65. package/dist/{migration-check-soB5uZEQ.mjs → migration-check-DWX4qdOB.mjs} +6 -5
  66. package/dist/migration-check-DWX4qdOB.mjs.map +1 -0
  67. package/dist/migration-cli.mjs +1 -1
  68. package/dist/migration-cli.mjs.map +1 -1
  69. package/dist/{migration-command-scaffold-DA-Lhx6o.mjs → migration-command-scaffold-Dv0tKmIJ.mjs} +4 -4
  70. package/dist/migration-command-scaffold-Dv0tKmIJ.mjs.map +1 -0
  71. package/dist/{migration-list-DlJJ_38Z.mjs → migration-list-DVHVmMrf.mjs} +4 -4
  72. package/dist/migration-list-DVHVmMrf.mjs.map +1 -0
  73. package/dist/{migration-log-CG0qQAFm.mjs → migration-log-BlkNEV6F.mjs} +4 -4
  74. package/dist/migration-log-BlkNEV6F.mjs.map +1 -0
  75. package/dist/{migration-path-target-Ce6OZImp.mjs → migration-path-target-zK6RG6JY.mjs} +2 -2
  76. package/dist/{migration-path-target-Ce6OZImp.mjs.map → migration-path-target-zK6RG6JY.mjs.map} +1 -1
  77. package/dist/{migration-plan-W_E8FQOk.mjs → migration-plan-o2aqjnfd.mjs} +6 -6
  78. package/dist/migration-plan-o2aqjnfd.mjs.map +1 -0
  79. package/dist/{migration-status-CgWSoI_g.mjs → migration-status-DCmV7fWX.mjs} +7 -6
  80. package/dist/migration-status-DCmV7fWX.mjs.map +1 -0
  81. package/dist/{telemetry-BIM4beEO.mjs → telemetry-BckOSUWG.mjs} +2 -2
  82. package/dist/{telemetry-BIM4beEO.mjs.map → telemetry-BckOSUWG.mjs.map} +1 -1
  83. package/dist/{types-C_tYiJYx.d.mts → types-qETCCNbe.d.mts} +2 -2
  84. package/dist/{types-C_tYiJYx.d.mts.map → types-qETCCNbe.d.mts.map} +1 -1
  85. package/dist/{verify-DcOYZ1tH.mjs → verify-CLw3YRbl.mjs} +7 -2
  86. package/dist/{verify-DcOYZ1tH.mjs.map → verify-CLw3YRbl.mjs.map} +1 -1
  87. package/package.json +21 -23
  88. package/src/cli.ts +7 -0
  89. package/src/commands/contract-emit.ts +1 -1
  90. package/src/commands/db-sign.ts +1 -1
  91. package/src/commands/db-verify.ts +1 -1
  92. package/src/commands/format.ts +75 -0
  93. package/src/commands/init/detect-package-manager.ts +8 -4
  94. package/src/commands/init/hygiene-package-scripts.ts +16 -3
  95. package/src/commands/inspect-live-schema.ts +1 -1
  96. package/src/commands/lsp.ts +32 -0
  97. package/src/commands/migrate.ts +1 -1
  98. package/src/commands/migration-check.ts +1 -1
  99. package/src/commands/migration-graph.ts +1 -1
  100. package/src/commands/migration-list.ts +1 -1
  101. package/src/commands/migration-log.ts +1 -1
  102. package/src/commands/migration-new.ts +1 -1
  103. package/src/commands/migration-plan.ts +1 -1
  104. package/src/commands/migration-show.ts +1 -1
  105. package/src/commands/migration-status.ts +1 -1
  106. package/src/commands/ref.ts +1 -1
  107. package/src/control-api/operations/contract-emit.ts +1 -1
  108. package/src/control-api/operations/db-verify.ts +1 -0
  109. package/src/control-api/operations/format.ts +98 -0
  110. package/src/exports/index.ts +1 -0
  111. package/src/migration-cli.ts +1 -1
  112. package/src/utils/combine-schema-results.ts +3 -0
  113. package/src/utils/formatters/verify.ts +8 -0
  114. package/src/utils/migration-command-scaffold.ts +1 -1
  115. package/dist/client-CJzuo5wX.mjs.map +0 -1
  116. package/dist/config-loader-p9JMrekQ.mjs +0 -88
  117. package/dist/config-loader-p9JMrekQ.mjs.map +0 -1
  118. package/dist/config-loader.d.mts +0 -16
  119. package/dist/config-loader.d.mts.map +0 -1
  120. package/dist/config-loader.mjs +0 -2
  121. package/dist/contract-emit-B_qriF8B.mjs.map +0 -1
  122. package/dist/contract-emit-C8HmtboH.mjs.map +0 -1
  123. package/dist/db-verify-C24FKhb7.mjs.map +0 -1
  124. package/dist/init-0HwB-Vh8.mjs.map +0 -1
  125. package/dist/inspect-live-schema-DF6IwcDl.mjs.map +0 -1
  126. package/dist/migration-check-soB5uZEQ.mjs.map +0 -1
  127. package/dist/migration-command-scaffold-DA-Lhx6o.mjs.map +0 -1
  128. package/dist/migration-list-DlJJ_38Z.mjs.map +0 -1
  129. package/dist/migration-log-CG0qQAFm.mjs.map +0 -1
  130. package/dist/migration-plan-W_E8FQOk.mjs.map +0 -1
  131. package/dist/migration-status-CgWSoI_g.mjs.map +0 -1
  132. package/src/config-loader.ts +0 -92
  133. package/src/config-path-validation.ts +0 -74
@@ -6,6 +6,10 @@ export type PackageManager = 'pnpm' | 'npm' | 'yarn' | 'bun' | 'deno';
6
6
 
7
7
  const KNOWN: ReadonlySet<string> = new Set<PackageManager>(['pnpm', 'npm', 'yarn', 'bun', 'deno']);
8
8
 
9
+ function isPackageManager(name: string): name is PackageManager {
10
+ return KNOWN.has(name);
11
+ }
12
+
9
13
  /**
10
14
  * Resolves the package manager `init` should drive for `add` / `install`
11
15
  * commands. Tries, in order:
@@ -28,12 +32,12 @@ const KNOWN: ReadonlySet<string> = new Set<PackageManager>(['pnpm', 'npm', 'yarn
28
32
  */
29
33
  export async function detectPackageManager(cwd: string): Promise<PackageManager> {
30
34
  const detected = await detect({ cwd });
31
- if (detected && KNOWN.has(detected.name)) {
32
- return detected.name as PackageManager;
35
+ if (detected && isPackageManager(detected.name)) {
36
+ return detected.name;
33
37
  }
34
38
  const userAgent = getUserAgent();
35
- if (userAgent !== null && KNOWN.has(userAgent)) {
36
- return userAgent as PackageManager;
39
+ if (userAgent !== null && isPackageManager(userAgent)) {
40
+ return userAgent;
37
41
  }
38
42
  return 'npm';
39
43
  }
@@ -1,3 +1,5 @@
1
+ import { blindCast } from '@prisma-next/utils/casts';
2
+
1
3
  /**
2
4
  * The package.json `scripts` entries `init` adds idempotently (FR3.5).
3
5
  * The script *name* mirrors the CLI subcommand path (`contract:emit` →
@@ -58,10 +60,18 @@ export function mergePackageScripts(
58
60
  existing: string,
59
61
  required: readonly RequiredScript[] = REQUIRED_SCRIPTS,
60
62
  ): PackageScriptsMergeResult {
61
- const parsed = JSON.parse(existing) as Record<string, unknown>;
63
+ const parsed = blindCast<
64
+ Record<string, unknown>,
65
+ 'JSON.parse returns `unknown`; package.json is a JSON object so its top level is a string-keyed record'
66
+ >(JSON.parse(existing));
62
67
  const scripts: Record<string, string> =
63
68
  typeof parsed['scripts'] === 'object' && parsed['scripts'] !== null
64
- ? { ...(parsed['scripts'] as Record<string, string>) }
69
+ ? {
70
+ ...blindCast<
71
+ Record<string, string>,
72
+ 'guarded above: parsed.scripts is a non-null object; package.json `scripts` values are command strings'
73
+ >(parsed['scripts']),
74
+ }
65
75
  : {};
66
76
 
67
77
  const warnings: string[] = [];
@@ -131,7 +141,10 @@ export interface EsmModuleTypeResult {
131
141
  * ESM and we don't silently overwrite that.
132
142
  */
133
143
  export function ensureEsmModuleType(existing: string): EsmModuleTypeResult {
134
- const parsed = JSON.parse(existing) as Record<string, unknown>;
144
+ const parsed = blindCast<
145
+ Record<string, unknown>,
146
+ 'JSON.parse returns `unknown`; package.json is a JSON object so its top level is a string-keyed record'
147
+ >(JSON.parse(existing));
135
148
  const currentType = parsed['type'];
136
149
 
137
150
  if (currentType === 'module') {
@@ -1,9 +1,9 @@
1
+ import { loadConfig } from '@prisma-next/config-loader';
1
2
  import type { AuthoringPslBlockDescriptorNamespace } from '@prisma-next/framework-components/authoring';
2
3
  import type { CoreSchemaView } from '@prisma-next/framework-components/control';
3
4
  import type { PslDocumentAst } from '@prisma-next/framework-components/psl-ast';
4
5
  import { notOk, ok, type Result } from '@prisma-next/utils/result';
5
6
  import { relative, resolve } from 'pathe';
6
- import { loadConfig } from '../config-loader';
7
7
  import { createControlClient } from '../control-api/client';
8
8
  import {
9
9
  CliStructuredError,
@@ -0,0 +1,32 @@
1
+ import { Command } from 'commander';
2
+ import {
3
+ addGlobalOptions,
4
+ setCommandDescriptions,
5
+ setCommandExamples,
6
+ } from '../utils/command-helpers';
7
+
8
+ export function createLspCommand(): Command {
9
+ const command = new Command('lsp');
10
+ setCommandDescriptions(
11
+ command,
12
+ 'Start the Prisma Next language server',
13
+ 'Launches a Language Server Protocol server that publishes PSL parse diagnostics\n' +
14
+ 'and handles whole-document PSL formatting for the schema inputs declared in\n' +
15
+ 'your config (contract.source.inputs). Formatting uses the Prisma Next PSL\n' +
16
+ 'formatter and the formatter block from the project config.\n' +
17
+ 'Communicates over stdio; intended to be spawned by an\n' +
18
+ 'editor, not run interactively. The server keeps running until the editor client\n' +
19
+ 'disconnects.',
20
+ );
21
+ setCommandExamples(command, ['prisma-next lsp --stdio']);
22
+ addGlobalOptions(command)
23
+ .option('--stdio', 'Communicate with the editor over stdio (the default and only transport)')
24
+ .action(async () => {
25
+ // Lazy import so `vscode-languageserver` stays off every other command's
26
+ // startup path — only `prisma-next lsp` pays its load cost.
27
+ const { startServer } = await import('@prisma-next/language-server');
28
+ startServer();
29
+ });
30
+
31
+ return command;
32
+ }
@@ -1,4 +1,5 @@
1
1
  import { readFile } from 'node:fs/promises';
2
+ import { loadConfig } from '@prisma-next/config-loader';
2
3
  import type { Contract } from '@prisma-next/contract/types';
3
4
  import { createControlStack } from '@prisma-next/framework-components/control';
4
5
  import { type ContractSpaceMember, requireHeadRef } from '@prisma-next/migration-tools/aggregate';
@@ -11,7 +12,6 @@ import { readRefs } from '@prisma-next/migration-tools/refs';
11
12
  import { ifDefined } from '@prisma-next/utils/defined';
12
13
  import { notOk, ok, type Result } from '@prisma-next/utils/result';
13
14
  import { Command } from 'commander';
14
- import { loadConfig } from '../config-loader';
15
15
  import { createControlClient } from '../control-api/client';
16
16
  import { planMemberPath } from '../control-api/operations/migrate';
17
17
  import type {
@@ -1,5 +1,6 @@
1
1
  import { existsSync, readdirSync, readFileSync, statSync } from 'node:fs';
2
2
  import { readFile } from 'node:fs/promises';
3
+ import { loadConfig } from '@prisma-next/config-loader';
3
4
  import { createControlStack } from '@prisma-next/framework-components/control';
4
5
  import type {
5
6
  ContractSpaceAggregate,
@@ -25,7 +26,6 @@ import { ifDefined } from '@prisma-next/utils/defined';
25
26
  import { notOk, ok, type Result } from '@prisma-next/utils/result';
26
27
  import { Command } from 'commander';
27
28
  import { join, relative } from 'pathe';
28
- import { loadConfig } from '../config-loader';
29
29
  import {
30
30
  type CliStructuredError,
31
31
  errorAmbiguousMigrationRef,
@@ -1,9 +1,9 @@
1
+ import { loadConfig } from '@prisma-next/config-loader';
1
2
  import { EMPTY_CONTRACT_HASH } from '@prisma-next/migration-tools/constants';
2
3
  import type { MigrationGraph } from '@prisma-next/migration-tools/graph';
3
4
  import { ifDefined } from '@prisma-next/utils/defined';
4
5
  import { ok, type Result } from '@prisma-next/utils/result';
5
6
  import { Command } from 'commander';
6
- import { loadConfig } from '../config-loader';
7
7
  import type { CliStructuredError } from '../utils/cli-errors';
8
8
  import {
9
9
  addGlobalOptions,
@@ -1,3 +1,4 @@
1
+ import { loadConfig } from '@prisma-next/config-loader';
1
2
  import type {
2
3
  ContractSpaceAggregate,
3
4
  ContractSpaceMember,
@@ -13,7 +14,6 @@ import {
13
14
  import { ifDefined } from '@prisma-next/utils/defined';
14
15
  import { notOk, ok, type Result } from '@prisma-next/utils/result';
15
16
  import { Command } from 'commander';
16
- import { loadConfig } from '../config-loader';
17
17
  import {
18
18
  type CliStructuredError,
19
19
  errorInvalidSpaceId,
@@ -1,9 +1,9 @@
1
+ import { loadConfig } from '@prisma-next/config-loader';
1
2
  import type { LedgerEntryRecord } from '@prisma-next/contract/types';
2
3
  import { MigrationToolsError } from '@prisma-next/migration-tools/errors';
3
4
  import { ifDefined } from '@prisma-next/utils/defined';
4
5
  import { notOk, ok, type Result } from '@prisma-next/utils/result';
5
6
  import { Command } from 'commander';
6
- import { loadConfig } from '../config-loader';
7
7
  import { createControlClient } from '../control-api/client';
8
8
  import {
9
9
  CliStructuredError,
@@ -9,6 +9,7 @@
9
9
  */
10
10
 
11
11
  import { readFile } from 'node:fs/promises';
12
+ import { loadConfig } from '@prisma-next/config-loader';
12
13
  import type { Contract } from '@prisma-next/contract/types';
13
14
  import { getEmittedArtifactPaths } from '@prisma-next/emitter';
14
15
  import { APP_SPACE_ID, createControlStack } from '@prisma-next/framework-components/control';
@@ -25,7 +26,6 @@ import { writeMigrationTs } from '@prisma-next/migration-tools/migration-ts';
25
26
  import { notOk, ok, type Result } from '@prisma-next/utils/result';
26
27
  import { Command } from 'commander';
27
28
  import { join, relative } from 'pathe';
28
- import { loadConfig } from '../config-loader';
29
29
  import {
30
30
  CliStructuredError,
31
31
  errorFileNotFound,
@@ -1,4 +1,5 @@
1
1
  import { mkdir, readFile, writeFile } from 'node:fs/promises';
2
+ import { loadConfig } from '@prisma-next/config-loader';
2
3
  import type { Contract } from '@prisma-next/contract/types';
3
4
  import { getEmittedArtifactPaths } from '@prisma-next/emitter';
4
5
  import {
@@ -21,7 +22,6 @@ import { writeMigrationTs } from '@prisma-next/migration-tools/migration-ts';
21
22
  import { notOk, ok, type Result } from '@prisma-next/utils/result';
22
23
  import { Command } from 'commander';
23
24
  import { join, relative } from 'pathe';
24
- import { loadConfig } from '../config-loader';
25
25
  import {
26
26
  type CliErrorConflict,
27
27
  CliStructuredError,
@@ -1,4 +1,5 @@
1
1
  import { readFile } from 'node:fs/promises';
2
+ import { loadConfig } from '@prisma-next/config-loader';
2
3
  import type { Contract } from '@prisma-next/contract/types';
3
4
  import {
4
5
  APP_SPACE_ID,
@@ -13,7 +14,6 @@ import { ifDefined } from '@prisma-next/utils/defined';
13
14
  import { notOk, ok, type Result } from '@prisma-next/utils/result';
14
15
  import { Command } from 'commander';
15
16
  import { relative } from 'pathe';
16
- import { loadConfig } from '../config-loader';
17
17
  import { createControlClient } from '../control-api/client';
18
18
  import {
19
19
  type CliStructuredError,
@@ -1,3 +1,4 @@
1
+ import { loadConfig } from '@prisma-next/config-loader';
1
2
  import type { LedgerEntryRecord } from '@prisma-next/contract/types';
2
3
  import type {
3
4
  ContractMarkerRecordLike,
@@ -17,7 +18,6 @@ import { ifDefined } from '@prisma-next/utils/defined';
17
18
  import { notOk, ok, type Result } from '@prisma-next/utils/result';
18
19
  import { dim, yellow } from 'colorette';
19
20
  import { Command } from 'commander';
20
- import { loadConfig } from '../config-loader';
21
21
  import { createControlClient } from '../control-api/client';
22
22
  import {
23
23
  CliStructuredError,
@@ -1,4 +1,5 @@
1
1
  import { readFile } from 'node:fs/promises';
2
+ import { loadConfig } from '@prisma-next/config-loader';
2
3
  import { EMPTY_CONTRACT_HASH } from '@prisma-next/migration-tools/constants';
3
4
  import { MigrationToolsError } from '@prisma-next/migration-tools/errors';
4
5
  import { findLatestMigration, isGraphNode } from '@prisma-next/migration-tools/migration-graph';
@@ -14,7 +15,6 @@ import {
14
15
  import { notOk, ok, type Result } from '@prisma-next/utils/result';
15
16
  import { Command } from 'commander';
16
17
  import { join } from 'pathe';
17
- import { loadConfig } from '../config-loader';
18
18
  import {
19
19
  CliStructuredError,
20
20
  errorFileNotFound,
@@ -1,4 +1,5 @@
1
1
  import { mkdir } from 'node:fs/promises';
2
+ import { loadConfig } from '@prisma-next/config-loader';
2
3
  import type { Contract } from '@prisma-next/contract/types';
3
4
  import { emit, getEmittedArtifactPaths } from '@prisma-next/emitter';
4
5
  import { createControlStack } from '@prisma-next/framework-components/control';
@@ -7,7 +8,6 @@ import { blindCast } from '@prisma-next/utils/casts';
7
8
  import { ifDefined } from '@prisma-next/utils/defined';
8
9
  import type { JsonObject } from '@prisma-next/utils/json';
9
10
  import { dirname, join } from 'pathe';
10
- import { loadConfig } from '../../config-loader';
11
11
  import { errorContractConfigMissing, errorRuntime } from '../../utils/cli-errors';
12
12
  import { queueEmitByOutput } from '../../utils/emit-queue';
13
13
  import { toExtensionInputs } from '../../utils/extension-pack-inputs';
@@ -272,6 +272,7 @@ function buildSkippedSchemaResult(member: ContractSpaceMember): VerifyDatabaseSc
272
272
  target: { expected: contract.target },
273
273
  schema: {
274
274
  issues: [],
275
+ schemaDiffIssues: [],
275
276
  root: {
276
277
  status: 'pass',
277
278
  kind: 'skipped',
@@ -0,0 +1,98 @@
1
+ import { readFile, writeFile } from 'node:fs/promises';
2
+ import { EOL } from 'node:os';
3
+ import { loadConfig } from '@prisma-next/config-loader';
4
+ import { type FormatOptions, format, PslFormatError } from '@prisma-next/psl-parser/format';
5
+ import { notOk, ok, type Result } from '@prisma-next/utils/result';
6
+ import { CliStructuredError, errorRuntime, errorUnexpected } from '../../utils/cli-errors';
7
+
8
+ export interface FormatOperationOptions {
9
+ readonly configPath?: string;
10
+ readonly eol?: string;
11
+ }
12
+
13
+ export interface FormatOperationResult {
14
+ readonly formatted: boolean;
15
+ readonly path?: string;
16
+ }
17
+
18
+ export function resolveNewline(
19
+ formatterNewline: 'LF' | 'CRLF' | undefined,
20
+ eol: string,
21
+ ): 'LF' | 'CRLF' {
22
+ if (formatterNewline !== undefined) {
23
+ return formatterNewline;
24
+ }
25
+ return eol === '\r\n' ? 'CRLF' : 'LF';
26
+ }
27
+
28
+ export async function executeFormat(
29
+ options: FormatOperationOptions,
30
+ ): Promise<Result<FormatOperationResult, CliStructuredError>> {
31
+ const eol = options.eol ?? EOL;
32
+
33
+ let config: Awaited<ReturnType<typeof loadConfig>>;
34
+ try {
35
+ config = await loadConfig(options.configPath);
36
+ } catch (error) {
37
+ if (CliStructuredError.is(error)) {
38
+ return notOk(error);
39
+ }
40
+ return notOk(errorUnexpected(error instanceof Error ? error.message : String(error)));
41
+ }
42
+
43
+ const source = config.contract?.source;
44
+ if (source?.sourceFormat !== 'psl') {
45
+ return ok({ formatted: false });
46
+ }
47
+
48
+ const inputPath = source.inputs?.[0];
49
+ if (inputPath === undefined) {
50
+ return ok({ formatted: false });
51
+ }
52
+
53
+ let contents: string;
54
+ try {
55
+ contents = await readFile(inputPath, 'utf-8');
56
+ } catch (error) {
57
+ return notOk(
58
+ errorRuntime('Failed to read contract source file', {
59
+ why: error instanceof Error ? error.message : String(error),
60
+ fix: `Check that ${inputPath} exists and is readable.`,
61
+ }),
62
+ );
63
+ }
64
+
65
+ const formatOptions: FormatOptions = {
66
+ indent: config.formatter?.indent ?? 2,
67
+ newline: resolveNewline(config.formatter?.newline, eol),
68
+ };
69
+
70
+ let formatted: string;
71
+ try {
72
+ formatted = format(contents, formatOptions);
73
+ } catch (error) {
74
+ if (error instanceof PslFormatError) {
75
+ return notOk(
76
+ errorRuntime('Cannot format PSL with parse errors', {
77
+ why: error.message,
78
+ fix: 'Fix the parse errors in your schema and try again.',
79
+ meta: { diagnostics: error.diagnostics },
80
+ }),
81
+ );
82
+ }
83
+ return notOk(errorUnexpected(error instanceof Error ? error.message : String(error)));
84
+ }
85
+
86
+ try {
87
+ await writeFile(inputPath, formatted, 'utf-8');
88
+ } catch (error) {
89
+ return notOk(
90
+ errorRuntime('Failed to write formatted contract source file', {
91
+ why: error instanceof Error ? error.message : String(error),
92
+ fix: `Check that ${inputPath} is writable.`,
93
+ }),
94
+ );
95
+ }
96
+
97
+ return ok({ formatted: true, path: inputPath });
98
+ }
@@ -1,4 +1,5 @@
1
1
  // CLI-specific exports
2
2
  export { createContractEmitCommand } from '../commands/contract-emit';
3
+ export { createFormatCommand } from '../commands/format';
3
4
  export type { LoadTsContractOptions } from '../load-ts-contract';
4
5
  export { loadContractFromTs } from '../load-ts-contract';
@@ -45,6 +45,7 @@
45
45
  import { readFileSync, realpathSync, writeFileSync } from 'node:fs';
46
46
  import type { Writable } from 'node:stream';
47
47
  import { fileURLToPath } from 'node:url';
48
+ import { loadConfig } from '@prisma-next/config-loader';
48
49
  import {
49
50
  CliStructuredError,
50
51
  errorMigrationCliInvalidConfigArg,
@@ -57,7 +58,6 @@ import type { MigrationMetadata } from '@prisma-next/migration-tools/metadata';
57
58
  import { buildMigrationArtifacts, type Migration } from '@prisma-next/migration-tools/migration';
58
59
  import { Cli, Command, Option, UsageError } from 'clipanion';
59
60
  import { dirname, join } from 'pathe';
60
- import { loadConfig } from './config-loader';
61
61
 
62
62
  /**
63
63
  * Constructor shape accepted by `MigrationCLI.run`. `Migration` subclasses
@@ -30,6 +30,7 @@ export function combineSchemaResults(
30
30
  let okAll = true;
31
31
  let firstFailure: VerifyDatabaseSchemaResult | undefined;
32
32
  let issues: VerifyDatabaseSchemaResult['schema']['issues'] = [];
33
+ let schemaDiffIssues: VerifyDatabaseSchemaResult['schema']['schemaDiffIssues'] = [];
33
34
  const counts = { pass: 0, warn: 0, fail: 0, totalNodes: 0 };
34
35
  const childRoots: Array<VerifyDatabaseSchemaResult['schema']['root']> = [];
35
36
  for (const [, result] of perSpace) {
@@ -38,6 +39,7 @@ export function combineSchemaResults(
38
39
  if (firstFailure === undefined) firstFailure = result;
39
40
  }
40
41
  issues = [...issues, ...result.schema.issues];
42
+ schemaDiffIssues = [...schemaDiffIssues, ...result.schema.schemaDiffIssues];
41
43
  counts.pass += result.schema.counts.pass;
42
44
  counts.warn += result.schema.counts.warn;
43
45
  counts.fail += result.schema.counts.fail;
@@ -65,6 +67,7 @@ export function combineSchemaResults(
65
67
  target: appResult.target,
66
68
  schema: {
67
69
  issues,
70
+ schemaDiffIssues,
68
71
  root: {
69
72
  status: okAll ? 'pass' : 'fail',
70
73
  kind: 'aggregate',
@@ -539,6 +539,14 @@ export function formatSchemaVerifyOutput(
539
539
  });
540
540
  lines.push(...treeLines);
541
541
 
542
+ if (result.schema.schemaDiffIssues.length > 0) {
543
+ lines.push('');
544
+ lines.push(formatRed('Schema drift:'));
545
+ for (const issue of result.schema.schemaDiffIssues) {
546
+ lines.push(` ${formatRed('✖')} ${issue.message}`);
547
+ }
548
+ }
549
+
542
550
  // Add counts and timings in verbose mode
543
551
  if (isVerbose(flags, 1)) {
544
552
  lines.push(`${formatDimText(` Total time: ${result.timings.total}ms`)}`);
@@ -1,9 +1,9 @@
1
1
  import { readFile } from 'node:fs/promises';
2
2
  import { relative, resolve } from 'node:path';
3
+ import { loadConfig } from '@prisma-next/config-loader';
3
4
  import { hasMigrations } from '@prisma-next/framework-components/control';
4
5
  import { notOk, ok, type Result } from '@prisma-next/utils/result';
5
6
  import type { Command } from 'commander';
6
- import { loadConfig } from '../config-loader';
7
7
  import { createControlClient } from '../control-api/client';
8
8
  import type { ControlClient } from '../control-api/types';
9
9
  import {