prisma-zero 0.1.1 → 0.1.3

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -71,6 +71,32 @@ export const userTable = table('User')
71
71
  .primaryKey('id');
72
72
  ```
73
73
 
74
+ ## Postgres Time Support
75
+
76
+ Prisma Postgres `DateTime` native types continue to map to Zero `number()` columns, including `@db.Time` and `@db.Timetz`:
77
+
78
+ ```prisma
79
+ model StoreHours {
80
+ id String @id @default(uuid())
81
+ opensAt DateTime @db.Time(6)
82
+ closesAt DateTime @db.Timetz(6)
83
+ }
84
+ ```
85
+
86
+ The generated Zero schema will include:
87
+
88
+ ```ts
89
+ export const storeHoursTable = table('StoreHours')
90
+ .columns({
91
+ id: string(),
92
+ opensAt: number(),
93
+ closesAt: number(),
94
+ })
95
+ .primaryKey('id');
96
+ ```
97
+
98
+ This generator intentionally keeps `time` and `timetz` fields as plain `number()` columns so they match Zero's existing date/time handling. If your app uses `@db.Time` or `@db.Timetz`, make sure your installed `@rocicorp/zero` version includes upstream support for those Postgres types (`0.26.0+`).
99
+
74
100
  ### Supported Array Types
75
101
 
76
102
  - **Scalar arrays**: `String[]`, `Int[]`, `Float[]`, `Boolean[]`, `DateTime[]`, `BigInt[]`, `Decimal[]`
@@ -39,7 +39,7 @@ var import_promises = require("fs/promises");
39
39
  var import_path = require("path");
40
40
 
41
41
  // package.json
42
- var version = "0.1.1";
42
+ var version = "0.1.3";
43
43
 
44
44
  // src/generators/code-generator.ts
45
45
  function generateImports(schema, config) {
@@ -241,21 +241,27 @@ var TYPE_MAP = {
241
241
  BigInt: "number()",
242
242
  Decimal: "number()"
243
243
  };
244
+ var ARRAY_TS_TYPE_MAP = {
245
+ String: "string[]",
246
+ Boolean: "boolean[]",
247
+ Int: "number[]",
248
+ Float: "number[]",
249
+ DateTime: "number[]",
250
+ Json: "any[]",
251
+ BigInt: "number[]",
252
+ Decimal: "number[]"
253
+ };
244
254
  function mapPrismaTypeToZero(field) {
255
+ if (field.kind === "unsupported") {
256
+ return null;
257
+ }
245
258
  const isOptional = !field.isRequired;
246
259
  const mappedName = field.dbName && field.dbName !== field.name ? field.dbName : null;
247
260
  if (field.isList) {
248
- const tsTypeMap = {
249
- String: "string[]",
250
- Boolean: "boolean[]",
251
- Int: "number[]",
252
- Float: "number[]",
253
- DateTime: "number[]",
254
- Json: "any[]",
255
- BigInt: "number[]",
256
- Decimal: "number[]"
257
- };
258
- const tsType = field.kind === "enum" ? `${field.type}[]` : tsTypeMap[field.type] || "any[]";
261
+ const tsType = field.kind === "enum" ? `${field.type}[]` : ARRAY_TS_TYPE_MAP[field.type];
262
+ if (!tsType) {
263
+ return null;
264
+ }
259
265
  return {
260
266
  type: `json<${tsType}>()`,
261
267
  isOptional,
@@ -269,7 +275,13 @@ function mapPrismaTypeToZero(field) {
269
275
  mappedName
270
276
  };
271
277
  }
272
- const baseType = TYPE_MAP[field.type] || "string()";
278
+ if (field.kind !== "scalar") {
279
+ return null;
280
+ }
281
+ const baseType = TYPE_MAP[field.type];
282
+ if (!baseType) {
283
+ return null;
284
+ }
273
285
  return {
274
286
  type: baseType,
275
287
  isOptional,
@@ -337,6 +349,12 @@ function createImplicitManyToManyModel(model1, model2, relationName, config) {
337
349
  }
338
350
  const columnAType = mapPrismaTypeToZero(idFieldA);
339
351
  const columnBType = mapPrismaTypeToZero(idFieldB);
352
+ if (!columnAType || !columnBType) {
353
+ const unsupportedModel = !columnAType ? modelA : modelB;
354
+ throw new Error(
355
+ `Implicit relation ${relationName ?? "unknown"}: Model ${unsupportedModel.name} has an unsupported @id field.`
356
+ );
357
+ }
340
358
  return {
341
359
  tableName,
342
360
  originalTableName,
@@ -365,6 +383,14 @@ function createImplicitManyToManyModel(model1, model2, relationName, config) {
365
383
  }
366
384
  function mapRelationships(model, dmmf, config) {
367
385
  const relationships = {};
386
+ const isSupportedField = (target, fieldName) => {
387
+ const field = target.fields.find((f) => f.name === fieldName);
388
+ if (!field) {
389
+ return true;
390
+ }
391
+ return mapPrismaTypeToZero(field) !== null;
392
+ };
393
+ const areFieldsSupported = (target, fieldNames) => fieldNames.every((fieldName) => isSupportedField(target, fieldName));
368
394
  model.fields.filter((field) => field.relationName).forEach((field) => {
369
395
  var _a, _b, _c, _d, _e, _f, _g;
370
396
  const targetModel = dmmf.datamodel.models.find(
@@ -399,17 +425,25 @@ function mapRelationships(model, dmmf, config) {
399
425
  }
400
426
  const isSelfReferential = model.name === targetModel.name;
401
427
  const isModelA = isSelfReferential ? backReference ? field.name.localeCompare(backReference.name) < 0 : true : model.name === modelA.name;
428
+ const sourceField = [((_b = model.fields.find((f) => f.isId)) == null ? void 0 : _b.name) || "id"];
429
+ const destField = [isModelA ? "A" : "B"];
430
+ const targetDestField = [
431
+ ((_c = targetModel.fields.find((f) => f.isId)) == null ? void 0 : _c.name) || "id"
432
+ ];
433
+ if (!areFieldsSupported(model, sourceField) || !areFieldsSupported(targetModel, targetDestField)) {
434
+ return;
435
+ }
402
436
  relationships[field.name] = {
403
437
  type: "many",
404
438
  chain: [
405
439
  {
406
- sourceField: [((_b = model.fields.find((f) => f.isId)) == null ? void 0 : _b.name) || "id"],
407
- destField: [isModelA ? "A" : "B"],
440
+ sourceField,
441
+ destField,
408
442
  destSchema: getZeroTableName(joinTableName)
409
443
  },
410
444
  {
411
445
  sourceField: [isModelA ? "B" : "A"],
412
- destField: [((_c = targetModel.fields.find((f) => f.isId)) == null ? void 0 : _c.name) || "id"],
446
+ destField: targetDestField,
413
447
  destSchema: getZeroTableName(targetModel.name)
414
448
  }
415
449
  ]
@@ -419,6 +453,9 @@ function mapRelationships(model, dmmf, config) {
419
453
  const primaryKeyFields = ((_e = model.primaryKey) == null ? void 0 : _e.fields) || (idField ? [idField] : []);
420
454
  const sourceFields = ensureStringArray(primaryKeyFields);
421
455
  const destFields = (backReference == null ? void 0 : backReference.relationFromFields) ? ensureStringArray(backReference.relationFromFields) : [];
456
+ if (!areFieldsSupported(model, sourceFields) || !areFieldsSupported(targetModel, destFields)) {
457
+ return;
458
+ }
422
459
  relationships[field.name] = {
423
460
  sourceField: sourceFields,
424
461
  destField: destFields,
@@ -436,6 +473,9 @@ function mapRelationships(model, dmmf, config) {
436
473
  sourceFields = backReference.relationToFields ? ensureStringArray(backReference.relationToFields) : [];
437
474
  destFields = ensureStringArray(backReference.relationFromFields);
438
475
  }
476
+ if (!areFieldsSupported(model, sourceFields) || !areFieldsSupported(targetModel, destFields)) {
477
+ return;
478
+ }
439
479
  relationships[field.name] = {
440
480
  sourceField: sourceFields,
441
481
  destField: destFields,
@@ -450,13 +490,29 @@ function mapModel(model, dmmf, config) {
450
490
  var _a, _b;
451
491
  const columns = {};
452
492
  model.fields.filter((field) => !field.relationName).forEach((field) => {
453
- columns[field.name] = mapPrismaTypeToZero(field);
493
+ const mapping = mapPrismaTypeToZero(field);
494
+ if (!mapping) {
495
+ return;
496
+ }
497
+ columns[field.name] = mapping;
454
498
  });
455
499
  const idField = (_a = model.fields.find((f) => f.isId)) == null ? void 0 : _a.name;
456
500
  const primaryKey = ((_b = model.primaryKey) == null ? void 0 : _b.fields) || (idField ? [idField] : []);
457
501
  if (!primaryKey[0]) {
458
502
  throw new Error(`No primary key found for ${model.name}`);
459
503
  }
504
+ const unsupportedPrimaryKeys = primaryKey.filter((fieldName) => {
505
+ const field = model.fields.find((f) => f.name === fieldName);
506
+ if (!field) {
507
+ return false;
508
+ }
509
+ return mapPrismaTypeToZero(field) === null;
510
+ });
511
+ if (unsupportedPrimaryKeys.length > 0) {
512
+ throw new Error(
513
+ `Primary key field(s) ${unsupportedPrimaryKeys.join(", ")} in ${model.name} are not supported by Zero.`
514
+ );
515
+ }
460
516
  const databaseTableName = getTableNameFromModel(model);
461
517
  const tableName = getTableName(model.name, config);
462
518
  const shouldRemap = tableName !== databaseTableName;
package/dist/generator.js CHANGED
@@ -6,7 +6,7 @@ import { mkdir, writeFile } from "fs/promises";
6
6
  import { join } from "path";
7
7
 
8
8
  // package.json
9
- var version = "0.1.1";
9
+ var version = "0.1.3";
10
10
 
11
11
  // src/generators/code-generator.ts
12
12
  function generateImports(schema, config) {
@@ -208,21 +208,27 @@ var TYPE_MAP = {
208
208
  BigInt: "number()",
209
209
  Decimal: "number()"
210
210
  };
211
+ var ARRAY_TS_TYPE_MAP = {
212
+ String: "string[]",
213
+ Boolean: "boolean[]",
214
+ Int: "number[]",
215
+ Float: "number[]",
216
+ DateTime: "number[]",
217
+ Json: "any[]",
218
+ BigInt: "number[]",
219
+ Decimal: "number[]"
220
+ };
211
221
  function mapPrismaTypeToZero(field) {
222
+ if (field.kind === "unsupported") {
223
+ return null;
224
+ }
212
225
  const isOptional = !field.isRequired;
213
226
  const mappedName = field.dbName && field.dbName !== field.name ? field.dbName : null;
214
227
  if (field.isList) {
215
- const tsTypeMap = {
216
- String: "string[]",
217
- Boolean: "boolean[]",
218
- Int: "number[]",
219
- Float: "number[]",
220
- DateTime: "number[]",
221
- Json: "any[]",
222
- BigInt: "number[]",
223
- Decimal: "number[]"
224
- };
225
- const tsType = field.kind === "enum" ? `${field.type}[]` : tsTypeMap[field.type] || "any[]";
228
+ const tsType = field.kind === "enum" ? `${field.type}[]` : ARRAY_TS_TYPE_MAP[field.type];
229
+ if (!tsType) {
230
+ return null;
231
+ }
226
232
  return {
227
233
  type: `json<${tsType}>()`,
228
234
  isOptional,
@@ -236,7 +242,13 @@ function mapPrismaTypeToZero(field) {
236
242
  mappedName
237
243
  };
238
244
  }
239
- const baseType = TYPE_MAP[field.type] || "string()";
245
+ if (field.kind !== "scalar") {
246
+ return null;
247
+ }
248
+ const baseType = TYPE_MAP[field.type];
249
+ if (!baseType) {
250
+ return null;
251
+ }
240
252
  return {
241
253
  type: baseType,
242
254
  isOptional,
@@ -304,6 +316,12 @@ function createImplicitManyToManyModel(model1, model2, relationName, config) {
304
316
  }
305
317
  const columnAType = mapPrismaTypeToZero(idFieldA);
306
318
  const columnBType = mapPrismaTypeToZero(idFieldB);
319
+ if (!columnAType || !columnBType) {
320
+ const unsupportedModel = !columnAType ? modelA : modelB;
321
+ throw new Error(
322
+ `Implicit relation ${relationName ?? "unknown"}: Model ${unsupportedModel.name} has an unsupported @id field.`
323
+ );
324
+ }
307
325
  return {
308
326
  tableName,
309
327
  originalTableName,
@@ -332,6 +350,14 @@ function createImplicitManyToManyModel(model1, model2, relationName, config) {
332
350
  }
333
351
  function mapRelationships(model, dmmf, config) {
334
352
  const relationships = {};
353
+ const isSupportedField = (target, fieldName) => {
354
+ const field = target.fields.find((f) => f.name === fieldName);
355
+ if (!field) {
356
+ return true;
357
+ }
358
+ return mapPrismaTypeToZero(field) !== null;
359
+ };
360
+ const areFieldsSupported = (target, fieldNames) => fieldNames.every((fieldName) => isSupportedField(target, fieldName));
335
361
  model.fields.filter((field) => field.relationName).forEach((field) => {
336
362
  var _a, _b, _c, _d, _e, _f, _g;
337
363
  const targetModel = dmmf.datamodel.models.find(
@@ -366,17 +392,25 @@ function mapRelationships(model, dmmf, config) {
366
392
  }
367
393
  const isSelfReferential = model.name === targetModel.name;
368
394
  const isModelA = isSelfReferential ? backReference ? field.name.localeCompare(backReference.name) < 0 : true : model.name === modelA.name;
395
+ const sourceField = [((_b = model.fields.find((f) => f.isId)) == null ? void 0 : _b.name) || "id"];
396
+ const destField = [isModelA ? "A" : "B"];
397
+ const targetDestField = [
398
+ ((_c = targetModel.fields.find((f) => f.isId)) == null ? void 0 : _c.name) || "id"
399
+ ];
400
+ if (!areFieldsSupported(model, sourceField) || !areFieldsSupported(targetModel, targetDestField)) {
401
+ return;
402
+ }
369
403
  relationships[field.name] = {
370
404
  type: "many",
371
405
  chain: [
372
406
  {
373
- sourceField: [((_b = model.fields.find((f) => f.isId)) == null ? void 0 : _b.name) || "id"],
374
- destField: [isModelA ? "A" : "B"],
407
+ sourceField,
408
+ destField,
375
409
  destSchema: getZeroTableName(joinTableName)
376
410
  },
377
411
  {
378
412
  sourceField: [isModelA ? "B" : "A"],
379
- destField: [((_c = targetModel.fields.find((f) => f.isId)) == null ? void 0 : _c.name) || "id"],
413
+ destField: targetDestField,
380
414
  destSchema: getZeroTableName(targetModel.name)
381
415
  }
382
416
  ]
@@ -386,6 +420,9 @@ function mapRelationships(model, dmmf, config) {
386
420
  const primaryKeyFields = ((_e = model.primaryKey) == null ? void 0 : _e.fields) || (idField ? [idField] : []);
387
421
  const sourceFields = ensureStringArray(primaryKeyFields);
388
422
  const destFields = (backReference == null ? void 0 : backReference.relationFromFields) ? ensureStringArray(backReference.relationFromFields) : [];
423
+ if (!areFieldsSupported(model, sourceFields) || !areFieldsSupported(targetModel, destFields)) {
424
+ return;
425
+ }
389
426
  relationships[field.name] = {
390
427
  sourceField: sourceFields,
391
428
  destField: destFields,
@@ -403,6 +440,9 @@ function mapRelationships(model, dmmf, config) {
403
440
  sourceFields = backReference.relationToFields ? ensureStringArray(backReference.relationToFields) : [];
404
441
  destFields = ensureStringArray(backReference.relationFromFields);
405
442
  }
443
+ if (!areFieldsSupported(model, sourceFields) || !areFieldsSupported(targetModel, destFields)) {
444
+ return;
445
+ }
406
446
  relationships[field.name] = {
407
447
  sourceField: sourceFields,
408
448
  destField: destFields,
@@ -417,13 +457,29 @@ function mapModel(model, dmmf, config) {
417
457
  var _a, _b;
418
458
  const columns = {};
419
459
  model.fields.filter((field) => !field.relationName).forEach((field) => {
420
- columns[field.name] = mapPrismaTypeToZero(field);
460
+ const mapping = mapPrismaTypeToZero(field);
461
+ if (!mapping) {
462
+ return;
463
+ }
464
+ columns[field.name] = mapping;
421
465
  });
422
466
  const idField = (_a = model.fields.find((f) => f.isId)) == null ? void 0 : _a.name;
423
467
  const primaryKey = ((_b = model.primaryKey) == null ? void 0 : _b.fields) || (idField ? [idField] : []);
424
468
  if (!primaryKey[0]) {
425
469
  throw new Error(`No primary key found for ${model.name}`);
426
470
  }
471
+ const unsupportedPrimaryKeys = primaryKey.filter((fieldName) => {
472
+ const field = model.fields.find((f) => f.name === fieldName);
473
+ if (!field) {
474
+ return false;
475
+ }
476
+ return mapPrismaTypeToZero(field) === null;
477
+ });
478
+ if (unsupportedPrimaryKeys.length > 0) {
479
+ throw new Error(
480
+ `Primary key field(s) ${unsupportedPrimaryKeys.join(", ")} in ${model.name} are not supported by Zero.`
481
+ );
482
+ }
427
483
  const databaseTableName = getTableNameFromModel(model);
428
484
  const tableName = getTableName(model.name, config);
429
485
  const shouldRemap = tableName !== databaseTableName;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "prisma-zero",
3
- "version": "0.1.1",
3
+ "version": "0.1.3",
4
4
  "description": "Generate Zero schemas from Prisma ORM schemas",
5
5
  "type": "module",
6
6
  "bin": {
@@ -36,27 +36,27 @@
36
36
  "url": "https://bugs.rocicorp.dev"
37
37
  },
38
38
  "dependencies": {
39
- "@prisma/generator-helper": "^7.1.0",
39
+ "@prisma/generator-helper": "^7.5.0",
40
40
  "camelcase": "^9.0.0"
41
41
  },
42
42
  "devDependencies": {
43
43
  "@rocicorp/prettier-config": "^0.4.0",
44
- "@rocicorp/zero": "0.25.2",
45
- "@types/node": "^24.10.1",
46
- "@types/pg": "^8.15.6",
44
+ "@rocicorp/zero": "1.0.0",
45
+ "@types/node": "^25.5.0",
46
+ "@types/pg": "^8.20.0",
47
47
  "@types/pluralize": "^0.0.33",
48
48
  "@types/ws": "^8.18.1",
49
- "@vitest/browser": "^4.0.15",
50
- "@vitest/coverage-v8": "4.0.15",
51
- "@vitest/ui": "^4.0.15",
52
- "oxlint": "^1.31.0",
53
- "oxlint-tsgolint": "^0.8.3",
54
- "prettier": "^3.7.4",
49
+ "@vitest/browser": "^4.1.1",
50
+ "@vitest/coverage-v8": "4.1.1",
51
+ "@vitest/ui": "^4.1.1",
52
+ "oxlint": "^1.57.0",
53
+ "oxlint-tsgolint": "^0.17.4",
54
+ "prettier": "^3.8.1",
55
55
  "tsup": "^8.5.1",
56
56
  "tsx": "^4.21.0",
57
- "typescript": "^5.9.3",
58
- "vite-tsconfig-paths": "^5.1.4",
59
- "vitest": "^4.0.15"
57
+ "typescript": "^6.0.2",
58
+ "vite-tsconfig-paths": "^6.1.1",
59
+ "vitest": "^4.1.1"
60
60
  },
61
61
  "prettier": "@rocicorp/prettier-config",
62
62
  "scripts": {