@skmdev/prisma-fixtures 0.1.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/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 skmdev
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
package/NOTICE ADDED
@@ -0,0 +1,29 @@
1
+ This package implements the fixture language described by
2
+ https://github.com/getbigger-io/prisma-fixtures
3
+ Reference revision: bed45fdf82af1002cd51c527d71f5b0b5757884d
4
+
5
+ The implementation has been rewritten for caller-owned Prisma clients, modern
6
+ Faker, bounded validation, asynchronous modules and an explicit public API.
7
+ Upstream copyright and permission notice are retained below.
8
+
9
+ MIT License
10
+
11
+ Copyright (c) 2019 Igor Ognichenko
12
+
13
+ Permission is hereby granted, free of charge, to any person obtaining a copy
14
+ of this software and associated documentation files (the "Software"), to deal
15
+ in the Software without restriction, including without limitation the rights
16
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
17
+ copies of the Software, and to permit persons to whom the Software is
18
+ furnished to do so, subject to the following conditions:
19
+
20
+ The above copyright notice and this permission notice shall be included in all
21
+ copies or substantial portions of the Software.
22
+
23
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
24
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
25
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
26
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
27
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
28
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
29
+ SOFTWARE.
package/README.md ADDED
@@ -0,0 +1,631 @@
1
+ # @skmdev/prisma-fixtures
2
+
3
+ Load YAML or JSON fixtures into Prisma using named references, generated data and
4
+ processors. The CLI can initialize a generated Prisma 7 client from your Prisma
5
+ config; the API also accepts a client you create.
6
+
7
+ Requires Node.js 22.18+; tested with Prisma 7.10 and PostgreSQL. Prisma itself is
8
+ provided by your application, not installed as a runtime dependency of this package.
9
+
10
+ - [Quick start](#quick-start)
11
+ - [Fixture format](#fixture-format)
12
+ - [Processors](#processors)
13
+ - [API and persistence](#api-and-persistence)
14
+ - [CLI](#cli)
15
+ - [Fixture linting and editor support](#fixture-linting-and-editor-support)
16
+ - [Trust and limits](#trust-and-limits)
17
+ - [Compatibility and attribution](#compatibility-and-attribution)
18
+ - [Contributing and releases](CONTRIBUTING.md)
19
+
20
+ ## Quick start
21
+
22
+ Start with an existing Prisma 7 application that has:
23
+
24
+ - `prisma`, `@prisma/client` and `@prisma/adapter-pg` installed.
25
+ - A `prisma.config.ts`, a `prisma-client` generator and a PostgreSQL database
26
+ configured through `DATABASE_URL`. See [client discovery](#client-discovery-and-overrides)
27
+ for TypeScript and CommonJS runtime setup.
28
+ - `User` and `Post` models compatible with the following fields, with their
29
+ tables already created by your application's migrations. Fixtures insert data;
30
+ they do not create tables.
31
+
32
+ <details>
33
+ <summary>Minimal User and Post models</summary>
34
+
35
+ ```prisma
36
+ model User {
37
+ id Int @id @default(autoincrement())
38
+ email String @unique
39
+ name String
40
+ posts Post[]
41
+ }
42
+
43
+ model Post {
44
+ id Int @id @default(autoincrement())
45
+ title String
46
+ authorId Int
47
+ author User @relation(fields: [authorId], references: [id])
48
+ }
49
+ ```
50
+
51
+ </details>
52
+
53
+ ### 1. Install and initialize
54
+
55
+ Run beside your `prisma.config.ts`:
56
+
57
+ ```sh
58
+ npm install --save-dev @skmdev/prisma-fixtures
59
+ npx prisma-fixtures init
60
+ ```
61
+
62
+ This creates `.prisma-fixtures` and a fixtures directory. It uses `prisma/fixtures`
63
+ if that directory already exists; otherwise it creates `fixtures`. The examples
64
+ below assume the latter, with this generated config:
65
+
66
+ ```json
67
+ {
68
+ "fixtures": ["./fixtures"]
69
+ }
70
+ ```
71
+
72
+ ### 2. Add fixtures
73
+
74
+ Use the directory recorded in `.prisma-fixtures` for both files.
75
+ Create `fixtures/users.yml`:
76
+
77
+ ```yaml
78
+ entity: User
79
+ items:
80
+ user{1..3}:
81
+ email: 'user($current)@example.test'
82
+ name: '{{person.firstName}} {{person.lastName}}'
83
+ ```
84
+
85
+ Create `fixtures/posts.yml`:
86
+
87
+ ```yaml
88
+ entity: Post
89
+ connectedFields: [author]
90
+ items:
91
+ post{1..3}:
92
+ title: 'Post ($current)'
93
+ author: '@user($current)'
94
+ ```
95
+
96
+ ### 3. Configure and run the seed
97
+
98
+ Add `seed` to `migrations` in your existing `prisma.config.ts`, keeping your other
99
+ settings:
100
+
101
+ ```ts
102
+ export default defineConfig({
103
+ // Keep your existing schema, datasource and other settings.
104
+ migrations: { seed: 'prisma-fixtures' },
105
+ })
106
+ ```
107
+
108
+ With `DATABASE_URL` available, generate the client, check the fixtures and seed:
109
+
110
+ ```sh
111
+ npx prisma generate
112
+ npx prisma-fixtures --lint
113
+ npx prisma db seed
114
+ ```
115
+
116
+ The CLI prints `Loaded 6 fixtures.`: three users, each with one related post,
117
+ committed in one transaction. It reads your Prisma config; no client wrapper is
118
+ needed. For loading from application code, see [API and persistence](#api-and-persistence).
119
+
120
+ Ordinary loading only inserts; it does not upsert or delete. Running this example
121
+ again fails on the unique emails. To clear and reload, use
122
+ `npx prisma db seed -- --reset`. **Reset clears data from all ordinary/partitioned
123
+ tables in non-system schemas, including tables absent from your fixtures.**
124
+ Migration history and tables preserved by `preserveTables` or Prisma's
125
+ `tables.external` remain; see [cleanup and reset](#cleanup-and-reset).
126
+
127
+ See the [framework and runtime examples](examples/README.md) for standalone apps
128
+ with Argon2 password processors and API endpoints. NestJS demonstrates a CommonJS client.
129
+
130
+ ## Fixture format
131
+
132
+ Each `.yml`, `.yaml` or `.json` file describes one entity. `User` and `user` both
133
+ resolve to the `user` delegate. Directory files are sorted; reference dependencies
134
+ determine insert order. Fixture names must be unique across all files.
135
+
136
+ | Feature | Example | Meaning |
137
+ | ------------------------ | -------------------------------- | ------------------------------------------------------------- |
138
+ | Inclusive range | `user{1..10}` | Ten independent records |
139
+ | Current index | `user($current)@example.test` | Numeric suffix of the expanded fixture name |
140
+ | Arithmetic | `($current*100)` | One `+`, `-`, `*` or `/` operation with a nonnegative integer |
141
+ | Record reference | `'@user1'` | The saved record |
142
+ | Field reference | `'@user1.id'` | An own field of the saved record |
143
+ | Random reference | `'@user*'` | One existing name with that prefix and a numeric suffix |
144
+ | Bounded random reference | `'@user{1..3}'` | One of user1, user2 or user3; all must exist |
145
+ | Literal `@` | `'@@literal'` | Produces `@literal` |
146
+ | Parameters | `'<{names.admin}>'` | A parameter from this document, converted to text |
147
+ | Faker | `'{{internet.email}}'` | Generated value; a standalone provider preserves its type |
148
+ | EJS | `"<%= ['a', 'b'].join(', ') %>"` | JavaScript template rendered to text |
149
+
150
+ ### References and relations
151
+
152
+ Quote references and template expressions in YAML. Arrays and nested objects are
153
+ supported. Random choices are selected once before dependency ordering. Missing
154
+ references and cycles fail before any insert. An absent scalar field can only be
155
+ detected after its referenced record has been saved; use a transaction for atomicity.
156
+
157
+ `connectedFields` converts a saved record into `{ connect: { id } }`, or an array
158
+ into `{ connect: [{ id }, ...] }`. Referenced models need an `id` field for this
159
+ shorthand. For other unique keys use Prisma's explicit nested input:
160
+
161
+ ```yaml
162
+ entity: Post
163
+ items:
164
+ post1:
165
+ title: Example
166
+ author:
167
+ connect:
168
+ email: '@user1.email'
169
+ ```
170
+
171
+ ### Deferred fields
172
+
173
+ For a nullable scalar link that cannot exist until another fixture is created,
174
+ declare `deferredFields`. The loader omits those fields on create, then updates
175
+ the saved rows after all creates. For example, a candidate's optional primary
176
+ resume points to a resume that requires the candidate to exist first:
177
+
178
+ ```yaml
179
+ # fixtures/candidates.yml
180
+ entity: Candidate
181
+ deferredFields: [primaryResumeId]
182
+ items:
183
+ candidate1:
184
+ id: can_local_1
185
+ primaryResumeId: rsu_local_1
186
+ ```
187
+
188
+ ```yaml
189
+ # fixtures/resumes.yml
190
+ entity: Resume
191
+ items:
192
+ resume1:
193
+ id: rsu_local_1
194
+ candidateId: '@candidate1.id'
195
+ ```
196
+
197
+ The loader creates `candidate1` without `primaryResumeId`, creates `resume1`, then
198
+ sets the candidate's primary resume. Use a fixed ID for the deferred link here:
199
+ `'@resume1.id'` would create a reference cycle because references are still
200
+ resolved before inserts. The models must allow `primaryResumeId` to be omitted
201
+ on create and accept the explicit string IDs shown above.
202
+
203
+ Deferred fields require a saved `id`, a Prisma `update` delegate and the default
204
+ writer. Use a transaction so a failed update rolls back the earlier creates.
205
+ Include an `@updatedAt` field in `deferredFields` when that update must retain a
206
+ specific fixture timestamp.
207
+
208
+ ### Parameters and templates
209
+
210
+ Parameters, locale and templates can be combined:
211
+
212
+ ```yaml
213
+ entity: User
214
+ locale: en
215
+ parameters:
216
+ names:
217
+ admin: Administrator
218
+ items:
219
+ admin1:
220
+ email: admin@example.test
221
+ name: '<{names.admin}> <%= name %>'
222
+ ```
223
+
224
+ EJS receives the normalized fixture (`name`, `entity`, `data`, `parameters`, etc.).
225
+ Expansion order is current-index substitution, EJS, Faker, parameters, references,
226
+ processor, connections, then persistence. Parameters are local to their document.
227
+ `<{process.env.NAME}>` falls back to the environment when no explicit parameter
228
+ with that path exists; missing variables raise an error.
229
+
230
+ Use Faker 10 providers, such as `{{number.int({"min": 1, "max": 10})}}` for a
231
+ number or `{{date.past}}` for a Date. Composed strings preserve surrounding text.
232
+ Locale falls back to English; see [compatibility](#compatibility-and-attribution)
233
+ for legacy provider aliases.
234
+
235
+ ### Reproducible data
236
+
237
+ Pass `seed` and `refDate` through the [CLI](#cli) or
238
+ [API](#load-definitions-in-your-own-transaction) to reproduce package-generated
239
+ Faker values, relative dates and random-reference choices.
240
+
241
+ `seed` is an integer from 0 through 4294967295. `refDate` must be a real canonical
242
+ UTC timestamp in `YYYY-MM-DDTHH:mm:ss.sssZ` form. Each load/reset owns its random
243
+ state; global Faker seeding does not affect it. Equal ordered inputs and options
244
+ reproduce package-generated values with the same package/Faker version. Fixture
245
+ order, hooks, environment values and database-generated IDs remain outside that
246
+ guarantee. An explicit Faker provider `refDate` argument overrides the operation
247
+ default.
248
+
249
+ ## Processors
250
+
251
+ Set `processor: ./user-processor.mjs` in the fixture document. Paths are relative
252
+ to that document; extensionless paths such as `./user-processor` are also resolved.
253
+ A processor exports a default class with an optional sync or
254
+ async `preProcess(name, object)` hook:
255
+
256
+ ```js
257
+ export default class UserProcessor {
258
+ async preProcess(name, object) {
259
+ return { ...object, email: object.email.toLowerCase() }
260
+ }
261
+ }
262
+ ```
263
+
264
+ CommonJS `module.exports = class ...` and `exports.default = class ...` work too.
265
+ Preloaded CommonJS hooks such as `ts-node/register` and `tsconfig-paths/register`
266
+ are honored, including their TypeScript and path-alias handling. Genuine ESM and
267
+ top-level-await modules fall back to native import. TypeScript processors can also
268
+ use Node's native type stripping for supported syntax (for example an ESM `.mts`
269
+ module). Native stripping does not convert ESM imports to CommonJS; match your
270
+ module extension/package configuration or compile your processor first. The hook
271
+ runs once per record after resolving references and before converting connections.
272
+ Return a plain object. No postProcess hook is defined by the upstream processor
273
+ contract.
274
+
275
+ ## API and persistence
276
+
277
+ | Entry point | Transaction ownership | Disconnects the client |
278
+ | ------------------------------------------------ | ----------------------------------------------- | ---------------------- |
279
+ | CLI | Automatic, or your configured guard | Yes |
280
+ | `PrismaFixtures.load(client)` | Automatic | No; caller owns it |
281
+ | `loadFixtures`, `cleanFixtures`, `resetFixtures` | Caller; pass a transaction client for atomicity | No; caller owns it |
282
+
283
+ CommonJS callers can use `require('@skmdev/prisma-fixtures')`.
284
+
285
+ ### Load from config
286
+
287
+ To load the paths in `.prisma-fixtures` without the CLI, use an existing Prisma
288
+ client (with its adapter already configured):
289
+
290
+ ```ts
291
+ import { PrismaFixtures } from '@skmdev/prisma-fixtures'
292
+
293
+ const fixtures = new PrismaFixtures()
294
+ const records = await fixtures.load(prisma)
295
+ ```
296
+
297
+ See the [runnable programmatic example](examples/hono/prisma/seed.mjs) and its
298
+ [setup instructions](examples/hono/README.md).
299
+
300
+ `load()` reads `.prisma-fixtures` from the current directory, applies its `seed`,
301
+ `refDate` and `timeout` settings, and loads in one transaction. The caller keeps
302
+ ownership of the client and disconnects it when finished. A configured `client`
303
+ module is ignored because the client is supplied; guarded client configs require
304
+ the CLI.
305
+
306
+ ### Load definitions in your own transaction
307
+
308
+ With your existing `prisma` client, read a file or directory and pass the
309
+ definitions to `loadFixtures`. Returned records are keyed by fixture name:
310
+
311
+ ```ts
312
+ import { loadFixtures, readFixtureDefinitions } from '@skmdev/prisma-fixtures'
313
+
314
+ const definitions = readFixtureDefinitions('./fixtures')
315
+ const records = await prisma.$transaction(
316
+ (tx) =>
317
+ loadFixtures(tx, definitions, {
318
+ seed: 42,
319
+ refDate: '2026-01-01T00:00:00.000Z',
320
+ }),
321
+ { timeout: 60_000 },
322
+ )
323
+
324
+ console.log(records.user1.id)
325
+ ```
326
+
327
+ Disconnect `prisma` when your application is finished with it. For a standalone
328
+ seed script, use `try`/`finally` as in the runnable example above.
329
+
330
+ ### Function reference
331
+
332
+ - `readFixtureDefinitions(path): FixtureDefinition[]` parses without executing
333
+ templates, importing processors or accessing a database.
334
+ - `loadFixtures(client, definitions, options?, writer?): Promise<Record<string, Record<string, unknown>>>`
335
+ validates and loads definitions. `options` accepts `seed` and `refDate`; passing
336
+ the writer directly as the third argument remains supported. It accepts either
337
+ a Prisma client or transaction client and does not disconnect it. Types
338
+ `FixtureDefinition`, `FixtureLoadOptions`, `FixtureWriter` and `FixtureProcessor`
339
+ are exported.
340
+ - The optional `writer(fixture, data)` returns the saved record and replaces the
341
+ default `create({ data })`. Use it to implement application-specific upserts.
342
+ When using a transaction, the writer must use that same transaction client.
343
+ - `cleanFixtures(client, options?)` clears PostgreSQL data, except exact
344
+ `options.preserveTables` entries.
345
+ - `resetFixtures(client, definitions, options?, writer?)` clears the database,
346
+ then loads the fixtures and returns the same record map as `loadFixtures`.
347
+ `FixtureResetOptions` combines `seed`, `refDate` and `preserveTables`. Passing
348
+ the writer as the third argument remains supported.
349
+ - Package-owned failures are `FixtureError` instances with a stable `code`, safe
350
+ `stage`/source/fixture/path context and the original private `cause` for API
351
+ callers. Opaque provider, hook and database messages are not copied into the
352
+ safe message.
353
+
354
+ ### Cleanup and reset
355
+
356
+ Default loading inserts records; it never deletes or automatically upserts.
357
+ Repeating a load can create duplicates or raise unique-constraint errors.
358
+ Use a transaction to prevent a failed reset from leaving data deleted:
359
+
360
+ ```ts
361
+ import { resetFixtures } from '@skmdev/prisma-fixtures'
362
+
363
+ const records = await prisma.$transaction(
364
+ (tx) =>
365
+ resetFixtures(tx, definitions, {
366
+ preserveTables: ['public.audit_log'],
367
+ }),
368
+ { timeout: 60_000 },
369
+ )
370
+ ```
371
+
372
+ Cleanup currently supports **PostgreSQL only** and requires `$executeRawUnsafe`.
373
+ It truncates all ordinary/partitioned tables across all non-system schemas in the
374
+ connected database, including implicit join tables. Tables, schema, indexes,
375
+ constraints, sequence values and `_prisma_migrations` records are preserved.
376
+ Each `preserveTables` entry names one existing table as `schema.table`;
377
+ its data and sequence state are also preserved. Unknown tables and
378
+ duplicate or malformed entries fail before truncation.
379
+ Foreign tables and materialized views are outside this cleanup scope.
380
+ Cleanup refuses inherited/partitioned structures with excluded descendants, so
381
+ truncating a parent cannot reach a preserved table, remote table or migration
382
+ history, and truncating a child cannot change rows visible through a preserved
383
+ ancestor. `RESTRICT` also makes a foreign key from a preserved table to a cleaned
384
+ table fail the cleanup instead of cascading or bypassing triggers.
385
+ The database user needs `TRUNCATE` privileges on every included table.
386
+
387
+ Cleanup needs no fixture definitions and executes no templates or processors.
388
+ Reset prepares templates and random references once and validates the load before
389
+ clearing data; an empty definition list still clears the database. Generated
390
+ values may change on each reset unless fixed generation options are supplied.
391
+ PostgreSQL's
392
+ [`TRUNCATE`](https://www.postgresql.org/docs/current/sql-truncate.html) runs
393
+ transactionally, fires `ON TRUNCATE` triggers and does not fire `ON DELETE` triggers.
394
+
395
+ Without a caller-owned transaction, an error can leave earlier deletes or inserts
396
+ applied. Rollback cannot undo external side effects in templates or processors.
397
+
398
+ ## CLI
399
+
400
+ Run these commands from your project directory:
401
+
402
+ | Command | Purpose |
403
+ | ------------------------------------------------------------------ | ------------------------------------------------------------------------------ |
404
+ | `npx prisma-fixtures init` | Create config and fixture directory without overwriting an existing config |
405
+ | `npx prisma-fixtures` | Load configured fixtures; equivalent to the Quick start's `npx prisma db seed` |
406
+ | `npx prisma-fixtures ./fixtures --list` | List fixture names and entities without executing code |
407
+ | `npx prisma-fixtures --lint` | Check fixtures without a client or database |
408
+ | `npx prisma-fixtures --clean` | Clear database data without reading fixtures (PostgreSQL only) |
409
+ | `npx prisma-fixtures --reset` | Clear database data, then load fixtures (PostgreSQL only) |
410
+ | `npx prisma-fixtures --seed 42 --refDate 2026-01-01T00:00:00.000Z` | Reproduce generated values and random references |
411
+ | `npx prisma-fixtures --config ./test/.prisma-fixtures` | Use a different config file |
412
+ | `npx prisma-fixtures --help` | Show all options |
413
+
414
+ Use only one of `--clean`, `--reset`, `--list` and `--lint` at a time. Clean and
415
+ reset affect tables beyond those in your fixtures; see [cleanup and reset](#cleanup-and-reset).
416
+ These destructive modes must be requested on the command line, not saved in config.
417
+
418
+ To reuse an existing directory during setup, run
419
+ `npx prisma-fixtures init ../shared/fixtures`. The directory must already exist.
420
+
421
+ ### Configuration
422
+
423
+ `.prisma-fixtures` is JSON. Start with the config from Quick start and add only
424
+ the options you need:
425
+
426
+ ```json
427
+ {
428
+ "fixtures": ["./fixtures"],
429
+ "timeout": 60000,
430
+ "seed": 42,
431
+ "refDate": "2026-01-01T00:00:00.000Z",
432
+ "preserveTables": ["public.audit_log"]
433
+ }
434
+ ```
435
+
436
+ | Config key | CLI override | Default / purpose |
437
+ | ---------------- | ----------------------- | ---------------------------------------------------------------------------------------- |
438
+ | `fixtures` | Positional paths | No default; nonempty array of paths, required unless using positional paths or `--clean` |
439
+ | `client` | `--client <module>` | Inferred from Prisma config; see client discovery below |
440
+ | `timeout` | `--timeout <ms>` | `60000`; positive integer transaction timeout in milliseconds |
441
+ | `schema` | `--schema <file>` | None; JSON Schema for `--lint` |
442
+ | `seed` | `--seed <integer>` | Random; integer from 0 through 4294967295 |
443
+ | `refDate` | `--refDate <timestamp>` | Current time; canonical UTC timestamp such as `2026-01-01T00:00:00.000Z` |
444
+ | `preserveTables` | Config only | `[]`; exact existing `schema.table` names excluded from clean/reset |
445
+
446
+ Unknown fields are rejected. Config paths are relative to the config file;
447
+ positional paths and CLI flags are relative to the working directory and override
448
+ matching config fields. Multiple fixture paths form one load and one transaction.
449
+ Discovery checks only the current directory; an explicit `--config` file must exist.
450
+
451
+ When `client` is omitted, `--clean` and `--reset` also preserve the PostgreSQL
452
+ tables listed in Prisma's `tables.external` (with
453
+ `experimental.externalTables` enabled). These `schema.table` entries are combined
454
+ with any explicit `preserveTables` entries. For example,
455
+ `tables: { external: ['public.flyway_schema_history'] }` in `prisma.config.ts`
456
+ preserves that table without repeating it in `.prisma-fixtures`. Direct API calls
457
+ still use only the options passed to them. Ordinary loading ignores the preserve
458
+ list and never deletes data.
459
+
460
+ ### Client discovery and overrides
461
+
462
+ For write commands with no explicit `client`, the CLI reads the adjacent Prisma
463
+ config's schema and datasource, resolves the static `prisma-client` output path,
464
+ and uses your installed `@prisma/adapter-pg`. Run `prisma generate` first.
465
+ For inferred `.ts`/`.cts` clients, it uses existing compiled output described by
466
+ `tsconfig.json` with `rootDir` and `outDir`, or the project's installed `ts-node`
467
+ when no loader is registered. Native ESM `.mts` clients work with Node's type
468
+ stripping; see the [Hono generator](examples/hono/prisma/schema/00_base.prisma).
469
+
470
+ An explicit `client` can be an object with `module` (generated client path),
471
+ `adapter: 'pg'` and optional `guard`, or a legacy module path exporting a client
472
+ or factory. `--client` overrides either form with a legacy module path.
473
+
474
+ | CLI-only option | Purpose |
475
+ | ---------------------------------- | ------------------------------------------------------------------------------------ |
476
+ | `--databaseUrl <url>` | Override the datasource URL; a legacy client module must export a factory |
477
+ | `--require <module>` | Preload a hook or path alias module; repeatable, resolved from the working directory |
478
+ | `--debug`, `-d` | Include error type/code without fixture values or URLs |
479
+ | `--no-color` | Accepted for compatibility; output is always plain |
480
+ | `--version`, `-v` / `--help`, `-h` | Print version/help without loading config |
481
+
482
+ The inferred client uses Prisma's datasource URL, then `DATABASE_URL` from the
483
+ environment if the config omits it. `--databaseUrl` overrides both. Keep
484
+ credentials out of the JSON config and shell history. The CLI does not
485
+ independently load `.env` files; a `prisma.config.ts` import of `dotenv/config`
486
+ works when Prisma loads that config. Otherwise use `--require dotenv/config`.
487
+ For application-specific safety checks, `client.guard` may name a module exporting
488
+ `fixtureDatabaseUrl(env)` and `fixtureTransaction(client, action, timeout)`.
489
+ Those functions validate the connection before client creation and wrap writes
490
+ in the application's transaction guard.
491
+
492
+ ### Transactions and diagnostics
493
+
494
+ The CLI wraps writes in one transaction (60-second timeout; override with
495
+ `--timeout <ms>`) and always disconnects a successfully acquired valid client.
496
+ `--list` and `--lint` skip all hooks, Prisma config loading and client imports;
497
+ `--list` only parses the JSON config and fixture documents.
498
+ Package diagnostics include a stable code and available filename, fixture name
499
+ and JSON Pointer field path. Output is escaped, bounded and omits fixture values,
500
+ URLs and opaque provider/client messages, including with `--debug`. API callers
501
+ can inspect a `FixtureError` cause in their own diagnostic harness.
502
+
503
+ ## Fixture linting and editor support
504
+
505
+ ```sh
506
+ npx prisma-fixtures --lint
507
+ npx prisma-fixtures ./fixtures --lint
508
+ npx prisma-fixtures --config ./test/.prisma-fixtures --lint
509
+ ```
510
+
511
+ Lint works without a client or database. It checks YAML/JSON syntax, duplicate
512
+ keys, allowed metadata, fixture shapes/names, ranges, current-index expressions,
513
+ duplicate fixture names across files and input limits. It also validates literal
514
+ fixed, wildcard and bounded references after combining all selected paths, and
515
+ rejects definite self/cross-file dependency cycles. A successful check exits
516
+ with code 0; the first error exits with code 1. YAML syntax diagnostics include
517
+ the filename, line/column and parser error code without printing fixture values.
518
+ `--lint` and `--list` are separate modes; use one at a time.
519
+
520
+ Lint does not execute templates, Faker, environment substitutions, processors,
521
+ preloads or random selection. Dynamic strings and references with several valid
522
+ candidates are counted as unresolved without failing lint or being added as
523
+ definite graph edges; a sole random candidate is a definite edge. Saved-record
524
+ field presence, rendered types, arbitrary hook behavior and database constraints
525
+ remain runtime checks. Formatting is separate: use your existing YAML formatter,
526
+ such as Prettier.
527
+
528
+ ### Generate a schema from your Prisma models
529
+
530
+ Add a generator to `prisma/schema.prisma`:
531
+
532
+ ```prisma
533
+ generator fixtures {
534
+ provider = "prisma-fixtures-generator"
535
+ output = "../generated/fixtures"
536
+ }
537
+ ```
538
+
539
+ Run `npx prisma generate`. This creates `generated/fixtures/schema.json` from
540
+ Prisma's model and create-input metadata. Output paths are relative to
541
+ `schema.prisma`; regenerate after changing your models. No database connection
542
+ is needed for this generator.
543
+
544
+ Use the same schema for CLI lint by adding `schema` to `.prisma-fixtures`:
545
+
546
+ ```json
547
+ {
548
+ "fixtures": ["./fixtures"],
549
+ "schema": "./generated/fixtures/schema.json"
550
+ }
551
+ ```
552
+
553
+ Or pass it explicitly:
554
+
555
+ ```sh
556
+ npx prisma-fixtures ./fixtures --lint --schema ./generated/fixtures/schema.json
557
+ ```
558
+
559
+ The generated schema selects fields by `entity`, accepts model and delegate
560
+ names, and checks known fields, scalar types, enums, nullability, lists and
561
+ required create inputs. Both nested relation inputs and direct foreign keys are
562
+ supported. References, Faker, EJS and parameter expressions remain dynamic
563
+ strings; their evaluated types are checked when loading. References and records
564
+ with an `id` (including arrays) are allowed for `connectedFields` shorthand.
565
+
566
+ Documents with a `processor` skip model item validation because the processor
567
+ may add, remove or transform fields; their fixture structure is still checked.
568
+ The schema does not reproduce every Prisma runtime rule: native database type
569
+ limits, DateTime/Decimal/Bytes conversions, relation-shorthand correctness,
570
+ uniqueness and reference targets still need runtime validation.
571
+
572
+ ### Editor setup
573
+
574
+ The schema comment is optional. Configure a file association once, or use a
575
+ comment in each fixture; you do not need both. For
576
+ [VS Code's Red Hat YAML extension](https://github.com/redhat-developer/vscode-yaml),
577
+ add this to `.vscode/settings.json` in your application:
578
+
579
+ ```json
580
+ {
581
+ "yaml.schemas": {
582
+ "./generated/fixtures/schema.json": [
583
+ "fixtures/**/*.yml",
584
+ "fixtures/**/*.yaml"
585
+ ]
586
+ }
587
+ }
588
+ ```
589
+
590
+ Or add a schema comment to each YAML file, with a path relative to that file:
591
+
592
+ ```yaml
593
+ # yaml-language-server: $schema=../generated/fixtures/schema.json
594
+ entity: User
595
+ items:
596
+ user1:
597
+ email: user1@example.test
598
+ ```
599
+
600
+ If you only need generic fixture metadata checks, the package also includes
601
+ `schema/fixture.schema.json`, exported as `@skmdev/prisma-fixtures/schema.json`.
602
+ Point your editor at `node_modules/@skmdev/prisma-fixtures/schema/fixture.schema.json`
603
+ to use it without generation.
604
+
605
+ The editor schema cannot check cross-file names, the literal dependency graph or
606
+ runtime behavior; run `--lint` in CI as well. This repository's `npm run verify`
607
+ includes `npm run lint:fixtures` for the example fixtures.
608
+
609
+ ## Trust and limits
610
+
611
+ Load trusted fixtures only. EJS, processors, preloads and client modules execute
612
+ JavaScript with your process permissions; parsing limits do not sandbox code.
613
+ The parser rejects prototype keys, duplicate names/keys and YAML aliases. Limits:
614
+ 100 fixture files, 1 MiB per file, 2,000 expanded records, depth 32 and 50,000
615
+ visited nodes per validated value. No fixture values are logged by the engine.
616
+
617
+ ## Compatibility and attribution
618
+
619
+ Legacy Faker aliases `name.firstName`, `name.lastName`, `name.title`,
620
+ `internet.userName` and `random.number` remain supported. This is syntax
621
+ compatibility, not identical random output or complete emulation of every removed
622
+ Faker API. New fixtures should use Faker 10 provider names.
623
+
624
+ The fixture language follows
625
+ [getbigger-io/prisma-fixtures](https://github.com/getbigger-io/prisma-fixtures),
626
+ with modern client injection and validation. This new package does not expose the
627
+ old `Loader`, `Builder`, `Parser`, `Resolver` or `fixturesIterator` class API, or
628
+ instantiate an implicit Prisma client. Replace that setup with the functions
629
+ above. MIT; see [LICENSE](LICENSE) and retained upstream attribution in [NOTICE](NOTICE).
630
+
631
+ Development checks and release instructions are in [CONTRIBUTING.md](CONTRIBUTING.md).