@prisma-next/cli 0.10.0 → 0.11.0-dev.1
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 +400 -13
- 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-DtavI0wJ.mjs} +109 -12
- package/dist/command-helpers-DtavI0wJ.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-uwT-Mj8-.mjs} +7 -12
- package/dist/contract-emit-uwT-Mj8-.mjs.map +1 -0
- package/dist/{contract-infer-Cnj8G1E2.mjs → contract-infer-pKkiCt7C.mjs} +9 -14
- package/dist/contract-infer-pKkiCt7C.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-AoIUriL4.mjs} +9 -13
- package/dist/db-verify-AoIUriL4.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-YX6lCJpG.mjs} +528 -627
- package/dist/init-YX6lCJpG.mjs.map +1 -0
- package/dist/{inspect-live-schema-CWLK_lgs.mjs → inspect-live-schema-LeWvkZVz.mjs} +4 -4
- package/dist/{inspect-live-schema-CWLK_lgs.mjs.map → inspect-live-schema-LeWvkZVz.mjs.map} +1 -1
- package/dist/{migration-command-scaffold-CmXXC1UZ.mjs → migration-command-scaffold-BtkunvFQ.mjs} +4 -4
- package/dist/{migration-command-scaffold-CmXXC1UZ.mjs.map → migration-command-scaffold-BtkunvFQ.mjs.map} +1 -1
- package/dist/{migration-plan-CHyUlBV0.mjs → migration-plan-C2jeH1J5.mjs} +8 -12
- package/dist/migration-plan-C2jeH1J5.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 +7 -7
- 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/hygiene-gitattributes.ts +2 -2
- package/src/commands/init/index.ts +15 -6
- 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 +26 -24
- 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 +14 -6
- 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,8 +1,11 @@
|
|
|
1
1
|
import { existsSync, readFileSync } from 'node:fs';
|
|
2
2
|
import * as clack from '@clack/prompts';
|
|
3
|
+
import { readUserConfig, resolveGating, writeUserConfig } from '@prisma-next/cli-telemetry';
|
|
3
4
|
import { extname, join, normalize } from 'pathe';
|
|
4
5
|
import type { GlobalFlags } from '../../utils/global-flags';
|
|
6
|
+
import { isCI } from '../../utils/is-ci';
|
|
5
7
|
import {
|
|
8
|
+
errorInitAuthoringSchemaPathMismatch,
|
|
6
9
|
errorInitInvalidFlagValue,
|
|
7
10
|
errorInitMissingFlags,
|
|
8
11
|
errorInitReinitNeedsForce,
|
|
@@ -68,12 +71,24 @@ export interface ResolvedInitInputs {
|
|
|
68
71
|
* is added separately via the install step.
|
|
69
72
|
*/
|
|
70
73
|
readonly removePreviousFacade: string | null;
|
|
74
|
+
/**
|
|
75
|
+
* Telemetry consent answer recorded during this `init` run, or `null`
|
|
76
|
+
* when no prompt was shown. The prompt fires only on the
|
|
77
|
+
* canPrompt + !autoAcceptPrompts + no env/CI opt-out +
|
|
78
|
+
* `enableTelemetry === undefined` intersection; outside that window
|
|
79
|
+
* the field is `null` and the stored preference (if any) stays
|
|
80
|
+
* unchanged. The answer has already been persisted to
|
|
81
|
+
* `$XDG_CONFIG_HOME/prisma-next/config.json` by
|
|
82
|
+
* the time `runInit` sees this value — it's surfaced here purely so
|
|
83
|
+
* the post-init summary can mention what the user chose.
|
|
84
|
+
*/
|
|
85
|
+
readonly enableTelemetry: boolean | null;
|
|
71
86
|
/**
|
|
72
87
|
* Whether to run `npx skills add prisma/prisma-next#v<version>` at the
|
|
73
88
|
* project level after install + emit. True by default; `--no-skill`
|
|
74
89
|
* sets it to `false`. The skill is always project-level (never
|
|
75
90
|
* user-level / global) so its version stays locked to the project's
|
|
76
|
-
* Prisma Next version — see `
|
|
91
|
+
* Prisma Next version — see `skill-install.ts`.
|
|
77
92
|
*/
|
|
78
93
|
readonly installProjectSkill: boolean;
|
|
79
94
|
}
|
|
@@ -91,6 +106,12 @@ const AUTHORING_VALUES: ReadonlyMap<string, AuthoringId> = new Map([
|
|
|
91
106
|
['ts', 'typescript'],
|
|
92
107
|
]);
|
|
93
108
|
|
|
109
|
+
export const TELEMETRY_CONSENT_MESSAGE = [
|
|
110
|
+
'Help us prioritize features by sharing anonymous CLI usage data?',
|
|
111
|
+
'The telemetry implementation is open source and fully transparent.',
|
|
112
|
+
'(packages/1-framework/3-tooling/cli-telemetry and apps/telemetry-backend).',
|
|
113
|
+
].join(' ');
|
|
114
|
+
|
|
94
115
|
/**
|
|
95
116
|
* Resolves every required input for `runInit`. In interactive mode, missing
|
|
96
117
|
* inputs are prompted via clack; in non-interactive mode, missing required
|
|
@@ -176,6 +197,11 @@ export async function resolveInitInputs(ctx: {
|
|
|
176
197
|
autoAcceptPrompts,
|
|
177
198
|
});
|
|
178
199
|
|
|
200
|
+
const enableTelemetry = await resolveTelemetryConsent({
|
|
201
|
+
canPrompt,
|
|
202
|
+
autoAcceptPrompts,
|
|
203
|
+
});
|
|
204
|
+
|
|
179
205
|
// Skill-install gating. `--no-skill` (commander parses
|
|
180
206
|
// `options.skill === false`) is the only escape hatch; otherwise
|
|
181
207
|
// project-level install is unconditional. The skill is always
|
|
@@ -193,10 +219,60 @@ export async function resolveInitInputs(ctx: {
|
|
|
193
219
|
strictProbe: Boolean(options.strictProbe),
|
|
194
220
|
reinit,
|
|
195
221
|
removePreviousFacade,
|
|
222
|
+
enableTelemetry,
|
|
196
223
|
installProjectSkill,
|
|
197
224
|
};
|
|
198
225
|
}
|
|
199
226
|
|
|
227
|
+
/**
|
|
228
|
+
* The interactive telemetry consent prompt. Shown as the last
|
|
229
|
+
* question of the `init` sequence iff:
|
|
230
|
+
* 1. `canPrompt === true` (interactive stdin / stdout combo),
|
|
231
|
+
* 2. `autoAcceptPrompts === false` (the user did not pass `--yes`),
|
|
232
|
+
* 3. neither telemetry env opt-out is active,
|
|
233
|
+
* 4. the process is not running in CI,
|
|
234
|
+
* 5. the stored `enableTelemetry` value is `undefined` (the user
|
|
235
|
+
* has never been asked, or skipped the prompt previously).
|
|
236
|
+
*
|
|
237
|
+
* Outside that intersection the function returns `null` and leaves the
|
|
238
|
+
* stored preference untouched. Inside it, the user's answer is
|
|
239
|
+
* persisted via `writeUserConfig({ enableTelemetry })` before this
|
|
240
|
+
* function returns; on an affirmative answer `writeUserConfig`
|
|
241
|
+
* generates and stores the v4 `installationId` in the same write.
|
|
242
|
+
*
|
|
243
|
+
* The wording names CLI usage data and points to the open-source
|
|
244
|
+
* client/backend paths. Default value is `true` to match precedent
|
|
245
|
+
* and to make the consent answer a single keystroke once it's been
|
|
246
|
+
* disclosed.
|
|
247
|
+
*/
|
|
248
|
+
async function resolveTelemetryConsent(opts: {
|
|
249
|
+
readonly canPrompt: boolean;
|
|
250
|
+
readonly autoAcceptPrompts: boolean;
|
|
251
|
+
}): Promise<boolean | null> {
|
|
252
|
+
if (!opts.canPrompt || opts.autoAcceptPrompts || isCI()) {
|
|
253
|
+
return null;
|
|
254
|
+
}
|
|
255
|
+
const config = readUserConfig();
|
|
256
|
+
const gating = resolveGating({ env: process.env, config });
|
|
257
|
+
if (!gating.enabled && gating.reason === 'env-override') {
|
|
258
|
+
return null;
|
|
259
|
+
}
|
|
260
|
+
const stored = config.enableTelemetry;
|
|
261
|
+
if (stored !== undefined) {
|
|
262
|
+
return null;
|
|
263
|
+
}
|
|
264
|
+
const result = await clack.confirm({
|
|
265
|
+
message: TELEMETRY_CONSENT_MESSAGE,
|
|
266
|
+
initialValue: true,
|
|
267
|
+
output: process.stderr,
|
|
268
|
+
});
|
|
269
|
+
if (clack.isCancel(result)) {
|
|
270
|
+
throw errorInitUserAborted();
|
|
271
|
+
}
|
|
272
|
+
writeUserConfig({ enableTelemetry: Boolean(result) });
|
|
273
|
+
return Boolean(result);
|
|
274
|
+
}
|
|
275
|
+
|
|
200
276
|
async function resolveWriteEnv(opts: {
|
|
201
277
|
readonly flag: boolean | undefined;
|
|
202
278
|
readonly canPrompt: boolean;
|
|
@@ -376,10 +452,11 @@ function validateSchemaPath(value: string, authoring: AuthoringId): string {
|
|
|
376
452
|
const ext = extname(trimmed).toLowerCase();
|
|
377
453
|
const expected = authoring === 'typescript' ? '.ts' : '.prisma';
|
|
378
454
|
if (ext !== expected) {
|
|
379
|
-
throw
|
|
380
|
-
|
|
381
|
-
|
|
382
|
-
|
|
455
|
+
throw errorInitAuthoringSchemaPathMismatch({
|
|
456
|
+
authoring,
|
|
457
|
+
schemaPath: trimmed,
|
|
458
|
+
actualExtension: ext.length > 0 ? ext : '(none)',
|
|
459
|
+
expectedExtension: expected,
|
|
383
460
|
});
|
|
384
461
|
}
|
|
385
462
|
return normalize(trimmed);
|
|
@@ -123,7 +123,7 @@ export function buildNextSteps(options: {
|
|
|
123
123
|
/**
|
|
124
124
|
* Whether the project-level Prisma Next skills install actually ran
|
|
125
125
|
* and succeeded during this `init`. When false (the user passed
|
|
126
|
-
* `--no-skill
|
|
126
|
+
* `--no-skill`, so the install was skipped), the
|
|
127
127
|
* "registered with your agent runtime" step is omitted — the skip is
|
|
128
128
|
* already surfaced in the warnings array with a manual-install hint.
|
|
129
129
|
*/
|
|
@@ -11,12 +11,12 @@ const exec = promisify(execFile);
|
|
|
11
11
|
* upstream `skills add`. Each `SkillSource` joins this base with its
|
|
12
12
|
* own subpath (and optional `#ref` for version-pinned clusters).
|
|
13
13
|
*/
|
|
14
|
-
export const
|
|
14
|
+
export const DEFAULT_SKILL_BASE = 'prisma/prisma-next';
|
|
15
15
|
|
|
16
16
|
/**
|
|
17
17
|
* One discovery scope inside the Prisma Next monorepo. The CLI emits
|
|
18
|
-
* one `skills add <base>/<subpath>[#ref] --
|
|
19
|
-
* during `init`.
|
|
18
|
+
* one `skills add <base>/<subpath>[#ref] --agent ... --skill '*' -y`
|
|
19
|
+
* invocation per source during `init`.
|
|
20
20
|
*
|
|
21
21
|
* `ref` semantics:
|
|
22
22
|
* - `cli`: pin to the CLI's own package version (lockstep with the
|
|
@@ -35,7 +35,7 @@ export interface SkillSource {
|
|
|
35
35
|
readonly description: string;
|
|
36
36
|
}
|
|
37
37
|
|
|
38
|
-
export const
|
|
38
|
+
export const DEFAULT_SKILL_SOURCES: readonly SkillSource[] = [
|
|
39
39
|
{
|
|
40
40
|
subpath: 'skills',
|
|
41
41
|
ref: 'cli',
|
|
@@ -56,7 +56,7 @@ export const DEFAULT_AGENT_SKILL_SOURCES: readonly SkillSource[] = [
|
|
|
56
56
|
/**
|
|
57
57
|
* Test-only escape hatch for pinning the install base to a local
|
|
58
58
|
* checkout. Production runs leave this unset, so installs always use
|
|
59
|
-
* `
|
|
59
|
+
* `DEFAULT_SKILL_BASE`.
|
|
60
60
|
*
|
|
61
61
|
* When set to an absolute filesystem path (typical for tests), the
|
|
62
62
|
* `#ref` fragment is dropped — local-path mode in upstream's CLI does
|
|
@@ -66,13 +66,28 @@ export const DEFAULT_AGENT_SKILL_SOURCES: readonly SkillSource[] = [
|
|
|
66
66
|
*/
|
|
67
67
|
function resolveAgentSkillBase(): string {
|
|
68
68
|
const override = process.env['PRISMA_NEXT_SKILLS_BASE']?.trim();
|
|
69
|
-
return override && override.length > 0 ? override :
|
|
69
|
+
return override && override.length > 0 ? override : DEFAULT_SKILL_BASE;
|
|
70
70
|
}
|
|
71
71
|
|
|
72
72
|
function isLocalPath(base: string): boolean {
|
|
73
73
|
return base.startsWith('/') || /^[a-zA-Z]:[\\/]/.test(base);
|
|
74
74
|
}
|
|
75
75
|
|
|
76
|
+
/** Agent slugs accepted by the upstream `skills add --agent` flag. */
|
|
77
|
+
export type SkillAgent = 'cursor' | 'claude-code' | 'codex' | 'windsurf';
|
|
78
|
+
|
|
79
|
+
/**
|
|
80
|
+
* Agents passed to every project-level init install. Upstream `skills add`
|
|
81
|
+
* is the source of truth for per-agent install behaviour; the CLI lists
|
|
82
|
+
* every supported runtime on one invocation and delegates the rest.
|
|
83
|
+
*/
|
|
84
|
+
export const DEFAULT_SKILL_AGENTS: readonly SkillAgent[] = [
|
|
85
|
+
'cursor',
|
|
86
|
+
'claude-code',
|
|
87
|
+
'codex',
|
|
88
|
+
'windsurf',
|
|
89
|
+
];
|
|
90
|
+
|
|
76
91
|
/**
|
|
77
92
|
* Build the `<base>/<subpath>[#ref]` URL the `skills` CLI will
|
|
78
93
|
* resolve. Exported for unit tests so the per-source format can be
|
|
@@ -94,37 +109,36 @@ export function formatSkillSourceUrl(source: SkillSource): string {
|
|
|
94
109
|
* rest of the install step so a single project consistently uses one
|
|
95
110
|
* runner.
|
|
96
111
|
*
|
|
97
|
-
* `--
|
|
98
|
-
*
|
|
99
|
-
* shows by default. A non-interactive scaffold step cannot present
|
|
100
|
-
* prompts.
|
|
112
|
+
* `--agent` takes space-separated slugs on one flag; `--skill '*'` and `-y`
|
|
113
|
+
* skip the multi-select prompts a non-interactive scaffold step cannot show.
|
|
101
114
|
*
|
|
102
115
|
* Exported for unit tests so the per-PM dispatch can be asserted
|
|
103
116
|
* without a live subprocess.
|
|
104
117
|
*/
|
|
105
|
-
export function formatSkillInstallCommand(
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
* project-local Claude symlinks when `.claude/` does not already exist. Run
|
|
113
|
-
* the explicit Claude Code install as well so fresh projects get
|
|
114
|
-
* `.claude/skills` without asking users to create that folder first.
|
|
115
|
-
*/
|
|
116
|
-
export function formatClaudeSkillInstallCommand(pm: PackageManager, source: SkillSource): string {
|
|
117
|
-
const args = [
|
|
118
|
+
export function formatSkillInstallCommand(args: {
|
|
119
|
+
readonly pm: PackageManager;
|
|
120
|
+
readonly source: SkillSource;
|
|
121
|
+
readonly agents?: readonly SkillAgent[];
|
|
122
|
+
}): string {
|
|
123
|
+
const agents = args.agents ?? DEFAULT_SKILL_AGENTS;
|
|
124
|
+
const cliArgs = [
|
|
118
125
|
'skills@latest',
|
|
119
126
|
'add',
|
|
120
|
-
formatSkillSourceUrl(source),
|
|
127
|
+
formatSkillSourceUrl(args.source),
|
|
121
128
|
'--agent',
|
|
122
|
-
|
|
129
|
+
...agents,
|
|
123
130
|
'--skill',
|
|
124
131
|
"'*'",
|
|
125
132
|
'-y',
|
|
126
133
|
];
|
|
127
|
-
return formatPackageManagerCommand(pm,
|
|
134
|
+
return formatPackageManagerCommand(args.pm, cliArgs);
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
/**
|
|
138
|
+
* Ordered skill-install commands for one init run. Exported for unit tests.
|
|
139
|
+
*/
|
|
140
|
+
export function resolveProjectSkillInstallCommands(pm: PackageManager): readonly string[] {
|
|
141
|
+
return DEFAULT_SKILL_SOURCES.map((source) => formatSkillInstallCommand({ pm, source }));
|
|
128
142
|
}
|
|
129
143
|
|
|
130
144
|
function formatPackageManagerCommand(pm: PackageManager, args: readonly string[]): string {
|
|
@@ -162,7 +176,7 @@ function commandToExec(command: string): {
|
|
|
162
176
|
|
|
163
177
|
/**
|
|
164
178
|
* Runs the project-level skill install for every source in
|
|
165
|
-
* `
|
|
179
|
+
* `DEFAULT_SKILL_SOURCES`, in order. Returns
|
|
166
180
|
* `{ ok: true, commands }` on success; throws a structured
|
|
167
181
|
* `errorInitSkillInstallFailed` on the first failure (subsequent
|
|
168
182
|
* sources are not attempted — the user opted into Prisma Next by
|
|
@@ -176,10 +190,7 @@ export async function runProjectLevelSkillInstall(ctx: {
|
|
|
176
190
|
readonly filesWritten: readonly string[];
|
|
177
191
|
}): Promise<{ readonly ok: true; readonly commands: readonly string[] }> {
|
|
178
192
|
const commands: string[] = [];
|
|
179
|
-
const installCommands =
|
|
180
|
-
formatSkillInstallCommand(ctx.pm, source),
|
|
181
|
-
formatClaudeSkillInstallCommand(ctx.pm, source),
|
|
182
|
-
]);
|
|
193
|
+
const installCommands = resolveProjectSkillInstallCommands(ctx.pm);
|
|
183
194
|
|
|
184
195
|
for (const command of installCommands) {
|
|
185
196
|
const { file, args } = commandToExec(command);
|
|
@@ -1,3 +1,5 @@
|
|
|
1
|
+
import { DEFAULT_CONTRACT_SOURCE_DIR } from '@prisma-next/config/config-types';
|
|
2
|
+
|
|
1
3
|
export type TargetId = 'postgres' | 'mongo';
|
|
2
4
|
export type AuthoringId = 'psl' | 'typescript';
|
|
3
5
|
|
|
@@ -11,9 +13,9 @@ export function targetLabel(target: TargetId): string {
|
|
|
11
13
|
|
|
12
14
|
export function defaultSchemaPath(authoring: AuthoringId): string {
|
|
13
15
|
if (authoring === 'typescript') {
|
|
14
|
-
return
|
|
16
|
+
return `${DEFAULT_CONTRACT_SOURCE_DIR}/contract.ts`;
|
|
15
17
|
}
|
|
16
|
-
return
|
|
18
|
+
return `${DEFAULT_CONTRACT_SOURCE_DIR}/contract.prisma`;
|
|
17
19
|
}
|
|
18
20
|
|
|
19
21
|
export function starterSchema(target: TargetId, authoring: AuthoringId): string {
|
|
@@ -42,9 +44,10 @@ export function schemaSample(target: TargetId, authoring: AuthoringId): string {
|
|
|
42
44
|
function schemaSamplePslPostgres(): string {
|
|
43
45
|
return `\`\`\`prisma
|
|
44
46
|
model User {
|
|
45
|
-
id
|
|
46
|
-
email
|
|
47
|
-
|
|
47
|
+
id Int @id @default(autoincrement())
|
|
48
|
+
email String @unique
|
|
49
|
+
username String?
|
|
50
|
+
name String?
|
|
48
51
|
}
|
|
49
52
|
\`\`\``;
|
|
50
53
|
}
|
|
@@ -52,9 +55,10 @@ model User {
|
|
|
52
55
|
function schemaSamplePslMongo(): string {
|
|
53
56
|
return `\`\`\`prisma
|
|
54
57
|
model User {
|
|
55
|
-
id
|
|
56
|
-
email
|
|
57
|
-
|
|
58
|
+
id ObjectId @id @map("_id")
|
|
59
|
+
email String @unique
|
|
60
|
+
username String?
|
|
61
|
+
name String?
|
|
58
62
|
@@map("users")
|
|
59
63
|
}
|
|
60
64
|
\`\`\``;
|
|
@@ -63,17 +67,16 @@ model User {
|
|
|
63
67
|
function schemaSampleTsPostgres(): string {
|
|
64
68
|
return `\`\`\`typescript
|
|
65
69
|
import { defineContract } from '@prisma-next/postgres/contract-builder';
|
|
66
|
-
import sqlFamily from '@prisma-next/postgres/family';
|
|
67
|
-
import postgresTarget from '@prisma-next/postgres/target';
|
|
68
70
|
|
|
69
71
|
export const contract = defineContract(
|
|
70
|
-
{
|
|
72
|
+
{},
|
|
71
73
|
({ field, model }) => ({
|
|
72
74
|
models: {
|
|
73
75
|
User: model('User', {
|
|
74
76
|
fields: {
|
|
75
77
|
id: field.id.uuidv7(),
|
|
76
78
|
email: field.text().unique(),
|
|
79
|
+
username: field.text().optional(),
|
|
77
80
|
name: field.text().optional(),
|
|
78
81
|
},
|
|
79
82
|
}),
|
|
@@ -86,11 +89,9 @@ export const contract = defineContract(
|
|
|
86
89
|
function schemaSampleTsMongo(): string {
|
|
87
90
|
return `\`\`\`typescript
|
|
88
91
|
import { defineContract } from '@prisma-next/mongo/contract-builder';
|
|
89
|
-
import mongoFamily from '@prisma-next/mongo/family';
|
|
90
|
-
import mongoTarget from '@prisma-next/mongo/target';
|
|
91
92
|
|
|
92
93
|
export const contract = defineContract(
|
|
93
|
-
{
|
|
94
|
+
{},
|
|
94
95
|
({ field, model }) => ({
|
|
95
96
|
models: {
|
|
96
97
|
User: model('User', {
|
|
@@ -98,6 +99,7 @@ export const contract = defineContract(
|
|
|
98
99
|
fields: {
|
|
99
100
|
_id: field.objectId(),
|
|
100
101
|
email: field.string(),
|
|
102
|
+
username: field.string().optional(),
|
|
101
103
|
name: field.string().optional(),
|
|
102
104
|
},
|
|
103
105
|
}),
|
|
@@ -113,6 +115,7 @@ function starterSchemaPslPostgres(): string {
|
|
|
113
115
|
model User {
|
|
114
116
|
id Int @id @default(autoincrement())
|
|
115
117
|
email String @unique
|
|
118
|
+
username String?
|
|
116
119
|
name String?
|
|
117
120
|
posts Post[]
|
|
118
121
|
createdAt DateTime @default(now())
|
|
@@ -135,10 +138,11 @@ function starterSchemaPslMongo(): string {
|
|
|
135
138
|
return `// use prisma-next
|
|
136
139
|
|
|
137
140
|
model User {
|
|
138
|
-
id
|
|
139
|
-
email
|
|
140
|
-
|
|
141
|
-
|
|
141
|
+
id ObjectId @id @map("_id")
|
|
142
|
+
email String @unique
|
|
143
|
+
username String?
|
|
144
|
+
name String?
|
|
145
|
+
posts Post[]
|
|
142
146
|
@@map("users")
|
|
143
147
|
}
|
|
144
148
|
|
|
@@ -155,17 +159,16 @@ model Post {
|
|
|
155
159
|
|
|
156
160
|
function starterSchemaTsPostgres(): string {
|
|
157
161
|
return `import { defineContract } from '@prisma-next/postgres/contract-builder';
|
|
158
|
-
import sqlFamily from '@prisma-next/postgres/family';
|
|
159
|
-
import postgresTarget from '@prisma-next/postgres/target';
|
|
160
162
|
|
|
161
163
|
export const contract = defineContract(
|
|
162
|
-
{
|
|
164
|
+
{},
|
|
163
165
|
({ field, model, rel }) => ({
|
|
164
166
|
models: {
|
|
165
167
|
User: model('User', {
|
|
166
168
|
fields: {
|
|
167
169
|
id: field.id.uuidv7(),
|
|
168
170
|
email: field.text().unique(),
|
|
171
|
+
username: field.text().optional(),
|
|
169
172
|
name: field.text().optional(),
|
|
170
173
|
createdAt: field.temporal.createdAt(),
|
|
171
174
|
updatedAt: field.temporal.updatedAt(),
|
|
@@ -196,11 +199,9 @@ export const contract = defineContract(
|
|
|
196
199
|
|
|
197
200
|
function starterSchemaTsMongo(): string {
|
|
198
201
|
return `import { defineContract } from '@prisma-next/mongo/contract-builder';
|
|
199
|
-
import mongoFamily from '@prisma-next/mongo/family';
|
|
200
|
-
import mongoTarget from '@prisma-next/mongo/target';
|
|
201
202
|
|
|
202
203
|
export const contract = defineContract(
|
|
203
|
-
{
|
|
204
|
+
{},
|
|
204
205
|
({ field, model, rel }) => ({
|
|
205
206
|
models: {
|
|
206
207
|
User: model('User', {
|
|
@@ -208,6 +209,7 @@ export const contract = defineContract(
|
|
|
208
209
|
fields: {
|
|
209
210
|
_id: field.objectId(),
|
|
210
211
|
email: field.string(),
|
|
212
|
+
username: field.string().optional(),
|
|
211
213
|
name: field.string().optional(),
|
|
212
214
|
},
|
|
213
215
|
relations: {
|
|
@@ -40,7 +40,14 @@ function envPlaceholderBody(target: TargetId): string {
|
|
|
40
40
|
if (target === 'postgres') {
|
|
41
41
|
lines.push('DATABASE_URL="postgresql://user:password@localhost:5432/mydb"');
|
|
42
42
|
} else {
|
|
43
|
-
lines.push(
|
|
43
|
+
lines.push(
|
|
44
|
+
'# Standalone local mongod / `docker run mongo:7` — no replica set required for first-run queries.',
|
|
45
|
+
);
|
|
46
|
+
lines.push(
|
|
47
|
+
'# Transactions and change streams need a replica set; add ?replicaSet=... only after initiating one.',
|
|
48
|
+
);
|
|
49
|
+
lines.push('');
|
|
50
|
+
lines.push('DATABASE_URL="mongodb://user:password@localhost:27017/mydb"');
|
|
44
51
|
}
|
|
45
52
|
lines.push('');
|
|
46
53
|
return lines.join('\n');
|
|
@@ -22,7 +22,7 @@ const user = await db.orm.users
|
|
|
22
22
|
.first();
|
|
23
23
|
|
|
24
24
|
// Your editor will show the type of user as
|
|
25
|
-
// { _id: ObjectId; email: string; name: string | null; posts: Post[] } | null
|
|
25
|
+
// { _id: ObjectId; email: string; username: string | null; name: string | null; posts: Post[] } | null
|
|
26
26
|
```
|
|
27
27
|
|
|
28
28
|
`db` connects to MongoDB lazily on the first query, so there is no manual `connect(...)` step in your application code. Call `await db.close()` if you need to release the underlying connection (typically only in tests or short-lived scripts). After `close()` the client is single-shot — any further query, `connect()`, or `runtime()` call rejects with `"Mongo client is closed"`. Construct a new `mongo({...})` if you need to use it again.
|
|
@@ -22,7 +22,7 @@ const user = await db.orm.User
|
|
|
22
22
|
.first();
|
|
23
23
|
|
|
24
24
|
// Your editor will show the type of user as
|
|
25
|
-
// { id: number; email: string;
|
|
25
|
+
// { id: number; email: string; username: string | null; name: string | null; createdAt: Date; posts: Post[] } | null
|
|
26
26
|
```
|
|
27
27
|
|
|
28
28
|
Your contract has two companion files in the same directory:
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
# {{projectName}}
|
|
2
|
+
|
|
3
|
+
Generated by `prisma-next init` with the Minimal template.
|
|
4
|
+
|
|
5
|
+
## First run
|
|
6
|
+
|
|
7
|
+
1. `{{runDbUp}}` — start the local MongoDB replica set with `mongodb-memory-server`. Data persists in `.mongo-data/` across restarts.
|
|
8
|
+
2. `{{runDev}}` — runs `src/index.ts`, which writes one row and reads it back.
|
|
9
|
+
|
|
10
|
+
## Available scripts
|
|
11
|
+
|
|
12
|
+
- `{{runDev}}` — run the Prisma Next sample script
|
|
13
|
+
|
|
14
|
+
### Database and migrations
|
|
15
|
+
|
|
16
|
+
- `{{runContractEmit}}` — emit contract artifacts after contract changes
|
|
17
|
+
- `{{runDbUp}}` — start the local MongoDB replica set with `mongodb-memory-server`. Data persists in `.mongo-data/` across restarts.
|
|
18
|
+
- `{{runDbDown}}` — stop the local MongoDB process (data preserved)
|
|
19
|
+
- `{{runDbReset}}` — stop and wipe `.mongo-data/` for a clean slate
|
|
20
|
+
- `{{runMigrationPlan}}` — create a MongoDB migration plan
|
|
21
|
+
- `{{runMigrate}}` — apply the planned MongoDB migration
|
|
22
|
+
- `{{runDbSeed}}` — insert sample users manually
|
|
23
|
+
|
|
24
|
+
## Prisma Next
|
|
25
|
+
|
|
26
|
+
Prisma Next setup is scaffolded in:
|
|
27
|
+
|
|
28
|
+
- `{{contractPath}}`
|
|
29
|
+
- `prisma-next.config.ts`
|
|
30
|
+
- `prisma/db.ts`
|
|
31
|
+
- `src/lib/prisma.ts`
|
|
32
|
+
|
|
33
|
+
For provider-specific Prisma Next reference docs, see `prisma-next.md`. Prisma Next skills live in the upstream `skills/` directory: https://github.com/prisma/prisma-next/tree/main/skills.
|
|
34
|
+
|
|
35
|
+
Node-based Prisma Next projects expect Node.js 24 LTS or newer.
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
# {{projectName}}
|
|
2
|
+
|
|
3
|
+
Generated by `prisma-next init` with the Minimal template.
|
|
4
|
+
|
|
5
|
+
## First run
|
|
6
|
+
|
|
7
|
+
1. `{{runDbInit}}` — applies your contract to the database (creates tables, signs the marker).
|
|
8
|
+
2. `{{runDev}}` — runs `src/index.ts`, which writes one row and reads it back.
|
|
9
|
+
|
|
10
|
+
## Available scripts
|
|
11
|
+
|
|
12
|
+
- `{{runDev}}` — run the Prisma Next sample script
|
|
13
|
+
|
|
14
|
+
### Database and migrations
|
|
15
|
+
|
|
16
|
+
- `{{runContractEmit}}` — emit contract artifacts after contract changes
|
|
17
|
+
- `{{runDbInit}}` — initialize database state manually
|
|
18
|
+
- `{{runDbUpdate}}` — update database state manually
|
|
19
|
+
- `{{runMigrationPlan}}` — create a migration plan
|
|
20
|
+
- `{{runMigrate}}` — apply a planned migration
|
|
21
|
+
- `{{runDbSeed}}` — insert sample users manually (run after `db:init`)
|
|
22
|
+
|
|
23
|
+
## Prisma Next
|
|
24
|
+
|
|
25
|
+
Prisma Next setup is scaffolded in:
|
|
26
|
+
|
|
27
|
+
- `{{contractPath}}`
|
|
28
|
+
- `prisma-next.config.ts`
|
|
29
|
+
- `prisma/db.ts`
|
|
30
|
+
- `src/lib/prisma.ts`
|
|
31
|
+
|
|
32
|
+
For provider-specific Prisma Next reference docs, see `prisma-next.md`. Prisma Next skills live in the upstream `skills/` directory: https://github.com/prisma/prisma-next/tree/main/skills.
|
|
33
|
+
|
|
34
|
+
Node-based Prisma Next projects expect Node.js 24 LTS or newer.
|
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
import { formatRunScriptCommand, type PackageManager } from '../detect-package-manager';
|
|
2
|
+
import type { TargetId } from './code-templates';
|
|
3
|
+
import { renderTemplate } from './render';
|
|
4
|
+
|
|
5
|
+
const sharedVariables = ['projectName', 'contractPath', 'runDev', 'runContractEmit'] as const;
|
|
6
|
+
|
|
7
|
+
export const postgresVariables = [
|
|
8
|
+
...sharedVariables,
|
|
9
|
+
'runDbInit',
|
|
10
|
+
'runDbUpdate',
|
|
11
|
+
'runMigrationPlan',
|
|
12
|
+
'runMigrate',
|
|
13
|
+
'runDbSeed',
|
|
14
|
+
] as const;
|
|
15
|
+
|
|
16
|
+
export const mongoVariables = [
|
|
17
|
+
...sharedVariables,
|
|
18
|
+
'runDbUp',
|
|
19
|
+
'runDbDown',
|
|
20
|
+
'runDbReset',
|
|
21
|
+
'runMigrationPlan',
|
|
22
|
+
'runMigrate',
|
|
23
|
+
'runDbSeed',
|
|
24
|
+
] as const;
|
|
25
|
+
|
|
26
|
+
type PostgresVars = Record<(typeof postgresVariables)[number], string>;
|
|
27
|
+
type MongoVars = Record<(typeof mongoVariables)[number], string>;
|
|
28
|
+
|
|
29
|
+
export function minimalProjectReadmeMd(
|
|
30
|
+
target: TargetId,
|
|
31
|
+
schemaPath: string,
|
|
32
|
+
projectName: string,
|
|
33
|
+
pm: PackageManager,
|
|
34
|
+
): string {
|
|
35
|
+
const run = (script: string): string => formatRunScriptCommand(pm, script);
|
|
36
|
+
const shared = {
|
|
37
|
+
projectName,
|
|
38
|
+
contractPath: schemaPath,
|
|
39
|
+
runDev: run('dev'),
|
|
40
|
+
runContractEmit: run('contract:emit'),
|
|
41
|
+
runMigrationPlan: run('migration:plan'),
|
|
42
|
+
runMigrate: run('migrate'),
|
|
43
|
+
runDbSeed: run('db:seed'),
|
|
44
|
+
};
|
|
45
|
+
|
|
46
|
+
if (target === 'mongo') {
|
|
47
|
+
const vars: MongoVars = {
|
|
48
|
+
...shared,
|
|
49
|
+
runDbUp: run('db:up'),
|
|
50
|
+
runDbDown: run('db:down'),
|
|
51
|
+
runDbReset: run('db:reset'),
|
|
52
|
+
};
|
|
53
|
+
return renderTemplate('readme-mongo.md', mongoVariables, vars);
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
const vars: PostgresVars = {
|
|
57
|
+
...shared,
|
|
58
|
+
runDbInit: run('db:init'),
|
|
59
|
+
runDbUpdate: run('db:update'),
|
|
60
|
+
};
|
|
61
|
+
return renderTemplate('readme-postgres.md', postgresVariables, vars);
|
|
62
|
+
}
|
package/src/commands/migrate.ts
CHANGED
|
@@ -43,9 +43,9 @@ import {
|
|
|
43
43
|
import { formatMigrationApplyCommandOutput } from '../utils/formatters/migrations';
|
|
44
44
|
import { formatStyledHeader } from '../utils/formatters/styled';
|
|
45
45
|
import type { CommonCommandOptions } from '../utils/global-flags';
|
|
46
|
-
import { type GlobalFlags,
|
|
46
|
+
import { type GlobalFlags, parseGlobalFlagsOrExit } from '../utils/global-flags';
|
|
47
47
|
import { handleResult } from '../utils/result-handler';
|
|
48
|
-
import { TerminalUI } from '../utils/terminal-ui';
|
|
48
|
+
import { createTerminalUI, type TerminalUI } from '../utils/terminal-ui';
|
|
49
49
|
|
|
50
50
|
interface MigrateCommandOptions extends CommonCommandOptions {
|
|
51
51
|
readonly db?: string;
|
|
@@ -319,13 +319,10 @@ export function createMigrateCommand(): Command {
|
|
|
319
319
|
'Target contract reference (hash, prefix, ref name, migration dir name, <dir>^, or ./path)',
|
|
320
320
|
)
|
|
321
321
|
.action(async (options: MigrateCommandOptions) => {
|
|
322
|
-
const flags =
|
|
322
|
+
const flags = parseGlobalFlagsOrExit(options);
|
|
323
323
|
const startTime = Date.now();
|
|
324
324
|
|
|
325
|
-
const ui =
|
|
326
|
-
color: flags.color,
|
|
327
|
-
interactive: flags.interactive,
|
|
328
|
-
});
|
|
325
|
+
const ui = createTerminalUI(flags);
|
|
329
326
|
|
|
330
327
|
const result = await executeMigrateCommand(options, flags, ui, startTime);
|
|
331
328
|
|
|
@@ -17,8 +17,8 @@ import {
|
|
|
17
17
|
} from '../utils/command-helpers';
|
|
18
18
|
import { formatStyledHeader } from '../utils/formatters/styled';
|
|
19
19
|
import type { CommonCommandOptions } from '../utils/global-flags';
|
|
20
|
-
import { type GlobalFlags,
|
|
21
|
-
import { TerminalUI } from '../utils/terminal-ui';
|
|
20
|
+
import { type GlobalFlags, parseGlobalFlagsOrExit } from '../utils/global-flags';
|
|
21
|
+
import { createTerminalUI, type TerminalUI } from '../utils/terminal-ui';
|
|
22
22
|
import { INTEGRITY_FAILED, OK, PRECONDITION } from './migration-check/exit-codes';
|
|
23
23
|
|
|
24
24
|
interface MigrationCheckOptions extends CommonCommandOptions {
|
|
@@ -335,8 +335,8 @@ export function createMigrationCheckCommand(): Command {
|
|
|
335
335
|
.argument('[migration]', 'Migration reference (directory name or hash) to check')
|
|
336
336
|
.option('--config <path>', 'Path to prisma-next.config.ts')
|
|
337
337
|
.action(async (target: string | undefined, options: MigrationCheckOptions) => {
|
|
338
|
-
const flags =
|
|
339
|
-
const ui =
|
|
338
|
+
const flags = parseGlobalFlagsOrExit(options);
|
|
339
|
+
const ui = createTerminalUI(flags);
|
|
340
340
|
|
|
341
341
|
let result: MigrationCheckResult;
|
|
342
342
|
let exitCode: number;
|