velocious 1.0.462 → 1.0.463

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 (36) hide show
  1. package/build/configuration.js +33 -8
  2. package/build/database/pool/async-tracked-multi-connection.js +66 -1
  3. package/build/database/pool/base.js +8 -0
  4. package/build/http-server/client/index.js +1 -1
  5. package/build/http-server/client/request-runner.js +1 -1
  6. package/build/http-server/worker-handler/index.js +3 -2
  7. package/build/routes/resolver.js +1 -1
  8. package/build/src/configuration.d.ts +13 -0
  9. package/build/src/configuration.d.ts.map +1 -1
  10. package/build/src/configuration.js +32 -10
  11. package/build/src/database/pool/async-tracked-multi-connection.d.ts +18 -0
  12. package/build/src/database/pool/async-tracked-multi-connection.d.ts.map +1 -1
  13. package/build/src/database/pool/async-tracked-multi-connection.js +62 -2
  14. package/build/src/database/pool/base.d.ts +5 -0
  15. package/build/src/database/pool/base.d.ts.map +1 -1
  16. package/build/src/database/pool/base.js +8 -1
  17. package/build/src/http-server/client/index.js +2 -2
  18. package/build/src/http-server/client/request-runner.js +2 -2
  19. package/build/src/http-server/worker-handler/index.d.ts.map +1 -1
  20. package/build/src/http-server/worker-handler/index.js +4 -3
  21. package/build/src/routes/resolver.d.ts.map +1 -1
  22. package/build/src/routes/resolver.js +2 -2
  23. package/build/tsconfig.tsbuildinfo +1 -1
  24. package/package.json +3 -2
  25. package/src/configuration.js +33 -8
  26. package/src/database/pool/async-tracked-multi-connection.js +66 -1
  27. package/src/database/pool/base.js +8 -0
  28. package/src/http-server/client/index.js +1 -1
  29. package/src/http-server/client/request-runner.js +1 -1
  30. package/src/http-server/worker-handler/index.js +3 -2
  31. package/src/routes/resolver.js +1 -1
  32. package/build/src/utils/ensure-error.d.ts +0 -7
  33. package/build/src/utils/ensure-error.d.ts.map +0 -1
  34. package/build/src/utils/ensure-error.js +0 -15
  35. package/build/utils/ensure-error.js +0 -15
  36. package/src/utils/ensure-error.js +0 -15
