velocious 1.0.643 → 1.0.645

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 (279) hide show
  1. package/README.md +21 -10
  2. package/build/authorization/ability.js +36 -13
  3. package/build/background-jobs/adapter.js +9 -0
  4. package/build/background-jobs/main.js +35 -10
  5. package/build/background-jobs/store.js +217 -38
  6. package/build/background-jobs/types.js +15 -0
  7. package/build/configuration-types.js +1 -1
  8. package/build/configuration.js +45 -9
  9. package/build/database/drivers/base-column.js +8 -0
  10. package/build/database/drivers/mssql/sql/update.js +1 -3
  11. package/build/database/drivers/mysql/sql/update.js +1 -3
  12. package/build/database/drivers/pgsql/column.js +22 -0
  13. package/build/database/drivers/pgsql/sql/update.js +1 -3
  14. package/build/database/drivers/sqlite/sql/alter-table.js +11 -1
  15. package/build/database/drivers/sqlite/sql/update.js +1 -3
  16. package/build/database/query/index.js +2 -2
  17. package/build/database/query/model-class-query.js +26 -10
  18. package/build/database/query/preloader/has-many.js +4 -1
  19. package/build/database/query/query-data.js +2 -1
  20. package/build/database/query/update-base.js +14 -0
  21. package/build/database/query/with-count.js +3 -1
  22. package/build/database/record/acts-as-list.js +37 -13
  23. package/build/database/record/attachments/store.js +336 -26
  24. package/build/database/record/auditing.js +6 -3
  25. package/build/database/record/index.js +197 -42
  26. package/build/database/record/instance-relationships/belongs-to.js +2 -1
  27. package/build/database/record/validators/uniqueness.js +8 -5
  28. package/build/environment-handlers/node/cli/commands/generate/frontend-models.js +24 -8
  29. package/build/frontend-model-controller.js +136 -53
  30. package/build/frontend-model-resource/base-resource.js +87 -37
  31. package/build/frontend-model-resource/velocious-attachment-resource.js +98 -31
  32. package/build/frontend-models/base.js +307 -59
  33. package/build/frontend-models/preloader.js +6 -3
  34. package/build/frontend-models/query.js +15 -6
  35. package/build/frontend-models/resource-definition.js +21 -2
  36. package/build/frontend-models/use-destroyed-event.js +1 -1
  37. package/build/frontend-models/use-model-class-event.js +2 -2
  38. package/build/frontend-models/use-updated-event.js +1 -1
  39. package/build/frontend-models/websocket-channel.js +256 -93
  40. package/build/frontend-models/websocket-publishers.js +218 -18
  41. package/build/http-server/client/websocket-session.js +13 -1
  42. package/build/http-server/websocket-channel.js +15 -2
  43. package/build/src/authorization/ability.d.ts +9 -3
  44. package/build/src/authorization/ability.d.ts.map +1 -1
  45. package/build/src/authorization/ability.js +33 -14
  46. package/build/src/background-jobs/adapter.d.ts +6 -0
  47. package/build/src/background-jobs/adapter.d.ts.map +1 -1
  48. package/build/src/background-jobs/adapter.js +9 -1
  49. package/build/src/background-jobs/main.d.ts +6 -0
  50. package/build/src/background-jobs/main.d.ts.map +1 -1
  51. package/build/src/background-jobs/main.js +34 -11
  52. package/build/src/background-jobs/store.d.ts +66 -12
  53. package/build/src/background-jobs/store.d.ts.map +1 -1
  54. package/build/src/background-jobs/store.js +191 -35
  55. package/build/src/background-jobs/types.d.ts +55 -0
  56. package/build/src/background-jobs/types.d.ts.map +1 -1
  57. package/build/src/background-jobs/types.js +16 -1
  58. package/build/src/configuration-types.d.ts +3 -3
  59. package/build/src/configuration-types.d.ts.map +1 -1
  60. package/build/src/configuration-types.js +2 -2
  61. package/build/src/configuration.d.ts +18 -8
  62. package/build/src/configuration.d.ts.map +1 -1
  63. package/build/src/configuration.js +38 -7
  64. package/build/src/database/drivers/base-column.d.ts +5 -0
  65. package/build/src/database/drivers/base-column.d.ts.map +1 -1
  66. package/build/src/database/drivers/base-column.js +8 -1
  67. package/build/src/database/drivers/mssql/sql/update.d.ts.map +1 -1
  68. package/build/src/database/drivers/mssql/sql/update.js +2 -4
  69. package/build/src/database/drivers/mysql/sql/update.d.ts.map +1 -1
  70. package/build/src/database/drivers/mysql/sql/update.js +2 -4
  71. package/build/src/database/drivers/pgsql/column.d.ts +5 -0
  72. package/build/src/database/drivers/pgsql/column.d.ts.map +1 -1
  73. package/build/src/database/drivers/pgsql/column.js +19 -1
  74. package/build/src/database/drivers/pgsql/sql/update.d.ts.map +1 -1
  75. package/build/src/database/drivers/pgsql/sql/update.js +2 -4
  76. package/build/src/database/drivers/sqlite/sql/alter-table.d.ts.map +1 -1
  77. package/build/src/database/drivers/sqlite/sql/alter-table.js +6 -2
  78. package/build/src/database/drivers/sqlite/sql/update.d.ts.map +1 -1
  79. package/build/src/database/drivers/sqlite/sql/update.js +2 -4
  80. package/build/src/database/query/index.d.ts +4 -4
  81. package/build/src/database/query/index.d.ts.map +1 -1
  82. package/build/src/database/query/index.js +3 -3
  83. package/build/src/database/query/model-class-query.d.ts +8 -2
  84. package/build/src/database/query/model-class-query.d.ts.map +1 -1
  85. package/build/src/database/query/model-class-query.js +25 -11
  86. package/build/src/database/query/preloader/has-many.d.ts.map +1 -1
  87. package/build/src/database/query/preloader/has-many.js +4 -2
  88. package/build/src/database/query/query-data.d.ts.map +1 -1
  89. package/build/src/database/query/query-data.js +3 -2
  90. package/build/src/database/query/update-base.d.ts +7 -0
  91. package/build/src/database/query/update-base.d.ts.map +1 -1
  92. package/build/src/database/query/update-base.js +13 -1
  93. package/build/src/database/query/with-count.d.ts.map +1 -1
  94. package/build/src/database/query/with-count.js +3 -2
  95. package/build/src/database/record/acts-as-list.d.ts.map +1 -1
  96. package/build/src/database/record/acts-as-list.js +35 -14
  97. package/build/src/database/record/attachments/store.d.ts +67 -0
  98. package/build/src/database/record/attachments/store.d.ts.map +1 -1
  99. package/build/src/database/record/attachments/store.js +301 -25
  100. package/build/src/database/record/auditing.d.ts.map +1 -1
  101. package/build/src/database/record/auditing.js +7 -4
  102. package/build/src/database/record/index.d.ts +78 -26
  103. package/build/src/database/record/index.d.ts.map +1 -1
  104. package/build/src/database/record/index.js +171 -42
  105. package/build/src/database/record/instance-relationships/belongs-to.d.ts.map +1 -1
  106. package/build/src/database/record/instance-relationships/belongs-to.js +3 -2
  107. package/build/src/database/record/validators/uniqueness.d.ts.map +1 -1
  108. package/build/src/database/record/validators/uniqueness.js +8 -6
  109. package/build/src/environment-handlers/node/cli/commands/generate/frontend-models.d.ts +4 -4
  110. package/build/src/environment-handlers/node/cli/commands/generate/frontend-models.d.ts.map +1 -1
  111. package/build/src/environment-handlers/node/cli/commands/generate/frontend-models.js +24 -12
  112. package/build/src/frontend-model-controller.d.ts +29 -8
  113. package/build/src/frontend-model-controller.d.ts.map +1 -1
  114. package/build/src/frontend-model-controller.js +122 -51
  115. package/build/src/frontend-model-resource/base-resource.d.ts +51 -24
  116. package/build/src/frontend-model-resource/base-resource.d.ts.map +1 -1
  117. package/build/src/frontend-model-resource/base-resource.js +83 -37
  118. package/build/src/frontend-model-resource/velocious-attachment-resource.d.ts +45 -26
  119. package/build/src/frontend-model-resource/velocious-attachment-resource.d.ts.map +1 -1
  120. package/build/src/frontend-model-resource/velocious-attachment-resource.js +89 -31
  121. package/build/src/frontend-models/base.d.ts +53 -20
  122. package/build/src/frontend-models/base.d.ts.map +1 -1
  123. package/build/src/frontend-models/base.js +275 -59
  124. package/build/src/frontend-models/preloader.d.ts.map +1 -1
  125. package/build/src/frontend-models/preloader.js +6 -4
  126. package/build/src/frontend-models/query.d.ts +2 -2
  127. package/build/src/frontend-models/query.d.ts.map +1 -1
  128. package/build/src/frontend-models/query.js +14 -7
  129. package/build/src/frontend-models/resource-definition.d.ts.map +1 -1
  130. package/build/src/frontend-models/resource-definition.js +20 -3
  131. package/build/src/frontend-models/use-destroyed-event.d.ts +1 -1
  132. package/build/src/frontend-models/use-destroyed-event.d.ts.map +1 -1
  133. package/build/src/frontend-models/use-destroyed-event.js +2 -2
  134. package/build/src/frontend-models/use-model-class-event.d.ts +2 -2
  135. package/build/src/frontend-models/use-model-class-event.d.ts.map +1 -1
  136. package/build/src/frontend-models/use-model-class-event.js +3 -3
  137. package/build/src/frontend-models/use-updated-event.d.ts +1 -1
  138. package/build/src/frontend-models/use-updated-event.d.ts.map +1 -1
  139. package/build/src/frontend-models/use-updated-event.js +2 -2
  140. package/build/src/frontend-models/websocket-channel.d.ts +60 -41
  141. package/build/src/frontend-models/websocket-channel.d.ts.map +1 -1
  142. package/build/src/frontend-models/websocket-channel.js +225 -85
  143. package/build/src/frontend-models/websocket-publishers.d.ts +9 -0
  144. package/build/src/frontend-models/websocket-publishers.d.ts.map +1 -1
  145. package/build/src/frontend-models/websocket-publishers.js +190 -19
  146. package/build/src/http-server/client/websocket-session.d.ts.map +1 -1
  147. package/build/src/http-server/client/websocket-session.js +11 -2
  148. package/build/src/http-server/websocket-channel.d.ts +20 -8
  149. package/build/src/http-server/websocket-channel.d.ts.map +1 -1
  150. package/build/src/http-server/websocket-channel.js +15 -3
  151. package/build/src/sync/assets/cache.d.ts +288 -0
  152. package/build/src/sync/assets/cache.d.ts.map +1 -0
  153. package/build/src/sync/assets/cache.js +883 -0
  154. package/build/src/sync/assets/types.d.ts +199 -0
  155. package/build/src/sync/assets/types.d.ts.map +1 -0
  156. package/build/src/sync/assets/types.js +56 -0
  157. package/build/src/sync/sync-api-client.d.ts.map +1 -1
  158. package/build/src/sync/sync-api-client.js +4 -3
  159. package/build/src/sync/sync-client.d.ts.map +1 -1
  160. package/build/src/sync/sync-client.js +3 -2
  161. package/build/src/sync/sync-envelope-replay-service.d.ts.map +1 -1
  162. package/build/src/sync/sync-envelope-replay-service.js +6 -5
  163. package/build/src/sync/sync-publisher.d.ts.map +1 -1
  164. package/build/src/sync/sync-publisher.js +4 -3
  165. package/build/src/testing/browser-frontend-model-event-hook-scenarios.d.ts +2 -2
  166. package/build/src/testing/browser-frontend-model-event-hook-scenarios.d.ts.map +1 -1
  167. package/build/src/testing/browser-frontend-model-event-hook-scenarios.js +3 -3
  168. package/build/src/testing/test-profiler.d.ts +4 -4
  169. package/build/src/testing/test-profiler.d.ts.map +1 -1
  170. package/build/src/testing/test-profiler.js +3 -3
  171. package/build/src/testing/test-runner.d.ts +279 -117
  172. package/build/src/testing/test-runner.d.ts.map +1 -1
  173. package/build/src/testing/test-runner.js +503 -933
  174. package/build/src/testing/test.d.ts +11 -72
  175. package/build/src/testing/test.d.ts.map +1 -1
  176. package/build/src/testing/test.js +131 -284
  177. package/build/src/testing/velocious-attempt-executor.d.ts +54 -0
  178. package/build/src/testing/velocious-attempt-executor.d.ts.map +1 -0
  179. package/build/src/testing/velocious-attempt-executor.js +408 -0
  180. package/build/src/testing/velocious-runner-reporter.d.ts +67 -0
  181. package/build/src/testing/velocious-runner-reporter.d.ts.map +1 -0
  182. package/build/src/testing/velocious-runner-reporter.js +240 -0
  183. package/build/src/testing/velocious-suite-hook-executor.d.ts +19 -0
  184. package/build/src/testing/velocious-suite-hook-executor.d.ts.map +1 -0
  185. package/build/src/testing/velocious-suite-hook-executor.js +41 -0
  186. package/build/src/testing/velocious-test-arguments.d.ts +39 -0
  187. package/build/src/testing/velocious-test-arguments.d.ts.map +1 -0
  188. package/build/src/testing/velocious-test-arguments.js +55 -0
  189. package/build/src/utils/model-primary-key.d.ts +73 -0
  190. package/build/src/utils/model-primary-key.d.ts.map +1 -0
  191. package/build/src/utils/model-primary-key.js +144 -0
  192. package/build/src/utils/sha256-bytes-hex.d.ts +7 -0
  193. package/build/src/utils/sha256-bytes-hex.d.ts.map +1 -0
  194. package/build/src/utils/sha256-bytes-hex.js +117 -0
  195. package/build/src/utils/sha256-hex.d.ts +2 -4
  196. package/build/src/utils/sha256-hex.d.ts.map +1 -1
  197. package/build/src/utils/sha256-hex.js +6 -120
  198. package/build/sync/assets/cache.js +998 -0
  199. package/build/sync/assets/types.js +62 -0
  200. package/build/sync/sync-api-client.js +3 -2
  201. package/build/sync/sync-client.js +2 -1
  202. package/build/sync/sync-envelope-replay-service.js +5 -4
  203. package/build/sync/sync-publisher.js +3 -2
  204. package/build/testing/browser-frontend-model-event-hook-scenarios.js +2 -2
  205. package/build/testing/test-profiler.js +2 -2
  206. package/build/testing/test-runner.js +496 -990
  207. package/build/testing/test.js +164 -306
  208. package/build/testing/velocious-attempt-executor.js +441 -0
  209. package/build/testing/velocious-runner-reporter.js +264 -0
  210. package/build/testing/velocious-suite-hook-executor.js +43 -0
  211. package/build/testing/velocious-test-arguments.js +64 -0
  212. package/build/tsconfig.tsbuildinfo +1 -1
  213. package/build/utils/model-primary-key.js +171 -0
  214. package/build/utils/sha256-bytes-hex.js +132 -0
  215. package/build/utils/sha256-hex.js +7 -135
  216. package/package.json +5 -4
  217. package/src/authorization/ability.js +36 -13
  218. package/src/background-jobs/adapter.js +9 -0
  219. package/src/background-jobs/main.js +35 -10
  220. package/src/background-jobs/store.js +217 -38
  221. package/src/background-jobs/types.js +15 -0
  222. package/src/configuration-types.js +1 -1
  223. package/src/configuration.js +45 -9
  224. package/src/database/drivers/base-column.js +8 -0
  225. package/src/database/drivers/mssql/sql/update.js +1 -3
  226. package/src/database/drivers/mysql/sql/update.js +1 -3
  227. package/src/database/drivers/pgsql/column.js +22 -0
  228. package/src/database/drivers/pgsql/sql/update.js +1 -3
  229. package/src/database/drivers/sqlite/sql/alter-table.js +11 -1
  230. package/src/database/drivers/sqlite/sql/update.js +1 -3
  231. package/src/database/query/index.js +2 -2
  232. package/src/database/query/model-class-query.js +26 -10
  233. package/src/database/query/preloader/has-many.js +4 -1
  234. package/src/database/query/query-data.js +2 -1
  235. package/src/database/query/update-base.js +14 -0
  236. package/src/database/query/with-count.js +3 -1
  237. package/src/database/record/acts-as-list.js +37 -13
  238. package/src/database/record/attachments/store.js +336 -26
  239. package/src/database/record/auditing.js +6 -3
  240. package/src/database/record/index.js +197 -42
  241. package/src/database/record/instance-relationships/belongs-to.js +2 -1
  242. package/src/database/record/validators/uniqueness.js +8 -5
  243. package/src/environment-handlers/node/cli/commands/generate/frontend-models.js +24 -8
  244. package/src/frontend-model-controller.js +136 -53
  245. package/src/frontend-model-resource/base-resource.js +87 -37
  246. package/src/frontend-model-resource/velocious-attachment-resource.js +98 -31
  247. package/src/frontend-models/base.js +307 -59
  248. package/src/frontend-models/preloader.js +6 -3
  249. package/src/frontend-models/query.js +15 -6
  250. package/src/frontend-models/resource-definition.js +21 -2
  251. package/src/frontend-models/use-destroyed-event.js +1 -1
  252. package/src/frontend-models/use-model-class-event.js +2 -2
  253. package/src/frontend-models/use-updated-event.js +1 -1
  254. package/src/frontend-models/websocket-channel.js +256 -93
  255. package/src/frontend-models/websocket-publishers.js +218 -18
  256. package/src/http-server/client/websocket-session.js +13 -1
  257. package/src/http-server/websocket-channel.js +15 -2
  258. package/src/sync/assets/cache.js +998 -0
  259. package/src/sync/assets/types.js +62 -0
  260. package/src/sync/sync-api-client.js +3 -2
  261. package/src/sync/sync-client.js +2 -1
  262. package/src/sync/sync-envelope-replay-service.js +5 -4
  263. package/src/sync/sync-publisher.js +3 -2
  264. package/src/testing/browser-frontend-model-event-hook-scenarios.js +2 -2
  265. package/src/testing/test-profiler.js +2 -2
  266. package/src/testing/test-runner.js +496 -990
  267. package/src/testing/test.js +164 -306
  268. package/src/testing/velocious-attempt-executor.js +441 -0
  269. package/src/testing/velocious-runner-reporter.js +264 -0
  270. package/src/testing/velocious-suite-hook-executor.js +43 -0
  271. package/src/testing/velocious-test-arguments.js +64 -0
  272. package/src/utils/model-primary-key.js +171 -0
  273. package/src/utils/sha256-bytes-hex.js +132 -0
  274. package/src/utils/sha256-hex.js +7 -135
  275. package/build/src/testing/testing-package-adapter.d.ts +0 -9
  276. package/build/src/testing/testing-package-adapter.d.ts.map +0 -1
  277. package/build/src/testing/testing-package-adapter.js +0 -69
  278. package/build/testing/testing-package-adapter.js +0 -76
  279. package/src/testing/testing-package-adapter.js +0 -76
