hekireki 0.2.6 → 0.2.8

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.
Files changed (84) hide show
  1. package/README.md +20 -6
  2. package/dist/generator/ecto/generator/ecto.js +61 -25
  3. package/dist/generator/mermaid-er/generator/er-content.d.ts +8 -2
  4. package/dist/generator/mermaid-er/generator/er-content.js +8 -6
  5. package/dist/generator/mermaid-er/generator/index.d.ts +1 -1
  6. package/dist/generator/mermaid-er/generator/index.js +1 -1
  7. package/dist/generator/mermaid-er/generator/model-fields.d.ts +4 -3
  8. package/dist/generator/mermaid-er/generator/model-fields.js +4 -3
  9. package/dist/generator/mermaid-er/generator/model-info.d.ts +6 -5
  10. package/dist/generator/mermaid-er/generator/model-info.js +4 -3
  11. package/dist/generator/mermaid-er/generator/relation-line.d.ts +11 -5
  12. package/dist/generator/mermaid-er/generator/relation-line.js +5 -4
  13. package/dist/generator/mermaid-er/helper/build-relation-line.d.ts +9 -0
  14. package/dist/generator/mermaid-er/{relationship → helper}/build-relation-line.js +9 -1
  15. package/dist/generator/mermaid-er/helper/extract-relations.d.ts +8 -0
  16. package/dist/generator/mermaid-er/{validator → helper}/extract-relations.js +5 -5
  17. package/dist/generator/mermaid-er/index.js +1 -1
  18. package/dist/generator/mermaid-er/utils/index.d.ts +34 -0
  19. package/dist/generator/mermaid-er/utils/index.js +48 -0
  20. package/dist/generator/valibot/generator/index.d.ts +0 -2
  21. package/dist/generator/valibot/generator/index.js +0 -2
  22. package/dist/generator/valibot/generator/schemas.js +1 -1
  23. package/dist/generator/valibot/generator/valibot.d.ts +2 -2
  24. package/dist/generator/valibot/generator/valibot.js +4 -12
  25. package/dist/generator/valibot/index.js +1 -1
  26. package/dist/generator/valibot/utils/index.d.ts +42 -0
  27. package/dist/generator/valibot/utils/index.js +61 -0
  28. package/dist/generator/zod/generator/index.d.ts +1 -3
  29. package/dist/generator/zod/generator/index.js +1 -3
  30. package/dist/generator/zod/generator/schemas.js +2 -1
  31. package/dist/generator/zod/generator/zod.d.ts +2 -2
  32. package/dist/generator/zod/generator/zod.js +8 -16
  33. package/dist/generator/zod/index.js +1 -1
  34. package/dist/generator/zod/utils/index.d.ts +42 -0
  35. package/dist/generator/zod/utils/index.js +61 -0
  36. package/dist/shared/utils/index.d.ts +52 -3
  37. package/dist/shared/utils/index.js +42 -3
  38. package/package.json +6 -6
  39. package/dist/generator/mermaid-er/relationship/build-relation-line.d.ts +0 -9
  40. package/dist/generator/mermaid-er/types.d.ts +0 -10
  41. package/dist/generator/mermaid-er/types.js +0 -1
  42. package/dist/generator/mermaid-er/validator/exclude-many-to-one-relations.d.ts +0 -7
  43. package/dist/generator/mermaid-er/validator/exclude-many-to-one-relations.js +0 -9
  44. package/dist/generator/mermaid-er/validator/extract-relations.d.ts +0 -7
  45. package/dist/generator/mermaid-er/validator/index.d.ts +0 -5
  46. package/dist/generator/mermaid-er/validator/index.js +0 -5
  47. package/dist/generator/mermaid-er/validator/is-relationship.d.ts +0 -7
  48. package/dist/generator/mermaid-er/validator/is-relationship.js +0 -8
  49. package/dist/generator/mermaid-er/validator/parse-relation.d.ts +0 -8
  50. package/dist/generator/mermaid-er/validator/parse-relation.js +0 -21
  51. package/dist/generator/mermaid-er/validator/remove-duplicate-relations.d.ts +0 -6
  52. package/dist/generator/mermaid-er/validator/remove-duplicate-relations.js +0 -8
  53. package/dist/generator/valibot/generator/infer-input.d.ts +0 -7
  54. package/dist/generator/valibot/generator/infer-input.js +0 -9
  55. package/dist/generator/valibot/generator/properties.d.ts +0 -7
  56. package/dist/generator/valibot/generator/properties.js +0 -15
  57. package/dist/generator/valibot/validator/index.d.ts +0 -2
  58. package/dist/generator/valibot/validator/index.js +0 -2
  59. package/dist/generator/valibot/validator/is-valibot-document.d.ts +0 -1
  60. package/dist/generator/valibot/validator/is-valibot-document.js +0 -10
  61. package/dist/generator/valibot/validator/is-valibot.d.ts +0 -6
  62. package/dist/generator/valibot/validator/is-valibot.js +0 -11
  63. package/dist/generator/zod/generator/infer.d.ts +0 -7
  64. package/dist/generator/zod/generator/infer.js +0 -9
  65. package/dist/generator/zod/generator/properties.d.ts +0 -13
  66. package/dist/generator/zod/generator/properties.js +0 -21
  67. package/dist/generator/zod/validator/index.d.ts +0 -2
  68. package/dist/generator/zod/validator/index.js +0 -2
  69. package/dist/generator/zod/validator/is-zod-document.d.ts +0 -6
  70. package/dist/generator/zod/validator/is-zod-document.js +0 -15
  71. package/dist/generator/zod/validator/is-zod.d.ts +0 -6
  72. package/dist/generator/zod/validator/is-zod.js +0 -11
  73. package/dist/shared/helper/group-by-model.d.ts +0 -8
  74. package/dist/shared/helper/group-by-model.js +0 -15
  75. package/dist/shared/types.d.ts +0 -16
  76. package/dist/shared/types.js +0 -1
  77. package/dist/shared/utils/capitalize.d.ts +0 -16
  78. package/dist/shared/utils/capitalize.js +0 -18
  79. package/dist/shared/utils/decapitalize.d.ts +0 -15
  80. package/dist/shared/utils/decapitalize.js +0 -17
  81. package/dist/shared/utils/snake-case.d.ts +0 -1
  82. package/dist/shared/utils/snake-case.js +0 -3
  83. package/dist/shared/validator/is-fields.d.ts +0 -12
  84. package/dist/shared/validator/is-fields.js +0 -8