@@ -2440,10 +2440,26 @@ export default class VelociousConfiguration {
2440
2440
  * @type {{[key: string]: import("./database/drivers/base.js").default}} */
2441
2441
  const dbs = {}
2442
2442
 
2443
+ return await this.withDatabaseIdentifierConnections({
2444
+ callback: actualWithConnectionsCallback,
2445
+ dbs,
2446
+ identifiers: this.getDatabaseIdentifiers(),
2447
+ name,
2448
+ stackLabel: "withConnections"
2449
+ })
2450
+ }
2451
+
2452
+ /**
2453
+ * Runs callback with database connections for the requested identifiers.
2454
+ * @template T
2455
+ * @param {{callback: WithConnectionsCallbackType<T>, dbs: Record<string, import("./database/drivers/base.js").default>, identifiers: string[], name: string, stackLabel: string}} args - Connection scope details.
2456
+ * @returns {Promise<T>} - Resolves with the callback result.
2457
+ */
2458
+ async withDatabaseIdentifierConnections({callback, dbs, identifiers, name, stackLabel}) {
2443
2459
  const stack = Error().stack
2444
2460
  const actualCallback = async () => {
2445
- return await withTrackedStack(stack || "withConnections", async () => {
2446
- return await actualWithConnectionsCallback(dbs)
2461
+ return await withTrackedStack(stack || stackLabel, async () => {
2462
+ return await callback(dbs)
2447
2463
  })
2448
2464
  }
2449
2465
 
@@ -2452,7 +2468,7 @@ export default class VelociousConfiguration {
2452
2468
  * @type {() => Promise<T>} */
2453
2469
  let runRequest = actualCallback
2454
2470
 
2455
- for (const identifier of this.getDatabaseIdentifiers()) {
2471
+ for (const identifier of identifiers) {
2456
2472
  let actualRunRequest = runRequest
2457
2473
 
2458
2474
  const nextRunRequest = async () => {
@@ -2545,14 +2561,23 @@ export default class VelociousConfiguration {
2545
2561
  if (!actualWithConnectionsCallback) throw new Error("ensureConnections requires a callback")
2546
2562
 
2547
2563
  const dbs = this.getCurrentConnections()
2548
- const identifiers = this.getDatabaseIdentifiers()
2549
- const hasAllConnections = identifiers.every((identifier) => dbs[identifier])
2564
+ const missingIdentifiers = this.getDatabaseIdentifiers().filter((identifier) => {
2565
+ if (!dbs[identifier]) return true
2566
+
2567
+ return !this.getDatabasePool(identifier).hasCurrentConnectionContext()
2568
+ })
2550
2569
 
2551
- if (hasAllConnections) {
2570
+ if (missingIdentifiers.length === 0) {
2552
2571
  return await actualWithConnectionsCallback(dbs)
2553
- } else {
2554
- return await this.withConnections({name}, actualWithConnectionsCallback)
2555
2572
  }
2573
+
2574
+ return await this.withDatabaseIdentifierConnections({
2575
+ callback: actualWithConnectionsCallback,
2576
+ dbs,
2577
+ identifiers: missingIdentifiers,
2578
+ name,
2579
+ stackLabel: "ensureConnections"
2580
+ })
2556
2581
  }
2557
2582
 
2558
2583
  /**
@@ -523,8 +523,65 @@ export default class VelociousDatabasePoolAsyncTrackedMultiConnection extends Ba
523
523
  */
524
524
  pendingCheckoutTimeoutError(checkout) {
525
525
  const checkoutName = checkout.options.name ? ` Checkout name: ${JSON.stringify(checkout.options.name)}.` : ""
526
+ const diagnostics = this.pendingCheckoutTimeoutDiagnostics(checkout)
526
527
 
527
- return new Error(`Timed out after ${checkout.timeoutMillis}ms waiting for database connection checkout from pool "${this.identifier}".${checkoutName}`)
528
+ return new Error(`Timed out after ${checkout.timeoutMillis}ms waiting for database connection checkout from pool "${this.identifier}".${checkoutName} ${diagnostics}`)
529
+ }
530
+
531
+ /**
532
+ * Builds sanitized diagnostics for a checkout timeout.
533
+ * @param {PendingCheckout} checkout - Timed-out checkout.
534
+ * @returns {string} - Pool state summary.
535
+ */
536
+ pendingCheckoutTimeoutDiagnostics(checkout) {
537
+ const snapshot = this.getDebugSnapshot()
538
+ const connectionSummaries = snapshot.connections
539
+ .map((connection) => this.pendingCheckoutTimeoutConnectionSummary(connection))
540
+ .join(", ")
541
+ const pendingSummaries = (snapshot.pendingCheckouts || [])
542
+ .map((pendingCheckout) => this.pendingCheckoutTimeoutPendingSummary(pendingCheckout))
543
+ .join(", ")
544
+ const waitedForMs = Math.max(0, Date.now() - checkout.enqueuedAt)
545
+
546
+ return `Pool state: max=${this.maxConnections() ?? "unbounded"}, inUse=${snapshot.inUseCount}, idle=${snapshot.idleCount}, pending=${snapshot.pendingCheckoutCount}, spawning=${snapshot.connectionsBeingSpawned}, timedOutWaitingForMs=${waitedForMs}, holders=[${connectionSummaries}], waiting=[${pendingSummaries}].`
547
+ }
548
+
549
+ /**
550
+ * Builds a sanitized connection summary for checkout timeout diagnostics.
551
+ * @param {Record<string, ?>} connection - Connection debug snapshot.
552
+ * @returns {string} - Sanitized connection state.
553
+ */
554
+ pendingCheckoutTimeoutConnectionSummary(connection) {
555
+ const parts = [`state=${connection.state}`]
556
+
557
+ if (connection.checkoutName) parts.push(`checkout=${JSON.stringify(connection.checkoutName)}`)
558
+ if (typeof connection.checkedOutForMs === "number") parts.push(`checkedOutForMs=${connection.checkedOutForMs}`)
559
+ if (typeof connection.idleForMs === "number") parts.push(`idleForMs=${connection.idleForMs}`)
560
+ if (typeof connection.openTransactions === "number") parts.push(`openTransactions=${connection.openTransactions}`)
561
+
562
+ const activeQuery = connection.activeQuery
563
+
564
+ if (activeQuery && typeof activeQuery === "object" && !Array.isArray(activeQuery)) {
565
+ const runningMs = (/** @type {Record<string, ?>} */ (activeQuery)).runningMs
566
+
567
+ if (typeof runningMs === "number") parts.push(`activeQueryMs=${runningMs}`)
568
+ }
569
+
570
+ return `{${parts.join(" ")}}`
571
+ }
572
+
573
+ /**
574
+ * Builds a sanitized pending checkout summary for checkout timeout diagnostics.
575
+ * @param {import("./base.js").DatabasePoolPendingCheckoutDebugSnapshot} pendingCheckout - Waiting checkout snapshot.
576
+ * @returns {string} - Sanitized pending checkout state.
577
+ */
578
+ pendingCheckoutTimeoutPendingSummary(pendingCheckout) {
579
+ const parts = [`index=${pendingCheckout.index}`, `waitingForMs=${pendingCheckout.waitingForMs}`]
580
+
581
+ if (pendingCheckout.checkoutName) parts.push(`checkout=${JSON.stringify(pendingCheckout.checkoutName)}`)
582
+ if (pendingCheckout.remainingTimeoutMs !== null) parts.push(`remainingTimeoutMs=${pendingCheckout.remainingTimeoutMs}`)
583
+
584
+ return `{${parts.join(" ")}}`
528
585
  }
529
586
 
530
587
  /**
@@ -763,6 +820,14 @@ export default class VelociousDatabasePoolAsyncTrackedMultiConnection extends Ba
763
820
  return this.getCurrentConnection()
764
821
  }
765
822
 
823
+ /**
824
+ * Returns whether this pool has a real async context for the current connection.
825
+ * @returns {boolean} - Whether nested code can reuse the current connection context.
826
+ */
827
+ hasCurrentConnectionContext() {
828
+ return this.asyncLocalStorage.getStore() !== undefined
829
+ }
830
+
766
831
  /**
767
832
  * Runs get debug snapshot.
768
833
  * @returns {import("./base.js").DatabasePoolDebugSnapshot} - Diagnostic snapshot for this pool.
@@ -144,6 +144,14 @@ class VelociousDatabasePoolBase {
144
144
  return this.getCurrentConnection()
145
145
  }
146
146
 
147
+ /**
148
+ * Returns whether the current connection is pinned to an execution context.
149
+ * @returns {boolean} - Whether the current connection can be reused by nested code.
150
+ */
151
+ hasCurrentConnectionContext() {
152
+ return true
153
+ }
154
+
147
155
  /**
148
156
  * Runs without current connection context.
149
157
  * @template T
@@ -4,8 +4,8 @@ import crypto from "crypto"
4
4
  import fs from "node:fs/promises"
5
5
  import {createReadStream} from "node:fs"
6
6
  import {digg} from "diggerize"
7
+ import {ensureError} from "typanic"
7
8
  import EventEmitter from "../../utils/event-emitter.js"
8
- import ensureError from "../../utils/ensure-error.js"
9
9
  import Logger from "../../logger.js"
10
10
  import Request from "./request.js"
11
11
  import RequestRunner from "./request-runner.js"
@@ -1,7 +1,7 @@
1
1
  // @ts-check
2
2
 
3
+ import {ensureError} from "typanic"
3
4
  import BacktraceCleaner from "../../utils/backtrace-cleaner-node.js"
4
- import ensureError from "../../utils/ensure-error.js"
5
5
  import EventEmitter from "../../utils/event-emitter.js"
6
6
  import Logger from "../../logger.js"
7
7
  import RequestTiming from "./request-timing.js"
@@ -1,8 +1,8 @@
1
1
  // @ts-check
2
2
 
3
+ import {ensureError} from "typanic"
3
4
  import Logger from "../../logger.js"
4
5
  import {Worker} from "worker_threads"
5
- import ensureError from "../../utils/ensure-error.js"
6
6
  import websocketEventsHost from "../websocket-events-host.js"
7
7
 
8
8
  /**
@@ -109,7 +109,8 @@ export default class VelociousHttpServerWorker {
109
109
  onWorkerError = (error) => {
110
110
  this.logger.error(`Velocious worker ${this.workerCount} error`, error)
111
111
  void this._closeAllClients()
112
- throw ensureError(error) // Throws original error with backtrace and everything into the console
112
+ // Preserve Error instances for the original backtrace while wrapping non-Error throwables.
113
+ throw ensureError(error)
113
114
  }
114
115
 
115
116
  /**
@@ -4,9 +4,9 @@ import {dirname} from "path"
4
4
  import {fileURLToPath} from "url"
5
5
  import fs from "fs/promises"
6
6
  import * as inflection from "inflection"
7
+ import {ensureError} from "typanic"
7
8
  import Logger from "../logger.js"
8
9
  import UploadedFile from "../http-server/client/uploaded-file/uploaded-file.js"
9
- import ensureError from "../utils/ensure-error.js"
10
10
  import toImportSpecifier from "../utils/to-import-specifier.js"
11
11
 
12
12
  /**
@@ -1,7 +0,0 @@
1
- /**
2
- * Runs ensure error.
3
- * @param {?} error - Error instance.
4
- * @returns {Error} - The error.
5
- */
6
- export default function ensureError(error: unknown): Error;
7
- //# sourceMappingURL=ensure-error.d.ts.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"ensure-error.d.ts","sourceRoot":"","sources":["../../../src/utils/ensure-error.js"],"names":[],"mappings":"AAEA;;;;GAIG;AACH,2CAHW,OAAC,GACC,KAAK,CAQjB"}
@@ -1,15 +0,0 @@
1
- // @ts-check
2
- /**
3
- * Runs ensure error.
4
- * @param {?} error - Error instance.
5
- * @returns {Error} - The error.
6
- */
7
- export default function ensureError(error) {
8
- if (error instanceof Error) {
9
- return error;
10
- }
11
- else {
12
- return new Error(`Unknown error type ${typeof error}: ${error}`);
13
- }
14
- }
15
- //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiZW5zdXJlLWVycm9yLmpzIiwic291cmNlUm9vdCI6IiIsInNvdXJjZXMiOlsiLi4vLi4vLi4vc3JjL3V0aWxzL2Vuc3VyZS1lcnJvci5qcyJdLCJuYW1lcyI6W10sIm1hcHBpbmdzIjoiQUFBQSxZQUFZO0FBRVo7Ozs7R0FJRztBQUNILE1BQU0sQ0FBQyxPQUFPLFVBQVUsV0FBVyxDQUFDLEtBQUs7SUFDdkMsSUFBSSxLQUFLLFlBQVksS0FBSyxFQUFFLENBQUM7UUFDM0IsT0FBTyxLQUFLLENBQUE7SUFDZCxDQUFDO1NBQU0sQ0FBQztRQUNOLE9BQU8sSUFBSSxLQUFLLENBQUMsc0JBQXNCLE9BQU8sS0FBSyxLQUFLLEtBQUssRUFBRSxDQUFDLENBQUE7SUFDbEUsQ0FBQztBQUNILENBQUMiLCJzb3VyY2VzQ29udGVudCI6WyIvLyBAdHMtY2hlY2tcblxuLyoqXG4gKiBSdW5zIGVuc3VyZSBlcnJvci5cbiAqIEBwYXJhbSB7P30gZXJyb3IgLSBFcnJvciBpbnN0YW5jZS5cbiAqIEByZXR1cm5zIHtFcnJvcn0gLSBUaGUgZXJyb3IuXG4gKi9cbmV4cG9ydCBkZWZhdWx0IGZ1bmN0aW9uIGVuc3VyZUVycm9yKGVycm9yKSB7XG4gIGlmIChlcnJvciBpbnN0YW5jZW9mIEVycm9yKSB7XG4gICAgcmV0dXJuIGVycm9yXG4gIH0gZWxzZSB7XG4gICAgcmV0dXJuIG5ldyBFcnJvcihgVW5rbm93biBlcnJvciB0eXBlICR7dHlwZW9mIGVycm9yfTogJHtlcnJvcn1gKVxuICB9XG59XG5cbiJdfQ==
@@ -1,15 +0,0 @@
1
- // @ts-check
2
-
3
- /**
4
- * Runs ensure error.
5
- * @param {?} error - Error instance.
6
- * @returns {Error} - The error.
7
- */
8
- export default function ensureError(error) {
9
- if (error instanceof Error) {
10
- return error
11
- } else {
12
- return new Error(`Unknown error type ${typeof error}: ${error}`)
13
- }
14
- }
15
-
@@ -1,15 +0,0 @@
1
- // @ts-check
2
-
3
- /**
4
- * Runs ensure error.
5
- * @param {?} error - Error instance.
6
- * @returns {Error} - The error.
7
- */
8
- export default function ensureError(error) {
9
- if (error instanceof Error) {
10
- return error
11
- } else {
12
- return new Error(`Unknown error type ${typeof error}: ${error}`)
13
- }
14
- }
15
-