@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/dist/index.js ADDED
@@ -0,0 +1,257 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.PrismaFixtures = exports.readFixtureDefinitions = exports.FixtureError = void 0;
4
+ exports.loadFixtures = loadFixtures;
5
+ exports.cleanFixtures = cleanFixtures;
6
+ exports.resetFixtures = resetFixtures;
7
+ const fixture_document_1 = require("./fixture-document");
8
+ const fixture_reference_1 = require("./fixture-reference");
9
+ const fixture_template_1 = require("./fixture-template");
10
+ const cleanup_options_1 = require("./cleanup-options");
11
+ const load_options_1 = require("./load-options");
12
+ const fixture_error_1 = require("./fixture-error");
13
+ const fixture_config_1 = require("./fixture-config");
14
+ var fixture_error_2 = require("./fixture-error");
15
+ Object.defineProperty(exports, "FixtureError", { enumerable: true, get: function () { return fixture_error_2.FixtureError; } });
16
+ exports.readFixtureDefinitions = fixture_document_1.readFixtureDocuments;
17
+ class PrismaFixtures {
18
+ async load(client) {
19
+ const config = (0, fixture_config_1.readFixtureConfig)();
20
+ if (!config.fixtures) {
21
+ throw new Error('Fixture config must define fixtures');
22
+ }
23
+ if (typeof config.client === 'object' && config.client.guard) {
24
+ throw new Error('Guarded fixture config requires the CLI');
25
+ }
26
+ const definitions = (0, fixture_document_1.readFixturePaths)(config.fixtures);
27
+ const transaction = client?.$transaction;
28
+ if (typeof transaction !== 'function') {
29
+ throw new Error('The client must implement $transaction');
30
+ }
31
+ return transaction.call(client, (tx) => loadFixtures(tx, definitions, {
32
+ seed: config.seed,
33
+ refDate: config.refDate,
34
+ }), { timeout: config.timeout ?? 60_000 });
35
+ }
36
+ }
37
+ exports.PrismaFixtures = PrismaFixtures;
38
+ async function loadFixtures(client, definitions, optionsOrWrite, finalWrite) {
39
+ if (typeof optionsOrWrite === 'function' && finalWrite !== undefined) {
40
+ throw new Error('Invalid fixture loader arguments');
41
+ }
42
+ const options = (0, load_options_1.normalizeLoadOptions)(typeof optionsOrWrite === 'function' ? undefined : optionsOrWrite);
43
+ const write = typeof optionsOrWrite === 'function' ? optionsOrWrite : finalWrite;
44
+ const fixtures = await prepareFixtures(client, definitions, write, options);
45
+ return writeFixtures(fixtures, write);
46
+ }
47
+ async function cleanFixtures(client, options) {
48
+ const { preserveTables = [] } = (0, cleanup_options_1.normalizeCleanupOptions)(options);
49
+ if (client === null ||
50
+ (typeof client !== 'object' && typeof client !== 'function')) {
51
+ throw new Error('Invalid fixture cleaner arguments');
52
+ }
53
+ const execute = client.$executeRawUnsafe;
54
+ if (typeof execute !== 'function') {
55
+ throw new Error('Fixture cleaner requires $executeRawUnsafe');
56
+ }
57
+ const preservedHex = Buffer.from(JSON.stringify(preserveTables.map((name) => {
58
+ const [schema, table] = name.split('.');
59
+ return { schema, table };
60
+ }))).toString('hex');
61
+ // PostgreSQL only: config is hex data; catalog identifiers are quoted by PostgreSQL.
62
+ await execute.call(client, `DO $$
63
+ DECLARE
64
+ tables text;
65
+ preserved jsonb := convert_from(decode('${preservedHex}', 'hex'), 'UTF8')::jsonb;
66
+ BEGIN
67
+ IF EXISTS (
68
+ SELECT 1
69
+ FROM jsonb_to_recordset(preserved) AS requested("schema" text, "table" text)
70
+ WHERE NOT EXISTS (
71
+ SELECT 1
72
+ FROM pg_catalog.pg_class AS candidate
73
+ JOIN pg_catalog.pg_namespace AS namespace ON namespace.oid = candidate.relnamespace
74
+ WHERE namespace.nspname = requested."schema"
75
+ AND candidate.relname = requested."table"
76
+ AND candidate.relkind IN ('r', 'p')
77
+ )
78
+ ) THEN
79
+ RAISE EXCEPTION 'Fixture cleanup preserve table not found';
80
+ END IF;
81
+
82
+ -- TRUNCATE follows inheritance; reject every cleanup/exclusion boundary.
83
+ IF EXISTS (
84
+ WITH cleanup_tables AS (
85
+ SELECT candidate.oid
86
+ FROM pg_catalog.pg_class AS candidate
87
+ JOIN pg_catalog.pg_namespace AS namespace ON namespace.oid = candidate.relnamespace
88
+ WHERE candidate.relkind IN ('r', 'p')
89
+ AND namespace.nspname <> 'information_schema'
90
+ AND namespace.nspname !~ '^pg_'
91
+ AND candidate.relname <> '_prisma_migrations'
92
+ AND NOT EXISTS (
93
+ SELECT 1
94
+ FROM jsonb_to_recordset(preserved) AS requested("schema" text, "table" text)
95
+ WHERE requested."schema" = namespace.nspname
96
+ AND requested."table" = candidate.relname
97
+ )
98
+ )
99
+ SELECT 1
100
+ FROM pg_catalog.pg_inherits AS inheritance
101
+ WHERE
102
+ EXISTS (
103
+ SELECT 1 FROM cleanup_tables WHERE cleanup_tables.oid = inheritance.inhparent
104
+ ) <>
105
+ EXISTS (
106
+ SELECT 1 FROM cleanup_tables WHERE cleanup_tables.oid = inheritance.inhrelid
107
+ )
108
+ ) THEN
109
+ RAISE EXCEPTION 'Fixture cleanup cannot truncate excluded descendant tables';
110
+ END IF;
111
+
112
+ SELECT string_agg(
113
+ format('%I.%I', schemaname, tablename),
114
+ ', ' ORDER BY schemaname, tablename
115
+ )
116
+ INTO tables
117
+ FROM pg_catalog.pg_tables
118
+ WHERE schemaname <> 'information_schema'
119
+ AND schemaname !~ '^pg_'
120
+ AND tablename <> '_prisma_migrations'
121
+ AND NOT EXISTS (
122
+ SELECT 1
123
+ FROM jsonb_to_recordset(preserved) AS requested("schema" text, "table" text)
124
+ WHERE requested."schema" = schemaname
125
+ AND requested."table" = tablename
126
+ );
127
+
128
+ IF tables IS NOT NULL THEN
129
+ EXECUTE 'TRUNCATE TABLE ' || tables || ' CONTINUE IDENTITY RESTRICT';
130
+ END IF;
131
+ END
132
+ $$;`);
133
+ }
134
+ async function resetFixtures(client, definitions, optionsOrWrite, finalWrite) {
135
+ if (typeof optionsOrWrite === 'function' && finalWrite !== undefined) {
136
+ throw new Error('Invalid fixture loader arguments');
137
+ }
138
+ const options = (0, load_options_1.normalizeResetOptions)(typeof optionsOrWrite === 'function' ? undefined : optionsOrWrite);
139
+ const write = typeof optionsOrWrite === 'function' ? optionsOrWrite : finalWrite;
140
+ const fixtures = await prepareFixtures(client, definitions, write, options.load);
141
+ await cleanFixtures(client, options.cleanup);
142
+ return writeFixtures(fixtures, write);
143
+ }
144
+ async function writeFixtures(fixtures, write) {
145
+ const records = Object.create(null);
146
+ const deferredUpdates = [];
147
+ for (const { fixture, delegate, processor } of fixtures) {
148
+ const resolved = (0, fixture_reference_1.resolveFixtureReferences)(fixture.data, records, fixture);
149
+ if (!(0, fixture_document_1.isFixtureRecord)(resolved))
150
+ throw new Error('Invalid fixture data');
151
+ const processed = await (0, fixture_template_1.runFixtureProcessor)(processor, fixture, resolved);
152
+ const data = (0, fixture_reference_1.applyFixtureConnections)(processed, fixture.connectedFields, fixture);
153
+ const deferred = Object.fromEntries((fixture.deferredFields ?? []).map((field) => [field, data[field]]));
154
+ const createData = { ...data };
155
+ for (const field of fixture.deferredFields ?? [])
156
+ delete createData[field];
157
+ let saved;
158
+ try {
159
+ saved = write
160
+ ? await write(fixture, data)
161
+ : await delegate.create({ data: createData });
162
+ }
163
+ catch (error) {
164
+ throw (0, fixture_error_1.wrapFixtureError)(error, 'FIXTURE_WRITE_FAILED', 'Fixture persistence failed', (0, fixture_document_1.fixtureErrorContext)(fixture, 'writing fixture'));
165
+ }
166
+ if (!(0, fixture_document_1.isFixtureRecord)(saved)) {
167
+ throw (0, fixture_error_1.createFixtureError)('FIXTURE_WRITE_FAILED', 'Fixture writer returned an invalid record', (0, fixture_document_1.fixtureErrorContext)(fixture, 'writing fixture'));
168
+ }
169
+ records[fixture.name] = saved;
170
+ if (fixture.deferredFields?.length) {
171
+ if (saved.id === undefined) {
172
+ throw (0, fixture_error_1.createFixtureError)('FIXTURE_WRITE_FAILED', 'Deferred fixture update requires a saved id', (0, fixture_document_1.fixtureErrorContext)(fixture, 'writing fixture'));
173
+ }
174
+ deferredUpdates.push({
175
+ fixture,
176
+ delegate: delegate,
177
+ id: saved.id,
178
+ data: deferred,
179
+ });
180
+ }
181
+ }
182
+ for (const { fixture, delegate, id, data } of deferredUpdates) {
183
+ try {
184
+ const saved = await delegate.update({ where: { id }, data });
185
+ if (!(0, fixture_document_1.isFixtureRecord)(saved))
186
+ throw new Error('Invalid updated record');
187
+ records[fixture.name] = saved;
188
+ }
189
+ catch (error) {
190
+ throw (0, fixture_error_1.wrapFixtureError)(error, 'FIXTURE_WRITE_FAILED', 'Fixture persistence failed', (0, fixture_document_1.fixtureErrorContext)(fixture, 'updating fixture'));
191
+ }
192
+ }
193
+ return records;
194
+ }
195
+ async function prepareFixtures(client, definitions, write, options) {
196
+ if (client === null ||
197
+ (typeof client !== 'object' && typeof client !== 'function') ||
198
+ (write !== undefined && typeof write !== 'function')) {
199
+ throw new Error('Invalid fixture loader arguments');
200
+ }
201
+ if (!Array.isArray(definitions) ||
202
+ definitions.length > fixture_document_1.MAX_FIXTURE_DEFINITIONS) {
203
+ throw new Error('Invalid fixture definitions');
204
+ }
205
+ const names = new Set();
206
+ for (const definition of definitions) {
207
+ (0, fixture_document_1.assertFixtureDefinition)(definition);
208
+ if (names.has(definition.name))
209
+ throw new Error('Duplicate fixture name');
210
+ names.add(definition.name);
211
+ }
212
+ const runtime = new Map();
213
+ for (const fixture of definitions) {
214
+ const delegate = write ? undefined : resolveDelegate(client, fixture.entity);
215
+ if (fixture.deferredFields?.length && (write || !delegate?.update)) {
216
+ throw new Error('Deferred fields require a client update delegate');
217
+ }
218
+ let processor;
219
+ if (fixture.processor) {
220
+ try {
221
+ processor = await (0, fixture_template_1.loadFixtureProcessor)(fixture.processor);
222
+ }
223
+ catch (error) {
224
+ throw (0, fixture_error_1.wrapFixtureError)(error, 'FIXTURE_PROCESSOR_FAILED', 'Fixture processor could not be loaded', (0, fixture_document_1.fixtureErrorContext)(fixture, 'loading processor'));
225
+ }
226
+ }
227
+ runtime.set(fixture.name, {
228
+ delegate,
229
+ ...(processor ? { processor } : {}),
230
+ });
231
+ }
232
+ const randomizer = await (0, fixture_template_1.createFixtureRandomizer)(options.seed);
233
+ const rendered = [];
234
+ for (const fixture of definitions) {
235
+ const result = {
236
+ ...fixture,
237
+ data: await (0, fixture_template_1.renderFixtureTemplates)(fixture, randomizer, options.refDate),
238
+ };
239
+ (0, fixture_document_1.inheritFixtureSource)(fixture, result);
240
+ rendered.push(result);
241
+ }
242
+ return (0, fixture_reference_1.prepareFixtureReferences)(rendered, randomizer).map((fixture) => ({
243
+ fixture,
244
+ ...runtime.get(fixture.name),
245
+ }));
246
+ }
247
+ function resolveDelegate(client, entity) {
248
+ const clientRecord = client;
249
+ const delegateName = entity[0].toLowerCase() + entity.slice(1);
250
+ const candidate = clientRecord[entity] ?? clientRecord[delegateName];
251
+ if (candidate === null ||
252
+ (typeof candidate !== 'object' && typeof candidate !== 'function') ||
253
+ typeof candidate.create !== 'function') {
254
+ throw new Error(`Fixture model delegate not found: ${entity}`);
255
+ }
256
+ return candidate;
257
+ }
@@ -0,0 +1,11 @@
1
+ import { type FixtureCleanupOptions } from './cleanup-options';
2
+ export type FixtureLoadOptions = {
3
+ seed?: number;
4
+ refDate?: string;
5
+ };
6
+ export type FixtureResetOptions = FixtureCleanupOptions & FixtureLoadOptions;
7
+ export declare function normalizeLoadOptions(value?: unknown): FixtureLoadOptions;
8
+ export declare function normalizeResetOptions(value?: unknown): {
9
+ load: FixtureLoadOptions;
10
+ cleanup: FixtureCleanupOptions;
11
+ };
@@ -0,0 +1,47 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.normalizeLoadOptions = normalizeLoadOptions;
4
+ exports.normalizeResetOptions = normalizeResetOptions;
5
+ const cleanup_options_1 = require("./cleanup-options");
6
+ const fixture_document_1 = require("./fixture-document");
7
+ const fixture_error_1 = require("./fixture-error");
8
+ function normalizeLoadOptions(value = {}) {
9
+ if (!(0, fixture_document_1.isFixtureRecord)(value) ||
10
+ Object.keys(value).some((key) => !['seed', 'refDate'].includes(key))) {
11
+ throw invalidLoadOptions();
12
+ }
13
+ const { seed, refDate } = value;
14
+ if ((seed !== undefined &&
15
+ (typeof seed !== 'number' ||
16
+ !Number.isInteger(seed) ||
17
+ seed < 0 ||
18
+ seed > 0xffff_ffff)) ||
19
+ (refDate !== undefined && !isCanonicalReferenceDate(refDate))) {
20
+ throw invalidLoadOptions();
21
+ }
22
+ return {
23
+ ...(seed === undefined ? {} : { seed }),
24
+ ...(refDate === undefined ? {} : { refDate }),
25
+ };
26
+ }
27
+ function normalizeResetOptions(value = {}) {
28
+ if (!(0, fixture_document_1.isFixtureRecord)(value) ||
29
+ Object.keys(value).some((key) => !['seed', 'refDate', 'preserveTables'].includes(key))) {
30
+ throw invalidLoadOptions();
31
+ }
32
+ return {
33
+ load: normalizeLoadOptions({ seed: value.seed, refDate: value.refDate }),
34
+ cleanup: (0, cleanup_options_1.normalizeCleanupOptions)({ preserveTables: value.preserveTables }),
35
+ };
36
+ }
37
+ function isCanonicalReferenceDate(value) {
38
+ if (typeof value !== 'string' ||
39
+ !/^\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}\.\d{3}Z$/.test(value)) {
40
+ return false;
41
+ }
42
+ const parsed = new Date(value);
43
+ return !Number.isNaN(parsed.valueOf()) && parsed.toISOString() === value;
44
+ }
45
+ function invalidLoadOptions() {
46
+ return (0, fixture_error_1.createFixtureError)('FIXTURE_OPTIONS_INVALID', 'Invalid fixture load options', { stage: 'validating options' });
47
+ }
@@ -0,0 +1,8 @@
1
+ import { type FixturePreservedTable } from './cleanup-options';
2
+ type PrismaDefaults = {
3
+ module: string;
4
+ databaseUrl?: string;
5
+ preserveTables: FixturePreservedTable[];
6
+ };
7
+ export declare function loadPrismaDefaults(configRoot: string, requireFromCwd: NodeRequire, databaseUrlOverride?: string): Promise<PrismaDefaults>;
8
+ export {};
@@ -0,0 +1,111 @@
1
+ "use strict";
2
+ var __importDefault = (this && this.__importDefault) || function (mod) {
3
+ return (mod && mod.__esModule) ? mod : { "default": mod };
4
+ };
5
+ Object.defineProperty(exports, "__esModule", { value: true });
6
+ exports.loadPrismaDefaults = loadPrismaDefaults;
7
+ const node_fs_1 = __importDefault(require("node:fs"));
8
+ const node_module_1 = require("node:module");
9
+ const node_path_1 = __importDefault(require("node:path"));
10
+ const cleanup_options_1 = require("./cleanup-options");
11
+ async function loadPrismaDefaults(configRoot, requireFromCwd, databaseUrlOverride) {
12
+ const requireFromPrisma = (0, node_module_1.createRequire)(requireFromCwd.resolve('prisma/package.json'));
13
+ const { loadConfigFromFile } = requireFromPrisma('@prisma/config');
14
+ const previousUrl = process.env.DATABASE_URL;
15
+ if (databaseUrlOverride !== undefined) {
16
+ process.env.DATABASE_URL = databaseUrlOverride;
17
+ }
18
+ let loaded;
19
+ try {
20
+ loaded = await loadConfigFromFile({ configRoot });
21
+ }
22
+ finally {
23
+ if (databaseUrlOverride !== undefined) {
24
+ if (previousUrl === undefined)
25
+ delete process.env.DATABASE_URL;
26
+ else
27
+ process.env.DATABASE_URL = previousUrl;
28
+ }
29
+ }
30
+ if (!loaded.resolvedPath || !loaded.config || loaded.error) {
31
+ throw new Error('Prisma config could not be loaded');
32
+ }
33
+ const schemaPath = loaded.config.schema ?? defaultSchema(configRoot);
34
+ const source = generatedClient(schemaPath);
35
+ return {
36
+ module: runtimeClient(source, configRoot, requireFromCwd),
37
+ databaseUrl: loaded.config.datasource?.url,
38
+ preserveTables: (0, cleanup_options_1.normalizeCleanupOptions)({
39
+ preserveTables: loaded.config.tables?.external ?? [],
40
+ }).preserveTables ?? [],
41
+ };
42
+ }
43
+ function defaultSchema(root) {
44
+ for (const name of ['prisma/schema.prisma', 'schema.prisma']) {
45
+ const file = node_path_1.default.join(root, name);
46
+ if (node_fs_1.default.existsSync(file))
47
+ return file;
48
+ }
49
+ throw new Error('Prisma schema not found');
50
+ }
51
+ function generatedClient(schemaPath) {
52
+ const files = [];
53
+ function visit(target) {
54
+ if (node_fs_1.default.statSync(target).isDirectory()) {
55
+ for (const entry of node_fs_1.default.readdirSync(target).sort())
56
+ visit(node_path_1.default.join(target, entry));
57
+ }
58
+ else if (target.endsWith('.prisma')) {
59
+ files.push(target);
60
+ }
61
+ }
62
+ visit(schemaPath);
63
+ const clients = [];
64
+ for (const file of files) {
65
+ const source = node_fs_1.default
66
+ .readFileSync(file, 'utf8')
67
+ .replace(/\/\*[\s\S]*?\*\/|\/\/[^\n]*/g, '');
68
+ for (const match of source.matchAll(/^\s*generator\s+\w+\s*\{([^}]*)\}/gm)) {
69
+ const field = (name) => match[1].match(new RegExp(`^\\s*${name}\\s*=\\s*"([^"\\n]*)"`, 'm'))?.[1];
70
+ if (field('provider') !== 'prisma-client')
71
+ continue;
72
+ const output = field('output');
73
+ const extension = field('generatedFileExtension') ?? 'ts';
74
+ if (!output ||
75
+ !['ts', 'mts', 'cts', 'js', 'mjs', 'cjs'].includes(extension)) {
76
+ throw new Error('Prisma client output must be a static supported path');
77
+ }
78
+ clients.push(node_path_1.default.resolve(node_path_1.default.dirname(file), output, `client.${extension}`));
79
+ }
80
+ }
81
+ if (clients.length !== 1)
82
+ throw new Error('Expected one prisma-client generator');
83
+ if (!node_fs_1.default.existsSync(clients[0])) {
84
+ throw new Error('Generated Prisma client not found; run prisma generate');
85
+ }
86
+ return clients[0];
87
+ }
88
+ function runtimeClient(source, configRoot, requireFromCwd) {
89
+ const extension = node_path_1.default.extname(source);
90
+ if (extension !== '.ts' && extension !== '.cts')
91
+ return source;
92
+ if (require.extensions[extension])
93
+ return source;
94
+ const tsconfig = node_path_1.default.join(configRoot, 'tsconfig.json');
95
+ if (node_fs_1.default.existsSync(tsconfig)) {
96
+ const ts = requireFromCwd('typescript');
97
+ const parsed = ts.readConfigFile(tsconfig, ts.sys.readFile);
98
+ const { rootDir, outDir } = parsed.config?.compilerOptions ?? {};
99
+ if (typeof rootDir === 'string' && typeof outDir === 'string') {
100
+ const relative = node_path_1.default.relative(node_path_1.default.resolve(configRoot, rootDir), source);
101
+ if (relative &&
102
+ !relative.startsWith('..') &&
103
+ !node_path_1.default.isAbsolute(relative)) {
104
+ const emitted = node_path_1.default.resolve(configRoot, outDir, relative.replace(/\.(?:ts|cts)$/, extension === '.cts' ? '.cjs' : '.js'));
105
+ if (node_fs_1.default.existsSync(emitted))
106
+ return emitted;
107
+ }
108
+ }
109
+ }
110
+ return source;
111
+ }
package/package.json ADDED
@@ -0,0 +1,83 @@
1
+ {
2
+ "name": "@skmdev/prisma-fixtures",
3
+ "version": "0.1.1",
4
+ "description": "YAML and JSON fixtures with references, templates and processors for Prisma.",
5
+ "license": "MIT",
6
+ "author": "skmdev <skmdev29@gmail.com>",
7
+ "repository": {
8
+ "type": "git",
9
+ "url": "git+https://github.com/skmdev/prisma-fixtures.git"
10
+ },
11
+ "homepage": "https://github.com/skmdev/prisma-fixtures#readme",
12
+ "bugs": "https://github.com/skmdev/prisma-fixtures/issues",
13
+ "keywords": [
14
+ "prisma",
15
+ "fixtures",
16
+ "yaml",
17
+ "seed",
18
+ "testing"
19
+ ],
20
+ "type": "commonjs",
21
+ "main": "./dist/index.js",
22
+ "types": "./dist/index.d.ts",
23
+ "exports": {
24
+ ".": {
25
+ "types": "./dist/index.d.ts",
26
+ "default": "./dist/index.js"
27
+ },
28
+ "./schema.json": "./schema/fixture.schema.json"
29
+ },
30
+ "bin": {
31
+ "prisma-fixtures": "dist/cli.js",
32
+ "prisma-fixtures-generator": "dist/generator.js"
33
+ },
34
+ "files": [
35
+ "dist",
36
+ "schema/fixture.schema.json",
37
+ "README.md",
38
+ "LICENSE",
39
+ "NOTICE"
40
+ ],
41
+ "engines": {
42
+ "node": ">=22.18.0"
43
+ },
44
+ "publishConfig": {
45
+ "access": "public"
46
+ },
47
+ "scripts": {
48
+ "build": "tsc -p tsconfig.json && node -e \"for (const file of Object.values(require('./package.json').bin)) require('node:fs').chmodSync(file, 0o755)\"",
49
+ "typecheck": "tsc -p tsconfig.json --noEmit",
50
+ "lint": "prettier --check .",
51
+ "lint:fixtures": "npm run build && npm exec --yes --package=. -- node test/lint-examples.cjs",
52
+ "format": "prettier --write .",
53
+ "test": "npm run build && node --test test/*.test.cjs",
54
+ "test:integration": "node test/integration.cjs",
55
+ "test:examples": "node test/integration.cjs --examples",
56
+ "verify": "npm run lint && npm run typecheck && npm test && npm run lint:fixtures",
57
+ "prepack": "npm run build"
58
+ },
59
+ "dependencies": {
60
+ "@faker-js/faker": "10.6.0",
61
+ "@prisma/generator-helper": "7.10.0",
62
+ "ajv": "8.20.0",
63
+ "ejs": "6.0.1",
64
+ "yaml": "2.9.1"
65
+ },
66
+ "devDependencies": {
67
+ "@prisma/adapter-pg": "7.10.0",
68
+ "@prisma/client": "7.10.0",
69
+ "@types/ejs": "3.1.5",
70
+ "@types/node": "22.19.15",
71
+ "prettier": "3.8.3",
72
+ "prisma": "7.10.0",
73
+ "typescript": "5.9.3"
74
+ },
75
+ "overrides": {
76
+ "@prisma/config": {
77
+ "deepmerge-ts": "8.0.0"
78
+ },
79
+ "prisma": {
80
+ "mysql2": "3.24.4"
81
+ }
82
+ }
83
+ }
@@ -0,0 +1,119 @@
1
+ {
2
+ "$schema": "http://json-schema.org/draft-07/schema#",
3
+ "title": "Prisma Fixtures document",
4
+ "description": "A YAML or JSON fixture document for one Prisma entity.",
5
+ "type": "object",
6
+ "required": ["entity", "items"],
7
+ "additionalProperties": false,
8
+ "properties": {
9
+ "entity": {
10
+ "$ref": "#/definitions/entityName"
11
+ },
12
+ "parameters": {
13
+ "description": "Document-local values available to <{parameter.path}> templates.",
14
+ "allOf": [
15
+ {
16
+ "$ref": "#/definitions/safeObject"
17
+ }
18
+ ],
19
+ "default": {}
20
+ },
21
+ "locale": {
22
+ "description": "Faker locale name; English is used as a fallback.",
23
+ "type": "string",
24
+ "minLength": 1
25
+ },
26
+ "processor": {
27
+ "description": "Processor module path, resolved relative to this fixture document.",
28
+ "type": "string",
29
+ "minLength": 1
30
+ },
31
+ "connectedFields": {
32
+ "description": "Fields whose fixture references are converted to Prisma connect inputs.",
33
+ "type": "array",
34
+ "uniqueItems": true,
35
+ "items": {
36
+ "$ref": "#/definitions/fieldName"
37
+ }
38
+ },
39
+ "deferredFields": {
40
+ "description": "Scalar fields written by Prisma update after all fixture creates; requires a saved id and no custom writer.",
41
+ "type": "array",
42
+ "minItems": 1,
43
+ "uniqueItems": true,
44
+ "items": {
45
+ "$ref": "#/definitions/fieldName"
46
+ }
47
+ },
48
+ "items": {
49
+ "description": "Fixture records keyed by a name or inclusive numeric range such as user{1..3}.",
50
+ "type": "object",
51
+ "propertyNames": {
52
+ "$ref": "#/definitions/itemName"
53
+ },
54
+ "additionalProperties": {
55
+ "$ref": "#/definitions/safeObject"
56
+ }
57
+ }
58
+ },
59
+ "definitions": {
60
+ "entityName": {
61
+ "description": "Prisma model/delegate name.",
62
+ "type": "string",
63
+ "pattern": "^[A-Za-z][A-Za-z0-9_-]{0,127}$"
64
+ },
65
+ "fieldName": {
66
+ "description": "Prisma relation field name.",
67
+ "type": "string",
68
+ "pattern": "^[A-Za-z][A-Za-z0-9_]*$",
69
+ "not": {
70
+ "enum": ["__proto__", "prototype", "constructor"]
71
+ }
72
+ },
73
+ "itemName": {
74
+ "description": "Fixture name or inclusive numeric range.",
75
+ "type": "string",
76
+ "pattern": "^(?:[A-Za-z][A-Za-z0-9_-]{0,127}|[A-Za-z][A-Za-z0-9_-]{0,126}\\{[0-9]+\\.\\.[0-9]+\\})$",
77
+ "not": {
78
+ "enum": ["__proto__", "prototype", "constructor"]
79
+ }
80
+ },
81
+ "safeObject": {
82
+ "type": "object",
83
+ "propertyNames": {
84
+ "not": {
85
+ "enum": ["__proto__", "prototype", "constructor"]
86
+ }
87
+ },
88
+ "additionalProperties": {
89
+ "$ref": "#/definitions/jsonValue"
90
+ }
91
+ },
92
+ "jsonValue": {
93
+ "description": "A JSON-compatible value; template expressions remain ordinary strings.",
94
+ "anyOf": [
95
+ {
96
+ "type": "string"
97
+ },
98
+ {
99
+ "type": "number"
100
+ },
101
+ {
102
+ "type": "boolean"
103
+ },
104
+ {
105
+ "type": "null"
106
+ },
107
+ {
108
+ "type": "array",
109
+ "items": {
110
+ "$ref": "#/definitions/jsonValue"
111
+ }
112
+ },
113
+ {
114
+ "$ref": "#/definitions/safeObject"
115
+ }
116
+ ]
117
+ }
118
+ }
119
+ }