@kurotako/parser-prisma 0.1.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/CHANGELOG.md +14 -0
- package/LICENSE +21 -0
- package/README.md +40 -0
- package/dist/index.cjs +879 -0
- package/dist/index.cjs.map +1 -0
- package/dist/index.d.cts +70 -0
- package/dist/index.d.ts +70 -0
- package/dist/index.js +840 -0
- package/dist/index.js.map +1 -0
- package/package.json +69 -0
package/dist/index.cjs
ADDED
|
@@ -0,0 +1,879 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __create = Object.create;
|
|
3
|
+
var __defProp = Object.defineProperty;
|
|
4
|
+
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
|
|
5
|
+
var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
6
|
+
var __getProtoOf = Object.getPrototypeOf;
|
|
7
|
+
var __hasOwnProp = Object.prototype.hasOwnProperty;
|
|
8
|
+
var __export = (target, all) => {
|
|
9
|
+
for (var name in all)
|
|
10
|
+
__defProp(target, name, { get: all[name], enumerable: true });
|
|
11
|
+
};
|
|
12
|
+
var __copyProps = (to, from, except, desc) => {
|
|
13
|
+
if (from && typeof from === "object" || typeof from === "function") {
|
|
14
|
+
for (let key of __getOwnPropNames(from))
|
|
15
|
+
if (!__hasOwnProp.call(to, key) && key !== except)
|
|
16
|
+
__defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
|
|
17
|
+
}
|
|
18
|
+
return to;
|
|
19
|
+
};
|
|
20
|
+
var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(
|
|
21
|
+
// If the importer is in node compatibility mode or this is not an ESM
|
|
22
|
+
// file that has been converted to a CommonJS file using a Babel-
|
|
23
|
+
// compatible transform (i.e. "__esModule" has not been set), then set
|
|
24
|
+
// "default" to the CommonJS "module.exports" for node compatibility.
|
|
25
|
+
isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target,
|
|
26
|
+
mod
|
|
27
|
+
));
|
|
28
|
+
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
|
|
29
|
+
|
|
30
|
+
// src/index.ts
|
|
31
|
+
var index_exports = {};
|
|
32
|
+
__export(index_exports, {
|
|
33
|
+
PrismaInputError: () => PrismaInputError,
|
|
34
|
+
PrismaParserOptions: () => PrismaParserOptions,
|
|
35
|
+
PrismaPeerMissingError: () => PrismaPeerMissingError,
|
|
36
|
+
PrismaSchemaError: () => PrismaSchemaError,
|
|
37
|
+
prismaParser: () => prismaParser
|
|
38
|
+
});
|
|
39
|
+
module.exports = __toCommonJS(index_exports);
|
|
40
|
+
|
|
41
|
+
// src/errors.ts
|
|
42
|
+
var import_core = require("@kurotako/core");
|
|
43
|
+
var PrismaInputError = class extends import_core.TakoError {
|
|
44
|
+
namespace;
|
|
45
|
+
resolvedPath;
|
|
46
|
+
constructor(namespace, resolvedPath, detail) {
|
|
47
|
+
super(
|
|
48
|
+
"prisma_input",
|
|
49
|
+
`prisma parser (namespace '${namespace}'): ${detail} (resolved path: ${resolvedPath})`
|
|
50
|
+
);
|
|
51
|
+
this.namespace = namespace;
|
|
52
|
+
this.resolvedPath = resolvedPath;
|
|
53
|
+
}
|
|
54
|
+
};
|
|
55
|
+
var PrismaPeerMissingError = class extends import_core.TakoError {
|
|
56
|
+
namespace;
|
|
57
|
+
constructor(namespace, options) {
|
|
58
|
+
super(
|
|
59
|
+
"prisma_peer_missing",
|
|
60
|
+
`prisma parser (namespace '${namespace}'): '@prisma/internals' could not be resolved. Add it as a devDependency (\`bun add -d @prisma/internals\`, matching your Prisma major). In a monorepo it is resolved from the directory holding the schema, so it may be installed in the sub-project that owns the schema rather than at the repo root. Note: installing it pulls @prisma/engines, whose postinstall downloads a schema-engine binary.`,
|
|
61
|
+
options
|
|
62
|
+
);
|
|
63
|
+
this.namespace = namespace;
|
|
64
|
+
}
|
|
65
|
+
};
|
|
66
|
+
var PrismaSchemaError = class extends import_core.TakoError {
|
|
67
|
+
namespace;
|
|
68
|
+
prismaMessage;
|
|
69
|
+
constructor(namespace, prismaMessage, options) {
|
|
70
|
+
super(
|
|
71
|
+
"prisma_schema",
|
|
72
|
+
`prisma parser (namespace '${namespace}'): the Prisma schema is invalid:
|
|
73
|
+
${prismaMessage}`,
|
|
74
|
+
options
|
|
75
|
+
);
|
|
76
|
+
this.namespace = namespace;
|
|
77
|
+
this.prismaMessage = prismaMessage;
|
|
78
|
+
}
|
|
79
|
+
};
|
|
80
|
+
|
|
81
|
+
// src/options.ts
|
|
82
|
+
var v = __toESM(require("valibot"), 1);
|
|
83
|
+
var PrismaParserOptions = v.strictObject({
|
|
84
|
+
schema: v.optional(v.string(), "./prisma/schema.prisma"),
|
|
85
|
+
version: v.optional(v.picklist([7, 8]))
|
|
86
|
+
});
|
|
87
|
+
|
|
88
|
+
// src/parser.ts
|
|
89
|
+
var import_node_path3 = require("path");
|
|
90
|
+
var import_config = require("@kurotako/config");
|
|
91
|
+
|
|
92
|
+
// src/detect.ts
|
|
93
|
+
var import_promises = require("fs/promises");
|
|
94
|
+
var import_node_path = require("path");
|
|
95
|
+
var PRISMA_EXT = ".prisma";
|
|
96
|
+
var CONTRACT_FILE = "contract.json";
|
|
97
|
+
function toPosix(p) {
|
|
98
|
+
return import_node_path.sep === "/" ? p : p.split(import_node_path.sep).join("/");
|
|
99
|
+
}
|
|
100
|
+
async function pathKind(p) {
|
|
101
|
+
try {
|
|
102
|
+
const s = await (0, import_promises.stat)(p);
|
|
103
|
+
return s.isDirectory() ? "dir" : "file";
|
|
104
|
+
} catch (err) {
|
|
105
|
+
if (err.code === "ENOENT") {
|
|
106
|
+
return "missing";
|
|
107
|
+
}
|
|
108
|
+
throw err;
|
|
109
|
+
}
|
|
110
|
+
}
|
|
111
|
+
async function collectPrismaFiles(dir) {
|
|
112
|
+
const found = [];
|
|
113
|
+
const entries = await (0, import_promises.readdir)(dir, { withFileTypes: true });
|
|
114
|
+
for (const entry of entries) {
|
|
115
|
+
const full = (0, import_node_path.join)(dir, entry.name);
|
|
116
|
+
if (entry.isFile() && entry.name.endsWith(PRISMA_EXT)) {
|
|
117
|
+
found.push(full);
|
|
118
|
+
} else if (entry.isDirectory()) {
|
|
119
|
+
const nested = await (0, import_promises.readdir)(full, { withFileTypes: true });
|
|
120
|
+
for (const child of nested) {
|
|
121
|
+
if (child.isFile() && child.name.endsWith(PRISMA_EXT)) {
|
|
122
|
+
found.push((0, import_node_path.join)(full, child.name));
|
|
123
|
+
}
|
|
124
|
+
}
|
|
125
|
+
}
|
|
126
|
+
}
|
|
127
|
+
return found;
|
|
128
|
+
}
|
|
129
|
+
async function dirHasContract(dir) {
|
|
130
|
+
return await pathKind((0, import_node_path.join)(dir, CONTRACT_FILE)) === "file";
|
|
131
|
+
}
|
|
132
|
+
async function readTuples(root, files) {
|
|
133
|
+
const tuples = await Promise.all(
|
|
134
|
+
files.map(
|
|
135
|
+
async (file) => [
|
|
136
|
+
toPosix((0, import_node_path.relative)(root, file)),
|
|
137
|
+
await (0, import_promises.readFile)(file, "utf8")
|
|
138
|
+
]
|
|
139
|
+
)
|
|
140
|
+
);
|
|
141
|
+
return tuples.sort(([a], [b]) => a < b ? -1 : a > b ? 1 : 0);
|
|
142
|
+
}
|
|
143
|
+
async function resolveMode7(namespace, resolved, kind) {
|
|
144
|
+
if (kind === "file") {
|
|
145
|
+
return {
|
|
146
|
+
mode: 7,
|
|
147
|
+
kind: "file",
|
|
148
|
+
files: await readTuples((0, import_node_path.dirname)(resolved), [resolved])
|
|
149
|
+
};
|
|
150
|
+
}
|
|
151
|
+
const files = await collectPrismaFiles(resolved);
|
|
152
|
+
if (files.length === 0) {
|
|
153
|
+
throw new PrismaInputError(
|
|
154
|
+
namespace,
|
|
155
|
+
resolved,
|
|
156
|
+
"the schema folder contains no .prisma file"
|
|
157
|
+
);
|
|
158
|
+
}
|
|
159
|
+
return { mode: 7, kind: "folder", files: await readTuples(resolved, files) };
|
|
160
|
+
}
|
|
161
|
+
async function resolveMode8(namespace, resolved, kind) {
|
|
162
|
+
if (kind === "dir") {
|
|
163
|
+
const contractPath = (0, import_node_path.join)(resolved, CONTRACT_FILE);
|
|
164
|
+
if (await pathKind(contractPath) !== "file") {
|
|
165
|
+
throw new PrismaInputError(
|
|
166
|
+
namespace,
|
|
167
|
+
resolved,
|
|
168
|
+
`version 8 mode expects a ${CONTRACT_FILE} in the folder`
|
|
169
|
+
);
|
|
170
|
+
}
|
|
171
|
+
return { mode: 8, kind: "contract", contractPath };
|
|
172
|
+
}
|
|
173
|
+
return { mode: 8, kind: "contract", contractPath: resolved };
|
|
174
|
+
}
|
|
175
|
+
async function resolveInput(cwd, o, namespace = "<unknown>") {
|
|
176
|
+
const resolved = (0, import_node_path.resolve)(cwd, o.schema);
|
|
177
|
+
const kind = await pathKind(resolved);
|
|
178
|
+
if (kind === "missing") {
|
|
179
|
+
throw new PrismaInputError(
|
|
180
|
+
namespace,
|
|
181
|
+
resolved,
|
|
182
|
+
"the schema path does not exist"
|
|
183
|
+
);
|
|
184
|
+
}
|
|
185
|
+
if (o.version === 8) {
|
|
186
|
+
return resolveMode8(namespace, resolved, kind);
|
|
187
|
+
}
|
|
188
|
+
if (o.version === 7) {
|
|
189
|
+
return resolveMode7(namespace, resolved, kind);
|
|
190
|
+
}
|
|
191
|
+
if (kind === "file") {
|
|
192
|
+
if ((0, import_node_path.basename)(resolved) === CONTRACT_FILE) {
|
|
193
|
+
return { mode: 8, kind: "contract", contractPath: resolved };
|
|
194
|
+
}
|
|
195
|
+
if (resolved.endsWith(PRISMA_EXT)) {
|
|
196
|
+
return resolveMode7(namespace, resolved, "file");
|
|
197
|
+
}
|
|
198
|
+
throw new PrismaInputError(
|
|
199
|
+
namespace,
|
|
200
|
+
resolved,
|
|
201
|
+
`not a .prisma file or a ${CONTRACT_FILE}`
|
|
202
|
+
);
|
|
203
|
+
}
|
|
204
|
+
if (await dirHasContract(resolved)) {
|
|
205
|
+
return resolveMode8(namespace, resolved, "dir");
|
|
206
|
+
}
|
|
207
|
+
return resolveMode7(namespace, resolved, "dir");
|
|
208
|
+
}
|
|
209
|
+
|
|
210
|
+
// src/dmmf/load.ts
|
|
211
|
+
var import_promises2 = require("fs/promises");
|
|
212
|
+
var import_node_module = require("module");
|
|
213
|
+
var import_node_path2 = require("path");
|
|
214
|
+
var import_node_url = require("url");
|
|
215
|
+
|
|
216
|
+
// src/dmmf/read.ts
|
|
217
|
+
function readField(f) {
|
|
218
|
+
const field = {
|
|
219
|
+
name: f.name,
|
|
220
|
+
type: f.type,
|
|
221
|
+
kind: f.kind === "enum" ? "enum" : f.kind === "unsupported" ? "unsupported" : "scalar",
|
|
222
|
+
isList: f.isList,
|
|
223
|
+
isRequired: f.isRequired,
|
|
224
|
+
isUnique: f.isUnique,
|
|
225
|
+
isUpdatedAt: f.isUpdatedAt ?? false,
|
|
226
|
+
hasDefaultValue: f.hasDefaultValue,
|
|
227
|
+
nativeType: f.nativeType ? [f.nativeType[0], [...f.nativeType[1]]] : null
|
|
228
|
+
};
|
|
229
|
+
if (f.default !== void 0) {
|
|
230
|
+
field.default = f.default;
|
|
231
|
+
}
|
|
232
|
+
if (f.documentation !== void 0) {
|
|
233
|
+
field.doc = f.documentation;
|
|
234
|
+
}
|
|
235
|
+
return field;
|
|
236
|
+
}
|
|
237
|
+
function readEdge(f) {
|
|
238
|
+
const edge = {
|
|
239
|
+
fieldName: f.name,
|
|
240
|
+
relationName: f.relationName ?? "",
|
|
241
|
+
targetEntity: f.type,
|
|
242
|
+
isList: f.isList,
|
|
243
|
+
isRequired: f.isRequired,
|
|
244
|
+
fromFields: [...f.relationFromFields ?? []],
|
|
245
|
+
toFields: [...f.relationToFields ?? []]
|
|
246
|
+
};
|
|
247
|
+
if (f.relationOnDelete !== void 0) {
|
|
248
|
+
edge.onDelete = f.relationOnDelete;
|
|
249
|
+
}
|
|
250
|
+
if (f.relationOnUpdate !== void 0) {
|
|
251
|
+
edge.onUpdate = f.relationOnUpdate;
|
|
252
|
+
}
|
|
253
|
+
return edge;
|
|
254
|
+
}
|
|
255
|
+
function readPrimaryKey(model) {
|
|
256
|
+
if (model.primaryKey && model.primaryKey.fields.length > 0) {
|
|
257
|
+
return [...model.primaryKey.fields];
|
|
258
|
+
}
|
|
259
|
+
const id = model.fields.find((f) => f.isId);
|
|
260
|
+
return id ? [id.name] : [];
|
|
261
|
+
}
|
|
262
|
+
function readUniques(model) {
|
|
263
|
+
if (model.uniqueIndexes.length > 0) {
|
|
264
|
+
return model.uniqueIndexes.map((u) => {
|
|
265
|
+
const entry = { fields: [...u.fields] };
|
|
266
|
+
if (u.name) {
|
|
267
|
+
entry.name = u.name;
|
|
268
|
+
}
|
|
269
|
+
return entry;
|
|
270
|
+
});
|
|
271
|
+
}
|
|
272
|
+
return model.uniqueFields.map((fields) => ({ fields: [...fields] }));
|
|
273
|
+
}
|
|
274
|
+
function readIndexes(modelName, all) {
|
|
275
|
+
if (!all) {
|
|
276
|
+
return [];
|
|
277
|
+
}
|
|
278
|
+
return all.filter((idx) => idx.model === modelName && idx.type === "normal").map((idx) => {
|
|
279
|
+
const entry = { fields: idx.fields.map((f) => f.name) };
|
|
280
|
+
if (idx.name) {
|
|
281
|
+
entry.name = idx.name;
|
|
282
|
+
}
|
|
283
|
+
if (idx.algorithm) {
|
|
284
|
+
entry.type = idx.algorithm.toLowerCase();
|
|
285
|
+
}
|
|
286
|
+
return entry;
|
|
287
|
+
});
|
|
288
|
+
}
|
|
289
|
+
function readEntity(model, doc) {
|
|
290
|
+
const fields = [];
|
|
291
|
+
const relationEdges = [];
|
|
292
|
+
for (const f of model.fields) {
|
|
293
|
+
if (f.kind === "object") {
|
|
294
|
+
relationEdges.push(readEdge(f));
|
|
295
|
+
} else {
|
|
296
|
+
fields.push(readField(f));
|
|
297
|
+
}
|
|
298
|
+
}
|
|
299
|
+
const entity = {
|
|
300
|
+
name: model.name,
|
|
301
|
+
fields,
|
|
302
|
+
relationEdges,
|
|
303
|
+
primaryKey: readPrimaryKey(model),
|
|
304
|
+
uniques: readUniques(model),
|
|
305
|
+
indexes: readIndexes(model.name, doc.datamodel.indexes)
|
|
306
|
+
};
|
|
307
|
+
if (model.dbName) {
|
|
308
|
+
entity.dbName = model.dbName;
|
|
309
|
+
}
|
|
310
|
+
if (model.documentation !== void 0) {
|
|
311
|
+
entity.doc = model.documentation;
|
|
312
|
+
}
|
|
313
|
+
return entity;
|
|
314
|
+
}
|
|
315
|
+
function readEnum(e) {
|
|
316
|
+
const def = {
|
|
317
|
+
name: e.name,
|
|
318
|
+
values: e.values.map((value) => {
|
|
319
|
+
const entry = { name: value.name };
|
|
320
|
+
if (value.dbName) {
|
|
321
|
+
entry.dbName = value.dbName;
|
|
322
|
+
}
|
|
323
|
+
return entry;
|
|
324
|
+
})
|
|
325
|
+
};
|
|
326
|
+
if (e.dbName) {
|
|
327
|
+
def.dbName = e.dbName;
|
|
328
|
+
}
|
|
329
|
+
if (e.documentation !== void 0) {
|
|
330
|
+
def.doc = e.documentation;
|
|
331
|
+
}
|
|
332
|
+
return def;
|
|
333
|
+
}
|
|
334
|
+
function toPrismaModel(doc) {
|
|
335
|
+
return {
|
|
336
|
+
entities: doc.datamodel.models.map((m) => readEntity(m, doc)),
|
|
337
|
+
enums: doc.datamodel.enums.map(readEnum)
|
|
338
|
+
};
|
|
339
|
+
}
|
|
340
|
+
|
|
341
|
+
// src/dmmf/load.ts
|
|
342
|
+
async function resolveInternals(ctx) {
|
|
343
|
+
const base = ctx.anchorDir ?? ctx.cwd;
|
|
344
|
+
const require2 = (0, import_node_module.createRequire)((0, import_node_path2.join)(base, "noop.js"));
|
|
345
|
+
let entry;
|
|
346
|
+
try {
|
|
347
|
+
entry = require2.resolve("@prisma/internals");
|
|
348
|
+
} catch (err) {
|
|
349
|
+
throw new PrismaPeerMissingError(ctx.namespace, { cause: err });
|
|
350
|
+
}
|
|
351
|
+
let mod;
|
|
352
|
+
try {
|
|
353
|
+
mod = await import((0, import_node_url.pathToFileURL)(entry).href);
|
|
354
|
+
} catch (err) {
|
|
355
|
+
throw new PrismaPeerMissingError(ctx.namespace, { cause: err });
|
|
356
|
+
}
|
|
357
|
+
const getDMMF = mod.default?.getDMMF ?? mod.getDMMF;
|
|
358
|
+
if (typeof getDMMF !== "function") {
|
|
359
|
+
throw new PrismaPeerMissingError(ctx.namespace);
|
|
360
|
+
}
|
|
361
|
+
let prismaVersion = "unknown";
|
|
362
|
+
try {
|
|
363
|
+
const pkg = JSON.parse(
|
|
364
|
+
await (0, import_promises2.readFile)(require2.resolve("@prisma/internals/package.json"), "utf8")
|
|
365
|
+
);
|
|
366
|
+
if (typeof pkg.version === "string") {
|
|
367
|
+
prismaVersion = pkg.version;
|
|
368
|
+
}
|
|
369
|
+
} catch {
|
|
370
|
+
ctx.logger.debug(
|
|
371
|
+
"prisma parser: could not read @prisma/internals version",
|
|
372
|
+
{ namespace: ctx.namespace }
|
|
373
|
+
);
|
|
374
|
+
}
|
|
375
|
+
return { getDMMF, prismaVersion };
|
|
376
|
+
}
|
|
377
|
+
async function readDmmf(input, ctx) {
|
|
378
|
+
const { getDMMF, prismaVersion } = await resolveInternals(ctx);
|
|
379
|
+
const datamodel = input.kind === "file" ? input.files[0]?.[1] ?? "" : input.files.map(([path, content]) => [path, content]);
|
|
380
|
+
let doc;
|
|
381
|
+
try {
|
|
382
|
+
doc = await getDMMF({ datamodel });
|
|
383
|
+
} catch (err) {
|
|
384
|
+
const message = err instanceof Error ? err.message : String(err);
|
|
385
|
+
throw new PrismaSchemaError(ctx.namespace, message, { cause: err });
|
|
386
|
+
}
|
|
387
|
+
return { model: toPrismaModel(doc), prismaVersion };
|
|
388
|
+
}
|
|
389
|
+
|
|
390
|
+
// src/map/build.ts
|
|
391
|
+
var import_ir = require("@kurotako/ir");
|
|
392
|
+
|
|
393
|
+
// src/map/defaults.ts
|
|
394
|
+
var ID_FORMATS = {
|
|
395
|
+
uuid: "uuid",
|
|
396
|
+
cuid: "cuid",
|
|
397
|
+
ulid: "ulid"
|
|
398
|
+
};
|
|
399
|
+
function isCall(raw) {
|
|
400
|
+
return typeof raw === "object" && !Array.isArray(raw) && raw !== null;
|
|
401
|
+
}
|
|
402
|
+
function mapDefault(raw) {
|
|
403
|
+
if (raw === void 0) {
|
|
404
|
+
return {};
|
|
405
|
+
}
|
|
406
|
+
if (!isCall(raw)) {
|
|
407
|
+
return { default: { kind: "value", value: raw } };
|
|
408
|
+
}
|
|
409
|
+
const { name, args } = raw;
|
|
410
|
+
if (name === "dbgenerated") {
|
|
411
|
+
return { default: { kind: "expr", expr: "dbgenerated", args: [...args] } };
|
|
412
|
+
}
|
|
413
|
+
const idFormat = ID_FORMATS[name];
|
|
414
|
+
if (idFormat !== void 0) {
|
|
415
|
+
const format = name === "cuid" && args[0] === 2 ? "cuid2" : idFormat;
|
|
416
|
+
return { default: { kind: "expr", expr: `${name}()` }, format };
|
|
417
|
+
}
|
|
418
|
+
return { default: { kind: "expr", expr: `${name}()` } };
|
|
419
|
+
}
|
|
420
|
+
|
|
421
|
+
// src/map/scalars.ts
|
|
422
|
+
var SCALAR_TABLE = {
|
|
423
|
+
String: "string",
|
|
424
|
+
Boolean: "boolean",
|
|
425
|
+
Int: "int",
|
|
426
|
+
BigInt: "bigint",
|
|
427
|
+
Float: "float",
|
|
428
|
+
Decimal: "decimal",
|
|
429
|
+
DateTime: "datetime",
|
|
430
|
+
Json: "json",
|
|
431
|
+
Bytes: "bytes"
|
|
432
|
+
};
|
|
433
|
+
var LENGTH_NATIVE = /* @__PURE__ */ new Set(["VarChar", "Char", "NVarChar", "String"]);
|
|
434
|
+
var UUID_NATIVE = /* @__PURE__ */ new Set(["Uuid", "ObjectId"]);
|
|
435
|
+
var NOOP_NATIVE = /* @__PURE__ */ new Set([
|
|
436
|
+
"Text",
|
|
437
|
+
"Citext",
|
|
438
|
+
"Xml",
|
|
439
|
+
"Bit",
|
|
440
|
+
"VarBit",
|
|
441
|
+
"Inet",
|
|
442
|
+
"Line",
|
|
443
|
+
"LongText",
|
|
444
|
+
"MediumText",
|
|
445
|
+
"TinyText",
|
|
446
|
+
"SmallInt",
|
|
447
|
+
"MediumInt",
|
|
448
|
+
"UnsignedInt",
|
|
449
|
+
"UnsignedBigInt",
|
|
450
|
+
"Money",
|
|
451
|
+
"Real",
|
|
452
|
+
"DoublePrecision",
|
|
453
|
+
"Decimal",
|
|
454
|
+
"Numeric",
|
|
455
|
+
"SmallMoney",
|
|
456
|
+
"Timestamp",
|
|
457
|
+
"Timestamptz",
|
|
458
|
+
"DateTime2",
|
|
459
|
+
"DateTimeOffset"
|
|
460
|
+
]);
|
|
461
|
+
function refineNative(native, constraints, result, field, logger) {
|
|
462
|
+
const [name, args] = native;
|
|
463
|
+
if (LENGTH_NATIVE.has(name)) {
|
|
464
|
+
const n = Number(args[0]);
|
|
465
|
+
if (Number.isFinite(n)) {
|
|
466
|
+
constraints.maxLength = n;
|
|
467
|
+
}
|
|
468
|
+
return;
|
|
469
|
+
}
|
|
470
|
+
if (UUID_NATIVE.has(name)) {
|
|
471
|
+
result.scalarOverride = "uuid";
|
|
472
|
+
return;
|
|
473
|
+
}
|
|
474
|
+
if (name === "Date") {
|
|
475
|
+
result.scalarOverride = "date";
|
|
476
|
+
return;
|
|
477
|
+
}
|
|
478
|
+
if (name === "Time" || name === "Timetz") {
|
|
479
|
+
constraints.format = "time";
|
|
480
|
+
return;
|
|
481
|
+
}
|
|
482
|
+
if (NOOP_NATIVE.has(name)) {
|
|
483
|
+
return;
|
|
484
|
+
}
|
|
485
|
+
logger?.debug(`prisma parser: ignoring unmapped native type @db.${name}`, {
|
|
486
|
+
field: field.name
|
|
487
|
+
});
|
|
488
|
+
}
|
|
489
|
+
function mapFieldType(field, logger) {
|
|
490
|
+
const constraints = {};
|
|
491
|
+
if (field.kind === "unsupported") {
|
|
492
|
+
return { type: { kind: "unknown", hint: field.type }, constraints };
|
|
493
|
+
}
|
|
494
|
+
if (field.kind === "enum") {
|
|
495
|
+
return { type: { kind: "enum", ref: field.type }, constraints };
|
|
496
|
+
}
|
|
497
|
+
const scalar = SCALAR_TABLE[field.type];
|
|
498
|
+
if (scalar === void 0) {
|
|
499
|
+
return { type: { kind: "unknown", hint: field.type }, constraints };
|
|
500
|
+
}
|
|
501
|
+
const result = {
|
|
502
|
+
type: { kind: "scalar", scalar },
|
|
503
|
+
constraints
|
|
504
|
+
};
|
|
505
|
+
if (field.nativeType) {
|
|
506
|
+
refineNative(field.nativeType, constraints, result, field, logger);
|
|
507
|
+
}
|
|
508
|
+
return result;
|
|
509
|
+
}
|
|
510
|
+
|
|
511
|
+
// src/map/relations.ts
|
|
512
|
+
var ACTION_MAP = {
|
|
513
|
+
Cascade: "cascade",
|
|
514
|
+
Restrict: "restrict",
|
|
515
|
+
SetNull: "setNull",
|
|
516
|
+
SetDefault: "setDefault",
|
|
517
|
+
NoAction: "noAction"
|
|
518
|
+
};
|
|
519
|
+
function mapAction(raw) {
|
|
520
|
+
return raw === void 0 ? void 0 : ACTION_MAP[raw];
|
|
521
|
+
}
|
|
522
|
+
function lcfirst(s) {
|
|
523
|
+
return s.length === 0 ? s : s.charAt(0).toLowerCase() + s.slice(1);
|
|
524
|
+
}
|
|
525
|
+
function isImplicitM2M(edges) {
|
|
526
|
+
return edges.length === 2 && edges.every(
|
|
527
|
+
({ edge }) => edge.isList && edge.fromFields.length === 0 && edge.toFields.length === 0
|
|
528
|
+
);
|
|
529
|
+
}
|
|
530
|
+
function pkScalar(entities, entityName, logger) {
|
|
531
|
+
const entity = entities.get(entityName);
|
|
532
|
+
if (entity && entity.primaryKey.length === 1) {
|
|
533
|
+
const pkName = entity.primaryKey[0];
|
|
534
|
+
const field = entity.fields.find((f) => f.name === pkName);
|
|
535
|
+
if (field) {
|
|
536
|
+
const mapped = mapFieldType(field);
|
|
537
|
+
if (mapped.scalarOverride) {
|
|
538
|
+
return mapped.scalarOverride;
|
|
539
|
+
}
|
|
540
|
+
if (mapped.type.kind === "scalar") {
|
|
541
|
+
return mapped.type.scalar;
|
|
542
|
+
}
|
|
543
|
+
}
|
|
544
|
+
}
|
|
545
|
+
logger?.debug(
|
|
546
|
+
`prisma parser: could not resolve primary-key scalar of '${entityName}', defaulting to string`
|
|
547
|
+
);
|
|
548
|
+
return "string";
|
|
549
|
+
}
|
|
550
|
+
function normalRelation(edge, back) {
|
|
551
|
+
const owning = edge.fromFields.length > 0;
|
|
552
|
+
const relation = {
|
|
553
|
+
name: edge.fieldName,
|
|
554
|
+
target: { namespace: "", entity: edge.targetEntity },
|
|
555
|
+
cardinality: edge.isList ? "many" : "one",
|
|
556
|
+
optional: !edge.isRequired,
|
|
557
|
+
owning
|
|
558
|
+
};
|
|
559
|
+
if (owning) {
|
|
560
|
+
relation.fkFields = [...edge.fromFields];
|
|
561
|
+
relation.references = [...edge.toFields];
|
|
562
|
+
}
|
|
563
|
+
if (back) {
|
|
564
|
+
relation.backRelation = back.fieldName;
|
|
565
|
+
}
|
|
566
|
+
const onDelete = mapAction(edge.onDelete);
|
|
567
|
+
if (onDelete) {
|
|
568
|
+
relation.onDelete = onDelete;
|
|
569
|
+
}
|
|
570
|
+
const onUpdate = mapAction(edge.onUpdate);
|
|
571
|
+
if (onUpdate) {
|
|
572
|
+
relation.onUpdate = onUpdate;
|
|
573
|
+
}
|
|
574
|
+
return relation;
|
|
575
|
+
}
|
|
576
|
+
function materialiseM2M(a, b, relationName, entities, logger) {
|
|
577
|
+
const sorted = [a.owner, b.owner].sort(
|
|
578
|
+
(l, r) => l < r ? -1 : l > r ? 1 : 0
|
|
579
|
+
);
|
|
580
|
+
const x = sorted[0] ?? a.owner;
|
|
581
|
+
const y = sorted[1] ?? b.owner;
|
|
582
|
+
const defaultName = `${x}To${y}`;
|
|
583
|
+
const name = relationName !== "" && relationName !== defaultName ? relationName : `${x}${y}`;
|
|
584
|
+
const fkX = `${lcfirst(x)}Id`;
|
|
585
|
+
const fkY = `${lcfirst(y)}Id`;
|
|
586
|
+
const relX = lcfirst(x);
|
|
587
|
+
const relY = lcfirst(y);
|
|
588
|
+
const pkX = entities.get(x)?.primaryKey[0] ?? "id";
|
|
589
|
+
const pkY = entities.get(y)?.primaryKey[0] ?? "id";
|
|
590
|
+
const synthetic = {
|
|
591
|
+
name,
|
|
592
|
+
fields: [
|
|
593
|
+
{ name: fkX, scalar: pkScalar(entities, x, logger) },
|
|
594
|
+
{ name: fkY, scalar: pkScalar(entities, y, logger) }
|
|
595
|
+
],
|
|
596
|
+
primaryKey: [fkX, fkY],
|
|
597
|
+
relations: [
|
|
598
|
+
{
|
|
599
|
+
name: relX,
|
|
600
|
+
target: { namespace: "", entity: x },
|
|
601
|
+
cardinality: "one",
|
|
602
|
+
optional: false,
|
|
603
|
+
owning: true,
|
|
604
|
+
fkFields: [fkX],
|
|
605
|
+
references: [pkX],
|
|
606
|
+
onDelete: "cascade"
|
|
607
|
+
},
|
|
608
|
+
{
|
|
609
|
+
name: relY,
|
|
610
|
+
target: { namespace: "", entity: y },
|
|
611
|
+
cardinality: "one",
|
|
612
|
+
optional: false,
|
|
613
|
+
owning: true,
|
|
614
|
+
fkFields: [fkY],
|
|
615
|
+
references: [pkY],
|
|
616
|
+
onDelete: "cascade"
|
|
617
|
+
}
|
|
618
|
+
]
|
|
619
|
+
};
|
|
620
|
+
const mkRewrite = (edge, backName) => ({
|
|
621
|
+
owner: edge.owner,
|
|
622
|
+
relation: {
|
|
623
|
+
name: edge.edge.fieldName,
|
|
624
|
+
target: { namespace: "", entity: name },
|
|
625
|
+
cardinality: "many",
|
|
626
|
+
optional: false,
|
|
627
|
+
owning: false,
|
|
628
|
+
backRelation: backName
|
|
629
|
+
}
|
|
630
|
+
});
|
|
631
|
+
return {
|
|
632
|
+
synthetic,
|
|
633
|
+
rewrites: [
|
|
634
|
+
mkRewrite(a, a.owner === x ? relX : relY),
|
|
635
|
+
mkRewrite(b, b.owner === x ? relX : relY)
|
|
636
|
+
]
|
|
637
|
+
};
|
|
638
|
+
}
|
|
639
|
+
function buildRelations(model, logger) {
|
|
640
|
+
const entities = new Map(model.entities.map((e) => [e.name, e]));
|
|
641
|
+
const relations = /* @__PURE__ */ new Map();
|
|
642
|
+
const syntheticEntities = [];
|
|
643
|
+
const push = (owner, relation) => {
|
|
644
|
+
const list = relations.get(owner) ?? [];
|
|
645
|
+
list.push(relation);
|
|
646
|
+
relations.set(owner, list);
|
|
647
|
+
};
|
|
648
|
+
const groups = /* @__PURE__ */ new Map();
|
|
649
|
+
for (const entity of model.entities) {
|
|
650
|
+
for (const edge of entity.relationEdges) {
|
|
651
|
+
const list = groups.get(edge.relationName) ?? [];
|
|
652
|
+
list.push({ owner: entity.name, edge });
|
|
653
|
+
groups.set(edge.relationName, list);
|
|
654
|
+
}
|
|
655
|
+
}
|
|
656
|
+
for (const [relationName, edges] of groups) {
|
|
657
|
+
if (isImplicitM2M(edges)) {
|
|
658
|
+
const [a, b] = edges;
|
|
659
|
+
const { synthetic, rewrites } = materialiseM2M(
|
|
660
|
+
a,
|
|
661
|
+
b,
|
|
662
|
+
relationName,
|
|
663
|
+
entities,
|
|
664
|
+
logger
|
|
665
|
+
);
|
|
666
|
+
syntheticEntities.push(synthetic);
|
|
667
|
+
for (const { owner, relation } of rewrites) {
|
|
668
|
+
push(owner, relation);
|
|
669
|
+
}
|
|
670
|
+
continue;
|
|
671
|
+
}
|
|
672
|
+
for (const { owner, edge } of edges) {
|
|
673
|
+
const back = edges.find((e) => e.edge !== edge)?.edge;
|
|
674
|
+
push(owner, normalRelation(edge, back));
|
|
675
|
+
}
|
|
676
|
+
}
|
|
677
|
+
return { relations, syntheticEntities };
|
|
678
|
+
}
|
|
679
|
+
|
|
680
|
+
// src/map/build.ts
|
|
681
|
+
var INDEX_TYPES = /* @__PURE__ */ new Set([
|
|
682
|
+
"btree",
|
|
683
|
+
"hash",
|
|
684
|
+
"gin",
|
|
685
|
+
"gist",
|
|
686
|
+
"brin",
|
|
687
|
+
"spgist"
|
|
688
|
+
]);
|
|
689
|
+
function asIndexType(raw) {
|
|
690
|
+
return raw !== void 0 && INDEX_TYPES.has(raw) ? raw : void 0;
|
|
691
|
+
}
|
|
692
|
+
function fillEnum(eb, e) {
|
|
693
|
+
for (const value of e.values) {
|
|
694
|
+
const opts = {};
|
|
695
|
+
if (value.dbName !== void 0) {
|
|
696
|
+
opts.dbName = value.dbName;
|
|
697
|
+
}
|
|
698
|
+
if (value.doc !== void 0) {
|
|
699
|
+
opts.doc = value.doc;
|
|
700
|
+
}
|
|
701
|
+
eb.value(value.name, opts);
|
|
702
|
+
}
|
|
703
|
+
if (e.doc !== void 0) {
|
|
704
|
+
eb.doc(e.doc);
|
|
705
|
+
}
|
|
706
|
+
if (e.dbName !== void 0) {
|
|
707
|
+
eb.dbName(e.dbName);
|
|
708
|
+
}
|
|
709
|
+
}
|
|
710
|
+
function addRelation(eb, rel, namespace) {
|
|
711
|
+
eb.relation(rel.name, (rb) => {
|
|
712
|
+
rb.to(namespace, rel.target.entity);
|
|
713
|
+
if (rel.cardinality === "many") {
|
|
714
|
+
rb.many();
|
|
715
|
+
} else {
|
|
716
|
+
rb.one();
|
|
717
|
+
}
|
|
718
|
+
if (rel.optional) {
|
|
719
|
+
rb.optional();
|
|
720
|
+
}
|
|
721
|
+
if (rel.owning) {
|
|
722
|
+
rb.owning();
|
|
723
|
+
}
|
|
724
|
+
if (rel.backRelation !== void 0) {
|
|
725
|
+
rb.backRelation(rel.backRelation);
|
|
726
|
+
}
|
|
727
|
+
if (rel.fkFields) {
|
|
728
|
+
rb.fkFields(...rel.fkFields);
|
|
729
|
+
}
|
|
730
|
+
if (rel.references) {
|
|
731
|
+
rb.references(...rel.references);
|
|
732
|
+
}
|
|
733
|
+
if (rel.onDelete) {
|
|
734
|
+
rb.onDelete(rel.onDelete);
|
|
735
|
+
}
|
|
736
|
+
if (rel.onUpdate) {
|
|
737
|
+
rb.onUpdate(rel.onUpdate);
|
|
738
|
+
}
|
|
739
|
+
});
|
|
740
|
+
}
|
|
741
|
+
function buildSourceIR(namespace, model, parserVersion, logger) {
|
|
742
|
+
const b = (0, import_ir.createSourceIR)({ namespace, parser: "prisma", parserVersion });
|
|
743
|
+
for (const e of model.enums) {
|
|
744
|
+
b.addEnum(e.name, (eb) => fillEnum(eb, e));
|
|
745
|
+
}
|
|
746
|
+
const { relations, syntheticEntities } = buildRelations(model, logger);
|
|
747
|
+
for (const entity of model.entities) {
|
|
748
|
+
b.addEntity(entity.name, (eb) => {
|
|
749
|
+
for (const field of entity.fields) {
|
|
750
|
+
eb.field(field.name, (fb) => {
|
|
751
|
+
const mapped = mapFieldType(field, logger);
|
|
752
|
+
const scalar = mapped.scalarOverride ?? (mapped.type.kind === "scalar" ? mapped.type.scalar : void 0);
|
|
753
|
+
if (scalar !== void 0) {
|
|
754
|
+
fb.scalar(scalar);
|
|
755
|
+
} else if (mapped.type.kind === "enum") {
|
|
756
|
+
fb.enum(mapped.type.ref);
|
|
757
|
+
} else {
|
|
758
|
+
fb.unknown(
|
|
759
|
+
mapped.type.kind === "unknown" ? mapped.type.hint : void 0
|
|
760
|
+
);
|
|
761
|
+
}
|
|
762
|
+
const isString = scalar === "string";
|
|
763
|
+
const { maxLength, format: nativeFormat } = mapped.constraints;
|
|
764
|
+
if (maxLength !== void 0) {
|
|
765
|
+
fb.maxLength(maxLength);
|
|
766
|
+
}
|
|
767
|
+
const mappedDefault = mapDefault(field.default);
|
|
768
|
+
if (mappedDefault.default) {
|
|
769
|
+
fb.default(mappedDefault.default);
|
|
770
|
+
}
|
|
771
|
+
const format = mappedDefault.format ?? nativeFormat;
|
|
772
|
+
if (format !== void 0) {
|
|
773
|
+
if (isString) {
|
|
774
|
+
fb.format(format);
|
|
775
|
+
} else {
|
|
776
|
+
logger?.debug(
|
|
777
|
+
`prisma parser: dropping format '${format}' on non-string field '${entity.name}.${field.name}'`
|
|
778
|
+
);
|
|
779
|
+
}
|
|
780
|
+
}
|
|
781
|
+
if (field.isList) {
|
|
782
|
+
fb.list();
|
|
783
|
+
}
|
|
784
|
+
if (!field.isRequired) {
|
|
785
|
+
fb.nullable();
|
|
786
|
+
}
|
|
787
|
+
if (field.hasDefaultValue || field.isUpdatedAt) {
|
|
788
|
+
fb.optional();
|
|
789
|
+
}
|
|
790
|
+
if (field.isUnique) {
|
|
791
|
+
fb.unique();
|
|
792
|
+
}
|
|
793
|
+
if (field.doc !== void 0) {
|
|
794
|
+
fb.doc(field.doc);
|
|
795
|
+
}
|
|
796
|
+
});
|
|
797
|
+
}
|
|
798
|
+
if (entity.primaryKey.length > 0) {
|
|
799
|
+
eb.primaryKey(...entity.primaryKey);
|
|
800
|
+
}
|
|
801
|
+
for (const unique of entity.uniques) {
|
|
802
|
+
eb.unique(
|
|
803
|
+
unique.fields,
|
|
804
|
+
unique.name ? { name: unique.name } : void 0
|
|
805
|
+
);
|
|
806
|
+
}
|
|
807
|
+
for (const index of entity.indexes) {
|
|
808
|
+
const opts = {};
|
|
809
|
+
if (index.name) {
|
|
810
|
+
opts.name = index.name;
|
|
811
|
+
}
|
|
812
|
+
const type = asIndexType(index.type);
|
|
813
|
+
if (type) {
|
|
814
|
+
opts.type = type;
|
|
815
|
+
}
|
|
816
|
+
eb.index(index.fields, opts);
|
|
817
|
+
}
|
|
818
|
+
if (entity.doc !== void 0) {
|
|
819
|
+
eb.doc(entity.doc);
|
|
820
|
+
}
|
|
821
|
+
if (entity.dbName !== void 0) {
|
|
822
|
+
eb.dbName(entity.dbName);
|
|
823
|
+
}
|
|
824
|
+
for (const rel of relations.get(entity.name) ?? []) {
|
|
825
|
+
addRelation(eb, rel, namespace);
|
|
826
|
+
}
|
|
827
|
+
});
|
|
828
|
+
}
|
|
829
|
+
for (const synthetic of syntheticEntities) {
|
|
830
|
+
b.addEntity(synthetic.name, (eb) => {
|
|
831
|
+
for (const field of synthetic.fields) {
|
|
832
|
+
eb.field(field.name, (fb) => fb.scalar(field.scalar));
|
|
833
|
+
}
|
|
834
|
+
eb.primaryKey(...synthetic.primaryKey);
|
|
835
|
+
for (const rel of synthetic.relations) {
|
|
836
|
+
addRelation(eb, rel, namespace);
|
|
837
|
+
}
|
|
838
|
+
});
|
|
839
|
+
}
|
|
840
|
+
return b.build();
|
|
841
|
+
}
|
|
842
|
+
|
|
843
|
+
// src/parser.ts
|
|
844
|
+
var prismaParser = (0, import_config.defineParser)({
|
|
845
|
+
name: "prisma",
|
|
846
|
+
optionsSchema: PrismaParserOptions,
|
|
847
|
+
async parse(ctx, options) {
|
|
848
|
+
const input = await resolveInput(ctx.cwd, options, ctx.namespace);
|
|
849
|
+
if (input.mode === 8) {
|
|
850
|
+
throw new PrismaInputError(
|
|
851
|
+
ctx.namespace,
|
|
852
|
+
input.contractPath,
|
|
853
|
+
"the Prisma 8 contract.json mode is not implemented in kurotako v1"
|
|
854
|
+
);
|
|
855
|
+
}
|
|
856
|
+
const { model, prismaVersion } = await readDmmf(input, ctx);
|
|
857
|
+
return buildSourceIR(
|
|
858
|
+
ctx.namespace,
|
|
859
|
+
model,
|
|
860
|
+
`prisma@${prismaVersion}`,
|
|
861
|
+
ctx.logger
|
|
862
|
+
);
|
|
863
|
+
},
|
|
864
|
+
async watchPaths(ctx, options) {
|
|
865
|
+
return [(0, import_node_path3.resolve)(ctx.cwd, options.schema)];
|
|
866
|
+
},
|
|
867
|
+
anchor(rootDir, options) {
|
|
868
|
+
return (0, import_node_path3.dirname)((0, import_node_path3.resolve)(rootDir, options.schema));
|
|
869
|
+
}
|
|
870
|
+
});
|
|
871
|
+
// Annotate the CommonJS export names for ESM import in node:
|
|
872
|
+
0 && (module.exports = {
|
|
873
|
+
PrismaInputError,
|
|
874
|
+
PrismaParserOptions,
|
|
875
|
+
PrismaPeerMissingError,
|
|
876
|
+
PrismaSchemaError,
|
|
877
|
+
prismaParser
|
|
878
|
+
});
|
|
879
|
+
//# sourceMappingURL=index.cjs.map
|