velocious 1.0.446 → 1.0.448

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.
Files changed (71) hide show
  1. package/README.md +2 -2
  2. package/build/configuration-types.js +4 -2
  3. package/build/database/pool/async-tracked-multi-connection.js +125 -8
  4. package/build/database/pool/base.js +14 -1
  5. package/build/database/record/index.js +10 -10
  6. package/build/frontend-models/base.js +99 -91
  7. package/build/frontend-models/preloader.js +7 -7
  8. package/build/frontend-models/query.js +18 -18
  9. package/build/frontend-models/use-created-event.js +1 -1
  10. package/build/frontend-models/use-destroyed-event.js +1 -1
  11. package/build/frontend-models/use-model-class-event.js +1 -1
  12. package/build/frontend-models/use-updated-event.js +1 -1
  13. package/build/routes/resolver.js +17 -14
  14. package/build/src/configuration-types.d.ts +16 -6
  15. package/build/src/configuration-types.d.ts.map +1 -1
  16. package/build/src/configuration-types.js +5 -3
  17. package/build/src/database/pool/async-tracked-multi-connection.d.ts +60 -4
  18. package/build/src/database/pool/async-tracked-multi-connection.d.ts.map +1 -1
  19. package/build/src/database/pool/async-tracked-multi-connection.js +116 -9
  20. package/build/src/database/pool/base.d.ts +38 -1
  21. package/build/src/database/pool/base.d.ts.map +1 -1
  22. package/build/src/database/pool/base.js +14 -2
  23. package/build/src/database/record/index.d.ts +19 -19
  24. package/build/src/database/record/index.d.ts.map +1 -1
  25. package/build/src/database/record/index.js +11 -11
  26. package/build/src/frontend-models/base.d.ts +163 -153
  27. package/build/src/frontend-models/base.d.ts.map +1 -1
  28. package/build/src/frontend-models/base.js +100 -92
  29. package/build/src/frontend-models/preloader.d.ts +10 -10
  30. package/build/src/frontend-models/preloader.d.ts.map +1 -1
  31. package/build/src/frontend-models/preloader.js +8 -8
  32. package/build/src/frontend-models/query.d.ts +8 -8
  33. package/build/src/frontend-models/query.d.ts.map +1 -1
  34. package/build/src/frontend-models/query.js +19 -19
  35. package/build/src/frontend-models/use-created-event.d.ts +2 -2
  36. package/build/src/frontend-models/use-created-event.d.ts.map +1 -1
  37. package/build/src/frontend-models/use-created-event.js +2 -2
  38. package/build/src/frontend-models/use-destroyed-event.d.ts +1 -1
  39. package/build/src/frontend-models/use-destroyed-event.d.ts.map +1 -1
  40. package/build/src/frontend-models/use-destroyed-event.js +2 -2
  41. package/build/src/frontend-models/use-model-class-event.d.ts +1 -1
  42. package/build/src/frontend-models/use-model-class-event.d.ts.map +1 -1
  43. package/build/src/frontend-models/use-model-class-event.js +2 -2
  44. package/build/src/frontend-models/use-updated-event.d.ts +1 -1
  45. package/build/src/frontend-models/use-updated-event.d.ts.map +1 -1
  46. package/build/src/frontend-models/use-updated-event.js +2 -2
  47. package/build/src/routes/resolver.d.ts.map +1 -1
  48. package/build/src/routes/resolver.js +7 -4
  49. package/build/src/utils/model-scope.d.ts +4 -4
  50. package/build/src/utils/model-scope.d.ts.map +1 -1
  51. package/build/src/utils/model-scope.js +3 -3
  52. package/build/src/utils/ransack.d.ts +1 -1
  53. package/build/src/utils/ransack.d.ts.map +1 -1
  54. package/build/src/utils/ransack.js +2 -2
  55. package/build/utils/model-scope.js +2 -2
  56. package/build/utils/ransack.js +1 -1
  57. package/package.json +1 -1
  58. package/src/configuration-types.js +4 -2
  59. package/src/database/pool/async-tracked-multi-connection.js +125 -8
  60. package/src/database/pool/base.js +14 -1
  61. package/src/database/record/index.js +10 -10
  62. package/src/frontend-models/base.js +99 -91
  63. package/src/frontend-models/preloader.js +7 -7
  64. package/src/frontend-models/query.js +18 -18
  65. package/src/frontend-models/use-created-event.js +1 -1
  66. package/src/frontend-models/use-destroyed-event.js +1 -1
  67. package/src/frontend-models/use-model-class-event.js +1 -1
  68. package/src/frontend-models/use-updated-event.js +1 -1
  69. package/src/routes/resolver.js +17 -14
  70. package/src/utils/model-scope.js +2 -2
  71. package/src/utils/ransack.js +1 -1
