prisma-arktype 2.0.0 → 2.2.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 +103 -25
  2. package/dist/index.js +126 -110
  3. package/package.json +7 -7
package/README.md CHANGED
@@ -132,75 +132,102 @@ const whereResult = UserWhere(whereClause);
132
132
 
133
133
  ## Annotations
134
134
 
135
- Control schema generation using annotations in your Prisma schema:
135
+ Control schema generation using annotations in your Prisma schema. All annotations are added as documentation comments (`///`).
136
+
137
+ ### Available Annotations
138
+
139
+ | Annotation | Scope | Description |
140
+ |------------|-------|-------------|
141
+ | `@prisma-arktype.hide` | Model or Field | Completely hide from all generated schemas |
142
+ | `@prisma-arktype.input.hide` | Field | Hide from Create and Update input schemas |
143
+ | `@prisma-arktype.create.input.hide` | Field | Hide from Create input schema only |
144
+ | `@prisma-arktype.update.input.hide` | Field | Hide from Update input schema only |
145
+ | `@prisma-arktype.typeOverwrite="<type>"` | Field | Override the generated ArkType type |
136
146
 
137
147
  ### Hide Fields/Models
138
148
 
149
+ Completely exclude models or fields from all generated schemas:
150
+
139
151
  ```prisma
140
152
  /// @prisma-arktype.hide
141
153
  model InternalModel {
142
154
  id String @id
155
+ secret String
143
156
  }
144
157
 
145
158
  model User {
146
159
  id String @id
160
+ email String
147
161
  /// @prisma-arktype.hide
148
- internalField String
162
+ passwordHash String
149
163
  }
150
164
  ```
151
165
 
152
166
  ### Hide from Input Models
153
167
 
168
+ Control which fields appear in Create and Update schemas:
169
+
154
170
  ```prisma
155
171
  model User {
156
172
  id String @id
173
+ email String
174
+
157
175
  /// @prisma-arktype.input.hide
176
+ /// Hidden from both Create and Update
158
177
  computedField String
178
+
159
179
  /// @prisma-arktype.create.input.hide
160
- onlyInUpdates String
180
+ /// Only appears in Update schema
181
+ lastModified DateTime
182
+
161
183
  /// @prisma-arktype.update.input.hide
162
- onlyInCreates String
184
+ /// Only appears in Create schema
185
+ initialStatus String
163
186
  }
164
187
  ```
165
188
 
166
189
  ### Type Override
167
190
 
191
+ Override the default type mapping with custom ArkType type strings:
192
+
168
193
  ```prisma
169
194
  model User {
170
195
  id String @id
171
196
  /// @prisma-arktype.typeOverwrite="string.email"
172
197
  email String
198
+ /// @prisma-arktype.typeOverwrite="string.url"
199
+ website String
173
200
  /// @prisma-arktype.typeOverwrite="string.numeric"
174
201
  phone String
175
202
  }
176
203
  ```
177
204
 
178
- ### Custom Options
179
-
180
- ```prisma
181
- model User {
182
- id String @id
183
- /// @prisma-arktype.options{minLength: 3, maxLength: 50}
184
- username String
185
- }
186
- ```
205
+ This allows you to use any ArkType type definition, including built-in refinements like `string.email`, `string.url`, `number.integer`, etc.
187
206
 
188
207
  ## Type Mapping
189
208
 
190
209
  Prisma types are mapped to ArkType as follows:
191
210
 
192
- | Prisma Type | ArkType Type |
193
- |-------------|--------------|
194
- | `String` | `"string"` |
195
- | `Int` | `"integer"` |
196
- | `BigInt` | `"integer"` |
197
- | `Float` | `"number"` |
198
- | `Decimal` | `"number"` |
199
- | `Boolean` | `"boolean"` |
200
- | `DateTime` | `"Date"` |
201
- | `Json` | `"unknown"` |
202
- | `Bytes` | `"instanceof Buffer"` |
203
- | Enums | Union of literal values |
211
+ | Prisma Type | ArkType Type | Example Output |
212
+ |-------------|--------------|----------------|
213
+ | `String` | `"string"` | `"string"` |
214
+ | `Int` | `"number.integer"` | `"number.integer"` |
215
+ | `BigInt` | `"number.integer"` | `"number.integer"` |
216
+ | `Float` | `"number"` | `"number"` |
217
+ | `Decimal` | `"number"` | `"number"` |
218
+ | `Boolean` | `"boolean"` | `"boolean"` |
219
+ | `DateTime` | `"Date"` | `"Date"` |
220
+ | `Json` | `"unknown"` | `"unknown"` |
221
+ | `Bytes` | `"instanceof Buffer"` | `"instanceof Buffer"` |
222
+ | Enums | Union of literal values | `type("'USD' \| 'EUR' \| 'GBP'")` |
223
+ | Relations | `"unknown"` | `type("unknown").array()` for lists |
224
+
225
+ ### Special Handling
226
+
227
+ - **Optional fields**: Use `?` on the key name (`"name?": "string"`)
228
+ - **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()`
204
231
 
205
232
  ## Differences from prismabox
206
233
 
@@ -239,6 +266,57 @@ pnpm lint
239
266
  pnpm lint:fix
240
267
  ```
