@prisma/dev 0.24.16 → 0.25.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 ADDED
@@ -0,0 +1,346 @@
1
+ # `@prisma/dev`
2
+
3
+ A local Prisma Postgres server for development and testing.
4
+
5
+ It runs a real PostgreSQL database in your own Node process — no Docker, no separate service to install — and exposes it over both a plain TCP connection string and the `prisma+postgres://` HTTP protocol that deployed Prisma Postgres uses.
6
+
7
+ > **Not for production.** This is a development and testing tool.
8
+
9
+ There are two ways to use it:
10
+
11
+ - **[`@prisma/dev/vite`](#vite-plugin)** — a Vite plugin that runs a database for the lifetime of your dev server.
12
+ - **[`@prisma/dev`](#programmatic-server)** — start and stop a server from your own code.
13
+
14
+ ## Install
15
+
16
+ ```bash
17
+ npm install --save-dev @prisma/dev
18
+ ```
19
+
20
+ ## Vite plugin
21
+
22
+ Add the plugin and your app gets a database whenever `vite dev` runs.
23
+
24
+ ```ts
25
+ import { prismaDev } from "@prisma/dev/vite";
26
+ import { defineConfig } from "vite";
27
+
28
+ export default defineConfig({
29
+ plugins: [prismaDev({ name: "my-app" })],
30
+ });
31
+ ```
32
+
33
+ That sets two environment variables before any of your code runs:
34
+
35
+ | Variable | Value |
36
+ | --------------------- | ---------------------------------------------------------------------------------- |
37
+ | `DATABASE_URL` | TCP connection string for the main database |
38
+ | `SHADOW_DATABASE_URL` | TCP connection string for the shadow database, which `prisma migrate dev` requires |
39
+
40
+ Set `name` per project. It decides where the database files are persisted, so two projects left on the default name would share data.
41
+
42
+ Production builds are untouched. See [When the plugin does nothing](#when-the-plugin-does-nothing).
43
+
44
+ ### Applying migrations and seeding
45
+
46
+ A fresh database has no schema. You could migrate from application code, but then migration logic ships in your production artifact for the sake of local development. `onDatabaseReady` keeps it in the dev-only config instead.
47
+
48
+ ```ts
49
+ import { execFile } from "node:child_process";
50
+ import { promisify } from "node:util";
51
+
52
+ import { prismaDev } from "@prisma/dev/vite";
53
+ import { defineConfig } from "vite";
54
+
55
+ const execFileAsync = promisify(execFile);
56
+
57
+ export default defineConfig({
58
+ plugins: [
59
+ prismaDev({
60
+ name: "my-app",
61
+ async onDatabaseReady({ env }) {
62
+ await execFileAsync("npx", ["prisma", "migrate", "deploy"], { env: { ...process.env, ...env } });
63
+ },
64
+ }),
65
+ ],
66
+ });
67
+ ```
68
+
69
+ The hook runs before any application or framework code executes, so nothing can observe an unprepared database. `vite dev` waits for it, so a slow migration delays startup.
70
+
71
+ The plugin does this work in Vite's `configureServer` hook. That is the earliest hook which only runs for a dev server that is actually going to serve. Anything earlier also runs for config resolutions that serve nothing — a bare `resolveConfig` call, or the child compiler a framework builds from your config file — and starting a database for those is wrong. See [Frameworks that build a child compiler](#frameworks-that-build-a-child-compiler).
72
+
73
+ One hook is registered, but it can do as much as you need. Sequence with plain `await`:
74
+
75
+ ```ts
76
+ async onDatabaseReady(database) {
77
+ await migrate(database);
78
+ await seed(database);
79
+ }
80
+ ```
81
+
82
+ It receives:
83
+
84
+ | Property | Description |
85
+ | ------------------- | ---------------------------------------------------------------------- |
86
+ | `databaseUrl` | TCP connection string for the main database |
87
+ | `shadowDatabaseUrl` | TCP connection string for the shadow database |
88
+ | `prismaPostgresUrl` | The `prisma+postgres://` connection string. Carries an API key |
89
+ | `env` | The variables this plugin manages, mapped to the local database |
90
+ | `name` | The server name |
91
+ | `owned` | `false` when the plugin attached to a database another process started |
92
+
93
+ #### Pass `database.env` when spawning a subprocess
94
+
95
+ Spread `env` explicitly, as the example above does.
96
+
97
+ It names the local database directly, so a migration cannot pick up a variable this plugin does not manage. That matters if you disabled one with [`env: { databaseUrl: false }`](#keeping-your-own-value-for-a-variable): the ambient `process.env` still holds your own value, while `database.env` holds the local one.
98
+
99
+ #### The hook runs once per database
100
+
101
+ Not once per dev server. Two dev servers sharing one database in one process — see [Frameworks that build a child compiler](#frameworks-that-build-a-child-compiler) — run it once. A config restart that does not change the plugin's own options keeps the running database, so it does not re-run the hook either.
102
+
103
+ It does run when the plugin attached to a database that `prisma dev` was already running. Make it idempotent — `prisma migrate deploy` already is.
104
+
105
+ #### If the hook fails
106
+
107
+ The plugin shuts the database down and Vite fails to start. It will not serve your app against a half-prepared database.
108
+
109
+ ### Throwaway databases
110
+
111
+ By default the database is persisted under `name` and survives dev server restarts. Set `persistenceMode: "stateless"` to get a clean, in-memory database on every start instead.
112
+
113
+ ```ts
114
+ prismaDev({
115
+ name: "my-app",
116
+ test: { name: "my-tests", persistenceMode: "stateless" },
117
+ });
118
+ ```
119
+
120
+ The clearest use is integration tests, where every run should start from a known state. See [Databases for Vitest runs](#databases-for-vitest-runs).
121
+
122
+ For interactive `vite dev` it is worth understanding what you give up:
123
+
124
+ - Editing `vite.config.ts` or an `.env` file restarts the dev server, which **discards the data**. Anything you seeded or entered by hand is gone.
125
+ - `onDatabaseReady` gets an empty database on every start, so it has to do the full migrate and seed each time.
126
+ - The database is invisible to `prisma dev ls`, `stop`, and `rm`, and cannot be shared with a running `prisma dev`.
127
+ - Ports are chosen from whatever is free rather than the defaults below, unless you set them explicitly. That matters if you want to `psql` in at a predictable address.
128
+
129
+ ### Sharing a database with `prisma dev`
130
+
131
+ If a `prisma dev` server is already running under the same `name`, the plugin uses it instead of starting its own, and leaves it running when Vite exits. `database.owned` is `false` in that case.
132
+
133
+ This applies to the default `stateful` mode only. A `stateless` plugin always starts its own throwaway database, so asking for one never hands you a persistent database by surprise.
134
+
135
+ So this works, with both terminals on one database:
136
+
137
+ ```bash
138
+ # terminal 1
139
+ npx prisma dev --name my-app
140
+
141
+ # terminal 2 — vite.config.ts uses prismaDev({ name: "my-app" })
142
+ npx vite dev
143
+ ```
144
+
145
+ ### Frameworks that build a child compiler
146
+
147
+ Some frameworks compile part of your app with a second, internal Vite server built from the same config file. React Router does this, and so the plugin has to cope with being instantiated twice in one process.
148
+
149
+ The plugin does. Two acquisitions of one `name` converge on one database: the second joins the first rather than restarting it, the database stays up until the last of them lets go, and `onDatabaseReady` runs once. Nothing needs configuring for this.
150
+
151
+ Two consequences are worth knowing:
152
+
153
+ - **A child compiler runs without a database.** React Router builds its child compiler before any dev server is configured, and drops the hook the plugin uses. Nothing in a child compiler queries a database, but code that runs there cannot expect `DATABASE_URL` to be set.
154
+ - **Commands that only resolve the config start nothing.** `react-router typegen` and `react-router build` load `vite.config.ts` without serving anything, so no database starts and no migration runs. A typecheck is a typecheck.
155
+
156
+ ### Reading a database's address from another process
157
+
158
+ `process.env` reaches the Vite process and its children. It does not reach a `psql` session, a migration you run by hand, or anything else started from a second terminal.
159
+
160
+ Look the server up by name instead:
161
+
162
+ ```ts
163
+ import { getPrismaDevServerConnection } from "@prisma/dev";
164
+
165
+ const connection = await getPrismaDevServerConnection({ name: "my-app" });
166
+
167
+ console.log(connection?.databaseUrl); // postgres://...
168
+ ```
169
+
170
+ It resolves to `null` when no server of that name is running. Only `stateful` servers are visible — a `stateless` one writes no state and can only be addressed through the process that started it.
171
+
172
+ This is why hardcoding a connection string into an `.env` file is not necessary, and why pinning ports is not either. See [Ports and stable addresses](#ports-and-stable-addresses).
173
+
174
+ ### Connection strings never reach the browser
175
+
176
+ The plugin writes to `process.env` and nothing else. It deliberately does not use Vite's `define` or `import.meta.env`.
177
+
178
+ Those inline values into browser assets. Both the TCP connection string and the `prisma+postgres://` URL carry credentials, so inlining either would hand database access to every visitor of your app.
179
+
180
+ Server-side code, SSR loaders, and Prisma Client all read `process.env`, which is the surface that actually needs these values.
181
+
182
+ ### The database's lifetime
183
+
184
+ The database runs inside the Vite process, so it cannot outlive it. There is no background daemon and nothing to clean up by hand.
185
+
186
+ Quitting Vite normally, or with `Ctrl-C`, shuts the database down cleanly. A `kill -9` takes the database down with the process; the next start recovers on its own, though it may pause briefly to do so.
187
+
188
+ Editing `vite.config.ts` or an `.env` file restarts the Vite dev server. The database is only restarted along with it if the edit changed this plugin's own options, in which case the new options have to win. Otherwise the running database is carried over untouched. Either way your data is persisted, unless you opted into [a throwaway database](#throwaway-databases). Editing application code does not touch the database.
189
+
190
+ ### When the plugin does nothing
191
+
192
+ No database is started when:
193
+
194
+ - the command is `vite build`
195
+ - the command is `vite preview`
196
+ - the config was loaded by Vitest, which reads the same `vite.config.ts`
197
+ - the config was only resolved, and no dev server was configured — see [Frameworks that build a child compiler](#frameworks-that-build-a-child-compiler)
198
+
199
+ See [Databases for Vitest runs](#databases-for-vitest-runs) to allow one under Vitest.
200
+
201
+ ### Databases for Vitest runs
202
+
203
+ Vitest reads the same `vite.config.ts`, so by default the plugin starts nothing there. A plain `vitest run` should not boot a database as a config side effect.
204
+
205
+ Opt in with the `test` option:
206
+
207
+ ```ts
208
+ prismaDev({
209
+ name: "my-app",
210
+ test: { name: "my-app-test", persistenceMode: "stateless" },
211
+ });
212
+ ```
213
+
214
+ Each entry under `test` replaces the corresponding option above it. Everything you do not name is inherited. Here the test run gets its own throwaway database and inherits the `env` mapping, while `vite dev` keeps its persistent `my-app` one.
215
+
216
+ The port options are inherited too, but this database is `stateless`, so it takes whatever ports are free rather than the ones it inherited — see [Throwaway databases](#throwaway-databases). Set the ports under `test` if a run needs a predictable address. Note that pinning them collides with a `vite dev` on the same ports if both run at once.
217
+
218
+ `test: {}` opts in with the surrounding options unchanged, so `vite dev` and `vitest run` share one database.
219
+
220
+ Setting `test` has no effect outside Vitest, so there is one config file and no branching in it.
221
+
222
+ ### Keeping your own value for a variable
223
+
224
+ The plugin owns every variable it writes. If your environment already points one somewhere else, the local database wins and the plugin logs a warning naming what it displaced.
225
+
226
+ This is deliberate. Many frameworks load `.env` into `process.env` before the plugin runs, so deferring to the existing value would leave a project whose `.env` already names `DATABASE_URL` with a running database that nothing connects to.
227
+
228
+ To keep your own value, disable that variable:
229
+
230
+ ```ts
231
+ prismaDev({
232
+ env: { databaseUrl: false },
233
+ name: "my-app",
234
+ });
235
+ ```
236
+
237
+ The plugin then never writes `DATABASE_URL`, and reports no conflict. `database.env` in [`onDatabaseReady`](#applying-migrations-and-seeding) still carries the local connection string.
238
+
239
+ A variable that already holds exactly the value the plugin was going to write is not warned about. There is no conflict to report.
240
+
241
+ ### Using the `prisma+postgres://` URL
242
+
243
+ Off by default, because it embeds an API key. Opt in by naming a variable:
244
+
245
+ ```ts
246
+ prismaDev({
247
+ env: { prismaPostgresUrl: "PRISMA_DATABASE_URL" },
248
+ name: "my-app",
249
+ });
250
+ ```
251
+
252
+ ### Options
253
+
254
+ | Option | Default | Description |
255
+ | ----------------------- | ----------------------- | -------------------------------------------------------------- |
256
+ | `name` | `"default"` | Server name, which decides where data is persisted |
257
+ | `onDatabaseReady` | — | Async hook to migrate and seed before Vite starts |
258
+ | `persistenceMode` | `"stateful"` | `"stateless"` for a clean in-memory database on every start |
259
+ | `env.databaseUrl` | `"DATABASE_URL"` | Variable for the main TCP connection string. `false` to skip |
260
+ | `env.shadowDatabaseUrl` | `"SHADOW_DATABASE_URL"` | Variable for the shadow TCP connection string. `false` to skip |
261
+ | `env.prismaPostgresUrl` | `false` | Variable for the `prisma+postgres://` URL. Name it to opt in |
262
+ | `test` | `false` | Options for Vitest runs. An object opts in, `false` stays off |
263
+ | `port` | `51213` | HTTP server port |
264
+ | `databasePort` | `51214` | Main database port |
265
+ | `shadowDatabasePort` | `51215` | Shadow database port |
266
+ | `streamsPort` | `51216` | Colocated Prisma Streams server port |
267
+ | `debug` | `false` | Log the server's debug output |
268
+
269
+ ### Ports and stable addresses
270
+
271
+ A named `stateful` server records the ports it settled on and reuses them the next time it starts. So `name` alone is enough for a stable address.
272
+
273
+ To find out which ports a server actually landed on, [look it up by name](#reading-a-databases-address-from-another-process). That is also the recommended way to reach the database from outside the Vite process.
274
+
275
+ Passing a port explicitly changes the failure mode, deliberately:
276
+
277
+ - **A default port that is taken moves.** The plugin falls back to another free port rather than refusing to start.
278
+ - **A port you named is bound or the start fails.** It is never silently moved. A caller who names a port has something outside this process depending on it, and pointing that something at a port nothing is listening on is worse than a clear error.
279
+
280
+ A `stateless` database records nothing, so it takes any free port on every start unless you name one.
281
+
282
+ ## Programmatic server
283
+
284
+ For test harnesses, scripts, and integrations outside Vite:
285
+
286
+ ```ts
287
+ import { startPrismaDevServer } from "@prisma/dev";
288
+
289
+ const server = await startPrismaDevServer({ name: "my-tests" });
290
+
291
+ console.log(server.database.prismaORMConnectionString); // postgres://...
292
+ console.log(server.ppg.url); // prisma+postgres://...
293
+
294
+ await server.close();
295
+ ```
296
+
297
+ The resolved server exposes:
298
+
299
+ | Property | Description |
300
+ | ---------------- | ------------------------------------------------------------------------------- |
301
+ | `database` | `connectionString`, `prismaORMConnectionString`, and a `psql` `terminalCommand` |
302
+ | `shadowDatabase` | The same shape, for `prisma migrate` |
303
+ | `ppg.url` | The `prisma+postgres://` connection string, API key included |
304
+ | `http.url` | Base URL of the HTTP server |
305
+ | `name` | The server name |
306
+ | `close()` | Shuts everything down |
307
+
308
+ ### Persistence
309
+
310
+ `startPrismaDevServer()` defaults to `persistenceMode: "stateless"`, which discards all data when the server closes. That suits tests.
311
+
312
+ Pass `persistenceMode: "stateful"` to persist data under the given `name`, matching `prisma dev` and the Vite plugin's default.
313
+
314
+ ```ts
315
+ const server = await startPrismaDevServer({ name: "my-app", persistenceMode: "stateful" });
316
+ ```
317
+
318
+ ### Ports
319
+
320
+ Defaults are `51213` for HTTP, `51214` for the database, `51215` for the shadow database, and `51216` for the Prisma Streams server. Override any of them with `port`, `databasePort`, `shadowDatabasePort`, and `streamsPort`.
321
+
322
+ A default that is taken falls back to another free port. A port you pass explicitly is bound or the call rejects — with `PortNotAvailableError` when nothing can bind it, and `PortBelongsToAnotherServerError` when another Prisma Dev server already holds it.
323
+
324
+ Starting a second server under a `name` that is already running rejects with `ServerAlreadyRunningError`.
325
+
326
+ ### Looking up a running server
327
+
328
+ ```ts
329
+ import { getPrismaDevServerConnection } from "@prisma/dev";
330
+
331
+ const connection = await getPrismaDevServerConnection({ name: "my-app" });
332
+ ```
333
+
334
+ Resolves the `databaseUrl`, `shadowDatabaseUrl`, and `prismaPostgresUrl` of a running `stateful` server by name, or `null` when none is running. Use it from a process that did not start the database and therefore cannot read its `process.env`.
335
+
336
+ ### Experimental
337
+
338
+ `server.experimental` exposes change-data-capture events, query insights, and the colocated Prisma Streams endpoint. These are experimental and may change without notice.
339
+
340
+ ## Requirements
341
+
342
+ The Vite plugin supports Vite 5, 6, 7, and 8. `vite` is an optional peer dependency, so installing `@prisma/dev` without Vite is fine — the plugin module references Vite for types only.
343
+
344
+ ## License
345
+
346
+ ISC
@@ -0,0 +1 @@
1
+ import{a as q}from"./chunk-662IKR3V.js";import{a as W,b as _,f as x}from"./chunk-PP43TGA5.js";import{a as O,b as R,c as A,d as I,e as v,f as M,h as $,i as U}from"./chunk-HFONW2ZS.js";import{d as C,j as y}from"./chunk-DWY47FQV.js";import{b as N}from"./chunk-DOJAPHLY.js";import Z from"find-my-way";async function F(r,e){let{port:t}=e;if(e.dryRun)return{async close(){},port:t,url:`http://localhost:${t}`};let n=!!e.debug,[{registerAccelerateRoutes:s},{registerUtilityRoutes:i},{createHTTPServer:l}]=await Promise.all([import("./accelerate-TRR43YJ5.js"),import("./utility-YEFNU35E.js"),import("./server-QR7P25DP.js")]),a={databaseDumpPath:e.databaseDumpPath,db:r,debug:n,name:e.name,port:t,shadowDBPort:e.shadowDatabasePort},o=Z({defaultRoute(u,p){N(p,"Not Found",404)},ignoreTrailingSlash:!1});s(o,a,n),i(o,a,n);let c=await l({router:o}).listen(t);return e.port=c.port,a.port=c.port,{async close(){let{Engine:u}=await import("./engine-YTEOPVUP.js");await Promise.allSettled([c.close(),u.stopAll()])},port:c.port,url:c.url}}import{isDeepStrictEqual as j}from"util";var ee="application/json",E={connection:"close","content-type":ee},T={apiVersion:"durable.streams/schema-registry/v1",schema:{additionalProperties:!0,properties:{applicationName:{type:["string","null"]},durationMs:{type:"number"},groupKey:{type:["string","null"]},query:{type:"string"},queryId:{type:"string"},reads:{type:"number"},rowsReturned:{type:"number"},tables:{items:{type:"string"},type:"array"},timestamp:{format:"date-time",type:"string"}},required:["durationMs","query","queryId","reads","rowsReturned","tables","timestamp"],type:"object"},search:{aliases:{applicationName:"applicationName",groupKey:"groupKey",queryId:"queryId"},fields:{applicationName:{bindings:[{jsonPointer:"/applicationName",version:1}],exact:!0,exists:!0,kind:"keyword"},eventTime:{bindings:[{jsonPointer:"/timestamp",version:1}],column:!0,exact:!0,exists:!0,kind:"date",sortable:!0},groupKey:{bindings:[{jsonPointer:"/groupKey",version:1}],exact:!0,exists:!0,kind:"keyword"},queryId:{bindings:[{jsonPointer:"/queryId",version:1}],exact:!0,exists:!0,kind:"keyword"}},primaryTimestampField:"eventTime"}};function H(r){return{...r,snapshot:async e=>await re(r.url,e)}}async function K(r){let e={serverUrl:r.serverUrl,sqlitePath:r.sqlitePath,streamName:R,url:v(r.serverUrl,R)};await ne(e);let t=new k({debug:r.debug,streamUrl:e.url}),n=r.bridge.subscribe(s=>{t.enqueue(s)});return{close:async()=>{n(),await t.close()},experimental:e}}async function re(r,e){if(!r)return W();let t=await te(r);return _(t,e)}async function te(r){let e=await fetch(`${r}?offset=-1&format=json`,{headers:{connection:"close"}});if(!e.ok)throw new Error(`Failed to read ${r}: HTTP ${e.status}`);return await e.json()}var k=class{#t;#r;#e;constructor(e){this.#t=e.debug,this.#r=Promise.resolve(),this.#e=e.streamUrl}enqueue(e){e.length!==0&&(this.#r=this.#r.then(async()=>{await ie(this.#e,e),this.#t&&console.debug(`[streams] appended ${e.length} query insight record(s) to ${this.#e}`)}).catch(t=>{console.error("[streams] failed to ingest query insights into prisma-queries",t)}))}async close(){await this.#r}};async function ne(r){let e=await fetch(r.url,{headers:E,method:"PUT"});if(!e.ok)throw await P(e,`Failed to create ${r.streamName}`);let t=await ae(r);if(se(t,r.streamName))return;if(t.currentVersion>0)throw new Error(`Failed to install schema for ${r.streamName}: existing schema registry is incompatible and requires a lens migration`);let n=await fetch(`${r.url}/_schema`,{body:JSON.stringify(T),headers:E,method:"POST"});if(!n.ok)throw await P(n,`Failed to install schema for ${r.streamName}`)}function se(r,e){if(r.currentVersion<=0||r.schema!==e)return!1;let t=r.schemas[String(r.currentVersion)];return j(t,T.schema)&&j(r.search,T.search)}async function ae(r){let e=await fetch(`${r.url}/_schema`,{headers:E,method:"GET"});if(!e.ok)throw await P(e,`Failed to inspect schema for ${r.streamName}`);return await e.json()}async function ie(r,e){let t=await fetch(r,{body:JSON.stringify(e),headers:E,method:"POST"});if(!t.ok)throw await P(t,"Failed to append to prisma-queries")}async function P(r,e){let t=await r.text().catch(()=>"");return new Error(`${e}: HTTP ${r.status}${t?` ${t}`:""}`)}import{randomUUID as oe}from"crypto";import{setTimeout as L}from"timers/promises";import{isDeepStrictEqual as V}from"util";import{process as le}from"std-env";var ce="application/json",S={connection:"close","content-type":ce},ue={apiVersion:"durable.streams/profile/v1",profile:{kind:"state-protocol",touch:{enabled:!0,onMissingBefore:"coarse"}}},B={apiVersion:"durable.streams/schema-registry/v1",schema:{additionalProperties:!0,properties:{headers:{properties:{operation:{type:"string"},timestamp:{format:"date-time",type:"string"}},required:["timestamp","operation"],type:"object"},key:{type:"string"},type:{type:"string"}},required:["type","key","headers"],type:"object"},search:{aliases:{rowKey:"key",table:"type"},fields:{eventTime:{bindings:[{jsonPointer:"/headers/timestamp",version:1}],column:!0,exact:!0,exists:!0,kind:"date",sortable:!0},key:{bindings:[{jsonPointer:"/key",version:1}],exact:!0,exists:!0,kind:"keyword"},operation:{bindings:[{jsonPointer:"/headers/operation",version:1}],exact:!0,exists:!0,kind:"keyword"},type:{bindings:[{jsonPointer:"/type",version:1}],exact:!0,exists:!0,kind:"keyword"}},primaryTimestampField:"eventTime"}};async function G(r){let{dbServer:e,debug:t,name:n,persistenceMode:s,port:i,queryInsightsBridge:l,walBridge:a}=r,o=s==="stateless",c=o?Ee(n):n,u=I(c),p=!o&&await C(u);le.env.DS_LOCAL_DATA_ROOT=A(),o&&await y(u);let m=await de({debug:t,hadExistingStreamsData:p,name:c,port:i}),g={serverUrl:m.exports.http.url,sqlitePath:m.exports.sqlite.path,streamName:O,url:v(m.exports.http.url)};try{await he(g);let d=await K({bridge:l,debug:t,serverUrl:m.exports.http.url,sqlitePath:m.exports.sqlite.path}),h=new D({dbServer:e,debug:t,streamUrl:g.url}),z=a.subscribe(X=>{h.enqueue(X)});return{close:async()=>{z(),await h.close(),await d.close(),await m.close(),await L(100),o&&await y(u)},experimental:g,experimentalQueryInsights:d.experimental}}catch(d){throw await m.close().catch(()=>{}),await L(100),o&&await y(u).catch(()=>{}),d}}var me=["database disk image is malformed","duplicate column name:","file is not a database","malformed database schema","no such column:","no such table:","schema_version row missing after migration","unexpected schema version:"];async function de(r){let{debug:e,hadExistingStreamsData:t,name:n,port:s}=r,{startLocalDurableStreamsServer:i}=await import("@prisma/streams-local"),l=()=>i({hostname:"127.0.0.1",name:n,port:s});try{return await l()}catch(a){if(!t||!pe(a))throw a;return console.warn(`[streams] resetting incompatible durable streams data for "${n}"`),e&&console.debug(`[streams] original durable streams startup error for "${n}"`,a),await y(I(n)),await l()}}function pe(r){return ge(r).map(t=>t.toLowerCase()).some(t=>me.some(n=>t.includes(n)))}function ge(r){let e=[],t=[r],n=new Set;for(;t.length>0;){let s=t.shift();if(!(s==null||n.has(s))){if(n.add(s),typeof s=="string"){e.push(s);continue}if(s instanceof AggregateError)for(let i of s.errors)t.push(i);if(s instanceof Error){e.push(s.message);let i=s.cause;i!==void 0&&t.push(i)}}}return e}var D=class{#t;#r;#e;#n;#s;constructor(e){this.#t=e.dbServer,this.#r=e.debug,this.#e=Promise.resolve(),this.#n=e.streamUrl,this.#s=new Set}enqueue(e){e.length!==0&&(this.#e=this.#e.then(async()=>{let t=await this.#i(e);t.length!==0&&(await fe(this.#n,t),this.#r&&console.debug(`[streams] appended ${t.length} state-protocol record(s) to ${this.#n}`))}).catch(t=>{console.error("[streams] failed to ingest WAL events into prisma-wal",t)}))}async close(){await this.#e}async#i(e){let t=[],n=new Date().toISOString();for(let s of e)t.push(...await this.#o(s,n));return t}async#o(e,t){let n=`${e.schema}.${e.table}`,s=await this.#t.getPrimaryKeyColumns(e.schema,e.table),i=J(e.record),l=J(e.oldRecord),a=this.#a(n,s,l),o=this.#a(n,s,i),c=e.txid===""?void 0:e.txid;return e.type==="insert"?i&&o?[{headers:{operation:"insert",timestamp:t,txid:c},key:o,old_value:null,type:n,value:i}]:[]:e.type==="delete"?l&&a?[{headers:{operation:"delete",timestamp:t,txid:c},key:a,old_value:l,type:n,value:null}]:[]:!i||!l||!a||!o?[]:a!==o?[{headers:{operation:"delete",timestamp:t,txid:c},key:a,old_value:l,type:n,value:null},{headers:{operation:"insert",timestamp:t,txid:c},key:o,old_value:null,type:n,value:i}]:[{headers:{operation:"update",timestamp:t,txid:c},key:o,old_value:l,type:n,value:i}]}#a(e,t,n){if(!n)return null;let s=ve(n,t);return s||(this.#s.has(e)||(this.#s.add(e),console.warn(`[streams] falling back to full-row keys for ${e} because no primary key could be resolved`)),we(n))}};async function he(r){let e=await fetch(r.url,{headers:S,method:"PUT"});if(!e.ok)throw await f(e,`Failed to create ${r.streamName}`);let t=await fetch(`${r.url}/_profile`,{body:JSON.stringify(ue),headers:S,method:"POST"});if(!t.ok)throw await f(t,`Failed to configure ${r.streamName}`);let n=await ye(r);if(Se(n,r.streamName))return;if(n.currentVersion>0)throw new Error(`Failed to install schema for ${r.streamName}: existing schema registry is incompatible and requires a lens migration`);let s=await fetch(`${r.url}/_schema`,{body:JSON.stringify(B),headers:S,method:"POST"});if(!s.ok)throw await f(s,`Failed to install schema for ${r.streamName}`)}async function ye(r){let e=await fetch(`${r.url}/_schema`,{headers:S,method:"GET"});if(!e.ok)throw await f(e,`Failed to inspect schema for ${r.streamName}`);return await e.json()}function Se(r,e){if(r.currentVersion<=0||r.schema!==e)return!1;let t=r.schemas[String(r.currentVersion)];return V(t,B.schema)&&V(r.search,B.search)}async function fe(r,e){let t=await fetch(r,{body:JSON.stringify(e),headers:S,method:"POST"});if(!t.ok)throw await f(t,"Failed to append to prisma-wal")}async function f(r,e){let t=await r.text().catch(()=>"");return new Error(`${e}: HTTP ${r.status}${t?` ${t}`:""}`)}function ve(r,e){if(e.length===0)return null;let t=[];for(let n of e){if(!Object.prototype.hasOwnProperty.call(r,n))return null;let s=be(r[n]);if(s==null)return null;t.push(e.length===1?s:`${n}=${s}`)}return t.join("|")}function we(r){return JSON.stringify(Q(r))}function Q(r){return Array.isArray(r)?r.map(e=>Q(e)):r&&typeof r=="object"?Object.fromEntries(Object.entries(r).sort(([e],[t])=>e.localeCompare(t)).map(([e,t])=>[e,Q(t)])):r}function J(r){return r?structuredClone(r):null}function be(r){if(r===null)return"null";if(r===void 0)return"undefined";if(typeof r=="string")return r;if(typeof r=="number")return Number.isFinite(r)?String(r):null;if(typeof r=="bigint")return r.toString();if(typeof r=="boolean")return r?"true":"false";try{return JSON.stringify(r)}catch{return null}}function Ee(r){return`${r}.${oe().replaceAll("-","")}`}async function tr(r){let e=await M.createExclusively(r),t=null,n=null,s=null,i=null;try{[t,s]=await Promise.all([x("database",e),x("shadow_database",e)]);let l,a;e.dryRun?(l=Ie(),a=Te(),i=ke()):(l=await t.attachWalEventBridge(),a=await t.attachQueryInsightsBridge(),i=await G({dbServer:t,debug:e.debug,name:e.name,port:e.streamsPort,persistenceMode:e.persistenceMode,queryInsightsBridge:a,walBridge:l})),n=await F(t,e);let o=Pe(t,s,n,e);await e.writeServerDump(o,e.dryRun?{}:{queryInsights:i.experimentalQueryInsights,streams:i.experimental});let c=Re(l),u=H(i.experimentalQueryInsights),p=t,m=s,g=n,d=i,h=async()=>{c.close(),await Y(e,[g,d,p,m])};return{close:h,dbServer:t,httpServer:n,server:{...o,close:h,experimental:{queryInsights:u,streams:i.experimental,wal:c.api},name:e.name},serverState:e,shadowDbServer:s,streamsServer:i,queryInsightsBridge:a,walBridge:l}}catch(l){return await xe(e,[n,i,t,s],l)}}function Pe(r,e,t,n){let s=`prisma+postgres://localhost:${t.port}/?${new URLSearchParams({api_key:q({databaseUrl:r.prismaORMConnectionString,name:n.name,shadowDatabaseUrl:e.prismaORMConnectionString})}).toString()}`;return{database:{connectionString:r.connectionString,prismaORMConnectionString:r.prismaORMConnectionString,terminalCommand:r.terminalCommand},http:{url:t.url},ppg:{url:s},shadowDatabase:{connectionString:e.prismaORMConnectionString,prismaORMConnectionString:e.prismaORMConnectionString,terminalCommand:e.terminalCommand}}}async function Y(r,e){let t=[];for(let n of e)try{await n.close()}catch(s){t.push(s)}try{await r.close()}catch(n){t.push(n)}if(t.length>0)throw new AggregateError(t,"Failed to close some servers")}async function xe(r,e,t){try{await Y(r,e.filter(n=>n!==null))}catch(n){throw new AggregateError([t,n],"Failed to start Prisma Dev server cleanly")}throw t}function Re(r){let e=new Set;return{api:{stream:()=>{let t=()=>{},n=Be(r,()=>{e.delete(t)});return t=()=>n.close(),e.add(t),n.stream},subscribe:t=>r.subscribe(t)},close:()=>{for(let t of[...e])t();e.clear()}}}function Ie(){return{async close(){},async poll(){},subscribe(){return()=>{}}}}function Te(){return{async close(){},subscribe(){return()=>{}}}}function ke(){return{async close(){},experimental:{serverUrl:"",sqlitePath:"",streamName:"",url:""},experimentalQueryInsights:{serverUrl:"",sqlitePath:"",streamName:"",url:""}}}function Be(r,e){let t=[],n=!1,s=null,i=r.subscribe(a=>{if(!n){if(s){let o=s;s=null,o.resolve({done:!1,value:a});return}t.push(a)}}),l=()=>{if(!n&&(n=!0,i(),t.length=0,e(),s)){let a=s;s=null,a.resolve({done:!0,value:void 0})}};return{close:l,stream:{[Symbol.asyncIterator](){return this},next(){return t.length>0?Promise.resolve({done:!1,value:t.shift()}):n?Promise.resolve({done:!0,value:void 0}):new Promise((a,o)=>{s={reject:o,resolve:a}})},return(){return l(),Promise.resolve({done:!0,value:void 0})},throw(a){let o=s;return l(),o&&o.reject(a),Promise.reject(a instanceof Error?a:new Error(String(a)))}}}}var De="default";function Qe(r){return{databaseUrl:r.database.prismaORMConnectionString??r.database.connectionString,prismaPostgresUrl:r.ppg.url,shadowDatabaseUrl:r.shadowDatabase.prismaORMConnectionString??r.shadowDatabase.connectionString}}async function ir(r){let{debug:e,name:t=De}=r??{},n=await $(t,{debug:e});return!U(n)||!n.exports?null:{...Qe(n.exports),name:n.name}}export{tr as a,Qe as b,ir as c};
@@ -0,0 +1 @@
1
+ import{a as r}from"./chunk-J6FIMHSD.js";async function s(e){let{server:t}=await r(e);return t}async function A(e){return await s(e)}export{s as a,A as b};