@vsrepo/drizzle-adapter 0.1.0 → 0.1.1
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 +112 -62
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +34 -1
- package/dist/index.d.ts +34 -1
- package/dist/index.js +111 -62
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
package/dist/index.cjs
CHANGED
|
@@ -20,7 +20,8 @@ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: tru
|
|
|
20
20
|
// src/index.ts
|
|
21
21
|
var index_exports = {};
|
|
22
22
|
__export(index_exports, {
|
|
23
|
-
DrizzleAdapter: () => DrizzleAdapter
|
|
23
|
+
DrizzleAdapter: () => DrizzleAdapter,
|
|
24
|
+
VSRepoDrizzleAdapter: () => DrizzleAdapter
|
|
24
25
|
});
|
|
25
26
|
module.exports = __toCommonJS(index_exports);
|
|
26
27
|
|
|
@@ -331,16 +332,21 @@ function validateRelations(table, dialect, relations) {
|
|
|
331
332
|
}
|
|
332
333
|
|
|
333
334
|
// src/parsers/columns.parser.ts
|
|
334
|
-
function parseColumns(select) {
|
|
335
|
+
function parseColumns(select, relationsKeysSet) {
|
|
335
336
|
const columns = {};
|
|
336
337
|
let withResult;
|
|
337
338
|
for (const [key, value] of Object.entries(select)) {
|
|
338
339
|
if (value === void 0) continue;
|
|
339
|
-
if (
|
|
340
|
+
if (typeof value !== "boolean") {
|
|
340
341
|
withResult ??= {};
|
|
341
342
|
const nested = parseColumns(value);
|
|
342
343
|
withResult[key] = nested.with ? { columns: nested.columns, with: nested.with } : { columns: nested.columns };
|
|
343
344
|
} else {
|
|
345
|
+
if (relationsKeysSet?.has(key)) {
|
|
346
|
+
withResult ??= {};
|
|
347
|
+
withResult[key] = value;
|
|
348
|
+
continue;
|
|
349
|
+
}
|
|
344
350
|
columns[key] = value;
|
|
345
351
|
}
|
|
346
352
|
}
|
|
@@ -893,7 +899,7 @@ async function resolveFkHereField(tx, relation, field, currentFkValue) {
|
|
|
893
899
|
}
|
|
894
900
|
const pkValue = field[relation.relatedPk];
|
|
895
901
|
if (pkValue === void 0) {
|
|
896
|
-
const [created] = await tx.insert(relation.table).values(field).returning();
|
|
902
|
+
const [created] = await tx.insert(relation.table).values(field).returning({ [relation.relatedPk]: relation.table[relation.relatedPk] });
|
|
897
903
|
return created[relation.relatedPk];
|
|
898
904
|
}
|
|
899
905
|
const existing = await tx.select({ pk: pkColumn }).from(relation.table).where((0, import_drizzle_orm8.eq)(pkColumn, pkValue)).limit(1);
|
|
@@ -931,7 +937,7 @@ async function resolveOtmField(tx, relation, items, ownPkValue) {
|
|
|
931
937
|
const withPk = items.filter((item) => item[relation.relatedPk] !== void 0);
|
|
932
938
|
const connectedIds = [];
|
|
933
939
|
for (const item of withoutPk) {
|
|
934
|
-
const [insertedRow] = await tx.insert(relation.table).values({ ...item, [relation.fkThere]: ownPkValue }).returning();
|
|
940
|
+
const [insertedRow] = await tx.insert(relation.table).values({ ...item, [relation.fkThere]: ownPkValue }).returning({ [relation.relatedPk]: relation.table[relation.relatedPk] });
|
|
935
941
|
if (insertedRow?.[relation.relatedPk] !== void 0) {
|
|
936
942
|
connectedIds.push(insertedRow[relation.relatedPk]);
|
|
937
943
|
}
|
|
@@ -963,22 +969,25 @@ async function resolveOtoFkThereField(tx, relation, field, ownPkValue) {
|
|
|
963
969
|
return;
|
|
964
970
|
}
|
|
965
971
|
const pkValue = field[relation.relatedPk];
|
|
966
|
-
|
|
972
|
+
const resolveSetData = async () => {
|
|
973
|
+
const setData = relation.restriction === "set" ? { ...omitKey(field, relation.relatedPk), [relation.fkThere]: ownPkValue } : { [relation.fkThere]: ownPkValue };
|
|
974
|
+
await tx.update(relation.table).set(setData).where((0, import_drizzle_orm8.eq)(pkColumn, pkValue));
|
|
975
|
+
};
|
|
967
976
|
if (pkValue === void 0) {
|
|
968
|
-
const [
|
|
969
|
-
|
|
977
|
+
const [otoRel] = await tx.select({ pk: pkColumn }).from(relation.table).where((0, import_drizzle_orm8.eq)(fkColumn, ownPkValue)).limit(1);
|
|
978
|
+
if (!otoRel) {
|
|
979
|
+
await tx.insert(relation.table).values({ ...field, [relation.fkThere]: ownPkValue });
|
|
980
|
+
} else {
|
|
981
|
+
await resolveSetData();
|
|
982
|
+
}
|
|
970
983
|
} else {
|
|
971
984
|
const existing = await tx.select({ pk: pkColumn }).from(relation.table).where((0, import_drizzle_orm8.eq)(pkColumn, pkValue)).limit(1);
|
|
972
985
|
if (existing.length === 0) {
|
|
973
986
|
await tx.insert(relation.table).values({ ...field, [relation.fkThere]: ownPkValue });
|
|
974
987
|
} else {
|
|
975
|
-
|
|
976
|
-
await tx.update(relation.table).set(setData).where((0, import_drizzle_orm8.eq)(pkColumn, pkValue));
|
|
988
|
+
await resolveSetData();
|
|
977
989
|
}
|
|
978
990
|
}
|
|
979
|
-
if (relation.restriction === "set" && keepPk !== void 0) {
|
|
980
|
-
await tx.delete(relation.table).where((0, import_drizzle_orm8.and)((0, import_drizzle_orm8.eq)(fkColumn, ownPkValue), (0, import_drizzle_orm8.ne)(pkColumn, keepPk)));
|
|
981
|
-
}
|
|
982
991
|
}
|
|
983
992
|
async function resolveFkThereFields(tx, entries, ownPkValue) {
|
|
984
993
|
for (const [key, relation, value] of entries) {
|
|
@@ -1012,6 +1021,7 @@ var DrizzleAdapter = class extends import_vsrepo10.VSRepoAdapter {
|
|
|
1012
1021
|
pk;
|
|
1013
1022
|
queryKey;
|
|
1014
1023
|
relations;
|
|
1024
|
+
relationsKeysSet;
|
|
1015
1025
|
constructor(db, config) {
|
|
1016
1026
|
super();
|
|
1017
1027
|
const validated = validateDrizzleAdapterConfig(db, config);
|
|
@@ -1022,6 +1032,9 @@ var DrizzleAdapter = class extends import_vsrepo10.VSRepoAdapter {
|
|
|
1022
1032
|
const fieldsConfig = resolveFieldsConfig(this.table);
|
|
1023
1033
|
this.pk = fieldsConfig.pk;
|
|
1024
1034
|
this.relations = validateRelations(this.table, this.dialect, validated.config.relations);
|
|
1035
|
+
if (this.relations) {
|
|
1036
|
+
this.relationsKeysSet = new Set(Object.keys(this.relations));
|
|
1037
|
+
}
|
|
1025
1038
|
}
|
|
1026
1039
|
/**
|
|
1027
1040
|
* Returns `db.query[queryKey]` (the relational query builder entry for
|
|
@@ -1044,7 +1057,7 @@ var DrizzleAdapter = class extends import_vsrepo10.VSRepoAdapter {
|
|
|
1044
1057
|
let columns;
|
|
1045
1058
|
let withArg;
|
|
1046
1059
|
if (options.select) {
|
|
1047
|
-
const parsedSelect = parseColumns(options.select);
|
|
1060
|
+
const parsedSelect = parseColumns(options.select, this.relationsKeysSet);
|
|
1048
1061
|
columns = parsedSelect.columns;
|
|
1049
1062
|
withArg = parsedSelect.with;
|
|
1050
1063
|
} else if (options.relations) {
|
|
@@ -1123,6 +1136,21 @@ var DrizzleAdapter = class extends import_vsrepo10.VSRepoAdapter {
|
|
|
1123
1136
|
paginationApplied: opts?.limit !== void 0 || opts?.offset !== void 0
|
|
1124
1137
|
};
|
|
1125
1138
|
}
|
|
1139
|
+
/**
|
|
1140
|
+
* Fetches the current row matching `where` (or `null` when none exists),
|
|
1141
|
+
* using the same where-resolution path as `update` — the single place
|
|
1142
|
+
* "find the row behind a where" is materialized.
|
|
1143
|
+
*
|
|
1144
|
+
* Internal callers that already fetched a row in their own transaction
|
|
1145
|
+
* (`save`, `upsert`) use this to detect/create, then hand that same row
|
|
1146
|
+
* to `updateCore` as `current`, so the row is only ever read once.
|
|
1147
|
+
*/
|
|
1148
|
+
async findCurrentByWhere(where, opts) {
|
|
1149
|
+
const arg = {
|
|
1150
|
+
where: (await this.resolveFindWhere(where, { db: opts?.db, limit: 1 })).where
|
|
1151
|
+
};
|
|
1152
|
+
return await this.getQueryBuilder(opts?.db).findFirst(arg) ?? null;
|
|
1153
|
+
}
|
|
1126
1154
|
/**
|
|
1127
1155
|
* Strips relation fields from a payload — used by `createMany`/`updateMany`/
|
|
1128
1156
|
* `updateManyReturning`, since batch statements only accept flat column
|
|
@@ -1232,12 +1260,18 @@ var DrizzleAdapter = class extends import_vsrepo10.VSRepoAdapter {
|
|
|
1232
1260
|
return await this.create(obj, options);
|
|
1233
1261
|
}
|
|
1234
1262
|
return await this.runTransactional(options?.db, async (tx) => {
|
|
1235
|
-
const
|
|
1236
|
-
|
|
1237
|
-
|
|
1263
|
+
const current = await this.findCurrentByWhere({ [this.pk]: pkValue }, {
|
|
1264
|
+
db: tx
|
|
1265
|
+
});
|
|
1266
|
+
if (current === null) {
|
|
1238
1267
|
return this.create(obj, { ...options, db: tx });
|
|
1239
1268
|
}
|
|
1240
|
-
return this.
|
|
1269
|
+
return this.updateCore(
|
|
1270
|
+
{ [this.pk]: pkValue },
|
|
1271
|
+
obj,
|
|
1272
|
+
{ ...options, db: tx },
|
|
1273
|
+
current
|
|
1274
|
+
);
|
|
1241
1275
|
});
|
|
1242
1276
|
} catch (error) {
|
|
1243
1277
|
throw mapDrizzleError(error, "save", this.dialect);
|
|
@@ -1264,7 +1298,7 @@ var DrizzleAdapter = class extends import_vsrepo10.VSRepoAdapter {
|
|
|
1264
1298
|
false
|
|
1265
1299
|
);
|
|
1266
1300
|
await resolveFkHereFields(tx, fkHereEntries, scalarFields, void 0);
|
|
1267
|
-
const [created] = await tx.insert(this.table).values(scalarFields).returning();
|
|
1301
|
+
const [created] = await tx.insert(this.table).values(scalarFields).returning({ [this.pk]: this.table[this.pk] });
|
|
1268
1302
|
const ownPkValue = created[this.pk];
|
|
1269
1303
|
await resolveFkThereFields(tx, fkThereEntries, ownPkValue);
|
|
1270
1304
|
const readArg = await this.resolveReadArgs(
|
|
@@ -1272,7 +1306,7 @@ var DrizzleAdapter = class extends import_vsrepo10.VSRepoAdapter {
|
|
|
1272
1306
|
options
|
|
1273
1307
|
);
|
|
1274
1308
|
const result = await this.getQueryBuilder(tx).findFirst(readArg);
|
|
1275
|
-
return result
|
|
1309
|
+
return result;
|
|
1276
1310
|
});
|
|
1277
1311
|
} catch (error) {
|
|
1278
1312
|
throw mapDrizzleError(error, "create", this.dialect);
|
|
@@ -1286,7 +1320,7 @@ var DrizzleAdapter = class extends import_vsrepo10.VSRepoAdapter {
|
|
|
1286
1320
|
if (options?.ignoreConflicts) qb = qb.onConflictDoNothing();
|
|
1287
1321
|
const result = await qb;
|
|
1288
1322
|
const affected = resolveRawResult(this.dialect, result, true);
|
|
1289
|
-
return { count: affected
|
|
1323
|
+
return { count: affected };
|
|
1290
1324
|
} catch (error) {
|
|
1291
1325
|
throw mapDrizzleError(error, "createMany", this.dialect);
|
|
1292
1326
|
}
|
|
@@ -1344,42 +1378,53 @@ var DrizzleAdapter = class extends import_vsrepo10.VSRepoAdapter {
|
|
|
1344
1378
|
}
|
|
1345
1379
|
async update(where, obj, options) {
|
|
1346
1380
|
try {
|
|
1347
|
-
return await this.
|
|
1348
|
-
const current = await this.getQueryBuilder(tx).findFirst({
|
|
1349
|
-
where: (await this.resolveFindWhere(where, { db: tx, limit: 1 })).where
|
|
1350
|
-
});
|
|
1351
|
-
if (!current) {
|
|
1352
|
-
throw new import_vsrepo10.VSRepoAdapterError(
|
|
1353
|
-
"'update' found no record matching the given 'where'.",
|
|
1354
|
-
import_vsrepo10.AdapterErrorCode.NOT_FOUND,
|
|
1355
|
-
null
|
|
1356
|
-
);
|
|
1357
|
-
}
|
|
1358
|
-
const ownPkValue = current[this.pk];
|
|
1359
|
-
const objAny = obj;
|
|
1360
|
-
const { scalarFields, fkHereEntries, fkThereEntries } = splitWritePayload(
|
|
1361
|
-
objAny,
|
|
1362
|
-
this.relations,
|
|
1363
|
-
this.pk,
|
|
1364
|
-
true
|
|
1365
|
-
);
|
|
1366
|
-
await resolveFkHereFields(tx, fkHereEntries, scalarFields, current);
|
|
1367
|
-
if (Object.keys(scalarFields).length > 0) {
|
|
1368
|
-
const pkColumn = this.table[this.pk];
|
|
1369
|
-
await tx.update(this.table).set(scalarFields).where((0, import_drizzle_orm9.eq)(pkColumn, ownPkValue));
|
|
1370
|
-
}
|
|
1371
|
-
await resolveFkThereFields(tx, fkThereEntries, ownPkValue);
|
|
1372
|
-
const readArg = await this.resolveReadArgs(
|
|
1373
|
-
{ [this.pk]: ownPkValue },
|
|
1374
|
-
options
|
|
1375
|
-
);
|
|
1376
|
-
const result = await this.getQueryBuilder(tx).findFirst(readArg);
|
|
1377
|
-
return result ?? current;
|
|
1378
|
-
});
|
|
1381
|
+
return await this.updateCore(where, obj, options);
|
|
1379
1382
|
} catch (error) {
|
|
1380
1383
|
throw mapDrizzleError(error, "update", this.dialect);
|
|
1381
1384
|
}
|
|
1382
1385
|
}
|
|
1386
|
+
/**
|
|
1387
|
+
* Shared implementation behind `update` — also reached from `save` and
|
|
1388
|
+
* `upsert`, which pass in `current` (the row they already fetched in the
|
|
1389
|
+
* same transaction) so the row is read only once instead of once per
|
|
1390
|
+
* method. The `'update' found no record...` contract applies whenever it
|
|
1391
|
+
* runs without a preloaded row (i.e. when called through the public
|
|
1392
|
+
* `update`), and always as a safety net.
|
|
1393
|
+
*
|
|
1394
|
+
* Invariant: when `current` is provided, it must be the full row read
|
|
1395
|
+
* from the exact `tx` referenced by `options.db` (never from outside a
|
|
1396
|
+
* transaction, and never a pk-only projection — `resolveFkHereFields`
|
|
1397
|
+
* reads the current FK values off it).
|
|
1398
|
+
*/
|
|
1399
|
+
async updateCore(where, obj, options, current) {
|
|
1400
|
+
return this.runTransactional(options?.db, async (tx) => {
|
|
1401
|
+
const resolved = current ?? await this.findCurrentByWhere(where, { db: tx });
|
|
1402
|
+
if (!resolved) {
|
|
1403
|
+
throw new import_vsrepo10.VSRepoAdapterError(
|
|
1404
|
+
"'update' found no record matching the given 'where'.",
|
|
1405
|
+
import_vsrepo10.AdapterErrorCode.NOT_FOUND,
|
|
1406
|
+
null
|
|
1407
|
+
);
|
|
1408
|
+
}
|
|
1409
|
+
const ownPkValue = resolved[this.pk];
|
|
1410
|
+
const objAny = obj;
|
|
1411
|
+
const { scalarFields, fkHereEntries, fkThereEntries } = splitWritePayload(
|
|
1412
|
+
objAny,
|
|
1413
|
+
this.relations,
|
|
1414
|
+
this.pk,
|
|
1415
|
+
true
|
|
1416
|
+
);
|
|
1417
|
+
await resolveFkHereFields(tx, fkHereEntries, scalarFields, resolved);
|
|
1418
|
+
if (Object.keys(scalarFields).length > 0) {
|
|
1419
|
+
const pkColumn = this.table[this.pk];
|
|
1420
|
+
await tx.update(this.table).set(scalarFields).where((0, import_drizzle_orm9.eq)(pkColumn, ownPkValue));
|
|
1421
|
+
}
|
|
1422
|
+
await resolveFkThereFields(tx, fkThereEntries, ownPkValue);
|
|
1423
|
+
const readArg = await this.resolveReadArgs({ [this.pk]: ownPkValue }, options);
|
|
1424
|
+
const result = await this.getQueryBuilder(tx).findFirst(readArg);
|
|
1425
|
+
return result;
|
|
1426
|
+
});
|
|
1427
|
+
}
|
|
1383
1428
|
async updateMany(where, obj, options) {
|
|
1384
1429
|
try {
|
|
1385
1430
|
const executor = options?.db ?? this.db;
|
|
@@ -1441,15 +1486,15 @@ var DrizzleAdapter = class extends import_vsrepo10.VSRepoAdapter {
|
|
|
1441
1486
|
async upsert(where, create, update, options) {
|
|
1442
1487
|
try {
|
|
1443
1488
|
return await this.runTransactional(options?.db, async (tx) => {
|
|
1444
|
-
const current = await this.
|
|
1445
|
-
where: (await this.resolveFindWhere(where, { db: tx, limit: 1 })).where
|
|
1446
|
-
});
|
|
1489
|
+
const current = await this.findCurrentByWhere(where, { db: tx });
|
|
1447
1490
|
if (current) {
|
|
1448
1491
|
const ownPkValue = current[this.pk];
|
|
1449
|
-
return this.
|
|
1450
|
-
|
|
1451
|
-
|
|
1452
|
-
|
|
1492
|
+
return this.updateCore(
|
|
1493
|
+
{ [this.pk]: ownPkValue },
|
|
1494
|
+
update,
|
|
1495
|
+
{ ...options, db: tx },
|
|
1496
|
+
current
|
|
1497
|
+
);
|
|
1453
1498
|
}
|
|
1454
1499
|
return this.create(create, { ...options, db: tx });
|
|
1455
1500
|
});
|
|
@@ -1462,7 +1507,11 @@ var DrizzleAdapter = class extends import_vsrepo10.VSRepoAdapter {
|
|
|
1462
1507
|
try {
|
|
1463
1508
|
return await this.runTransactional(options?.db, async (tx) => {
|
|
1464
1509
|
const current = await this.getQueryBuilder(tx).findFirst({
|
|
1465
|
-
where: (await this.resolveFindWhere(where, { db: tx, limit: 1 })).where
|
|
1510
|
+
where: (await this.resolveFindWhere(where, { db: tx, limit: 1 })).where,
|
|
1511
|
+
// Only the pk is needed: atomic updates touch a single numeric
|
|
1512
|
+
// column and never resolve relations — a full-row read here
|
|
1513
|
+
// would ship the whole entity back to the client for nothing.
|
|
1514
|
+
columns: { [this.pk]: true }
|
|
1466
1515
|
});
|
|
1467
1516
|
if (!current) {
|
|
1468
1517
|
throw new import_vsrepo10.VSRepoAdapterError(
|
|
@@ -1526,6 +1575,7 @@ var DrizzleAdapter = class extends import_vsrepo10.VSRepoAdapter {
|
|
|
1526
1575
|
};
|
|
1527
1576
|
// Annotate the CommonJS export names for ESM import in node:
|
|
1528
1577
|
0 && (module.exports = {
|
|
1529
|
-
DrizzleAdapter
|
|
1578
|
+
DrizzleAdapter,
|
|
1579
|
+
VSRepoDrizzleAdapter
|
|
1530
1580
|
});
|
|
1531
1581
|
//# sourceMappingURL=index.cjs.map
|