hekireki 0.7.0 → 0.7.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -2,18 +2,31 @@
2
2
 
3
3
  # Hekireki
4
4
 
5
- **[Hekireki](https://www.npmjs.com/package/hekireki)** is a tool that generates validation schemas for Zod, Valibot, ArkType, and Effect Schema, as well as [Drizzle ORM](https://orm.drizzle.team/) schemas and ER diagrams, from [Prisma](https://www.prisma.io/) schemas annotated with comments.
5
+ **[Hekireki](https://www.npmjs.com/package/hekireki)** is a tool that generates validation schemas, ORM models, and ER diagrams from [Prisma](https://www.prisma.io/) schemas supporting TypeScript, Python, Go, Rust, and Elixir.
6
6
 
7
7
  ## Features
8
8
 
9
+ ### TypeScript Validation Libraries
10
+
9
11
  - 💎 Automatically generates [Zod](https://zod.dev/) schemas from your Prisma schema
10
12
  - 🤖 Automatically generates [Valibot](https://valibot.dev/) schemas from your Prisma schema
11
13
  - 🏹 Automatically generates [ArkType](https://arktype.io/) schemas from your Prisma schema
12
14
  - ⚡ Automatically generates [Effect Schema](https://effect.website/docs/schema/introduction/) from your Prisma schema
15
+ - 📦 Automatically generates [TypeBox](https://github.com/sinclairzx81/typebox) schemas from your Prisma schema
16
+ - 📋 Automatically generates [AJV](https://ajv.js.org/)-compatible JSON Schema objects from your Prisma schema
17
+
18
+ ### ORM / Schema Generation (Multi-Language)
19
+
13
20
  - 🗄️ Automatically generates [Drizzle ORM](https://orm.drizzle.team/) table schemas and relations from your Prisma schema
21
+ - 🐍 Automatically generates [SQLAlchemy](https://www.sqlalchemy.org/) models (Python) — with `Mapped[T]` type hints, relationships, enums, composite keys, and index support
22
+ - 🐹 Automatically generates [GORM](https://gorm.io/) models (Go) — with struct tags, JSON tags, relationships, enums, composite keys, and index support
23
+ - 🦀 Automatically generates [Sea-ORM](https://www.sea-ql.org/SeaORM/) entities (Rust) — with `DeriveEntityModel`, relations, enums, serde support, and `rename_all`
24
+ - 🧪 Generates [Ecto](https://hexdocs.pm/ecto/Ecto.Schema.html) schemas (Elixir) — with associations (`belongs_to`, `has_many`, `has_one`), composite primary keys, `@type t` typespecs, array fields, `@@map`/`@map` support, and `@moduledoc`
25
+
26
+ ### Diagrams & Documentation
27
+
14
28
  - 📊 Creates [Mermaid](https://mermaid.js.org/) ER diagrams with PK/FK markers
15
29
  - 📝 Generates [DBML](https://dbml.dbdiagram.io/) (Database Markup Language) files and **PNG** ER diagrams via [dbml-renderer](https://github.com/softwaretechnik-berlin/dbml-renderer) — output format is determined by the file extension (`.dbml` or `.png`)
16
- - 🧪 Generates [Ecto](https://hexdocs.pm/ecto/Ecto.Schema.html) schemas for Elixir projects — with associations (`belongs_to`, `has_many`, `has_one`), composite primary keys, `@type t` typespecs, array fields, `@@map`/`@map` support, and `@moduledoc`
17
30
 
18
31
  ## Installation
19
32
 
@@ -62,10 +75,41 @@ generator Hekireki-Effect {
62
75
  relation = true
63
76
  }
64
77
 
78
+ generator Hekireki-TypeBox {
79
+ provider = "hekireki-typebox"
80
+ type = true
81
+ comment = true
82
+ relation = true
83
+ }
84
+
85
+ generator Hekireki-AJV {
86
+ provider = "hekireki-ajv"
87
+ type = true
88
+ comment = true
89
+ relation = true
90
+ }
91
+
65
92
  generator Hekireki-Drizzle {
66
93
  provider = "hekireki-drizzle"
67
94
  }
68
95
 
96
+ generator Hekireki-SQLAlchemy {
97
+ provider = "hekireki-sqlalchemy"
98
+ output = "./sqlalchemy"
99
+ }
100
+
101
+ generator Hekireki-GORM {
102
+ provider = "hekireki-gorm"
103
+ output = "./gorm"
104
+ package = "model"
105
+ }
106
+
107
+ generator Hekireki-SeaORM {
108
+ provider = "hekireki-sea-orm"
109
+ output = "./sea_orm"
110
+ renameAll = "camelCase"
111
+ }
112
+
69
113
  generator Hekireki-Ecto {
70
114
  provider = "hekireki-ecto"
71
115
  output = "./ecto"
@@ -88,12 +132,16 @@ model User {
88
132
  /// @v.pipe(v.string(), v.uuid())
89
133
  /// @a."string.uuid"
90
134
  /// @e.Schema.UUID
135
+ /// @t.Type.String({ format: 'uuid' })
136
+ /// @j.{ type: 'string' as const, format: 'uuid' as const }
91
137
  id String @id @default(uuid())
92
138
  /// Display name
93
139
  /// @z.string().min(1).max(50)
94
140
  /// @v.pipe(v.string(), v.minLength(1), v.maxLength(50))
95
141
  /// @a."1 <= string <= 50"
96
142
  /// @e.Schema.String.pipe(Schema.minLength(1), Schema.maxLength(50))
143
+ /// @t.Type.String({ minLength: 1, maxLength: 50 })
144
+ /// @j.{ type: 'string' as const, minLength: 1, maxLength: 50 }
97
145
  name String
98
146
  /// One-to-many relation to Post
99
147
  posts Post[]
@@ -105,24 +153,32 @@ model Post {
105
153
  /// @v.pipe(v.string(), v.uuid())
106
154
  /// @a."string.uuid"
107
155
  /// @e.Schema.UUID
156
+ /// @t.Type.String({ format: 'uuid' })
157
+ /// @j.{ type: 'string' as const, format: 'uuid' as const }
108
158
  id String @id @default(uuid())
109
159
  /// Article title
110
160
  /// @z.string().min(1).max(100)
111
161
  /// @v.pipe(v.string(), v.minLength(1), v.maxLength(100))
112
162
  /// @a."1 <= string <= 100"
113
163
  /// @e.Schema.String.pipe(Schema.minLength(1), Schema.maxLength(100))
164
+ /// @t.Type.String({ minLength: 1, maxLength: 100 })
165
+ /// @j.{ type: 'string' as const, minLength: 1, maxLength: 100 }
114
166
  title String
115
167
  /// Body content (no length limit)
116
168
  /// @z.string()
117
169
  /// @v.string()
118
170
  /// @a."string"
119
171
  /// @e.Schema.String
172
+ /// @t.Type.String()
173
+ /// @j.{ type: 'string' as const }
120
174
  content String
121
175
  /// Foreign key referencing User.id
122
176
  /// @z.uuid()
123
177
  /// @v.pipe(v.string(), v.uuid())
124
178
  /// @a."string.uuid"
125
179
  /// @e.Schema.UUID
180
+ /// @t.Type.String({ format: 'uuid' })
181
+ /// @j.{ type: 'string' as const, format: 'uuid' as const }
126
182
  userId String
127
183
  /// Prisma relation definition
128
184
  user User @relation(fields: [userId], references: [id])
@@ -295,6 +351,108 @@ export const PostSchema = Schema.Struct({
295
351
  export type Post = Schema.Schema.Type<typeof PostSchema>
296
352
  ```
297
353
 
354
+ ### TypeBox
355
+
356
+ ```ts
357
+ import { type Static, Type } from '@sinclair/typebox'
358
+
359
+ export const UserSchema = Type.Object({
360
+ /** Primary key */
361
+ id: Type.String({ format: 'uuid' }),
362
+ /** Display name */
363
+ name: Type.String({ minLength: 1, maxLength: 50 }),
364
+ })
365
+
366
+ export type User = Static<typeof UserSchema>
367
+
368
+ export const PostSchema = Type.Object({
369
+ /** Primary key */
370
+ id: Type.String({ format: 'uuid' }),
371
+ /** Article title */
372
+ title: Type.String({ minLength: 1, maxLength: 100 }),
373
+ /** Body content (no length limit) */
374
+ content: Type.String(),
375
+ /** Foreign key referencing User.id */
376
+ userId: Type.String({ format: 'uuid' }),
377
+ })
378
+
379
+ export type Post = Static<typeof PostSchema>
380
+
381
+ export const UserRelationsSchema = Type.Object({
382
+ ...UserSchema.properties,
383
+ posts: Type.Array(PostSchema),
384
+ })
385
+
386
+ export type UserRelations = Static<typeof UserRelationsSchema>
387
+
388
+ export const PostRelationsSchema = Type.Object({
389
+ ...PostSchema.properties,
390
+ user: UserSchema,
391
+ })
392
+
393
+ export type PostRelations = Static<typeof PostRelationsSchema>
394
+ ```
395
+
396
+ ### AJV (JSON Schema)
397
+
398
+ ```ts
399
+ import type { FromSchema } from 'json-schema-to-ts'
400
+
401
+ export const UserSchema = {
402
+ type: 'object' as const,
403
+ properties: {
404
+ /** Primary key */
405
+ id: { type: 'string' as const, format: 'uuid' as const },
406
+ /** Display name */
407
+ name: { type: 'string' as const, minLength: 1, maxLength: 50 },
408
+ },
409
+ required: ['id', 'name'] as const,
410
+ additionalProperties: false,
411
+ } as const
412
+
413
+ export type User = FromSchema<typeof UserSchema>
414
+
415
+ export const PostSchema = {
416
+ type: 'object' as const,
417
+ properties: {
418
+ /** Primary key */
419
+ id: { type: 'string' as const, format: 'uuid' as const },
420
+ /** Article title */
421
+ title: { type: 'string' as const, minLength: 1, maxLength: 100 },
422
+ /** Body content (no length limit) */
423
+ content: { type: 'string' as const },
424
+ /** Foreign key referencing User.id */
425
+ userId: { type: 'string' as const, format: 'uuid' as const },
426
+ },
427
+ required: ['id', 'title', 'content', 'userId'] as const,
428
+ additionalProperties: false,
429
+ } as const
430
+
431
+ export type Post = FromSchema<typeof PostSchema>
432
+
433
+ export const UserRelationsSchema = {
434
+ type: 'object' as const,
435
+ properties: {
436
+ ...UserSchema.properties,
437
+ posts: { type: 'array' as const, items: PostSchema },
438
+ },
439
+ additionalProperties: false,
440
+ } as const
441
+
442
+ export type UserRelations = FromSchema<typeof UserRelationsSchema>
443
+
444
+ export const PostRelationsSchema = {
445
+ type: 'object' as const,
446
+ properties: {
447
+ ...PostSchema.properties,
448
+ user: UserSchema,
449
+ },
450
+ additionalProperties: false,
451
+ } as const
452
+
453
+ export type PostRelations = FromSchema<typeof PostRelationsSchema>
454
+ ```
455
+
298
456
  ### Drizzle
299
457
 
300
458
  ```ts
@@ -390,18 +548,90 @@ defmodule DBSchema.Post do
390
548
  end
391
549
  ```
392
550
 
393
- **Supported features:**
551
+ ### SQLAlchemy
394
552
 
395
- | Feature | Details |
396
- |---|---|
397
- | Primary keys | UUID (`@default(uuid())`), autoincrement (`@default(autoincrement())`), CUID, composite (`@@id`) |
398
- | Associations | `belongs_to`, `has_many`, `has_one` with correct FK types |
399
- | Timestamps | `timestamps()` with `inserted_at_source`/`updated_at_source` for `@map`/camelCase fields |
400
- | Enums | `Ecto.Enum` with `values: [...]` |
401
- | Array fields | `{:array, :type}` for Prisma list scalars (`String[]`, `Int[]`, etc.) |
402
- | Name mapping | `@@map` → schema table name, `@map` → field `source:` option |
403
- | Typespecs | `@type t :: %__MODULE__{...}` auto-generated for Dialyzer |
404
- | `@moduledoc` | Uses `///` model documentation, or `false` when absent |
553
+ ```python
554
+ from sqlalchemy import ForeignKey
555
+ from sqlalchemy.orm import DeclarativeBase, Mapped, mapped_column, relationship
556
+
557
+
558
+ class Base(DeclarativeBase):
559
+ pass
560
+
561
+
562
+ class User(Base):
563
+ __tablename__ = "user"
564
+
565
+ id: Mapped[str] = mapped_column(primary_key=True)
566
+ name: Mapped[str]
567
+
568
+ posts: Mapped[list["Post"]] = relationship(back_populates="user")
569
+
570
+
571
+ class Post(Base):
572
+ __tablename__ = "post"
573
+
574
+ id: Mapped[str] = mapped_column(primary_key=True)
575
+ title: Mapped[str]
576
+ content: Mapped[str]
577
+ user_id: Mapped[str] = mapped_column(ForeignKey("user.id"))
578
+
579
+ user: Mapped["User"] = relationship(back_populates="posts")
580
+ ```
581
+
582
+ ### GORM
583
+
584
+ ```go
585
+ package model
586
+
587
+ type User struct {
588
+ ID string `gorm:"column:id;primaryKey;type:char(36)" json:"id"`
589
+ Name string `gorm:"column:name;not null" json:"name"`
590
+ Posts []Post `gorm:"foreignKey:UserID"`
591
+ }
592
+
593
+ type Post struct {
594
+ ID string `gorm:"column:id;primaryKey;type:char(36)" json:"id"`
595
+ Title string `gorm:"column:title;not null" json:"title"`
596
+ Content string `gorm:"column:content;not null" json:"content"`
597
+ UserID string `gorm:"column:user_id;not null" json:"user_id"`
598
+ User User
599
+ }
600
+ ```
601
+
602
+ ### Sea-ORM
603
+
604
+ Each model is output as a separate `.rs` file with `mod.rs` and `prelude.rs`, following Sea-ORM conventions.
605
+
606
+ **user.rs:**
607
+
608
+ ```rust
609
+ use sea_orm::entity::prelude::*;
610
+ use serde::{Deserialize, Serialize};
611
+
612
+ #[derive(Clone, Debug, PartialEq, DeriveEntityModel, Serialize, Deserialize)]
613
+ #[serde(rename_all = "camelCase")]
614
+ #[sea_orm(table_name = "user")]
615
+ pub struct Model {
616
+ #[sea_orm(primary_key, auto_increment = false)]
617
+ pub id: String,
618
+ pub name: String,
619
+ }
620
+
621
+ #[derive(Copy, Clone, Debug, EnumIter, DeriveRelation)]
622
+ pub enum Relation {
623
+ #[sea_orm(has_many = "super::post::Entity")]
624
+ Posts,
625
+ }
626
+
627
+ impl Related<super::post::Entity> for Entity {
628
+ fn to() -> RelationDef {
629
+ Relation::Posts.def()
630
+ }
631
+ }
632
+
633
+ impl ActiveModelBehavior for ActiveModel {}
634
+ ```
405
635
 
406
636
  ### DBML
407
637
 
@@ -487,6 +717,24 @@ generator Hekireki-Effect {
487
717
  relation = true // Generate relation schemas (default: false)
488
718
  }
489
719
 
720
+ // TypeBox Generator
721
+ generator Hekireki-TypeBox {
722
+ provider = "hekireki-typebox"
723
+ output = "./typebox" // Output path (default: ./typebox/index.ts)
724
+ type = true // Generate TypeScript types (default: false)
725
+ comment = true // Include schema documentation (default: false)
726
+ relation = true // Generate relation schemas (default: false)
727
+ }
728
+
729
+ // AJV (JSON Schema) Generator
730
+ generator Hekireki-AJV {
731
+ provider = "hekireki-ajv"
732
+ output = "./ajv" // Output path (default: ./ajv/index.ts)
733
+ type = true // Generate TypeScript types (default: false)
734
+ comment = true // Include schema documentation (default: false)
735
+ relation = true // Generate relation schemas (default: false)
736
+ }
737
+
490
738
  // Drizzle ORM Schema Generator
491
739
  generator Hekireki-Drizzle {
492
740
  provider = "hekireki-drizzle"
@@ -499,7 +747,27 @@ generator Hekireki-ER {
499
747
  output = "./mermaid-er" // Output path (default: ./mermaid-er/ER.md)
500
748
  }
501
749
 
502
- // Ecto Generator
750
+ // SQLAlchemy Generator (Python)
751
+ generator Hekireki-SQLAlchemy {
752
+ provider = "hekireki-sqlalchemy"
753
+ output = "./sqlalchemy" // Output path (default: ./sqlalchemy/models.py)
754
+ }
755
+
756
+ // GORM Generator (Go)
757
+ generator Hekireki-GORM {
758
+ provider = "hekireki-gorm"
759
+ output = "./gorm" // Output path (default: ./gorm/models.go)
760
+ package = "model" // Go package name (default: model)
761
+ }
762
+
763
+ // Sea-ORM Generator (Rust)
764
+ generator Hekireki-SeaORM {
765
+ provider = "hekireki-sea-orm"
766
+ output = "./sea_orm" // Output directory for .rs files
767
+ renameAll = "camelCase" // #[serde(rename_all = "...")] attribute (optional)
768
+ }
769
+
770
+ // Ecto Generator (Elixir)
503
771
  generator Hekireki-Ecto {
504
772
  provider = "hekireki-ecto"
505
773
  output = "./ecto" // Output directory (default: ./ecto/)
@@ -0,0 +1,6 @@
1
+ import { GeneratorOptions } from "@prisma/generator-helper";
2
+
3
+ //#region src/generator/ajv/index.d.ts
4
+ declare function main(options: GeneratorOptions): Promise<void>;
5
+ //#endregion
6
+ export { main };
@@ -0,0 +1,87 @@
1
+ #!/usr/bin/env node
2
+ import { t as fmt } from "../../format-CzXgkLDe.js";
3
+ import { c as parseDocumentWithoutAnnotations, d as mkdir, f as writeFile, s as makeValidationExtractor, t as getBool } from "../../utils-COHZyQue.js";
4
+ import { n as validationSchemas, t as makeRelationsOnly } from "../../prisma-ChsFqlYX.js";
5
+ import path from "node:path";
6
+ import pkg from "@prisma/generator-helper";
7
+
8
+ //#region src/helper/ajv.ts
9
+ function makeAjvInfer(modelName) {
10
+ return `export type ${modelName} = FromSchema<typeof ${modelName}Schema>`;
11
+ }
12
+ function makeAjvEnumExpression(values) {
13
+ return `{ enum: [${values.map((v) => `'${v}'`).join(", ")}] as const }`;
14
+ }
15
+ const PRISMA_TO_AJV = {
16
+ String: "{ type: 'string' as const }",
17
+ Int: "{ type: 'integer' as const }",
18
+ Float: "{ type: 'number' as const }",
19
+ Boolean: "{ type: 'boolean' as const }",
20
+ DateTime: "{ type: 'string' as const, format: 'date-time' as const }",
21
+ BigInt: "{ type: 'integer' as const }",
22
+ Decimal: "{ type: 'number' as const }",
23
+ Json: "{}",
24
+ Bytes: "{ type: 'string' as const }"
25
+ };
26
+ function makeAjvSchemas(modelFields, comment) {
27
+ const modelName = modelFields[0].modelName;
28
+ const properties = modelFields.map((field) => {
29
+ return `${comment && field.comment.length > 0 ? `${field.comment.map((c) => ` /** ${c} */`).join("\n")}\n` : ""} ${field.fieldName}: ${field.validation ?? "{ type: 'unknown' as const }"},`;
30
+ }).join("\n");
31
+ const requiredFields = modelFields.filter((f) => f.isRequired).map((f) => f.fieldName);
32
+ return `export const ${modelName}Schema = {\n type: 'object' as const,\n properties: {\n${properties}\n },${requiredFields.length > 0 ? `\n required: [${requiredFields.map((f) => `'${f}'`).join(", ")}] as const,` : ""}\n additionalProperties: false,\n} as const`;
33
+ }
34
+ function makeAjvRelations(model, relProps, options) {
35
+ if (relProps.length === 0) return null;
36
+ const base = ` ...${model.name}Schema.properties,`;
37
+ const rels = relProps.map((r) => ` ${r.key}: ${r.isMany ? `{ type: 'array' as const, items: ${r.targetModel}Schema }` : `${r.targetModel}Schema`},`).join("\n");
38
+ const typeLine = options?.includeType ? `\n\nexport type ${model.name}Relations = FromSchema<typeof ${model.name}RelationsSchema>` : "";
39
+ return `export const ${model.name}RelationsSchema = {\n type: 'object' as const,\n properties: {\n${base}\n${rels}\n },\n additionalProperties: false,\n} as const${typeLine}`;
40
+ }
41
+ function ajv(models, type, comment, enums) {
42
+ return validationSchemas(models, type, comment, {
43
+ importStatement: type ? `import type { FromSchema } from 'json-schema-to-ts'` : "",
44
+ annotationPrefix: "@j.",
45
+ parseDocument: parseDocumentWithoutAnnotations,
46
+ extractValidation: makeValidationExtractor("@j."),
47
+ inferType: makeAjvInfer,
48
+ schemas: makeAjvSchemas,
49
+ typeMapping: PRISMA_TO_AJV,
50
+ enums,
51
+ formatEnum: makeAjvEnumExpression
52
+ });
53
+ }
54
+
55
+ //#endregion
56
+ //#region src/generator/ajv/index.ts
57
+ const { generatorHandler } = pkg;
58
+ async function main(options) {
59
+ if (!(options.generator.isCustomOutput && options.generator.output?.value)) throw new Error("output is required for Hekireki-AJV. Please specify output in your generator config.");
60
+ const output = options.generator.output.value;
61
+ const resolved = path.extname(output) ? {
62
+ dir: path.dirname(output),
63
+ file: output
64
+ } : {
65
+ dir: output,
66
+ file: path.join(output, "index.ts")
67
+ };
68
+ const enableRelation = getBool(options.generator.config?.relation);
69
+ const fmtResult = await fmt([ajv(options.dmmf.datamodel.models, getBool(options.generator.config?.type), getBool(options.generator.config?.comment), options.dmmf.datamodel.enums), enableRelation ? makeRelationsOnly(options.dmmf, getBool(options.generator.config?.type), makeAjvRelations) : ""].filter(Boolean).join("\n\n"));
70
+ if (!fmtResult.ok) throw new Error(`Format error: ${fmtResult.error}`);
71
+ const mkdirResult = await mkdir(resolved.dir);
72
+ if (!mkdirResult.ok) throw new Error(`Failed to create directory: ${mkdirResult.error}`);
73
+ const writeResult = await writeFile(resolved.file, fmtResult.value);
74
+ if (!writeResult.ok) throw new Error(`Failed to write file: ${writeResult.error}`);
75
+ }
76
+ generatorHandler({
77
+ onManifest() {
78
+ return {
79
+ defaultOutput: ".",
80
+ prettyName: "Hekireki-AJV"
81
+ };
82
+ },
83
+ onGenerate: main
84
+ });
85
+
86
+ //#endregion
87
+ export { main };
@@ -1,11 +1,25 @@
1
1
  #!/usr/bin/env node
2
2
  import { t as fmt } from "../../format-CzXgkLDe.js";
3
- import { N as mkdir, O as parseDocumentWithoutAnnotations, P as writeFile, a as getBool, d as makeArktypeProperties, f as makeArktypeSchema, j as schemaFromFields, l as makeArktypeEnumExpression, u as makeArktypeInfer, w as makeValidationExtractor } from "../../utils-DeZn2r_T.js";
4
- import { n as validationSchemas, t as makeRelationsOnly } from "../../prisma-Cc0YxSiO.js";
3
+ import { c as parseDocumentWithoutAnnotations, d as mkdir, f as writeFile, l as schemaFromFields, s as makeValidationExtractor, t as getBool } from "../../utils-COHZyQue.js";
4
+ import { n as validationSchemas, t as makeRelationsOnly } from "../../prisma-ChsFqlYX.js";
5
5
  import path from "node:path";
6
6
  import pkg from "@prisma/generator-helper";
7
7
 
8
8
  //#region src/helper/arktype.ts
9
+ function makeArktypeInfer(modelName) {
10
+ return `export type ${modelName} = typeof ${modelName}Schema.infer`;
11
+ }
12
+ function makeArktypeSchema(modelName, fields) {
13
+ return `export const ${modelName}Schema = type({\n${fields}\n})`;
14
+ }
15
+ function makeArktypeProperties(fields, comment) {
16
+ return fields.map((field) => {
17
+ return `${comment && field.comment.length > 0 ? `${field.comment.map((c) => ` /** ${c} */`).join("\n")}\n` : ""} ${field.fieldName}: ${field.validation ?? "\"unknown\""},`;
18
+ }).join("\n");
19
+ }
20
+ function makeArktypeEnumExpression(values) {
21
+ return `"${values.map((v) => `'${v}'`).join(" | ")}"`;
22
+ }
9
23
  const PRISMA_TO_ARKTYPE = {
10
24
  String: "\"string\"",
11
25
  Int: "\"number\"",
@@ -53,7 +67,7 @@ async function main(options) {
53
67
  dir: output,
54
68
  file: path.join(output, "index.ts")
55
69
  };
56
- const enableRelation = options.generator.config?.relation === "true" || Array.isArray(options.generator.config?.relation) && options.generator.config?.relation[0] === "true";
70
+ const enableRelation = getBool(options.generator.config?.relation);
57
71
  const fmtResult = await fmt([arktype(options.dmmf.datamodel.models, getBool(options.generator.config?.type), getBool(options.generator.config?.comment), options.dmmf.datamodel.enums), enableRelation ? makeRelationsOnly(options.dmmf, getBool(options.generator.config?.type), makeArktypeRelations) : ""].filter(Boolean).join("\n\n"));
58
72
  if (!fmtResult.ok) throw new Error(`Format error: ${fmtResult.error}`);
59
73
  const mkdirResult = await mkdir(resolved.dir);
@@ -1,11 +1,30 @@
1
1
  #!/usr/bin/env node
2
- import { F as writeFileBinary, M as stripAnnotations, N as mkdir, P as writeFile, _ as makeEnum, i as formatConstraints, o as getString, r as escapeNote, t as combineKeys, y as makeRefName } from "../../utils-DeZn2r_T.js";
2
+ import { d as mkdir, f as writeFile, n as getString, p as writeFileBinary, u as stripAnnotations } from "../../utils-COHZyQue.js";
3
3
  import path from "node:path";
4
4
  import pkg from "@prisma/generator-helper";
5
5
  import { Resvg } from "@resvg/resvg-js";
6
6
  import { run } from "@softwaretechnik/dbml-renderer";
7
7
 
8
8
  //#region src/helper/dbml.ts
9
+ function escapeNote(str) {
10
+ return str.replace(/'/g, "\\'");
11
+ }
12
+ function formatConstraints(constraints) {
13
+ return constraints.length > 0 ? ` [${constraints.join(", ")}]` : "";
14
+ }
15
+ function makeEnum(enumDef) {
16
+ return [
17
+ `Enum ${enumDef.name} {`,
18
+ ...enumDef.values.map((v) => ` ${v}`),
19
+ "}"
20
+ ].join("\n");
21
+ }
22
+ function makeRefName(ref) {
23
+ return ref.name ?? `${ref.fromTable}_${ref.fromColumn}_${ref.toTable}_${ref.toColumn}_fk`;
24
+ }
25
+ function combineKeys(keys) {
26
+ return keys.length > 1 ? `(${keys.join(", ")})` : keys[0];
27
+ }
9
28
  function quote(value) {
10
29
  return `'${escapeNote(value)}'`;
11
30
  }
@@ -34,43 +53,37 @@ function makePrismaColumn(column) {
34
53
  ].filter((c) => Boolean(c));
35
54
  return ` ${column.name} ${column.type}${formatConstraints(constraints)}`;
36
55
  }
37
- function resolveFieldType(field, models, mapToDbSchema) {
38
- const baseType = mapToDbSchema ? models.find((m) => m.name === field.type)?.dbName ?? field.type : field.type;
39
- return field.isList && !field.relationName ? `${baseType}[]` : baseType;
40
- }
41
- function resolveDefaultValue(field) {
42
- const defaultDef = field.default;
43
- if (defaultDef?.name === "autoincrement") return void 0;
44
- if (defaultDef?.name === "now") return "`now()`";
45
- if (field.hasDefaultValue && typeof field.default !== "object") return field.type === "String" || field.type === "Json" || field.kind === "enum" ? `'${field.default}'` : String(field.default);
46
- }
47
56
  function toDBMLColumn(field, models, mapToDbSchema) {
48
57
  const defaultDef = field.default;
58
+ const baseType = mapToDbSchema ? models.find((m) => m.name === field.type)?.dbName ?? field.type : field.type;
59
+ const type = field.isList && !field.relationName ? `${baseType}[]` : baseType;
60
+ const defaultValue = (() => {
61
+ if (defaultDef?.name === "autoincrement") return void 0;
62
+ if (defaultDef?.name === "now") return "`now()`";
63
+ if (field.hasDefaultValue && typeof field.default !== "object") return field.type === "String" || field.type === "Json" || field.kind === "enum" ? `'${field.default}'` : String(field.default);
64
+ })();
49
65
  return {
50
66
  name: field.name,
51
- type: resolveFieldType(field, models, mapToDbSchema),
67
+ type,
52
68
  isPrimaryKey: field.isId,
53
69
  isIncrement: defaultDef?.name === "autoincrement",
54
70
  isUnique: field.isUnique,
55
71
  isNotNull: field.isRequired && !field.isId,
56
- defaultValue: resolveDefaultValue(field),
72
+ defaultValue,
57
73
  note: stripAnnotations(field.documentation)
58
74
  };
59
75
  }
60
- function makeTableIndexes(model) {
61
- return [...model.primaryKey?.fields && model.primaryKey.fields.length > 0 ? [{
62
- columns: model.primaryKey.fields,
63
- isPrimaryKey: true
64
- }] : [], ...model.uniqueFields.filter((c) => c.length > 1).map((c) => ({
65
- columns: c,
66
- isUnique: true
67
- }))];
68
- }
69
76
  function makeTables(models, mapToDbSchema = false) {
70
77
  return models.map((model) => {
71
78
  const modelName = mapToDbSchema && model.dbName ? model.dbName : model.name;
72
79
  const columnLines = model.fields.map((field) => toDBMLColumn(field, models, mapToDbSchema)).map(makePrismaColumn).join("\n");
73
- const indexes = makeTableIndexes(model);
80
+ const indexes = [...model.primaryKey?.fields && model.primaryKey.fields.length > 0 ? [{
81
+ columns: model.primaryKey.fields,
82
+ isPrimaryKey: true
83
+ }] : [], ...model.uniqueFields.filter((c) => c.length > 1).map((c) => ({
84
+ columns: c,
85
+ isUnique: true
86
+ }))];
74
87
  const indexBlock = indexes.length > 0 ? `\n\n indexes {\n${indexes.map(makeIndex).join("\n")}\n }` : "";
75
88
  const strippedNote = stripAnnotations(model.documentation);
76
89
  return `Table ${modelName} {\n${columnLines}${indexBlock}${strippedNote ? `\n\n Note: ${quote(escapeNote(strippedNote))}` : ""}\n}`;
@@ -84,14 +97,11 @@ function makeEnums(enums) {
84
97
  });
85
98
  });
86
99
  }
87
- function getRelationOperator(models, from, to) {
88
- return (models.find((m) => m.name === to)?.fields.find((f) => f.type === from))?.isList ? ">" : "-";
89
- }
90
100
  function makeRelations(models, mapToDbSchema = false) {
91
101
  return models.flatMap((model) => model.fields.filter((field) => field.relationName && field.relationToFields?.length && field.relationFromFields?.length).map((field) => {
92
102
  const relationFrom = model.name;
93
103
  const relationTo = field.type;
94
- const operator = getRelationOperator(models, relationFrom, relationTo);
104
+ const operator = (models.find((m) => m.name === relationTo)?.fields.find((f) => f.type === relationFrom))?.isList ? ">" : "-";
95
105
  const relationFromName = mapToDbSchema && model.dbName ? model.dbName : model.name;
96
106
  const relatedModel = models.find((m) => m.name === relationTo);
97
107
  const relationToName = mapToDbSchema && relatedModel?.dbName ? relatedModel.dbName : relationTo;
@@ -118,25 +128,25 @@ function dbmlContent(datamodel, mapToDbSchema = false) {
118
128
  ...refs
119
129
  ].join("\n\n");
120
130
  }
121
- const makeDbmlFile = async (outputDir, content, fileName) => {
131
+ async function makeDbmlFile(outputDir, content, fileName) {
122
132
  const writeResult = await writeFile(`${outputDir}/${fileName}`, content);
123
133
  if (!writeResult.ok) return {
124
134
  ok: false,
125
135
  error: `Failed to write DBML: ${writeResult.error}`
126
136
  };
127
137
  return { ok: true };
128
- };
129
- const makePng = async (outputDir, dbml, fileName) => {
138
+ }
139
+ async function makePng(outputDir, dbml, fileName) {
130
140
  return makePngFile(`${outputDir}/${fileName}`, dbml);
131
- };
132
- const makePngFile = async (outputPath, dbml) => {
141
+ }
142
+ async function makePngFile(outputPath, dbml) {
133
143
  const writeResult = await writeFileBinary(outputPath, new Resvg(run(dbml, "svg"), { font: { loadSystemFonts: true } }).render().asPng());
134
144
  if (!writeResult.ok) return {
135
145
  ok: false,
136
146
  error: `Failed to write PNG: ${writeResult.error}`
137
147
  };
138
148
  return { ok: true };
139
- };
149
+ }
140
150
 
141
151
  //#endregion
142
152
  //#region src/generator/dbml/index.ts