@tursodatabase/serverless 1.1.3 → 1.2.0-pre.2
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/README.md +8 -0
- package/dist/compat/index.cjs +133 -49
- package/dist/compat/index.js +133 -49
- package/dist/index.cjs +218 -92
- package/dist/index.d.cts +78 -15
- package/dist/index.d.ts +78 -15
- package/dist/index.js +218 -92
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -235,6 +235,67 @@ async function executePipeline(url, authToken, request, remoteEncryptionKey, sig
|
|
|
235
235
|
return response.json();
|
|
236
236
|
}
|
|
237
237
|
|
|
238
|
+
// src/args.ts
|
|
239
|
+
function normalizeArgs(args) {
|
|
240
|
+
if (args === void 0) return [];
|
|
241
|
+
if (Array.isArray(args)) return args;
|
|
242
|
+
if (args !== null && typeof args === "object" && args.constructor === Object) {
|
|
243
|
+
return args;
|
|
244
|
+
}
|
|
245
|
+
return [args];
|
|
246
|
+
}
|
|
247
|
+
function isQueryOptions(value) {
|
|
248
|
+
return value != null && typeof value === "object" && !Array.isArray(value) && Object.prototype.hasOwnProperty.call(value, "queryTimeout");
|
|
249
|
+
}
|
|
250
|
+
function splitBindParameters(bindParameters) {
|
|
251
|
+
if (bindParameters.length === 0) {
|
|
252
|
+
return { params: void 0, queryOptions: void 0 };
|
|
253
|
+
}
|
|
254
|
+
if (isQueryOptions(bindParameters[bindParameters.length - 1])) {
|
|
255
|
+
if (bindParameters.length === 1) {
|
|
256
|
+
return { params: void 0, queryOptions: bindParameters[0] };
|
|
257
|
+
}
|
|
258
|
+
return {
|
|
259
|
+
params: bindParameters.length === 2 ? bindParameters[0] : bindParameters.slice(0, -1),
|
|
260
|
+
queryOptions: bindParameters[bindParameters.length - 1]
|
|
261
|
+
};
|
|
262
|
+
}
|
|
263
|
+
return {
|
|
264
|
+
params: bindParameters.length === 1 ? bindParameters[0] : bindParameters,
|
|
265
|
+
queryOptions: void 0
|
|
266
|
+
};
|
|
267
|
+
}
|
|
268
|
+
function encodeSqlArgs(args = []) {
|
|
269
|
+
let positionalArgs = [];
|
|
270
|
+
let namedArgs = [];
|
|
271
|
+
if (Array.isArray(args)) {
|
|
272
|
+
positionalArgs = args.map(encodeValue);
|
|
273
|
+
} else {
|
|
274
|
+
const keys = Object.keys(args);
|
|
275
|
+
const isNumericKeys = keys.length > 0 && keys.every((key) => /^\d+$/.test(key));
|
|
276
|
+
if (isNumericKeys) {
|
|
277
|
+
const sortedKeys = keys.sort((a, b) => parseInt(a, 10) - parseInt(b, 10));
|
|
278
|
+
const maxIndex = parseInt(sortedKeys[sortedKeys.length - 1], 10);
|
|
279
|
+
positionalArgs = new Array(maxIndex);
|
|
280
|
+
for (const key of sortedKeys) {
|
|
281
|
+
const index = parseInt(key, 10) - 1;
|
|
282
|
+
positionalArgs[index] = encodeValue(args[key]);
|
|
283
|
+
}
|
|
284
|
+
for (let i = 0; i < positionalArgs.length; i++) {
|
|
285
|
+
if (positionalArgs[i] === void 0) {
|
|
286
|
+
positionalArgs[i] = { type: "null" };
|
|
287
|
+
}
|
|
288
|
+
}
|
|
289
|
+
} else {
|
|
290
|
+
namedArgs = Object.entries(args).map(([name, value]) => ({
|
|
291
|
+
name,
|
|
292
|
+
value: encodeValue(value)
|
|
293
|
+
}));
|
|
294
|
+
}
|
|
295
|
+
}
|
|
296
|
+
return { args: positionalArgs, namedArgs };
|
|
297
|
+
}
|
|
298
|
+
|
|
238
299
|
// src/session.ts
|
|
239
300
|
function normalizeUrl(url) {
|
|
240
301
|
return url.replace(/^libsql:\/\//, "https://");
|
|
@@ -312,41 +373,15 @@ var Session = class {
|
|
|
312
373
|
* @returns Promise resolving to the raw response and cursor entries
|
|
313
374
|
*/
|
|
314
375
|
async executeRaw(sql, args = [], queryOptions) {
|
|
315
|
-
|
|
316
|
-
let namedArgs = [];
|
|
317
|
-
if (Array.isArray(args)) {
|
|
318
|
-
positionalArgs = args.map(encodeValue);
|
|
319
|
-
} else {
|
|
320
|
-
const keys = Object.keys(args);
|
|
321
|
-
const isNumericKeys = keys.length > 0 && keys.every((key) => /^\d+$/.test(key));
|
|
322
|
-
if (isNumericKeys) {
|
|
323
|
-
const sortedKeys = keys.sort((a, b) => parseInt(a) - parseInt(b));
|
|
324
|
-
const maxIndex = parseInt(sortedKeys[sortedKeys.length - 1]);
|
|
325
|
-
positionalArgs = new Array(maxIndex);
|
|
326
|
-
for (const key of sortedKeys) {
|
|
327
|
-
const index = parseInt(key) - 1;
|
|
328
|
-
positionalArgs[index] = encodeValue(args[key]);
|
|
329
|
-
}
|
|
330
|
-
for (let i = 0; i < positionalArgs.length; i++) {
|
|
331
|
-
if (positionalArgs[i] === void 0) {
|
|
332
|
-
positionalArgs[i] = { type: "null" };
|
|
333
|
-
}
|
|
334
|
-
}
|
|
335
|
-
} else {
|
|
336
|
-
namedArgs = Object.entries(args).map(([name, value]) => ({
|
|
337
|
-
name,
|
|
338
|
-
value: encodeValue(value)
|
|
339
|
-
}));
|
|
340
|
-
}
|
|
341
|
-
}
|
|
376
|
+
const encodedArgs = encodeSqlArgs(args);
|
|
342
377
|
const request = {
|
|
343
378
|
baton: this.baton,
|
|
344
379
|
batch: {
|
|
345
380
|
steps: [{
|
|
346
381
|
stmt: {
|
|
347
382
|
sql,
|
|
348
|
-
args:
|
|
349
|
-
named_args: namedArgs,
|
|
383
|
+
args: encodedArgs.args,
|
|
384
|
+
named_args: encodedArgs.namedArgs,
|
|
350
385
|
want_rows: true
|
|
351
386
|
}
|
|
352
387
|
}]
|
|
@@ -437,23 +472,76 @@ var Session = class {
|
|
|
437
472
|
}
|
|
438
473
|
/**
|
|
439
474
|
* Execute multiple SQL statements in a batch.
|
|
440
|
-
*
|
|
441
|
-
*
|
|
442
|
-
*
|
|
475
|
+
*
|
|
476
|
+
* When `mode` is set, the batch is sent as a single Hrana request that
|
|
477
|
+
* also carries `BEGIN <mode>` / `COMMIT` / `ROLLBACK` steps using the
|
|
478
|
+
* server-side condition chain, giving atomic execution in one round-trip.
|
|
479
|
+
* When `mode` is omitted, the user statements are sent as-is and run
|
|
480
|
+
* under autocommit (or whatever transaction is already active on this
|
|
481
|
+
* stream).
|
|
482
|
+
*
|
|
483
|
+
* @param statements - Array of SQL statements to execute.
|
|
484
|
+
* @param mode - Optional locking mode; when set, the batch executes
|
|
485
|
+
* atomically. Accepts the same values as `Database.transaction(...)`
|
|
486
|
+
* variants: `"deferred"`, `"immediate"`, `"exclusive"`, `"concurrent"`.
|
|
487
|
+
* @returns Promise resolving to batch execution results.
|
|
443
488
|
*/
|
|
444
|
-
async batch(statements, queryOptions) {
|
|
489
|
+
async batch(statements, mode, queryOptions) {
|
|
490
|
+
const userSteps = statements.map((statement) => {
|
|
491
|
+
if (typeof statement === "string") {
|
|
492
|
+
return {
|
|
493
|
+
stmt: { sql: statement, args: [], named_args: [], want_rows: false }
|
|
494
|
+
};
|
|
495
|
+
}
|
|
496
|
+
const encodedArgs = encodeSqlArgs(statement.args ?? []);
|
|
497
|
+
return {
|
|
498
|
+
stmt: {
|
|
499
|
+
sql: statement.sql,
|
|
500
|
+
args: encodedArgs.args,
|
|
501
|
+
named_args: encodedArgs.namedArgs,
|
|
502
|
+
want_rows: false
|
|
503
|
+
}
|
|
504
|
+
};
|
|
505
|
+
});
|
|
506
|
+
let steps;
|
|
507
|
+
let firstUserStepIdx = 0;
|
|
508
|
+
let lastUserStepIdx = userSteps.length - 1;
|
|
509
|
+
let beginIdx = -1;
|
|
510
|
+
let commitIdx = -1;
|
|
511
|
+
let rollbackIdx = -1;
|
|
512
|
+
if (mode === void 0) {
|
|
513
|
+
steps = userSteps;
|
|
514
|
+
} else {
|
|
515
|
+
beginIdx = 0;
|
|
516
|
+
firstUserStepIdx = 1;
|
|
517
|
+
lastUserStepIdx = userSteps.length;
|
|
518
|
+
commitIdx = lastUserStepIdx + 1;
|
|
519
|
+
rollbackIdx = commitIdx + 1;
|
|
520
|
+
steps = [
|
|
521
|
+
{ stmt: { sql: `BEGIN ${mode.toUpperCase()}`, args: [], named_args: [], want_rows: false } },
|
|
522
|
+
...userSteps.map((step, i) => ({
|
|
523
|
+
...step,
|
|
524
|
+
condition: { type: "ok", step: i === 0 ? beginIdx : firstUserStepIdx + i - 1 }
|
|
525
|
+
})),
|
|
526
|
+
{
|
|
527
|
+
stmt: { sql: "COMMIT", args: [], named_args: [], want_rows: false },
|
|
528
|
+
condition: { type: "ok", step: lastUserStepIdx }
|
|
529
|
+
},
|
|
530
|
+
{
|
|
531
|
+
stmt: { sql: "ROLLBACK", args: [], named_args: [], want_rows: false },
|
|
532
|
+
condition: {
|
|
533
|
+
type: "and",
|
|
534
|
+
conds: [
|
|
535
|
+
{ type: "ok", step: beginIdx },
|
|
536
|
+
{ type: "not", cond: { type: "ok", step: commitIdx } }
|
|
537
|
+
]
|
|
538
|
+
}
|
|
539
|
+
}
|
|
540
|
+
];
|
|
541
|
+
}
|
|
445
542
|
const request = {
|
|
446
543
|
baton: this.baton,
|
|
447
|
-
batch: {
|
|
448
|
-
steps: statements.map((sql) => ({
|
|
449
|
-
stmt: {
|
|
450
|
-
sql,
|
|
451
|
-
args: [],
|
|
452
|
-
named_args: [],
|
|
453
|
-
want_rows: false
|
|
454
|
-
}
|
|
455
|
-
}))
|
|
456
|
-
}
|
|
544
|
+
batch: { steps }
|
|
457
545
|
};
|
|
458
546
|
let batchResult;
|
|
459
547
|
try {
|
|
@@ -469,21 +557,46 @@ var Session = class {
|
|
|
469
557
|
}
|
|
470
558
|
let totalRowsAffected = 0;
|
|
471
559
|
let lastInsertRowid;
|
|
560
|
+
let deferredError = null;
|
|
561
|
+
let currentStep;
|
|
562
|
+
const isUserStep = (step) => {
|
|
563
|
+
if (mode === void 0) {
|
|
564
|
+
return true;
|
|
565
|
+
}
|
|
566
|
+
return step !== void 0 && step >= firstUserStepIdx && step <= lastUserStepIdx;
|
|
567
|
+
};
|
|
472
568
|
for await (const entry of entries) {
|
|
473
569
|
switch (entry.type) {
|
|
570
|
+
case "step_begin":
|
|
571
|
+
currentStep = entry.step;
|
|
572
|
+
break;
|
|
474
573
|
case "step_end":
|
|
475
|
-
if (
|
|
476
|
-
|
|
477
|
-
|
|
478
|
-
|
|
479
|
-
|
|
574
|
+
if (isUserStep(currentStep)) {
|
|
575
|
+
if (entry.affected_row_count !== void 0) {
|
|
576
|
+
totalRowsAffected += entry.affected_row_count;
|
|
577
|
+
}
|
|
578
|
+
if (entry.last_insert_rowid !== void 0 && entry.last_insert_rowid !== null) {
|
|
579
|
+
lastInsertRowid = typeof entry.last_insert_rowid === "number" ? entry.last_insert_rowid : parseInt(entry.last_insert_rowid, 10);
|
|
580
|
+
}
|
|
480
581
|
}
|
|
582
|
+
currentStep = void 0;
|
|
481
583
|
break;
|
|
482
584
|
case "step_error":
|
|
585
|
+
if (mode === void 0) {
|
|
586
|
+
throw new DatabaseError(entry.error?.message || "Batch execution failed", entry.error?.code);
|
|
587
|
+
}
|
|
588
|
+
if (deferredError === null && entry.step !== rollbackIdx) {
|
|
589
|
+
deferredError = new DatabaseError(entry.error?.message || "Batch execution failed", entry.error?.code);
|
|
590
|
+
}
|
|
591
|
+
currentStep = void 0;
|
|
592
|
+
break;
|
|
483
593
|
case "error":
|
|
484
594
|
throw new DatabaseError(entry.error?.message || "Batch execution failed", entry.error?.code);
|
|
485
595
|
}
|
|
486
596
|
}
|
|
597
|
+
if (deferredError !== null) {
|
|
598
|
+
throw deferredError;
|
|
599
|
+
}
|
|
487
600
|
return {
|
|
488
601
|
rowsAffected: totalRowsAffected,
|
|
489
602
|
lastInsertRowid
|
|
@@ -545,37 +658,6 @@ var Session = class {
|
|
|
545
658
|
}
|
|
546
659
|
};
|
|
547
660
|
|
|
548
|
-
// src/args.ts
|
|
549
|
-
function normalizeArgs(args) {
|
|
550
|
-
if (args === void 0) return [];
|
|
551
|
-
if (Array.isArray(args)) return args;
|
|
552
|
-
if (args !== null && typeof args === "object" && args.constructor === Object) {
|
|
553
|
-
return args;
|
|
554
|
-
}
|
|
555
|
-
return [args];
|
|
556
|
-
}
|
|
557
|
-
function isQueryOptions(value) {
|
|
558
|
-
return value != null && typeof value === "object" && !Array.isArray(value) && Object.prototype.hasOwnProperty.call(value, "queryTimeout");
|
|
559
|
-
}
|
|
560
|
-
function splitBindParameters(bindParameters) {
|
|
561
|
-
if (bindParameters.length === 0) {
|
|
562
|
-
return { params: void 0, queryOptions: void 0 };
|
|
563
|
-
}
|
|
564
|
-
if (isQueryOptions(bindParameters[bindParameters.length - 1])) {
|
|
565
|
-
if (bindParameters.length === 1) {
|
|
566
|
-
return { params: void 0, queryOptions: bindParameters[0] };
|
|
567
|
-
}
|
|
568
|
-
return {
|
|
569
|
-
params: bindParameters.length === 2 ? bindParameters[0] : bindParameters.slice(0, -1),
|
|
570
|
-
queryOptions: bindParameters[bindParameters.length - 1]
|
|
571
|
-
};
|
|
572
|
-
}
|
|
573
|
-
return {
|
|
574
|
-
params: bindParameters.length === 1 ? bindParameters[0] : bindParameters,
|
|
575
|
-
queryOptions: void 0
|
|
576
|
-
};
|
|
577
|
-
}
|
|
578
|
-
|
|
579
661
|
// src/statement.ts
|
|
580
662
|
var Statement = class _Statement {
|
|
581
663
|
constructor(sessionConfig, sql, columns) {
|
|
@@ -1007,20 +1089,61 @@ var Connection = class {
|
|
|
1007
1089
|
}
|
|
1008
1090
|
}
|
|
1009
1091
|
/**
|
|
1010
|
-
*
|
|
1011
|
-
*
|
|
1012
|
-
*
|
|
1013
|
-
*
|
|
1014
|
-
*
|
|
1015
|
-
*
|
|
1092
|
+
* Executes a batch of SQL statements over this connection.
|
|
1093
|
+
*
|
|
1094
|
+
* By default, batch() is not transactional: each statement runs in its
|
|
1095
|
+
* own autocommit step, so a failure mid-batch leaves earlier successful
|
|
1096
|
+
* statements committed. Pass a `mode` to make the batch atomic — the
|
|
1097
|
+
* statements are wrapped in `BEGIN <mode>` / `COMMIT` (with `ROLLBACK`
|
|
1098
|
+
* on failure) and dispatched as a single Hrana request, so the whole
|
|
1099
|
+
* batch completes in one round-trip. When called from inside a
|
|
1100
|
+
* `connection.transaction(...)` callback the `mode` argument is ignored
|
|
1101
|
+
* and the surrounding transaction is reused.
|
|
1102
|
+
*
|
|
1103
|
+
* When `mode` is set, `batch()` owns the surrounding
|
|
1104
|
+
* `BEGIN`/`COMMIT`/`ROLLBACK`, so the `statements` array must not
|
|
1105
|
+
* contain its own transaction-control SQL (`BEGIN`, `COMMIT`,
|
|
1106
|
+
* `ROLLBACK`, `SAVEPOINT`, `RELEASE`). The input is not validated
|
|
1107
|
+
* for that — a user-supplied `COMMIT` will close the wrapper
|
|
1108
|
+
* transaction mid-batch and leave earlier statements committed,
|
|
1109
|
+
* defeating the all-or-nothing contract.
|
|
1110
|
+
*
|
|
1111
|
+
* @param statements - An array of SQL strings or `{ sql, args }` objects.
|
|
1112
|
+
* @param mode - When set, makes the batch atomic. Accepts the same
|
|
1113
|
+
* values as `connection.transaction(...)` variants: `"deferred"`,
|
|
1114
|
+
* `"immediate"`, `"exclusive"`, `"concurrent"`. Ignored when already
|
|
1115
|
+
* inside a transaction.
|
|
1116
|
+
* @returns An object with `rowsAffected` (sum of affected rows) and
|
|
1117
|
+
* `lastInsertRowid` (rowid of the last successful insert).
|
|
1118
|
+
*
|
|
1016
1119
|
* @example
|
|
1017
|
-
*
|
|
1018
|
-
* await
|
|
1019
|
-
* "
|
|
1020
|
-
* "INSERT INTO users
|
|
1021
|
-
* "INSERT INTO users (name) VALUES ('Bob')"
|
|
1120
|
+
* // Plain SQL strings (non-atomic).
|
|
1121
|
+
* await db.batch([
|
|
1122
|
+
* "INSERT INTO users(name) VALUES ('Alice')",
|
|
1123
|
+
* "INSERT INTO users(name) VALUES ('Bob')",
|
|
1022
1124
|
* ]);
|
|
1023
|
-
*
|
|
1125
|
+
*
|
|
1126
|
+
* @example
|
|
1127
|
+
* // Positional and named bind parameters.
|
|
1128
|
+
* await db.batch([
|
|
1129
|
+
* { sql: "INSERT INTO users(name, email) VALUES (?, ?)", args: ["Carol", "carol@example.net"] },
|
|
1130
|
+
* { sql: "INSERT INTO users(name, email) VALUES (:name, :email)", args: { name: "Dave", email: "dave@example.net" } },
|
|
1131
|
+
* ]);
|
|
1132
|
+
*
|
|
1133
|
+
* @example
|
|
1134
|
+
* // Atomic via the mode parameter.
|
|
1135
|
+
* await db.batch([
|
|
1136
|
+
* { sql: "INSERT INTO users(name) VALUES (?)", args: ["Eve"] },
|
|
1137
|
+
* { sql: "INSERT INTO users(name) VALUES (?)", args: ["Frank"] },
|
|
1138
|
+
* ], "immediate");
|
|
1139
|
+
*
|
|
1140
|
+
* @example
|
|
1141
|
+
* // Atomic via the transaction() API for mixed workloads.
|
|
1142
|
+
* const txn = db.transaction(async () => {
|
|
1143
|
+
* await db.batch([{ sql: "INSERT INTO users(name) VALUES (?)", args: ["Eve"] }]);
|
|
1144
|
+
* await db.execute("UPDATE counters SET n = n + 1");
|
|
1145
|
+
* });
|
|
1146
|
+
* await txn.immediate();
|
|
1024
1147
|
*/
|
|
1025
1148
|
async batch(statements, mode, queryOptions) {
|
|
1026
1149
|
if (!this.isOpen) {
|
|
@@ -1028,7 +1151,8 @@ var Connection = class {
|
|
|
1028
1151
|
}
|
|
1029
1152
|
await this.execLock.acquire();
|
|
1030
1153
|
try {
|
|
1031
|
-
|
|
1154
|
+
const effectiveMode = this._inTransaction ? void 0 : mode;
|
|
1155
|
+
return await this.session.batch(statements, effectiveMode, queryOptions);
|
|
1032
1156
|
} finally {
|
|
1033
1157
|
this.execLock.release();
|
|
1034
1158
|
}
|
|
@@ -1101,12 +1225,14 @@ var Connection = class {
|
|
|
1101
1225
|
const properties = {
|
|
1102
1226
|
default: { value: wrapTxn("") },
|
|
1103
1227
|
deferred: { value: wrapTxn("DEFERRED") },
|
|
1228
|
+
concurrent: { value: wrapTxn("CONCURRENT") },
|
|
1104
1229
|
immediate: { value: wrapTxn("IMMEDIATE") },
|
|
1105
1230
|
exclusive: { value: wrapTxn("EXCLUSIVE") },
|
|
1106
1231
|
database: { value: this, enumerable: true }
|
|
1107
1232
|
};
|
|
1108
1233
|
Object.defineProperties(properties.default.value, properties);
|
|
1109
1234
|
Object.defineProperties(properties.deferred.value, properties);
|
|
1235
|
+
Object.defineProperties(properties.concurrent.value, properties);
|
|
1110
1236
|
Object.defineProperties(properties.immediate.value, properties);
|
|
1111
1237
|
Object.defineProperties(properties.exclusive.value, properties);
|
|
1112
1238
|
return properties.default.value;
|