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.
@@ -1,4 +1,4 @@
1
- import { c as isFields, s as groupByModel } from "./utils-DeZn2r_T.js";
1
+ import { i as isFields, r as groupByModel } from "./utils-COHZyQue.js";
2
2
 
3
3
  //#region src/helper/prisma.ts
4
4
  function collectRelationProps(models) {
@@ -61,15 +61,15 @@ function validationSchemas(models, type, comment, config) {
61
61
  fieldName: f.name
62
62
  })));
63
63
  for (const { modelName, fieldName } of missing) console.warn(`Warning: Field "${modelName}.${fieldName}" has no ${config.annotationPrefix} annotation and will be omitted from the schema`);
64
- const schemaResults = Object.values(groupByModel(isFields(modelFields))).map((fields) => ({
64
+ const schemas = Object.values(groupByModel(isFields(modelFields))).map((fields) => ({
65
65
  schema: config.schemas(fields, comment),
66
66
  inferType: type ? config.inferType(fields[0].modelName) : ""
67
- }));
68
- return [
67
+ })).flatMap(({ schema, inferType }) => [schema, inferType].filter(Boolean)).join("\n\n");
68
+ return config.importStatement ? [
69
69
  config.importStatement,
70
70
  "",
71
- schemaResults.flatMap(({ schema, inferType }) => [schema, inferType].filter(Boolean)).join("\n\n")
72
- ].join("\n");
71
+ schemas
72
+ ].join("\n") : schemas;
73
73
  }
74
74
 
75
75
  //#endregion
@@ -0,0 +1,116 @@
1
+ import fs from "node:fs/promises";
2
+
3
+ //#region src/fsp/index.ts
4
+ async function mkdir(dir) {
5
+ try {
6
+ await fs.mkdir(dir, { recursive: true });
7
+ return {
8
+ ok: true,
9
+ value: void 0
10
+ };
11
+ } catch (e) {
12
+ return {
13
+ ok: false,
14
+ error: e instanceof Error ? e.message : String(e)
15
+ };
16
+ }
17
+ }
18
+ async function writeFile(path, data) {
19
+ try {
20
+ await fs.writeFile(path, data, "utf-8");
21
+ return {
22
+ ok: true,
23
+ value: void 0
24
+ };
25
+ } catch (e) {
26
+ return {
27
+ ok: false,
28
+ error: e instanceof Error ? e.message : String(e)
29
+ };
30
+ }
31
+ }
32
+ async function writeFileBinary(path, data) {
33
+ try {
34
+ await fs.writeFile(path, data);
35
+ return {
36
+ ok: true,
37
+ value: void 0
38
+ };
39
+ } catch (e) {
40
+ return {
41
+ ok: false,
42
+ error: e instanceof Error ? e.message : String(e)
43
+ };
44
+ }
45
+ }
46
+
47
+ //#endregion
48
+ //#region src/utils/index.ts
49
+ function getString(v, fallback) {
50
+ return typeof v === "string" ? v : Array.isArray(v) ? v[0] ?? fallback : fallback;
51
+ }
52
+ function getBool(v, fallback = false) {
53
+ return v === true || v === "true" || Array.isArray(v) && v[0] === "true" ? true : fallback;
54
+ }
55
+ function makeSnakeCase(name) {
56
+ return name.replace(/([a-z0-9])([A-Z])/g, "$1_$2").toLowerCase();
57
+ }
58
+ function makeValidationExtractor(annotationPrefix) {
59
+ const escaped = annotationPrefix.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
60
+ const regex = new RegExp(`${escaped}(.+?)(?:\\n|$)`);
61
+ return function extractValidation(documentation) {
62
+ if (!documentation) return null;
63
+ return documentation.match(regex)?.[1]?.trim() ?? null;
64
+ };
65
+ }
66
+ function parseDocumentWithoutAnnotations(documentation) {
67
+ if (!documentation) return [];
68
+ return documentation.split("\n").map((line) => line.trim()).filter((line) => ![
69
+ "@z.",
70
+ "@v.",
71
+ "@a.",
72
+ "@e.",
73
+ "@t.",
74
+ "@j."
75
+ ].some((prefix) => line.startsWith(prefix))).filter((line) => ![
76
+ "@z",
77
+ "@v",
78
+ "@a",
79
+ "@e",
80
+ "@t",
81
+ "@j"
82
+ ].includes(line)).filter((line) => line.length > 0);
83
+ }
84
+ function stripAnnotations(doc) {
85
+ if (!doc) return void 0;
86
+ const result = doc.split("\n").filter((line) => {
87
+ const trimmed = line.trim();
88
+ return !(trimmed.startsWith("@z.") || trimmed.startsWith("@v.") || trimmed.startsWith("@a.") || trimmed.startsWith("@e.") || trimmed.startsWith("@t.") || trimmed.startsWith("@j.") || trimmed.startsWith("@relation") || trimmed === "@z" || trimmed === "@v" || trimmed === "@a" || trimmed === "@e" || trimmed === "@t" || trimmed === "@j");
89
+ }).join("\n").trim();
90
+ return result.length > 0 ? result : void 0;
91
+ }
92
+ function makePropertiesGenerator(libraryPrefix, wrapCardinality) {
93
+ return function makeProperties(modelFields, includeComments) {
94
+ return modelFields.filter((field) => field.validation).map((field) => {
95
+ const cleanLines = field.comment.filter((line) => !(line.includes("@relation") || line.includes("@z") || line.includes("@v") || line.includes("@a") || line.includes("@e") || line.includes("@t") || line.includes("@j")));
96
+ const docComment = includeComments && cleanLines.length > 0 ? ` /**\n${cleanLines.map((line) => ` * ${line}`).join("\n")}\n */\n` : "";
97
+ const base = `${libraryPrefix}.${field.validation}`;
98
+ const wrapped = wrapCardinality ? wrapCardinality(base, field.isRequired) : base;
99
+ return `${docComment} ${field.fieldName}: ${wrapped}`;
100
+ }).join(",\n");
101
+ };
102
+ }
103
+ function groupByModel(validFields) {
104
+ const raw = Object.groupBy(validFields, (f) => f.modelName);
105
+ return Object.fromEntries(Object.entries(raw).filter((entry) => entry[1] != null));
106
+ }
107
+ function isFields(modelFields) {
108
+ return modelFields.flat().filter((field) => field.validation !== null);
109
+ }
110
+ function schemaFromFields(modelFields, comment, schemaBuilder, propertiesGenerator) {
111
+ const modelName = modelFields[0].modelName;
112
+ return schemaBuilder(modelName, propertiesGenerator(modelFields, comment));
113
+ }
114
+
115
+ //#endregion
116
+ export { makePropertiesGenerator as a, parseDocumentWithoutAnnotations as c, mkdir as d, writeFile as f, isFields as i, schemaFromFields as l, getString as n, makeSnakeCase as o, writeFileBinary as p, groupByModel as r, makeValidationExtractor as s, getBool as t, stripAnnotations as u };
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "hekireki",
3
3
  "type": "module",
