@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.js CHANGED
@@ -144,6 +144,38 @@ function datetime() {
144
144
  }
145
145
 
146
146
  // src/manifest.ts
147
+ function createConstraintName(table, columns, suffix) {
148
+ const base = [table, ...columns].join("_").replace(/[^a-zA-Z0-9_]+/g, "_").replace(/_+/g, "_").replace(/^_+|_+$/g, "").toLowerCase();
149
+ return `${base}_${suffix}`;
150
+ }
151
+ function normalizeConstraints(modelName, table, fields, constraints) {
152
+ const normalize = (entries, unique) => (entries ?? []).map((entry) => {
153
+ if (!entry.length) {
154
+ throw new Error(
155
+ `Model "${modelName}" defines an empty ${unique ? "unique" : "index"} constraint.`
156
+ );
157
+ }
158
+ const columns = entry.map((fieldName) => {
159
+ const field = fields[fieldName];
160
+ if (!field) {
161
+ throw new Error(
162
+ `Model "${modelName}" defines a ${unique ? "unique" : "index"} constraint on unknown field "${fieldName}".`
163
+ );
164
+ }
165
+ return field.column;
166
+ });
167
+ return {
168
+ name: createConstraintName(table, columns, unique ? "unique" : "idx"),
169
+ fields: [...entry],
170
+ columns,
171
+ unique
172
+ };
173
+ });
174
+ return {
175
+ unique: normalize(constraints.unique, true),
176
+ indexes: normalize(constraints.indexes, false)
177
+ };
178
+ }
147
179
  function createManifest(schema) {
148
180
  const models = Object.fromEntries(
149
181
  Object.entries(schema.models).map(
@@ -171,7 +203,13 @@ function createManifest(schema) {
171
203
  table: definition.table,
172
204
  description: definition.description,
173
205
  fields,
174
- relations: definition.relations
206
+ relations: definition.relations,
207
+ constraints: normalizeConstraints(
208
+ name,
209
+ definition.table,
210
+ fields,
211
+ definition.constraints
212
+ )
175
213
  }
176
214
  ];
177
215
  }
@@ -183,6 +221,7 @@ function createManifest(schema) {
183
221
  // src/generators.ts
184
222
  var capitalize = (value) => value.charAt(0).toUpperCase() + value.slice(1);
185
223
  var pluralize = (value) => value.endsWith("s") ? value : `${value}s`;
224
+ var camelize = (value) => value.replace(/[^a-zA-Z0-9]+(.)/g, (_, char) => char.toUpperCase()).replace(/^[^a-zA-Z]+/, "").replace(/^[A-Z]/, (char) => char.toLowerCase());
186
225
  function resolveReferenceTarget(manifest, model2, foreignKey, fallbackTarget) {
187
226
  const reference = model2.fields[foreignKey]?.references;
188
227
  if (!reference) {
@@ -217,6 +256,16 @@ function prismaType(field) {
217
256
  return "DateTime";
218
257
  }
219
258
  }
259
+ function drizzleConstraintProperty(constraint) {
260
+ return camelize(constraint.name) || "constraint";
261
+ }
262
+ function constrainedFields(model2) {
263
+ return new Set(
264
+ [...model2.constraints.unique, ...model2.constraints.indexes].flatMap(
265
+ (constraint) => constraint.fields
266
+ )
267
+ );
268
+ }
220
269
  function drizzleImports(dialect, manifest) {
221
270
  const models = Object.values(manifest.models);
222
271
  const needsBoolean = models.some(
@@ -225,12 +274,17 @@ function drizzleImports(dialect, manifest) {
225
274
  const needsDate = models.some(
226
275
  (model2) => Object.values(model2.fields).some((field) => field.kind === "datetime")
227
276
  );
277
+ const needsIndexes = models.some(
278
+ (model2) => model2.constraints.indexes.length || model2.constraints.unique.length
279
+ );
228
280
  if (dialect === "pg") {
229
281
  return [
230
282
  "pgTable",
231
283
  "text",
232
284
  needsBoolean ? "boolean" : null,
233
- needsDate ? "timestamp" : null
285
+ needsDate ? "timestamp" : null,
286
+ needsIndexes ? "index" : null,
287
+ needsIndexes ? "uniqueIndex" : null
234
288
  ].filter(Boolean);
235
289
  }
236
290
  if (dialect === "mysql") {
@@ -239,12 +293,20 @@ function drizzleImports(dialect, manifest) {
239
293
  "varchar",
240
294
  "text",
241
295
  needsBoolean ? "boolean" : null,
242
- needsDate ? "datetime" : null
296
+ needsDate ? "datetime" : null,
297
+ needsIndexes ? "index" : null,
298
+ needsIndexes ? "uniqueIndex" : null
243
299
  ].filter(Boolean);
244
300
  }
245
- return ["sqliteTable", "text", "integer"];
301
+ return [
302
+ "sqliteTable",
303
+ "text",
304
+ "integer",
305
+ needsIndexes ? "index" : null,
306
+ needsIndexes ? "uniqueIndex" : null
307
+ ].filter(Boolean);
246
308
  }
247
- function drizzleColumn(field, dialect) {
309
+ function drizzleColumn(field, dialect, options = {}) {
248
310
  if (field.kind === "id") {
249
311
  if (dialect === "mysql") {
250
312
  return `varchar("${field.column}", { length: 191 }).primaryKey()`;
@@ -253,7 +315,7 @@ function drizzleColumn(field, dialect) {
253
315
  }
254
316
  if (field.kind === "string") {
255
317
  if (dialect === "mysql") {
256
- const base = field.unique || field.references ? `varchar("${field.column}", { length: 191 })` : `text("${field.column}")`;
318
+ const base = field.unique || field.references || options.indexed ? `varchar("${field.column}", { length: 191 })` : `text("${field.column}")`;
257
319
  return `${base}${field.nullable ? "" : ".notNull()"}${field.unique ? ".unique()" : ""}${field.defaultValue !== void 0 ? `.default(${JSON.stringify(field.defaultValue)})` : ""}`;
258
320
  }
259
321
  return `text("${field.column}")${field.nullable ? "" : ".notNull()"}${field.unique ? ".unique()" : ""}${field.defaultValue !== void 0 ? `.default(${JSON.stringify(field.defaultValue)})` : ""}`;
@@ -270,14 +332,14 @@ function drizzleColumn(field, dialect) {
270
332
  if (dialect === "sqlite") {
271
333
  return `integer("${field.column}", { mode: "timestamp" })${field.nullable ? "" : ".notNull()"}`;
272
334
  }
273
- return `timestamp("${field.column}")${field.nullable ? "" : ".notNull()"}`;
335
+ return `timestamp("${field.column}", { withTimezone: true, mode: "date" })${field.nullable ? "" : ".notNull()"}`;
274
336
  }
275
- function sqlType(field, dialect) {
337
+ function sqlType(field, dialect, options = {}) {
276
338
  if (field.kind === "id") {
277
339
  return dialect === "mysql" ? "varchar(191)" : "text";
278
340
  }
279
341
  if (field.kind === "string") {
280
- return dialect === "mysql" && (field.unique || field.references) ? "varchar(191)" : "text";
342
+ return dialect === "mysql" && (field.unique || field.references || options.indexed) ? "varchar(191)" : "text";
281
343
  }
282
344
  if (field.kind === "boolean") {
283
345
  return dialect === "sqlite" ? "integer" : "boolean";
@@ -288,7 +350,7 @@ function sqlType(field, dialect) {
288
350
  if (dialect === "sqlite") {
289
351
  return "text";
290
352
  }
291
- return "timestamp";
353
+ return "timestamptz";
292
354
  }
293
355
  function sqlIdentifier(dialect, value) {
294
356
  if (dialect === "mysql") {
@@ -296,6 +358,14 @@ function sqlIdentifier(dialect, value) {
296
358
  }
297
359
  return `"${value}"`;
298
360
  }
361
+ function sqlCreateIndexStatement(dialect, table, constraint) {
362
+ const indexName = sqlIdentifier(dialect, constraint.name);
363
+ const tableName = sqlIdentifier(dialect, table);
364
+ const columns = constraint.columns.map((column) => sqlIdentifier(dialect, column)).join(", ");
365
+ const createKeyword = constraint.unique ? "create unique index" : "create index";
366
+ const ifNotExists = dialect === "mysql" ? "" : " if not exists";
367
+ return `${createKeyword}${ifNotExists} ${indexName} on ${tableName}(${columns});`;
368
+ }
299
369
  function renderPrismaSchema(schema, options = {}) {
300
370
  const manifest = createManifest(schema);
301
371
  const provider = options.provider ?? "postgresql";
@@ -378,10 +448,18 @@ function renderPrismaSchema(schema, options = {}) {
378
448
  relation.many ? ` ${relationName} ${capitalize(relation.sourceModel)}[]` : ` ${relationName} ${capitalize(relation.sourceModel)}?`
379
449
  );
380
450
  }
381
- const mapLine = model2.table !== modelName ? `
382
- @@map("${model2.table}")` : "";
451
+ const modelLines = [
452
+ ...lines,
453
+ ...model2.constraints.unique.map(
454
+ (constraint) => ` @@unique([${constraint.fields.join(", ")}])`
455
+ ),
456
+ ...model2.constraints.indexes.map(
457
+ (constraint) => ` @@index([${constraint.fields.join(", ")}])`
458
+ ),
459
+ ...model2.table !== modelName ? [` @@map("${model2.table}")`] : []
460
+ ];
383
461
  return `model ${modelName} {
384
- ${lines.join("\n")}${mapLine}
462
+ ${modelLines.join("\n")}
385
463
  }`;
386
464
  });
387
465
  return `generator ${generatorName} {
@@ -401,17 +479,33 @@ function renderDrizzleSchema(schema, options) {
401
479
  const coreImports = drizzleImports(options.dialect, manifest).join(", ");
402
480
  const tableFactory = options.dialect === "pg" ? "pgTable" : options.dialect === "mysql" ? "mysqlTable" : "sqliteTable";
403
481
  const modelBlocks = Object.values(manifest.models).map((model2) => {
482
+ const indexedFields = constrainedFields(model2);
404
483
  const lines = Object.values(model2.fields).map((field) => {
405
- let value = drizzleColumn(field, options.dialect);
484
+ let value = drizzleColumn(field, options.dialect, { indexed: indexedFields.has(field.name) });
406
485
  if (field.references) {
407
486
  const [targetModel, targetField] = field.references.split(".");
408
487
  value += `.references(() => ${targetModel}.${targetField})`;
409
488
  }
410
489
  return ` ${field.name}: ${value}`;
411
490
  });
412
- return `export const ${model2.name} = ${tableFactory}("${model2.table}", {
491
+ const constraintLines = [
492
+ ...model2.constraints.unique.map(
493
+ (constraint) => ` ${drizzleConstraintProperty(constraint)}: uniqueIndex("${constraint.name}").on(${constraint.fields.map((fieldName) => `table.${fieldName}`).join(", ")})`
494
+ ),
495
+ ...model2.constraints.indexes.map(
496
+ (constraint) => ` ${drizzleConstraintProperty(constraint)}: index("${constraint.name}").on(${constraint.fields.map((fieldName) => `table.${fieldName}`).join(", ")})`
497
+ )
498
+ ];
499
+ if (!constraintLines.length) {
500
+ return `export const ${model2.name} = ${tableFactory}("${model2.table}", {
413
501
  ${lines.join(",\n")}
414
502
  });`;
503
+ }
504
+ return `export const ${model2.name} = ${tableFactory}("${model2.table}", {
505
+ ${lines.join(",\n")}
506
+ }, (table) => ({
507
+ ${constraintLines.join(",\n")}
508
+ }));`;
415
509
  });
416
510
  const relationBlocks = Object.values(manifest.models).map((model2) => {
417
511
  const lines = Object.entries(model2.relations).flatMap(([relationName, relation]) => {
@@ -450,10 +544,13 @@ ${[...modelBlocks, ...relationBlocks].join("\n\n")}
450
544
  }
451
545
  function renderSafeSql(schema, options) {
452
546
  const manifest = createManifest(schema);
453
- const statements = Object.values(manifest.models).map((model2) => {
547
+ const statements = Object.values(manifest.models).flatMap((model2) => {
548
+ const indexedFields = constrainedFields(model2);
454
549
  const columns = Object.values(model2.fields).map((field) => {
455
550
  const parts = [
456
- `${sqlIdentifier(options.dialect, field.column)} ${sqlType(field, options.dialect)}`
551
+ `${sqlIdentifier(options.dialect, field.column)} ${sqlType(field, options.dialect, {
552
+ indexed: indexedFields.has(field.name)
553
+ })}`
457
554
  ];
458
555
  if (field.kind === "id") parts.push("primary key");
459
556
  if (!field.nullable) parts.push("not null");
@@ -476,9 +573,17 @@ function renderSafeSql(schema, options) {
476
573
  }
477
574
  return ` ${parts.join(" ")}`;
478
575
  });
479
- return `create table if not exists ${sqlIdentifier(options.dialect, model2.table)} (
576
+ return [
577
+ `create table if not exists ${sqlIdentifier(options.dialect, model2.table)} (
480
578
  ${columns.join(",\n")}
481
- );`;
579
+ );`,
580
+ ...model2.constraints.unique.map(
581
+ (constraint) => sqlCreateIndexStatement(options.dialect, model2.table, constraint)
582
+ ),
583
+ ...model2.constraints.indexes.map(
584
+ (constraint) => sqlCreateIndexStatement(options.dialect, model2.table, constraint)
585
+ )
586
+ ];
482
587
  });
483
588
  return `${statements.join("\n\n")}
484
589
  `;
@@ -802,6 +907,7 @@ function model(config) {
802
907
  table: config.table,
803
908
  fields: config.fields,
804
909
  relations: config.relations ?? {},
910
+ constraints: config.constraints ?? {},
805
911
  description: config.description
806
912
  };
807
913
  }