encore.dev 1.46.17 → 1.46.18

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.
@@ -34,38 +34,14 @@ export type Primitive =
34
34
  | null
35
35
  | undefined;
36
36
 
37
- /**
38
- * Constructing a new database object will result in Encore provisioning a database with
39
- * that name and returning this object to represent it.
40
- *
41
- * If you want to reference an existing database, use `Database.Named(name)` as it is a
42
- * compile error to create duplicate databases.
43
- */
44
- export class SQLDatabase {
45
- private readonly impl: runtime.SQLDatabase;
46
-
47
- /**
48
- * Creates a new database with the given name and configuration
49
- */
50
- // eslint-disable-next-line @typescript-eslint/no-unused-vars
51
- constructor(name: string, cfg?: SQLDatabaseConfig) {
52
- this.impl = runtime.RT.sqlDatabase(name);
53
- }
54
-
55
- /**
56
- * Reference an existing database by name, if the database doesn't
57
- * exist yet, use `new Database(name)` instead.
58
- */
59
- static named<name extends string>(name: StringLiteral<name>): SQLDatabase {
60
- return new SQLDatabase(name);
61
- }
37
+ type SQLQueryExecutor =
38
+ | runtime.SQLConn
39
+ | runtime.SQLDatabase
40
+ | runtime.Transaction;
62
41
 
63
- /**
64
- * Returns the connection string for the database
65
- */
66
- get connectionString(): string {
67
- return this.impl.connString();
68
- }
42
+ /** Base class containing shared query functionality */
43
+ class BaseQueryExecutor {
44
+ constructor(protected readonly impl: SQLQueryExecutor) {}
69
45
 
70
46
  /**
71
47
  * query queries the database using a template string, replacing your placeholders in the template
@@ -92,9 +68,7 @@ export class SQLDatabase {
92
68
  const cursor = await this.impl.query(query, args, source);
93
69
  while (true) {
94
70
  const row = await cursor.next();
95
- if (row === null) {
96
- break;
97
- }
71
+ if (row === null) break;
98
72
  yield row.values() as T;
99
73
  }
100
74
  }
@@ -125,10 +99,7 @@ export class SQLDatabase {
125
99
  const result = await this.impl.query(query, args, source);
126
100
  while (true) {
127
101
  const row = await result.next();
128
- if (row === null) {
129
- break;
130
- }
131
-
102
+ if (row === null) break;
132
103
  yield row.values() as T;
133
104
  }
134
105
  }
@@ -158,12 +129,9 @@ export class SQLDatabase {
158
129
  const result: T[] = [];
159
130
  while (true) {
160
131
  const row = await cursor.next();
161
- if (row === null) {
162
- break;
163
- }
132
+ if (row === null) break;
164
133
  result.push(row.values() as T);
165
134
  }
166
-
167
135
  return result;
168
136
  }
169
137
 
@@ -189,12 +157,9 @@ export class SQLDatabase {
189
157
  const result: T[] = [];
190
158
  while (true) {
191
159
  const row = await cursor.next();
192
- if (row === null) {
193
- break;
194
- }
160
+ if (row === null) break;
195
161
  result.push(row.values() as T);
196
162
  }
197
-
198
163
  return result;
199
164
  }
200
165
 
@@ -216,10 +181,8 @@ export class SQLDatabase {
216
181
  const args = buildQueryArgs(params);
217
182
  const source = getCurrentRequest();
218
183
  const result = await this.impl.query(query, args, source);
219
- while (true) {
220
- const row = await result.next();
221
- return row ? (row.values() as T) : null;
222
- }
184
+ const row = await result.next();
185
+ return row ? (row.values() as T) : null;
223
186
  }
224
187
 
225
188
  /**
@@ -244,10 +207,8 @@ export class SQLDatabase {
244
207
  const args = buildQueryArgs(params);
245
208
  const source = getCurrentRequest();
246
209
  const result = await this.impl.query(query, args, source);
247
- while (true) {
248
- const row = await result.next();
249
- return row ? (row.values() as T) : null;
250
- }
210
+ const row = await result.next();
211
+ return row ? (row.values() as T) : null;
251
212
  }
252
213
 
253
214
  /**
@@ -267,7 +228,7 @@ export class SQLDatabase {
267
228
 
268
229
  // Need to await the cursor to process any errors from things like
269
230
  // unique constraint violations.
270
- let cur = await this.impl.query(query, args, source);
231
+ const cur = await this.impl.query(query, args, source);
271
232
  await cur.next();
272
233
  }
273
234
 
@@ -289,263 +250,112 @@ export class SQLDatabase {
289
250
 
290
251
  // Need to await the cursor to process any errors from things like
291
252
  // unique constraint violations.
292
- let cur = await this.impl.query(query, args, source);
253
+ const cur = await this.impl.query(query, args, source);
293
254
  await cur.next();
294
255
  }
295
-
296
- /**
297
- * Acquires a database connection from the database pool.
298
- *
299
- * When the connection is closed or is garbage-collected, it is returned to the pool.
300
- * @returns a new connection to the database
301
- */
302
- async acquire(): Promise<Connection> {
303
- const impl = await this.impl.acquire();
304
- return new Connection(impl);
305
- }
306
256
  }
307
257
 
308
258
  /**
309
- * Represents a dedicated connection to a database.
259
+ * Constructing a new database object will result in Encore provisioning a database with
260
+ * that name and returning this object to represent it.
261
+ *
262
+ * If you want to reference an existing database, use `Database.Named(name)` as it is a
263
+ * compile error to create duplicate databases.
310
264
  */
311
- export class Connection {
312
- private readonly impl: runtime.SQLConn;
265
+ export class SQLDatabase extends BaseQueryExecutor {
266
+ protected declare readonly impl: runtime.SQLDatabase;
313
267
 
314
- constructor(impl: runtime.SQLConn) {
315
- this.impl = impl;
268
+ constructor(name: string, cfg?: SQLDatabaseConfig) {
269
+ super(runtime.RT.sqlDatabase(name));
316
270
  }
317
271
 
318
272
  /**
319
- * Returns the connection to the database pool.
273
+ * Reference an existing database by name, if the database doesn't
274
+ * exist yet, use `new Database(name)` instead.
320
275
  */
321
- async close() {
322
- await this.impl.close();
276
+ static named<name extends string>(name: StringLiteral<name>): SQLDatabase {
277
+ return new SQLDatabase(name);
323
278
  }
324
279
 
325
280
  /**
326
- * query queries the database using a template string, replacing your placeholders in the template
327
- * with parametrised values without risking SQL injections.
328
- *
329
- * It returns an async generator, that allows iterating over the results
330
- * in a streaming fashion using `for await`.
331
- *
332
- * @example
333
- *
334
- * const email = "foo@example.com";
335
- * const result = database.query`SELECT id FROM users WHERE email=${email}`
336
- *
337
- * This produces the query: "SELECT id FROM users WHERE email=$1".
281
+ * Returns the connection string for the database
338
282
  */
339
- // eslint-disable-next-line @typescript-eslint/no-explicit-any
340
- async *query<T extends Row = Record<string, any>>(
341
- strings: TemplateStringsArray,
342
- ...params: Primitive[]
343
- ): AsyncGenerator<T> {
344
- const query = buildQuery(strings, params);
345
- const args = buildQueryArgs(params);
346
- const source = getCurrentRequest();
347
- const cursor = await this.impl.query(query, args, source);
348
- while (true) {
349
- const row = await cursor.next();
350
- if (row === null) {
351
- break;
352
- }
353
- yield row.values() as T;
354
- }
283
+ get connectionString(): string {
284
+ return this.impl.connString();
355
285
  }
356
286
 
357
287
  /**
358
- * rawQuery queries the database using a raw parametrised SQL query and parameters.
359
- *
360
- * It returns an async generator, that allows iterating over the results
361
- * in a streaming fashion using `for await`.
362
- *
363
- * @example
364
- * const query = "SELECT id FROM users WHERE email=$1";
365
- * const email = "foo@example.com";
366
- * for await (const row of database.rawQuery(query, email)) {
367
- * console.log(row);
368
- * }
288
+ * Acquires a database connection from the database pool.
369
289
  *
370
- * @param query - The raw SQL query string.
371
- * @param params - The parameters to be used in the query.
372
- * @returns An async generator that yields rows from the query result.
290
+ * When the connection is closed or is garbage-collected, it is returned to the pool.
291
+ * @returns a new connection to the database
373
292
  */
374
- async *rawQuery<T extends Row = Record<string, any>>(
375
- query: string,
376
- ...params: Primitive[]
377
- ): AsyncGenerator<T> {
378
- const args = buildQueryArgs(params);
379
- const source = getCurrentRequest();
380
- const result = await this.impl.query(query, args, source);
381
- while (true) {
382
- const row = await result.next();
383
- if (row === null) {
384
- break;
385
- }
386
-
387
- yield row.values() as T;
388
- }
293
+ async acquire(): Promise<Connection> {
294
+ const impl = await this.impl.acquire();
295
+ return new Connection(impl);
389
296
  }
390
297
 
391
298
  /**
392
- * queryAll queries the database using a template string, replacing your placeholders in the template
393
- * with parametrised values without risking SQL injections.
394
- *
395
- * It returns an array of all results.
396
- *
397
- * @example
299
+ * Begins a database transaction.
398
300
  *
399
- * const email = "foo@example.com";
400
- * const result = database.queryAll`SELECT id FROM users WHERE email=${email}`
401
- *
402
- * This produces the query: "SELECT id FROM users WHERE email=$1".
301
+ * Make sure to always call `rollback` or `commit` to prevent hanging transactions.
302
+ * @returns a transaction object that implements AsycDisposable
403
303
  */
404
- // eslint-disable-next-line @typescript-eslint/no-explicit-any
405
- async queryAll<T extends Row = Record<string, any>>(
406
- strings: TemplateStringsArray,
407
- ...params: Primitive[]
408
- ): Promise<T[]> {
409
- const query = buildQuery(strings, params);
410
- const args = buildQueryArgs(params);
304
+ async begin(): Promise<Transaction> {
411
305
  const source = getCurrentRequest();
412
- const cursor = await this.impl.query(query, args, source);
413
- const result: T[] = [];
414
- while (true) {
415
- const row = await cursor.next();
416
- if (row === null) {
417
- break;
418
- }
419
- result.push(row.values() as T);
420
- }
421
-
422
- return result;
306
+ const impl = await this.impl.begin(source);
307
+ return new Transaction(impl);
423
308
  }
309
+ }
424
310
 
425
- /**
426
- * rawQueryAll queries the database using a raw parametrised SQL query and parameters.
427
- *
428
- * It returns an array of all results.
429
- *
430
- * @example
431
- *
432
- * const query = "SELECT id FROM users WHERE email=$1";
433
- * const email = "foo@example.com";
434
- * const rows = await database.rawQueryAll(query, email);
435
- */
436
- // eslint-disable-next-line @typescript-eslint/no-explicit-any
437
- async rawQueryAll<T extends Row = Record<string, any>>(
438
- query: string,
439
- ...params: Primitive[]
440
- ): Promise<T[]> {
441
- const args = buildQueryArgs(params);
442
- const source = getCurrentRequest();
443
- const cursor = await this.impl.query(query, args, source);
444
- const result: T[] = [];
445
- while (true) {
446
- const row = await cursor.next();
447
- if (row === null) {
448
- break;
449
- }
450
- result.push(row.values() as T);
451
- }
311
+ export class Transaction extends BaseQueryExecutor implements AsyncDisposable {
312
+ protected declare readonly impl: runtime.Transaction;
313
+ private done: boolean = false;
452
314
 
453
- return result;
315
+ constructor(impl: runtime.Transaction) {
316
+ super(impl);
454
317
  }
455
318
 
456
319
  /**
457
- * queryRow is like query but returns only a single row.
458
- * If the query selects no rows it returns null.
459
- * Otherwise it returns the first row and discards the rest.
460
- *
461
- * @example
462
- * const email = "foo@example.com";
463
- * const result = database.queryRow`SELECT id FROM users WHERE email=${email}`
320
+ * Commit the transaction.
464
321
  */
465
- // eslint-disable-next-line @typescript-eslint/no-explicit-any
466
- async queryRow<T extends Row = Record<string, any>>(
467
- strings: TemplateStringsArray,
468
- ...params: Primitive[]
469
- ): Promise<T | null> {
470
- const query = buildQuery(strings, params);
471
- const args = buildQueryArgs(params);
322
+ async commit() {
323
+ this.done = true;
472
324
  const source = getCurrentRequest();
473
- const result = await this.impl.query(query, args, source);
474
- while (true) {
475
- const row = await result.next();
476
- return row ? (row.values() as T) : null;
477
- }
325
+ await this.impl.commit(source);
478
326
  }
479
327
 
480
328
  /**
481
- * rawQueryRow is like rawQuery but returns only a single row.
482
- * If the query selects no rows, it returns null.
483
- * Otherwise, it returns the first row and discards the rest.
484
- *
485
- * @example
486
- * const query = "SELECT id FROM users WHERE email=$1";
487
- * const email = "foo@example.com";
488
- * const result = await database.rawQueryRow(query, email);
489
- * console.log(result);
490
- *
491
- * @param query - The raw SQL query string.
492
- * @param params - The parameters to be used in the query.
493
- * @returns A promise that resolves to a single row or null.
329
+ * Rollback the transaction.
494
330
  */
495
- async rawQueryRow<T extends Row = Record<string, any>>(
496
- query: string,
497
- ...params: Primitive[]
498
- ): Promise<T | null> {
499
- const args = buildQueryArgs(params);
331
+ async rollback() {
332
+ this.done = true;
500
333
  const source = getCurrentRequest();
501
- const result = await this.impl.query(query, args, source);
502
- while (true) {
503
- const row = await result.next();
504
- return row ? (row.values() as T) : null;
334
+ await this.impl.rollback(source);
335
+ }
336
+
337
+ async [Symbol.asyncDispose]() {
338
+ if (!this.done) {
339
+ await this.rollback();
505
340
  }
506
341
  }
342
+ }
507
343
 
508
- /**
509
- * exec executes a query without returning any rows.
510
- *
511
- * @example
512
- * const email = "foo@example.com";
513
- * const result = database.exec`DELETE FROM users WHERE email=${email}`
514
- */
515
- async exec(
516
- strings: TemplateStringsArray,
517
- ...params: Primitive[]
518
- ): Promise<void> {
519
- const query = buildQuery(strings, params);
520
- const args = buildQueryArgs(params);
521
- const source = getCurrentRequest();
344
+ /**
345
+ * Represents a dedicated connection to a database.
346
+ */
347
+ export class Connection extends BaseQueryExecutor {
348
+ protected declare readonly impl: runtime.SQLConn;
522
349
 
523
- // Need to await the cursor to process any errors from things like
524
- // unique constraint violations.
525
- let cur = await this.impl.query(query, args, source);
526
- await cur.next();
350
+ constructor(impl: runtime.SQLConn) {
351
+ super(impl);
527
352
  }
528
353
 
529
354
  /**
530
- * rawExec executes a query without returning any rows.
531
- *
532
- * @example
533
- * const query = "DELETE FROM users WHERE email=$1";
534
- * const email = "foo@example.com";
535
- * await database.rawExec(query, email);
536
- *
537
- * @param query - The raw SQL query string.
538
- * @param params - The parameters to be used in the query.
539
- * @returns A promise that resolves when the query has been executed.
355
+ * Returns the connection to the database pool.
540
356
  */
541
- async rawExec(query: string, ...params: Primitive[]): Promise<void> {
542
- const args = buildQueryArgs(params);
543
- const source = getCurrentRequest();
544
-
545
- // Need to await the cursor to process any errors from things like
546
- // unique constraint violations.
547
- let cur = await this.impl.query(query, args, source);
548
- await cur.next();
357
+ async close() {
358
+ await this.impl.close();
549
359
  }
550
360
  }
551
361