241
268
 
269
+ ### Testing
270
+
271
+ This library has a **completely schema-independent test suite** using self-contained test models in `prisma/schema/test-models.prisma`.
272
+
273
+ #### Running Tests
274
+
275
+ ```bash
276
+ # Run all tests
277
+ pnpm test:e2e
278
+
279
+ # Run tests in watch mode
280
+ pnpm test:watch
281
+ ```
282
+
283
+ #### Test Architecture
284
+
285
+ The test suite is designed to be **100% independent** of production schemas:
286
+
287
+ - **Self-Contained Schema** - `prisma/schema/test-models.prisma` contains all models needed for testing
288
+ - **No Production Dependencies** - Tests work even if production schemas don't exist
289
+ - **Comprehensive Coverage** - Test models cover all Prisma types, relations, and generator features
290
+ - **Portable** - Can be used across different projects or extracted as a standalone test suite
291
+
292
+ #### Test Model Categories
293
+
294
+ The test schema includes specialized models for testing:
295
+
296
+ 1. **Basic CRUD** - `TestUser`, `TestPost`, `TestProfile`
297
+ 2. **All Prisma Types** - `TestAllTypes` (String, Int, BigInt, Float, Decimal, Boolean, DateTime, Json, Bytes)
298
+ 3. **Relations** - One-to-one, one-to-many, many-to-many, composite keys
299
+ 4. **Annotations** - `@prisma-arktype.hide`, `@prisma-arktype.input.hide`, `@prisma-arktype.typeOverwrite`
300
+ 5. **Query Operations** - Select, Include, OrderBy schemas
301
+ 6. **Enums** - `TestCurrency`, `TestStatus`
302
+
303
+ #### Adding New Tests
304
+
305
+ 1. **Add test models** to `prisma/schema/test-models.prisma` if needed
306
+ 2. **Update mapping** in `__tests__/config/model-mapping.ts` to reference your models
307
+ 3. **Write tests** using helper functions from `__tests__/utils/test-helpers.ts`
308
+ 4. **Run tests** - `pnpm test`
309
+
310
+ See existing test files for examples.
311
+
312
+ #### Why Schema-Independent?
313
+
314
+ - ✅ Tests never break due to production schema changes
315
+ - ✅ Contributors can run tests without setting up production databases
316
+ - ✅ Tests can be run in isolation (CI/CD, local development)
317
+ - ✅ Clear, documented examples of generator usage
318
+ - ✅ Easy to test new features by adding new test models
319
+
242
320
  ### Publishing
243
321
 
244
322
  This project uses [Changesets](https://github.com/changesets/changesets) for version management and publishing.
package/dist/index.js CHANGED
@@ -40,9 +40,6 @@ function isHiddenInputCreate(annotation) {
40
40
  function isHiddenInputUpdate(annotation) {
41
41
  return annotation.type === "HIDDEN_INPUT_UPDATE";
42
42
  }
43
- function isOptions(annotation) {
44
- return annotation.type === "OPTIONS";
45
- }
46
43
  function isTypeOverwrite(annotation) {
47
44
  return annotation.type === "TYPE_OVERWRITE";
48
45
  }
@@ -69,10 +66,8 @@ const annotationKeys = [
69
66
  ],
70
67
  type: "HIDDEN_INPUT_UPDATE"
71
68
  },
72
- { keys: ["@prisma-arktype.options"], type: "OPTIONS" },
73
69
  { keys: ["@prisma-arktype.typeOverwrite"], type: "TYPE_OVERWRITE" }
74
70
  ];