4
- "version": "0.7.0",
4
+ "version": "0.7.2",
5
5
  "license": "MIT",
6
6
  "description": "Hekireki is a tool that generates validation schemas for Zod, Valibot, ArkType, and Effect Schema, as well as ER diagrams and DBML, from Prisma schemas annotated with comments.",
7
7
  "keywords": [
@@ -13,8 +13,15 @@
13
13
  "mermaid",
14
14
  "ecto",
15
15
  "dbml",
16
+ "typebox",
17
+ "ajv",
18
+ "json-schema",
16
19
  "docs",
17
- "documentation"
20
+ "documentation",
21
+ "gorm",
22
+ "go",
23
+ "sea-orm",
24
+ "rust"
18
25
  ],
19
26
  "homepage": "https://github.com/nakita628/hekireki",
20
27
  "publishConfig": {
@@ -40,15 +47,18 @@
40
47
  "hekireki-effect": "dist/generator/effect/index.js",
41
48
  "hekireki-dbml": "dist/generator/dbml/index.js",
42
49
  "hekireki-docs": "dist/generator/docs/index.js",
43
- "hekireki-drizzle": "dist/generator/drizzle/index.js"
50
+ "hekireki-drizzle": "dist/generator/drizzle/index.js",
51
+ "hekireki-typebox": "dist/generator/typebox/index.js",
52
+ "hekireki-ajv": "dist/generator/ajv/index.js",
53
+ "hekireki-sqlalchemy": "dist/generator/sqlalchemy/index.js",
54
+ "hekireki-gorm": "dist/generator/gorm/index.js",
55
+ "hekireki-sea-orm": "dist/generator/sea-orm/index.js"
44
56
  },
