@xylex-group/better-auth-athena 1.0.3 → 1.0.6
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/CONTRIBUTING.md +17 -0
- package/README.md +1 -1
- package/dist/index.cjs +466 -295
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +4 -0
- package/dist/index.d.ts +4 -0
- package/dist/index.js +467 -298
- package/dist/index.js.map +1 -1
- package/package.json +4 -5
package/dist/index.js
CHANGED
|
@@ -2,9 +2,7 @@
|
|
|
2
2
|
import {
|
|
3
3
|
createAdapterFactory
|
|
4
4
|
} from "better-auth/adapters";
|
|
5
|
-
import {
|
|
6
|
-
createClient
|
|
7
|
-
} from "@xylex-group/athena";
|
|
5
|
+
import { createClient } from "@xylex-group/athena";
|
|
8
6
|
|
|
9
7
|
// src/config.ts
|
|
10
8
|
import fs from "fs";
|
|
@@ -12,9 +10,9 @@ import path from "path";
|
|
|
12
10
|
import YAML from "yaml";
|
|
13
11
|
var defaultAthenaGlobalConfig = {
|
|
14
12
|
athena: {
|
|
15
|
-
url: "
|
|
16
|
-
apiKey: "",
|
|
17
|
-
client: "
|
|
13
|
+
url: "https://mirror3.athena-db.com",
|
|
14
|
+
apiKey: "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJyb2xlIjoiYXV0aGVudGljYXRlZCIsImVtYWlsIjoiZmxvcmlzQHh5bGV4LmFpIiwiZXhwIjoyNDk3MDMzNjY2fQ.LdPqTGaFq5pTokW1DA81WFjmG4nReJCOSKr3mFtXNoA",
|
|
15
|
+
client: "athena_logging"
|
|
18
16
|
}
|
|
19
17
|
};
|
|
20
18
|
var DEFAULT_CONFIG_FILENAME = "config.yaml";
|
|
@@ -89,7 +87,7 @@ function getAthenaGlobalConfig(options) {
|
|
|
89
87
|
return { config: cached, version };
|
|
90
88
|
}
|
|
91
89
|
|
|
92
|
-
// src/
|
|
90
|
+
// src/utils.ts
|
|
93
91
|
function toSnakeCase(key) {
|
|
94
92
|
return key.replace(/([a-z0-9])([A-Z])/g, "$1_$2").replace(/__/g, "_").toLowerCase();
|
|
95
93
|
}
|
|
@@ -134,7 +132,8 @@ function toDbRecord(data) {
|
|
|
134
132
|
);
|
|
135
133
|
return coerceDateFields(withDbKeys);
|
|
136
134
|
}
|
|
137
|
-
|
|
135
|
+
var defaultColumnMapper = (col) => hasUppercase(col) ? toSnakeCase(col) : col;
|
|
136
|
+
function applyWhere(builder, field, operator, value, columnMapper = defaultColumnMapper) {
|
|
138
137
|
const dbField = columnMapper(field);
|
|
139
138
|
switch (operator) {
|
|
140
139
|
case "eq":
|
|
@@ -167,6 +166,451 @@ function isMissingColumnError(error) {
|
|
|
167
166
|
const msg = String(error ?? "");
|
|
168
167
|
return msg.includes("specified column does not exist") || msg.includes("column does not exist");
|
|
169
168
|
}
|
|
169
|
+
function isSuccessMessageInError(error) {
|
|
170
|
+
const msg = String(error ?? "").toLowerCase();
|
|
171
|
+
return msg === "data inserted successfully" || msg === "data updated successfully" || msg === "data deleted successfully";
|
|
172
|
+
}
|
|
173
|
+
function snakeMapper(col) {
|
|
174
|
+
return hasUppercase(col) ? toSnakeCase(col) : col;
|
|
175
|
+
}
|
|
176
|
+
function identityMapper(col) {
|
|
177
|
+
return col;
|
|
178
|
+
}
|
|
179
|
+
function getRowValue(row, field) {
|
|
180
|
+
if (Object.prototype.hasOwnProperty.call(row, field)) return row[field];
|
|
181
|
+
const snake = hasUppercase(field) ? toSnakeCase(field) : field;
|
|
182
|
+
return row[snake];
|
|
183
|
+
}
|
|
184
|
+
function filterRowsByWhere(rows, where) {
|
|
185
|
+
if (!where.length) return rows;
|
|
186
|
+
return rows.filter((row) => {
|
|
187
|
+
for (const { field, operator, value } of where) {
|
|
188
|
+
const rowVal = getRowValue(row, field);
|
|
189
|
+
switch (operator) {
|
|
190
|
+
case "eq":
|
|
191
|
+
if (rowVal !== value) return false;
|
|
192
|
+
break;
|
|
193
|
+
case "ne":
|
|
194
|
+
if (rowVal === value) return false;
|
|
195
|
+
break;
|
|
196
|
+
case "in":
|
|
197
|
+
if (!Array.isArray(value) || !value.includes(rowVal)) return false;
|
|
198
|
+
break;
|
|
199
|
+
case "not_in":
|
|
200
|
+
if (Array.isArray(value) && value.includes(rowVal)) return false;
|
|
201
|
+
break;
|
|
202
|
+
default:
|
|
203
|
+
if (rowVal !== value) return false;
|
|
204
|
+
}
|
|
205
|
+
}
|
|
206
|
+
return true;
|
|
207
|
+
});
|
|
208
|
+
}
|
|
209
|
+
|
|
210
|
+
// src/methods/create.ts
|
|
211
|
+
function createMethod(deps) {
|
|
212
|
+
const { ensureDbClient } = deps;
|
|
213
|
+
return async function create({
|
|
214
|
+
model,
|
|
215
|
+
data
|
|
216
|
+
}) {
|
|
217
|
+
const db = ensureDbClient();
|
|
218
|
+
const insertData = toDbRecord(data);
|
|
219
|
+
const { data: result, error } = await db.from(model).insert(insertData).select();
|
|
220
|
+
if (error && !isSuccessMessageInError(error)) {
|
|
221
|
+
throw new Error(`[AthenaAdapter] create on "${model}" failed: ${error}`);
|
|
222
|
+
}
|
|
223
|
+
const row = Array.isArray(result) ? result[0] : result;
|
|
224
|
+
return mapRowToBetterAuth(row ?? insertData);
|
|
225
|
+
};
|
|
226
|
+
}
|
|
227
|
+
|
|
228
|
+
// src/methods/update.ts
|
|
229
|
+
function updateMethod(deps) {
|
|
230
|
+
const { ensureDbClient } = deps;
|
|
231
|
+
return async function update({
|
|
232
|
+
model,
|
|
233
|
+
where,
|
|
234
|
+
update
|
|
235
|
+
}) {
|
|
236
|
+
const db = ensureDbClient();
|
|
237
|
+
const updateData = toDbRecord(update);
|
|
238
|
+
const build = (useRetryShape) => {
|
|
239
|
+
if (!useRetryShape) {
|
|
240
|
+
let b2 = db.from(model).update({ data: updateData, set: updateData });
|
|
241
|
+
for (const clause of where) {
|
|
242
|
+
b2 = applyWhere(b2, clause.field, clause.operator, clause.value);
|
|
243
|
+
}
|
|
244
|
+
return b2;
|
|
245
|
+
}
|
|
246
|
+
let b = db.from(model).update(updateData, {
|
|
247
|
+
updateBody: { data: updateData, set: updateData }
|
|
248
|
+
});
|
|
249
|
+
for (const clause of where) {
|
|
250
|
+
b = applyWhere(b, clause.field, clause.operator, clause.value);
|
|
251
|
+
}
|
|
252
|
+
return b;
|
|
253
|
+
};
|
|
254
|
+
const run = async (b) => {
|
|
255
|
+
const { data: result, error } = await b.select();
|
|
256
|
+
return { result, error };
|
|
257
|
+
};
|
|
258
|
+
const first = await run(build(false));
|
|
259
|
+
if (first.error && !isSuccessMessageInError(first.error)) {
|
|
260
|
+
const msg = String(first.error);
|
|
261
|
+
if (msg.toLowerCase().includes("update payload required")) {
|
|
262
|
+
const retry = await run(build(true));
|
|
263
|
+
if (retry.error && !isSuccessMessageInError(retry.error)) {
|
|
264
|
+
throw new Error(
|
|
265
|
+
`[AthenaAdapter] update on "${model}" failed: ${retry.error}`
|
|
266
|
+
);
|
|
267
|
+
}
|
|
268
|
+
const row2 = Array.isArray(retry.result) ? retry.result[0] : retry.result;
|
|
269
|
+
return row2 ? mapRowToBetterAuth(row2) : null;
|
|
270
|
+
}
|
|
271
|
+
throw new Error(`[AthenaAdapter] update on "${model}" failed: ${first.error}`);
|
|
272
|
+
}
|
|
273
|
+
const row = Array.isArray(first.result) ? first.result[0] : first.result;
|
|
274
|
+
return row ? mapRowToBetterAuth(row) : null;
|
|
275
|
+
};
|
|
276
|
+
}
|
|
277
|
+
|
|
278
|
+
// src/methods/update-many.ts
|
|
279
|
+
function updateManyMethod(deps) {
|
|
280
|
+
const { ensureDbClient } = deps;
|
|
281
|
+
return async function updateMany({
|
|
282
|
+
model,
|
|
283
|
+
where,
|
|
284
|
+
update
|
|
285
|
+
}) {
|
|
286
|
+
const db = ensureDbClient();
|
|
287
|
+
const updateData = toDbRecord(update);
|
|
288
|
+
const build = (useRetryShape) => {
|
|
289
|
+
if (!useRetryShape) {
|
|
290
|
+
let b2 = db.from(model).update({ data: updateData, set: updateData });
|
|
291
|
+
for (const clause of where) {
|
|
292
|
+
b2 = applyWhere(b2, clause.field, clause.operator, clause.value);
|
|
293
|
+
}
|
|
294
|
+
return b2;
|
|
295
|
+
}
|
|
296
|
+
let b = db.from(model).update(updateData, {
|
|
297
|
+
updateBody: { data: updateData, set: updateData }
|
|
298
|
+
});
|
|
299
|
+
for (const clause of where) {
|
|
300
|
+
b = applyWhere(b, clause.field, clause.operator, clause.value);
|
|
301
|
+
}
|
|
302
|
+
return b;
|
|
303
|
+
};
|
|
304
|
+
const run = async (b) => {
|
|
305
|
+
const { data: result, error } = await b.select();
|
|
306
|
+
return { result, error };
|
|
307
|
+
};
|
|
308
|
+
const first = await run(build(false));
|
|
309
|
+
if (first.error && !isSuccessMessageInError(first.error)) {
|
|
310
|
+
const msg = String(first.error);
|
|
311
|
+
if (msg.toLowerCase().includes("update payload required")) {
|
|
312
|
+
const retry = await run(build(true));
|
|
313
|
+
if (retry.error && !isSuccessMessageInError(retry.error)) {
|
|
314
|
+
throw new Error(
|
|
315
|
+
`[AthenaAdapter] updateMany on "${model}" failed: ${retry.error}`
|
|
316
|
+
);
|
|
317
|
+
}
|
|
318
|
+
return Array.isArray(retry.result) ? retry.result.length : retry.result ? 1 : 0;
|
|
319
|
+
}
|
|
320
|
+
throw new Error(
|
|
321
|
+
`[AthenaAdapter] updateMany on "${model}" failed: ${first.error}`
|
|
322
|
+
);
|
|
323
|
+
}
|
|
324
|
+
return Array.isArray(first.result) ? first.result.length : first.result ? 1 : 0;
|
|
325
|
+
};
|
|
326
|
+
}
|
|
327
|
+
|
|
328
|
+
// src/methods/delete.ts
|
|
329
|
+
function deleteMethod(deps) {
|
|
330
|
+
const { ensureDbClient, headers } = deps;
|
|
331
|
+
return async function del({
|
|
332
|
+
model,
|
|
333
|
+
where
|
|
334
|
+
}) {
|
|
335
|
+
const db = ensureDbClient();
|
|
336
|
+
let builder = db.from(model);
|
|
337
|
+
for (const clause of where) {
|
|
338
|
+
builder = applyWhere(
|
|
339
|
+
builder,
|
|
340
|
+
clause.field,
|
|
341
|
+
clause.operator,
|
|
342
|
+
clause.value
|
|
343
|
+
);
|
|
344
|
+
}
|
|
345
|
+
const { error } = await builder.delete(
|
|
346
|
+
headers ? { headers } : void 0
|
|
347
|
+
);
|
|
348
|
+
if (error && !isSuccessMessageInError(error)) {
|
|
349
|
+
throw new Error(
|
|
350
|
+
`[AthenaAdapter] delete on "${model}" failed: ${error}`
|
|
351
|
+
);
|
|
352
|
+
}
|
|
353
|
+
};
|
|
354
|
+
}
|
|
355
|
+
|
|
356
|
+
// src/methods/delete-many.ts
|
|
357
|
+
function deleteManyMethod(deps) {
|
|
358
|
+
const { ensureDbClient, headers } = deps;
|
|
359
|
+
return async function deleteMany({
|
|
360
|
+
model,
|
|
361
|
+
where
|
|
362
|
+
}) {
|
|
363
|
+
const db = ensureDbClient();
|
|
364
|
+
const mainBuilder = db.from(model);
|
|
365
|
+
let builder = mainBuilder;
|
|
366
|
+
for (const clause of where) {
|
|
367
|
+
builder = applyWhere(
|
|
368
|
+
builder,
|
|
369
|
+
clause.field,
|
|
370
|
+
clause.operator,
|
|
371
|
+
clause.value
|
|
372
|
+
);
|
|
373
|
+
}
|
|
374
|
+
const { data: result, error } = await builder.delete(
|
|
375
|
+
headers ? { headers } : void 0
|
|
376
|
+
).select();
|
|
377
|
+
if (error && !isSuccessMessageInError(error)) {
|
|
378
|
+
throw new Error(
|
|
379
|
+
`[AthenaAdapter] deleteMany on "${model}" failed: ${error}`
|
|
380
|
+
);
|
|
381
|
+
}
|
|
382
|
+
const deletedCount = Array.isArray(result) ? result.length : result ? 1 : 0;
|
|
383
|
+
const inClause = where.find(
|
|
384
|
+
(c) => c.operator === "in" && c.value != null
|
|
385
|
+
);
|
|
386
|
+
if (inClause && Array.isArray(inClause.value) && deletedCount < inClause.value.length) {
|
|
387
|
+
let n = 0;
|
|
388
|
+
for (const v of inClause.value) {
|
|
389
|
+
const b = db.from(model);
|
|
390
|
+
const filtered = applyWhere(
|
|
391
|
+
b,
|
|
392
|
+
inClause.field,
|
|
393
|
+
"eq",
|
|
394
|
+
v
|
|
395
|
+
);
|
|
396
|
+
const { data: rowData, error: rowErr } = await filtered.delete(
|
|
397
|
+
headers ? { headers } : void 0
|
|
398
|
+
).select();
|
|
399
|
+
if (rowErr && isSuccessMessageInError(rowErr)) continue;
|
|
400
|
+
n += Array.isArray(rowData) ? rowData.length : rowData ? 1 : 0;
|
|
401
|
+
}
|
|
402
|
+
return n;
|
|
403
|
+
}
|
|
404
|
+
return deletedCount;
|
|
405
|
+
};
|
|
406
|
+
}
|
|
407
|
+
|
|
408
|
+
// src/methods/find-one.ts
|
|
409
|
+
function findOneMethod(deps) {
|
|
410
|
+
const { ensureDbClient } = deps;
|
|
411
|
+
return async function findOne({
|
|
412
|
+
model,
|
|
413
|
+
where,
|
|
414
|
+
select
|
|
415
|
+
}) {
|
|
416
|
+
const db = ensureDbClient();
|
|
417
|
+
const run = async (columnMapper) => {
|
|
418
|
+
const columns = select && select.length > 0 ? select.map((c) => columnMapper(c)).join(", ") : void 0;
|
|
419
|
+
let builder = db.from(model).select(columns);
|
|
420
|
+
for (const clause of where) {
|
|
421
|
+
builder = applyWhere(
|
|
422
|
+
builder,
|
|
423
|
+
clause.field,
|
|
424
|
+
clause.operator,
|
|
425
|
+
clause.value,
|
|
426
|
+
columnMapper
|
|
427
|
+
);
|
|
428
|
+
}
|
|
429
|
+
const { data: result, error } = await builder.limit(1);
|
|
430
|
+
return { result, error };
|
|
431
|
+
};
|
|
432
|
+
const first = await run(snakeMapper);
|
|
433
|
+
if (first.error) {
|
|
434
|
+
if (isMissingColumnError(first.error)) {
|
|
435
|
+
const retry = await run(identityMapper);
|
|
436
|
+
if (retry.error) {
|
|
437
|
+
throw new Error(
|
|
438
|
+
`[AthenaAdapter] findOne on "${model}" failed: ${retry.error}`
|
|
439
|
+
);
|
|
440
|
+
}
|
|
441
|
+
const rows2 = Array.isArray(retry.result) ? retry.result : retry.result ? [retry.result] : [];
|
|
442
|
+
const row2 = rows2[0] ?? null;
|
|
443
|
+
return row2 ? mapRowToBetterAuth(row2) : null;
|
|
444
|
+
}
|
|
445
|
+
throw new Error(
|
|
446
|
+
`[AthenaAdapter] findOne on "${model}" failed: ${first.error}`
|
|
447
|
+
);
|
|
448
|
+
}
|
|
449
|
+
const rows = Array.isArray(first.result) ? first.result : first.result ? [first.result] : [];
|
|
450
|
+
const row = rows[0] ?? null;
|
|
451
|
+
if (!row) return null;
|
|
452
|
+
const mapped = mapRowToBetterAuth(row);
|
|
453
|
+
const filtered = filterRowsByWhere(
|
|
454
|
+
[mapped],
|
|
455
|
+
where
|
|
456
|
+
);
|
|
457
|
+
return filtered[0] ? mapped : null;
|
|
458
|
+
};
|
|
459
|
+
}
|
|
460
|
+
|
|
461
|
+
// src/methods/find-many.ts
|
|
462
|
+
function findManyMethod(deps) {
|
|
463
|
+
const { ensureDbClient } = deps;
|
|
464
|
+
return async function findMany({
|
|
465
|
+
model,
|
|
466
|
+
where,
|
|
467
|
+
limit,
|
|
468
|
+
sortBy,
|
|
469
|
+
offset,
|
|
470
|
+
select
|
|
471
|
+
}) {
|
|
472
|
+
const db = ensureDbClient();
|
|
473
|
+
const run = async (columnMapper, opts) => {
|
|
474
|
+
const columns = select && select.length > 0 ? select.map((c) => columnMapper(c)).join(", ") : void 0;
|
|
475
|
+
let builder = db.from(model).select(columns);
|
|
476
|
+
if (!opts?.skipWhere && where) {
|
|
477
|
+
for (const clause of where) {
|
|
478
|
+
builder = applyWhere(
|
|
479
|
+
builder,
|
|
480
|
+
clause.field,
|
|
481
|
+
clause.operator,
|
|
482
|
+
clause.value,
|
|
483
|
+
columnMapper
|
|
484
|
+
);
|
|
485
|
+
}
|
|
486
|
+
}
|
|
487
|
+
const effectiveLimit = opts?.limitOverride ?? limit;
|
|
488
|
+
const effectiveOffset = opts?.offsetOverride ?? offset;
|
|
489
|
+
if (effectiveLimit !== void 0) {
|
|
490
|
+
builder = builder.limit(effectiveLimit);
|
|
491
|
+
}
|
|
492
|
+
if (effectiveOffset !== void 0) {
|
|
493
|
+
builder = builder.offset(effectiveOffset);
|
|
494
|
+
}
|
|
495
|
+
const { data: result, error } = await builder;
|
|
496
|
+
return { result, error };
|
|
497
|
+
};
|
|
498
|
+
const first = await run(snakeMapper);
|
|
499
|
+
const pickRows = (res) => Array.isArray(res) ? res : [];
|
|
500
|
+
const applySort = (rows) => {
|
|
501
|
+
if (!sortBy) return rows;
|
|
502
|
+
const sortField = sortBy.field;
|
|
503
|
+
rows.sort((a, b) => {
|
|
504
|
+
const aVal = a[sortField];
|
|
505
|
+
const bVal = b[sortField];
|
|
506
|
+
if (aVal == null && bVal == null) return 0;
|
|
507
|
+
if (aVal == null) return sortBy.direction === "asc" ? -1 : 1;
|
|
508
|
+
if (bVal == null) return sortBy.direction === "asc" ? 1 : -1;
|
|
509
|
+
const cmp = typeof aVal === "string" && typeof bVal === "string" ? aVal.localeCompare(bVal) : aVal < bVal ? -1 : aVal > bVal ? 1 : 0;
|
|
510
|
+
return sortBy.direction === "asc" ? cmp : -cmp;
|
|
511
|
+
});
|
|
512
|
+
return rows;
|
|
513
|
+
};
|
|
514
|
+
const mapAndSort = (rows) => {
|
|
515
|
+
const betterAuthRows = rows.map((r) => mapRowToBetterAuth(r));
|
|
516
|
+
if (!where?.length) return applySort(betterAuthRows);
|
|
517
|
+
const filtered = filterRowsByWhere(
|
|
518
|
+
betterAuthRows,
|
|
519
|
+
where
|
|
520
|
+
);
|
|
521
|
+
if (filtered.length !== betterAuthRows.length) {
|
|
522
|
+
const off = offset ?? 0;
|
|
523
|
+
const end = limit !== void 0 ? off + limit : void 0;
|
|
524
|
+
return applySort(filtered).slice(off, end);
|
|
525
|
+
}
|
|
526
|
+
return applySort(filtered);
|
|
527
|
+
};
|
|
528
|
+
const postFilterAndSlice = (rows) => {
|
|
529
|
+
const mappedSorted = mapAndSort(rows);
|
|
530
|
+
const off = offset ?? 0;
|
|
531
|
+
const end = limit !== void 0 ? off + limit : void 0;
|
|
532
|
+
return mappedSorted.slice(off, end);
|
|
533
|
+
};
|
|
534
|
+
if (first.error) {
|
|
535
|
+
if (isMissingColumnError(first.error)) {
|
|
536
|
+
const retry = await run(identityMapper);
|
|
537
|
+
if (retry.error) {
|
|
538
|
+
throw new Error(
|
|
539
|
+
`[AthenaAdapter] findMany on "${model}" failed: ${retry.error}`
|
|
540
|
+
);
|
|
541
|
+
}
|
|
542
|
+
const retryRows = pickRows(retry.result);
|
|
543
|
+
if (where?.length) {
|
|
544
|
+
const broad = await run(identityMapper, {
|
|
545
|
+
skipWhere: true,
|
|
546
|
+
limitOverride: Math.max((offset ?? 0) + (limit ?? 0) + 500, 5e3),
|
|
547
|
+
offsetOverride: 0
|
|
548
|
+
});
|
|
549
|
+
if (!broad.error) return postFilterAndSlice(pickRows(broad.result));
|
|
550
|
+
}
|
|
551
|
+
return mapAndSort(retryRows);
|
|
552
|
+
}
|
|
553
|
+
throw new Error(
|
|
554
|
+
`[AthenaAdapter] findMany on "${model}" failed: ${first.error}`
|
|
555
|
+
);
|
|
556
|
+
}
|
|
557
|
+
const firstRows = pickRows(first.result);
|
|
558
|
+
if (where?.length) {
|
|
559
|
+
const broad = await run(snakeMapper, {
|
|
560
|
+
skipWhere: true,
|
|
561
|
+
limitOverride: Math.max((offset ?? 0) + (limit ?? 0) + 500, 5e3),
|
|
562
|
+
offsetOverride: 0
|
|
563
|
+
});
|
|
564
|
+
if (!broad.error) return postFilterAndSlice(pickRows(broad.result));
|
|
565
|
+
}
|
|
566
|
+
return mapAndSort(firstRows);
|
|
567
|
+
};
|
|
568
|
+
}
|
|
569
|
+
|
|
570
|
+
// src/methods/count.ts
|
|
571
|
+
function countMethod(deps) {
|
|
572
|
+
const { ensureDbClient } = deps;
|
|
573
|
+
return async function count({
|
|
574
|
+
model,
|
|
575
|
+
where
|
|
576
|
+
}) {
|
|
577
|
+
const db = ensureDbClient();
|
|
578
|
+
const run = async (columnMapper) => {
|
|
579
|
+
let builder = db.from(model).select();
|
|
580
|
+
if (where) {
|
|
581
|
+
for (const clause of where) {
|
|
582
|
+
builder = applyWhere(
|
|
583
|
+
builder,
|
|
584
|
+
clause.field,
|
|
585
|
+
clause.operator,
|
|
586
|
+
clause.value,
|
|
587
|
+
columnMapper
|
|
588
|
+
);
|
|
589
|
+
}
|
|
590
|
+
}
|
|
591
|
+
const { data: result, error } = await builder;
|
|
592
|
+
return { result, error };
|
|
593
|
+
};
|
|
594
|
+
const first = await run(snakeMapper);
|
|
595
|
+
if (first.error) {
|
|
596
|
+
if (isMissingColumnError(first.error)) {
|
|
597
|
+
const retry = await run(identityMapper);
|
|
598
|
+
if (retry.error) {
|
|
599
|
+
throw new Error(
|
|
600
|
+
`[AthenaAdapter] count on "${model}" failed: ${retry.error}`
|
|
601
|
+
);
|
|
602
|
+
}
|
|
603
|
+
return Array.isArray(retry.result) ? retry.result.length : 0;
|
|
604
|
+
}
|
|
605
|
+
throw new Error(
|
|
606
|
+
`[AthenaAdapter] count on "${model}" failed: ${first.error}`
|
|
607
|
+
);
|
|
608
|
+
}
|
|
609
|
+
return Array.isArray(first.result) ? first.result.length : 0;
|
|
610
|
+
};
|
|
611
|
+
}
|
|
612
|
+
|
|
613
|
+
// src/index.ts
|
|
170
614
|
var athenaAdapter = (config) => {
|
|
171
615
|
let dbClient = null;
|
|
172
616
|
let lastDbConfigVersion = -1;
|
|
@@ -175,7 +619,8 @@ var athenaAdapter = (config) => {
|
|
|
175
619
|
if (shouldUseFixedConfig) {
|
|
176
620
|
if (!dbClient) {
|
|
177
621
|
dbClient = createClient(config.url, config.apiKey, {
|
|
178
|
-
client: config.client
|
|
622
|
+
client: config.client,
|
|
623
|
+
...config.headers && { headers: config.headers }
|
|
179
624
|
});
|
|
180
625
|
}
|
|
181
626
|
return dbClient;
|
|
@@ -193,18 +638,21 @@ var athenaAdapter = (config) => {
|
|
|
193
638
|
);
|
|
194
639
|
}
|
|
195
640
|
if (!dbClient || version2 !== lastDbConfigVersion) {
|
|
196
|
-
dbClient = createClient(url, apiKey, {
|
|
641
|
+
dbClient = createClient(url, apiKey, {
|
|
642
|
+
client,
|
|
643
|
+
...config.headers && { headers: config.headers }
|
|
644
|
+
});
|
|
197
645
|
lastDbConfigVersion = version2;
|
|
198
646
|
}
|
|
199
647
|
return dbClient;
|
|
200
648
|
}
|
|
649
|
+
const deps = { ensureDbClient, headers: config.headers };
|
|
201
650
|
return createAdapterFactory({
|
|
202
651
|
config: {
|
|
203
652
|
adapterId: "athena",
|
|
204
653
|
adapterName: "Athena Adapter",
|
|
205
654
|
usePlural: config.usePlural ?? false,
|
|
206
655
|
debugLogs: config.debugLogs ?? false,
|
|
207
|
-
// Athena/Postgres supports all these natively
|
|
208
656
|
supportsJSON: true,
|
|
209
657
|
supportsDates: true,
|
|
210
658
|
supportsBooleans: true,
|
|
@@ -213,293 +661,14 @@ var athenaAdapter = (config) => {
|
|
|
213
661
|
},
|
|
214
662
|
adapter: () => {
|
|
215
663
|
return {
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
const insertData = toDbRecord(data);
|
|
225
|
-
const { data: result, error } = await db.from(model).insert(insertData).select();
|
|
226
|
-
if (error) {
|
|
227
|
-
throw new Error(
|
|
228
|
-
`[AthenaAdapter] create on "${model}" failed: ${error}`
|
|
229
|
-
);
|
|
230
|
-
}
|
|
231
|
-
const row = Array.isArray(result) ? result[0] : result;
|
|
232
|
-
return mapRowToBetterAuth(row ?? insertData);
|
|
233
|
-
},
|
|
234
|
-
// ------------------------------------------------------------------
|
|
235
|
-
// UPDATE
|
|
236
|
-
// ------------------------------------------------------------------
|
|
237
|
-
update: async ({
|
|
238
|
-
model,
|
|
239
|
-
where,
|
|
240
|
-
update
|
|
241
|
-
}) => {
|
|
242
|
-
const db = ensureDbClient();
|
|
243
|
-
const updateData = toDbRecord(update);
|
|
244
|
-
let builder = db.from(model).update({ set: updateData });
|
|
245
|
-
for (const clause of where) {
|
|
246
|
-
builder = applyWhere(
|
|
247
|
-
builder,
|
|
248
|
-
clause.field,
|
|
249
|
-
clause.operator,
|
|
250
|
-
clause.value
|
|
251
|
-
);
|
|
252
|
-
}
|
|
253
|
-
const { data: result, error } = await builder.select();
|
|
254
|
-
if (error) {
|
|
255
|
-
throw new Error(
|
|
256
|
-
`[AthenaAdapter] update on "${model}" failed: ${error}`
|
|
257
|
-
);
|
|
258
|
-
}
|
|
259
|
-
const row = Array.isArray(result) ? result[0] : result;
|
|
260
|
-
return row ? mapRowToBetterAuth(row) : null;
|
|
261
|
-
},
|
|
262
|
-
// ------------------------------------------------------------------
|
|
263
|
-
// UPDATE MANY
|
|
264
|
-
// ------------------------------------------------------------------
|
|
265
|
-
updateMany: async ({
|
|
266
|
-
model,
|
|
267
|
-
where,
|
|
268
|
-
update
|
|
269
|
-
}) => {
|
|
270
|
-
const db = ensureDbClient();
|
|
271
|
-
const updateData = toDbRecord(update);
|
|
272
|
-
let builder = db.from(model).update({ set: updateData });
|
|
273
|
-
for (const clause of where) {
|
|
274
|
-
builder = applyWhere(
|
|
275
|
-
builder,
|
|
276
|
-
clause.field,
|
|
277
|
-
clause.operator,
|
|
278
|
-
clause.value
|
|
279
|
-
);
|
|
280
|
-
}
|
|
281
|
-
const { data: result, error } = await builder.select();
|
|
282
|
-
if (error) {
|
|
283
|
-
throw new Error(
|
|
284
|
-
`[AthenaAdapter] updateMany on "${model}" failed: ${error}`
|
|
285
|
-
);
|
|
286
|
-
}
|
|
287
|
-
return Array.isArray(result) ? result.length : result ? 1 : 0;
|
|
288
|
-
},
|
|
289
|
-
// ------------------------------------------------------------------
|
|
290
|
-
// DELETE
|
|
291
|
-
// ------------------------------------------------------------------
|
|
292
|
-
delete: async ({
|
|
293
|
-
model,
|
|
294
|
-
where
|
|
295
|
-
}) => {
|
|
296
|
-
const db = ensureDbClient();
|
|
297
|
-
let builder = db.from(model);
|
|
298
|
-
for (const clause of where) {
|
|
299
|
-
builder = applyWhere(
|
|
300
|
-
builder,
|
|
301
|
-
clause.field,
|
|
302
|
-
clause.operator,
|
|
303
|
-
clause.value
|
|
304
|
-
);
|
|
305
|
-
}
|
|
306
|
-
const { error } = await builder.delete();
|
|
307
|
-
if (error) {
|
|
308
|
-
throw new Error(
|
|
309
|
-
`[AthenaAdapter] delete on "${model}" failed: ${error}`
|
|
310
|
-
);
|
|
311
|
-
}
|
|
312
|
-
},
|
|
313
|
-
// ------------------------------------------------------------------
|
|
314
|
-
// DELETE MANY
|
|
315
|
-
// ------------------------------------------------------------------
|
|
316
|
-
deleteMany: async ({
|
|
317
|
-
model,
|
|
318
|
-
where
|
|
319
|
-
}) => {
|
|
320
|
-
const db = ensureDbClient();
|
|
321
|
-
let builder = db.from(model);
|
|
322
|
-
for (const clause of where) {
|
|
323
|
-
builder = applyWhere(
|
|
324
|
-
builder,
|
|
325
|
-
clause.field,
|
|
326
|
-
clause.operator,
|
|
327
|
-
clause.value
|
|
328
|
-
);
|
|
329
|
-
}
|
|
330
|
-
const { data: result, error } = await builder.delete().select();
|
|
331
|
-
if (error) {
|
|
332
|
-
throw new Error(
|
|
333
|
-
`[AthenaAdapter] deleteMany on "${model}" failed: ${error}`
|
|
334
|
-
);
|
|
335
|
-
}
|
|
336
|
-
return Array.isArray(result) ? result.length : result ? 1 : 0;
|
|
337
|
-
},
|
|
338
|
-
// ------------------------------------------------------------------
|
|
339
|
-
// FIND ONE
|
|
340
|
-
// ------------------------------------------------------------------
|
|
341
|
-
findOne: async ({
|
|
342
|
-
model,
|
|
343
|
-
where,
|
|
344
|
-
select
|
|
345
|
-
}) => {
|
|
346
|
-
const db = ensureDbClient();
|
|
347
|
-
const snakeMapper = (col) => hasUppercase(col) ? toSnakeCase(col) : col;
|
|
348
|
-
const identityMapper = (col) => col;
|
|
349
|
-
const run = async (columnMapper) => {
|
|
350
|
-
const columns = select && select.length > 0 ? select.map((c) => columnMapper(c)).join(", ") : void 0;
|
|
351
|
-
let builder = db.from(model).select(columns);
|
|
352
|
-
for (const clause of where) {
|
|
353
|
-
builder = applyWhere(
|
|
354
|
-
builder,
|
|
355
|
-
clause.field,
|
|
356
|
-
clause.operator,
|
|
357
|
-
clause.value,
|
|
358
|
-
columnMapper
|
|
359
|
-
);
|
|
360
|
-
}
|
|
361
|
-
const { data: result, error } = await builder.limit(1);
|
|
362
|
-
return { result, error };
|
|
363
|
-
};
|
|
364
|
-
const first = await run(snakeMapper);
|
|
365
|
-
if (first.error) {
|
|
366
|
-
if (isMissingColumnError(first.error)) {
|
|
367
|
-
const retry = await run(identityMapper);
|
|
368
|
-
if (retry.error) {
|
|
369
|
-
throw new Error(
|
|
370
|
-
`[AthenaAdapter] findOne on "${model}" failed: ${retry.error}`
|
|
371
|
-
);
|
|
372
|
-
}
|
|
373
|
-
const rows2 = Array.isArray(retry.result) ? retry.result : retry.result ? [retry.result] : [];
|
|
374
|
-
const row2 = rows2[0] ?? null;
|
|
375
|
-
return row2 ? mapRowToBetterAuth(row2) : null;
|
|
376
|
-
}
|
|
377
|
-
throw new Error(
|
|
378
|
-
`[AthenaAdapter] findOne on "${model}" failed: ${first.error}`
|
|
379
|
-
);
|
|
380
|
-
}
|
|
381
|
-
const rows = Array.isArray(first.result) ? first.result : first.result ? [first.result] : [];
|
|
382
|
-
const row = rows[0] ?? null;
|
|
383
|
-
return row ? mapRowToBetterAuth(row) : null;
|
|
384
|
-
},
|
|
385
|
-
// ------------------------------------------------------------------
|
|
386
|
-
// FIND MANY
|
|
387
|
-
// ------------------------------------------------------------------
|
|
388
|
-
findMany: async ({
|
|
389
|
-
model,
|
|
390
|
-
where,
|
|
391
|
-
limit,
|
|
392
|
-
sortBy,
|
|
393
|
-
offset,
|
|
394
|
-
select
|
|
395
|
-
}) => {
|
|
396
|
-
const db = ensureDbClient();
|
|
397
|
-
const snakeMapper = (col) => hasUppercase(col) ? toSnakeCase(col) : col;
|
|
398
|
-
const identityMapper = (col) => col;
|
|
399
|
-
const run = async (columnMapper) => {
|
|
400
|
-
const columns = select && select.length > 0 ? select.map((c) => columnMapper(c)).join(", ") : void 0;
|
|
401
|
-
let builder = db.from(model).select(columns);
|
|
402
|
-
if (where) {
|
|
403
|
-
for (const clause of where) {
|
|
404
|
-
builder = applyWhere(
|
|
405
|
-
builder,
|
|
406
|
-
clause.field,
|
|
407
|
-
clause.operator,
|
|
408
|
-
clause.value,
|
|
409
|
-
columnMapper
|
|
410
|
-
);
|
|
411
|
-
}
|
|
412
|
-
}
|
|
413
|
-
if (limit !== void 0) {
|
|
414
|
-
builder = builder.limit(limit);
|
|
415
|
-
}
|
|
416
|
-
if (offset !== void 0) {
|
|
417
|
-
builder = builder.offset(offset);
|
|
418
|
-
}
|
|
419
|
-
const { data: result, error } = await builder;
|
|
420
|
-
return { result, error };
|
|
421
|
-
};
|
|
422
|
-
const first = await run(snakeMapper);
|
|
423
|
-
const pickRows = (res) => Array.isArray(res) ? res : [];
|
|
424
|
-
const applySort = (rows) => {
|
|
425
|
-
if (!sortBy) return rows;
|
|
426
|
-
const sortField = sortBy.field;
|
|
427
|
-
rows.sort((a, b) => {
|
|
428
|
-
const aVal = a[sortField];
|
|
429
|
-
const bVal = b[sortField];
|
|
430
|
-
if (aVal == null && bVal == null) return 0;
|
|
431
|
-
if (aVal == null) return sortBy.direction === "asc" ? -1 : 1;
|
|
432
|
-
if (bVal == null) return sortBy.direction === "asc" ? 1 : -1;
|
|
433
|
-
const cmp = typeof aVal === "string" && typeof bVal === "string" ? aVal.localeCompare(bVal) : aVal < bVal ? -1 : aVal > bVal ? 1 : 0;
|
|
434
|
-
return sortBy.direction === "asc" ? cmp : -cmp;
|
|
435
|
-
});
|
|
436
|
-
return rows;
|
|
437
|
-
};
|
|
438
|
-
const mapAndSort = (rows) => {
|
|
439
|
-
const betterAuthRows = rows.map(
|
|
440
|
-
(r) => mapRowToBetterAuth(r)
|
|
441
|
-
);
|
|
442
|
-
return applySort(betterAuthRows);
|
|
443
|
-
};
|
|
444
|
-
if (first.error) {
|
|
445
|
-
if (isMissingColumnError(first.error)) {
|
|
446
|
-
const retry = await run(identityMapper);
|
|
447
|
-
if (retry.error) {
|
|
448
|
-
throw new Error(
|
|
449
|
-
`[AthenaAdapter] findMany on "${model}" failed: ${retry.error}`
|
|
450
|
-
);
|
|
451
|
-
}
|
|
452
|
-
return mapAndSort(pickRows(retry.result));
|
|
453
|
-
}
|
|
454
|
-
throw new Error(
|
|
455
|
-
`[AthenaAdapter] findMany on "${model}" failed: ${first.error}`
|
|
456
|
-
);
|
|
457
|
-
}
|
|
458
|
-
return mapAndSort(pickRows(first.result));
|
|
459
|
-
},
|
|
460
|
-
// ------------------------------------------------------------------
|
|
461
|
-
// COUNT
|
|
462
|
-
// ------------------------------------------------------------------
|
|
463
|
-
count: async ({
|
|
464
|
-
model,
|
|
465
|
-
where
|
|
466
|
-
}) => {
|
|
467
|
-
const db = ensureDbClient();
|
|
468
|
-
const snakeMapper = (col) => hasUppercase(col) ? toSnakeCase(col) : col;
|
|
469
|
-
const identityMapper = (col) => col;
|
|
470
|
-
const run = async (columnMapper) => {
|
|
471
|
-
let builder = db.from(model).select();
|
|
472
|
-
if (where) {
|
|
473
|
-
for (const clause of where) {
|
|
474
|
-
builder = applyWhere(
|
|
475
|
-
builder,
|
|
476
|
-
clause.field,
|
|
477
|
-
clause.operator,
|
|
478
|
-
clause.value,
|
|
479
|
-
columnMapper
|
|
480
|
-
);
|
|
481
|
-
}
|
|
482
|
-
}
|
|
483
|
-
const { data: result, error } = await builder;
|
|
484
|
-
return { result, error };
|
|
485
|
-
};
|
|
486
|
-
const first = await run(snakeMapper);
|
|
487
|
-
if (first.error) {
|
|
488
|
-
if (isMissingColumnError(first.error)) {
|
|
489
|
-
const retry = await run(identityMapper);
|
|
490
|
-
if (retry.error) {
|
|
491
|
-
throw new Error(
|
|
492
|
-
`[AthenaAdapter] count on "${model}" failed: ${retry.error}`
|
|
493
|
-
);
|
|
494
|
-
}
|
|
495
|
-
return Array.isArray(retry.result) ? retry.result.length : 0;
|
|
496
|
-
}
|
|
497
|
-
throw new Error(
|
|
498
|
-
`[AthenaAdapter] count on "${model}" failed: ${first.error}`
|
|
499
|
-
);
|
|
500
|
-
}
|
|
501
|
-
return Array.isArray(first.result) ? first.result.length : 0;
|
|
502
|
-
}
|
|
664
|
+
create: createMethod(deps),
|
|
665
|
+
update: updateMethod(deps),
|
|
666
|
+
updateMany: updateManyMethod(deps),
|
|
667
|
+
delete: deleteMethod(deps),
|
|
668
|
+
deleteMany: deleteManyMethod(deps),
|
|
669
|
+
findOne: findOneMethod(deps),
|
|
670
|
+
findMany: findManyMethod(deps),
|
|
671
|
+
count: countMethod(deps)
|
|
503
672
|
};
|
|
504
673
|
}
|
|
505
674
|
});
|