@@ -0,0 +1,264 @@
1
+ // @ts-check
2
+
3
+ import { addTrackedStackToError } from "../utils/with-tracked-stack.js"
4
+ import BacktraceCleaner from "../utils/backtrace-cleaner-node.js"
5
+ import picocolors from "picocolors"
6
+ import restArgsError from "../utils/rest-args-error.js"
7
+ import { testEvents } from "./test.js"
8
+
9
+ /** @typedef {import("@velocious/testing/runner").TestDeclaration} PackageTestDeclaration */
10
+
11
+ /** Stops package traversal after framework-owned connection quarantine. */
12
+ export class AbortRemainingTestsError extends Error {}
13
+
14
+ export default class VelociousRunnerReporter {
15
+ /**
16
+ * Creates the legacy event and result projection adapter.
17
+ * @param {object} args - Constructor arguments.
18
+ * @param {import("./test-runner.js").default} args.testRunner - Owning Velocious runner.
19
+ */
20
+ constructor({testRunner, ...restArgs}) {
21
+ restArgsError(restArgs)
22
+ this.testRunner = testRunner
23
+ /** @type {WeakMap<PackageTestDeclaration, import("./test-runner.js").AttemptConsoleOutput[]>} */
24
+ this.attemptConsoleOutputs = new WeakMap()
25
+ /** @type {PackageTestDeclaration | undefined} */
26
+ this.activeTest = undefined
27
+ }
28
+
29
+ /**
30
+ * Translates one awaited package runner event into the legacy contract.
31
+ * @param {import("@velocious/testing/runner").RunnerEvent} event - Structured package event.
32
+ * @returns {Promise<void>} - Resolves after legacy listeners finish.
33
+ */
34
+ async onEvent(event) {
35
+ if (event.type === "test:start") {
36
+ this.activeTest = this.testRunner.findTestDeclaration(event.fullName)
37
+ if (this.activeTest) {
38
+ const metadata = this.testRunner.testMetadata(this.activeTest)
39
+ console.log(`${" ".repeat(metadata.descriptions.length * 2)}it ${metadata.testDescription}`)
40
+ }
41
+ return
42
+ }
43
+
44
+ if (event.type === "attempt:finish") {
45
+ await this.reportAttemptEvent(event)
46
+ return
47
+ }
48
+
49
+ if (event.type === "test:finish") {
50
+ await this.reportTestEvent(event)
51
+ return
52
+ }
53
+
54
+ if (event.type === "run:finish") this.testRunner.recordPackageResult(event.result)
55
+ }
56
+
57
+ /**
58
+ * Projects attempt failure/retry events while retaining the raw thrown value.
59
+ * @param {import("@velocious/testing/runner").RunnerEvent} event - Attempt event.
60
+ * @returns {Promise<void>} - Resolves after listeners finish.
61
+ */
62
+ async reportAttemptEvent(event) {
63
+ const test = this.activeTest || this.testRunner.findTestDeclaration(event.fullName)
64
+
65
+ if (!test) throw new Error(`Package runner attempt did not match a declaration: ${event.fullName}`)
66
+
67
+ // Narrows the structured event payload for this event discriminator.
68
+ const attempt = /** @type {import("@velocious/testing/runner").TestAttemptResult} */ (event.attempt)
69
+ const outcome = this.testRunner.attemptOutcome(test, attempt.attemptNumber)
70
+ const attemptConsoleOutputs = this.attemptConsoleOutputs.get(test) || []
71
+
72
+ if (attempt.consoleOutput) {
73
+ attemptConsoleOutputs.push({attemptNumber: attempt.attemptNumber, output: attempt.consoleOutput.trimEnd()})
74
+ this.attemptConsoleOutputs.set(test, attemptConsoleOutputs)
75
+ }
76
+
77
+ const retryCount = this.testRunner.retryCount(test)
78
+ const failed = outcome?.failed ?? Boolean(attempt.error)
79
+ const error = outcome?.error
80
+ const retriesUsed = Math.min(attempt.attemptNumber, retryCount)
81
+ const willRetry = failed && !outcome?.abortRemainingTests && attempt.attemptNumber <= retryCount
82
+ const {descriptions, testDescription} = this.testRunner.testMetadata(test)
83
+ const compatibility = this.testRunner.testData(test)
84
+
85
+ if (failed) {
86
+ await this.emitEvent("testAttemptFailed", {
87
+ configuration: this.testRunner.getConfiguration(),
88
+ descriptions,
89
+ error,
90
+ attemptNumber: attempt.attemptNumber,
91
+ nextAttempt: willRetry ? attempt.attemptNumber + 1 : undefined,
92
+ retriesUsed,
93
+ retryCount,
94
+ testArgs: compatibility.testArgs,
95
+ testData: compatibility.testData,
96
+ testDescription,
97
+ testRunner: this.testRunner,
98
+ willRetry
99
+ })
100
+ }
101
+
102
+ if (willRetry) {
103
+ console.warn(picocolors.red(`${" ".repeat(descriptions.length * 2)} Retrying (${retriesUsed}/${retryCount}) after error: ${error instanceof Error ? error.message : String(error)}`))
104
+ await this.emitEvent("testRetrying", {
105
+ configuration: this.testRunner.getConfiguration(),
106
+ descriptions,
107
+ error,
108
+ nextAttempt: attempt.attemptNumber + 1,
109
+ retriesUsed,
110
+ retryCount,
111
+ testArgs: compatibility.testArgs,
112
+ testData: compatibility.testData,
113
+ testDescription,
114
+ testRunner: this.testRunner
115
+ })
116
+ }
117
+
118
+ if (attempt.attemptNumber > 1) {
119
+ await this.emitEvent("testRetried", {
120
+ configuration: this.testRunner.getConfiguration(),
121
+ descriptions,
122
+ error,
123
+ attemptNumber: attempt.attemptNumber,
124
+ retriesUsed,
125
+ retryCount,
126
+ testArgs: compatibility.testArgs,
127
+ testData: compatibility.testData,
128
+ testDescription,
129
+ testRunner: this.testRunner
130
+ })
131
+ }
132
+
133
+ if (outcome?.abortRemainingTests) {
134
+ const metadata = this.testRunner.testMetadata(test)
135
+
136
+ this.testRunner.recordTestDuration({
137
+ durationMs: attempt.durationMs,
138
+ filePath: compatibility.testData.filePath ?? "<unknown>",
139
+ fullDescription: metadata.fullDescription,
140
+ line: compatibility.testData.line ?? 0
141
+ })
142
+ await this.reportFailedTest({
143
+ attemptConsoleOutputs,
144
+ descriptions,
145
+ error,
146
+ leftPadding: " ".repeat(descriptions.length * 2),
147
+ testArgs: compatibility.testArgs,
148
+ testData: compatibility.testData,
149
+ testDescription
150
+ })
151
+ this.testRunner.completeTestDeclaration(test)
152
+ this.activeTest = undefined
153
+ throw new AbortRemainingTestsError("Velocious quarantined an attempt-owned database connection")
154
+ }
155
+ }
156
+
157
+ /**
158
+ * Projects final package result accounting and failures.
159
+ * @param {import("@velocious/testing/runner").RunnerEvent} event - Test result event.
160
+ * @returns {Promise<void>} - Resolves after listeners finish.
161
+ */
162
+ async reportTestEvent(event) {
163
+ // Narrows the structured event payload for this event discriminator.
164
+ const packageTestResult = /** @type {import("@velocious/testing/runner").TestResult} */ (event.test)
165
+ const test = this.activeTest || this.testRunner.findTestDeclaration(packageTestResult.fullName)
166
+
167
+ if (!test) throw new Error(`Package runner result did not match a declaration: ${packageTestResult.fullName}`)
168
+
169
+ const metadata = this.testRunner.testMetadata(test)
170
+ const compatibility = this.testRunner.testData(test)
171
+ const durationMs = packageTestResult.attempts.reduce((total, attempt) => total + attempt.durationMs, 0)
172
+
173
+ if (packageTestResult.attempts.length > 0) {
174
+ this.testRunner.recordTestDuration({
175
+ durationMs,
176
+ filePath: compatibility.testData.filePath ?? "<unknown>",
177
+ fullDescription: metadata.fullDescription,
178
+ line: compatibility.testData.line ?? 0
179
+ })
180
+ }
181
+
182
+ if (packageTestResult.status === "passed") {
183
+ this.testRunner.recordSuccessfulTest()
184
+ } else {
185
+ const finalAttempt = packageTestResult.attempts.at(-1)
186
+ const outcome = finalAttempt
187
+ ? this.testRunner.attemptOutcome(test, finalAttempt.attemptNumber)
188
+ : undefined
189
+ const error = outcome?.failed
190
+ ? outcome.error
191
+ : this.testRunner.setupFailureFor(test)
192
+
193
+ await this.reportFailedTest({
194
+ attemptConsoleOutputs: this.attemptConsoleOutputs.get(test) || [],
195
+ descriptions: metadata.descriptions,
196
+ error,
197
+ leftPadding: " ".repeat(metadata.descriptions.length * 2),
198
+ testArgs: compatibility.testArgs,
199
+ testData: compatibility.testData,
200
+ testDescription: metadata.testDescription
201
+ })
202
+ }
203
+
204
+ this.testRunner.completeTestDeclaration(test)
205
+ this.activeTest = undefined
206
+ }
207
+
208
+ /**
209
+ * Records and emits one final failed test result.
210
+ * @param {object} args - Final failure metadata.
211
+ * @param {import("./test-runner.js").AttemptConsoleOutput[]} args.attemptConsoleOutputs - Captured output across attempts.
212
+ * @param {string[]} args.descriptions - Parent description stack.
213
+ * @param {ReturnType<typeof JSON.parse>} args.error - Raw final failure.
214
+ * @param {string} args.leftPadding - Console indentation.
215
+ * @param {import("./test-runner.js").TestArgs} args.testArgs - Stable test arguments.
216
+ * @param {import("./test-runner.js").TestData} args.testData - Test registration.
217
+ * @param {string} args.testDescription - Test description.
218
+ * @returns {Promise<void>} - Resolves after the final-failure listener completes.
219
+ */
220
+ async reportFailedTest({attemptConsoleOutputs, descriptions, error, leftPadding, testArgs, testData, testDescription}) {
221
+ const testRunner = this.testRunner
222
+ const consoleOutput = testRunner.buildConsoleOutput(attemptConsoleOutputs)
223
+
224
+ if (error instanceof Error) {
225
+ console.error(picocolors.red(`${leftPadding} Test failed: ${error.message}`))
226
+ addTrackedStackToError(error)
227
+
228
+ const backtraceCleaner = new BacktraceCleaner(error)
229
+ const cleanedStack = backtraceCleaner.getCleanedStack()
230
+ const stackLines = cleanedStack?.split("\n")
231
+
232
+ if (stackLines) {
233
+ for (const stackLine of stackLines) console.error(picocolors.red(`${leftPadding} ${stackLine}`))
234
+ }
235
+ } else {
236
+ console.error(picocolors.red(`${leftPadding} Test failed with a ${typeof error}: ${String(error)}`))
237
+ }
238
+
239
+ testRunner.printFailedConsoleOutput({consoleOutput, leftPadding})
240
+ testRunner.recordFailedTest({descriptions, error, consoleOutput, testData, testDescription})
241
+
242
+ await this.emitEvent("testFailed", {
243
+ configuration: testRunner.getConfiguration(),
244
+ descriptions,
245
+ error,
246
+ testArgs,
247
+ testData,
248
+ testDescription,
249
+ testRunner
250
+ })
251
+
252
+ testRunner.printRerunCommand({descriptions, testDescription, testData, leftPadding})
253
+ }
254
+
255
+ /**
256
+ * Emits one legacy event and awaits listeners in registration order.
257
+ * @param {string} eventName - Event name.
258
+ * @param {object} payload - Event payload.
259
+ * @returns {Promise<void>} - Resolves when all listeners complete.
260
+ */
261
+ async emitEvent(eventName, payload) {
262
+ for (const listener of testEvents.listeners(eventName)) await listener(payload)
263
+ }
264
+ }
@@ -0,0 +1,43 @@
1
+ // @ts-check
2
+
3
+ import restArgsError from "../utils/rest-args-error.js"
4
+
5
+ export default class VelociousSuiteHookExecutor {
6
+ /**
7
+ * Creates an executor for Velocious suite hooks.
8
+ * @param {object} args - Constructor arguments.
9
+ * @param {import("./test-runner.js").default} args.testRunner - Owning Velocious runner.
10
+ */
11
+ constructor({testRunner, ...restArgs}) {
12
+ restArgsError(restArgs)
13
+ this.testRunner = testRunner
14
+ }
15
+
16
+ /**
17
+ * Supplies the framework configuration while package traversal owns ordering,
18
+ * timeout enforcement, aggregation, and active-scope cleanup.
19
+ * @param {import("@velocious/testing/runner").SuiteHookExecutorInput} input - Package hook input.
20
+ * @returns {Promise<void>} - Resolves after the hook completes.
21
+ */
22
+ async execute({context, defaultExecute, fullName, hook, phase, suite, timeoutMs, ...restArgs}) {
23
+ restArgsError(restArgs)
24
+ void context
25
+ void fullName
26
+ void timeoutMs
27
+ const metadata = this.testRunner.hookMetadata(hook)
28
+
29
+ try {
30
+ await this.testRunner.runProfileSpan({
31
+ phase,
32
+ declarationIndex: metadata.declarationIndex,
33
+ declarationScopeId: metadata.declarationScopeId,
34
+ filePath: metadata.ownerFilePath
35
+ }, async () => {
36
+ await defaultExecute([{configuration: this.testRunner.getConfiguration()}])
37
+ })
38
+ } catch (error) {
39
+ this.testRunner.recordSuiteHookFailure({suite, phase, error})
40
+ throw error
41
+ }
42
+ }
43
+ }
@@ -0,0 +1,64 @@
1
+ // @ts-check
2
+
3
+ import restArgsError from "../utils/rest-args-error.js"
4
+
5
+ /** @typedef {import("./test-runner.js").TestArgs} TestArgs */
6
+ /** @typedef {import("./test-runner.js").TestData} TestData */
7
+ /** @typedef {import("@velocious/testing/runner").TestDeclaration} PackageTestDeclaration */
8
+
9
+ export default class VelociousTestArguments {
10
+ /**
11
+ * Creates the framework-owned test argument adapter.
12
+ * @param {object} args - Constructor arguments.
13
+ * @param {import("./test-runner.js").default} args.testRunner - Owning Velocious runner.
14
+ */
15
+ constructor({testRunner, ...restArgs}) {
16
+ restArgsError(restArgs)
17
+ this.testRunner = testRunner
18
+ }
19
+
20
+ /**
21
+ * Resolves stable Velocious arguments after package-owned table arguments.
22
+ * @param {object} input - Package resolver input.
23
+ * @param {PackageTestDeclaration} input.test - Selected declaration.
24
+ * @returns {Promise<ReturnType<typeof JSON.parse>[]>} - Callback arguments.
25
+ */
26
+ async resolve({test}) {
27
+ const compatibility = await this.testRunner.testCompatibility(test)
28
+
29
+ return [...test.rowArguments, compatibility.testArgs]
30
+ }
31
+
32
+ /**
33
+ * Copies declaration metadata before selection can inspect it.
34
+ * @param {PackageTestDeclaration} testData - Test registration.
35
+ * @returns {TestArgs} - Independent test arguments.
36
+ */
37
+ copy(testData) {
38
+ const testArgs = /** @type {TestArgs} */ (Object.assign({}, testData.options))
39
+
40
+ if (testArgs.retry === undefined && typeof testData.options.retries === "number") {
41
+ testArgs.retry = testData.options.retries
42
+ }
43
+ if (testArgs.timeoutSeconds === undefined && typeof testData.options.timeoutMs === "number") {
44
+ testArgs.timeoutSeconds = testData.options.timeoutMs / 1000
45
+ }
46
+
47
+ return testArgs
48
+ }
49
+
50
+ /**
51
+ * Injects type-specific framework collaborators after selection.
52
+ * @param {TestArgs} testArgs - Selected test arguments.
53
+ * @returns {Promise<void>} - Resolves after required collaborators are ready.
54
+ */
55
+ async inject(testArgs) {
56
+ if (testArgs.type == "model" || testArgs.type == "request") {
57
+ testArgs.application = await this.testRunner.application()
58
+ }
59
+
60
+ if (testArgs.type == "request") {
61
+ testArgs.client = await this.testRunner.requestClient()
62
+ }
63
+ }
64
+ }
@@ -0,0 +1,171 @@
1
+ // @ts-check
2
+
3
+ import isPlainObject from "./plain-object.js"
4
+
5
+ /** @typedef {string | number} ModelPrimaryKeyScalar */
6
+ /** @typedef {string | string[]} ModelPrimaryKeyDefinition */
7
+ /** @typedef {Record<string, ModelPrimaryKeyScalar>} CompositeModelPrimaryKeyValue */
8
+ /** @typedef {ModelPrimaryKeyScalar | CompositeModelPrimaryKeyValue} ModelPrimaryKeyValue */
9
+
10
+ /**
11
+ * Validates an untrusted composite identity and returns its definition-ordered values.
12
+ * @param {string[]} primaryKey - Composite primary-key definition.
13
+ * @param {ReturnType<typeof JSON.parse>} value - Candidate identity.
14
+ * @returns {CompositeModelPrimaryKeyValue} - Validated identity.
15
+ */
16
+ export function validatedCompositePrimaryKeyValue(primaryKey, value) {
17
+ if (!isPlainObject(value)) {
18
+ throw new TypeError("Expected composite primary key identity to be a plain object.")
19
+ }
20
+
21
+ const valueKeys = Object.keys(value)
22
+
23
+ if (valueKeys.length !== primaryKey.length || primaryKey.some((key) => !Object.prototype.hasOwnProperty.call(value, key))) {
24
+ throw new TypeError(`Expected composite primary key identity fields: ${primaryKey.join(", ")}.`)
25
+ }
26
+
27
+ /** @type {CompositeModelPrimaryKeyValue} */
28
+ const validatedValue = {}
29
+
30
+ for (const key of primaryKey) {
31
+ const keyValue = value[key]
32
+
33
+ if (typeof keyValue !== "string" && typeof keyValue !== "number") {
34
+ throw new TypeError(`Expected composite primary key identity field '${key}' to be a string or number.`)
35
+ }
36
+
37
+ validatedValue[key] = keyValue
38
+ }
39
+
40
+ return validatedValue
41
+ }
42
+
43
+ /**
44
+ * Returns a scalar primary-key definition or fails at a feature boundary that cannot represent composite identities.
45
+ * @param {ModelPrimaryKeyDefinition} primaryKey - Primary-key definition.
46
+ * @param {string} operation - Operation requiring a scalar identity.
47
+ * @returns {string} - Scalar primary-key column or attribute name.
48
+ */
49
+ export function scalarModelPrimaryKey(primaryKey, operation) {
50
+ if (Array.isArray(primaryKey)) {
51
+ throw new Error(`${operation} does not support composite primary keys.`)
52
+ }
53
+
54
+ return primaryKey
55
+ }
56
+
57
+ /**
58
+ * Returns a scalar identity or fails at a feature boundary that cannot represent composite identities.
59
+ * @param {ModelPrimaryKeyValue} value - Model identity.
60
+ * @param {string} operation - Operation requiring a scalar identity.
61
+ * @returns {ModelPrimaryKeyScalar} - Scalar identity.
62
+ */
63
+ export function scalarModelPrimaryKeyValue(value, operation) {
64
+ if (typeof value !== "string" && typeof value !== "number") {
65
+ throw new Error(`${operation} does not support composite primary keys.`)
66
+ }
67
+
68
+ return value
69
+ }
70
+
71
+ /**
72
+ * Builds query conditions from a scalar or composite model identity.
73
+ * @param {ModelPrimaryKeyDefinition} primaryKey - Primary-key definition.
74
+ * @param {ReturnType<typeof JSON.parse>} value - Candidate identity.
75
+ * @returns {Record<string, ModelPrimaryKeyScalar>} - Primary-key query conditions.
76
+ */
77
+ export function modelPrimaryKeyConditions(primaryKey, value) {
78
+ if (Array.isArray(primaryKey)) return validatedCompositePrimaryKeyValue(primaryKey, value)
79
+
80
+ return {[primaryKey]: /** @type {ModelPrimaryKeyScalar} */ (value)}
81
+ }
82
+
83
+ /**
84
+ * Builds a qualified SQL predicate matching any supplied composite identity.
85
+ * @param {object} args - Arguments.
86
+ * @param {typeof import("../database/record/index.js").default} args.modelClass - Model class owning the identity attributes.
87
+ * @param {string[]} args.primaryKey - Composite primary-key definition.
88
+ * @param {import("../database/query/model-class-query.js").default<typeof import("../database/record/index.js").default>} args.query - Query receiving the predicate.
89
+ * @param {ModelPrimaryKeyValue[]} args.values - Candidate identities.
90
+ * @returns {string} - Qualified SQL predicate.
91
+ */
92
+ export function compositeModelPrimaryKeyCohortSql({modelClass, primaryKey, query, values}) {
93
+ if (values.length === 0) return "(1 = 0)"
94
+
95
+ const tableSql = query.driver.quoteTable(query.getTableReferenceForJoin())
96
+
97
+ return `(${values.map((value) => {
98
+ const conditions = modelPrimaryKeyConditions(primaryKey, value)
99
+ const identitySql = primaryKey.map((attributeName) => {
100
+ const columnName = modelClass.getColumnNameForAttributeName(attributeName)
101
+
102
+ return `${tableSql}.${query.driver.quoteColumn(columnName)} = ${query.driver.quote(conditions[attributeName])}`
103
+ }).join(" AND ")
104
+
105
+ return `(${identitySql})`
106
+ }).join(" OR ")})`
107
+ }
108
+
109
+ /**
110
+ * Reads a scalar or composite identity through the owning model's value reader.
111
+ * @param {ModelPrimaryKeyDefinition} primaryKey - Primary-key definition.
112
+ * @param {(key: string) => ReturnType<typeof JSON.parse>} read - Primary-key value reader.
113
+ * @returns {ModelPrimaryKeyValue} - Model identity.
114
+ */
115
+ export function readModelPrimaryKeyValue(primaryKey, read) {
116
+ if (!Array.isArray(primaryKey)) return /** @type {ModelPrimaryKeyScalar} */ (read(primaryKey))
117
+
118
+ /** @type {CompositeModelPrimaryKeyValue} */
119
+ const value = {}
120
+
121
+ for (const key of primaryKey) value[key] = /** @type {ModelPrimaryKeyScalar} */ (read(key))
122
+
123
+ return validatedCompositePrimaryKeyValue(primaryKey, value)
124
+ }
125
+
126
+ /**
127
+ * Returns an unambiguous map key while preserving legacy scalar map keys.
128
+ * @param {ModelPrimaryKeyDefinition} primaryKey - Primary-key definition.
129
+ * @param {ReturnType<typeof JSON.parse>} value - Candidate identity.
130
+ * @returns {string} - Canonical identity key.
131
+ */
132
+ export function modelPrimaryKeyCacheKey(primaryKey, value) {
133
+ if (!Array.isArray(primaryKey)) return String(value)
134
+
135
+ const validatedValue = validatedCompositePrimaryKeyValue(primaryKey, value)
136
+
137
+ return JSON.stringify(primaryKey.map((key) => validatedValue[key]))
138
+ }
139
+
140
+ /**
141
+ * Restores a model identity from its canonical string representation.
142
+ * @param {ModelPrimaryKeyDefinition} primaryKey - Primary-key definition.
143
+ * @param {string} cacheKey - Canonical identity key.
144
+ * @returns {ModelPrimaryKeyValue} - Restored identity.
145
+ */
146
+ export function modelPrimaryKeyValueFromCacheKey(primaryKey, cacheKey) {
147
+ if (!Array.isArray(primaryKey)) return cacheKey
148
+
149
+ let parsed
150
+
151
+ try {
152
+ parsed = JSON.parse(cacheKey)
153
+ } catch (error) {
154
+ if (!(error instanceof SyntaxError)) throw error
155
+
156
+ throw new TypeError("Expected composite primary key cache value to be valid JSON.", {cause: error})
157
+ }
158
+
159
+ if (!Array.isArray(parsed) || parsed.length !== primaryKey.length) {
160
+ throw new TypeError(`Expected composite primary key cache value with ${primaryKey.length} fields.`)
161
+ }
162
+
163
+ /** @type {CompositeModelPrimaryKeyValue} */
164
+ const value = {}
165
+
166
+ for (let index = 0; index < primaryKey.length; index += 1) {
167
+ value[primaryKey[index]] = parsed[index]
168
+ }
169
+
170
+ return validatedCompositePrimaryKeyValue(primaryKey, value)
171
+ }
@@ -0,0 +1,132 @@
1
+ // @ts-check
2
+
3
+ /**
4
+ * Computes a SHA-256 hex digest for arbitrary bytes without Node-only imports.
5
+ * @param {Uint8Array} bytes - Bytes to digest.
6
+ * @returns {string} Hex digest.
7
+ */
8
+ export default function sha256BytesHex(bytes) {
9
+ const bitLength = bytes.length * 8
10
+ const blockCount = Math.ceil((bytes.length + 9) / 64)
11
+ const block = new Uint8Array(64)
12
+ const hash = SHA256_INITIAL_HASH.slice()
13
+ /** @type {number[]} */
14
+ const words = new Array(64)
15
+
16
+ const highLength = Math.floor(bitLength / 0x100000000)
17
+ const lowLength = bitLength >>> 0
18
+
19
+ for (let blockIndex = 0; blockIndex < blockCount; blockIndex += 1) {
20
+ const offset = blockIndex * 64
21
+ const chunkLength = Math.min(64, Math.max(0, bytes.length - offset))
22
+
23
+ block.fill(0)
24
+ if (chunkLength > 0) block.set(bytes.subarray(offset, offset + chunkLength))
25
+ if (bytes.length >= offset && bytes.length < offset + 64) block[bytes.length - offset] = 0x80
26
+
27
+ if (blockIndex === blockCount - 1) {
28
+ block[56] = (highLength >>> 24) & 0xff
29
+ block[57] = (highLength >>> 16) & 0xff
30
+ block[58] = (highLength >>> 8) & 0xff
31
+ block[59] = highLength & 0xff
32
+ block[60] = (lowLength >>> 24) & 0xff
33
+ block[61] = (lowLength >>> 16) & 0xff
34
+ block[62] = (lowLength >>> 8) & 0xff
35
+ block[63] = lowLength & 0xff
36
+ }
37
+
38
+ for (let index = 0; index < 16; index++) {
39
+ const byteIndex = index * 4
40
+
41
+ words[index] = ((block[byteIndex] << 24) | (block[byteIndex + 1] << 16) | (block[byteIndex + 2] << 8) | block[byteIndex + 3]) >>> 0
42
+ }
43
+
44
+ for (let index = 16; index < 64; index++) {
45
+ const s0 = rotateRight(words[index - 15], 7) ^ rotateRight(words[index - 15], 18) ^ (words[index - 15] >>> 3)
46
+ const s1 = rotateRight(words[index - 2], 17) ^ rotateRight(words[index - 2], 19) ^ (words[index - 2] >>> 10)
47
+
48
+ words[index] = add32(words[index - 16], s0, words[index - 7], s1)
49
+ }
50
+
51
+ let a = hash[0]
52
+ let b = hash[1]
53
+ let c = hash[2]
54
+ let d = hash[3]
55
+ let e = hash[4]
56
+ let f = hash[5]
57
+ let g = hash[6]
58
+ let h = hash[7]
59
+
60
+ for (let index = 0; index < 64; index++) {
61
+ const s1 = rotateRight(e, 6) ^ rotateRight(e, 11) ^ rotateRight(e, 25)
62
+ const ch = (e & f) ^ ((~e) & g)
63
+ const temp1 = add32(h, s1, ch, SHA256_K[index], words[index])
64
+ const s0 = rotateRight(a, 2) ^ rotateRight(a, 13) ^ rotateRight(a, 22)
65
+ const maj = (a & b) ^ (a & c) ^ (b & c)
66
+ const temp2 = add32(s0, maj)
67
+
68
+ h = g
69
+ g = f
70
+ f = e
71
+ e = add32(d, temp1)
72
+ d = c
73
+ c = b
74
+ b = a
75
+ a = add32(temp1, temp2)
76
+ }
77
+
78
+ hash[0] = add32(hash[0], a)
79
+ hash[1] = add32(hash[1], b)
80
+ hash[2] = add32(hash[2], c)
81
+ hash[3] = add32(hash[3], d)
82
+ hash[4] = add32(hash[4], e)
83
+ hash[5] = add32(hash[5], f)
84
+ hash[6] = add32(hash[6], g)
85
+ hash[7] = add32(hash[7], h)
86
+ }
87
+
88
+ return hash.map((value) => value.toString(16).padStart(8, "0")).join("")
89
+ }
90
+
91
+ const SHA256_INITIAL_HASH = [
92
+ 0x6a09e667, 0xbb67ae85, 0x3c6ef372, 0xa54ff53a,
93
+ 0x510e527f, 0x9b05688c, 0x1f83d9ab, 0x5be0cd19
94
+ ]
95
+
96
+ const SHA256_K = [
97
+ 0x428a2f98, 0x71374491, 0xb5c0fbcf, 0xe9b5dba5,
98
+ 0x3956c25b, 0x59f111f1, 0x923f82a4, 0xab1c5ed5,
99
+ 0xd807aa98, 0x12835b01, 0x243185be, 0x550c7dc3,
100
+ 0x72be5d74, 0x80deb1fe, 0x9bdc06a7, 0xc19bf174,
101
+ 0xe49b69c1, 0xefbe4786, 0x0fc19dc6, 0x240ca1cc,
102
+ 0x2de92c6f, 0x4a7484aa, 0x5cb0a9dc, 0x76f988da,
103
+ 0x983e5152, 0xa831c66d, 0xb00327c8, 0xbf597fc7,
104
+ 0xc6e00bf3, 0xd5a79147, 0x06ca6351, 0x14292967,
105
+ 0x27b70a85, 0x2e1b2138, 0x4d2c6dfc, 0x53380d13,
106
+ 0x650a7354, 0x766a0abb, 0x81c2c92e, 0x92722c85,
107
+ 0xa2bfe8a1, 0xa81a664b, 0xc24b8b70, 0xc76c51a3,
108
+ 0xd192e819, 0xd6990624, 0xf40e3585, 0x106aa070,
109
+ 0x19a4c116, 0x1e376c08, 0x2748774c, 0x34b0bcb5,
110
+ 0x391c0cb3, 0x4ed8aa4a, 0x5b9cca4f, 0x682e6ff3,
111
+ 0x748f82ee, 0x78a5636f, 0x84c87814, 0x8cc70208,
112
+ 0x90befffa, 0xa4506ceb, 0xbef9a3f7, 0xc67178f2
113
+ ]
114
+
115
+ /**
116
+ * Rotates an unsigned value right.
117
+ * @param {number} value Unsigned value.
118
+ * @param {number} bits Rotation count.
119
+ * @returns {number} Rotated value.
120
+ */
121
+ function rotateRight(value, bits) {
122
+ return (value >>> bits) | (value << (32 - bits))
123
+ }
124
+
125
+ /**
126
+ * Adds numbers with unsigned 32-bit overflow.
127
+ * @param {...number} values Values to add.
128
+ * @returns {number} Unsigned sum.
129
+ */
130
+ function add32(...values) {
131
+ return values.reduce((sum, value) => (sum + value) >>> 0, 0)
132
+ }