@farming-labs/orm 0.0.8 → 0.0.9

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/dist/index.cjs CHANGED
@@ -187,6 +187,38 @@ function datetime() {
187
187
  }
188
188
 
189
189
  // src/manifest.ts
190
+ function createConstraintName(table, columns, suffix) {
191
+ const base = [table, ...columns].join("_").replace(/[^a-zA-Z0-9_]+/g, "_").replace(/_+/g, "_").replace(/^_+|_+$/g, "").toLowerCase();
192
+ return `${base}_${suffix}`;
193
+ }
194
+ function normalizeConstraints(modelName, table, fields, constraints) {
195
+ const normalize = (entries, unique) => (entries ?? []).map((entry) => {
196
+ if (!entry.length) {
197
+ throw new Error(
198
+ `Model "${modelName}" defines an empty ${unique ? "unique" : "index"} constraint.`
199
+ );
200
+ }
201
+ const columns = entry.map((fieldName) => {
202
+ const field = fields[fieldName];
203
+ if (!field) {
204
+ throw new Error(
205
+ `Model "${modelName}" defines a ${unique ? "unique" : "index"} constraint on unknown field "${fieldName}".`
206
+ );
207
+ }
208
+ return field.column;
209
+ });
210
+ return {
211
+ name: createConstraintName(table, columns, unique ? "unique" : "idx"),
212
+ fields: [...entry],
213
+ columns,
214
+ unique
215
+ };
216
+ });
217
+ return {
218
+ unique: normalize(constraints.unique, true),
219
+ indexes: normalize(constraints.indexes, false)
220
+ };
221
+ }
190
222
  function createManifest(schema) {
191
223
  const models = Object.fromEntries(
192
224
  Object.entries(schema.models).map(
@@ -214,7 +246,13 @@ function createManifest(schema) {
214
246
  table: definition.table,
215
247
  description: definition.description,
216
248
  fields,
217
- relations: definition.relations
249
+ relations: definition.relations,
250
+ constraints: normalizeConstraints(
251
+ name,
252
+ definition.table,
253
+ fields,
254
+ definition.constraints
255
+ )
218
256
  }
219
257
  ];
220
258
  }
@@ -226,6 +264,7 @@ function createManifest(schema) {
226
264
  // src/generators.ts
227
265
  var capitalize = (value) => value.charAt(0).toUpperCase() + value.slice(1);
228
266
  var pluralize = (value) => value.endsWith("s") ? value : `${value}s`;
267
+ var camelize = (value) => value.replace(/[^a-zA-Z0-9]+(.)/g, (_, char) => char.toUpperCase()).replace(/^[^a-zA-Z]+/, "").replace(/^[A-Z]/, (char) => char.toLowerCase());
229
268
  function resolveReferenceTarget(manifest, model2, foreignKey, fallbackTarget) {
230
269
  const reference = model2.fields[foreignKey]?.references;
231
270
  if (!reference) {
@@ -260,6 +299,16 @@ function prismaType(field) {
260
299
  return "DateTime";
261
300
  }
262
301
  }
302
+ function drizzleConstraintProperty(constraint) {
303
+ return camelize(constraint.name) || "constraint";
304
+ }
305
+ function constrainedFields(model2) {
306
+ return new Set(
307
+ [...model2.constraints.unique, ...model2.constraints.indexes].flatMap(
308
+ (constraint) => constraint.fields
309
+ )
310
+ );
311
+ }
263
312
  function drizzleImports(dialect, manifest) {
264
313
  const models = Object.values(manifest.models);
265
314
  const needsBoolean = models.some(
@@ -268,12 +317,17 @@ function drizzleImports(dialect, manifest) {
268
317
  const needsDate = models.some(
269
318
  (model2) => Object.values(model2.fields).some((field) => field.kind === "datetime")
270
319
  );
320
+ const needsIndexes = models.some(
321
+ (model2) => model2.constraints.indexes.length || model2.constraints.unique.length
322
+ );
271
323
  if (dialect === "pg") {
272
324
  return [
273
325
  "pgTable",
274
326
  "text",
275
327
  needsBoolean ? "boolean" : null,
276
- needsDate ? "timestamp" : null
328
+ needsDate ? "timestamp" : null,
329
+ needsIndexes ? "index" : null,
330
+ needsIndexes ? "uniqueIndex" : null
277
331
  ].filter(Boolean);
278
332
  }
279
333
  if (dialect === "mysql") {
@@ -282,12 +336,20 @@ function drizzleImports(dialect, manifest) {
282
336
  "varchar",
283
337
  "text",
284
338
  needsBoolean ? "boolean" : null,
285
- needsDate ? "datetime" : null
339
+ needsDate ? "datetime" : null,
340
+ needsIndexes ? "index" : null,
341
+ needsIndexes ? "uniqueIndex" : null
286
342
  ].filter(Boolean);
287
343
  }
288
- return ["sqliteTable", "text", "integer"];
344
+ return [
345
+ "sqliteTable",
346
+ "text",
347
+ "integer",
348
+ needsIndexes ? "index" : null,
349
+ needsIndexes ? "uniqueIndex" : null
350
+ ].filter(Boolean);
289
351
  }
290
- function drizzleColumn(field, dialect) {
352
+ function drizzleColumn(field, dialect, options = {}) {
291
353
  if (field.kind === "id") {
292
354
  if (dialect === "mysql") {
293
355
  return `varchar("${field.column}", { length: 191 }).primaryKey()`;
@@ -296,7 +358,7 @@ function drizzleColumn(field, dialect) {
296
358
  }
297
359
  if (field.kind === "string") {
298
360
  if (dialect === "mysql") {
299
- const base = field.unique || field.references ? `varchar("${field.column}", { length: 191 })` : `text("${field.column}")`;
361
+ const base = field.unique || field.references || options.indexed ? `varchar("${field.column}", { length: 191 })` : `text("${field.column}")`;
300
362
  return `${base}${field.nullable ? "" : ".notNull()"}${field.unique ? ".unique()" : ""}${field.defaultValue !== void 0 ? `.default(${JSON.stringify(field.defaultValue)})` : ""}`;
301
363
  }
302
364
  return `text("${field.column}")${field.nullable ? "" : ".notNull()"}${field.unique ? ".unique()" : ""}${field.defaultValue !== void 0 ? `.default(${JSON.stringify(field.defaultValue)})` : ""}`;
@@ -313,14 +375,14 @@ function drizzleColumn(field, dialect) {
313
375
  if (dialect === "sqlite") {
314
376
  return `integer("${field.column}", { mode: "timestamp" })${field.nullable ? "" : ".notNull()"}`;
315
377
  }
316
- return `timestamp("${field.column}")${field.nullable ? "" : ".notNull()"}`;
378
+ return `timestamp("${field.column}", { withTimezone: true, mode: "date" })${field.nullable ? "" : ".notNull()"}`;
317
379
  }
318
- function sqlType(field, dialect) {
380
+ function sqlType(field, dialect, options = {}) {
319
381
  if (field.kind === "id") {
320
382
  return dialect === "mysql" ? "varchar(191)" : "text";
321
383
  }
322
384
  if (field.kind === "string") {
323
- return dialect === "mysql" && (field.unique || field.references) ? "varchar(191)" : "text";
385
+ return dialect === "mysql" && (field.unique || field.references || options.indexed) ? "varchar(191)" : "text";
324
386
  }
325
387
  if (field.kind === "boolean") {
326
388
  return dialect === "sqlite" ? "integer" : "boolean";
@@ -331,7 +393,7 @@ function sqlType(field, dialect) {
331
393
  if (dialect === "sqlite") {
332
394
  return "text";
333
395
  }
334
- return "timestamp";
396
+ return "timestamptz";
335
397
  }
336
398
  function sqlIdentifier(dialect, value) {
337
399
  if (dialect === "mysql") {
@@ -339,6 +401,14 @@ function sqlIdentifier(dialect, value) {
339
401
  }
340
402
  return `"${value}"`;
341
403
  }
404
+ function sqlCreateIndexStatement(dialect, table, constraint) {
405
+ const indexName = sqlIdentifier(dialect, constraint.name);
406
+ const tableName = sqlIdentifier(dialect, table);
407
+ const columns = constraint.columns.map((column) => sqlIdentifier(dialect, column)).join(", ");
408
+ const createKeyword = constraint.unique ? "create unique index" : "create index";
409
+ const ifNotExists = dialect === "mysql" ? "" : " if not exists";
410
+ return `${createKeyword}${ifNotExists} ${indexName} on ${tableName}(${columns});`;
411
+ }
342
412
  function renderPrismaSchema(schema, options = {}) {
343
413
  const manifest = createManifest(schema);
344
414
  const provider = options.provider ?? "postgresql";
@@ -421,10 +491,18 @@ function renderPrismaSchema(schema, options = {}) {
421
491
  relation.many ? ` ${relationName} ${capitalize(relation.sourceModel)}[]` : ` ${relationName} ${capitalize(relation.sourceModel)}?`
422
492
  );
423
493
  }
424
- const mapLine = model2.table !== modelName ? `
425
- @@map("${model2.table}")` : "";
494
+ const modelLines = [
495
+ ...lines,
496
+ ...model2.constraints.unique.map(
497
+ (constraint) => ` @@unique([${constraint.fields.join(", ")}])`
498
+ ),
499
+ ...model2.constraints.indexes.map(
500
+ (constraint) => ` @@index([${constraint.fields.join(", ")}])`
501
+ ),
502
+ ...model2.table !== modelName ? [` @@map("${model2.table}")`] : []
503
+ ];
426
504
  return `model ${modelName} {
427
- ${lines.join("\n")}${mapLine}
505
+ ${modelLines.join("\n")}
428
506
  }`;
429
507
  });
430
508
  return `generator ${generatorName} {
@@ -444,17 +522,33 @@ function renderDrizzleSchema(schema, options) {
444
522
  const coreImports = drizzleImports(options.dialect, manifest).join(", ");
445
523
  const tableFactory = options.dialect === "pg" ? "pgTable" : options.dialect === "mysql" ? "mysqlTable" : "sqliteTable";
446
524
  const modelBlocks = Object.values(manifest.models).map((model2) => {
525
+ const indexedFields = constrainedFields(model2);
447
526
  const lines = Object.values(model2.fields).map((field) => {
448
- let value = drizzleColumn(field, options.dialect);
527
+ let value = drizzleColumn(field, options.dialect, { indexed: indexedFields.has(field.name) });
449
528
  if (field.references) {
450
529
  const [targetModel, targetField] = field.references.split(".");
451
530
  value += `.references(() => ${targetModel}.${targetField})`;
452
531
  }
453
532
  return ` ${field.name}: ${value}`;
454
533
  });
455
- return `export const ${model2.name} = ${tableFactory}("${model2.table}", {
534
+ const constraintLines = [
535
+ ...model2.constraints.unique.map(
536
+ (constraint) => ` ${drizzleConstraintProperty(constraint)}: uniqueIndex("${constraint.name}").on(${constraint.fields.map((fieldName) => `table.${fieldName}`).join(", ")})`
537
+ ),
538
+ ...model2.constraints.indexes.map(
539
+ (constraint) => ` ${drizzleConstraintProperty(constraint)}: index("${constraint.name}").on(${constraint.fields.map((fieldName) => `table.${fieldName}`).join(", ")})`
540
+ )
541
+ ];
542
+ if (!constraintLines.length) {
543
+ return `export const ${model2.name} = ${tableFactory}("${model2.table}", {
456
544
  ${lines.join(",\n")}
457
545
  });`;
546
+ }
547
+ return `export const ${model2.name} = ${tableFactory}("${model2.table}", {
548
+ ${lines.join(",\n")}
549
+ }, (table) => ({
550
+ ${constraintLines.join(",\n")}
551
+ }));`;
458
552
  });
459
553
  const relationBlocks = Object.values(manifest.models).map((model2) => {
460
554
  const lines = Object.entries(model2.relations).flatMap(([relationName, relation]) => {
@@ -493,10 +587,13 @@ ${[...modelBlocks, ...relationBlocks].join("\n\n")}
493
587
  }
494
588
  function renderSafeSql(schema, options) {
495
589
  const manifest = createManifest(schema);
496
- const statements = Object.values(manifest.models).map((model2) => {
590
+ const statements = Object.values(manifest.models).flatMap((model2) => {
591
+ const indexedFields = constrainedFields(model2);
497
592
  const columns = Object.values(model2.fields).map((field) => {
498
593
  const parts = [
499
- `${sqlIdentifier(options.dialect, field.column)} ${sqlType(field, options.dialect)}`
594
+ `${sqlIdentifier(options.dialect, field.column)} ${sqlType(field, options.dialect, {
595
+ indexed: indexedFields.has(field.name)
596
+ })}`
500
597
  ];
501
598
  if (field.kind === "id") parts.push("primary key");
502
599
  if (!field.nullable) parts.push("not null");
@@ -519,9 +616,17 @@ function renderSafeSql(schema, options) {
519
616
  }
520
617
  return ` ${parts.join(" ")}`;
521
618
  });
522
- return `create table if not exists ${sqlIdentifier(options.dialect, model2.table)} (
619
+ return [
620
+ `create table if not exists ${sqlIdentifier(options.dialect, model2.table)} (
523
621
  ${columns.join(",\n")}
524
- );`;
622
+ );`,
623
+ ...model2.constraints.unique.map(
624
+ (constraint) => sqlCreateIndexStatement(options.dialect, model2.table, constraint)
625
+ ),
626
+ ...model2.constraints.indexes.map(
627
+ (constraint) => sqlCreateIndexStatement(options.dialect, model2.table, constraint)
628
+ )
629
+ ];
525
630
  });
526
631
  return `${statements.join("\n\n")}
527
632
  `;
@@ -845,6 +950,7 @@ function model(config) {
845
950
  table: config.table,
846
951
  fields: config.fields,
847
952
  relations: config.relations ?? {},
953
+ constraints: config.constraints ?? {},
848
954
  description: config.description
849
955
  };
850
956
  }