package/README.md CHANGED
@@ -93,7 +93,7 @@ model Post {
93
93
  ## Zod
94
94
 
95
95
  ```ts
96
- import { z } from 'zod/v4'
96
+ import * as z from 'zod'
97
97
 
98
98
  export const UserSchema = z.object({
99
99
  /**
@@ -191,9 +191,15 @@ erDiagram
191
191
  ```elixir
192
192
  defmodule DBSchema.User do
193
193
  use Ecto.Schema
194
- @primary_key false
194
+
195
+ @primary_key {:id, :binary_id, autogenerate: true}
196
+
197
+ @type t :: %__MODULE__{
198
+ id: Ecto.UUID.t(),
199
+ name: String.t()
200
+ }
201
+
195
202
  schema "user" do
196
- field(:id, :binary_id, primary_key: true)
197
203
  field(:name, :string)
198
204
  end
199
205
  end
@@ -202,9 +208,17 @@ end
202
208
  ```elixir
203
209
  defmodule DBSchema.Post do
204
210
  use Ecto.Schema
205
- @primary_key false
211
+
212
+ @primary_key {:id, :binary_id, autogenerate: true}
213
+
214
+ @type t :: %__MODULE__{
215
+ id: Ecto.UUID.t(),
216
+ title: String.t(),
217
+ content: String.t(),
218
+ userId: String.t()
219
+ }
220
+
206
221
  schema "post" do
207
- field(:id, :binary_id, primary_key: true)
208
222
  field(:title, :string)
209
223
  field(:content, :string)
210
224
  field(:userId, :string)
@@ -222,7 +236,7 @@ end
222
236
  | `file` | `string` | `index.ts` | File Name |
223
237
  | `type` | `boolean` | `false` | Generate TypeScript types |
224
238
  | `comment` | `boolean` | `false` | Include schema documentation |
225
- | `zod` | `string` | `'v4'` | Zod import version (`'v4-mini'`, `'@hono/zod-openapi'`, or default `'v4'`) |
239
+ | `zod` | `string` | `'v4'` | Zod import version (`'mini'`, `'@hono/zod-openapi'`, or default `'v4'`) |
226
240
 
227
241
  ### Valibot Generator Options
228
242
 
@@ -20,6 +20,56 @@ function getPrimaryKeyConfig(field) {
20
20
  omitIdFieldInSchema: false,
21
21
  };
22
22
  }
23
+ function getFieldDefaultOption(field) {
24
+ const def = field.default;
25
+ if (def === undefined || def === null)
26
+ return null;
27
+ if (typeof def === 'string')
28
+ return `default: "${def}"`;
29
+ if (typeof def === 'number' || typeof def === 'boolean')
30
+ return `default: ${def}`;
31
+ return null;
32
+ }
33
+ function ectoTypeToTypespec(type) {
34
+ switch (type) {
35
+ case 'string':
36
+ return 'String.t()';
37
+ case 'integer':
38
+ return 'integer()';
39
+ case 'float':
40
+ return 'float()';
41
+ case 'boolean':
42
+ return 'boolean()';
43
+ case 'binary_id':
44
+ return 'Ecto.UUID.t()';
45
+ case 'naive_datetime':
46
+ return 'NaiveDateTime.t()';
47
+ case 'utc_datetime':
48
+ return 'DateTime.t()';
49
+ default:
50
+ return 'term()';
51
+ }
52
+ }
53
+ function buildTimestampsLine(fields) {
54
+ const insertedAliases = ['inserted_at', 'created_at', 'createdAt'];
55
+ const updatedAliases = ['updated_at', 'modified_at', 'updatedAt', 'modifiedAt'];
56
+ const inserted = fields.find((f) => insertedAliases.includes(f.name));
57
+ const updated = fields.find((f) => updatedAliases.includes(f.name));
58
+ const exclude = new Set();
59
+ if (inserted)
60
+ exclude.add(inserted.name);
61
+ if (updated)
62
+ exclude.add(updated.name);
63
+ if (!(inserted || updated))
64
+ return { line: null, exclude };
65
+ if (inserted?.name === 'inserted_at' && updated?.name === 'updated_at') {
66
+ return { line: ' timestamps()', exclude };
67
+ }
68
+ return {
69
+ line: ` timestamps(inserted_at: :${inserted?.name ?? 'inserted_at'}, updated_at: :${updated?.name ?? 'updated_at'})`,
70
+ exclude,
71
+ };
72
+ }
23
73
  export function ectoSchemas(models, app) {
24
74
  return models
25
75
  .map((model) => {
@@ -27,10 +77,12 @@ export function ectoSchemas(models, app) {
27
77
  if (!idField)
28
78
  return '';
29
79
  const pk = getPrimaryKeyConfig(idField);
30
- const fields = model.fields.filter((f) => !(f.relationName || (f.isId && pk.omitIdFieldInSchema)));
80
+ const fields = model.fields.map((f) => ({ ...f }));
81
+ const { line: timestampsLine, exclude: timestampsExclude } = buildTimestampsLine(fields);
82
+ const schemaFieldsRaw = fields.filter((f) => !(f.relationName || (f.isId && pk.omitIdFieldInSchema) || timestampsExclude.has(f.name)));
31
83
  const typeSpecFields = [
32
84
  `id: ${pk.typeSpec}`,
33
- ...fields.map((f) => `${f.name}: ${ectoTypeToTypespec(prismaTypeToEctoType(f.type))}`),
85
+ ...schemaFieldsRaw.map((f) => `${f.name}: ${ectoTypeToTypespec(prismaTypeToEctoType(f.type))}`),
34
86
  ];
35
87
  const typeSpecLines = [
36
88
  ' @type t :: %__MODULE__{',
@@ -40,9 +92,12 @@ export function ectoSchemas(models, app) {
40
92
  }),
41
93
  ' }',
42
94
  ];
43
- const schemaFields = fields.map((f) => {
44
- const type = prismaTypeToEctoType(f.type);
45
- return ` field(:${f.name}, :${type})`;
95
+ const schemaFields = schemaFieldsRaw.map((f) => {
96
+ const type = f.isId ? 'binary_id' : prismaTypeToEctoType(f.type);
97
+ const primary = f.isId && !pk.omitIdFieldInSchema ? ', primary_key: true' : '';
98
+ const defaultOpt = getFieldDefaultOption(f);
99
+ const defaultClause = defaultOpt ? `, ${defaultOpt}` : '';
100
+ return ` field(:${f.name}, :${type}${primary}${defaultClause})`;
46
101
  });
47
102
  const lines = [
48
103
  `defmodule ${app}.${model.name} do`,
@@ -54,6 +109,7 @@ export function ectoSchemas(models, app) {
54
109
  '',
55
110
  ` schema "${snakeCase(model.name)}" do`,
56
111
  ...schemaFields,
112
+ ...(timestampsLine ? [timestampsLine] : []),
57
113
  ' end',
58
114
  'end',
59
115
  ];
@@ -62,26 +118,6 @@ export function ectoSchemas(models, app) {
62
118
  .filter(Boolean)
63
119
  .join('\n\n');
64
120
  }
65
- function ectoTypeToTypespec(type) {
66
- switch (type) {
67
- case 'string':
68
- return 'String.t()';
69
- case 'integer':
70
- return 'integer()';
71
- case 'float':
72
- return 'float()';
73
- case 'boolean':
74
- return 'boolean()';
75
- case 'binary_id':
76
- return 'Ecto.UUID.t()';
77
- case 'naive_datetime':
78
- return 'NaiveDateTime.t()';
79
- case 'utc_datetime':
80
- return 'DateTime.t()';
81
- default:
82
- return 'term()';
83
- }
84
- }
85
121
  export async function writeEctoSchemasToFiles(models, app, outDir) {
86
122
  await fsp.mkdir(outDir, { recursive: true });
87
123
  for (const model of models) {
@@ -1,2 +1,8 @@
1
- import type { ERContent, Model } from '../types.js';
2
- export declare function erContent(models: readonly Model[]): ERContent;
1
+ import type { DMMF } from '@prisma/generator-helper';
2
+ /**
3
+ * Generate Mermaid ER diagram content from Prisma models.
4
+ *
5
+ * @param models - The list of Prisma DMMF models.
6
+ * @returns An array of Mermaid ER diagram lines.
7
+ */
8
+ export declare function erContent(models: readonly DMMF.Model[]): readonly string[];
@@ -1,14 +1,16 @@
1
1
  import { modelInfo } from '../generator/index.js';
2
- import { extractRelations, removeDuplicateRelations } from '../validator/index.js';
3
- /**
4
- * generate ER content
5
- * @param { readonly Model[] } models - models
6
- * @returns { ERContent } - ER content
7
- */
2
+ import { extractRelations } from '../helper/extract-relations.js';
3
+ import { removeDuplicateRelations } from '../utils/index.js';
8
4
  // ER diagram header
9
5
  const ER_HEADER = ['```mermaid', 'erDiagram'];
10
6
  // ER diagram footer
11
7
  const ER_FOOTER = ['```'];
8
+ /**
9
+ * Generate Mermaid ER diagram content from Prisma models.
10
+ *
11
+ * @param models - The list of Prisma DMMF models.
12
+ * @returns An array of Mermaid ER diagram lines.
13
+ */
12
14
  export function erContent(models) {
13
15
  // extract all relations
14
16
  const allRelations = models.flatMap(extractRelations);
@@ -1,4 +1,4 @@
1
1
  export { erContent } from './er-content.js';
2
- export { modelInfo } from './model-info.js';
3
2
  export { modelFields } from './model-fields.js';
3
+ export { modelInfo } from './model-info.js';
4
4
  export { relationLine } from './relation-line.js';
@@ -1,4 +1,4 @@
1
1
  export { erContent } from './er-content.js';
2
- export { modelInfo } from './model-info.js';
3
2
  export { modelFields } from './model-fields.js';
3
+ export { modelInfo } from './model-info.js';
4
4
  export { relationLine } from './relation-line.js';
@@ -1,7 +1,8 @@
1
1
  import type { DMMF } from '@prisma/generator-helper';
2
2
  /**
3
- * generate model fields
4
- * @param { DMMF.Model } model
5
- * @returns { string[] }
3
+ * Generate Mermaid ER field lines from a Prisma model.
4
+ *
5
+ * @param model - A Prisma DMMF model definition.
6
+ * @returns An array of strings representing each field in Mermaid ER syntax, excluding relation fields and annotations.
6
7
  */
7
8
  export declare function modelFields(model: DMMF.Model): string[];
@@ -1,9 +1,10 @@
1
1
  const ZOD_ANNOTATION = '@z.';
2
2
  const VALIBOT_ANNOTATION = '@v.';
3
3
  /**
4
- * generate model fields
5
- * @param { DMMF.Model } model
6
- * @returns { string[] }
4
+ * Generate Mermaid ER field lines from a Prisma model.
5
+ *
6
+ * @param model - A Prisma DMMF model definition.
7
+ * @returns An array of strings representing each field in Mermaid ER syntax, excluding relation fields and annotations.
7
8
  */
8
9
  export function modelFields(model) {
9
10
  return model.fields
@@ -1,7 +1,8 @@
1
- import type { Model } from '../types.js';
1
+ import type { DMMF } from '@prisma/generator-helper';
2
2
  /**
3
- * generate model info
4
- * @param model
5
- * @returns
3
+ * Generate Mermaid ER diagram model block from a Prisma model.
4
+ *
5
+ * @param model - A Prisma DMMF model definition.
6
+ * @returns An array of strings representing the model block in Mermaid ER syntax.
6
7
  */
7
- export declare function modelInfo(model: Model): readonly string[];
8
+ export declare function modelInfo(model: DMMF.Model): readonly string[];
@@ -1,8 +1,9 @@
1
1
  import { modelFields } from './index.js';
2
2
  /**
3
- * generate model info
4
- * @param model
5
- * @returns
3
+ * Generate Mermaid ER diagram model block from a Prisma model.
4
+ *
5
+ * @param model - A Prisma DMMF model definition.
6
+ * @returns An array of strings representing the model block in Mermaid ER syntax.
6
7
  */
7
8
  export function modelInfo(model) {
8
9
  return [` ${model.name} {`, ...modelFields(model), ' }'];
@@ -1,7 +1,13 @@
1
- import type { Relation } from '../types.js';
2
1
  /**
3
- * generate relation line
4
- * @param { Relation } relation
5
- * @returns { string } relation line
2
+ * Generate a Mermaid ER diagram relation line from a relation definition.
3
+ *
4
+ * @param relation - The relation definition including model and field names.
5
+ * @returns A string representing the relation line in Mermaid ER syntax.
6
6
  */
7
- export declare function relationLine(relation: Relation): string;
7
+ export declare function relationLine(relation: {
8
+ fromModel: string;
9
+ toModel: string;
10
+ fromField: string;
11
+ toField: string;
12
+ type: string;
13
+ }): string;
@@ -1,8 +1,9 @@
1
- import { buildRelationLine } from '../relationship/build-relation-line.js';
1
+ import { buildRelationLine } from '../helper/build-relation-line.js';
2
2
  /**
3
- * generate relation line
4
- * @param { Relation } relation
5
- * @returns { string } relation line
3
+ * Generate a Mermaid ER diagram relation line from a relation definition.
4
+ *
5
+ * @param relation - The relation definition including model and field names.
6
+ * @returns A string representing the relation line in Mermaid ER syntax.
6
7
  */
7
8
  export function relationLine(relation) {
8
9
  const cardinality = buildRelationLine(relation.type);
@@ -0,0 +1,9 @@
1
+ /**
2
+ * Generate a Mermaid ER diagram relation connector from a custom relationship string.
3
+ *
4
+ * @param input - A relationship string like `"one-to-many"` or `"zero-one-to-one-optional"`.
5
+ * @returns The Mermaid connector string (e.g., `"||--}|"` or `"|o..}o"`).
6
+ *
7
+ * @throws If the input format is invalid or contains unknown relationship types.
8
+ */
9
+ export declare function buildRelationLine(input: string): string;
@@ -1,10 +1,18 @@
1
- import { isRelationship } from '../validator/is-relationship.js';
1
+ import { isRelationship } from '../utils/index.js';
2
2
  const RELATIONSHIPS = {
3
3
  'zero-one': '|o',
4
4
  one: '||',
5
5
  'zero-many': '}o',
6
6
  many: '}|',
7
7
  };
8
+ /**
9
+ * Generate a Mermaid ER diagram relation connector from a custom relationship string.
10
+ *
11
+ * @param input - A relationship string like `"one-to-many"` or `"zero-one-to-one-optional"`.
12
+ * @returns The Mermaid connector string (e.g., `"||--}|"` or `"|o..}o"`).
13
+ *
14
+ * @throws If the input format is invalid or contains unknown relationship types.
15
+ */
8
16
  export function buildRelationLine(input) {
9
17
  const parts = input.split('-to-');
10
18
  if (parts.length !== 2) {
@@ -0,0 +1,8 @@
1
+ import type { DMMF } from '@prisma/generator-helper';
2
+ /**
3
+ * Extract Mermaid ER diagram relation lines from a Prisma model.
4
+ *
5
+ * @param model - A Prisma DMMF model definition.
6
+ * @returns An array of Mermaid ER diagram relation lines based on `@relation` annotations.
7
+ */
8
+ export declare function extractRelations(model: DMMF.Model): readonly string[];
@@ -1,13 +1,13 @@
1
1
  import { relationLine } from '../generator/relation-line.js';
2
- import { parseRelation } from './index.js';
2
+ import { parseRelation } from '../utils/index.js';
3
3
  /**
4
- * extract relations from model
5
- * @param { Model } model
6
- * @returns { readonly string[] }
4
+ * Extract Mermaid ER diagram relation lines from a Prisma model.
5
+ *
6
+ * @param model - A Prisma DMMF model definition.
7
+ * @returns An array of Mermaid ER diagram relation lines based on `@relation` annotations.
7
8
  */
8
9
  export function extractRelations(model) {
9
10
  const relations = [];
10
- // @relation annotation
11
11
  if (model.documentation) {
12
12
  const annotationRelations = model.documentation
13
13
  .split('\n')
@@ -1,7 +1,7 @@
1
1
  #!/usr/bin/env node
2
- import { erContent } from './generator/er-content.js';
3
2
  import fsp from 'node:fs/promises';
4
3
  import pkg from '@prisma/generator-helper';
4
+ import { erContent } from './generator/er-content.js';
5
5
  const { generatorHandler } = pkg;
6
6
  export async function main(options) {
7
7
  const content = erContent(options.dmmf.datamodel.models);
@@ -0,0 +1,34 @@
1
+ /**
2
+ * Remove duplicate relations and exclude any that are many-to-one.
3
+ *
4
+ * @param relations - An array of Mermaid ER diagram relation lines.
5
+ * @returns A filtered array excluding 'many-to-one' relations.
6
+ */
7
+ export declare function excludeManyToOneRelations(relations: readonly string[]): readonly string[];
8
+ /**
9
+ * Check if the given key is a valid relationship type.
10
+ *
11
+ * @param key - The key to check.
12
+ * @returns `true` if the key is one of the valid relationship types, otherwise `false`.
13
+ */
14
+ export declare function isRelationship(key: string): key is 'zero-one' | 'one' | 'zero-many' | 'many';
15
+ /**
16
+ * Parse a `@relation` annotation line into a structured relation object.
17
+ *
18
+ * @param line - A string representing a single `@relation` annotation.
19
+ * @returns An object containing relation details if the line is valid, otherwise `null`.
20
+ */
21
+ export declare function parseRelation(line: string): {
22
+ fromModel: string;
23
+ fromField: string;
24
+ toModel: string;
25
+ toField: string;
26
+ type: string;
27
+ } | null;
28
+ /**
29
+ * Remove duplicate relation lines from an array of Mermaid ER diagram relations.
30
+ *
31
+ * @param relations - An array of relation lines (e.g., generated from `relationLine`).
32
+ * @returns A new array with duplicates removed, preserving insertion order.
33
+ */
34
+ export declare function removeDuplicateRelations(relations: readonly string[]): readonly string[];
@@ -0,0 +1,48 @@
1
+ /**
2
+ * Remove duplicate relations and exclude any that are many-to-one.
3
+ *
4
+ * @param relations - An array of Mermaid ER diagram relation lines.
5
+ * @returns A filtered array excluding 'many-to-one' relations.
6
+ */
7
+ export function excludeManyToOneRelations(relations) {
8
+ return [...new Set(relations)].filter((r) => !r.includes('many-to-one'));
9
+ }
10
+ /**
11
+ * Check if the given key is a valid relationship type.
12
+ *
13
+ * @param key - The key to check.
14
+ * @returns `true` if the key is one of the valid relationship types, otherwise `false`.
15
+ */
16
+ export function isRelationship(key) {
17
+ return ['zero-one', 'one', 'zero-many', 'many'].includes(key);
18
+ }
19
+ /**
20
+ * Parse a `@relation` annotation line into a structured relation object.
21
+ *
22
+ * @param line - A string representing a single `@relation` annotation.
23
+ * @returns An object containing relation details if the line is valid, otherwise `null`.
24
+ */
25
+ export function parseRelation(line) {
26
+ const relationRegex = /^@relation\s+(\w+)\.(\w+)\s+(\w+)\.(\w+)\s+(\w+-to-\w+)$/;
27
+ const match = line.trim().match(relationRegex);
28
+ if (!match) {
29
+ return null;
30
+ }
31
+ const [, fromModel, fromField, toModel, toField, relationType] = match;
32
+ return {
33
+ fromModel,
34
+ fromField,
35
+ toModel,
36
+ toField,
37
+ type: relationType,
38
+ };
39
+ }
40
+ /**
41
+ * Remove duplicate relation lines from an array of Mermaid ER diagram relations.
42
+ *
43
+ * @param relations - An array of relation lines (e.g., generated from `relationLine`).
44
+ * @returns A new array with duplicates removed, preserving insertion order.
45
+ */
46
+ export function removeDuplicateRelations(relations) {
47
+ return [...new Set(relations)];
48
+ }
@@ -1,5 +1,3 @@
1
- export { inferInput } from './infer-input.js';
2
- export { properties } from './properties.js';
3
1
  export { schema } from './schema.js';
4
2
  export { schemas } from './schemas.js';
5
3
  export { valibot } from './valibot.js';
@@ -1,5 +1,3 @@
1
- export { inferInput } from './infer-input.js';
2
- export { properties } from './properties.js';
3
1
  export { schema } from './schema.js';
4
2
  export { schemas } from './schemas.js';
5
3
  export { valibot } from './valibot.js';
@@ -1,4 +1,4 @@
1
- import { properties } from './properties.js';
1
+ import { properties } from '../utils/index.js';
2
2
  import { schema } from './schema.js';
3
3
  /**
4
4
  * generate valibot schemas
@@ -1,4 +1,4 @@
1
- import type { Model } from '../../mermaid-er/types.js';
1
+ import type { DMMF } from '@prisma/generator-helper';
2
2
  /**
3
3
  * Generate Valibot schemas and types
4
4
  * @param models - The models to generate the Valibot schemas and types for
@@ -6,4 +6,4 @@ import type { Model } from '../../mermaid-er/types.js';
6
6
  * @param comment - Whether to include comments in the generated code
7
7
  * @returns The generated Valibot schemas and types
8
8
  */
9
- export declare function valibot(models: readonly Model[], type: boolean, comment: boolean): string;
9
+ export declare function valibot(models: readonly Readonly<DMMF.Model>[], type: boolean, comment: boolean): string;
@@ -1,10 +1,6 @@
1
- import { isValibot } from '../validator/is-valibot.js';
1
+ import { groupByModel, isFields } from '../../../shared/utils/index.js';
2
+ import { inferInput, isValibot, isValibotDocument } from '../utils/index.js';
2
3
  import { schemas } from './schemas.js';
3
- import { groupByModel } from '../../../shared/helper/group-by-model.js';
4
- import { isFields } from '../../../shared/validator/is-fields.js';
5
- import { inferInput } from './infer-input.js';
6
- import { isValibotDocument } from '../validator/is-valibot-document.js';
7
- const VALIBOT_IMPORT = `import * as v from 'valibot'\n`;
8
4
  /**
9
5
  * Generate Valibot schemas and types
10
6
  * @param models - The models to generate the Valibot schemas and types for
@@ -30,18 +26,14 @@ export function valibot(models, type, comment) {
30
26
  }));
31
27
  return fields;
32
28
  });
33
- // null exclude
34
- const validFields = isFields(modelFields);
35
- // group by model
36
- const groupedByModel = groupByModel(validFields);
37
- const valibots = Object.values(groupedByModel).map((fields) => {
29
+ const valibots = Object.values(groupByModel(isFields(modelFields))).map((fields) => {
38
30
  return {
39
31
  generateValibotSchema: schemas(fields, comment),
40
32
  generateValibotInfer: type ? inferInput(fields[0].modelName) : '',
41
33
  };
42
34
  });
43
35
  return [
44
- VALIBOT_IMPORT,
36
+ `import * as v from 'valibot'`,
45
37
  '',
46
38
  valibots
47
39
  .flatMap(({ generateValibotSchema, generateValibotInfer }) => [generateValibotSchema, generateValibotInfer].filter(Boolean))
@@ -1,8 +1,8 @@
1
1
  #!/usr/bin/env node
2
- import { valibot } from './generator/valibot.js';
3
2
  import fsp from 'node:fs/promises';
4
3
  import pkg from '@prisma/generator-helper';
5
4
  import { fmt } from '../../shared/format/index.js';
5
+ import { valibot } from './generator/valibot.js';
6
6
  const { generatorHandler } = pkg;
7
7
  export async function main(options) {
8
8
  const output = options.generator.output?.value ?? './valibot';
@@ -0,0 +1,42 @@
1
+ /**
2
+ * Generates a `v.InferInput` type for the specified model.
3
+ *
4
+ * @param modelName - The name of the model.
5
+ * @returns The generated TypeScript type definition line.
6
+ */
7
+ export declare function inferInput(modelName: string): string;
8
+ /**
9
+ * Generates Valibot property definitions from model fields.
10
+ *
11
+ * Filters out fields without validation, removes documentation tags like
12
+ * @relation, @v, @z, and optionally includes doc comments.
13
+ *
14
+ * @param modelFields - The list of model fields with metadata.
15
+ * @param comment - Whether to include documentation comments.
16
+ * @returns A string containing formatted Valibot property definitions.
17
+ */
18
+ export declare function properties(modelFields: {
19
+ documentation: string;
20
+ modelName: string;
21
+ fieldName: string;
22
+ validation: string | null;
23
+ comment: string[];
24
+ }[], comment: boolean): string;
25
+ /**
26
+ * Parses documentation lines and filters out Valibot validation entries.
27
+ *
28
+ * Lines containing "@v." will be excluded.
29
+ *
30
+ * @param documentation - The raw documentation string.
31
+ * @returns An array of non-Valibot documentation lines.
32
+ */
33
+ export declare function isValibotDocument(documentation?: string): string[];
34
+ /**
35
+ * Extracts the Valibot validation expression from a documentation string.
36
+ *
37
+ * Searches for a line starting with "@v." and returns the expression that follows.
38
+ *
39
+ * @param documentation - The documentation string to search.
40
+ * @returns The Valibot expression without "@v." prefix, or null if not found.
41
+ */
42
+ export declare function isValibot(documentation?: string): string | null;
@@ -0,0 +1,61 @@
1
+ /**
2
+ * Generates a `v.InferInput` type for the specified model.
3
+ *
4
+ * @param modelName - The name of the model.
5
+ * @returns The generated TypeScript type definition line.
6
+ */
7
+ export function inferInput(modelName) {
8
+ return `export type ${modelName} = v.InferInput<typeof ${modelName}Schema>`;
9
+ }
10
+ /**
11
+ * Generates Valibot property definitions from model fields.
12
+ *
13
+ * Filters out fields without validation, removes documentation tags like
14
+ * @relation, @v, @z, and optionally includes doc comments.
15
+ *
16
+ * @param modelFields - The list of model fields with metadata.
17
+ * @param comment - Whether to include documentation comments.
18
+ * @returns A string containing formatted Valibot property definitions.
19
+ */
20
+ export function properties(modelFields, comment) {
21
+ const fields = modelFields
22
+ .filter((field) => field.validation)
23
+ .map((field) => {
24
+ const cleanDoc = field.comment
25
+ .filter((line) => !(line.includes('@relation') || line.includes('@v') || line.includes('@z')))
26
+ .join('\n')
27
+ .trim();
28
+ const docComment = comment && cleanDoc ? ` /**\n * ${cleanDoc}\n */\n` : '';
29
+ return `${docComment} ${field.fieldName}: v.${field.validation}`;
30
+ })
31
+ .join(',\n');
32
+ return fields;
33
+ }
34
+ /**
35
+ * Parses documentation lines and filters out Valibot validation entries.
36
+ *
37
+ * Lines containing "@v." will be excluded.
38
+ *
39
+ * @param documentation - The raw documentation string.
40
+ * @returns An array of non-Valibot documentation lines.
41
+ */
42
+ export function isValibotDocument(documentation) {
43
+ return (documentation
44
+ ?.split('\n')
45
+ .map((line) => line.trim())
46
+ .filter((line) => line && !line.includes('@v.')) ?? []);
47
+ }
48
+ /**
49
+ * Extracts the Valibot validation expression from a documentation string.
50
+ *
51
+ * Searches for a line starting with "@v." and returns the expression that follows.
52
+ *
53
+ * @param documentation - The documentation string to search.
54
+ * @returns The Valibot expression without "@v." prefix, or null if not found.
55
+ */
56
+ export function isValibot(documentation) {
57
+ if (!documentation)
58
+ return null;
59
+ const match = documentation.match(/@v\.(.+?)(?:\n|$)/);
60
+ return match ? match[1].trim() : null;
61
+ }