prisma-next 0.7.0 → 0.8.0-dev.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/cli.mjs +10 -9
- package/dist/cli.mjs.map +1 -1
- package/dist/{client-BCnP7cHo.mjs → client-4d26awB-.mjs} +8 -5
- package/dist/client-4d26awB-.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.mjs +2 -2
- package/dist/commands/db-schema.mjs +1 -1
- package/dist/commands/db-sign.mjs +1 -1
- package/dist/commands/db-update.mjs +2 -2
- package/dist/commands/db-verify.mjs +1 -1
- package/dist/commands/migration-apply.mjs +1 -1
- package/dist/commands/migration-plan.d.mts.map +1 -1
- package/dist/commands/migration-plan.mjs +1 -1
- package/dist/commands/migration-show.mjs +1 -1
- package/dist/commands/migration-status.mjs +1 -1
- package/dist/{contract-emit-9DBda5Ou.mjs → contract-emit-BhKR-D9Y.mjs} +2 -2
- package/dist/{contract-emit-9DBda5Ou.mjs.map → contract-emit-BhKR-D9Y.mjs.map} +1 -1
- package/dist/{contract-emit-B77TsJqf.mjs → contract-emit-DLc5GYbr.mjs} +6 -2
- package/dist/contract-emit-DLc5GYbr.mjs.map +1 -0
- package/dist/{contract-infer-ByxhPjpW.mjs → contract-infer-Bnla2kuK.mjs} +2 -2
- package/dist/{contract-infer-ByxhPjpW.mjs.map → contract-infer-Bnla2kuK.mjs.map} +1 -1
- package/dist/{db-verify-Czm5T-J4.mjs → db-verify-DitNxDiE.mjs} +2 -2
- package/dist/{db-verify-Czm5T-J4.mjs.map → db-verify-DitNxDiE.mjs.map} +1 -1
- package/dist/exports/control-api.d.mts.map +1 -1
- package/dist/exports/control-api.mjs +2 -2
- package/dist/exports/index.mjs +1 -1
- package/dist/exports/init-output.mjs +1 -1
- package/dist/{init-BRKnARU6.mjs → init-CVBXQidr.mjs} +342 -207
- package/dist/init-CVBXQidr.mjs.map +1 -0
- package/dist/{inspect-live-schema-DxdBd4Er.mjs → inspect-live-schema-CyzAzPzF.mjs} +2 -2
- package/dist/{inspect-live-schema-DxdBd4Er.mjs.map → inspect-live-schema-CyzAzPzF.mjs.map} +1 -1
- package/dist/{migration-command-scaffold-BdV8JYXV.mjs → migration-command-scaffold-Jp1rosw8.mjs} +2 -2
- package/dist/{migration-command-scaffold-BdV8JYXV.mjs.map → migration-command-scaffold-Jp1rosw8.mjs.map} +1 -1
- package/dist/{migration-plan-mRu5K81L.mjs → migration-plan-q1pPoOCf.mjs} +9 -3
- package/dist/migration-plan-q1pPoOCf.mjs.map +1 -0
- package/dist/{migration-status-By9G5p2H.mjs → migration-status-Do4Ei0i_.mjs} +2 -2
- package/dist/{migration-status-By9G5p2H.mjs.map → migration-status-Do4Ei0i_.mjs.map} +1 -1
- package/dist/{output-B16Kefzx.mjs → output-nBJ6NvsE.mjs} +12 -11
- package/dist/{output-B16Kefzx.mjs.map → output-nBJ6NvsE.mjs.map} +1 -1
- package/package.json +11 -11
- package/dist/agent-skill-mongo.md +0 -138
- package/dist/agent-skill-postgres.md +0 -106
- package/dist/client-BCnP7cHo.mjs.map +0 -1
- package/dist/contract-emit-B77TsJqf.mjs.map +0 -1
- package/dist/init-BRKnARU6.mjs.map +0 -1
- package/dist/migration-plan-mRu5K81L.mjs.map +0 -1
|
@@ -90,20 +90,21 @@ function renderInitOutro(ui, output, flags) {
|
|
|
90
90
|
*/
|
|
91
91
|
function buildNextSteps(options) {
|
|
92
92
|
const steps = [];
|
|
93
|
-
|
|
93
|
+
let stepNumber = 1;
|
|
94
|
+
const push = (text) => {
|
|
95
|
+
steps.push(`${stepNumber}. ${text}`);
|
|
96
|
+
stepNumber += 1;
|
|
97
|
+
};
|
|
98
|
+
push("Set DATABASE_URL in your environment (export it or add it to .env).");
|
|
94
99
|
if (!options.contractEmitted) {
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
steps.push(`2. Edit your schema at ${options.schemaPath}, then re-run \`${options.emitCommand}\`.`);
|
|
101
|
-
steps.push("3. Open prisma-next.md for a quick reference on how to write your first typed query.");
|
|
102
|
-
steps.push("4. The .agents/skills/prisma-next/SKILL.md file is wired up for AI-coding agents in this project.");
|
|
103
|
-
}
|
|
100
|
+
push(`Emit the contract: \`${options.emitCommand}\``);
|
|
101
|
+
push(`Edit your schema at ${options.schemaPath}, then re-run the emit command.`);
|
|
102
|
+
} else push(`Edit your schema at ${options.schemaPath}, then re-run \`${options.emitCommand}\`.`);
|
|
103
|
+
push("Open prisma-next.md for a quick reference on how to write your first typed query.");
|
|
104
|
+
if (options.skillRegistered) push("@prisma-next/skills is registered with your agent runtime — open the project in your IDE and ask the agent to add a model, run a query, or plan a migration.");
|
|
104
105
|
return steps;
|
|
105
106
|
}
|
|
106
107
|
//#endregion
|
|
107
108
|
export { renderInitOutro as i, buildNextSteps as n, formatInitJson as r, InitOutputSchema as t };
|
|
108
109
|
|
|
109
|
-
//# sourceMappingURL=output-
|
|
110
|
+
//# sourceMappingURL=output-nBJ6NvsE.mjs.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"output-
|
|
1
|
+
{"version":3,"file":"output-nBJ6NvsE.mjs","names":[],"sources":["../src/commands/init/output.ts"],"sourcesContent":["import { type } from 'arktype';\nimport type { GlobalFlags } from '../../utils/global-flags';\nimport type { TerminalUI } from '../../utils/terminal-ui';\n\n/**\n * arktype schema for the structured success document `init --json` writes\n * to stdout (FR1.5). The same shape backs the human-readable outro\n * renderer (FR10), so the two output modes carry identical information.\n *\n * `target` is normalised to the user-facing flag value (`mongodb` rather\n * than the internal `mongo`) so consumers can round-trip the document\n * straight into a follow-up `--target` invocation.\n *\n * The `ok: true` literal is the documented success/error discriminator —\n * see [Style Guide § JSON Semantics](../../../../../../../docs/CLI%20Style%20Guide.md#json-semantics).\n * Error envelopes (`CliErrorEnvelope`) carry `ok: false` so consumers can\n * branch with `if (doc.ok)` without inspecting the rest of the structure.\n */\nexport const InitOutputSchema = type({\n ok: 'true',\n target: \"'postgres'|'mongodb'\",\n authoring: \"'psl'|'typescript'\",\n schemaPath: 'string',\n filesWritten: 'string[]',\n /**\n * FR9.1 — files removed from disk during this run. Populated only on\n * re-init when previously-emitted contract artefacts (`contract.json`,\n * `contract.d.ts`, `start-/end-contract.*`, `ops.json`,\n * `migration.json`) were left behind by an earlier run. Empty on a\n * green-field init.\n */\n filesDeleted: 'string[]',\n packagesInstalled: {\n skipped: 'boolean',\n deps: 'string[]',\n devDeps: 'string[]',\n },\n contractEmitted: 'boolean',\n nextSteps: 'string[]',\n warnings: 'string[]',\n});\n\nexport type InitOutput = typeof InitOutputSchema.infer;\n\n/**\n * Serialises the output document for `--json`. Sorted keys are not enforced\n * — `JSON.stringify` preserves insertion order, and the schema field order\n * is the documented order, which matches what users will see when they\n * `jq .` the result.\n */\nexport function formatInitJson(output: InitOutput): string {\n return JSON.stringify(output, null, 2);\n}\n\n/**\n * Renders the human-readable outro on stderr (FR10.1). Re-uses the same\n * data structure as the JSON output so the two stay in lock-step.\n *\n * Warnings come before \"Next steps\" because they describe state the user\n * needs to be aware of before acting on the next-steps list.\n */\nexport function renderInitOutro(ui: TerminalUI, output: InitOutput, flags: GlobalFlags): void {\n if (flags.quiet || flags.json) {\n return;\n }\n\n for (const warning of output.warnings) {\n ui.warn(warning);\n }\n\n const lines: string[] = [];\n lines.push(`Target: ${output.target}`);\n lines.push(`Authoring: ${output.authoring}`);\n lines.push(`Schema: ${output.schemaPath}`);\n lines.push('');\n lines.push('Files written:');\n for (const file of output.filesWritten) {\n lines.push(` • ${file}`);\n }\n\n if (output.filesDeleted.length > 0) {\n lines.push('');\n lines.push('Files deleted (stale contract artefacts):');\n for (const file of output.filesDeleted) {\n lines.push(` • ${file}`);\n }\n }\n\n if (!output.packagesInstalled.skipped) {\n lines.push('');\n lines.push('Packages installed:');\n for (const dep of output.packagesInstalled.deps) {\n lines.push(` • ${dep}`);\n }\n for (const dep of output.packagesInstalled.devDeps) {\n lines.push(` • ${dep} (dev)`);\n }\n }\n\n lines.push('');\n lines.push('Next steps:');\n for (const step of output.nextSteps) {\n lines.push(` ${step}`);\n }\n\n ui.note(lines.join('\\n'), 'Done');\n}\n\n/**\n * Builds the `nextSteps` array from the resolved scaffold state. Steps are\n * ordered by the workflow a user needs to follow: configure connection →\n * (emit if not yet done) → run a starter query → docs / agent skill.\n *\n * The strings are stable and human-readable; agents wanting to act on them\n * should match on substrings (e.g. \"DATABASE_URL\") rather than exact text,\n * since copy may evolve.\n */\nexport function buildNextSteps(options: {\n readonly target: 'postgres' | 'mongodb';\n readonly contractEmitted: boolean;\n readonly emitCommand: string;\n readonly schemaPath: string;\n /**\n * Whether the project-level `@prisma-next/skills` install actually ran\n * and succeeded during this `init`. When false (the user passed\n * `--no-skill` or `--no-install`, so the install was skipped), the\n * \"registered with your agent runtime\" step is omitted — the skip is\n * already surfaced in the warnings array with a manual-install hint.\n */\n readonly skillRegistered: boolean;\n}): string[] {\n const steps: string[] = [];\n let stepNumber = 1;\n const push = (text: string): void => {\n steps.push(`${stepNumber}. ${text}`);\n stepNumber += 1;\n };\n push('Set DATABASE_URL in your environment (export it or add it to .env).');\n if (!options.contractEmitted) {\n push(`Emit the contract: \\`${options.emitCommand}\\``);\n push(`Edit your schema at ${options.schemaPath}, then re-run the emit command.`);\n } else {\n push(`Edit your schema at ${options.schemaPath}, then re-run \\`${options.emitCommand}\\`.`);\n }\n push('Open prisma-next.md for a quick reference on how to write your first typed query.');\n if (options.skillRegistered) {\n push(\n '@prisma-next/skills is registered with your agent runtime — open the project in your IDE and ask the agent to add a model, run a query, or plan a migration.',\n );\n }\n return steps;\n}\n"],"mappings":";;;;;;;;;;;;;;;;AAkBA,MAAa,mBAAmB,KAAK;CACnC,IAAI;CACJ,QAAQ;CACR,WAAW;CACX,YAAY;CACZ,cAAc;;;;;;;;CAQd,cAAc;CACd,mBAAmB;EACjB,SAAS;EACT,MAAM;EACN,SAAS;EACV;CACD,iBAAiB;CACjB,WAAW;CACX,UAAU;CACX,CAAC;;;;;;;AAUF,SAAgB,eAAe,QAA4B;CACzD,OAAO,KAAK,UAAU,QAAQ,MAAM,EAAE;;;;;;;;;AAUxC,SAAgB,gBAAgB,IAAgB,QAAoB,OAA0B;CAC5F,IAAI,MAAM,SAAS,MAAM,MACvB;CAGF,KAAK,MAAM,WAAW,OAAO,UAC3B,GAAG,KAAK,QAAQ;CAGlB,MAAM,QAAkB,EAAE;CAC1B,MAAM,KAAK,cAAc,OAAO,SAAS;CACzC,MAAM,KAAK,cAAc,OAAO,YAAY;CAC5C,MAAM,KAAK,cAAc,OAAO,aAAa;CAC7C,MAAM,KAAK,GAAG;CACd,MAAM,KAAK,iBAAiB;CAC5B,KAAK,MAAM,QAAQ,OAAO,cACxB,MAAM,KAAK,OAAO,OAAO;CAG3B,IAAI,OAAO,aAAa,SAAS,GAAG;EAClC,MAAM,KAAK,GAAG;EACd,MAAM,KAAK,4CAA4C;EACvD,KAAK,MAAM,QAAQ,OAAO,cACxB,MAAM,KAAK,OAAO,OAAO;;CAI7B,IAAI,CAAC,OAAO,kBAAkB,SAAS;EACrC,MAAM,KAAK,GAAG;EACd,MAAM,KAAK,sBAAsB;EACjC,KAAK,MAAM,OAAO,OAAO,kBAAkB,MACzC,MAAM,KAAK,OAAO,MAAM;EAE1B,KAAK,MAAM,OAAO,OAAO,kBAAkB,SACzC,MAAM,KAAK,OAAO,IAAI,QAAQ;;CAIlC,MAAM,KAAK,GAAG;CACd,MAAM,KAAK,cAAc;CACzB,KAAK,MAAM,QAAQ,OAAO,WACxB,MAAM,KAAK,KAAK,OAAO;CAGzB,GAAG,KAAK,MAAM,KAAK,KAAK,EAAE,OAAO;;;;;;;;;;;AAYnC,SAAgB,eAAe,SAalB;CACX,MAAM,QAAkB,EAAE;CAC1B,IAAI,aAAa;CACjB,MAAM,QAAQ,SAAuB;EACnC,MAAM,KAAK,GAAG,WAAW,IAAI,OAAO;EACpC,cAAc;;CAEhB,KAAK,sEAAsE;CAC3E,IAAI,CAAC,QAAQ,iBAAiB;EAC5B,KAAK,wBAAwB,QAAQ,YAAY,IAAI;EACrD,KAAK,uBAAuB,QAAQ,WAAW,iCAAiC;QAEhF,KAAK,uBAAuB,QAAQ,WAAW,kBAAkB,QAAQ,YAAY,KAAK;CAE5F,KAAK,oFAAoF;CACzF,IAAI,QAAQ,iBACV,KACE,+JACD;CAEH,OAAO"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "prisma-next",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.8.0-dev.2",
|
|
4
4
|
"license": "Apache-2.0",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"sideEffects": false,
|
|
@@ -14,6 +14,14 @@
|
|
|
14
14
|
"dependencies": {
|
|
15
15
|
"@clack/prompts": "^1.3.0",
|
|
16
16
|
"@dagrejs/dagre": "^3.0.0",
|
|
17
|
+
"@prisma-next/config": "0.8.0-dev.2",
|
|
18
|
+
"@prisma-next/contract": "0.8.0-dev.2",
|
|
19
|
+
"@prisma-next/emitter": "0.8.0-dev.2",
|
|
20
|
+
"@prisma-next/errors": "0.8.0-dev.2",
|
|
21
|
+
"@prisma-next/framework-components": "0.8.0-dev.2",
|
|
22
|
+
"@prisma-next/migration-tools": "0.8.0-dev.2",
|
|
23
|
+
"@prisma-next/psl-printer": "0.8.0-dev.2",
|
|
24
|
+
"@prisma-next/utils": "0.8.0-dev.2",
|
|
17
25
|
"arktype": "^2.1.29",
|
|
18
26
|
"c12": "^3.3.4",
|
|
19
27
|
"clipanion": "4.0.0-rc.4",
|
|
@@ -26,18 +34,10 @@
|
|
|
26
34
|
"pathe": "^2.0.3",
|
|
27
35
|
"string-width": "^8.2.1",
|
|
28
36
|
"strip-ansi": "^7.2.0",
|
|
29
|
-
"wrap-ansi": "^10.0.0"
|
|
30
|
-
"@prisma-next/config": "0.7.0",
|
|
31
|
-
"@prisma-next/contract": "0.7.0",
|
|
32
|
-
"@prisma-next/emitter": "0.7.0",
|
|
33
|
-
"@prisma-next/errors": "0.7.0",
|
|
34
|
-
"@prisma-next/framework-components": "0.7.0",
|
|
35
|
-
"@prisma-next/migration-tools": "0.7.0",
|
|
36
|
-
"@prisma-next/psl-printer": "0.7.0",
|
|
37
|
-
"@prisma-next/utils": "0.7.0"
|
|
37
|
+
"wrap-ansi": "^10.0.0"
|
|
38
38
|
},
|
|
39
39
|
"devDependencies": {
|
|
40
|
-
"@prisma-next/cli": "0.
|
|
40
|
+
"@prisma-next/cli": "0.8.0-dev.2"
|
|
41
41
|
},
|
|
42
42
|
"publishConfig": {
|
|
43
43
|
"access": "public"
|
|
@@ -1,138 +0,0 @@
|
|
|
1
|
-
# Prisma Next — project skill
|
|
2
|
-
|
|
3
|
-
This project uses **Prisma Next** with **MongoDB** via `@prisma-next/mongo`. Prisma Next lets the user define data models in a contract file and query them with a fully typed ORM.
|
|
4
|
-
|
|
5
|
-
## Files
|
|
6
|
-
|
|
7
|
-
- **Contract**: `{{schemaPath}}` ({{authoringLabel}} authoring) — the user's data models. Edit this to add or change models.
|
|
8
|
-
- **Config**: `prisma-next.config.ts` — tells the CLI where the contract is and how to connect to the database. Loads `.env` via `dotenv/config`.
|
|
9
|
-
- **Database client**: `{{schemaDir}}/db.ts` — `import { db } from '{{dbImportPath}}'`. This is the entry point for all queries.
|
|
10
|
-
- **Generated files** (do not edit by hand):
|
|
11
|
-
- `{{schemaDir}}/contract.json` — compiled contract, used at runtime
|
|
12
|
-
- `{{schemaDir}}/contract.d.ts` — TypeScript types for the contract, used for autocomplete and type checking
|
|
13
|
-
|
|
14
|
-
## Commands
|
|
15
|
-
|
|
16
|
-
- `{{pkgRun}} contract emit` — regenerate `contract.json` and `contract.d.ts` after changing the contract
|
|
17
|
-
- `{{pkgRun}} db init` — bootstrap a database to match the contract (creates collections, indexes, constraints). Additive only — won't drop existing structures.
|
|
18
|
-
- `{{pkgRun}} db update` — update the database to match the current contract. Prompts for confirmation on destructive changes. Use `--dry-run` to preview.
|
|
19
|
-
- `{{pkgRun}} migration plan` — create a new migration from contract changes (offline, no database needed). Use `--name <slug>` to name it.
|
|
20
|
-
- `{{pkgRun}} migration apply` — apply pending migrations to the database
|
|
21
|
-
- `{{pkgRun}} migration status` — show which migrations are applied and which are pending
|
|
22
|
-
- `{{pkgRun}} migration show <name>` — show details of a specific migration
|
|
23
|
-
|
|
24
|
-
## How to write queries
|
|
25
|
-
|
|
26
|
-
Use the ORM (`db.orm`). Each root accessor is the lowercased plural form emitted by `prisma-next contract emit` (typically the `@@map`-ped collection name) — for `model User { @@map("users") }` use `db.orm.users`, for `model Post { @@map("posts") }` use `db.orm.posts`. The Mongo facade has no raw-SQL surface. Two escape hatches exist for cases the ORM can't express; both are covered under "Escape hatches" below.
|
|
27
|
-
|
|
28
|
-
```typescript
|
|
29
|
-
import { db } from '{{dbImportPath}}';
|
|
30
|
-
|
|
31
|
-
// Find one record
|
|
32
|
-
const user = await db.orm.users
|
|
33
|
-
.where({ email: 'alice@example.com' })
|
|
34
|
-
.first();
|
|
35
|
-
// Returns { _id: ObjectId; email: string; ... } | null
|
|
36
|
-
|
|
37
|
-
// Find multiple records — `.all()` returns an AsyncIterableResult, consume
|
|
38
|
-
// either as an async iterable (below) or by `await`ing it to materialise an Array.
|
|
39
|
-
for await (const user of db.orm.users
|
|
40
|
-
.select('_id', 'email')
|
|
41
|
-
.take(10)
|
|
42
|
-
.all()) {
|
|
43
|
-
// Each `user` is { _id: ObjectId; email: string }
|
|
44
|
-
}
|
|
45
|
-
// Returns AsyncIterableResult<{ _id: ObjectId; email: string }>
|
|
46
|
-
|
|
47
|
-
// Filter, order, limit
|
|
48
|
-
const recentPosts = await db.orm.posts
|
|
49
|
-
.where({ authorId: userId })
|
|
50
|
-
.orderBy({ createdAt: -1 })
|
|
51
|
-
.select('_id', 'title', 'createdAt')
|
|
52
|
-
.take(50)
|
|
53
|
-
.all();
|
|
54
|
-
|
|
55
|
-
// Include relations (reference relations only — embedded relations come back automatically)
|
|
56
|
-
const usersWithPosts = await db.orm.users
|
|
57
|
-
.select('_id', 'email')
|
|
58
|
-
.include('posts')
|
|
59
|
-
.take(10)
|
|
60
|
-
.all();
|
|
61
|
-
```
|
|
62
|
-
|
|
63
|
-
### Key ORM methods
|
|
64
|
-
|
|
65
|
-
- `.where({ field: value, ... })` — filter records by an equality object. Pass a raw filter expression for `$gt`/`$in`/`$regex` etc.
|
|
66
|
-
- `.select('field1', 'field2', ...)` — pick which fields to return
|
|
67
|
-
- `.orderBy({ field: 1 | -1 })` — sort results (1 = ascending, -1 = descending)
|
|
68
|
-
- `.take(n)` / `.skip(n)` — limit and offset
|
|
69
|
-
- `.all()` — execute and return all matching records as an `AsyncIterableResult`
|
|
70
|
-
- `.first()` — execute with limit 1 and return the first matching row, or `null`
|
|
71
|
-
- `.include('relationName')` — eager-load a reference relation (`$lookup`); embedded relations are already part of the row
|
|
72
|
-
- `.variant('VariantName')` — narrow a polymorphic collection to a discriminator value
|
|
73
|
-
|
|
74
|
-
## Escape hatches
|
|
75
|
-
|
|
76
|
-
The ORM covers the common cases. When you genuinely need something it can't express, prefer these — in order — over reaching for `db.runtime()` (which is an internal executor surface, not a `mongodb`-driver handle):
|
|
77
|
-
|
|
78
|
-
1. **Typed raw aggregations — `db.query`.** The facade exposes a `db.query` builder that runs aggregation pipelines through the same runtime + middleware + codec stack as `db.orm`, so results stay typed against the contract. Use this for `$lookup`/`$facet`/`$graphLookup`/window-function pipelines that the ORM doesn't surface.
|
|
79
|
-
|
|
80
|
-
2. **Direct `mongodb` driver control — `mongoClient` binding.** If you need a raw `MongoClient` (e.g. for transactions, change streams, sessions, or a driver feature Prisma Next doesn't expose), construct one yourself and pass it to `mongo({ mongoClient, dbName, contractJson })`. Your code keeps the `MongoClient` reference and uses it directly, while the same `db` object still gives you the typed ORM surface:
|
|
81
|
-
|
|
82
|
-
```typescript
|
|
83
|
-
import { MongoClient } from 'mongodb';
|
|
84
|
-
import mongo from '@prisma-next/mongo/runtime';
|
|
85
|
-
import type { Contract } from './contract.d';
|
|
86
|
-
import contractJson from './contract.json' with { type: 'json' };
|
|
87
|
-
|
|
88
|
-
const client = new MongoClient(process.env['DATABASE_URL']!);
|
|
89
|
-
await client.connect();
|
|
90
|
-
|
|
91
|
-
export const db = mongo<Contract>({ contractJson, mongoClient: client, dbName: 'mydb' });
|
|
92
|
-
|
|
93
|
-
const session = client.startSession();
|
|
94
|
-
try {
|
|
95
|
-
await session.withTransaction(async () => {
|
|
96
|
-
await db.orm.users.createAll([{ /* ... */ }]);
|
|
97
|
-
});
|
|
98
|
-
} finally {
|
|
99
|
-
await session.endSession();
|
|
100
|
-
}
|
|
101
|
-
```
|
|
102
|
-
|
|
103
|
-
## Rules
|
|
104
|
-
|
|
105
|
-
- **Never hand-edit** `contract.json` or `contract.d.ts`. Always regenerate them with `contract emit`.
|
|
106
|
-
- **Always emit after contract changes.** When you modify `{{schemaPath}}`, run `{{pkgRun}} contract emit` before writing any code that depends on the new or changed models.
|
|
107
|
-
- **Don't restructure `db.ts`.** It's scaffolded by init and works as-is. `db` connects lazily on the first query — there is no `db.connect(...)` step.
|
|
108
|
-
- **Root accessors are emitter-driven.** Use the lowercased plural collection name (e.g. `db.orm.users`, `db.orm.posts`) — not the PascalCase model name. Re-run `{{pkgRun}} contract emit` if a new model's accessor isn't appearing on `db.orm`.
|
|
109
|
-
- **Connection string** is `DATABASE_URL` in `.env`. If the user reports connection errors, check this value and the `.env` file.
|
|
110
|
-
- **Transactions and change streams** require a MongoDB **replica set**. The Mongo facade does not yet expose `db.transaction(...)` — for now, use the `mongoClient` escape hatch above to drive transactions/sessions directly. See the quick reference for dev-environment options; the typed transaction API is tracked under [TML-2313](https://linear.app/prisma-company/issue/TML-2313/mongo-dev-replica-set-story-is-missing-transactions-change-streams).
|
|
111
|
-
- **Don't reach for `db.runtime()`** as an escape hatch. It returns the internal executor (`MongoRuntime`), not a `mongodb` `MongoClient` or `Db`. Use `db.query` for raw aggregations and the `mongoClient` binding for direct driver control.
|
|
112
|
-
|
|
113
|
-
## Workflow for common tasks
|
|
114
|
-
|
|
115
|
-
**User wants to add a new model or field:**
|
|
116
|
-
1. Edit `{{schemaPath}}`
|
|
117
|
-
2. Run `{{pkgRun}} contract emit`
|
|
118
|
-
3. Write query code using `db.orm.<collection>` (lowercased plural, see Rules above)
|
|
119
|
-
|
|
120
|
-
**User wants to query data:**
|
|
121
|
-
1. Import `db` from `{{dbImportPath}}`
|
|
122
|
-
2. Use `db.orm.<collection>` with `.where()`, `.select()`, `.all()`, `.first()`, etc.
|
|
123
|
-
|
|
124
|
-
**User wants to set up or change the database connection:**
|
|
125
|
-
1. Edit `DATABASE_URL` in `.env`
|
|
126
|
-
2. The config file (`prisma-next.config.ts`) reads it automatically via `dotenv/config`
|
|
127
|
-
|
|
128
|
-
**User wants to set up the database for the first time:**
|
|
129
|
-
1. Run `{{pkgRun}} db init`
|
|
130
|
-
|
|
131
|
-
**User wants to update the database after changing the contract:**
|
|
132
|
-
1. Quick path: `{{pkgRun}} db update` — compares the database to the contract and applies changes directly
|
|
133
|
-
2. Migration path (for production workflows):
|
|
134
|
-
- `{{pkgRun}} migration plan --name describe-the-change` — creates a migration
|
|
135
|
-
- `{{pkgRun}} migration apply` — applies pending migrations
|
|
136
|
-
|
|
137
|
-
**User wants to check what migrations need to be applied:**
|
|
138
|
-
1. Run `{{pkgRun}} migration status`
|
|
@@ -1,106 +0,0 @@
|
|
|
1
|
-
# Prisma Next — project skill
|
|
2
|
-
|
|
3
|
-
This project uses **Prisma Next** with **PostgreSQL** via `@prisma-next/postgres`. Prisma Next lets the user define data models in a contract file and query them with a fully typed ORM.
|
|
4
|
-
|
|
5
|
-
## Files
|
|
6
|
-
|
|
7
|
-
- **Contract**: `{{schemaPath}}` ({{authoringLabel}} authoring) — the user's data models. Edit this to add or change models.
|
|
8
|
-
- **Config**: `prisma-next.config.ts` — tells the CLI where the contract is and how to connect to the database. Loads `.env` via `dotenv/config`.
|
|
9
|
-
- **Database client**: `{{schemaDir}}/db.ts` — `import { db } from '{{dbImportPath}}'`. This is the entry point for all queries.
|
|
10
|
-
- **Generated files** (do not edit by hand):
|
|
11
|
-
- `{{schemaDir}}/contract.json` — compiled contract, used at runtime
|
|
12
|
-
- `{{schemaDir}}/contract.d.ts` — TypeScript types for the contract, used for autocomplete and type checking
|
|
13
|
-
|
|
14
|
-
## Commands
|
|
15
|
-
|
|
16
|
-
- `{{pkgRun}} contract emit` — regenerate `contract.json` and `contract.d.ts` after changing the contract
|
|
17
|
-
- `{{pkgRun}} db init` — bootstrap a database to match the contract (creates tables, indexes, constraints). Additive only — won't drop existing structures.
|
|
18
|
-
- `{{pkgRun}} db update` — update the database to match the current contract. Prompts for confirmation on destructive changes. Use `--dry-run` to preview.
|
|
19
|
-
- `{{pkgRun}} migration plan` — create a new migration from contract changes (offline, no database needed). Use `--name <slug>` to name it.
|
|
20
|
-
- `{{pkgRun}} migration apply` — apply pending migrations to the database
|
|
21
|
-
- `{{pkgRun}} migration status` — show which migrations are applied and which are pending
|
|
22
|
-
- `{{pkgRun}} migration show <name>` — show details of a specific migration
|
|
23
|
-
|
|
24
|
-
## How to write queries
|
|
25
|
-
|
|
26
|
-
Always use the ORM (`db.orm`). Only fall back to `db.sql` if the user explicitly asks for raw SQL or the ORM doesn't support the operation.
|
|
27
|
-
|
|
28
|
-
```typescript
|
|
29
|
-
import { db } from '{{dbImportPath}}';
|
|
30
|
-
|
|
31
|
-
// Find one record
|
|
32
|
-
const user = await db.orm.User
|
|
33
|
-
.where(user => user.email.eq('alice@example.com'))
|
|
34
|
-
.first();
|
|
35
|
-
// Returns { id: number; email: string; ... } | null
|
|
36
|
-
|
|
37
|
-
// Find multiple records
|
|
38
|
-
const users = await db.orm.User
|
|
39
|
-
.select('id', 'email')
|
|
40
|
-
.take(10)
|
|
41
|
-
.all();
|
|
42
|
-
// Returns Array<{ id: number; email: string }>
|
|
43
|
-
|
|
44
|
-
// Filter, order, limit
|
|
45
|
-
const recentPosts = await db.orm.Post
|
|
46
|
-
.where(post => post.authorId.eq(userId))
|
|
47
|
-
.orderBy(post => post.createdAt.desc())
|
|
48
|
-
.select('id', 'title', 'createdAt')
|
|
49
|
-
.take(50)
|
|
50
|
-
.all();
|
|
51
|
-
|
|
52
|
-
// Include relations
|
|
53
|
-
const usersWithPosts = await db.orm.User
|
|
54
|
-
.select('id', 'email')
|
|
55
|
-
.include('posts', post =>
|
|
56
|
-
post.select('id', 'title').orderBy(p => p.createdAt.desc()).take(5)
|
|
57
|
-
)
|
|
58
|
-
.take(10)
|
|
59
|
-
.all();
|
|
60
|
-
```
|
|
61
|
-
|
|
62
|
-
### Key ORM methods
|
|
63
|
-
|
|
64
|
-
- `.where(predicate)` — filter records. Predicate receives a model accessor with `.eq()`, `.neq()`, `.ilike()`, `.lt()`, `.gt()`, etc.
|
|
65
|
-
- `.select('field1', 'field2', ...)` — pick which fields to return
|
|
66
|
-
- `.orderBy(accessor => accessor.field.asc()` or `.desc())` — sort results
|
|
67
|
-
- `.take(n)` — limit number of results
|
|
68
|
-
- `.all()` — execute and return all matching records as an array
|
|
69
|
-
- `.first()` — execute and return the first matching record, or `null`
|
|
70
|
-
- `.first({ id: value })` — find a single record by primary key, or `null`
|
|
71
|
-
- `.include('relation', builder => ...)` — eager-load a relation
|
|
72
|
-
|
|
73
|
-
## Rules
|
|
74
|
-
|
|
75
|
-
- **Never hand-edit** `contract.json` or `contract.d.ts`. Always regenerate them with `contract emit`.
|
|
76
|
-
- **Always emit after contract changes.** When you modify `{{schemaPath}}`, run `{{pkgRun}} contract emit` before writing any code that depends on the new or changed models.
|
|
77
|
-
- **Don't restructure `db.ts`.** It's scaffolded by init and works as-is.
|
|
78
|
-
- **Use `db.orm` for queries**, not `db.sql`. The ORM is the primary query surface.
|
|
79
|
-
- **Connection string** is `DATABASE_URL` in `.env`. If the user reports connection errors, check this value and the `.env` file.
|
|
80
|
-
|
|
81
|
-
## Workflow for common tasks
|
|
82
|
-
|
|
83
|
-
**User wants to add a new model or field:**
|
|
84
|
-
1. Edit `{{schemaPath}}`
|
|
85
|
-
2. Run `{{pkgRun}} contract emit`
|
|
86
|
-
3. Write query code using `db.orm.ModelName`
|
|
87
|
-
|
|
88
|
-
**User wants to query data:**
|
|
89
|
-
1. Import `db` from `{{dbImportPath}}`
|
|
90
|
-
2. Use `db.orm.ModelName` with `.where()`, `.select()`, `.all()`, `.first()`, etc.
|
|
91
|
-
|
|
92
|
-
**User wants to set up or change the database connection:**
|
|
93
|
-
1. Edit `DATABASE_URL` in `.env`
|
|
94
|
-
2. The config file (`prisma-next.config.ts`) reads it automatically via `dotenv/config`
|
|
95
|
-
|
|
96
|
-
**User wants to set up the database for the first time:**
|
|
97
|
-
1. Run `{{pkgRun}} db init`
|
|
98
|
-
|
|
99
|
-
**User wants to update the database after changing the contract:**
|
|
100
|
-
1. Quick path: `{{pkgRun}} db update` — compares the database to the contract and applies changes directly
|
|
101
|
-
2. Migration path (for production workflows):
|
|
102
|
-
- `{{pkgRun}} migration plan --name describe-the-change` — creates a migration
|
|
103
|
-
- `{{pkgRun}} migration apply` — applies pending migrations
|
|
104
|
-
|
|
105
|
-
**User wants to check what migrations need to be applied:**
|
|
106
|
-
1. Run `{{pkgRun}} migration status`
|