75
- const prismaArktypeOptionsRegex = /@prisma-arktype\.options\{(.+)\}/;
76
71
  const prismaArktypeTypeOverwriteRegex = /@prisma-arktype\.typeOverwrite=(.+)/;
77
72
  function extractAnnotations(documentation) {
78
73
  const annotations = [];
@@ -84,14 +79,7 @@ function extractAnnotations(documentation) {
84
79
  for (const key of keys) {
85
80
  if (line.includes(key)) {
86
81
  isAnnotation = true;
87
- if (type === "OPTIONS") {
88
- const match = line.match(prismaArktypeOptionsRegex);
89
- if (match && match[1]) {
90
- annotations.push({ type: "OPTIONS", value: match[1] });
91
- } else {
92
- throw new Error(`Invalid OPTIONS annotation: ${line}`);
93
- }
94
- } else if (type === "TYPE_OVERWRITE") {
82
+ if (type === "TYPE_OVERWRITE") {
95
83
  const match = line.match(prismaArktypeTypeOverwriteRegex);
96
84
  if (match && match[1]) {
97
85
  annotations.push({
@@ -123,13 +111,6 @@ function extractAnnotations(documentation) {
123
111
  hiddenInputUpdate: annotations.some(isHiddenInputUpdate)
124
112
  };
125
113
  }
126
- function generateArktypeOptions(annotations) {
127
- const optionsAnnotations = annotations.filter(isOptions);
128
- if (optionsAnnotations.length === 0) {
129
- return "";
130
- }
131
- return `.pipe(${optionsAnnotations.map((a) => a.value).join(", ")})`;
132
- }
133
114
 
134
115
  const primitiveTypes = [
135
116
  "Int",
@@ -146,30 +127,29 @@ function isPrimitivePrismaFieldType(type) {
146
127
  return primitiveTypes.includes(type);
147
128
  }
148
129
  function stringifyPrimitiveType(type, annotations) {
149
- const options = generateArktypeOptions(annotations);
150
130
  switch (type) {
151
131
  case "Int":
152
132
  case "BigInt":
153
- return `"number.integer"${options}`;
133
+ return `"number.integer"`;
154
134
  case "Float":
155
135
  case "Decimal":
156
- return `"number"${options}`;
136
+ return `"number"`;
157
137
  case "String":
158
- return `"string"${options}`;
138
+ return `"string"`;
159
139
  case "DateTime":
160
- return `"Date"${options}`;
140
+ return `"Date"`;
161
141
  case "Json":
162
- return `"unknown"${options}`;
142
+ return `"unknown"`;
163
143
  case "Boolean":
164
- return `"boolean"${options}`;
144
+ return `"boolean"`;
165
145
  case "Bytes":
166
- return `"instanceof Buffer"${options}`;
146
+ return `"instanceof Buffer"`;
167
147
  default:
168
148
  throw new Error(`Unsupported primitive type: ${type}`);
169
149
  }
170
150
  }
171
151
 
172
- function wrapWithArray(input) {
152
+ function wrapPrimitiveWithArray(input) {
173
153
  return `${input}[]`;
174
154
  }
175
155
  function makeUnion(inputModels) {
@@ -190,38 +170,38 @@ function processEnums(enums) {
190
170
  Object.freeze(processedEnums);
191
171
  }
192
172
  function stringifyEnum(enumData) {
193
- const { annotations, hidden } = extractAnnotations(enumData.documentation);
173
+ const { hidden } = extractAnnotations(enumData.documentation);
194
174
  if (hidden) {
195
175
  return;
196
176
  }
197
177
  const values = enumData.values.map((v) => `'${v.name}'`);
198
- const options = generateArktypeOptions(annotations);
199
- return `type("${makeUnion(values)}")${options}`;
178
+ return `type("${makeUnion(values)}")`;
200
179
  }
201
180
 
202
181
  const processedPlain = [];
203
182
  function processPlain(models) {
204
183
  for (const model of models) {
205
- const stringified = stringifyPlain(model);
206
- if (stringified) {
184
+ const result = stringifyPlain(model);
185
+ if (result) {
207
186
  processedPlain.push({
208
187
  name: model.name,
209
- stringified
188
+ stringified: result.stringified,
189
+ enumDependencies: result.enumDependencies
210
190
  });
211
191
  }
212
192
  }
213
193
  Object.freeze(processedPlain);
214
194
  }
215
- const enumMatch$1 = /type\("(.+)"\)/;
216
195
  function stringifyPlain(model, isInputCreate = false, isInputUpdate = false) {
217
196
  const config = getConfig();
218
- const { annotations: modelAnnotations, hidden } = extractAnnotations(
197
+ const { hidden } = extractAnnotations(
219
198
  model.documentation
220
199
  );
221
200
  if (hidden) {
222
201
  return;
223
202
  }
224
203
  const fields = [];
204
+ const enumDependencies = [];
225
205
  for (const field of model.fields) {
226
206
  const {
227
207
  annotations: fieldAnnotations,
@@ -244,21 +224,32 @@ function stringifyPlain(model, isInputCreate = false, isInputUpdate = false) {
244
224
  if (typeOverwrite) {
245
225
  fieldType = typeOverwrite.value;
246
226
  } else if (isPrimitivePrismaFieldType(field.type)) {
247
- fieldType = stringifyPrimitiveType(field.type, fieldAnnotations);
227
+ fieldType = stringifyPrimitiveType(field.type);
248
228
  } else if (field.kind === "enum") {
249
229
  const enumDef = processedEnums.find((e) => e.name === field.type);
250
230
  if (!enumDef) continue;
251
- const match = enumDef.stringified.match(enumMatch$1);
252
- fieldType = match ? `"${match[1]}"` : `"'${field.type}'"`;
231
+ if (!enumDependencies.includes(field.type)) {
232
+ enumDependencies.push(field.type);
233
+ }
234
+ fieldType = field.type;
253
235
  } else {
254
236
  continue;
255
237
  }
238
+ const isEnumType = field.kind === "enum" && !typeOverwrite;
256
239
  if (field.isList) {
257
- fieldType = `"${wrapWithArray(fieldType.slice(1, -1))}"`;
240
+ if (isEnumType) {
241
+ fieldType = `${fieldType}.array()`;
242
+ } else {
243
+ fieldType = `"${wrapPrimitiveWithArray(fieldType.slice(1, -1))}"`;
244
+ }
258
245
  }
259
246
  if (!field.isRequired) {
260
- const inner = fieldType.slice(1, -1);
261
- 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
+ }
262
253
  fieldName += "?";
263
254
  }
264
255
  if (field.hasDefaultValue || isInputUpdate) {
@@ -268,10 +259,12 @@ function stringifyPlain(model, isInputCreate = false, isInputUpdate = false) {
268
259
  }
269
260
  fields.push(`"${fieldName}": ${fieldType}`);
270
261
  }
271
- const options = generateArktypeOptions(modelAnnotations);
272
- return `{
262
+ return {
263
+ stringified: `{
273
264
  ${fields.join(",\n ")}
274
- }${options}`;
265
+ }`,
266
+ enumDependencies
267
+ };
275
268
  }
276
269
  function stringifyPlainInputCreate(model) {
277
270
  return stringifyPlain(model, true, false);
@@ -285,11 +278,12 @@ function processCreate(models) {
285
278
  for (const model of models) {
286
279
  const { hidden } = extractAnnotations(model.documentation);
287
280
  if (hidden) continue;
288
- const stringified = stringifyPlainInputCreate(model);
289
- if (stringified) {
281
+ const result = stringifyPlainInputCreate(model);
282
+ if (result) {
290
283
  processedCreate.push({
291
284
  name: model.name,
292
- stringified
285
+ stringified: result.stringified,
286
+ enumDependencies: result.enumDependencies
293
287
  });
294
288
  }
295
289
  }
@@ -310,7 +304,7 @@ function processInclude(models) {
310
304
  Object.freeze(processedInclude);
311
305
  }
312
306
  function stringifyInclude(model) {
313
- const { annotations: modelAnnotations, hidden } = extractAnnotations(
307
+ const { hidden } = extractAnnotations(
314
308
  model.documentation
315
309
  );
316
310
  if (hidden) {
@@ -327,10 +321,9 @@ function stringifyInclude(model) {
327
321
  if (fields.length <= 1) {
328
322
  return;
329
323
  }
330
- const options = generateArktypeOptions(modelAnnotations);
331
324
  return `{
332
325
  ${fields.join(",\n ")}
333
- }${options}`;
326
+ }`;
334
327
  }
335
328
 
336
329
  const processedOrderBy = [];
@@ -347,7 +340,7 @@ function processOrderBy(models) {
347
340
  Object.freeze(processedOrderBy);
348
341
  }
349
342
  function stringifyOrderBy(model) {
350
- const { annotations: modelAnnotations, hidden } = extractAnnotations(
343
+ const { hidden } = extractAnnotations(
351
344
  model.documentation
352
345
  );
353
346
  if (hidden) {
@@ -361,10 +354,9 @@ function stringifyOrderBy(model) {
361
354
  if (field.kind === "object") continue;
362
355
  fields.push(`"${field.name}?": ${sortOrder}`);
363
356
  }
364
- const options = generateArktypeOptions(modelAnnotations);
365
357
  return `{
366
358
  ${fields.join(",\n ")}
367
- }${options}`;
359
+ }`;
368
360
  }
369
361
 
370
362
  const processedRelations = [];
@@ -383,7 +375,7 @@ function processRelations(models) {
383
375
  Object.freeze(processedRelations);
384
376
  }
385
377
  function stringifyRelations(model) {
386
- const { annotations: modelAnnotations, hidden } = extractAnnotations(
378
+ const { hidden } = extractAnnotations(
387
379
  model.documentation
388
380
  );
389
381
  if (hidden) {
@@ -394,22 +386,22 @@ function stringifyRelations(model) {
394
386
  const { hidden: fieldHidden } = extractAnnotations(field.documentation);
395
387
  if (fieldHidden) continue;
396
388
  if (field.kind !== "object") continue;
397
- let fieldType = `"unknown"`;
389
+ let fieldType;
398
390
  if (field.isList) {
399
- fieldType = `"unknown[]"`;
400
- }
401
- if (!field.isRequired) {
391
+ fieldType = `type("unknown").array()`;
392
+ } else if (!field.isRequired) {
402
393
  fieldType = `"unknown | null"`;
394
+ } else {
395
+ fieldType = `"unknown"`;
403
396
  }
404
397
  fields.push(`"${field.name}": ${fieldType}`);
405
398
  }
406
399
  if (fields.length === 0) {
407
400
  return;
408
401
  }
409
- const options = generateArktypeOptions(modelAnnotations);
410
402
  return `{
411
403
  ${fields.join(",\n ")}
412
- }${options}`;
404
+ }`;
413
405
  }
414
406
  function processRelationsCreate(models) {
415
407
  for (const model of models) {
@@ -424,7 +416,7 @@ function processRelationsCreate(models) {
424
416
  Object.freeze(processedRelationsCreate);
425
417
  }
426
418
  function stringifyRelationsInputCreate(model, allModels) {
427
- const { annotations: modelAnnotations, hidden } = extractAnnotations(
419
+ const { hidden } = extractAnnotations(
428
420
  model.documentation
429
421
  );
430
422
  if (hidden) {
@@ -454,10 +446,9 @@ function stringifyRelationsInputCreate(model, allModels) {
454
446
  if (fields.length === 0) {
455
447
  return;
456
448
  }
457
- const options = generateArktypeOptions(modelAnnotations);
458
449
  return `{
459
450
  ${fields.join(",\n ")}
460
- }${options}`;
451
+ }`;
461
452
  }
462
453
  function processRelationsUpdate(models) {
463
454
  for (const model of models) {
@@ -472,7 +463,7 @@ function processRelationsUpdate(models) {
472
463
  Object.freeze(processedRelationsUpdate);
473
464
  }
474
465
  function stringifyRelationsInputUpdate(model, allModels) {
475
- const { annotations: modelAnnotations, hidden } = extractAnnotations(
466
+ const { hidden } = extractAnnotations(
476
467
  model.documentation
477
468
  );
478
469
  if (hidden) {
@@ -498,12 +489,14 @@ function stringifyRelationsInputUpdate(model, allModels) {
498
489
  }
499
490
  let fieldType;
500
491
  if (field.isList) {
501
- fieldType = `{ "connect"?: { "id": ${idType} }[], "disconnect"?: { "id": ${idType} }[] }`;
492
+ const connectType = `type({ "id": ${idType} }).array()`;
493
+ const disconnectType = `type({ "id": ${idType} }).array()`;
494
+ fieldType = `{ "connect?": ${connectType}, "disconnect?": ${disconnectType} }`;
502
495
  } else {
503
496
  if (field.isRequired) {
504
497
  fieldType = `{ "connect": { "id": ${idType} } }`;
505
498
  } else {
506
- fieldType = `{ "connect"?: { "id": ${idType} }, "disconnect"?: "boolean" }`;
499
+ fieldType = `{ "connect?": { "id": ${idType} }, "disconnect?": "boolean" }`;
507
500
  }
508
501
  }
509
502
  fields.push(`"${field.name}?": ${fieldType}`);
@@ -511,10 +504,9 @@ function stringifyRelationsInputUpdate(model, allModels) {
511
504
  if (fields.length === 0) {
512
505
  return;
513
506
  }
514
- const options = generateArktypeOptions(modelAnnotations);
515
507
  return `{
516
508
  ${fields.join(",\n ")}
517
- }${options}`;
509
+ }`;
518
510
  }
519
511
 
520
512
  const processedSelect = [];
@@ -531,7 +523,7 @@ function processSelect(models) {
531
523
  Object.freeze(processedSelect);
532
524
  }
533
525
  function stringifySelect(model) {
534
- const { annotations: modelAnnotations, hidden } = extractAnnotations(
526
+ const { hidden } = extractAnnotations(
535
527
  model.documentation
536
528
  );
537
529
  if (hidden) {
@@ -544,10 +536,9 @@ function stringifySelect(model) {
544
536
  fields.push(`"${field.name}?": "boolean"`);
545
537
  }
546
538
  fields.push(`"_count?": "boolean"`);
547
- const options = generateArktypeOptions(modelAnnotations);
548
539
  return `{
549
540
  ${fields.join(",\n ")}
550
- }${options}`;
541
+ }`;
551
542
  }
552
543
 
553
544
  const processedUpdate = [];
@@ -555,11 +546,12 @@ function processUpdate(models) {
555
546
  for (const model of models) {
556
547
  const { hidden } = extractAnnotations(model.documentation);
557
548
  if (hidden) continue;
558
- const stringified = stringifyPlainInputUpdate(model);
559
- if (stringified) {
549
+ const result = stringifyPlainInputUpdate(model);
550
+ if (result) {
560
551
  processedUpdate.push({
561
552
  name: model.name,
562
- stringified
553
+ stringified: result.stringified,
554
+ enumDependencies: result.enumDependencies
563
555
  });
564
556
  }
565
557
  }
@@ -570,33 +562,35 @@ const processedWhere = [];
570
562
  const processedWhereUnique = [];
571
563
  function processWhere(models) {
572
564
  for (const model of models) {
573
- const stringified = stringifyWhere(model);
574
- if (stringified) {
565
+ const result = stringifyWhere(model);
566
+ if (result) {
575
567
  processedWhere.push({
576
568
  name: model.name,
577
- stringified
569
+ stringified: result.stringified,
570
+ enumDependencies: result.enumDependencies
578
571
  });
579
572
  }
580
- const stringifiedUnique = stringifyWhereUnique(model);
581
- if (stringifiedUnique) {
573
+ const uniqueResult = stringifyWhereUnique(model);
574
+ if (uniqueResult) {
582
575
  processedWhereUnique.push({
583
576
  name: model.name,
584
- stringified: stringifiedUnique
577
+ stringified: uniqueResult.stringified,
578
+ enumDependencies: uniqueResult.enumDependencies
585
579
  });
586
580
  }
587
581
  }
588
582
  Object.freeze(processedWhere);
589
583
  Object.freeze(processedWhereUnique);
590
584
  }
591
- const enumMatch = /type\("(.+)"\)/;
592
585
  function stringifyWhere(model) {
593
- const { annotations: modelAnnotations, hidden } = extractAnnotations(
586
+ const { hidden } = extractAnnotations(
594
587
  model.documentation
595
588
  );
596
589
  if (hidden) {
597
590
  return;
598
591
  }
599
592
  const fields = [];
593
+ const enumDependencies = [];
600
594
  for (const field of model.fields) {
601
595
  const { annotations: fieldAnnotations, hidden: fieldHidden } = extractAnnotations(field.documentation);
602
596
  if (fieldHidden) continue;
@@ -604,36 +598,46 @@ function stringifyWhere(model) {
604
598
  const typeOverwrite = fieldAnnotations.find(isTypeOverwrite);
605
599
  let fieldType;
606
600
  if (typeOverwrite) {
607
- fieldType = `"${typeOverwrite.value}"`;
601
+ fieldType = typeOverwrite.value;
608
602
  } else if (isPrimitivePrismaFieldType(field.type)) {
609
- fieldType = stringifyPrimitiveType(field.type, fieldAnnotations);
603
+ fieldType = stringifyPrimitiveType(field.type);
610
604
  } else if (field.kind === "enum") {
611
605
  const enumDef = processedEnums.find((e) => e.name === field.type);
612
606
  if (!enumDef) continue;
613
- const match = enumDef.stringified.match(enumMatch);
614
- fieldType = match ? `"${match[1]}"` : `"'${field.type}'"`;
607
+ if (!enumDependencies.includes(field.type)) {
608
+ enumDependencies.push(field.type);
609
+ }
610
+ fieldType = field.type;
615
611
  } else {
616
612
  continue;
617
613
  }
614
+ const isEnumType = field.kind === "enum" && !typeOverwrite;
618
615
  if (field.isList) {
619
- const inner = fieldType.slice(1, -1);
620
- fieldType = `"${wrapWithArray(inner)}"`;
616
+ if (isEnumType) {
617
+ fieldType = `${fieldType}.array()`;
618
+ } else {
619
+ const inner = fieldType.slice(1, -1);
620
+ fieldType = `"${wrapPrimitiveWithArray(inner)}"`;
621
+ }
621
622
  }
622
623
  fields.push(`"${field.name}?": ${fieldType}`);
623
624
  }
624
- const options = generateArktypeOptions(modelAnnotations);
625
- return `{
625
+ return {
626
+ stringified: `{
626
627
  ${fields.join(",\n ")}
627
- }${options}`;
628
+ }`,
629
+ enumDependencies
630
+ };
628
631
  }
629
632
  function stringifyWhereUnique(model) {
630
- const { annotations: modelAnnotations, hidden } = extractAnnotations(
633
+ const { hidden } = extractAnnotations(
631
634
  model.documentation
632
635
  );
633
636
  if (hidden) {
634
637
  return;
635
638
  }
636
639
  const fields = [];
640
+ const enumDependencies = [];
637
641
  for (const field of model.fields) {
638
642
  const { annotations: fieldAnnotations, hidden: fieldHidden } = extractAnnotations(field.documentation);
639
643
  if (fieldHidden) continue;
@@ -642,14 +646,16 @@ function stringifyWhereUnique(model) {
642
646
  const typeOverwrite = fieldAnnotations.find(isTypeOverwrite);
643
647
  let fieldType;
644
648
  if (typeOverwrite) {
645
- fieldType = `"${typeOverwrite.value}"`;
649
+ fieldType = typeOverwrite.value;
646
650
  } else if (isPrimitivePrismaFieldType(field.type)) {
647
- fieldType = stringifyPrimitiveType(field.type, fieldAnnotations);
651
+ fieldType = stringifyPrimitiveType(field.type);
648
652
  } else if (field.kind === "enum") {
649
653
  const enumDef = processedEnums.find((e) => e.name === field.type);
650
654
  if (!enumDef) continue;
651
- const match = enumDef.stringified.match(enumMatch);
652
- fieldType = match ? `"${match[1]}"` : `"'${field.type}'"`;
655
+ if (!enumDependencies.includes(field.type)) {
656
+ enumDependencies.push(field.type);
657
+ }
658
+ fieldType = field.type;
653
659
  } else {
654
660
  continue;
655
661
  }
@@ -658,15 +664,24 @@ function stringifyWhereUnique(model) {
658
664
  if (fields.length === 0) {
659
665
  return;
660
666
  }
661
- const options = generateArktypeOptions(modelAnnotations);
662
- return `{
667
+ return {
668
+ stringified: `{
663
669
  ${fields.join(",\n ")}
664
- }${options}`;
670
+ }`,
671
+ enumDependencies
672
+ };
665
673
  }
666
674
 
667
675
  async function format(input) {
668
676
  return input;
669
677
  }
678
+ function generateEnumImports(enumDependencies) {
679
+ if (!enumDependencies || enumDependencies.length === 0) {
680
+ return "";
681
+ }
682
+ return `${enumDependencies.map((enumName) => `import { ${enumName} } from "./${enumName}";`).join("\n")}
683
+ `;
684
+ }
670
685
  function mapAllModelsForWrite(processedEnums, processedPlain, processedRelations, processedWhere, processedWhereUnique, processedCreate, processedUpdate, processedRelationsCreate, processedRelationsUpdate, processedSelect, processedInclude, processedOrderBy) {
671
686
  const config = getConfig();
672
687
  const modelMap = /* @__PURE__ */ new Map();
@@ -679,7 +694,8 @@ function mapAllModelsForWrite(processedEnums, processedPlain, processedRelations
679
694
  modelMap.set(model.name, content);
680
695
  }
681
696
  for (const model of processedPlain) {
682
- const content = `${arktypeImport}export const ${model.name}Plain = type(${model.stringified});
697
+ const enumImports = generateEnumImports(model.enumDependencies);
698
+ const content = `${arktypeImport}${enumImports}export const ${model.name}Plain = type(${model.stringified});
683
699
  `;
684
700
  modelMap.set(`${model.name}Plain`, content);
685
701
  }
@@ -706,22 +722,26 @@ export const ${plain.name} = ${plain.name}Plain;
706
722
  }
707
723
  }
708
724
  for (const model of processedWhere) {
709
- const content = `${arktypeImport}export const ${model.name}Where = type(${model.stringified});
725
+ const enumImports = generateEnumImports(model.enumDependencies);
726
+ const content = `${arktypeImport}${enumImports}export const ${model.name}Where = type(${model.stringified});
710
727
  `;
711
728
  modelMap.set(`${model.name}Where`, content);
712
729
  }
713
730
  for (const model of processedWhereUnique) {
714
- const content = `${arktypeImport}export const ${model.name}WhereUnique = type(${model.stringified});
731
+ const enumImports = generateEnumImports(model.enumDependencies);
732
+ const content = `${arktypeImport}${enumImports}export const ${model.name}WhereUnique = type(${model.stringified});
715
733
  `;
716
734
  modelMap.set(`${model.name}WhereUnique`, content);
717
735
  }
718
736
  for (const model of processedCreate) {
719
- const content = `${arktypeImport}export const ${model.name}Create = type(${model.stringified});
737
+ const enumImports = generateEnumImports(model.enumDependencies);
738
+ const content = `${arktypeImport}${enumImports}export const ${model.name}Create = type(${model.stringified});
720
739
  `;
721
740
  modelMap.set(`${model.name}Create`, content);
722
741
  }
723
742
  for (const model of processedUpdate) {
724
- const content = `${arktypeImport}export const ${model.name}Update = type(${model.stringified});
743
+ const enumImports = generateEnumImports(model.enumDependencies);
744
+ const content = `${arktypeImport}${enumImports}export const ${model.name}Update = type(${model.stringified});
725
745
  `;
726
746
  modelMap.set(`${model.name}Update`, content);
727
747
  }
@@ -773,10 +793,6 @@ async function write(processedEnums, processedPlain, processedRelations, process
773
793
  const filePath = join(config.output, `${name}.ts`);
774
794
  writePromises.push(writeFile(filePath, await format(content)));
775
795
  }
776
- const barrelExports = Array.from(modelMap.keys()).map((name) => `export * from "./${name}";`).join("\n");
777
- writePromises.push(
778
- writeFile(join(config.output, "index.ts"), await format(barrelExports))
779
- );
780
796
  await Promise.all(writePromises);
781
797
  }
782
798
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "prisma-arktype",
3
- "version": "2.0.0",
3
+ "version": "2.2.0",
4
4
  "description": "Generate ArkType schemas from your Prisma schema",
5
5
  "type": "module",
6
6
  "main": "./dist/index.js",
@@ -40,19 +40,19 @@
40
40
  "arktype": "^2.1.25"
41
41
  },
42
42
  "devDependencies": {
43
- "@biomejs/biome": "^2.3.3",
44
- "@changesets/cli": "^2.29.7",
43
+ "@biomejs/biome": "2.3.11",
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",
48
- "@tilli-pro/biome": "^0.8.0",
48
+ "@tilli-pro/biome": "0.9.0",
49
49
  "@types/node": "^24.10.0",
50
50
  "lefthook": "^2.0.2",
51
51
  "pkgroll": "^2.20.1",
52
- "prisma": "^6.18.0",
53
- "tsx": "^4.20.6",
52
+ "prisma": "7.2.0",
53
+ "tsx": "^4.21.0",
54
54
  "typescript": "^5.9.3",
55
- "vitest": "^2.1.9"
55
+ "vitest": "^4.0.16"
56
56
  },
57
57
  "scripts": {
58
58
  "build": "pkgroll",