velocious 1.0.606 → 1.0.607

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 (64) hide show
  1. package/README.md +44 -1
  2. package/build/background-jobs/job-semantics.js +145 -0
  3. package/build/background-jobs/local-adapter.js +161 -0
  4. package/build/background-jobs/local-dispatcher.js +418 -0
  5. package/build/background-jobs/local-job-registry.js +59 -0
  6. package/build/background-jobs/local-store.js +1219 -0
  7. package/build/background-jobs/store.js +33 -97
  8. package/build/background-jobs/types.js +26 -1
  9. package/build/configuration-types.js +3 -1
  10. package/build/configuration.js +16 -1
  11. package/build/database/drivers/sqlite/index.js +46 -41
  12. package/build/environment-handlers/browser.js +10 -0
  13. package/build/src/background-jobs/job-semantics.d.ts +65 -0
  14. package/build/src/background-jobs/job-semantics.d.ts.map +1 -0
  15. package/build/src/background-jobs/job-semantics.js +125 -0
  16. package/build/src/background-jobs/local-adapter.d.ts +161 -0
  17. package/build/src/background-jobs/local-adapter.d.ts.map +1 -0
  18. package/build/src/background-jobs/local-adapter.js +141 -0
  19. package/build/src/background-jobs/local-dispatcher.d.ts +185 -0
  20. package/build/src/background-jobs/local-dispatcher.d.ts.map +1 -0
  21. package/build/src/background-jobs/local-dispatcher.js +383 -0
  22. package/build/src/background-jobs/local-job-registry.d.ts +26 -0
  23. package/build/src/background-jobs/local-job-registry.d.ts.map +1 -0
  24. package/build/src/background-jobs/local-job-registry.js +51 -0
  25. package/build/src/background-jobs/local-store.d.ts +364 -0
  26. package/build/src/background-jobs/local-store.d.ts.map +1 -0
  27. package/build/src/background-jobs/local-store.js +1078 -0
  28. package/build/src/background-jobs/store.d.ts +0 -9
  29. package/build/src/background-jobs/store.d.ts.map +1 -1
  30. package/build/src/background-jobs/store.js +18 -88
  31. package/build/src/background-jobs/types.d.ts +97 -2
  32. package/build/src/background-jobs/types.d.ts.map +1 -1
  33. package/build/src/background-jobs/types.js +27 -2
  34. package/build/src/configuration-types.d.ts +9 -2
  35. package/build/src/configuration-types.d.ts.map +1 -1
  36. package/build/src/configuration-types.js +4 -2
  37. package/build/src/configuration.d.ts +5 -0
  38. package/build/src/configuration.d.ts.map +1 -1
  39. package/build/src/configuration.js +15 -2
  40. package/build/src/database/drivers/sqlite/index.d.ts +8 -0
  41. package/build/src/database/drivers/sqlite/index.d.ts.map +1 -1
  42. package/build/src/database/drivers/sqlite/index.js +47 -41
  43. package/build/src/environment-handlers/browser.d.ts +9 -0
  44. package/build/src/environment-handlers/browser.d.ts.map +1 -1
  45. package/build/src/environment-handlers/browser.js +10 -1
  46. package/build/src/testing/test-runner.d.ts +42 -10
  47. package/build/src/testing/test-runner.d.ts.map +1 -1
  48. package/build/src/testing/test-runner.js +135 -35
  49. package/build/testing/test-runner.js +141 -33
  50. package/build/tsconfig.tsbuildinfo +1 -1
  51. package/package.json +1 -1
  52. package/scripts/test-browser.js +5 -1
  53. package/src/background-jobs/job-semantics.js +145 -0
  54. package/src/background-jobs/local-adapter.js +161 -0
  55. package/src/background-jobs/local-dispatcher.js +418 -0
  56. package/src/background-jobs/local-job-registry.js +59 -0
  57. package/src/background-jobs/local-store.js +1219 -0
  58. package/src/background-jobs/store.js +33 -97
  59. package/src/background-jobs/types.js +26 -1
  60. package/src/configuration-types.js +3 -1
  61. package/src/configuration.js +16 -1
  62. package/src/database/drivers/sqlite/index.js +46 -41
  63. package/src/environment-handlers/browser.js +10 -0
  64. package/src/testing/test-runner.js +141 -33
@@ -111,6 +111,13 @@ import { SHARED_TRANSACTION_BROKER_ENV } from "./shared-transaction-proxy-driver
111
111
  * ordinary test failure (the promise already settled).
112
112
  * @typedef {Error & {velociousTestTimeout?: true}} TestTimeoutError
113
113
  */
