prisma-arktype 2.1.0 → 2.3.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.
Files changed (3) hide show
  1. package/README.md +112 -14
  2. package/dist/index.js +114 -48
  3. package/package.json +2 -2
package/README.md CHANGED
@@ -86,16 +86,18 @@ generator prisma-arktype {
86
86
 
87
87
  For each model, the generator creates multiple schema types:
88
88
 
89
- - **`ModelPlain`** - Fields without relationships
90
- - **`ModelRelations`** - Relationship definitions only
91
- - **`Model`** - Complete composite schema (Plain & Relations)
92
- - **`ModelWhere`** - Where clause schema
93
- - **`ModelWhereUnique`** - Unique where clause schema
94
- - **`ModelCreate`** - Create input schema
95
- - **`ModelUpdate`** - Update input schema
96
- - **`ModelSelect`** - Select schema
97
- - **`ModelInclude`** - Include schema
98
- - **`ModelOrderBy`** - OrderBy schema
89
+ - **`ModelPlain`** - Scalar fields only (strings, numbers, dates, enums) - no relations
90
+ - **`ModelRelations`** - Relationship fields only, referencing related model Plain types
91
+ - **`Model`** - Complete composite schema combining Plain & Relations
92
+ - **`ModelWhere`** - Where clause schema for filtering
93
+ - **`ModelWhereUnique`** - Unique where clause schema for finding specific records
94
+ - **`ModelCreate`** - Input schema for creating records
95
+ - **`ModelUpdate`** - Input schema for updating records
96
+ - **`ModelSelect`** - Schema for selecting specific fields
97
+ - **`ModelInclude`** - Schema for including relations
98
+ - **`ModelOrderBy`** - Schema for ordering results
99
+
100
+ **Enums** are generated as separate reusable types that are imported and referenced by models that use them.
99
101
 
100
102
  ### Using Generated Schemas
101
103
 
@@ -130,6 +132,101 @@ const whereResult = UserWhere(whereClause);
130
132
  // ...
131
133
  ```
132
134
 
135
+ ### Generated Code Examples
136
+
137
+ #### Enum Generation
138
+
139
+ For a Prisma enum like:
140
+ ```prisma
141
+ enum Currency {
142
+ USD
143
+ EUR
144
+ GBP
145
+ }
146
+ ```
147
+
148
+ The generator creates a separate reusable type:
149
+ ```typescript
150
+ // Currency.ts
151
+ import { type } from "arktype";
152
+
153
+ export const Currency = type("'USD' | 'EUR' | 'GBP'");
154
+ ```
155
+
156
+ Which is then imported and used in models:
157
+ ```typescript
158
+ // PaymentPlain.ts
159
+ import { type } from "arktype";
160
+ import { Currency } from "./Currency";
161
+
162
+ export const PaymentPlain = type({
163
+ "id": "string",
164
+ "amount": "number",
165
+ "currency": Currency, // Required enum
166
+ "status?": Currency.or("null") // Optional enum
167
+ });
168
+ ```
169
+
170
+ #### Relation Generation
171
+
172
+ For Prisma models with relations like:
173
+ ```prisma
174
+ model User {
175
+ id String @id
176
+ email String
177
+ posts Post[]
178
+ }
179
+
180
+ model Post {
181
+ id String @id
182
+ title String
183
+ author User @relation(fields: [authorId], references: [id])
184
+ authorId String
185
+ }
186
+ ```
187
+
188
+ The generator creates Plain types (without relations):
189
+ ```typescript
190
+ // UserPlain.ts
191
+ export const UserPlain = type({
192
+ "id": "string",
193
+ "email": "string"
194
+ });
195
+
196
+ // PostPlain.ts
197
+ export const PostPlain = type({
198
+ "id": "string",
199
+ "title": "string",
200
+ "authorId": "string"
201
+ });
202
+ ```
203
+
204
+ And Relations types that reference the Plain types:
205
+ ```typescript
206
+ // UserRelations.ts
207
+ import { PostPlain } from "./PostPlain";
208
+
209
+ export const UserRelations = type({
210
+ "posts": PostPlain.array() // Array of Post objects
211
+ });
212
+
213
+ // PostRelations.ts
214
+ import { UserPlain } from "./UserPlain";
215
+
216
+ export const PostRelations = type({
217
+ "author": UserPlain // Single User object
218
+ });
219
+ ```
220
+
221
+ The combined model merges both:
222
+ ```typescript
223
+ // User.ts
224
+ import { UserPlain } from "./UserPlain";
225
+ import { UserRelations } from "./UserRelations";
226
+
227
+ export const User = type(() => UserPlain.and(UserRelations));
228
+ ```
229
+
133
230
  ## Annotations
134
231
 
135
232
  Control schema generation using annotations in your Prisma schema. All annotations are added as documentation comments (`///`).
@@ -219,15 +316,16 @@ Prisma types are mapped to ArkType as follows:
219
316
  | `DateTime` | `"Date"` | `"Date"` |
220
317
  | `Json` | `"unknown"` | `"unknown"` |
221
318
  | `Bytes` | `"instanceof Buffer"` | `"instanceof Buffer"` |
222
- | Enums | Union of literal values | `type("'USD' \| 'EUR' \| 'GBP'")` |
223
- | Relations | `"unknown"` | `type("unknown").array()` for lists |
319
+ | Enums | Reference to enum type | `Currency` (imported from `./Currency`) |
320
+ | Relations | Reference to related Plain type | `PostPlain` or `PostPlain.array()` |
224
321
 
225
322
  ### Special Handling
226
323
 
227
324
  - **Optional fields**: Use `?` on the key name (`"name?": "string"`)
228
325
  - **Nullable fields**: Add `| null` to the type (`"string | null"`)
229
- - **Arrays**: Use `.array()` syntax for lists (`type("string").array()`)
230
- - **Enums**: Generated as string literal unions wrapped in `type()`
326
+ - **Arrays**: Use `.array()` syntax for lists (`type("string").array()` or `Currency.array()`)
327
+ - **Enums**: Generated as separate reusable type definitions and imported where used
328
+ - **Relations**: Reference the Plain type of the related model, imported automatically
231
329
 
232
330
  ## Differences from prismabox
233
331
 
package/dist/index.js CHANGED
@@ -181,17 +181,17 @@ function stringifyEnum(enumData) {
181
181
  const processedPlain = [];
182
182
  function processPlain(models) {
183
183
  for (const model of models) {
184
- const stringified = stringifyPlain(model);
185
- if (stringified) {
184
+ const result = stringifyPlain(model);
185
+ if (result) {
186
186
  processedPlain.push({
187
187
  name: model.name,
188
- stringified
188
+ stringified: result.stringified,
189
+ enumDependencies: result.enumDependencies
189
190
  });
190
191
  }
191
192
  }
192
193
  Object.freeze(processedPlain);
193
194
  }
194
- const enumMatch$1 = /type\("(.+)"\)/;
195
195
  function stringifyPlain(model, isInputCreate = false, isInputUpdate = false) {
196
196
  const config = getConfig();
197
197
  const { hidden } = extractAnnotations(
@@ -201,6 +201,7 @@ function stringifyPlain(model, isInputCreate = false, isInputUpdate = false) {
201
201
  return;
202
202
  }
203
203
  const fields = [];
204
+ const enumDependencies = [];
204
205
  for (const field of model.fields) {
205
206
  const {
206
207
  annotations: fieldAnnotations,
@@ -227,17 +228,28 @@ function stringifyPlain(model, isInputCreate = false, isInputUpdate = false) {
227
228
  } else if (field.kind === "enum") {
228
229
  const enumDef = processedEnums.find((e) => e.name === field.type);
229
230
  if (!enumDef) continue;
230
- const match = enumDef.stringified.match(enumMatch$1);
231
- fieldType = match ? `"${match[1]}"` : `"'${field.type}'"`;
231
+ if (!enumDependencies.includes(field.type)) {
232
+ enumDependencies.push(field.type);
233
+ }
234
+ fieldType = field.type;
232
235
  } else {
233
236
  continue;
234
237
  }
238
+ const isEnumType = field.kind === "enum" && !typeOverwrite;
235
239
  if (field.isList) {
236
- fieldType = `"${wrapPrimitiveWithArray(fieldType.slice(1, -1))}"`;
240
+ if (isEnumType) {
241
+ fieldType = `${fieldType}.array()`;
242
+ } else {
243
+ fieldType = `"${wrapPrimitiveWithArray(fieldType.slice(1, -1))}"`;
244
+ }
237
245
  }
238
246
  if (!field.isRequired) {
239
- const inner = fieldType.slice(1, -1);
240
- fieldType = `"${inner} | null"`;
247
+ if (isEnumType) {
248
+ fieldType = `${fieldType}.or("null")`;
249
+ } else {
250
+ const inner = fieldType.slice(1, -1);
251
+ fieldType = `"${inner} | null"`;
252
+ }
241
253
  fieldName += "?";
242
254
  }
243
255
  if (field.hasDefaultValue || isInputUpdate) {
@@ -247,9 +259,12 @@ function stringifyPlain(model, isInputCreate = false, isInputUpdate = false) {
247
259
  }
248
260
  fields.push(`"${fieldName}": ${fieldType}`);
249
261
  }
250
- return `{
262
+ return {
263
+ stringified: `{
251
264
  ${fields.join(",\n ")}
252
- }`;
265
+ }`,
266
+ enumDependencies
267
+ };
253
268
  }
254
269
  function stringifyPlainInputCreate(model) {
255
270
  return stringifyPlain(model, true, false);
@@ -263,11 +278,12 @@ function processCreate(models) {
263
278
  for (const model of models) {
264
279
  const { hidden } = extractAnnotations(model.documentation);
265
280
  if (hidden) continue;
266
- const stringified = stringifyPlainInputCreate(model);
267
- if (stringified) {
281
+ const result = stringifyPlainInputCreate(model);
282
+ if (result) {
268
283
  processedCreate.push({
269
284
  name: model.name,
270
- stringified
285
+ stringified: result.stringified,
286
+ enumDependencies: result.enumDependencies
271
287
  });
272
288
  }
273
289
  }
@@ -348,11 +364,12 @@ const processedRelationsCreate = [];
348
364
  const processedRelationsUpdate = [];
349
365
  function processRelations(models) {
350
366
  for (const model of models) {
351
- const stringified = stringifyRelations(model);
352
- if (stringified) {
367
+ const result = stringifyRelations(model);
368
+ if (result) {
353
369
  processedRelations.push({
354
370
  name: model.name,
355
- stringified
371
+ stringified: result.stringified,
372
+ modelDependencies: result.modelDependencies
356
373
  });
357
374
  }
358
375
  }
@@ -366,26 +383,34 @@ function stringifyRelations(model) {
366
383
  return;
367
384
  }
368
385
  const fields = [];
386
+ const modelDependencies = [];
369
387
  for (const field of model.fields) {
370
388
  const { hidden: fieldHidden } = extractAnnotations(field.documentation);
371
389
  if (fieldHidden) continue;
372
390
  if (field.kind !== "object") continue;
391
+ const relatedModelPlain = `${field.type}Plain`;
392
+ if (!modelDependencies.includes(field.type)) {
393
+ modelDependencies.push(field.type);
394
+ }
373
395
  let fieldType;
374
396
  if (field.isList) {
375
- fieldType = `type("unknown").array()`;
397
+ fieldType = `${relatedModelPlain}.array()`;
376
398
  } else if (!field.isRequired) {
377
- fieldType = `"unknown | null"`;
399
+ fieldType = `${relatedModelPlain}.or("null")`;
378
400
  } else {
379
- fieldType = `"unknown"`;
401
+ fieldType = relatedModelPlain;
380
402
  }
381
403
  fields.push(`"${field.name}": ${fieldType}`);
382
404
  }
383
405
  if (fields.length === 0) {
384
406
  return;
385
407
  }
386
- return `{
408
+ return {
409
+ stringified: `{
387
410
  ${fields.join(",\n ")}
388
- }`;
411
+ }`,
412
+ modelDependencies
413
+ };
389
414
  }
390
415
  function processRelationsCreate(models) {
391
416
  for (const model of models) {
@@ -530,11 +555,12 @@ function processUpdate(models) {
530
555
  for (const model of models) {
531
556
  const { hidden } = extractAnnotations(model.documentation);
532
557
  if (hidden) continue;
533
- const stringified = stringifyPlainInputUpdate(model);
534
- if (stringified) {
558
+ const result = stringifyPlainInputUpdate(model);
559
+ if (result) {
535
560
  processedUpdate.push({
536
561
  name: model.name,
537
- stringified
562
+ stringified: result.stringified,
563
+ enumDependencies: result.enumDependencies
538
564
  });
539
565
  }
540
566
  }
@@ -545,25 +571,26 @@ const processedWhere = [];
545
571
  const processedWhereUnique = [];
546
572
  function processWhere(models) {
547
573
  for (const model of models) {
548
- const stringified = stringifyWhere(model);
549
- if (stringified) {
574
+ const result = stringifyWhere(model);
575
+ if (result) {
550
576
  processedWhere.push({
551
577
  name: model.name,
552
- stringified
578
+ stringified: result.stringified,
579
+ enumDependencies: result.enumDependencies
553
580
  });
554
581
  }
555
- const stringifiedUnique = stringifyWhereUnique(model);
556
- if (stringifiedUnique) {
582
+ const uniqueResult = stringifyWhereUnique(model);
583
+ if (uniqueResult) {
557
584
  processedWhereUnique.push({
558
585
  name: model.name,
559
- stringified: stringifiedUnique
586
+ stringified: uniqueResult.stringified,
587
+ enumDependencies: uniqueResult.enumDependencies
560
588
  });
561
589
  }
562
590
  }
563
591
  Object.freeze(processedWhere);
564
592
  Object.freeze(processedWhereUnique);
565
593
  }
566
- const enumMatch = /type\("(.+)"\)/;
567
594
  function stringifyWhere(model) {
568
595
  const { hidden } = extractAnnotations(
569
596
  model.documentation
@@ -572,6 +599,7 @@ function stringifyWhere(model) {
572
599
  return;
573
600
  }
574
601
  const fields = [];
602
+ const enumDependencies = [];
575
603
  for (const field of model.fields) {
576
604
  const { annotations: fieldAnnotations, hidden: fieldHidden } = extractAnnotations(field.documentation);
577
605
  if (fieldHidden) continue;
@@ -585,20 +613,30 @@ function stringifyWhere(model) {
585
613
  } else if (field.kind === "enum") {
586
614
  const enumDef = processedEnums.find((e) => e.name === field.type);
587
615
  if (!enumDef) continue;
588
- const match = enumDef.stringified.match(enumMatch);
589
- fieldType = match ? `"${match[1]}"` : `"'${field.type}'"`;
616
+ if (!enumDependencies.includes(field.type)) {
617
+ enumDependencies.push(field.type);
618
+ }
619
+ fieldType = field.type;
590
620
  } else {
591
621
  continue;
592
622
  }
623
+ const isEnumType = field.kind === "enum" && !typeOverwrite;
593
624
  if (field.isList) {
594
- const inner = fieldType.slice(1, -1);
595
- fieldType = `"${wrapPrimitiveWithArray(inner)}"`;
625
+ if (isEnumType) {
626
+ fieldType = `${fieldType}.array()`;
627
+ } else {
628
+ const inner = fieldType.slice(1, -1);
629
+ fieldType = `"${wrapPrimitiveWithArray(inner)}"`;
630
+ }
596
631
  }
597
632
  fields.push(`"${field.name}?": ${fieldType}`);
598
633
  }
599
- return `{
634
+ return {
635
+ stringified: `{
600
636
  ${fields.join(",\n ")}
601
- }`;
637
+ }`,
638
+ enumDependencies
639
+ };
602
640
  }
603
641
  function stringifyWhereUnique(model) {
604
642
  const { hidden } = extractAnnotations(
@@ -608,6 +646,7 @@ function stringifyWhereUnique(model) {
608
646
  return;
609
647
  }
610
648
  const fields = [];
649
+ const enumDependencies = [];
611
650
  for (const field of model.fields) {
612
651
  const { annotations: fieldAnnotations, hidden: fieldHidden } = extractAnnotations(field.documentation);
613
652
  if (fieldHidden) continue;
@@ -622,8 +661,10 @@ function stringifyWhereUnique(model) {
622
661
  } else if (field.kind === "enum") {
623
662
  const enumDef = processedEnums.find((e) => e.name === field.type);
624
663
  if (!enumDef) continue;
625
- const match = enumDef.stringified.match(enumMatch);
626
- fieldType = match ? `"${match[1]}"` : `"'${field.type}'"`;
664
+ if (!enumDependencies.includes(field.type)) {
665
+ enumDependencies.push(field.type);
666
+ }
667
+ fieldType = field.type;
627
668
  } else {
628
669
  continue;
629
670
  }
@@ -632,14 +673,33 @@ function stringifyWhereUnique(model) {
632
673
  if (fields.length === 0) {
633
674
  return;
634
675
  }
635
- return `{
676
+ return {
677
+ stringified: `{
636
678
  ${fields.join(",\n ")}
637
- }`;
679
+ }`,
680
+ enumDependencies
681
+ };
638
682
  }
639
683
 
640
684
  async function format(input) {
641
685
  return input;
642
686
  }
687
+ function generateEnumImports(enumDependencies) {
688
+ if (!enumDependencies || enumDependencies.length === 0) {
689
+ return "";
690
+ }
691
+ return `${enumDependencies.map((enumName) => `import { ${enumName} } from "./${enumName}";`).join("\n")}
692
+ `;
693
+ }
694
+ function generateModelImports(modelDependencies) {
695
+ if (!modelDependencies || modelDependencies.length === 0) {
696
+ return "";
697
+ }
698
+ return `${modelDependencies.map(
699
+ (modelName) => `import { ${modelName}Plain } from "./${modelName}Plain";`
700
+ ).join("\n")}
701
+ `;
702
+ }
643
703
  function mapAllModelsForWrite(processedEnums, processedPlain, processedRelations, processedWhere, processedWhereUnique, processedCreate, processedUpdate, processedRelationsCreate, processedRelationsUpdate, processedSelect, processedInclude, processedOrderBy) {
644
704
  const config = getConfig();
645
705
  const modelMap = /* @__PURE__ */ new Map();
@@ -652,12 +712,14 @@ function mapAllModelsForWrite(processedEnums, processedPlain, processedRelations
652
712
  modelMap.set(model.name, content);
653
713
  }
654
714
  for (const model of processedPlain) {
655
- const content = `${arktypeImport}export const ${model.name}Plain = type(${model.stringified});
715
+ const enumImports = generateEnumImports(model.enumDependencies);
716
+ const content = `${arktypeImport}${enumImports}export const ${model.name}Plain = type(${model.stringified});
656
717
  `;
657
718
  modelMap.set(`${model.name}Plain`, content);
658
719
  }
659
720
  for (const model of processedRelations) {
660
- const content = `${arktypeImport}export const ${model.name}Relations = type(${model.stringified});
721
+ const modelImports = generateModelImports(model.modelDependencies);
722
+ const content = `${arktypeImport}${modelImports}export const ${model.name}Relations = type(${model.stringified});
661
723
  `;
662
724
  modelMap.set(`${model.name}Relations`, content);
663
725
  }
@@ -679,22 +741,26 @@ export const ${plain.name} = ${plain.name}Plain;
679
741
  }
680
742
  }
681
743
  for (const model of processedWhere) {
682
- const content = `${arktypeImport}export const ${model.name}Where = type(${model.stringified});
744
+ const enumImports = generateEnumImports(model.enumDependencies);
745
+ const content = `${arktypeImport}${enumImports}export const ${model.name}Where = type(${model.stringified});
683
746
  `;
684
747
  modelMap.set(`${model.name}Where`, content);
685
748
  }
686
749
  for (const model of processedWhereUnique) {
687
- const content = `${arktypeImport}export const ${model.name}WhereUnique = type(${model.stringified});
750
+ const enumImports = generateEnumImports(model.enumDependencies);
751
+ const content = `${arktypeImport}${enumImports}export const ${model.name}WhereUnique = type(${model.stringified});
688
752
  `;
689
753
  modelMap.set(`${model.name}WhereUnique`, content);
690
754
  }
691
755
  for (const model of processedCreate) {
692
- const content = `${arktypeImport}export const ${model.name}Create = type(${model.stringified});
756
+ const enumImports = generateEnumImports(model.enumDependencies);
757
+ const content = `${arktypeImport}${enumImports}export const ${model.name}Create = type(${model.stringified});
693
758
  `;
694
759
  modelMap.set(`${model.name}Create`, content);
695
760
  }
696
761
  for (const model of processedUpdate) {
697
- const content = `${arktypeImport}export const ${model.name}Update = type(${model.stringified});
762
+ const enumImports = generateEnumImports(model.enumDependencies);
763
+ const content = `${arktypeImport}${enumImports}export const ${model.name}Update = type(${model.stringified});
698
764
  `;
699
765
  modelMap.set(`${model.name}Update`, content);
700
766
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "prisma-arktype",
3
- "version": "2.1.0",
3
+ "version": "2.3.0",
4
4
  "description": "Generate ArkType schemas from your Prisma schema",
5
5
  "type": "module",
6
6
  "main": "./dist/index.js",
@@ -41,7 +41,7 @@
41
41
  },
42
42
  "devDependencies": {
43
43
  "@biomejs/biome": "2.3.11",
44
- "@changesets/cli": "^2.29.7",
44
+ "@changesets/cli": "2.29.8",
45
45
  "@commitlint/cli": "^20.1.0",
46
46
  "@commitlint/config-conventional": "^20.0.0",
47
47
  "@prisma/client": "^6.18.0",