@farming-labs/orm 0.0.9 → 0.0.10

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
@@ -32,12 +32,17 @@ __export(index_exports, {
32
32
  hasOne: () => hasOne,
33
33
  id: () => id,
34
34
  manyToMany: () => manyToMany,
35
+ mergeUniqueLookupCreateData: () => mergeUniqueLookupCreateData,
35
36
  model: () => model,
36
37
  renderDrizzleSchema: () => renderDrizzleSchema,
37
38
  renderPrismaSchema: () => renderPrismaSchema,
38
39
  renderSafeSql: () => renderSafeSql,
39
40
  replaceGeneratedBlock: () => replaceGeneratedBlock,
40
- string: () => string
41
+ requireUniqueLookup: () => requireUniqueLookup,
42
+ resolveRowIdentityLookup: () => resolveRowIdentityLookup,
43
+ string: () => string,
44
+ toUniqueLookupWhere: () => toUniqueLookupWhere,
45
+ validateUniqueLookupUpdateData: () => validateUniqueLookupUpdateData
41
46
  });
42
47
  module.exports = __toCommonJS(index_exports);
43
48
 
@@ -187,6 +192,167 @@ function datetime() {
187
192
  }
188
193
 
189
194
  // src/manifest.ts
195
+ function equalLookupValues(left, right) {
196
+ if (left instanceof Date && right instanceof Date) {
197
+ return left.getTime() === right.getTime();
198
+ }
199
+ return Object.is(left, right);
200
+ }
201
+ function isFilterObject(value) {
202
+ return !!value && typeof value === "object" && !(value instanceof Date) && !Array.isArray(value);
203
+ }
204
+ function extractEqualityValue(filter) {
205
+ if (!isFilterObject(filter)) {
206
+ return {
207
+ supported: true,
208
+ value: filter
209
+ };
210
+ }
211
+ const keys = Object.keys(filter);
212
+ if (keys.length === 1 && "eq" in filter) {
213
+ return {
214
+ supported: true,
215
+ value: filter.eq
216
+ };
217
+ }
218
+ return {
219
+ supported: false,
220
+ value: void 0
221
+ };
222
+ }
223
+ function requireEqualityValues(model2, where, operation) {
224
+ const keys = Object.keys(where).filter((key) => key !== "AND" && key !== "OR" && key !== "NOT");
225
+ if ("AND" in where || "OR" in where || "NOT" in where || keys.length === 0) {
226
+ throw new Error(
227
+ `${operation} on model "${model2.name}" requires a unique equality filter in "where".`
228
+ );
229
+ }
230
+ const values = {};
231
+ for (const fieldName of keys) {
232
+ const field = model2.fields[fieldName];
233
+ if (!field) {
234
+ throw new Error(`Unknown field "${fieldName}" on model "${model2.name}".`);
235
+ }
236
+ const { supported, value } = extractEqualityValue(where[fieldName]);
237
+ if (!supported || value === void 0 || value === null) {
238
+ throw new Error(
239
+ `${operation} on model "${model2.name}" requires the "where" field "${fieldName}" to use a single non-null equality value.`
240
+ );
241
+ }
242
+ values[fieldName] = value;
243
+ }
244
+ return values;
245
+ }
246
+ function sameFieldSet(left, right) {
247
+ return left.length === right.length && left.every((fieldName) => right.includes(fieldName));
248
+ }
249
+ function requireUniqueLookup(model2, where, operation) {
250
+ const values = requireEqualityValues(model2, where, operation);
251
+ const keys = Object.keys(values);
252
+ if (keys.length === 1) {
253
+ const field = model2.fields[keys[0]];
254
+ if (field.kind === "id") {
255
+ return {
256
+ kind: "id",
257
+ fields: [field],
258
+ values
259
+ };
260
+ }
261
+ if (field.unique) {
262
+ return {
263
+ kind: "field",
264
+ fields: [field],
265
+ values
266
+ };
267
+ }
268
+ }
269
+ const constraint = model2.constraints.unique.find(
270
+ (candidate) => sameFieldSet([...candidate.fields], keys)
271
+ );
272
+ if (!constraint) {
273
+ throw new Error(
274
+ `${operation} on model "${model2.name}" requires the "where" clause to match an id field, unique field, or declared unique constraint using equality values only.`
275
+ );
276
+ }
277
+ return {
278
+ kind: "constraint",
279
+ fields: constraint.fields.map((fieldName) => model2.fields[fieldName]),
280
+ values: Object.fromEntries(
281
+ constraint.fields.map((fieldName) => [fieldName, values[fieldName]])
282
+ ),
283
+ constraint
284
+ };
285
+ }
286
+ function resolveRowIdentityLookup(model2, row) {
287
+ const idField = model2.fields.id;
288
+ if (idField && row[idField.name] !== void 0 && row[idField.name] !== null) {
289
+ return {
290
+ kind: "id",
291
+ fields: [idField],
292
+ values: {
293
+ [idField.name]: row[idField.name]
294
+ }
295
+ };
296
+ }
297
+ const uniqueField = Object.values(model2.fields).find(
298
+ (field) => field.unique && row[field.name] !== void 0 && row[field.name] !== null
299
+ );
300
+ if (uniqueField) {
301
+ return {
302
+ kind: "field",
303
+ fields: [uniqueField],
304
+ values: {
305
+ [uniqueField.name]: row[uniqueField.name]
306
+ }
307
+ };
308
+ }
309
+ for (const constraint of model2.constraints.unique) {
310
+ if (constraint.fields.every(
311
+ (fieldName) => row[fieldName] !== void 0 && row[fieldName] !== null
312
+ )) {
313
+ return {
314
+ kind: "constraint",
315
+ fields: constraint.fields.map((fieldName) => model2.fields[fieldName]),
316
+ values: Object.fromEntries(
317
+ constraint.fields.map((fieldName) => [fieldName, row[fieldName]])
318
+ ),
319
+ constraint
320
+ };
321
+ }
322
+ }
323
+ throw new Error(
324
+ `Model "${model2.name}" requires an "id" field, unique field, or declared unique constraint with non-null values for identity lookups.`
325
+ );
326
+ }
327
+ function toUniqueLookupWhere(lookup) {
328
+ return Object.fromEntries(lookup.fields.map((field) => [field.name, lookup.values[field.name]]));
329
+ }
330
+ function mergeUniqueLookupCreateData(model2, createData, lookup, operation) {
331
+ const output = {
332
+ ...createData
333
+ };
334
+ for (const field of lookup.fields) {
335
+ const currentValue = output[field.name];
336
+ const expectedValue = lookup.values[field.name];
337
+ if (currentValue !== void 0 && !equalLookupValues(currentValue, expectedValue)) {
338
+ throw new Error(
339
+ `${operation} on model "${model2.name}" requires create.${field.name} to match where.${field.name}.`
340
+ );
341
+ }
342
+ output[field.name] = currentValue ?? expectedValue;
343
+ }
344
+ return output;
345
+ }
346
+ function validateUniqueLookupUpdateData(model2, updateData, lookup, operation) {
347
+ for (const field of lookup.fields) {
348
+ const nextValue = updateData[field.name];
349
+ if (nextValue !== void 0 && !equalLookupValues(nextValue, lookup.values[field.name])) {
350
+ throw new Error(
351
+ `${operation} on model "${model2.name}" cannot change the conflict field "${field.name}".`
352
+ );
353
+ }
354
+ }
355
+ }
190
356
  function createConstraintName(table, columns, suffix) {
191
357
  const base = [table, ...columns].join("_").replace(/[^a-zA-Z0-9_]+/g, "_").replace(/_+/g, "_").replace(/^_+|_+$/g, "").toLowerCase();
192
358
  return `${base}_${suffix}`;
@@ -650,6 +816,14 @@ ${block}
650
816
  // src/memory.ts
651
817
  var import_node_crypto = require("crypto");
652
818
  var isDate = (value) => value instanceof Date;
819
+ var manifestCache = /* @__PURE__ */ new WeakMap();
820
+ function getManifest(schema) {
821
+ const cached = manifestCache.get(schema);
822
+ if (cached) return cached;
823
+ const next = createManifest(schema);
824
+ manifestCache.set(schema, next);
825
+ return next;
826
+ }
653
827
  function evaluateFilter(value, filter) {
654
828
  if (filter === void 0 || filter === null || typeof filter !== "object" || isDate(filter) || Array.isArray(filter)) {
655
829
  return value === filter;
@@ -842,6 +1016,11 @@ function createMemoryDriver(seed) {
842
1016
  return projectRow(schema, model2, row, args.select);
843
1017
  },
844
1018
  async findUnique(schema, model2, args) {
1019
+ requireUniqueLookup(
1020
+ getManifest(schema).models[model2],
1021
+ args.where,
1022
+ "FindUnique"
1023
+ );
845
1024
  const row = applyQuery(getRows(model2), args)[0];
846
1025
  if (!row) return null;
847
1026
  return projectRow(schema, model2, row, args.select);
@@ -873,12 +1052,32 @@ function createMemoryDriver(seed) {
873
1052
  return rows.length;
874
1053
  },
875
1054
  async upsert(schema, model2, args) {
1055
+ const lookup = requireUniqueLookup(
1056
+ getManifest(schema).models[model2],
1057
+ args.where,
1058
+ "Upsert"
1059
+ );
1060
+ validateUniqueLookupUpdateData(
1061
+ getManifest(schema).models[model2],
1062
+ args.update,
1063
+ lookup,
1064
+ "Upsert"
1065
+ );
876
1066
  const row = getRows(model2).find((item) => matchesWhere(item, args.where));
877
1067
  if (row) {
878
1068
  Object.assign(row, args.update);
879
1069
  return projectRow(schema, model2, row, args.select);
880
1070
  }
881
- const created = buildRow(schema, model2, args.create);
1071
+ const created = buildRow(
1072
+ schema,
1073
+ model2,
1074
+ mergeUniqueLookupCreateData(
1075
+ getManifest(schema).models[model2],
1076
+ args.create,
1077
+ lookup,
1078
+ "Upsert"
1079
+ )
1080
+ );
882
1081
  getRows(model2).push(created);
883
1082
  return projectRow(schema, model2, created, args.select);
884
1083
  },
@@ -974,11 +1173,16 @@ function defineSchema(models) {
974
1173
  hasOne,
975
1174
  id,
976
1175
  manyToMany,
1176
+ mergeUniqueLookupCreateData,
977
1177
  model,
978
1178
  renderDrizzleSchema,
979
1179
  renderPrismaSchema,
980
1180
  renderSafeSql,
981
1181
  replaceGeneratedBlock,
982
- string
1182
+ requireUniqueLookup,
1183
+ resolveRowIdentityLookup,
1184
+ string,
1185
+ toUniqueLookupWhere,
1186
+ validateUniqueLookupUpdateData
983
1187
  });
984
1188
  //# sourceMappingURL=index.cjs.map