114
+ /**
115
+ * SharedTransactionBrokerRegistration type.
116
+ * @typedef {object} SharedTransactionBrokerRegistration
117
+ * @property {SharedTransactionBroker} broker - Attempt broker and connection coordinator.
118
+ * @property {boolean} environmentPublished - Whether child-process coordinates were published.
119
+ * @property {string | undefined} previousEnvironment - Environment value to restore after publication.
120
+ */
114
121
 
115
122
  /**
116
123
  * Runs run with timeout.
@@ -498,6 +505,7 @@ export default class TestRunner {
498
505
  * @returns {boolean} - Whether the test should be skipped.
499
506
  */
500
507
  shouldSkipTest(testArgs, testData, testDescription, descriptions, lineMatchedInScope) {
508
+ if (this.hasTag(testArgs, "browser-only") && !this.isBrowserTestMode()) return true
501
509
  if (this.hasMatchingTag(testArgs.tags, this.getExcludeTagSet())) return true
502
510
 
503
511
  if (this._includeTagSet.size > 0 && !testArgs.focus) {
@@ -634,12 +642,11 @@ export default class TestRunner {
634
642
  }
635
643
 
636
644
  /**
637
- * Starts a capability-scoped broker for the active non-tenant physical
638
- * transaction connections. No broker/env is installed for truncation-only or
639
- * other transaction-disabled attempts.
640
- * @returns {Promise<{broker: SharedTransactionBroker, previousEnvironment: string | undefined} | undefined>} - Attempt registration.
645
+ * Selects the current non-tenant connections eligible for shared transaction work.
646
+ * @param {{transactionsOnly: boolean}} args - Selection options.
647
+ * @returns {Record<string, import("../database/drivers/base.js").default>} - Eligible connections by identifier.
641
648
  */
642
- async startSharedTransactionBroker() {
649
+ sharedTransactionConnections({transactionsOnly}) {
643
650
  const configuration = this.getConfiguration()
644
651
  const currentConnections = configuration.getCurrentConnections()
645
652
  /** @type {Record<string, import("../database/drivers/base.js").default>} */
@@ -647,14 +654,78 @@ export default class TestRunner {
647
654
 
648
655
  for (const [identifier, connection] of Object.entries(currentConnections)) {
649
656
  const pool = configuration.getDatabasePool(identifier)
650
- if (pool.getConfiguration().tenantOnly || !connection.insideTransaction()) continue
657
+
658
+ if (pool.getConfiguration().tenantOnly) continue
659
+ if (transactionsOnly && !connection.insideTransaction()) continue
651
660
  connections[identifier] = connection
652
661
  }
653
662
 
663
+ return connections
664
+ }
665
+
666
+ /**
667
+ * Installs physical-connection coordination before a transaction-opening hook
668
+ * can expose the shared connection to a long-lived in-process service.
669
+ * Child-process coordinates remain unpublished until the transaction exists.
670
+ * @returns {Promise<SharedTransactionBrokerRegistration | undefined>} - Prepared coordinator.
671
+ */
672
+ async prepareSharedTransactionBroker() {
673
+ const connections = this.sharedTransactionConnections({transactionsOnly: false})
674
+
675
+ if (Object.keys(connections).length === 0) return undefined
676
+
677
+ return {
678
+ broker: await SharedTransactionBroker.start({connections}),
679
+ environmentPublished: false,
680
+ previousEnvironment: undefined
681
+ }
682
+ }
683
+
684
+ /**
685
+ * Checks whether a prepared broker coordinates exactly the selected physical connections.
686
+ * @param {SharedTransactionBrokerRegistration | undefined} registration - Prepared coordinator.
687
+ * @param {Record<string, import("../database/drivers/base.js").default>} connections - Selected connections.
688
+ * @returns {boolean} - Whether the identifier set and physical connections match exactly.
689
+ */
690
+ sharedTransactionBrokerMatchesConnections(registration, connections) {
691
+ const identifiers = Object.keys(connections)
692
+
693
+ if (!registration || identifiers.length === 0) return false
694
+ if (Object.keys(registration.broker.connections).length !== identifiers.length) return false
695
+
696
+ for (const [identifier, connection] of Object.entries(connections)) {
697
+ if (registration.broker.connections[identifier] !== connection) return false
698
+ }
699
+
700
+ return true
701
+ }
702
+
703
+ /**
704
+ * Starts a capability-scoped broker for the active non-tenant physical
705
+ * transaction connections. No broker/env is installed for truncation-only or
706
+ * other transaction-disabled attempts.
707
+ * @param {SharedTransactionBrokerRegistration} [preparedRegistration] - Coordinator prepared before hooks.
708
+ * @param {Record<string, import("../database/drivers/base.js").default>} [selectedConnections] - Post-hook active connections.
709
+ * @returns {Promise<SharedTransactionBrokerRegistration | undefined>} - Attempt registration.
710
+ */
711
+ async startSharedTransactionBroker(preparedRegistration, selectedConnections) {
712
+ const connections = selectedConnections || this.sharedTransactionConnections({transactionsOnly: true})
713
+
654
714
  const databaseIdentifiers = Object.keys(connections)
655
- if (databaseIdentifiers.length === 0) return undefined
715
+ if (databaseIdentifiers.length === 0) {
716
+ await this.stopSharedTransactionBroker(preparedRegistration)
717
+ return undefined
718
+ }
719
+
720
+ let broker
721
+
722
+ if (preparedRegistration && this.sharedTransactionBrokerMatchesConnections(preparedRegistration, connections)) {
723
+ broker = preparedRegistration.broker
724
+ } else {
725
+ await this.stopSharedTransactionBroker(preparedRegistration)
726
+ broker = await SharedTransactionBroker.start({connections})
727
+ }
656
728
 
657
- const broker = await SharedTransactionBroker.start({connections})
658
729
  const previousEnvironment = process.env[SHARED_TRANSACTION_BROKER_ENV]
659
730
  process.env[SHARED_TRANSACTION_BROKER_ENV] = Buffer.from(JSON.stringify({
660
731
  address: broker.address(),
@@ -663,20 +734,23 @@ export default class TestRunner {
663
734
  expected: true
664
735
  })).toString("base64url")
665
736
 
666
- return {broker, previousEnvironment}
737
+ return {broker, environmentPublished: true, previousEnvironment}
667
738
  }
668
739
 
669
740
  /**
670
741
  * Revokes an attempt broker before database rollback hooks run and restores
671
742
  * the caller's environment so later pooled/spawned children cannot inherit it.
672
- * @param {{broker: SharedTransactionBroker, previousEnvironment: string | undefined} | undefined} registration - Attempt registration.
743
+ * @param {SharedTransactionBrokerRegistration | undefined} registration - Attempt registration.
673
744
  */
674
745
  async stopSharedTransactionBroker(registration) {
675
746
  if (!registration) return
676
- if (registration.previousEnvironment === undefined) {
677
- delete process.env[SHARED_TRANSACTION_BROKER_ENV]
678
- } else {
679
- process.env[SHARED_TRANSACTION_BROKER_ENV] = registration.previousEnvironment
747
+
748
+ if (registration.environmentPublished) {
749
+ if (registration.previousEnvironment === undefined) {
750
+ delete process.env[SHARED_TRANSACTION_BROKER_ENV]
751
+ } else {
752
+ process.env[SHARED_TRANSACTION_BROKER_ENV] = registration.previousEnvironment
753
+ }
680
754
  }
681
755
  await registration.broker.close()
682
756
  }
@@ -1153,8 +1227,11 @@ export default class TestRunner {
1153
1227
  let testLifecycle
1154
1228
  /** @type {{pool: import("../database/pool/base.js").default, registration: import("../database/pool/base.js").TestSharedConnectionRegistration}[]} */
1155
1229
  let testSharedConnectionRegistrations = []
1156
- /** @type {{broker: SharedTransactionBroker, previousEnvironment: string | undefined} | undefined} */
1230
+ let testSharedConnectionsActive = false
1231
+ /** @type {SharedTransactionBrokerRegistration | undefined} */
1157
1232
  let sharedTransactionBrokerRegistration
1233
+ /** @type {SharedTransactionBrokerRegistration | undefined} */
1234
+ let sharedTransactionBrokerPreparation
1158
1235
  const stopConsoleCapture = this.startConsoleCapture({
1159
1236
  passthrough: testConfig.consoleOutput === "live"
1160
1237
  })
@@ -1180,12 +1257,16 @@ export default class TestRunner {
1180
1257
  await this.getConfiguration().ensureConnections({name: `Test: ${testDescription}`}, async () => {
1181
1258
  // Register dynamic candidates before hooks so transaction state changes
1182
1259
  // made during a hook are immediately visible to any in-process work.
1183
- // Long-lived services such as a background-jobs main can dispatch DB
1184
- // work between a transaction-starting hook and broker activation just
1185
- // as an HTTP request can dispatch work from inside the hook itself.
1260
+ // Prepare transaction sharing before hooks so long-lived services cannot
1261
+ // use the shared connection while its coordinator is still missing.
1186
1262
  testSharedConnectionRegistrations = this.activateTestSharedConnections()
1263
+ testSharedConnectionsActive = true
1187
1264
 
1188
1265
  try {
1266
+ if (testArgs.databaseCleaning?.transaction === true) {
1267
+ sharedTransactionBrokerPreparation = await this.prepareSharedTransactionBroker()
1268
+ }
1269
+
1189
1270
  try {
1190
1271
  clearDeliveries()
1191
1272
  for (const beforeEachData of newBeforeEaches) {
@@ -1199,9 +1280,18 @@ export default class TestRunner {
1199
1280
  })
1200
1281
  }
1201
1282
 
1202
- sharedTransactionBrokerRegistration = await this.startSharedTransactionBroker()
1203
- if (sharedTransactionBrokerRegistration && testSharedConnectionRegistrations.length === 0) {
1283
+ const activeSharedTransactionConnections = this.sharedTransactionConnections({transactionsOnly: true})
1284
+ if (sharedTransactionBrokerPreparation && !this.sharedTransactionBrokerMatchesConnections(sharedTransactionBrokerPreparation, activeSharedTransactionConnections)) {
1285
+ this.clearTestSharedConnections(testSharedConnectionRegistrations)
1286
+ testSharedConnectionRegistrations = []
1287
+ testSharedConnectionsActive = false
1288
+ }
1289
+
1290
+ sharedTransactionBrokerRegistration = await this.startSharedTransactionBroker(sharedTransactionBrokerPreparation, activeSharedTransactionConnections)
1291
+ sharedTransactionBrokerPreparation = undefined
1292
+ if (sharedTransactionBrokerRegistration && !testSharedConnectionsActive) {
1204
1293
  testSharedConnectionRegistrations = this.activateTestSharedConnections()
1294
+ testSharedConnectionsActive = true
1205
1295
  }
1206
1296
 
1207
1297
  // Record which test is running so an async crash (an unhandled
@@ -1217,22 +1307,35 @@ export default class TestRunner {
1217
1307
  })
1218
1308
  } finally {
1219
1309
  try {
1220
- await this.stopSharedTransactionBroker(sharedTransactionBrokerRegistration)
1310
+ if (testSharedConnectionsActive) {
1311
+ this.clearTestSharedConnections(testSharedConnectionRegistrations)
1312
+ testSharedConnectionRegistrations = []
1313
+ testSharedConnectionsActive = false
1314
+ }
1221
1315
  } finally {
1222
- for (const afterEachData of newAfterEaches) {
1223
- await this.runProfileSpan({
1224
- phase: "afterEach",
1225
- declarationIndex: afterEachData.declarationIndex,
1226
- declarationScopeId: afterEachData.declarationScopeId,
1227
- filePath: afterEachData.ownerFilePath
1228
- }, async () => {
1229
- await afterEachData.callback({configuration: this.getConfiguration(), testArgs, testData})
1230
- })
1316
+ try {
1317
+ await this.stopSharedTransactionBroker(sharedTransactionBrokerRegistration || sharedTransactionBrokerPreparation)
1318
+ sharedTransactionBrokerRegistration = undefined
1319
+ sharedTransactionBrokerPreparation = undefined
1320
+ } finally {
1321
+ for (const afterEachData of newAfterEaches) {
1322
+ await this.runProfileSpan({
1323
+ phase: "afterEach",
1324
+ declarationIndex: afterEachData.declarationIndex,
1325
+ declarationScopeId: afterEachData.declarationScopeId,
1326
+ filePath: afterEachData.ownerFilePath
1327
+ }, async () => {
1328
+ await afterEachData.callback({configuration: this.getConfiguration(), testArgs, testData})
1329
+ })
1330
+ }
1231
1331
  }
1232
1332
  }
1233
1333
  }
1234
1334
  } finally {
1235
- this.clearTestSharedConnections(testSharedConnectionRegistrations)
1335
+ if (testSharedConnectionsActive) {
1336
+ this.clearTestSharedConnections(testSharedConnectionRegistrations)
1337
+ testSharedConnectionsActive = false
1338
+ }
1236
1339
  }
1237
1340
  })
1238
1341
  })
@@ -1283,9 +1386,14 @@ export default class TestRunner {
1283
1386
  // ensureConnections before activateTestSharedConnections() replaces
1284
1387
  // it. Clear it here so the next test checks out a fresh connection.
1285
1388
  // Idempotent when the lifecycle did settle and already cleared.
1286
- await this.stopSharedTransactionBroker(sharedTransactionBrokerRegistration)
1389
+ if (testSharedConnectionsActive) {
1390
+ this.clearTestSharedConnections(testSharedConnectionRegistrations)
1391
+ testSharedConnectionRegistrations = []
1392
+ testSharedConnectionsActive = false
1393
+ }
1394
+ await this.stopSharedTransactionBroker(sharedTransactionBrokerRegistration || sharedTransactionBrokerPreparation)
1287
1395
  sharedTransactionBrokerRegistration = undefined
1288
- this.clearTestSharedConnections(testSharedConnectionRegistrations)
1396
+ sharedTransactionBrokerPreparation = undefined
1289
1397
  }
1290
1398
 
1291
1399
  willRetry = retriesUsed < retryCount