package/README.md CHANGED
@@ -22,7 +22,7 @@
22
22
  * EJS-backed mailers with delivery, queueing, and payload rendering support (see [docs/mailers.md](docs/mailers.md))
23
23
  * Trusted reverse proxy handling for `request.remoteAddress()` (see [docs/trusted-proxies.md](docs/trusted-proxies.md))
24
24
  * In-process driver schema metadata caching (see [docs/schema-metadata-cache.md](docs/schema-metadata-cache.md))
25
- * Named database connection checkouts for debugging held connections (see [docs/database-connections.md](docs/database-connections.md))
25
+ * Named database connection checkouts, bounded pool waits, and debugging held connections (see [docs/database-connections.md](docs/database-connections.md))
26
26
  * Optional built-in debug endpoint for inspecting server and database connection state (see [docs/debug-endpoint.md](docs/debug-endpoint.md))
27
27
 
28
28
  # Setup
@@ -1453,7 +1453,7 @@ database: {
1453
1453
  }
1454
1454
  ```
1455
1455
 
1456
- `pool.max` caps live async-tracked connections for that pool. When the cap is reached, new checkouts wait until a matching checked-in connection can be handed over or capacity is freed. The built-in debug endpoint reports each in-use connection's `checkedOutForMs`, each idle connection's `idleForMs`, and queued `pendingCheckouts[].waitingForMs` so production diagnostics can distinguish long-held checkouts from pool-capacity waits.
1456
+ `pool.max` caps live async-tracked connections for that pool and defaults to `10` when omitted. When the cap is reached, new checkouts wait until a matching checked-in connection can be handed over or capacity is freed. Set `pool.max` to `null` only when a process is deliberately allowed to open an unbounded number of database connections. The built-in debug endpoint reports each in-use connection's `checkedOutForMs`, each idle connection's `idleForMs`, and queued `pendingCheckouts[].waitingForMs` so production diagnostics can distinguish long-held checkouts from pool-capacity waits.
1457
1457
 
1458
1458
  # Websockets
1459
1459
 
@@ -44,17 +44,19 @@
44
44
  * @property {boolean} [options.trustServerCertificate] - Whether to trust the server certificate (MSSQL).
45
45
  * @property {string} [password] - Password for the SQL user.
46
46
  * @property {object} [pool] - Connection pool configuration.
47
- * @property {number} [pool.max] - Maximum number of connections.
47
+ * @property {number | null} [pool.max] - Maximum number of connections. Set null to disable the cap.
48
48
  * @property {number} [pool.min] - Minimum number of connections.
49
49
  * @property {number} [pool.idleTimeoutMillis] - Idle timeout before releasing a connection.
50
+ * @property {number | null} [pool.checkoutTimeoutMillis] - Timeout while waiting for an available connection after the max connection cap is reached. Set null to wait indefinitely.
50
51
  * @property {string} [server] - SQL server hostname.
51
52
  * @property {string} [user] - SQL username.
52
53
  */
53
54
 
54
55
  /**
55
56
  * @typedef {object} DatabasePoolConfiguration
57
+ * @property {number | null} [checkoutTimeoutMillis] - Timeout while a checkout waits for an available async-tracked connection after the max live connection cap is reached. Set null to wait indefinitely. Default: 10000.
56
58
  * @property {number | null} [idleTimeoutMillis] - Idle timeout before closing a checked-in async-tracked connection. Set null to disable idle reaping. Default: 5000.
57
- * @property {number} [max] - Maximum live async-tracked connections for this pool. Extra checkouts wait until a matching connection is checked in or capacity is freed.
59
+ * @property {number | null} [max] - Maximum live async-tracked connections for this pool. Defaults to 10. Extra checkouts wait until a matching connection is checked in or capacity is freed. Set null to disable the cap.
58
60
  */
59
61
 
60
62
  /**
@@ -6,7 +6,9 @@ import BasePool, {POOL_CONFIGURATION_KEY} from "./base.js"
6
6
  export const CLOSED_CONNECTION = Symbol("velociousClosedConnection")
7
7
  const IDLE_CONNECTION_CHECKED_IN_AT = Symbol("velociousIdleConnectionCheckedInAt")
8
8
  const CONNECTION_CHECKED_OUT_AT = Symbol("velociousConnectionCheckedOutAt")
9
+ const DEFAULT_MAX_CONNECTIONS = 10
9
10
  const DEFAULT_IDLE_TIMEOUT_MILLIS = 5000
11
+ const DEFAULT_CHECKOUT_TIMEOUT_MILLIS = 10000
10
12
 
11
13
  /**
12
14
  * PendingCheckout type.
@@ -17,6 +19,9 @@ const DEFAULT_IDLE_TIMEOUT_MILLIS = 5000
17
19
  * @property {string} reuseKey - Database configuration reuse key needed by the checkout.
18
20
  * @property {(connection: import("../drivers/base.js").default) => void} resolve - Resolves with an activated connection.
19
21
  * @property {(error: Error) => void} reject - Rejects when checkout cannot complete.
22
+ * @property {number | null} timeoutAt - Timestamp when the checkout will time out, or null when disabled.
23
+ * @property {number | null} timeoutMillis - Milliseconds to wait before rejecting, or null when disabled.
24
+ * @property {ReturnType<typeof setTimeout> | undefined} timeoutTimer - Timer that rejects the pending checkout.
20
25
  */
21
26
 
22
27
  export default class VelociousDatabasePoolAsyncTrackedMultiConnection extends BasePool {
@@ -280,14 +285,37 @@ export default class VelociousDatabasePoolAsyncTrackedMultiConnection extends Ba
280
285
 
281
286
  /**
282
287
  * Runs max connections.
283
- * @returns {number | undefined} - Configured max live connections.
288
+ * @returns {number | null} - Configured max live connections.
284
289
  */
285
290
  maxConnections() {
286
291
  const value = this.getConfiguration().pool?.max
287
292
 
293
+ if (value === null) return null
288
294
  if (this.validMaxConnections(value)) return value
289
295
 
290
- return
296
+ return DEFAULT_MAX_CONNECTIONS
297
+ }
298
+
299
+ /**
300
+ * Runs checkout timeout millis.
301
+ * @returns {number | null} - Pending checkout timeout in milliseconds, or null when disabled.
302
+ */
303
+ checkoutTimeoutMillis() {
304
+ const value = this.getConfiguration().pool?.checkoutTimeoutMillis
305
+
306
+ if (value === null) return null
307
+ if (this.validCheckoutTimeoutMillis(value)) return value
308
+
309
+ return DEFAULT_CHECKOUT_TIMEOUT_MILLIS
310
+ }
311
+
312
+ /**
313
+ * Runs valid checkout timeout millis.
314
+ * @param {?} value - Candidate checkout timeout.
315
+ * @returns {value is number} - Whether the value is a valid timeout.
316
+ */
317
+ validCheckoutTimeoutMillis(value) {
318
+ return typeof value === "number" && Number.isFinite(value) && value >= 0
291
319
  }
292
320
 
293
321
  /**
@@ -320,7 +348,7 @@ export default class VelociousDatabasePoolAsyncTrackedMultiConnection extends Ba
320
348
  canSpawnConnection() {
321
349
  const maxConnections = this.maxConnections()
322
350
 
323
- return maxConnections === undefined || this.liveConnectionCount() < maxConnections
351
+ return maxConnections === null || this.liveConnectionCount() < maxConnections
324
352
  }
325
353
 
326
354
  /**
@@ -359,7 +387,23 @@ export default class VelociousDatabasePoolAsyncTrackedMultiConnection extends Ba
359
387
  */
360
388
  async waitForCheckout(databaseConfig, reuseKey, options = {}) {
361
389
  return await new Promise((resolve, reject) => {
362
- this.pendingCheckouts.push({databaseConfig, enqueuedAt: Date.now(), options, reject, resolve, reuseKey})
390
+ const enqueuedAt = Date.now()
391
+ const timeoutMillis = this.checkoutTimeoutMillis()
392
+ /** @type {PendingCheckout} */
393
+ const checkout = {
394
+ databaseConfig,
395
+ enqueuedAt,
396
+ options,
397
+ reject,
398
+ resolve,
399
+ reuseKey,
400
+ timeoutAt: timeoutMillis === null ? null : enqueuedAt + timeoutMillis,
401
+ timeoutMillis,
402
+ timeoutTimer: undefined
403
+ }
404
+
405
+ checkout.timeoutTimer = this.startPendingCheckoutTimeout(checkout)
406
+ this.pendingCheckouts.push(checkout)
363
407
  void this.drainPendingCheckouts().catch((error) => {
364
408
  const checkoutError = error instanceof Error ? error : new Error("Failed to drain pending database connection checkouts.", {cause: error})
365
409
 
@@ -398,17 +442,19 @@ export default class VelociousDatabasePoolAsyncTrackedMultiConnection extends Ba
398
442
  const checkout = this.pendingCheckouts[0]
399
443
 
400
444
  if (await this.closeIdleConnectionForPendingCheckoutCapacity(checkout)) continue
445
+ if (!this.pendingCheckouts.includes(checkout)) continue
401
446
  if (this.canSpawnConnection()) {
402
- this.pendingCheckouts.shift()
447
+ this.removePendingCheckoutAt(0)
403
448
  await this.spawnAndResolvePendingCheckout(checkout)
404
449
  continue
405
450
  }
406
451
 
407
452
  const reapedConnection = await this.idleConnectionForPendingCheckout(checkout)
408
453
 
454
+ if (!this.pendingCheckouts.includes(checkout)) continue
409
455
  if (!reapedConnection) return
410
456
 
411
- this.pendingCheckouts.shift()
457
+ this.removePendingCheckoutAt(0)
412
458
  await this.resolvePendingCheckout(checkout, reapedConnection)
413
459
  }
414
460
  }
@@ -424,7 +470,7 @@ export default class VelociousDatabasePoolAsyncTrackedMultiConnection extends Ba
424
470
 
425
471
  if (!connection) continue
426
472
 
427
- this.pendingCheckouts.splice(index, 1)
473
+ this.removePendingCheckoutAt(index)
428
474
  await this.resolvePendingCheckout(checkout, connection)
429
475
 
430
476
  return true
@@ -433,6 +479,71 @@ export default class VelociousDatabasePoolAsyncTrackedMultiConnection extends Ba
433
479
  return false
434
480
  }
435
481
 
482
+ /**
483
+ * Runs remove pending checkout at.
484
+ * @param {number} index - Pending checkout index.
485
+ * @returns {PendingCheckout} - Removed checkout.
486
+ */
487
+ removePendingCheckoutAt(index) {
488
+ const checkout = this.pendingCheckouts.splice(index, 1)[0]
489
+
490
+ this.clearPendingCheckoutTimeout(checkout)
491
+
492
+ return checkout
493
+ }
494
+
495
+ /**
496
+ * Runs start pending checkout timeout.
497
+ * @param {PendingCheckout} checkout - Pending checkout to time out.
498
+ * @returns {ReturnType<typeof setTimeout> | undefined} - Timer, if timeout is enabled.
499
+ */
500
+ startPendingCheckoutTimeout(checkout) {
501
+ if (checkout.timeoutMillis === null) return undefined
502
+
503
+ const timer = setTimeout(() => {
504
+ this.timeoutPendingCheckout(checkout)
505
+ }, checkout.timeoutMillis)
506
+
507
+ return timer
508
+ }
509
+
510
+ /**
511
+ * Runs timeout pending checkout.
512
+ * @param {PendingCheckout} checkout - Pending checkout to reject.
513
+ * @returns {void}
514
+ */
515
+ timeoutPendingCheckout(checkout) {
516
+ const index = this.pendingCheckouts.indexOf(checkout)
517
+
518
+ if (index === -1) return
519
+
520
+ this.removePendingCheckoutAt(index)
521
+ checkout.reject(this.pendingCheckoutTimeoutError(checkout))
522
+ }
523
+
524
+ /**
525
+ * Runs pending checkout timeout error.
526
+ * @param {PendingCheckout} checkout - Timed-out checkout.
527
+ * @returns {Error} - Timeout error.
528
+ */
529
+ pendingCheckoutTimeoutError(checkout) {
530
+ const checkoutName = checkout.options.name ? ` Checkout name: ${JSON.stringify(checkout.options.name)}.` : ""
531
+
532
+ return new Error(`Timed out after ${checkout.timeoutMillis}ms waiting for database connection checkout from pool "${this.identifier}".${checkoutName}`)
533
+ }
534
+
535
+ /**
536
+ * Runs clear pending checkout timeout.
537
+ * @param {PendingCheckout} checkout - Pending checkout.
538
+ * @returns {void}
539
+ */
540
+ clearPendingCheckoutTimeout(checkout) {
541
+ if (!checkout.timeoutTimer) return
542
+
543
+ clearTimeout(checkout.timeoutTimer)
544
+ checkout.timeoutTimer = undefined
545
+ }
546
+
436
547
  /**
437
548
  * Runs close idle connection for pending checkout capacity.
438
549
  * @param {PendingCheckout} checkout - Checkout waiting for a connection.
@@ -470,6 +581,8 @@ export default class VelociousDatabasePoolAsyncTrackedMultiConnection extends Ba
470
581
  if (connection) return connection
471
582
 
472
583
  await this.reapIdleConnections()
584
+ if (!this.pendingCheckouts.includes(checkout)) return
585
+
473
586
  connection = this.takeIdleConnectionForReuseKey(checkout.reuseKey, {includeOpenTransactions: false})
474
587
 
475
588
  return connection
@@ -760,14 +873,17 @@ export default class VelociousDatabasePoolAsyncTrackedMultiConnection extends Ba
760
873
  /**
761
874
  * Runs pending checkout debug snapshots.
762
875
  * @param {number} now - Current timestamp.
763
- * @returns {Array<Record<string, ?>>} - Pending checkout snapshots.
876
+ * @returns {import("./base.js").DatabasePoolPendingCheckoutDebugSnapshot[]} - Pending checkout snapshots.
764
877
  */
765
878
  pendingCheckoutDebugSnapshots(now) {
766
879
  return this.pendingCheckouts.map((checkout, index) => ({
767
880
  checkoutName: checkout.options.name,
768
881
  enqueuedAt: checkout.enqueuedAt,
769
882
  index,
883
+ remainingTimeoutMs: checkout.timeoutAt === null ? null : Math.max(0, checkout.timeoutAt - now),
770
884
  reuseKey: checkout.reuseKey,
885
+ timeoutAt: checkout.timeoutAt,
886
+ timeoutMillis: checkout.timeoutMillis,
771
887
  waitingForMs: Math.max(0, now - checkout.enqueuedAt)
772
888
  }))
773
889
  }
@@ -1129,6 +1245,7 @@ export default class VelociousDatabasePoolAsyncTrackedMultiConnection extends Ba
1129
1245
  this.pendingCheckouts = []
1130
1246
 
1131
1247
  for (const checkout of pendingCheckouts) {
1248
+ this.clearPendingCheckoutTimeout(checkout)
1132
1249
  checkout.reject(error)
1133
1250
  }
1134
1251
  }
@@ -12,6 +12,19 @@ export const POOL_CONFIGURATION_KEY = Symbol("velociousPoolConfigurationKey")
12
12
  * @property {string} [name] - Human-readable name for the checked-out connection.
13
13
  */
14
14
 
15
+ /**
16
+ * DatabasePoolPendingCheckoutDebugSnapshot type.
17
+ * @typedef {object} DatabasePoolPendingCheckoutDebugSnapshot
18
+ * @property {string | undefined} checkoutName - Human-readable checkout name.
19
+ * @property {number} enqueuedAt - Timestamp when the checkout started waiting.
20
+ * @property {number} index - Pending checkout queue index.
21
+ * @property {number | null} remainingTimeoutMs - Milliseconds before the checkout times out, or null when disabled.
22
+ * @property {string} reuseKey - Database configuration reuse key needed by the checkout.
23
+ * @property {number | null} timeoutAt - Timestamp when the checkout will time out, or null when disabled.
24
+ * @property {number | null} timeoutMillis - Timeout configured for the checkout, or null when disabled.
25
+ * @property {number} waitingForMs - Milliseconds already spent waiting.
26
+ */
27
+
15
28
  /**
16
29
  * DatabasePoolDebugSnapshot type.
17
30
  * @typedef {object} DatabasePoolDebugSnapshot
@@ -21,7 +34,7 @@ export const POOL_CONFIGURATION_KEY = Symbol("velociousPoolConfigurationKey")
21
34
  * @property {number} idleCount - Number of idle connections.
22
35
  * @property {string} identifier - Database identifier.
23
36
  * @property {number} inUseCount - Number of checked-out connections.
24
- * @property {Array<Record<string, ?>>} [pendingCheckouts] - Waiting checkout snapshots.
37
+ * @property {Array<DatabasePoolPendingCheckoutDebugSnapshot>} [pendingCheckouts] - Waiting checkout snapshots.
25
38
  * @property {number} pendingCheckoutCount - Number of queued checkout requests.
26
39
  * @property {string} poolClass - Pool class name.
27
40
  */
@@ -13,7 +13,7 @@
13
13
 
14
14
  /**
15
15
  * Model class constructor type used for static `this` typing.
16
- * @template {VelociousDatabaseRecord} T
16
+ * @template T
17
17
  * @typedef {{new (changes?: Record<string, unknown>): T}} ModelConstructor
18
18
  */
19
19
 
@@ -508,7 +508,7 @@ class VelociousDatabaseRecord {
508
508
  /**
509
509
  * Runs before validation.
510
510
  * @template R
511
- * @this {new (...args: Array<?>) => R}
511
+ * @this {ModelConstructor<R>}
512
512
  * @param {LifecycleCallbackType<R>} callback - Callback function or instance method name.
513
513
  * @returns {void}
514
514
  */
@@ -519,7 +519,7 @@ class VelociousDatabaseRecord {
519
519
  /**
520
520
  * Runs before save.
521
521
  * @template R
522
- * @this {new (...args: Array<?>) => R}
522
+ * @this {ModelConstructor<R>}
523
523
  * @param {LifecycleCallbackType<R>} callback - Callback function or instance method name.
524
524
  * @returns {void}
525
525
  */
@@ -530,7 +530,7 @@ class VelociousDatabaseRecord {
530
530
  /**
531
531
  * Runs before create.
532
532
  * @template R
533
- * @this {new (...args: Array<?>) => R}
533
+ * @this {ModelConstructor<R>}
534
534
  * @param {LifecycleCallbackType<R>} callback - Callback function or instance method name.
535
535
  * @returns {void}
536
536
  */
@@ -541,7 +541,7 @@ class VelociousDatabaseRecord {
541
541
  /**
542
542
  * Runs before update.
543
543
  * @template R
544
- * @this {new (...args: Array<?>) => R}
544
+ * @this {ModelConstructor<R>}
545
545
  * @param {LifecycleCallbackType<R>} callback - Callback function or instance method name.
546
546
  * @returns {void}
547
547
  */
@@ -552,7 +552,7 @@ class VelociousDatabaseRecord {
552
552
  /**
553
553
  * Runs before destroy.
554
554
  * @template R
555
- * @this {new (...args: Array<?>) => R}
555
+ * @this {ModelConstructor<R>}
556
556
  * @param {LifecycleCallbackType<R>} callback - Callback function or instance method name.
557
557
  * @returns {void}
558
558
  */
@@ -563,7 +563,7 @@ class VelociousDatabaseRecord {
563
563
  /**
564
564
  * Runs after save.
565
565
  * @template R
566
- * @this {new (...args: Array<?>) => R}
566
+ * @this {ModelConstructor<R>}
567
567
  * @param {LifecycleCallbackType<R>} callback - Callback function or instance method name.
568
568
  * @returns {void}
569
569
  */
@@ -574,7 +574,7 @@ class VelociousDatabaseRecord {
574
574
  /**
575
575
  * Runs after create.
576
576
  * @template R
577
- * @this {new (...args: Array<?>) => R}
577
+ * @this {ModelConstructor<R>}
578
578
  * @param {LifecycleCallbackType<R>} callback - Callback function or instance method name.
579
579
  * @returns {void}
580
580
  */
@@ -585,7 +585,7 @@ class VelociousDatabaseRecord {
585
585
  /**
586
586
  * Runs after update.
587
587
  * @template R
588
- * @this {new (...args: Array<?>) => R}
588
+ * @this {ModelConstructor<R>}
589
589
  * @param {LifecycleCallbackType<R>} callback - Callback function or instance method name.
590
590
  * @returns {void}
591
591
  */
@@ -596,7 +596,7 @@ class VelociousDatabaseRecord {
596
596
  /**
597
597
  * Runs after destroy.
598
598
  * @template R
599
- * @this {new (...args: Array<?>) => R}
599
+ * @this {ModelConstructor<R>}
600
600
  * @param {LifecycleCallbackType<R>} callback - Callback function or instance method name.
601
601
  * @returns {void}
602
602
  */