45
57
  "scripts": {
46
58
  "generate": "prisma generate",
47
59
  "deps": "rm -rf node_modules && pnpm install",
48
60
  "build": "tsdown",
49
61
  "typecheck": "tsc --noEmit",
50
- "test": "vitest run",
51
- "coverage": "vitest run --coverage",
52
62
  "release": "npm pkg fix && pnpm build && npm publish"
53
63
  },
54
64
  "dependencies": {
@@ -63,7 +73,6 @@
63
73
  "@prisma/client": "^7.4.0",
64
74
  "@types/node": "^25.2.3",
65
75
  "@typescript/native-preview": "7.0.0-dev.20260218.1",
66
- "@vitest/coverage-v8": "^4.0.18",
67
76
  "arktype": "^2.1.29",
68
77
  "effect": "^3.19.18",
69
78
  "prisma": "^7.4.0",
@@ -71,7 +80,9 @@
71
80
  "tsx": "^4.21.0",
72
81
  "typescript": "^5.9.3",
73
82
  "valibot": "1.2.0",
74
- "vitest": "^4.0.18",
83
+ "@sinclair/typebox": "^0.34.33",
84
+ "ajv": "^8.17.1",
85
+ "json-schema-to-ts": "^3.1.1",
75
86
  "zod": "^4.3.6"
76
87
  }
77
88
  }
