@prisma-next/cli 0.10.0 → 0.11.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +1 -1
- package/dist/{cli-errors-CF60g2cG.mjs → cli-errors-Djtz98Vm.mjs} +3 -3
- package/dist/cli-errors-Djtz98Vm.mjs.map +1 -0
- package/dist/cli.mjs +151 -12
- package/dist/cli.mjs.map +1 -1
- package/dist/{client-Brv4qlfB.mjs → client-oXO2WCPD.mjs} +6 -5
- package/dist/client-oXO2WCPD.mjs.map +1 -0
- package/dist/{command-helpers-D3vL5yi8.mjs → command-helpers-BSb0tRC8.mjs} +104 -10
- package/dist/command-helpers-BSb0tRC8.mjs.map +1 -0
- package/dist/commands/contract-emit.mjs +1 -1
- package/dist/commands/contract-infer.mjs +1 -1
- package/dist/commands/db-init.d.mts.map +1 -1
- package/dist/commands/db-init.mjs +19 -20
- package/dist/commands/db-init.mjs.map +1 -1
- package/dist/commands/db-schema.mjs +6 -10
- package/dist/commands/db-schema.mjs.map +1 -1
- package/dist/commands/db-sign.mjs +7 -11
- package/dist/commands/db-sign.mjs.map +1 -1
- package/dist/commands/db-update.d.mts.map +1 -1
- package/dist/commands/db-update.mjs +16 -17
- package/dist/commands/db-update.mjs.map +1 -1
- package/dist/commands/db-verify.mjs +1 -1
- package/dist/commands/migrate.d.mts +1 -1
- package/dist/commands/migrate.mjs +7 -11
- package/dist/commands/migrate.mjs.map +1 -1
- package/dist/commands/migration-check.mjs +4 -7
- package/dist/commands/migration-check.mjs.map +1 -1
- package/dist/commands/migration-graph.d.mts +1 -1
- package/dist/commands/migration-graph.mjs +6 -10
- package/dist/commands/migration-graph.mjs.map +1 -1
- package/dist/commands/migration-list.mjs +5 -9
- package/dist/commands/migration-list.mjs.map +1 -1
- package/dist/commands/migration-log.d.mts.map +1 -1
- package/dist/commands/migration-log.mjs +7 -10
- package/dist/commands/migration-log.mjs.map +1 -1
- package/dist/commands/migration-new.mjs +6 -10
- package/dist/commands/migration-new.mjs.map +1 -1
- package/dist/commands/migration-plan.d.mts +1 -1
- package/dist/commands/migration-plan.mjs +1 -1
- package/dist/commands/migration-show.d.mts +1 -1
- package/dist/commands/migration-show.mjs +8 -12
- package/dist/commands/migration-show.mjs.map +1 -1
- package/dist/commands/migration-status.d.mts +1 -1
- package/dist/commands/migration-status.d.mts.map +1 -1
- package/dist/commands/migration-status.mjs +36 -14
- package/dist/commands/migration-status.mjs.map +1 -1
- package/dist/commands/ref.d.mts +1 -1
- package/dist/commands/ref.mjs +9 -19
- package/dist/commands/ref.mjs.map +1 -1
- package/dist/{contract-emit-iynA3BCA.mjs → contract-emit-bcrpT-wD.mjs} +3 -3
- package/dist/{contract-emit-iynA3BCA.mjs.map → contract-emit-bcrpT-wD.mjs.map} +1 -1
- package/dist/{contract-emit-C3STUIBg.mjs → contract-emit-r4y8Zhf1.mjs} +7 -12
- package/dist/contract-emit-r4y8Zhf1.mjs.map +1 -0
- package/dist/{contract-infer-Cnj8G1E2.mjs → contract-infer-BmySmqVT.mjs} +8 -13
- package/dist/contract-infer-BmySmqVT.mjs.map +1 -0
- package/dist/{contract-space-aggregate-loader-pAc8CDfY.mjs → contract-space-aggregate-loader-BmNQwlws.mjs} +2 -2
- package/dist/{contract-space-aggregate-loader-pAc8CDfY.mjs.map → contract-space-aggregate-loader-BmNQwlws.mjs.map} +1 -1
- package/dist/{db-verify-D7cyH_zz.mjs → db-verify-BClPs3ph.mjs} +9 -13
- package/dist/db-verify-BClPs3ph.mjs.map +1 -0
- package/dist/exports/control-api.d.mts +1 -1
- package/dist/exports/control-api.mjs +2 -2
- package/dist/exports/index.mjs +2 -2
- package/dist/exports/init-output.mjs +1 -1
- package/dist/{framework-components-xFLFpZUO.mjs → framework-components-65gOHkHB.mjs} +2 -2
- package/dist/{framework-components-xFLFpZUO.mjs.map → framework-components-65gOHkHB.mjs.map} +1 -1
- package/dist/{global-flags-DGmw6Kqg.d.mts → global-flags-CdE7M0d9.d.mts} +4 -1
- package/dist/global-flags-CdE7M0d9.d.mts.map +1 -0
- package/dist/{graph-render-eJDcLWny.mjs → graph-render-DJVv0_uf.mjs} +1 -1
- package/dist/{graph-render-eJDcLWny.mjs.map → graph-render-DJVv0_uf.mjs.map} +1 -1
- package/dist/{init-eh2z5Tl6.mjs → init-BCJZPWE1.mjs} +547 -399
- package/dist/init-BCJZPWE1.mjs.map +1 -0
- package/dist/{inspect-live-schema-CWLK_lgs.mjs → inspect-live-schema-DSRbFoOL.mjs} +4 -4
- package/dist/{inspect-live-schema-CWLK_lgs.mjs.map → inspect-live-schema-DSRbFoOL.mjs.map} +1 -1
- package/dist/{migration-command-scaffold-CmXXC1UZ.mjs → migration-command-scaffold-Bzd9La5c.mjs} +4 -4
- package/dist/{migration-command-scaffold-CmXXC1UZ.mjs.map → migration-command-scaffold-Bzd9La5c.mjs.map} +1 -1
- package/dist/{migration-plan-CHyUlBV0.mjs → migration-plan-CFwqw3Gk.mjs} +8 -12
- package/dist/migration-plan-CFwqw3Gk.mjs.map +1 -0
- package/dist/{migration-types-D2FW63pr.d.mts → migration-types-BXWvz12q.d.mts} +1 -1
- package/dist/{migration-types-D2FW63pr.d.mts.map → migration-types-BXWvz12q.d.mts.map} +1 -1
- package/dist/{migrations-DyUf5lTt.mjs → migrations-CwZMa1Ck.mjs} +2 -2
- package/dist/{migrations-DyUf5lTt.mjs.map → migrations-CwZMa1Ck.mjs.map} +1 -1
- package/dist/{output-B60Gw5fu.mjs → output-BlsrGMEF.mjs} +1 -1
- package/dist/{output-B60Gw5fu.mjs.map → output-BlsrGMEF.mjs.map} +1 -1
- package/dist/quick-reference-mongo.md +1 -1
- package/dist/quick-reference-postgres.md +1 -1
- package/dist/readme-mongo.md +35 -0
- package/dist/readme-postgres.md +34 -0
- package/dist/{terminal-ui-XtOQsqe9.mjs → terminal-ui-BiB_8KNo.mjs} +131 -24
- package/dist/terminal-ui-BiB_8KNo.mjs.map +1 -0
- package/dist/{types-0aS865QN.d.mts → types--CqjMdk0.d.mts} +2 -2
- package/dist/{types-0aS865QN.d.mts.map → types--CqjMdk0.d.mts.map} +1 -1
- package/dist/{verify-D7ypCCe6.mjs → verify-Bom75OYI.mjs} +2 -2
- package/dist/{verify-D7ypCCe6.mjs.map → verify-Bom75OYI.mjs.map} +1 -1
- package/package.json +19 -17
- package/src/cli.ts +42 -0
- package/src/commands/contract-emit.ts +4 -4
- package/src/commands/contract-infer.ts +6 -6
- package/src/commands/db-init.ts +13 -5
- package/src/commands/db-schema.ts +4 -4
- package/src/commands/db-sign.ts +4 -4
- package/src/commands/db-update.ts +13 -5
- package/src/commands/db-verify.ts +5 -5
- package/src/commands/init/detect-package-manager.ts +15 -0
- package/src/commands/init/errors.ts +33 -2
- package/src/commands/init/index.ts +13 -5
- package/src/commands/init/init.ts +61 -32
- package/src/commands/init/inputs.ts +82 -5
- package/src/commands/init/output.ts +1 -1
- package/src/commands/init/{agent-skill-install.ts → skill-install.ts} +42 -31
- package/src/commands/init/templates/code-templates.ts +22 -22
- package/src/commands/init/templates/env.ts +8 -1
- package/src/commands/init/templates/quick-reference-mongo.md +1 -1
- package/src/commands/init/templates/quick-reference-postgres.md +1 -1
- 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/migrate.ts +4 -7
- package/src/commands/migration-check.ts +4 -4
- package/src/commands/migration-graph.ts +4 -4
- package/src/commands/migration-list.ts +4 -4
- package/src/commands/migration-log.ts +6 -5
- package/src/commands/migration-new.ts +4 -4
- package/src/commands/migration-plan.ts +4 -4
- package/src/commands/migration-show.ts +4 -4
- package/src/commands/migration-status.ts +49 -6
- package/src/commands/ref.ts +8 -8
- package/src/control-api/operations/apply-aggregate.ts +1 -0
- package/src/utils/cli-errors.ts +4 -0
- package/src/utils/command-helpers.ts +6 -2
- package/src/utils/global-flags.ts +105 -16
- package/src/utils/is-ci.ts +18 -0
- package/src/utils/telemetry.ts +141 -0
- package/src/utils/terminal-ui.ts +44 -23
- package/dist/cli-errors-CF60g2cG.mjs.map +0 -1
- package/dist/client-Brv4qlfB.mjs.map +0 -1
- package/dist/command-helpers-D3vL5yi8.mjs.map +0 -1
- package/dist/contract-emit-C3STUIBg.mjs.map +0 -1
- package/dist/contract-infer-Cnj8G1E2.mjs.map +0 -1
- package/dist/db-verify-D7cyH_zz.mjs.map +0 -1
- package/dist/errors-Cw6kyTyV.mjs +0 -56
- package/dist/errors-Cw6kyTyV.mjs.map +0 -1
- package/dist/global-flags-DGmw6Kqg.d.mts.map +0 -1
- package/dist/helpers-eqdN8tH6.mjs +0 -25
- package/dist/helpers-eqdN8tH6.mjs.map +0 -1
- package/dist/init-eh2z5Tl6.mjs.map +0 -1
- package/dist/migration-plan-CHyUlBV0.mjs.map +0 -1
- package/dist/result-handler-Bm_6dDYg.mjs +0 -25
- package/dist/result-handler-Bm_6dDYg.mjs.map +0 -1
- package/dist/terminal-ui-XtOQsqe9.mjs.map +0 -1
- /package/dist/{cli-errors-DdcjVLJV.d.mts → cli-errors-Czmx92Zy.d.mts} +0 -0
|
@@ -1,16 +1,178 @@
|
|
|
1
|
-
import { t as CliStructuredError } from "./cli-errors-
|
|
2
|
-
import {
|
|
3
|
-
import { t as TerminalUI } from "./terminal-ui-XtOQsqe9.mjs";
|
|
1
|
+
import { t as CliStructuredError } from "./cli-errors-Djtz98Vm.mjs";
|
|
2
|
+
import { a as isCI, i as formatErrorOutput, r as formatErrorJson, t as createTerminalUI } from "./terminal-ui-BiB_8KNo.mjs";
|
|
4
3
|
import { t as version } from "./cli.mjs";
|
|
5
|
-
import { i as renderInitOutro, n as buildNextSteps, r as formatInitJson, t as InitOutputSchema } from "./output-
|
|
4
|
+
import { i as renderInitOutro, n as buildNextSteps, r as formatInitJson, t as InitOutputSchema } from "./output-BlsrGMEF.mjs";
|
|
6
5
|
import { createRequire } from "node:module";
|
|
7
6
|
import { basename, dirname, extname, isAbsolute, join, normalize } from "pathe";
|
|
8
7
|
import * as clack from "@clack/prompts";
|
|
9
8
|
import { existsSync, mkdirSync, readFileSync, unlinkSync, writeFileSync } from "node:fs";
|
|
9
|
+
import { readUserConfig, resolveGating, writeUserConfig } from "@prisma-next/cli-telemetry";
|
|
10
10
|
import { execFile } from "node:child_process";
|
|
11
11
|
import { promisify } from "node:util";
|
|
12
12
|
import { detect, getUserAgent } from "package-manager-detector/detect";
|
|
13
13
|
import { applyEdits, modify, parse, printParseErrorCode } from "jsonc-parser";
|
|
14
|
+
//#region src/commands/init/detect-package-manager.ts
|
|
15
|
+
const KNOWN = new Set([
|
|
16
|
+
"pnpm",
|
|
17
|
+
"npm",
|
|
18
|
+
"yarn",
|
|
19
|
+
"bun",
|
|
20
|
+
"deno"
|
|
21
|
+
]);
|
|
22
|
+
/**
|
|
23
|
+
* Resolves the package manager `init` should drive for `add` / `install`
|
|
24
|
+
* commands. Tries, in order:
|
|
25
|
+
*
|
|
26
|
+
* 1. **`detect()`** — walks up from `cwd` looking for a lockfile, the
|
|
27
|
+
* `packageManager` field, the `devEngines.packageManager` field, or
|
|
28
|
+
* install metadata. This is the right answer whenever the user is
|
|
29
|
+
* anywhere inside an existing project, including a deep workspace
|
|
30
|
+
* subdirectory.
|
|
31
|
+
*
|
|
32
|
+
* 2. **`getUserAgent()`** — parses `npm_config_user_agent`, the env var
|
|
33
|
+
* every PM sets when it spawns a script. This catches the
|
|
34
|
+
* bare-directory case where there's no project to walk up to but the
|
|
35
|
+
* user invoked us via `pnpm dlx prisma-next init` / `bunx
|
|
36
|
+
* prisma-next init` / `yarn dlx …`. Same signal used by every
|
|
37
|
+
* `create-*` tool in the ecosystem (`create-vite`, `create-next-app`,
|
|
38
|
+
* `create-astro`, `@antfu/ni`, …).
|
|
39
|
+
*
|
|
40
|
+
* 3. **`npm`** — final fallback. Always present alongside Node.
|
|
41
|
+
*/
|
|
42
|
+
async function detectPackageManager(cwd) {
|
|
43
|
+
const detected = await detect({ cwd });
|
|
44
|
+
if (detected && KNOWN.has(detected.name)) return detected.name;
|
|
45
|
+
const userAgent = getUserAgent();
|
|
46
|
+
if (userAgent !== null && KNOWN.has(userAgent)) return userAgent;
|
|
47
|
+
return "npm";
|
|
48
|
+
}
|
|
49
|
+
function hasProjectManifest(cwd) {
|
|
50
|
+
return existsSync(join(cwd, "package.json")) || existsSync(join(cwd, "deno.json")) || existsSync(join(cwd, "deno.jsonc"));
|
|
51
|
+
}
|
|
52
|
+
function formatRunCommand(pm, bin, args) {
|
|
53
|
+
if (pm === "npm") return `npx ${bin} ${args}`;
|
|
54
|
+
if (pm === "deno") return `deno run npm:${bin} ${args}`;
|
|
55
|
+
return `${pm} ${bin} ${args}`;
|
|
56
|
+
}
|
|
57
|
+
function formatRunScriptCommand(pm, scriptName) {
|
|
58
|
+
switch (pm) {
|
|
59
|
+
case "deno": return `deno task ${scriptName}`;
|
|
60
|
+
case "bun": return `bun run ${scriptName}`;
|
|
61
|
+
case "pnpm": return `pnpm run ${scriptName}`;
|
|
62
|
+
case "yarn": return `yarn run ${scriptName}`;
|
|
63
|
+
default: return `npm run ${scriptName}`;
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
function formatAddArgs(pm, packages) {
|
|
67
|
+
if (pm === "deno") return ["add", ...packages.map((p) => `npm:${p}`)];
|
|
68
|
+
return ["add", ...packages];
|
|
69
|
+
}
|
|
70
|
+
function formatAddDevArgs(pm, packages) {
|
|
71
|
+
if (pm === "deno") return [
|
|
72
|
+
"add",
|
|
73
|
+
"--dev",
|
|
74
|
+
...packages.map((p) => `npm:${p}`)
|
|
75
|
+
];
|
|
76
|
+
return [
|
|
77
|
+
"add",
|
|
78
|
+
"-D",
|
|
79
|
+
...packages
|
|
80
|
+
];
|
|
81
|
+
}
|
|
82
|
+
//#endregion
|
|
83
|
+
//#region src/commands/init/detect-pnpm-catalog.ts
|
|
84
|
+
/**
|
|
85
|
+
* Walks up from `baseDir` looking for `pnpm-workspace.yaml`, then scans
|
|
86
|
+
* its top-level `catalog:` block for entries that match any of `packages`.
|
|
87
|
+
*
|
|
88
|
+
* Implements FR7.3 / Spec Decision 8 (honour-and-warn): when `init` runs
|
|
89
|
+
* inside a pnpm workspace whose catalog overrides one of the packages it
|
|
90
|
+
* installs, surface a structured warning so the user knows the catalog
|
|
91
|
+
* version (not the published `latest`) is what ended up in their
|
|
92
|
+
* `node_modules`. pnpm itself does this silently; the warning closes the
|
|
93
|
+
* "looks fine, must be wrong version six months later" gap.
|
|
94
|
+
*
|
|
95
|
+
* Notes / scope:
|
|
96
|
+
*
|
|
97
|
+
* - We only inspect the unnamed top-level `catalog:` block. pnpm also
|
|
98
|
+
* supports `catalogs:` (plural — *named* catalogs referenced via
|
|
99
|
+
* `catalog:foo` specifiers); those don't apply to a vanilla
|
|
100
|
+
* `pnpm add prisma-next` invocation, so we skip them.
|
|
101
|
+
* - We don't validate YAML syntax exhaustively. The file format pnpm
|
|
102
|
+
* ships is line-oriented and well-known; a minimal regex is more
|
|
103
|
+
* robust than depending on a YAML parser for one warning.
|
|
104
|
+
* - We don't compare against the registry's `latest` — pnpm uses the
|
|
105
|
+
* catalog version regardless, so the warning fires whenever a match
|
|
106
|
+
* exists. The user-facing copy explains how to opt out.
|
|
107
|
+
*/
|
|
108
|
+
function detectPnpmCatalogOverrides(baseDir, packages) {
|
|
109
|
+
const workspaceFile = findNearestPnpmWorkspaceFile(baseDir);
|
|
110
|
+
if (workspaceFile === null) return null;
|
|
111
|
+
const catalog = extractCatalogBlock(readFileSync(workspaceFile, "utf-8"));
|
|
112
|
+
if (catalog === null) return {
|
|
113
|
+
workspaceFile,
|
|
114
|
+
entries: []
|
|
115
|
+
};
|
|
116
|
+
const wanted = new Set(packages);
|
|
117
|
+
const entries = [];
|
|
118
|
+
for (const [name, version] of catalog) if (wanted.has(name)) entries.push({
|
|
119
|
+
name,
|
|
120
|
+
version
|
|
121
|
+
});
|
|
122
|
+
return {
|
|
123
|
+
workspaceFile,
|
|
124
|
+
entries
|
|
125
|
+
};
|
|
126
|
+
}
|
|
127
|
+
function findNearestPnpmWorkspaceFile(baseDir) {
|
|
128
|
+
let dir = baseDir;
|
|
129
|
+
let prev = "";
|
|
130
|
+
while (dir !== prev) {
|
|
131
|
+
const candidate = join(dir, "pnpm-workspace.yaml");
|
|
132
|
+
if (existsSync(candidate)) return candidate;
|
|
133
|
+
prev = dir;
|
|
134
|
+
dir = dirname(dir);
|
|
135
|
+
}
|
|
136
|
+
return null;
|
|
137
|
+
}
|
|
138
|
+
/**
|
|
139
|
+
* Returns the entries inside the top-level `catalog:` block as `[name, version]`
|
|
140
|
+
* pairs in document order, or `null` when no `catalog:` block exists.
|
|
141
|
+
*
|
|
142
|
+
* The parser is intentionally minimal: it reads line-by-line, locates the
|
|
143
|
+
* top-level `catalog:` line (no leading whitespace), then collects every
|
|
144
|
+
* subsequent indented line of the form `<key>: <value>` until the next
|
|
145
|
+
* top-level key (or end of file). Quotes around `<key>` and `<value>`
|
|
146
|
+
* are stripped; comments (`#…`) are ignored.
|
|
147
|
+
*/
|
|
148
|
+
function extractCatalogBlock(contents) {
|
|
149
|
+
const lines = contents.split(/\r?\n/);
|
|
150
|
+
const startIdx = lines.findIndex((line) => /^catalog\s*:\s*$/.test(line));
|
|
151
|
+
if (startIdx === -1) return null;
|
|
152
|
+
const entries = [];
|
|
153
|
+
for (let i = startIdx + 1; i < lines.length; i++) {
|
|
154
|
+
const raw = lines[i] ?? "";
|
|
155
|
+
if (raw.trim() === "" || /^\s*#/.test(raw)) continue;
|
|
156
|
+
if (!/^\s/.test(raw)) break;
|
|
157
|
+
const match = raw.match(/^\s+(?:'([^']+)'|"([^"]+)"|([^:\s'"]+))\s*:\s*(.*?)\s*(?:#.*)?$/);
|
|
158
|
+
if (!match) continue;
|
|
159
|
+
const name = match[1] ?? match[2] ?? match[3];
|
|
160
|
+
if (name === void 0) continue;
|
|
161
|
+
const version = stripQuotes((match[4] ?? "").trim());
|
|
162
|
+
if (version === "") continue;
|
|
163
|
+
entries.push([name, version]);
|
|
164
|
+
}
|
|
165
|
+
return entries;
|
|
166
|
+
}
|
|
167
|
+
function stripQuotes(value) {
|
|
168
|
+
if (value.length >= 2) {
|
|
169
|
+
const first = value[0];
|
|
170
|
+
const last = value[value.length - 1];
|
|
171
|
+
if (first === "\"" && last === "\"" || first === "'" && last === "'") return value.slice(1, -1);
|
|
172
|
+
}
|
|
173
|
+
return value;
|
|
174
|
+
}
|
|
175
|
+
//#endregion
|
|
14
176
|
//#region src/commands/init/errors.ts
|
|
15
177
|
/**
|
|
16
178
|
* Re-init in non-interactive mode without `--force`. Distinct from the
|
|
@@ -70,6 +232,26 @@ function errorInitInvalidFlagValue(options) {
|
|
|
70
232
|
});
|
|
71
233
|
}
|
|
72
234
|
/**
|
|
235
|
+
* `--authoring` and `--schema-path` disagree on file extension (e.g. PSL
|
|
236
|
+
* authoring with a `.ts` path). Surfaces before any scaffold files are
|
|
237
|
+
* written so the project tree stays untouched.
|
|
238
|
+
*/
|
|
239
|
+
function errorInitAuthoringSchemaPathMismatch(options) {
|
|
240
|
+
const expectedAuthoring = options.expectedExtension === ".ts" ? "typescript" : "psl";
|
|
241
|
+
return new CliStructuredError("5014", "Authoring and schema path do not match", {
|
|
242
|
+
domain: "CLI",
|
|
243
|
+
why: `\`--authoring ${options.authoring}\` requires a schema file ending in ${options.expectedExtension}, but \`--schema-path ${options.schemaPath}\` ends in ${options.actualExtension}.`,
|
|
244
|
+
fix: `Use a matching pair, for example \`--authoring ${expectedAuthoring} --schema-path <path>${options.expectedExtension}\`, or change \`--authoring\` to match the path you supplied. You can also omit \`--schema-path\` to use the default for the chosen authoring.`,
|
|
245
|
+
docsUrl: "https://prisma-next.dev/docs/cli/init",
|
|
246
|
+
meta: {
|
|
247
|
+
authoring: options.authoring,
|
|
248
|
+
schemaPath: options.schemaPath,
|
|
249
|
+
actualExtension: options.actualExtension,
|
|
250
|
+
expectedExtension: options.expectedExtension
|
|
251
|
+
}
|
|
252
|
+
});
|
|
253
|
+
}
|
|
254
|
+
/**
|
|
73
255
|
* The user cancelled an interactive prompt (Ctrl-C, escape, declined a
|
|
74
256
|
* selection). Distinct from `errorInitReinitNeedsForce` because that path
|
|
75
257
|
* applies to non-interactive mode where the user was never given the
|
|
@@ -206,367 +388,37 @@ function errorInitEmitFailed(options) {
|
|
|
206
388
|
domain: "CLI",
|
|
207
389
|
why: `\`prisma-next contract emit\` failed: ${options.cause}`,
|
|
208
390
|
fix: `Inspect your contract file, fix the underlying issue, then re-run \`${options.emitCommand}\`. Pass \`-v\` for the full error envelope.`,
|
|
209
|
-
docsUrl: "https://prisma-next.dev/docs/cli/contract-emit",
|
|
210
|
-
meta: {
|
|
211
|
-
filesWritten: options.filesWritten,
|
|
212
|
-
cause: options.cause
|
|
213
|
-
}
|
|
214
|
-
});
|
|
215
|
-
}
|
|
216
|
-
/**
|
|
217
|
-
* The project-level agent-skill install (`npx skills add
|
|
218
|
-
* prisma/prisma-next#v<version>`) failed after a successful dependency
|
|
219
|
-
* install + emit. The project's scaffold remains on disk; the user
|
|
220
|
-
* can either fix the underlying issue (network, registry, PATH) and
|
|
221
|
-
* run the install command manually, or re-run `init --no-skill` to
|
|
222
|
-
* proceed without the skill.
|
|
223
|
-
*
|
|
224
|
-
* Non-rolling-back, matching the existing install/emit failure
|
|
225
|
-
* semantics. Maps to exit code `6 = SKILL_INSTALL_FAILED`.
|
|
226
|
-
*/
|
|
227
|
-
function errorInitSkillInstallFailed(options) {
|
|
228
|
-
return new CliStructuredError("5013", "Failed to install Prisma Next skills", {
|
|
229
|
-
domain: "CLI",
|
|
230
|
-
why: `\`${options.skillInstallCommand}\` exited with an error: ${options.cause}`,
|
|
231
|
-
fix: `Either:
|
|
232
|
-
- Re-run \`prisma-next init --no-skill${options.filesWritten.length > 0 ? " --force" : ""}\` to skip the skill install for this run, or\n - Fix the underlying issue (network, npm registry, \`npx skills\` on PATH) and install manually:\n ${options.skillInstallCommand}`,
|
|
233
|
-
docsUrl: "https://prisma-next.dev/docs/cli/init#agent-skill",
|
|
234
|
-
meta: {
|
|
235
|
-
filesWritten: options.filesWritten,
|
|
236
|
-
skillInstallCommand: options.skillInstallCommand,
|
|
237
|
-
cause: options.cause
|
|
238
|
-
}
|
|
239
|
-
});
|
|
240
|
-
}
|
|
241
|
-
//#endregion
|
|
242
|
-
//#region src/commands/init/agent-skill-install.ts
|
|
243
|
-
const exec = promisify(execFile);
|
|
244
|
-
/**
|
|
245
|
-
* Default base for the GitHub-URL form `<owner>/<repo>` consumed by
|
|
246
|
-
* upstream `skills add`. Each `SkillSource` joins this base with its
|
|
247
|
-
* own subpath (and optional `#ref` for version-pinned clusters).
|
|
248
|
-
*/
|
|
249
|
-
const DEFAULT_AGENT_SKILL_BASE = "prisma/prisma-next";
|
|
250
|
-
const DEFAULT_AGENT_SKILL_SOURCES = [
|
|
251
|
-
{
|
|
252
|
-
subpath: "skills",
|
|
253
|
-
ref: "cli",
|
|
254
|
-
description: "usage skills (version-locked to installed Prisma Next)"
|
|
255
|
-
},
|
|
256
|
-
{
|
|
257
|
-
subpath: "skills/upgrade",
|
|
258
|
-
ref: null,
|
|
259
|
-
description: "upgrade skill (always tracks `main`)"
|
|
260
|
-
},
|
|
261
|
-
{
|
|
262
|
-
subpath: "skills/extension-author",
|
|
263
|
-
ref: null,
|
|
264
|
-
description: "extension-author skill (always tracks `main`)"
|
|
265
|
-
}
|
|
266
|
-
];
|
|
267
|
-
/**
|
|
268
|
-
* Test-only escape hatch for pinning the install base to a local
|
|
269
|
-
* checkout. Production runs leave this unset, so installs always use
|
|
270
|
-
* `DEFAULT_AGENT_SKILL_BASE`.
|
|
271
|
-
*
|
|
272
|
-
* When set to an absolute filesystem path (typical for tests), the
|
|
273
|
-
* `#ref` fragment is dropped — local-path mode in upstream's CLI does
|
|
274
|
-
* not accept refs, and the local clone has whatever content the test
|
|
275
|
-
* checked into it anyway. When set to anything else (e.g. a fork name
|
|
276
|
-
* `myuser/prisma-next`), the ref policy is preserved.
|
|
277
|
-
*/
|
|
278
|
-
function resolveAgentSkillBase() {
|
|
279
|
-
const override = process.env["PRISMA_NEXT_SKILLS_BASE"]?.trim();
|
|
280
|
-
return override && override.length > 0 ? override : DEFAULT_AGENT_SKILL_BASE;
|
|
281
|
-
}
|
|
282
|
-
function isLocalPath(base) {
|
|
283
|
-
return base.startsWith("/") || /^[a-zA-Z]:[\\/]/.test(base);
|
|
284
|
-
}
|
|
285
|
-
/**
|
|
286
|
-
* Build the `<base>/<subpath>[#ref]` URL the `skills` CLI will
|
|
287
|
-
* resolve. Exported for unit tests so the per-source format can be
|
|
288
|
-
* asserted without going through the full install loop.
|
|
289
|
-
*/
|
|
290
|
-
function formatSkillSourceUrl(source) {
|
|
291
|
-
const base = resolveAgentSkillBase();
|
|
292
|
-
const url = `${base}/${source.subpath}`;
|
|
293
|
-
if (source.ref === null) return url;
|
|
294
|
-
if (isLocalPath(base)) return url;
|
|
295
|
-
if (source.ref === "cli") return `${url}#v${version}`;
|
|
296
|
-
return url;
|
|
297
|
-
}
|
|
298
|
-
/**
|
|
299
|
-
* The skill-install command for one source, formatted for the
|
|
300
|
-
* project's detected package manager. `npx`/`pnpm dlx`/`bunx` are
|
|
301
|
-
* interchangeable to the user; we pick the variant that matches the
|
|
302
|
-
* rest of the install step so a single project consistently uses one
|
|
303
|
-
* runner.
|
|
304
|
-
*
|
|
305
|
-
* `--all` auto-selects every skill in the cluster and every detected
|
|
306
|
-
* agent runtime, skipping the multi-select prompts the `skills` CLI
|
|
307
|
-
* shows by default. A non-interactive scaffold step cannot present
|
|
308
|
-
* prompts.
|
|
309
|
-
*
|
|
310
|
-
* Exported for unit tests so the per-PM dispatch can be asserted
|
|
311
|
-
* without a live subprocess.
|
|
312
|
-
*/
|
|
313
|
-
function formatSkillInstallCommand(pm, source) {
|
|
314
|
-
return formatPackageManagerCommand(pm, [
|
|
315
|
-
"skills@latest",
|
|
316
|
-
"add",
|
|
317
|
-
formatSkillSourceUrl(source),
|
|
318
|
-
"--all"
|
|
319
|
-
]);
|
|
320
|
-
}
|
|
321
|
-
/**
|
|
322
|
-
* `skills add --all` should cover Claude Code, but upstream currently skips
|
|
323
|
-
* project-local Claude symlinks when `.claude/` does not already exist. Run
|
|
324
|
-
* the explicit Claude Code install as well so fresh projects get
|
|
325
|
-
* `.claude/skills` without asking users to create that folder first.
|
|
326
|
-
*/
|
|
327
|
-
function formatClaudeSkillInstallCommand(pm, source) {
|
|
328
|
-
return formatPackageManagerCommand(pm, [
|
|
329
|
-
"skills@latest",
|
|
330
|
-
"add",
|
|
331
|
-
formatSkillSourceUrl(source),
|
|
332
|
-
"--agent",
|
|
333
|
-
"claude-code",
|
|
334
|
-
"--skill",
|
|
335
|
-
"'*'",
|
|
336
|
-
"-y"
|
|
337
|
-
]);
|
|
338
|
-
}
|
|
339
|
-
function formatPackageManagerCommand(pm, args) {
|
|
340
|
-
switch (pm) {
|
|
341
|
-
case "pnpm": return `pnpm dlx ${args.join(" ")}`;
|
|
342
|
-
case "yarn": return `yarn dlx ${args.join(" ")}`;
|
|
343
|
-
case "bun": return `bunx ${args.join(" ")}`;
|
|
344
|
-
case "deno": return `deno run -A npm:${args.join(" ")}`;
|
|
345
|
-
case "npm": return `npx ${args.join(" ")}`;
|
|
346
|
-
}
|
|
347
|
-
}
|
|
348
|
-
/**
|
|
349
|
-
* Parse the project-pm-formatted command into an exec call. The
|
|
350
|
-
* format-then-parse split keeps the user-facing command string the same
|
|
351
|
-
* as the surface the structured error advertises, so a user who copies
|
|
352
|
-
* the error's `fix` line gets the same invocation that init just
|
|
353
|
-
* attempted. Single quotes are preserved in the display form so `*` is
|
|
354
|
-
* safe to copy into a shell, then stripped before `execFile`.
|
|
355
|
-
*/
|
|
356
|
-
function commandToExec(command) {
|
|
357
|
-
const tokens = (command.match(/'[^']*'|\S+/g) ?? []).map((token) => token.startsWith("'") && token.endsWith("'") ? token.slice(1, -1) : token);
|
|
358
|
-
return {
|
|
359
|
-
file: tokens[0] ?? "npx",
|
|
360
|
-
args: tokens.slice(1)
|
|
361
|
-
};
|
|
362
|
-
}
|
|
363
|
-
/**
|
|
364
|
-
* Runs the project-level skill install for every source in
|
|
365
|
-
* `DEFAULT_AGENT_SKILL_SOURCES`, in order. Returns
|
|
366
|
-
* `{ ok: true, commands }` on success; throws a structured
|
|
367
|
-
* `errorInitSkillInstallFailed` on the first failure (subsequent
|
|
368
|
-
* sources are not attempted — the user opted into Prisma Next by
|
|
369
|
-
* running `init` and a partial install would leave the project in an
|
|
370
|
-
* ambiguous state). The throw is intentionally fatal — project-level
|
|
371
|
-
* skill install is unconditional (modulo `--no-skill`).
|
|
372
|
-
*/
|
|
373
|
-
async function runProjectLevelSkillInstall(ctx) {
|
|
374
|
-
const commands = [];
|
|
375
|
-
const installCommands = DEFAULT_AGENT_SKILL_SOURCES.flatMap((source) => [formatSkillInstallCommand(ctx.pm, source), formatClaudeSkillInstallCommand(ctx.pm, source)]);
|
|
376
|
-
for (const command of installCommands) {
|
|
377
|
-
const { file, args } = commandToExec(command);
|
|
378
|
-
try {
|
|
379
|
-
await exec(file, args, { cwd: ctx.baseDir });
|
|
380
|
-
commands.push(command);
|
|
381
|
-
} catch (err) {
|
|
382
|
-
throw errorInitSkillInstallFailed({
|
|
383
|
-
skillInstallCommand: command,
|
|
384
|
-
filesWritten: ctx.filesWritten,
|
|
385
|
-
cause: redactSecrets$1(readChildStderr$1(err)) || (err instanceof Error ? err.message : String(err))
|
|
386
|
-
});
|
|
387
|
-
}
|
|
388
|
-
}
|
|
389
|
-
return {
|
|
390
|
-
ok: true,
|
|
391
|
-
commands
|
|
392
|
-
};
|
|
393
|
-
}
|
|
394
|
-
function readChildStderr$1(err) {
|
|
395
|
-
if (err instanceof Error && "stderr" in err) return String(err.stderr ?? "");
|
|
396
|
-
return "";
|
|
397
|
-
}
|
|
398
|
-
/**
|
|
399
|
-
* Strips credentials from a `scheme://user:pass@host/...` URL anywhere
|
|
400
|
-
* in `stderr`. Package-manager stderr regularly contains credentialed
|
|
401
|
-
* registry URLs (private npm registries, GitHub Packages tokens), and
|
|
402
|
-
* those bubble into the structured `errorInitSkillInstallFailed`
|
|
403
|
-
* envelope, which ends up in logs and CI output. Redact at the
|
|
404
|
-
* boundary so we never re-emit a secret.
|
|
405
|
-
*
|
|
406
|
-
* Exported for unit tests.
|
|
407
|
-
*/
|
|
408
|
-
function redactSecrets$1(stderr) {
|
|
409
|
-
if (!stderr) return stderr;
|
|
410
|
-
return stderr.replace(/([a-zA-Z][a-zA-Z0-9+.-]*:\/\/)([^/@\s]+)@/g, "$1***@");
|
|
411
|
-
}
|
|
412
|
-
/**
|
|
413
|
-
* Hand-rolled skill stub path that init must not leave behind. Removed
|
|
414
|
-
* on every init run so a project's `.agents/skills/prisma-next/` does
|
|
415
|
-
* not shadow the installed Prisma Next skill cluster.
|
|
416
|
-
*/
|
|
417
|
-
const LEGACY_SKILL_FILE = ".agents/skills/prisma-next/SKILL.md";
|
|
418
|
-
//#endregion
|
|
419
|
-
//#region src/commands/init/detect-package-manager.ts
|
|
420
|
-
const KNOWN = new Set([
|
|
421
|
-
"pnpm",
|
|
422
|
-
"npm",
|
|
423
|
-
"yarn",
|
|
424
|
-
"bun",
|
|
425
|
-
"deno"
|
|
426
|
-
]);
|
|
427
|
-
/**
|
|
428
|
-
* Resolves the package manager `init` should drive for `add` / `install`
|
|
429
|
-
* commands. Tries, in order:
|
|
430
|
-
*
|
|
431
|
-
* 1. **`detect()`** — walks up from `cwd` looking for a lockfile, the
|
|
432
|
-
* `packageManager` field, the `devEngines.packageManager` field, or
|
|
433
|
-
* install metadata. This is the right answer whenever the user is
|
|
434
|
-
* anywhere inside an existing project, including a deep workspace
|
|
435
|
-
* subdirectory.
|
|
436
|
-
*
|
|
437
|
-
* 2. **`getUserAgent()`** — parses `npm_config_user_agent`, the env var
|
|
438
|
-
* every PM sets when it spawns a script. This catches the
|
|
439
|
-
* bare-directory case where there's no project to walk up to but the
|
|
440
|
-
* user invoked us via `pnpm dlx prisma-next init` / `bunx
|
|
441
|
-
* prisma-next init` / `yarn dlx …`. Same signal used by every
|
|
442
|
-
* `create-*` tool in the ecosystem (`create-vite`, `create-next-app`,
|
|
443
|
-
* `create-astro`, `@antfu/ni`, …).
|
|
444
|
-
*
|
|
445
|
-
* 3. **`npm`** — final fallback. Always present alongside Node.
|
|
446
|
-
*/
|
|
447
|
-
async function detectPackageManager(cwd) {
|
|
448
|
-
const detected = await detect({ cwd });
|
|
449
|
-
if (detected && KNOWN.has(detected.name)) return detected.name;
|
|
450
|
-
const userAgent = getUserAgent();
|
|
451
|
-
if (userAgent !== null && KNOWN.has(userAgent)) return userAgent;
|
|
452
|
-
return "npm";
|
|
453
|
-
}
|
|
454
|
-
function hasProjectManifest(cwd) {
|
|
455
|
-
return existsSync(join(cwd, "package.json")) || existsSync(join(cwd, "deno.json")) || existsSync(join(cwd, "deno.jsonc"));
|
|
456
|
-
}
|
|
457
|
-
function formatRunCommand(pm, bin, args) {
|
|
458
|
-
if (pm === "npm") return `npx ${bin} ${args}`;
|
|
459
|
-
if (pm === "deno") return `deno run npm:${bin} ${args}`;
|
|
460
|
-
return `${pm} ${bin} ${args}`;
|
|
461
|
-
}
|
|
462
|
-
function formatAddArgs(pm, packages) {
|
|
463
|
-
if (pm === "deno") return ["add", ...packages.map((p) => `npm:${p}`)];
|
|
464
|
-
return ["add", ...packages];
|
|
465
|
-
}
|
|
466
|
-
function formatAddDevArgs(pm, packages) {
|
|
467
|
-
if (pm === "deno") return [
|
|
468
|
-
"add",
|
|
469
|
-
"--dev",
|
|
470
|
-
...packages.map((p) => `npm:${p}`)
|
|
471
|
-
];
|
|
472
|
-
return [
|
|
473
|
-
"add",
|
|
474
|
-
"-D",
|
|
475
|
-
...packages
|
|
476
|
-
];
|
|
477
|
-
}
|
|
478
|
-
//#endregion
|
|
479
|
-
//#region src/commands/init/detect-pnpm-catalog.ts
|
|
480
|
-
/**
|
|
481
|
-
* Walks up from `baseDir` looking for `pnpm-workspace.yaml`, then scans
|
|
482
|
-
* its top-level `catalog:` block for entries that match any of `packages`.
|
|
483
|
-
*
|
|
484
|
-
* Implements FR7.3 / Spec Decision 8 (honour-and-warn): when `init` runs
|
|
485
|
-
* inside a pnpm workspace whose catalog overrides one of the packages it
|
|
486
|
-
* installs, surface a structured warning so the user knows the catalog
|
|
487
|
-
* version (not the published `latest`) is what ended up in their
|
|
488
|
-
* `node_modules`. pnpm itself does this silently; the warning closes the
|
|
489
|
-
* "looks fine, must be wrong version six months later" gap.
|
|
490
|
-
*
|
|
491
|
-
* Notes / scope:
|
|
492
|
-
*
|
|
493
|
-
* - We only inspect the unnamed top-level `catalog:` block. pnpm also
|
|
494
|
-
* supports `catalogs:` (plural — *named* catalogs referenced via
|
|
495
|
-
* `catalog:foo` specifiers); those don't apply to a vanilla
|
|
496
|
-
* `pnpm add prisma-next` invocation, so we skip them.
|
|
497
|
-
* - We don't validate YAML syntax exhaustively. The file format pnpm
|
|
498
|
-
* ships is line-oriented and well-known; a minimal regex is more
|
|
499
|
-
* robust than depending on a YAML parser for one warning.
|
|
500
|
-
* - We don't compare against the registry's `latest` — pnpm uses the
|
|
501
|
-
* catalog version regardless, so the warning fires whenever a match
|
|
502
|
-
* exists. The user-facing copy explains how to opt out.
|
|
503
|
-
*/
|
|
504
|
-
function detectPnpmCatalogOverrides(baseDir, packages) {
|
|
505
|
-
const workspaceFile = findNearestPnpmWorkspaceFile(baseDir);
|
|
506
|
-
if (workspaceFile === null) return null;
|
|
507
|
-
const catalog = extractCatalogBlock(readFileSync(workspaceFile, "utf-8"));
|
|
508
|
-
if (catalog === null) return {
|
|
509
|
-
workspaceFile,
|
|
510
|
-
entries: []
|
|
511
|
-
};
|
|
512
|
-
const wanted = new Set(packages);
|
|
513
|
-
const entries = [];
|
|
514
|
-
for (const [name, version] of catalog) if (wanted.has(name)) entries.push({
|
|
515
|
-
name,
|
|
516
|
-
version
|
|
391
|
+
docsUrl: "https://prisma-next.dev/docs/cli/contract-emit",
|
|
392
|
+
meta: {
|
|
393
|
+
filesWritten: options.filesWritten,
|
|
394
|
+
cause: options.cause
|
|
395
|
+
}
|
|
517
396
|
});
|
|
518
|
-
return {
|
|
519
|
-
workspaceFile,
|
|
520
|
-
entries
|
|
521
|
-
};
|
|
522
|
-
}
|
|
523
|
-
function findNearestPnpmWorkspaceFile(baseDir) {
|
|
524
|
-
let dir = baseDir;
|
|
525
|
-
let prev = "";
|
|
526
|
-
while (dir !== prev) {
|
|
527
|
-
const candidate = join(dir, "pnpm-workspace.yaml");
|
|
528
|
-
if (existsSync(candidate)) return candidate;
|
|
529
|
-
prev = dir;
|
|
530
|
-
dir = dirname(dir);
|
|
531
|
-
}
|
|
532
|
-
return null;
|
|
533
397
|
}
|
|
534
398
|
/**
|
|
535
|
-
*
|
|
536
|
-
*
|
|
399
|
+
* The project-level skills install (`npx skills add
|
|
400
|
+
* prisma/prisma-next#v<version>`) failed after a successful dependency
|
|
401
|
+
* install + emit. The project's scaffold remains on disk; the user
|
|
402
|
+
* can either fix the underlying issue (network, registry, PATH) and
|
|
403
|
+
* run the install command manually, or re-run `init --no-skill` to
|
|
404
|
+
* proceed without the skill.
|
|
537
405
|
*
|
|
538
|
-
*
|
|
539
|
-
*
|
|
540
|
-
* subsequent indented line of the form `<key>: <value>` until the next
|
|
541
|
-
* top-level key (or end of file). Quotes around `<key>` and `<value>`
|
|
542
|
-
* are stripped; comments (`#…`) are ignored.
|
|
406
|
+
* Non-rolling-back, matching the existing install/emit failure
|
|
407
|
+
* semantics. Maps to exit code `6 = SKILL_INSTALL_FAILED`.
|
|
543
408
|
*/
|
|
544
|
-
function
|
|
545
|
-
|
|
546
|
-
|
|
547
|
-
|
|
548
|
-
|
|
549
|
-
|
|
550
|
-
|
|
551
|
-
|
|
552
|
-
|
|
553
|
-
|
|
554
|
-
|
|
555
|
-
|
|
556
|
-
|
|
557
|
-
const version = stripQuotes((match[4] ?? "").trim());
|
|
558
|
-
if (version === "") continue;
|
|
559
|
-
entries.push([name, version]);
|
|
560
|
-
}
|
|
561
|
-
return entries;
|
|
562
|
-
}
|
|
563
|
-
function stripQuotes(value) {
|
|
564
|
-
if (value.length >= 2) {
|
|
565
|
-
const first = value[0];
|
|
566
|
-
const last = value[value.length - 1];
|
|
567
|
-
if (first === "\"" && last === "\"" || first === "'" && last === "'") return value.slice(1, -1);
|
|
568
|
-
}
|
|
569
|
-
return value;
|
|
409
|
+
function errorInitSkillInstallFailed(options) {
|
|
410
|
+
return new CliStructuredError("5013", "Failed to install Prisma Next skills", {
|
|
411
|
+
domain: "CLI",
|
|
412
|
+
why: `\`${options.skillInstallCommand}\` exited with an error: ${options.cause}`,
|
|
413
|
+
fix: `Either:
|
|
414
|
+
- Re-run \`prisma-next init --no-skill${options.filesWritten.length > 0 ? " --force" : ""}\` to skip the skill install for this run, or\n - Fix the underlying issue (network, npm registry, \`npx skills\` on PATH) and install manually:\n ${options.skillInstallCommand}`,
|
|
415
|
+
docsUrl: "https://prisma-next.dev/docs/cli/init#skills",
|
|
416
|
+
meta: {
|
|
417
|
+
filesWritten: options.filesWritten,
|
|
418
|
+
skillInstallCommand: options.skillInstallCommand,
|
|
419
|
+
cause: options.cause
|
|
420
|
+
}
|
|
421
|
+
});
|
|
570
422
|
}
|
|
571
423
|
//#endregion
|
|
572
424
|
//#region src/commands/init/hygiene-gitattributes.ts
|
|
@@ -814,18 +666,20 @@ function schemaSample(target, authoring) {
|
|
|
814
666
|
function schemaSamplePslPostgres() {
|
|
815
667
|
return `\`\`\`prisma
|
|
816
668
|
model User {
|
|
817
|
-
id
|
|
818
|
-
email
|
|
819
|
-
|
|
669
|
+
id Int @id @default(autoincrement())
|
|
670
|
+
email String @unique
|
|
671
|
+
username String?
|
|
672
|
+
name String?
|
|
820
673
|
}
|
|
821
674
|
\`\`\``;
|
|
822
675
|
}
|
|
823
676
|
function schemaSamplePslMongo() {
|
|
824
677
|
return `\`\`\`prisma
|
|
825
678
|
model User {
|
|
826
|
-
id
|
|
827
|
-
email
|
|
828
|
-
|
|
679
|
+
id ObjectId @id @map("_id")
|
|
680
|
+
email String @unique
|
|
681
|
+
username String?
|
|
682
|
+
name String?
|
|
829
683
|
@@map("users")
|
|
830
684
|
}
|
|
831
685
|
\`\`\``;
|
|
@@ -833,17 +687,16 @@ model User {
|
|
|
833
687
|
function schemaSampleTsPostgres() {
|
|
834
688
|
return `\`\`\`typescript
|
|
835
689
|
import { defineContract } from '@prisma-next/postgres/contract-builder';
|
|
836
|
-
import sqlFamily from '@prisma-next/postgres/family';
|
|
837
|
-
import postgresTarget from '@prisma-next/postgres/target';
|
|
838
690
|
|
|
839
691
|
export const contract = defineContract(
|
|
840
|
-
{
|
|
692
|
+
{},
|
|
841
693
|
({ field, model }) => ({
|
|
842
694
|
models: {
|
|
843
695
|
User: model('User', {
|
|
844
696
|
fields: {
|
|
845
697
|
id: field.id.uuidv7(),
|
|
846
698
|
email: field.text().unique(),
|
|
699
|
+
username: field.text().optional(),
|
|
847
700
|
name: field.text().optional(),
|
|
848
701
|
},
|
|
849
702
|
}),
|
|
@@ -855,11 +708,9 @@ export const contract = defineContract(
|
|
|
855
708
|
function schemaSampleTsMongo() {
|
|
856
709
|
return `\`\`\`typescript
|
|
857
710
|
import { defineContract } from '@prisma-next/mongo/contract-builder';
|
|
858
|
-
import mongoFamily from '@prisma-next/mongo/family';
|
|
859
|
-
import mongoTarget from '@prisma-next/mongo/target';
|
|
860
711
|
|
|
861
712
|
export const contract = defineContract(
|
|
862
|
-
{
|
|
713
|
+
{},
|
|
863
714
|
({ field, model }) => ({
|
|
864
715
|
models: {
|
|
865
716
|
User: model('User', {
|
|
@@ -867,6 +718,7 @@ export const contract = defineContract(
|
|
|
867
718
|
fields: {
|
|
868
719
|
_id: field.objectId(),
|
|
869
720
|
email: field.string(),
|
|
721
|
+
username: field.string().optional(),
|
|
870
722
|
name: field.string().optional(),
|
|
871
723
|
},
|
|
872
724
|
}),
|
|
@@ -881,6 +733,7 @@ function starterSchemaPslPostgres() {
|
|
|
881
733
|
model User {
|
|
882
734
|
id Int @id @default(autoincrement())
|
|
883
735
|
email String @unique
|
|
736
|
+
username String?
|
|
884
737
|
name String?
|
|
885
738
|
posts Post[]
|
|
886
739
|
createdAt DateTime @default(now())
|
|
@@ -902,10 +755,11 @@ function starterSchemaPslMongo() {
|
|
|
902
755
|
return `// use prisma-next
|
|
903
756
|
|
|
904
757
|
model User {
|
|
905
|
-
id
|
|
906
|
-
email
|
|
907
|
-
|
|
908
|
-
|
|
758
|
+
id ObjectId @id @map("_id")
|
|
759
|
+
email String @unique
|
|
760
|
+
username String?
|
|
761
|
+
name String?
|
|
762
|
+
posts Post[]
|
|
909
763
|
@@map("users")
|
|
910
764
|
}
|
|
911
765
|
|
|
@@ -921,17 +775,16 @@ model Post {
|
|
|
921
775
|
}
|
|
922
776
|
function starterSchemaTsPostgres() {
|
|
923
777
|
return `import { defineContract } from '@prisma-next/postgres/contract-builder';
|
|
924
|
-
import sqlFamily from '@prisma-next/postgres/family';
|
|
925
|
-
import postgresTarget from '@prisma-next/postgres/target';
|
|
926
778
|
|
|
927
779
|
export const contract = defineContract(
|
|
928
|
-
{
|
|
780
|
+
{},
|
|
929
781
|
({ field, model, rel }) => ({
|
|
930
782
|
models: {
|
|
931
783
|
User: model('User', {
|
|
932
784
|
fields: {
|
|
933
785
|
id: field.id.uuidv7(),
|
|
934
786
|
email: field.text().unique(),
|
|
787
|
+
username: field.text().optional(),
|
|
935
788
|
name: field.text().optional(),
|
|
936
789
|
createdAt: field.temporal.createdAt(),
|
|
937
790
|
updatedAt: field.temporal.updatedAt(),
|
|
@@ -961,11 +814,9 @@ export const contract = defineContract(
|
|
|
961
814
|
}
|
|
962
815
|
function starterSchemaTsMongo() {
|
|
963
816
|
return `import { defineContract } from '@prisma-next/mongo/contract-builder';
|
|
964
|
-
import mongoFamily from '@prisma-next/mongo/family';
|
|
965
|
-
import mongoTarget from '@prisma-next/mongo/target';
|
|
966
817
|
|
|
967
818
|
export const contract = defineContract(
|
|
968
|
-
{
|
|
819
|
+
{},
|
|
969
820
|
({ field, model, rel }) => ({
|
|
970
821
|
models: {
|
|
971
822
|
User: model('User', {
|
|
@@ -973,6 +824,7 @@ export const contract = defineContract(
|
|
|
973
824
|
fields: {
|
|
974
825
|
_id: field.objectId(),
|
|
975
826
|
email: field.string(),
|
|
827
|
+
username: field.string().optional(),
|
|
976
828
|
name: field.string().optional(),
|
|
977
829
|
},
|
|
978
830
|
relations: {
|
|
@@ -1042,6 +894,11 @@ const AUTHORING_VALUES = new Map([
|
|
|
1042
894
|
["typescript", "typescript"],
|
|
1043
895
|
["ts", "typescript"]
|
|
1044
896
|
]);
|
|
897
|
+
const TELEMETRY_CONSENT_MESSAGE = [
|
|
898
|
+
"Help us prioritize features by sharing anonymous CLI usage data?",
|
|
899
|
+
"The telemetry implementation is open source and fully transparent.",
|
|
900
|
+
"(packages/1-framework/3-tooling/cli-telemetry and apps/telemetry-backend)."
|
|
901
|
+
].join(" ");
|
|
1045
902
|
/**
|
|
1046
903
|
* Resolves every required input for `runInit`. In interactive mode, missing
|
|
1047
904
|
* inputs are prompted via clack; in non-interactive mode, missing required
|
|
@@ -1089,6 +946,10 @@ async function resolveInitInputs(ctx) {
|
|
|
1089
946
|
canPrompt,
|
|
1090
947
|
autoAcceptPrompts
|
|
1091
948
|
});
|
|
949
|
+
const enableTelemetry = await resolveTelemetryConsent({
|
|
950
|
+
canPrompt,
|
|
951
|
+
autoAcceptPrompts
|
|
952
|
+
});
|
|
1092
953
|
const installProjectSkill = options.skill !== false;
|
|
1093
954
|
return {
|
|
1094
955
|
target: finalTarget,
|
|
@@ -1100,9 +961,49 @@ async function resolveInitInputs(ctx) {
|
|
|
1100
961
|
strictProbe: Boolean(options.strictProbe),
|
|
1101
962
|
reinit,
|
|
1102
963
|
removePreviousFacade,
|
|
964
|
+
enableTelemetry,
|
|
1103
965
|
installProjectSkill
|
|
1104
966
|
};
|
|
1105
967
|
}
|
|
968
|
+
/**
|
|
969
|
+
* The interactive telemetry consent prompt. Shown as the last
|
|
970
|
+
* question of the `init` sequence iff:
|
|
971
|
+
* 1. `canPrompt === true` (interactive stdin / stdout combo),
|
|
972
|
+
* 2. `autoAcceptPrompts === false` (the user did not pass `--yes`),
|
|
973
|
+
* 3. neither telemetry env opt-out is active,
|
|
974
|
+
* 4. the process is not running in CI,
|
|
975
|
+
* 5. the stored `enableTelemetry` value is `undefined` (the user
|
|
976
|
+
* has never been asked, or skipped the prompt previously).
|
|
977
|
+
*
|
|
978
|
+
* Outside that intersection the function returns `null` and leaves the
|
|
979
|
+
* stored preference untouched. Inside it, the user's answer is
|
|
980
|
+
* persisted via `writeUserConfig({ enableTelemetry })` before this
|
|
981
|
+
* function returns; on an affirmative answer `writeUserConfig`
|
|
982
|
+
* generates and stores the v4 `installationId` in the same write.
|
|
983
|
+
*
|
|
984
|
+
* The wording names CLI usage data and points to the open-source
|
|
985
|
+
* client/backend paths. Default value is `true` to match precedent
|
|
986
|
+
* and to make the consent answer a single keystroke once it's been
|
|
987
|
+
* disclosed.
|
|
988
|
+
*/
|
|
989
|
+
async function resolveTelemetryConsent(opts) {
|
|
990
|
+
if (!opts.canPrompt || opts.autoAcceptPrompts || isCI()) return null;
|
|
991
|
+
const config = readUserConfig();
|
|
992
|
+
const gating = resolveGating({
|
|
993
|
+
env: process.env,
|
|
994
|
+
config
|
|
995
|
+
});
|
|
996
|
+
if (!gating.enabled && gating.reason === "env-override") return null;
|
|
997
|
+
if (config.enableTelemetry !== void 0) return null;
|
|
998
|
+
const result = await clack.confirm({
|
|
999
|
+
message: TELEMETRY_CONSENT_MESSAGE,
|
|
1000
|
+
initialValue: true,
|
|
1001
|
+
output: process.stderr
|
|
1002
|
+
});
|
|
1003
|
+
if (clack.isCancel(result)) throw errorInitUserAborted();
|
|
1004
|
+
writeUserConfig({ enableTelemetry: Boolean(result) });
|
|
1005
|
+
return Boolean(result);
|
|
1006
|
+
}
|
|
1106
1007
|
async function resolveWriteEnv(opts) {
|
|
1107
1008
|
if (opts.flag !== void 0) return Boolean(opts.flag);
|
|
1108
1009
|
if (!opts.canPrompt || opts.autoAcceptPrompts) return false;
|
|
@@ -1214,10 +1115,11 @@ function validateSchemaPath(value, authoring) {
|
|
|
1214
1115
|
});
|
|
1215
1116
|
const ext = extname(trimmed).toLowerCase();
|
|
1216
1117
|
const expected = authoring === "typescript" ? ".ts" : ".prisma";
|
|
1217
|
-
if (ext !== expected) throw
|
|
1218
|
-
|
|
1219
|
-
|
|
1220
|
-
|
|
1118
|
+
if (ext !== expected) throw errorInitAuthoringSchemaPathMismatch({
|
|
1119
|
+
authoring,
|
|
1120
|
+
schemaPath: trimmed,
|
|
1121
|
+
actualExtension: ext.length > 0 ? ext : "(none)",
|
|
1122
|
+
expectedExtension: expected
|
|
1221
1123
|
});
|
|
1222
1124
|
return normalize(trimmed);
|
|
1223
1125
|
}
|
|
@@ -1511,6 +1413,189 @@ function removeDependency(existing, depName) {
|
|
|
1511
1413
|
return `${JSON.stringify(parsed, null, 2)}${trailingNewline}`;
|
|
1512
1414
|
}
|
|
1513
1415
|
//#endregion
|
|
1416
|
+
//#region src/commands/init/skill-install.ts
|
|
1417
|
+
const exec = promisify(execFile);
|
|
1418
|
+
/**
|
|
1419
|
+
* Default base for the GitHub-URL form `<owner>/<repo>` consumed by
|
|
1420
|
+
* upstream `skills add`. Each `SkillSource` joins this base with its
|
|
1421
|
+
* own subpath (and optional `#ref` for version-pinned clusters).
|
|
1422
|
+
*/
|
|
1423
|
+
const DEFAULT_SKILL_BASE = "prisma/prisma-next";
|
|
1424
|
+
const DEFAULT_SKILL_SOURCES = [
|
|
1425
|
+
{
|
|
1426
|
+
subpath: "skills",
|
|
1427
|
+
ref: "cli",
|
|
1428
|
+
description: "usage skills (version-locked to installed Prisma Next)"
|
|
1429
|
+
},
|
|
1430
|
+
{
|
|
1431
|
+
subpath: "skills/upgrade",
|
|
1432
|
+
ref: null,
|
|
1433
|
+
description: "upgrade skill (always tracks `main`)"
|
|
1434
|
+
},
|
|
1435
|
+
{
|
|
1436
|
+
subpath: "skills/extension-author",
|
|
1437
|
+
ref: null,
|
|
1438
|
+
description: "extension-author skill (always tracks `main`)"
|
|
1439
|
+
}
|
|
1440
|
+
];
|
|
1441
|
+
/**
|
|
1442
|
+
* Test-only escape hatch for pinning the install base to a local
|
|
1443
|
+
* checkout. Production runs leave this unset, so installs always use
|
|
1444
|
+
* `DEFAULT_SKILL_BASE`.
|
|
1445
|
+
*
|
|
1446
|
+
* When set to an absolute filesystem path (typical for tests), the
|
|
1447
|
+
* `#ref` fragment is dropped — local-path mode in upstream's CLI does
|
|
1448
|
+
* not accept refs, and the local clone has whatever content the test
|
|
1449
|
+
* checked into it anyway. When set to anything else (e.g. a fork name
|
|
1450
|
+
* `myuser/prisma-next`), the ref policy is preserved.
|
|
1451
|
+
*/
|
|
1452
|
+
function resolveAgentSkillBase() {
|
|
1453
|
+
const override = process.env["PRISMA_NEXT_SKILLS_BASE"]?.trim();
|
|
1454
|
+
return override && override.length > 0 ? override : DEFAULT_SKILL_BASE;
|
|
1455
|
+
}
|
|
1456
|
+
function isLocalPath(base) {
|
|
1457
|
+
return base.startsWith("/") || /^[a-zA-Z]:[\\/]/.test(base);
|
|
1458
|
+
}
|
|
1459
|
+
/**
|
|
1460
|
+
* Agents passed to every project-level init install. Upstream `skills add`
|
|
1461
|
+
* is the source of truth for per-agent install behaviour; the CLI lists
|
|
1462
|
+
* every supported runtime on one invocation and delegates the rest.
|
|
1463
|
+
*/
|
|
1464
|
+
const DEFAULT_SKILL_AGENTS = [
|
|
1465
|
+
"cursor",
|
|
1466
|
+
"claude-code",
|
|
1467
|
+
"codex",
|
|
1468
|
+
"windsurf"
|
|
1469
|
+
];
|
|
1470
|
+
/**
|
|
1471
|
+
* Build the `<base>/<subpath>[#ref]` URL the `skills` CLI will
|
|
1472
|
+
* resolve. Exported for unit tests so the per-source format can be
|
|
1473
|
+
* asserted without going through the full install loop.
|
|
1474
|
+
*/
|
|
1475
|
+
function formatSkillSourceUrl(source) {
|
|
1476
|
+
const base = resolveAgentSkillBase();
|
|
1477
|
+
const url = `${base}/${source.subpath}`;
|
|
1478
|
+
if (source.ref === null) return url;
|
|
1479
|
+
if (isLocalPath(base)) return url;
|
|
1480
|
+
if (source.ref === "cli") return `${url}#v${version}`;
|
|
1481
|
+
return url;
|
|
1482
|
+
}
|
|
1483
|
+
/**
|
|
1484
|
+
* The skill-install command for one source, formatted for the
|
|
1485
|
+
* project's detected package manager. `npx`/`pnpm dlx`/`bunx` are
|
|
1486
|
+
* interchangeable to the user; we pick the variant that matches the
|
|
1487
|
+
* rest of the install step so a single project consistently uses one
|
|
1488
|
+
* runner.
|
|
1489
|
+
*
|
|
1490
|
+
* `--agent` takes space-separated slugs on one flag; `--skill '*'` and `-y`
|
|
1491
|
+
* skip the multi-select prompts a non-interactive scaffold step cannot show.
|
|
1492
|
+
*
|
|
1493
|
+
* Exported for unit tests so the per-PM dispatch can be asserted
|
|
1494
|
+
* without a live subprocess.
|
|
1495
|
+
*/
|
|
1496
|
+
function formatSkillInstallCommand(args) {
|
|
1497
|
+
const agents = args.agents ?? DEFAULT_SKILL_AGENTS;
|
|
1498
|
+
const cliArgs = [
|
|
1499
|
+
"skills@latest",
|
|
1500
|
+
"add",
|
|
1501
|
+
formatSkillSourceUrl(args.source),
|
|
1502
|
+
"--agent",
|
|
1503
|
+
...agents,
|
|
1504
|
+
"--skill",
|
|
1505
|
+
"'*'",
|
|
1506
|
+
"-y"
|
|
1507
|
+
];
|
|
1508
|
+
return formatPackageManagerCommand(args.pm, cliArgs);
|
|
1509
|
+
}
|
|
1510
|
+
/**
|
|
1511
|
+
* Ordered skill-install commands for one init run. Exported for unit tests.
|
|
1512
|
+
*/
|
|
1513
|
+
function resolveProjectSkillInstallCommands(pm) {
|
|
1514
|
+
return DEFAULT_SKILL_SOURCES.map((source) => formatSkillInstallCommand({
|
|
1515
|
+
pm,
|
|
1516
|
+
source
|
|
1517
|
+
}));
|
|
1518
|
+
}
|
|
1519
|
+
function formatPackageManagerCommand(pm, args) {
|
|
1520
|
+
switch (pm) {
|
|
1521
|
+
case "pnpm": return `pnpm dlx ${args.join(" ")}`;
|
|
1522
|
+
case "yarn": return `yarn dlx ${args.join(" ")}`;
|
|
1523
|
+
case "bun": return `bunx ${args.join(" ")}`;
|
|
1524
|
+
case "deno": return `deno run -A npm:${args.join(" ")}`;
|
|
1525
|
+
case "npm": return `npx ${args.join(" ")}`;
|
|
1526
|
+
}
|
|
1527
|
+
}
|
|
1528
|
+
/**
|
|
1529
|
+
* Parse the project-pm-formatted command into an exec call. The
|
|
1530
|
+
* format-then-parse split keeps the user-facing command string the same
|
|
1531
|
+
* as the surface the structured error advertises, so a user who copies
|
|
1532
|
+
* the error's `fix` line gets the same invocation that init just
|
|
1533
|
+
* attempted. Single quotes are preserved in the display form so `*` is
|
|
1534
|
+
* safe to copy into a shell, then stripped before `execFile`.
|
|
1535
|
+
*/
|
|
1536
|
+
function commandToExec(command) {
|
|
1537
|
+
const tokens = (command.match(/'[^']*'|\S+/g) ?? []).map((token) => token.startsWith("'") && token.endsWith("'") ? token.slice(1, -1) : token);
|
|
1538
|
+
return {
|
|
1539
|
+
file: tokens[0] ?? "npx",
|
|
1540
|
+
args: tokens.slice(1)
|
|
1541
|
+
};
|
|
1542
|
+
}
|
|
1543
|
+
/**
|
|
1544
|
+
* Runs the project-level skill install for every source in
|
|
1545
|
+
* `DEFAULT_SKILL_SOURCES`, in order. Returns
|
|
1546
|
+
* `{ ok: true, commands }` on success; throws a structured
|
|
1547
|
+
* `errorInitSkillInstallFailed` on the first failure (subsequent
|
|
1548
|
+
* sources are not attempted — the user opted into Prisma Next by
|
|
1549
|
+
* running `init` and a partial install would leave the project in an
|
|
1550
|
+
* ambiguous state). The throw is intentionally fatal — project-level
|
|
1551
|
+
* skill install is unconditional (modulo `--no-skill`).
|
|
1552
|
+
*/
|
|
1553
|
+
async function runProjectLevelSkillInstall(ctx) {
|
|
1554
|
+
const commands = [];
|
|
1555
|
+
const installCommands = resolveProjectSkillInstallCommands(ctx.pm);
|
|
1556
|
+
for (const command of installCommands) {
|
|
1557
|
+
const { file, args } = commandToExec(command);
|
|
1558
|
+
try {
|
|
1559
|
+
await exec(file, args, { cwd: ctx.baseDir });
|
|
1560
|
+
commands.push(command);
|
|
1561
|
+
} catch (err) {
|
|
1562
|
+
throw errorInitSkillInstallFailed({
|
|
1563
|
+
skillInstallCommand: command,
|
|
1564
|
+
filesWritten: ctx.filesWritten,
|
|
1565
|
+
cause: redactSecrets$1(readChildStderr$1(err)) || (err instanceof Error ? err.message : String(err))
|
|
1566
|
+
});
|
|
1567
|
+
}
|
|
1568
|
+
}
|
|
1569
|
+
return {
|
|
1570
|
+
ok: true,
|
|
1571
|
+
commands
|
|
1572
|
+
};
|
|
1573
|
+
}
|
|
1574
|
+
function readChildStderr$1(err) {
|
|
1575
|
+
if (err instanceof Error && "stderr" in err) return String(err.stderr ?? "");
|
|
1576
|
+
return "";
|
|
1577
|
+
}
|
|
1578
|
+
/**
|
|
1579
|
+
* Strips credentials from a `scheme://user:pass@host/...` URL anywhere
|
|
1580
|
+
* in `stderr`. Package-manager stderr regularly contains credentialed
|
|
1581
|
+
* registry URLs (private npm registries, GitHub Packages tokens), and
|
|
1582
|
+
* those bubble into the structured `errorInitSkillInstallFailed`
|
|
1583
|
+
* envelope, which ends up in logs and CI output. Redact at the
|
|
1584
|
+
* boundary so we never re-emit a secret.
|
|
1585
|
+
*
|
|
1586
|
+
* Exported for unit tests.
|
|
1587
|
+
*/
|
|
1588
|
+
function redactSecrets$1(stderr) {
|
|
1589
|
+
if (!stderr) return stderr;
|
|
1590
|
+
return stderr.replace(/([a-zA-Z][a-zA-Z0-9+.-]*:\/\/)([^/@\s]+)@/g, "$1***@");
|
|
1591
|
+
}
|
|
1592
|
+
/**
|
|
1593
|
+
* Hand-rolled skill stub path that init must not leave behind. Removed
|
|
1594
|
+
* on every init run so a project's `.agents/skills/prisma-next/` does
|
|
1595
|
+
* not shadow the installed Prisma Next skill cluster.
|
|
1596
|
+
*/
|
|
1597
|
+
const LEGACY_SKILL_FILE = ".agents/skills/prisma-next/SKILL.md";
|
|
1598
|
+
//#endregion
|
|
1514
1599
|
//#region src/commands/init/templates/env.ts
|
|
1515
1600
|
/**
|
|
1516
1601
|
* The minimum supported server version for each target (FR8.1). The
|
|
@@ -1548,7 +1633,12 @@ function envPlaceholderBody(target) {
|
|
|
1548
1633
|
lines.push(`# Requires ${label} >= ${minVersion}.`);
|
|
1549
1634
|
lines.push("");
|
|
1550
1635
|
if (target === "postgres") lines.push("DATABASE_URL=\"postgresql://user:password@localhost:5432/mydb\"");
|
|
1551
|
-
else
|
|
1636
|
+
else {
|
|
1637
|
+
lines.push("# Standalone local mongod / `docker run mongo:7` — no replica set required for first-run queries.");
|
|
1638
|
+
lines.push("# Transactions and change streams need a replica set; add ?replicaSet=... only after initiating one.");
|
|
1639
|
+
lines.push("");
|
|
1640
|
+
lines.push("DATABASE_URL=\"mongodb://user:password@localhost:27017/mydb\"");
|
|
1641
|
+
}
|
|
1552
1642
|
lines.push("");
|
|
1553
1643
|
return lines.join("\n");
|
|
1554
1644
|
}
|
|
@@ -1634,6 +1724,54 @@ function requirementsBlock(target) {
|
|
|
1634
1724
|
].join("\n");
|
|
1635
1725
|
}
|
|
1636
1726
|
//#endregion
|
|
1727
|
+
//#region src/commands/init/templates/readme.ts
|
|
1728
|
+
const sharedVariables = [
|
|
1729
|
+
"projectName",
|
|
1730
|
+
"contractPath",
|
|
1731
|
+
"runDev",
|
|
1732
|
+
"runContractEmit"
|
|
1733
|
+
];
|
|
1734
|
+
const postgresVariables = [
|
|
1735
|
+
...sharedVariables,
|
|
1736
|
+
"runDbInit",
|
|
1737
|
+
"runDbUpdate",
|
|
1738
|
+
"runMigrationPlan",
|
|
1739
|
+
"runMigrate",
|
|
1740
|
+
"runDbSeed"
|
|
1741
|
+
];
|
|
1742
|
+
const mongoVariables = [
|
|
1743
|
+
...sharedVariables,
|
|
1744
|
+
"runDbUp",
|
|
1745
|
+
"runDbDown",
|
|
1746
|
+
"runDbReset",
|
|
1747
|
+
"runMigrationPlan",
|
|
1748
|
+
"runMigrate",
|
|
1749
|
+
"runDbSeed"
|
|
1750
|
+
];
|
|
1751
|
+
function minimalProjectReadmeMd(target, schemaPath, projectName, pm) {
|
|
1752
|
+
const run = (script) => formatRunScriptCommand(pm, script);
|
|
1753
|
+
const shared = {
|
|
1754
|
+
projectName,
|
|
1755
|
+
contractPath: schemaPath,
|
|
1756
|
+
runDev: run("dev"),
|
|
1757
|
+
runContractEmit: run("contract:emit"),
|
|
1758
|
+
runMigrationPlan: run("migration:plan"),
|
|
1759
|
+
runMigrate: run("migrate"),
|
|
1760
|
+
runDbSeed: run("db:seed")
|
|
1761
|
+
};
|
|
1762
|
+
if (target === "mongo") return renderTemplate("readme-mongo.md", mongoVariables, {
|
|
1763
|
+
...shared,
|
|
1764
|
+
runDbUp: run("db:up"),
|
|
1765
|
+
runDbDown: run("db:down"),
|
|
1766
|
+
runDbReset: run("db:reset")
|
|
1767
|
+
});
|
|
1768
|
+
return renderTemplate("readme-postgres.md", postgresVariables, {
|
|
1769
|
+
...shared,
|
|
1770
|
+
runDbInit: run("db:init"),
|
|
1771
|
+
runDbUpdate: run("db:update")
|
|
1772
|
+
});
|
|
1773
|
+
}
|
|
1774
|
+
//#endregion
|
|
1637
1775
|
//#region src/commands/init/templates/tsconfig.ts
|
|
1638
1776
|
/**
|
|
1639
1777
|
* Compiler options the scaffolded `prisma-next.config.ts` and `db.ts` need
|
|
@@ -1782,7 +1920,7 @@ function mergeTypesArray(existing) {
|
|
|
1782
1920
|
* structured CLI errors raised at every phase (input resolution, install,
|
|
1783
1921
|
* emit) and renders them via the same UI surface as success output
|
|
1784
1922
|
* (`--json` to stdout, human to stderr). Exit codes follow the documented
|
|
1785
|
-
* stable set in `./exit-codes.ts`
|
|
1923
|
+
* stable set in `./exit-codes.ts` and the
|
|
1786
1924
|
* [Style Guide § Exit Codes](../../../../../../../docs/CLI%20Style%20Guide.md#exit-codes).
|
|
1787
1925
|
*
|
|
1788
1926
|
* Layered for testability: the action handler in `./index.ts` is
|
|
@@ -1790,11 +1928,8 @@ function mergeTypesArray(existing) {
|
|
|
1790
1928
|
* function does no flag parsing of its own.
|
|
1791
1929
|
*/
|
|
1792
1930
|
async function runInit(baseDir, runOptions) {
|
|
1793
|
-
const { options, flags, canPrompt, probeOverrides } = runOptions;
|
|
1794
|
-
const ui =
|
|
1795
|
-
color: flags.color,
|
|
1796
|
-
interactive: flags.interactive
|
|
1797
|
-
});
|
|
1931
|
+
const { options, flags, canPrompt, probeOverrides, afterFirstTelemetryConsent } = runOptions;
|
|
1932
|
+
const ui = createTerminalUI(flags);
|
|
1798
1933
|
const warnings = [];
|
|
1799
1934
|
const filesWritten = [];
|
|
1800
1935
|
const filesDeleted = [];
|
|
@@ -1811,6 +1946,9 @@ async function runInit(baseDir, runOptions) {
|
|
|
1811
1946
|
if (CliStructuredError.is(error)) return emitError(ui, flags, error);
|
|
1812
1947
|
throw error;
|
|
1813
1948
|
}
|
|
1949
|
+
if (inputs.enableTelemetry === true && afterFirstTelemetryConsent !== void 0) try {
|
|
1950
|
+
await afterFirstTelemetryConsent(inputs);
|
|
1951
|
+
} catch {}
|
|
1814
1952
|
const pm = await detectPackageManager(baseDir);
|
|
1815
1953
|
const pkgRun = formatRunCommand(pm, "prisma-next", "").trimEnd();
|
|
1816
1954
|
const schemaDir = dirname(inputs.schemaPath);
|
|
@@ -1920,6 +2058,13 @@ async function runInit(baseDir, runOptions) {
|
|
|
1920
2058
|
if (typeWarning !== null) warnings.push(typeWarning);
|
|
1921
2059
|
if (synthesisePackageJson) warnings.push("No package.json found in the target directory; created a minimal one. Edit `name` / `version` to taste.");
|
|
1922
2060
|
}
|
|
2061
|
+
if (existsSync(join(baseDir, "src/index.ts"))) if (!existsSync(join(baseDir, "README.md"))) {
|
|
2062
|
+
const rawName = parsedPackageJson !== null && typeof parsedPackageJson["name"] === "string" ? parsedPackageJson["name"] : basename(baseDir);
|
|
2063
|
+
filesToWrite.push({
|
|
2064
|
+
path: "README.md",
|
|
2065
|
+
content: minimalProjectReadmeMd(inputs.target, inputs.schemaPath, sanitisePackageName(rawName), pm)
|
|
2066
|
+
});
|
|
2067
|
+
} else warnings.push("README.md already exists; leaving it untouched.");
|
|
1923
2068
|
for (const file of filesToWrite) {
|
|
1924
2069
|
const fullPath = join(baseDir, file.path);
|
|
1925
2070
|
mkdirSync(dirname(fullPath), { recursive: true });
|
|
@@ -1983,10 +2128,12 @@ async function runInit(baseDir, runOptions) {
|
|
|
1983
2128
|
filesWritten
|
|
1984
2129
|
}));
|
|
1985
2130
|
}
|
|
1986
|
-
const manualProjectSkillSummary =
|
|
2131
|
+
const manualProjectSkillSummary = DEFAULT_SKILL_SOURCES.map((source) => formatSkillInstallCommand({
|
|
2132
|
+
pm: install.effectivePm,
|
|
2133
|
+
source
|
|
2134
|
+
})).map((c) => `\`${c}\``).join(" && ");
|
|
1987
2135
|
let skillRegistered = false;
|
|
1988
|
-
if (!inputs.installProjectSkill) warnings.push(`Skipped Prisma Next
|
|
1989
|
-
else if (install.skipped) warnings.push(`Skipped Prisma Next agent-skill install because --no-install was passed. After you run install manually, install the skills with: ${manualProjectSkillSummary}`);
|
|
2136
|
+
if (!inputs.installProjectSkill) warnings.push(`Skipped Prisma Next skills install (--no-skill). To install the skills later, run: ${manualProjectSkillSummary}`);
|
|
1990
2137
|
else {
|
|
1991
2138
|
const spinner = ui.spinner();
|
|
1992
2139
|
spinner.start("Registering Prisma Next skills with the agent runtime...");
|
|
@@ -2077,7 +2224,8 @@ function exitCodeForError(error) {
|
|
|
2077
2224
|
case "5005":
|
|
2078
2225
|
case "5010":
|
|
2079
2226
|
case "5011":
|
|
2080
|
-
case "5012":
|
|
2227
|
+
case "5012":
|
|
2228
|
+
case "5014": return 2;
|
|
2081
2229
|
case "5006": return 3;
|
|
2082
2230
|
case "5007": return 4;
|
|
2083
2231
|
case "5008": return 5;
|
|
@@ -2299,7 +2447,7 @@ async function runEmit(ctx) {
|
|
|
2299
2447
|
const spinner = ctx.ui.spinner();
|
|
2300
2448
|
spinner.start("Emitting contract...");
|
|
2301
2449
|
try {
|
|
2302
|
-
const { executeContractEmit } = await import("./contract-emit-
|
|
2450
|
+
const { executeContractEmit } = await import("./contract-emit-bcrpT-wD.mjs").then((n) => n.t);
|
|
2303
2451
|
await executeContractEmit({ configPath: join(ctx.baseDir, "prisma-next.config.ts") });
|
|
2304
2452
|
spinner.stop("Contract emitted");
|
|
2305
2453
|
} catch (err) {
|
|
@@ -2352,4 +2500,4 @@ function sanitisePackageName(raw) {
|
|
|
2352
2500
|
//#endregion
|
|
2353
2501
|
export { runInit };
|
|
2354
2502
|
|
|
2355
|
-
//# sourceMappingURL=init-
|
|
2503
|
+
//# sourceMappingURL=init-BCJZPWE1.mjs.map
|