@@ -1,287 +0,0 @@
1
- import fs from "node:fs/promises";
2
-
3
- //#region src/fsp/index.ts
4
- async function mkdir(dir) {
5
- try {
6
- await fs.mkdir(dir, { recursive: true });
7
- return {
8
- ok: true,
9
- value: void 0
10
- };
11
- } catch (e) {
12
- return {
13
- ok: false,
14
- error: e instanceof Error ? e.message : String(e)
15
- };
16
- }
17
- }
18
- async function writeFile(path, data) {
19
- try {
20
- await fs.writeFile(path, data, "utf-8");
21
- return {
22
- ok: true,
23
- value: void 0
24
- };
25
- } catch (e) {
26
- return {
27
- ok: false,
28
- error: e instanceof Error ? e.message : String(e)
29
- };
30
- }
31
- }
32
- async function writeFileBinary(path, data) {
33
- try {
34
- await fs.writeFile(path, data);
35
- return {
36
- ok: true,
37
- value: void 0
38
- };
39
- } catch (e) {
40
- return {
41
- ok: false,
42
- error: e instanceof Error ? e.message : String(e)
43
- };
44
- }
45
- }
46
-
47
- //#endregion
48
- //#region src/utils/index.ts
49
- /**
50
- * Extract a string value from generator config.
51
- */
52
- const getString = (v, fallback) => typeof v === "string" ? v : Array.isArray(v) ? v[0] ?? fallback : fallback;
53
- /**
54
- * Extract a boolean value from generator config.
55
- */
56
- const getBool = (v, fallback = false) => v === true || v === "true" || Array.isArray(v) && v[0] === "true" ? true : fallback;
57
- /**
58
- * Convert camelCase/PascalCase to snake_case
59
- */
60
- function makeSnakeCase(name) {
61
- return name.replace(/([a-z0-9])([A-Z])/g, "$1_$2").toLowerCase();
62
- }
63
- /**
64
- * Create a validation extractor for a specific annotation prefix
65
- */
66
- function makeValidationExtractor(annotationPrefix) {
67
- const escaped = annotationPrefix.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
68
- const regex = new RegExp(`${escaped}(.+?)(?:\\n|$)`);
69
- return function extractValidation(documentation) {
70
- if (!documentation) return null;
71
- return documentation.match(regex)?.[1]?.trim() ?? null;
72
- };
73
- }
74
- /**
75
- * Parse documentation and filter out all annotation lines.
76
- */
77
- function parseDocumentWithoutAnnotations(documentation) {
78
- if (!documentation) return [];
79
- return documentation.split("\n").map((line) => line.trim()).filter((line) => ![
80
- "@z.",
81
- "@v.",
82
- "@a.",
83
- "@e."
84
- ].some((prefix) => line.startsWith(prefix))).filter((line) => ![
85
- "@z",
86
- "@v",
87
- "@a",
88
- "@e"
89
- ].includes(line)).filter((line) => line.length > 0);
90
- }
91
- /**
92
- * Format enum values as Zod enum expression
93
- */
94
- function makeZodEnumExpression(values) {
95
- return `enum([${values.map((v) => `'${v}'`).join(", ")}])`;
96
- }
97
- /**
98
- * Format enum values as Valibot picklist expression
99
- */
100
- function makeValibotEnumExpression(values) {
101
- return `picklist([${values.map((v) => `'${v}'`).join(", ")}])`;
102
- }
103
- /**
104
- * Format enum values as ArkType union expression
105
- */
106
- function makeArktypeEnumExpression(values) {
107
- return `"${values.map((v) => `'${v}'`).join(" | ")}"`;
108
- }
109
- /**
110
- * Format enum values as Effect Schema.Literal expression
111
- */
112
- function makeEffectEnumExpression(values) {
113
- return `Schema.Literal(${values.map((v) => `'${v}'`).join(", ")})`;
114
- }
115
- /**
116
- * Create a properties generator for a specific validation library
117
- */
118
- function makePropertiesGenerator(libraryPrefix, wrapCardinality) {
119
- return function makeProperties(modelFields, includeComments) {
120
- return modelFields.filter((field) => field.validation).map((field) => {
121
- const cleanLines = field.comment.filter((line) => !(line.includes("@relation") || line.includes("@v") || line.includes("@z")));
122
- const docComment = includeComments && cleanLines.length > 0 ? ` /**\n${cleanLines.map((line) => ` * ${line}`).join("\n")}\n */\n` : "";
123
- const base = `${libraryPrefix}.${field.validation}`;
124
- const wrapped = wrapCardinality ? wrapCardinality(base, field.isRequired) : base;
125
- return `${docComment} ${field.fieldName}: ${wrapped}`;
126
- }).join(",\n");
127
- };
128
- }
129
- /**
130
- * Create Zod type inference line
131
- */
132
- function makeZodInfer(modelName) {
133
- return `export type ${modelName} = z.infer<typeof ${modelName}Schema>`;
134
- }
135
- /**
136
- * Generate Zod schema
137
- */
138
- function makeZodSchema(modelName, fields) {
139
- return `export const ${modelName}Schema = z.object({\n${fields}\n})`;
140
- }
141
- /**
142
- * Create Valibot type inference line
143
- */
144
- function makeValibotInfer(modelName) {
145
- return `export type ${modelName} = v.InferInput<typeof ${modelName}Schema>`;
146
- }
147
- /**
148
- * Generate Valibot schema
149
- */
150
- function makeValibotSchema(modelName, fields) {
151
- return `export const ${modelName}Schema = v.object({\n${fields}\n})`;
152
- }
153
- /**
154
- * Generate ArkType infer type statement.
155
- */
156
- function makeArktypeInfer(modelName) {
157
- return `export type ${modelName} = typeof ${modelName}Schema.infer`;
158
- }
159
- /**
160
- * Generate ArkType schema
161
- */
162
- function makeArktypeSchema(modelName, fields) {
163
- return `export const ${modelName}Schema = type({\n${fields}\n})`;
164
- }
165
- /**
166
- * Generate properties for ArkType schema.
167
- */
168
- function makeArktypeProperties(fields, comment) {
169
- return fields.map((field) => {
170
- return `${comment && field.comment.length > 0 ? `${field.comment.map((c) => ` /** ${c} */`).join("\n")}\n` : ""} ${field.fieldName}: ${field.validation ?? "\"unknown\""},`;
171
- }).join("\n");
172
- }
173
- /**
174
- * Generate Effect Schema infer type statement.
175
- */
176
- function makeEffectInfer(modelName) {
177
- return `export type ${modelName} = Schema.Schema.Type<typeof ${modelName}Schema>`;
178
- }
179
- /**
180
- * Generate Effect Schema
181
- */
182
- function makeEffectSchema(modelName, fields) {
183
- return `export const ${modelName}Schema = Schema.Struct({\n${fields}\n})`;
184
- }
185
- /**
186
- * Generate properties for Effect Schema.
187
- */
188
- function makeEffectProperties(fields, comment) {
189
- return fields.map((field) => {
190
- return `${comment && field.comment.length > 0 ? `${field.comment.map((c) => ` /** ${c} */`).join("\n")}\n` : ""} ${field.fieldName}: ${field.validation ?? "Schema.Unknown"},`;
191
- }).join("\n");
192
- }
193
- /**
194
- * Remove duplicate relation lines from an array of Mermaid ER diagram relations.
195
- */
196
- function removeDuplicateRelations(relations) {
197
- return [...new Set(relations)];
198
- }
199
- function escapeNote(str) {
200
- return str.replace(/'/g, "\\'");
201
- }
202
- function formatConstraints(constraints) {
203
- return constraints.length > 0 ? ` [${constraints.join(", ")}]` : "";
204
- }
205
- function makeEnum(enumDef) {
206
- return [
207
- `Enum ${enumDef.name} {`,
208
- ...enumDef.values.map((v) => ` ${v}`),
209
- "}"
210
- ].join("\n");
211
- }
212
- function makeRefName(ref) {
213
- return ref.name ?? `${ref.fromTable}_${ref.fromColumn}_${ref.toTable}_${ref.toColumn}_fk`;
214
- }
215
- /**
216
- * Strip validation annotations (@z.*, @v.*, @a.*, @e.*) and relation annotations (@relation) from documentation
217
- */
218
- function stripAnnotations(doc) {
219
- if (!doc) return void 0;
220
- const result = doc.split("\n").filter((line) => {
221
- const trimmed = line.trim();
222
- return !(trimmed.startsWith("@z.") || trimmed.startsWith("@v.") || trimmed.startsWith("@a.") || trimmed.startsWith("@e.") || trimmed.startsWith("@relation") || trimmed === "@z" || trimmed === "@v" || trimmed === "@a" || trimmed === "@e");
223
- }).join("\n").trim();
224
- return result.length > 0 ? result : void 0;
225
- }
226
- /**
227
- * Combine keys for composite foreign keys
228
- */
229
- function combineKeys(keys) {
230
- return keys.length > 1 ? `(${keys.join(", ")})` : keys[0];
231
- }
232
- /**
233
- * Convert Prisma type to Ecto type
234
- */
235
- function prismaTypeToEctoType(type) {
236
- if (type === "Int") return "integer";
237
- if (type === "BigInt") return "integer";
238
- if (type === "Float") return "float";
239
- if (type === "Decimal") return "decimal";
240
- if (type === "String") return "string";
241
- if (type === "Boolean") return "boolean";
242
- if (type === "DateTime") return "utc_datetime";
243
- if (type === "Json") return "map";
244
- if (type === "Bytes") return "binary";
245
- return "string";
246
- }
247
- /**
248
- * Convert Ecto type to Elixir typespec
249
- */
250
- function ectoTypeToTypespec(type) {
251
- switch (type) {
252
- case "string": return "String.t()";
253
- case "integer": return "integer()";
254
- case "float": return "float()";
255
- case "boolean": return "boolean()";
256
- case "binary_id": return "Ecto.UUID.t()";
257
- case "naive_datetime": return "NaiveDateTime.t()";
258
- case "utc_datetime": return "DateTime.t()";
259
- case "decimal": return "Decimal.t()";
260
- case "map": return "map()";
261
- case "binary": return "binary()";
262
- default: return "term()";
263
- }
264
- }
265
- /**
266
- * Group valid fields by their model name.
267
- */
268
- function groupByModel(validFields) {
269
- const raw = Object.groupBy(validFields, (f) => f.modelName);
270
- return Object.fromEntries(Object.entries(raw).filter((entry) => entry[1] != null));
271
- }
272
- /**
273
- * Extract fields with validation from a nested array of model fields.
274
- */
275
- function isFields(modelFields) {
276
- return modelFields.flat().filter((field) => field.validation !== null);
277
- }
278
- /**
279
- * Creates schema from model fields.
280
- */
281
- function schemaFromFields(modelFields, comment, schemaBuilder, propertiesGenerator) {
282
- const modelName = modelFields[0].modelName;
283
- return schemaBuilder(modelName, propertiesGenerator(modelFields, comment));
284
- }
285
-
286
- //#endregion
287
- export { removeDuplicateRelations as A, makeValibotSchema as C, makeZodSchema as D, makeZodInfer as E, writeFileBinary as F, stripAnnotations as M, mkdir as N, parseDocumentWithoutAnnotations as O, writeFile as P, makeValibotInfer as S, makeZodEnumExpression as T, makeEnum as _, getBool as a, makeSnakeCase as b, isFields as c, makeArktypeProperties as d, makeArktypeSchema as f, makeEffectSchema as g, makeEffectProperties as h, formatConstraints as i, schemaFromFields as j, prismaTypeToEctoType as k, makeArktypeEnumExpression as l, makeEffectInfer as m, ectoTypeToTypespec as n, getString as o, makeEffectEnumExpression as p, escapeNote as r, groupByModel as s, combineKeys as t, makeArktypeInfer as u, makePropertiesGenerator as v, makeValidationExtractor as w, makeValibotEnumExpression as x, makeRefName as y };