@harperfast/harper 5.2.13 → 5.3.0-beta.1

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 (655) hide show
  1. package/agent/mcpTools.ts +1 -1
  2. package/agent/session.ts +25 -14
  3. package/bin/cliOperations.ts +58 -9
  4. package/bin/copyDb.ts +282 -59
  5. package/bin/deploySetup.ts +16 -5
  6. package/bin/harper.ts +1 -1
  7. package/bin/help.ts +4 -1
  8. package/bin/lite.ts +4 -1
  9. package/bin/restart.ts +120 -4
  10. package/bin/run.ts +6 -11
  11. package/bin/upgrade.js +7 -3
  12. package/bin/workloadIdentity.ts +119 -0
  13. package/components/Application.ts +3436 -242
  14. package/components/ApplicationScope.ts +8 -0
  15. package/components/EntryHandler.ts +59 -39
  16. package/components/OptionsWatcher.ts +440 -98
  17. package/components/RuntimeModuleTracker.ts +38 -7
  18. package/components/Scope.ts +56 -15
  19. package/components/awaitRestart.ts +84 -0
  20. package/components/componentLoader.ts +381 -32
  21. package/components/componentPreparationLock.ts +16 -5
  22. package/components/deploymentOperations.ts +4 -1
  23. package/components/deploymentRecorder.ts +9 -2
  24. package/components/mcp/adapters/harperHttp.ts +4 -0
  25. package/components/mcp/listChanged.ts +4 -0
  26. package/components/mcp/toolRegistry.ts +2 -0
  27. package/components/mcp/tools/operations.ts +9 -0
  28. package/components/mcp/tools/schemas/operationDescriptions.ts +2 -2
  29. package/components/operations.js +537 -113
  30. package/components/operationsValidation.js +98 -3
  31. package/components/packageComponent.ts +25 -1
  32. package/components/requestRestart.ts +11 -0
  33. package/components/status/ComponentStatusRegistry.ts +59 -0
  34. package/config/RootConfigWatcher.ts +240 -40
  35. package/config/configReadRetry.ts +62 -0
  36. package/config/configUtils.ts +357 -48
  37. package/config/harperConfigEnvVars.ts +170 -27
  38. package/config/parseConfigFile.ts +34 -0
  39. package/config/readConfigFileSync.ts +44 -0
  40. package/config/watcherArming.ts +59 -0
  41. package/config-root.schema.json +33 -0
  42. package/dataLayer/blobBackup.ts +160 -50
  43. package/dataLayer/delete.ts +6 -1
  44. package/dataLayer/harperBridge/ResourceBridge.ts +80 -10
  45. package/dataLayer/harperBridge/lmdbBridge/lmdbMethods/DeleteAuditLogsBeforeResults.js +3 -1
  46. package/dataLayer/hdbInfoController.ts +34 -1
  47. package/dataLayer/insert.ts +44 -1
  48. package/dataLayer/rocksdbBackup.ts +53 -10
  49. package/dataLayer/schema.ts +11 -1
  50. package/dataLayer/schemaDescribe.ts +8 -1
  51. package/dist/agent/mcpTools.js +1 -1
  52. package/dist/agent/mcpTools.js.map +1 -1
  53. package/dist/agent/session.d.ts +22 -0
  54. package/dist/agent/session.js +26 -15
  55. package/dist/agent/session.js.map +1 -1
  56. package/dist/bin/cliOperations.js +61 -9
  57. package/dist/bin/cliOperations.js.map +1 -1
  58. package/dist/bin/copyDb.d.ts +12 -1
  59. package/dist/bin/copyDb.js +248 -60
  60. package/dist/bin/copyDb.js.map +1 -1
  61. package/dist/bin/deploySetup.d.ts +2 -0
  62. package/dist/bin/deploySetup.js +11 -3
  63. package/dist/bin/deploySetup.js.map +1 -1
  64. package/dist/bin/harper.js +1 -1
  65. package/dist/bin/harper.js.map +1 -1
  66. package/dist/bin/help.js +4 -1
  67. package/dist/bin/help.js.map +1 -1
  68. package/dist/bin/lite.js +4 -1
  69. package/dist/bin/lite.js.map +1 -1
  70. package/dist/bin/restart.js +91 -6
  71. package/dist/bin/restart.js.map +1 -1
  72. package/dist/bin/run.js +4 -10
  73. package/dist/bin/run.js.map +1 -1
  74. package/dist/bin/upgrade.js +4 -3
  75. package/dist/bin/upgrade.js.map +1 -1
  76. package/dist/bin/workloadIdentity.d.ts +18 -0
  77. package/dist/bin/workloadIdentity.js +100 -0
  78. package/dist/bin/workloadIdentity.js.map +1 -0
  79. package/dist/components/Application.d.ts +235 -17
  80. package/dist/components/Application.js +3038 -238
  81. package/dist/components/Application.js.map +1 -1
  82. package/dist/components/ApplicationScope.d.ts +8 -0
  83. package/dist/components/ApplicationScope.js +7 -0
  84. package/dist/components/ApplicationScope.js.map +1 -1
  85. package/dist/components/EntryHandler.js +26 -10
  86. package/dist/components/EntryHandler.js.map +1 -1
  87. package/dist/components/OptionsWatcher.d.ts +4 -0
  88. package/dist/components/OptionsWatcher.js +440 -99
  89. package/dist/components/OptionsWatcher.js.map +1 -1
  90. package/dist/components/RuntimeModuleTracker.js +40 -6
  91. package/dist/components/RuntimeModuleTracker.js.map +1 -1
  92. package/dist/components/Scope.js +52 -13
  93. package/dist/components/Scope.js.map +1 -1
  94. package/dist/components/awaitRestart.d.ts +33 -0
  95. package/dist/components/awaitRestart.js +61 -0
  96. package/dist/components/awaitRestart.js.map +1 -0
  97. package/dist/components/componentLoader.d.ts +38 -1
  98. package/dist/components/componentLoader.js +313 -24
  99. package/dist/components/componentLoader.js.map +1 -1
  100. package/dist/components/componentPreparationLock.d.ts +5 -0
  101. package/dist/components/componentPreparationLock.js +14 -6
  102. package/dist/components/componentPreparationLock.js.map +1 -1
  103. package/dist/components/deploymentOperations.js +4 -1
  104. package/dist/components/deploymentOperations.js.map +1 -1
  105. package/dist/components/deploymentRecorder.d.ts +4 -2
  106. package/dist/components/deploymentRecorder.js +1 -0
  107. package/dist/components/deploymentRecorder.js.map +1 -1
  108. package/dist/components/mcp/adapters/harperHttp.js +4 -0
  109. package/dist/components/mcp/adapters/harperHttp.js.map +1 -1
  110. package/dist/components/mcp/listChanged.js +5 -0
  111. package/dist/components/mcp/listChanged.js.map +1 -1
  112. package/dist/components/mcp/toolRegistry.d.ts +1 -0
  113. package/dist/components/mcp/toolRegistry.js.map +1 -1
  114. package/dist/components/mcp/tools/operations.d.ts +5 -0
  115. package/dist/components/mcp/tools/operations.js +9 -0
  116. package/dist/components/mcp/tools/operations.js.map +1 -1
  117. package/dist/components/mcp/tools/schemas/operationDescriptions.js +2 -2
  118. package/dist/components/mcp/tools/schemas/operationDescriptions.js.map +1 -1
  119. package/dist/components/operations.d.ts +28 -0
  120. package/dist/components/operations.js +476 -113
  121. package/dist/components/operations.js.map +1 -1
  122. package/dist/components/operationsValidation.js +97 -3
  123. package/dist/components/operationsValidation.js.map +1 -1
  124. package/dist/components/packageComponent.js +24 -0
  125. package/dist/components/packageComponent.js.map +1 -1
  126. package/dist/components/requestRestart.d.ts +1 -0
  127. package/dist/components/requestRestart.js +7 -0
  128. package/dist/components/requestRestart.js.map +1 -1
  129. package/dist/components/status/ComponentStatusRegistry.d.ts +0 -4
  130. package/dist/components/status/ComponentStatusRegistry.js +63 -0
  131. package/dist/components/status/ComponentStatusRegistry.js.map +1 -1
  132. package/dist/config/RootConfigWatcher.d.ts +9 -1
  133. package/dist/config/RootConfigWatcher.js +228 -37
  134. package/dist/config/RootConfigWatcher.js.map +1 -1
  135. package/dist/config/configReadRetry.d.ts +8 -0
  136. package/dist/config/configReadRetry.js +62 -0
  137. package/dist/config/configReadRetry.js.map +1 -0
  138. package/dist/config/configUtils.d.ts +11 -2
  139. package/dist/config/configUtils.js +304 -47
  140. package/dist/config/configUtils.js.map +1 -1
  141. package/dist/config/harperConfigEnvVars.d.ts +16 -0
  142. package/dist/config/harperConfigEnvVars.js +162 -25
  143. package/dist/config/harperConfigEnvVars.js.map +1 -1
  144. package/dist/config/parseConfigFile.d.ts +4 -0
  145. package/dist/config/parseConfigFile.js +35 -0
  146. package/dist/config/parseConfigFile.js.map +1 -0
  147. package/dist/config/readConfigFileSync.d.ts +1 -0
  148. package/dist/config/readConfigFileSync.js +47 -0
  149. package/dist/config/readConfigFileSync.js.map +1 -0
  150. package/dist/config/watcherArming.d.ts +15 -0
  151. package/dist/config/watcherArming.js +59 -0
  152. package/dist/config/watcherArming.js.map +1 -0
  153. package/dist/dataLayer/blobBackup.d.ts +49 -20
  154. package/dist/dataLayer/blobBackup.js +139 -50
  155. package/dist/dataLayer/blobBackup.js.map +1 -1
  156. package/dist/dataLayer/delete.js +1 -1
  157. package/dist/dataLayer/delete.js.map +1 -1
  158. package/dist/dataLayer/harperBridge/ResourceBridge.d.ts +14 -1
  159. package/dist/dataLayer/harperBridge/ResourceBridge.js +72 -12
  160. package/dist/dataLayer/harperBridge/ResourceBridge.js.map +1 -1
  161. package/dist/dataLayer/harperBridge/lmdbBridge/lmdbMethods/DeleteAuditLogsBeforeResults.d.ts +3 -1
  162. package/dist/dataLayer/harperBridge/lmdbBridge/lmdbMethods/DeleteAuditLogsBeforeResults.js +3 -1
  163. package/dist/dataLayer/harperBridge/lmdbBridge/lmdbMethods/DeleteAuditLogsBeforeResults.js.map +1 -1
  164. package/dist/dataLayer/hdbInfoController.d.ts +10 -0
  165. package/dist/dataLayer/hdbInfoController.js +30 -1
  166. package/dist/dataLayer/hdbInfoController.js.map +1 -1
  167. package/dist/dataLayer/insert.d.ts +9 -1
  168. package/dist/dataLayer/insert.js +30 -0
  169. package/dist/dataLayer/insert.js.map +1 -1
  170. package/dist/dataLayer/rocksdbBackup.d.ts +2 -2
  171. package/dist/dataLayer/rocksdbBackup.js +45 -8
  172. package/dist/dataLayer/rocksdbBackup.js.map +1 -1
  173. package/dist/dataLayer/schema.js +8 -0
  174. package/dist/dataLayer/schema.js.map +1 -1
  175. package/dist/dataLayer/schemaDescribe.js +8 -1
  176. package/dist/dataLayer/schemaDescribe.js.map +1 -1
  177. package/dist/index.d.ts +2 -1
  178. package/dist/index.js +4 -1
  179. package/dist/index.js.map +1 -1
  180. package/dist/json/systemSchema.json +55 -0
  181. package/dist/resources/DatabaseTransaction.d.ts +77 -0
  182. package/dist/resources/DatabaseTransaction.js +586 -54
  183. package/dist/resources/DatabaseTransaction.js.map +1 -1
  184. package/dist/resources/LMDBTransaction.d.ts +2 -1
  185. package/dist/resources/LMDBTransaction.js +43 -6
  186. package/dist/resources/LMDBTransaction.js.map +1 -1
  187. package/dist/resources/PrimaryRocksDatabase.d.ts +1 -0
  188. package/dist/resources/PrimaryRocksDatabase.js +49 -5
  189. package/dist/resources/PrimaryRocksDatabase.js.map +1 -1
  190. package/dist/resources/RecordEncoder.d.ts +20 -0
  191. package/dist/resources/RecordEncoder.js +110 -9
  192. package/dist/resources/RecordEncoder.js.map +1 -1
  193. package/dist/resources/RequestTarget.d.ts +2 -0
  194. package/dist/resources/RequestTarget.js.map +1 -1
  195. package/dist/resources/Resource.js +117 -24
  196. package/dist/resources/Resource.js.map +1 -1
  197. package/dist/resources/ResourceInterface.d.ts +28 -1
  198. package/dist/resources/ResourceInterface.js.map +1 -1
  199. package/dist/resources/RocksIndexStore.d.ts +6 -1
  200. package/dist/resources/RocksIndexStore.js +24 -9
  201. package/dist/resources/RocksIndexStore.js.map +1 -1
  202. package/dist/resources/RocksTransactionLogStore.d.ts +24 -1
  203. package/dist/resources/RocksTransactionLogStore.js +160 -49
  204. package/dist/resources/RocksTransactionLogStore.js.map +1 -1
  205. package/dist/resources/Table.d.ts +165 -10
  206. package/dist/resources/Table.js +1805 -303
  207. package/dist/resources/Table.js.map +1 -1
  208. package/dist/resources/analytics/write.js +10 -3
  209. package/dist/resources/analytics/write.js.map +1 -1
  210. package/dist/resources/auditStore.d.ts +181 -2
  211. package/dist/resources/auditStore.js +640 -29
  212. package/dist/resources/auditStore.js.map +1 -1
  213. package/dist/resources/blob.d.ts +129 -9
  214. package/dist/resources/blob.js +992 -114
  215. package/dist/resources/blob.js.map +1 -1
  216. package/dist/resources/branchDatabase.d.ts +48 -0
  217. package/dist/resources/branchDatabase.js +892 -0
  218. package/dist/resources/branchDatabase.js.map +1 -0
  219. package/dist/resources/crdt.js +50 -12
  220. package/dist/resources/crdt.js.map +1 -1
  221. package/dist/resources/dataLoader.js +3 -4
  222. package/dist/resources/dataLoader.js.map +1 -1
  223. package/dist/resources/databases.d.ts +157 -14
  224. package/dist/resources/databases.js +1462 -325
  225. package/dist/resources/databases.js.map +1 -1
  226. package/dist/resources/defineTable.d.ts +10 -2
  227. package/dist/resources/defineTable.js +9 -1
  228. package/dist/resources/defineTable.js.map +1 -1
  229. package/dist/resources/derivedIndexRegistry.d.ts +5 -0
  230. package/dist/resources/derivedIndexRegistry.js +68 -0
  231. package/dist/resources/derivedIndexRegistry.js.map +1 -0
  232. package/dist/resources/derivedIndexRuntime.d.ts +215 -0
  233. package/dist/resources/derivedIndexRuntime.js +2027 -0
  234. package/dist/resources/derivedIndexRuntime.js.map +1 -0
  235. package/dist/resources/graphql.d.ts +1 -1
  236. package/dist/resources/graphql.js +52 -16
  237. package/dist/resources/graphql.js.map +1 -1
  238. package/dist/resources/indexes/HierarchicalNavigableSmallWorld.d.ts +103 -9
  239. package/dist/resources/indexes/HierarchicalNavigableSmallWorld.js +854 -41
  240. package/dist/resources/indexes/HierarchicalNavigableSmallWorld.js.map +1 -1
  241. package/dist/resources/indexes/hnswDerivedIndex.d.ts +67 -0
  242. package/dist/resources/indexes/hnswDerivedIndex.js +464 -0
  243. package/dist/resources/indexes/hnswDerivedIndex.js.map +1 -0
  244. package/dist/resources/indexes/hnswPlaneBinding.d.ts +65 -0
  245. package/dist/resources/indexes/hnswPlaneBinding.js +91 -0
  246. package/dist/resources/indexes/hnswPlaneBinding.js.map +1 -0
  247. package/dist/resources/longLivedTransactions.d.ts +71 -0
  248. package/dist/resources/longLivedTransactions.js +358 -0
  249. package/dist/resources/longLivedTransactions.js.map +1 -0
  250. package/dist/resources/models/backendRegistry.d.ts +26 -0
  251. package/dist/resources/models/backendRegistry.js +60 -2
  252. package/dist/resources/models/backendRegistry.js.map +1 -1
  253. package/dist/resources/models/bootstrap.d.ts +33 -1
  254. package/dist/resources/models/bootstrap.js +416 -31
  255. package/dist/resources/models/bootstrap.js.map +1 -1
  256. package/dist/resources/nodeIdMapping.d.ts +5 -0
  257. package/dist/resources/nodeIdMapping.js +49 -0
  258. package/dist/resources/nodeIdMapping.js.map +1 -1
  259. package/dist/resources/recordLock.d.ts +123 -0
  260. package/dist/resources/recordLock.js +315 -0
  261. package/dist/resources/recordLock.js.map +1 -0
  262. package/dist/resources/recordLockCoordinator.d.ts +557 -0
  263. package/dist/resources/recordLockCoordinator.js +2565 -0
  264. package/dist/resources/recordLockCoordinator.js.map +1 -0
  265. package/dist/resources/replayLogs.d.ts +14 -1
  266. package/dist/resources/replayLogs.js +157 -24
  267. package/dist/resources/replayLogs.js.map +1 -1
  268. package/dist/resources/replayLogsGuards.d.ts +94 -7
  269. package/dist/resources/replayLogsGuards.js +111 -7
  270. package/dist/resources/replayLogsGuards.js.map +1 -1
  271. package/dist/resources/replicatedApplyFailure.d.ts +16 -0
  272. package/dist/resources/replicatedApplyFailure.js +63 -0
  273. package/dist/resources/replicatedApplyFailure.js.map +1 -0
  274. package/dist/resources/scheduler/scheduler.js +3 -3
  275. package/dist/resources/scheduler/scheduler.js.map +1 -1
  276. package/dist/resources/search.d.ts +10 -4
  277. package/dist/resources/search.js +283 -37
  278. package/dist/resources/search.js.map +1 -1
  279. package/dist/resources/tracked.d.ts +5 -1
  280. package/dist/resources/tracked.js +74 -23
  281. package/dist/resources/tracked.js.map +1 -1
  282. package/dist/resources/transactionBroadcast.d.ts +1 -1
  283. package/dist/resources/transactionBroadcast.js +2 -2
  284. package/dist/resources/transactionBroadcast.js.map +1 -1
  285. package/dist/security/auth.js +61 -30
  286. package/dist/security/auth.js.map +1 -1
  287. package/dist/security/authn/oidc/claims.d.ts +22 -0
  288. package/dist/security/authn/oidc/claims.js +71 -0
  289. package/dist/security/authn/oidc/claims.js.map +1 -0
  290. package/dist/security/authn/oidc/identityToken.d.ts +27 -0
  291. package/dist/security/authn/oidc/identityToken.js +111 -0
  292. package/dist/security/authn/oidc/identityToken.js.map +1 -0
  293. package/dist/security/authn/oidc/jwks.d.ts +25 -0
  294. package/dist/security/authn/oidc/jwks.js +261 -0
  295. package/dist/security/authn/oidc/jwks.js.map +1 -0
  296. package/dist/security/authn/oidc/providers/generic.d.ts +13 -0
  297. package/dist/security/authn/oidc/providers/generic.js +34 -0
  298. package/dist/security/authn/oidc/providers/generic.js.map +1 -0
  299. package/dist/security/authn/oidc/providers/githubActions.d.ts +11 -0
  300. package/dist/security/authn/oidc/providers/githubActions.js +129 -0
  301. package/dist/security/authn/oidc/providers/githubActions.js.map +1 -0
  302. package/dist/security/authn/oidc/providers/index.d.ts +37 -0
  303. package/dist/security/authn/oidc/providers/index.js +24 -0
  304. package/dist/security/authn/oidc/providers/index.js.map +1 -0
  305. package/dist/security/authn/oidc/tokenExchange.d.ts +12 -0
  306. package/dist/security/authn/oidc/tokenExchange.js +306 -0
  307. package/dist/security/authn/oidc/tokenExchange.js.map +1 -0
  308. package/dist/security/authn/oidc/trustPolicyOperations.d.ts +49 -0
  309. package/dist/security/authn/oidc/trustPolicyOperations.js +358 -0
  310. package/dist/security/authn/oidc/trustPolicyOperations.js.map +1 -0
  311. package/dist/security/authn/oidc/types.d.ts +38 -0
  312. package/dist/security/authn/oidc/types.js +6 -0
  313. package/dist/security/authn/oidc/types.js.map +1 -0
  314. package/dist/security/certificateVerification/index.js +40 -11
  315. package/dist/security/certificateVerification/index.js.map +1 -1
  316. package/dist/security/certificateVerification/trustedIssuers.d.ts +24 -0
  317. package/dist/security/certificateVerification/trustedIssuers.js +79 -0
  318. package/dist/security/certificateVerification/trustedIssuers.js.map +1 -0
  319. package/dist/security/certificateVerification/types.d.ts +1 -0
  320. package/dist/security/credentialProvenance.d.ts +35 -0
  321. package/dist/security/credentialProvenance.js +51 -0
  322. package/dist/security/credentialProvenance.js.map +1 -0
  323. package/dist/security/credentialRejection.d.ts +4 -0
  324. package/dist/security/credentialRejection.js +24 -0
  325. package/dist/security/credentialRejection.js.map +1 -0
  326. package/dist/security/deferredAuthentication.d.ts +36 -0
  327. package/dist/security/deferredAuthentication.js +70 -0
  328. package/dist/security/deferredAuthentication.js.map +1 -0
  329. package/dist/security/impersonation.d.ts +21 -0
  330. package/dist/security/impersonation.js +108 -9
  331. package/dist/security/impersonation.js.map +1 -1
  332. package/dist/security/jsLoader.d.ts +6 -0
  333. package/dist/security/jsLoader.js +77 -15
  334. package/dist/security/jsLoader.js.map +1 -1
  335. package/dist/security/keys.js +301 -71
  336. package/dist/security/keys.js.map +1 -1
  337. package/dist/security/operationScope.d.ts +21 -0
  338. package/dist/security/operationScope.js +36 -0
  339. package/dist/security/operationScope.js.map +1 -0
  340. package/dist/security/permissionsTranslator.js +21 -0
  341. package/dist/security/permissionsTranslator.js.map +1 -1
  342. package/dist/security/tokenAuthentication.d.ts +19 -1
  343. package/dist/security/tokenAuthentication.js +191 -10
  344. package/dist/security/tokenAuthentication.js.map +1 -1
  345. package/dist/security/user.js +4 -3
  346. package/dist/security/user.js.map +1 -1
  347. package/dist/server/DurableSubscriptionsSession.d.ts +2 -2
  348. package/dist/server/DurableSubscriptionsSession.js +67 -10
  349. package/dist/server/DurableSubscriptionsSession.js.map +1 -1
  350. package/dist/server/REST.js +106 -2
  351. package/dist/server/REST.js.map +1 -1
  352. package/dist/server/graphqlQuerying.js +4 -0
  353. package/dist/server/graphqlQuerying.js.map +1 -1
  354. package/dist/server/http.d.ts +16 -1
  355. package/dist/server/http.js +122 -17
  356. package/dist/server/http.js.map +1 -1
  357. package/dist/server/itc/serverHandlers.js +8 -1
  358. package/dist/server/itc/serverHandlers.js.map +1 -1
  359. package/dist/server/jobs/jobProcess.js +6 -2
  360. package/dist/server/jobs/jobProcess.js.map +1 -1
  361. package/dist/server/jobs/jobs.js +4 -1
  362. package/dist/server/jobs/jobs.js.map +1 -1
  363. package/dist/server/liveSubscriptionAuth.d.ts +26 -4
  364. package/dist/server/liveSubscriptionAuth.js +105 -39
  365. package/dist/server/liveSubscriptionAuth.js.map +1 -1
  366. package/dist/server/loadRootComponents.js +49 -10
  367. package/dist/server/loadRootComponents.js.map +1 -1
  368. package/dist/server/mqtt.d.ts +2 -0
  369. package/dist/server/mqtt.js +165 -30
  370. package/dist/server/mqtt.js.map +1 -1
  371. package/dist/server/nodeName.d.ts +2 -0
  372. package/dist/server/nodeName.js +107 -23
  373. package/dist/server/nodeName.js.map +1 -1
  374. package/dist/server/serverHelpers/Headers.d.ts +27 -0
  375. package/dist/server/serverHelpers/Headers.js +145 -1
  376. package/dist/server/serverHelpers/Headers.js.map +1 -1
  377. package/dist/server/serverHelpers/NodeAdapterResponse.d.ts +48 -0
  378. package/dist/server/serverHelpers/NodeAdapterResponse.js +220 -0
  379. package/dist/server/serverHelpers/NodeAdapterResponse.js.map +1 -0
  380. package/dist/server/serverHelpers/Request.d.ts +5 -10
  381. package/dist/server/serverHelpers/Request.js +38 -136
  382. package/dist/server/serverHelpers/Request.js.map +1 -1
  383. package/dist/server/serverHelpers/contentTypes.d.ts +11 -0
  384. package/dist/server/serverHelpers/contentTypes.js +221 -40
  385. package/dist/server/serverHelpers/contentTypes.js.map +1 -1
  386. package/dist/server/serverHelpers/deployValidationState.d.ts +3 -0
  387. package/dist/server/serverHelpers/deployValidationState.js +9 -19
  388. package/dist/server/serverHelpers/deployValidationState.js.map +1 -1
  389. package/dist/server/serverHelpers/operationAuthorizationState.d.ts +12 -0
  390. package/dist/server/serverHelpers/operationAuthorizationState.js +24 -2
  391. package/dist/server/serverHelpers/operationAuthorizationState.js.map +1 -1
  392. package/dist/server/serverHelpers/registeredOperations.d.ts +5 -4
  393. package/dist/server/serverHelpers/registeredOperations.js +74 -21
  394. package/dist/server/serverHelpers/registeredOperations.js.map +1 -1
  395. package/dist/server/serverHelpers/requestSanitization.d.ts +11 -0
  396. package/dist/server/serverHelpers/requestSanitization.js +20 -0
  397. package/dist/server/serverHelpers/requestSanitization.js.map +1 -0
  398. package/dist/server/serverHelpers/serverHandlers.js +6 -3
  399. package/dist/server/serverHelpers/serverHandlers.js.map +1 -1
  400. package/dist/server/serverHelpers/serverUtilities.d.ts +18 -0
  401. package/dist/server/serverHelpers/serverUtilities.js +178 -29
  402. package/dist/server/serverHelpers/serverUtilities.js.map +1 -1
  403. package/dist/server/serverHelpers/sharedMessageEncoding.d.ts +67 -0
  404. package/dist/server/serverHelpers/sharedMessageEncoding.js +280 -0
  405. package/dist/server/serverHelpers/sharedMessageEncoding.js.map +1 -0
  406. package/dist/server/serverHelpers/uwsServer.js +19 -1
  407. package/dist/server/serverHelpers/uwsServer.js.map +1 -1
  408. package/dist/server/static.js +24 -28
  409. package/dist/server/static.js.map +1 -1
  410. package/dist/server/storageReclamation.d.ts +5 -0
  411. package/dist/server/storageReclamation.js +17 -1
  412. package/dist/server/storageReclamation.js.map +1 -1
  413. package/dist/server/threads/isolatedApplications.d.ts +47 -0
  414. package/dist/server/threads/isolatedApplications.js +171 -0
  415. package/dist/server/threads/isolatedApplications.js.map +1 -0
  416. package/dist/server/threads/itc.d.ts +7 -2
  417. package/dist/server/threads/itc.js +5 -1
  418. package/dist/server/threads/itc.js.map +1 -1
  419. package/dist/server/threads/logRotationTransport.d.ts +1 -0
  420. package/dist/server/threads/logRotationTransport.js +33 -0
  421. package/dist/server/threads/logRotationTransport.js.map +1 -0
  422. package/dist/server/threads/manageThreads.d.ts +83 -3
  423. package/dist/server/threads/manageThreads.js +769 -75
  424. package/dist/server/threads/manageThreads.js.map +1 -1
  425. package/dist/server/threads/socketRouter.d.ts +1 -0
  426. package/dist/server/threads/socketRouter.js +277 -31
  427. package/dist/server/threads/socketRouter.js.map +1 -1
  428. package/dist/server/threads/threadHeapMemory.d.ts +2 -0
  429. package/dist/server/threads/threadHeapMemory.js +31 -0
  430. package/dist/server/threads/threadHeapMemory.js.map +1 -0
  431. package/dist/server/threads/threadServer.js +76 -22
  432. package/dist/server/threads/threadServer.js.map +1 -1
  433. package/dist/sqlEngine/config.d.ts +1 -3
  434. package/dist/sqlEngine/config.js +19 -16
  435. package/dist/sqlEngine/config.js.map +1 -1
  436. package/dist/sqlTranslator/index.d.ts +1 -1
  437. package/dist/sqlTranslator/index.js +30 -7
  438. package/dist/sqlTranslator/index.js.map +1 -1
  439. package/dist/upgrade/directives/5-3-0.d.ts +7 -0
  440. package/dist/upgrade/directives/5-3-0.js +148 -0
  441. package/dist/upgrade/directives/5-3-0.js.map +1 -0
  442. package/dist/upgrade/directives/directivesController.js +2 -1
  443. package/dist/upgrade/directives/directivesController.js.map +1 -1
  444. package/dist/utility/OperationFunctionCaller.js +2 -1
  445. package/dist/utility/OperationFunctionCaller.js.map +1 -1
  446. package/dist/utility/common_utils.d.ts +16 -0
  447. package/dist/utility/common_utils.js +32 -6
  448. package/dist/utility/common_utils.js.map +1 -1
  449. package/dist/utility/componentNames.d.ts +8 -0
  450. package/dist/utility/componentNames.js +12 -1
  451. package/dist/utility/componentNames.js.map +1 -1
  452. package/dist/utility/environment/environmentManager.js +3 -6
  453. package/dist/utility/environment/environmentManager.js.map +1 -1
  454. package/dist/utility/environment/systemInformation.d.ts +1 -0
  455. package/dist/utility/environment/systemInformation.js +1 -0
  456. package/dist/utility/environment/systemInformation.js.map +1 -1
  457. package/dist/utility/errors/commonErrors.d.ts +2 -0
  458. package/dist/utility/errors/commonErrors.js +2 -0
  459. package/dist/utility/errors/commonErrors.js.map +1 -1
  460. package/dist/utility/errors/hdbError.d.ts +33 -0
  461. package/dist/utility/errors/hdbError.js +58 -1
  462. package/dist/utility/errors/hdbError.js.map +1 -1
  463. package/dist/utility/globalSchema.d.ts +18 -0
  464. package/dist/utility/hdbTerms.d.ts +18 -0
  465. package/dist/utility/hdbTerms.js +20 -2
  466. package/dist/utility/hdbTerms.js.map +1 -1
  467. package/dist/utility/logging/harper_logger.d.ts +2 -0
  468. package/dist/utility/logging/harper_logger.js +286 -30
  469. package/dist/utility/logging/harper_logger.js.map +1 -1
  470. package/dist/utility/logging/logGenerationCoordinator.d.ts +35 -0
  471. package/dist/utility/logging/logGenerationCoordinator.js +184 -0
  472. package/dist/utility/logging/logGenerationCoordinator.js.map +1 -0
  473. package/dist/utility/logging/logRotation.d.ts +46 -0
  474. package/dist/utility/logging/logRotation.js +365 -0
  475. package/dist/utility/logging/logRotation.js.map +1 -0
  476. package/dist/utility/logging/logRotator.d.ts +1 -1
  477. package/dist/utility/logging/logRotator.js +192 -85
  478. package/dist/utility/logging/logRotator.js.map +1 -1
  479. package/dist/utility/nodeIdentity.d.ts +9 -0
  480. package/dist/utility/nodeIdentity.js +58 -0
  481. package/dist/utility/nodeIdentity.js.map +1 -0
  482. package/dist/utility/npmUtilities.js +11 -7
  483. package/dist/utility/npmUtilities.js.map +1 -1
  484. package/dist/utility/operationPermissions.d.ts +3 -1
  485. package/dist/utility/operationPermissions.js +16 -1
  486. package/dist/utility/operationPermissions.js.map +1 -1
  487. package/dist/utility/operation_authorization.d.ts +10 -7
  488. package/dist/utility/operation_authorization.js +212 -41
  489. package/dist/utility/operation_authorization.js.map +1 -1
  490. package/dist/utility/watchPath.d.ts +29 -0
  491. package/dist/utility/watchPath.js +68 -0
  492. package/dist/utility/watchPath.js.map +1 -0
  493. package/dist/utility/watcherFallback.d.ts +41 -0
  494. package/dist/utility/watcherFallback.js +153 -0
  495. package/dist/utility/watcherFallback.js.map +1 -1
  496. package/dist/validation/configValidator.d.ts +12 -0
  497. package/dist/validation/configValidator.js +205 -75
  498. package/dist/validation/configValidator.js.map +1 -1
  499. package/dist/validation/installValidator.js +12 -0
  500. package/dist/validation/installValidator.js.map +1 -1
  501. package/dist/validation/validationWrapper.d.ts +11 -0
  502. package/dist/validation/validationWrapper.js +16 -3
  503. package/dist/validation/validationWrapper.js.map +1 -1
  504. package/index.ts +8 -0
  505. package/json/systemSchema.json +55 -0
  506. package/npm-shrinkwrap.json +286 -194
  507. package/package.json +15 -7
  508. package/resources/DESIGN.md +219 -52
  509. package/resources/DatabaseTransaction.ts +657 -52
  510. package/resources/LMDBTransaction.ts +46 -6
  511. package/resources/PrimaryRocksDatabase.ts +46 -7
  512. package/resources/RecordEncoder.ts +124 -9
  513. package/resources/RequestTarget.ts +2 -0
  514. package/resources/Resource.ts +114 -22
  515. package/resources/ResourceInterface.ts +31 -0
  516. package/resources/RocksIndexStore.ts +30 -9
  517. package/resources/RocksTransactionLogStore.ts +188 -51
  518. package/resources/Table.ts +1960 -301
  519. package/resources/analytics/write.ts +10 -3
  520. package/resources/auditStore.ts +647 -32
  521. package/resources/blob.ts +1029 -110
  522. package/resources/branchDatabase.ts +941 -0
  523. package/resources/crdt.ts +76 -12
  524. package/resources/dataLoader.ts +3 -4
  525. package/resources/databases.ts +1584 -320
  526. package/resources/defineTable.ts +18 -2
  527. package/resources/derivedIndexRegistry.ts +56 -0
  528. package/resources/derivedIndexRuntime.ts +2292 -0
  529. package/resources/graphql.ts +72 -17
  530. package/resources/indexes/HierarchicalNavigableSmallWorld.ts +911 -48
  531. package/resources/indexes/hnswDerivedIndex.ts +531 -0
  532. package/resources/indexes/hnswPlaneBinding.ts +174 -0
  533. package/resources/longLivedTransactions.ts +360 -0
  534. package/resources/models/backendRegistry.ts +84 -2
  535. package/resources/models/bootstrap.ts +473 -28
  536. package/resources/nodeIdMapping.ts +50 -0
  537. package/resources/recordLock.ts +419 -0
  538. package/resources/recordLockCoordinator.ts +3043 -0
  539. package/resources/replayLogs.ts +152 -26
  540. package/resources/replayLogsGuards.ts +171 -8
  541. package/resources/replicatedApplyFailure.ts +77 -0
  542. package/resources/scheduler/scheduler.ts +4 -4
  543. package/resources/search.ts +288 -46
  544. package/resources/tracked.ts +73 -22
  545. package/resources/transactionBroadcast.ts +3 -3
  546. package/security/auth.ts +68 -29
  547. package/security/authn/oidc/claims.ts +72 -0
  548. package/security/authn/oidc/identityToken.ts +129 -0
  549. package/security/authn/oidc/jwks.ts +260 -0
  550. package/security/authn/oidc/providers/generic.ts +40 -0
  551. package/security/authn/oidc/providers/githubActions.ts +137 -0
  552. package/security/authn/oidc/providers/index.ts +52 -0
  553. package/security/authn/oidc/tokenExchange.ts +300 -0
  554. package/security/authn/oidc/trustPolicyOperations.ts +343 -0
  555. package/security/authn/oidc/types.ts +41 -0
  556. package/security/certificateVerification/index.ts +54 -13
  557. package/security/certificateVerification/trustedIssuers.ts +76 -0
  558. package/security/certificateVerification/types.ts +1 -0
  559. package/security/credentialProvenance.ts +47 -0
  560. package/security/credentialRejection.ts +22 -0
  561. package/security/deferredAuthentication.ts +71 -0
  562. package/security/impersonation.ts +117 -12
  563. package/security/jsLoader.ts +83 -18
  564. package/security/keys.ts +298 -72
  565. package/security/operationScope.ts +33 -0
  566. package/security/permissionsTranslator.js +23 -0
  567. package/security/tokenAuthentication.ts +233 -12
  568. package/security/user.ts +4 -3
  569. package/server/DESIGN.md +194 -16
  570. package/server/DurableSubscriptionsSession.ts +71 -11
  571. package/server/REST.ts +115 -4
  572. package/server/graphqlQuerying.ts +4 -0
  573. package/server/http.ts +133 -20
  574. package/server/itc/serverHandlers.js +8 -1
  575. package/server/jobs/jobProcess.ts +8 -2
  576. package/server/jobs/jobs.ts +4 -1
  577. package/server/liveSubscriptionAuth.ts +129 -46
  578. package/server/loadRootComponents.js +50 -8
  579. package/server/mqtt.ts +179 -38
  580. package/server/nodeName.ts +103 -21
  581. package/server/serverHelpers/Headers.ts +135 -0
  582. package/server/serverHelpers/NodeAdapterResponse.ts +221 -0
  583. package/server/serverHelpers/Request.ts +33 -131
  584. package/server/serverHelpers/contentTypes.ts +217 -36
  585. package/server/serverHelpers/deployValidationState.ts +24 -13
  586. package/server/serverHelpers/operationAuthorizationState.ts +34 -3
  587. package/server/serverHelpers/registeredOperations.ts +79 -22
  588. package/server/serverHelpers/requestSanitization.ts +15 -0
  589. package/server/serverHelpers/serverHandlers.js +6 -3
  590. package/server/serverHelpers/serverUtilities.ts +232 -40
  591. package/server/serverHelpers/sharedMessageEncoding.ts +307 -0
  592. package/server/serverHelpers/uwsServer.ts +17 -2
  593. package/server/static.ts +23 -29
  594. package/server/storageReclamation.ts +15 -2
  595. package/server/threads/isolatedApplications.ts +157 -0
  596. package/server/threads/itc.js +11 -1
  597. package/server/threads/logRotationTransport.ts +40 -0
  598. package/server/threads/manageThreads.js +771 -66
  599. package/server/threads/socketRouter.ts +291 -30
  600. package/server/threads/threadHeapMemory.ts +26 -0
  601. package/server/threads/threadServer.js +73 -22
  602. package/sqlTranslator/index.ts +31 -8
  603. package/studio/web/assets/{Chat-4RrB5134.js → Chat-D3j-1yY1.js} +1 -1
  604. package/studio/web/assets/{FloatingChat-omlNMDcJ.js → FloatingChat-BxJGYcfB.js} +3 -3
  605. package/studio/web/assets/{apiToken-Bke3wvfZ.js → apiToken-CT55oWOe.js} +1 -1
  606. package/studio/web/assets/{applications-DvFDYJgK.js → applications-D9Ct9_vm.js} +1 -1
  607. package/studio/web/assets/{cssMode-C1vRa7zh.js → cssMode-DV8H7VwA.js} +1 -1
  608. package/studio/web/assets/{editor-3XRWEDWX.js → editor-uatc0unt.js} +1 -1
  609. package/studio/web/assets/{html-CTY5tdMr.js → html-Bm6D6paN.js} +1 -1
  610. package/studio/web/assets/{htmlMode-CG1vSD9t.js → htmlMode-CEn7tpLG.js} +1 -1
  611. package/studio/web/assets/{index-Dfpeofdu.js → index-BIXW6Pu4.js} +5 -5
  612. package/studio/web/assets/{index.lazy-B1VOIv-t.js → index.lazy-UI7L-Vrk.js} +1 -1
  613. package/studio/web/assets/{javascript-CJeJzGnI.js → javascript-CJ0G3AFZ.js} +1 -1
  614. package/studio/web/assets/{jsonMode-CYPBwM82.js → jsonMode-DQADAYEa.js} +1 -1
  615. package/studio/web/assets/{languageServices-IKH4GUHl.js → languageServices-CAQJXWcI.js} +1 -1
  616. package/studio/web/assets/{lspLanguageFeatures-BC8_gxKG.js → lspLanguageFeatures-CCQ8P5sY.js} +1 -1
  617. package/studio/web/assets/{notifications-BfKBpYcq.js → notifications-BbxTU6Aw.js} +1 -1
  618. package/studio/web/assets/{notifications-DFbArTfC.js → notifications-Cvb3P1lB.js} +1 -1
  619. package/studio/web/assets/{profile-Q4-T6c-S.js → profile-Yyb7gsvL.js} +1 -1
  620. package/studio/web/assets/{regions-CtkV0xje.js → regions-OgjGHlU5.js} +1 -1
  621. package/studio/web/assets/{register-1ZZuMsiA.js → register-6qwNEOY3.js} +2 -2
  622. package/studio/web/assets/{setComponentFile-ZWLcWv5X.js → setComponentFile-BilDMtgB.js} +1 -1
  623. package/studio/web/assets/{setup-DWprJyJy.js → setup-J6qJ7OIU.js} +2 -2
  624. package/studio/web/assets/{status--aNm8isn.js → status-0RWGcfyD.js} +1 -1
  625. package/studio/web/assets/{toggleHighContrast-Cq_lt3XD.js → toggleHighContrast-BIn-vErT.js} +1 -1
  626. package/studio/web/assets/{tsMode-pjgytARx.js → tsMode-DgUXku4d.js} +1 -1
  627. package/studio/web/assets/{typescript-Cdg0mqUh.js → typescript-C9orXcsM.js} +1 -1
  628. package/studio/web/assets/{useEntityRestURL-RZhaY8Rn.js → useEntityRestURL-BEoXXbUB.js} +1 -1
  629. package/studio/web/assets/{workers-BgoXIqQe.js → workers-JVzSDmgx.js} +1 -1
  630. package/studio/web/assets/{xml-BwSeDMiP.js → xml-Cq-S8S4X.js} +1 -1
  631. package/studio/web/assets/{yaml-DotCUG5l.js → yaml-sfoRdh1M.js} +1 -1
  632. package/studio/web/index.html +1 -1
  633. package/upgrade/directives/5-3-0.ts +132 -0
  634. package/upgrade/directives/directivesController.ts +2 -1
  635. package/utility/OperationFunctionCaller.ts +2 -1
  636. package/utility/common_utils.ts +30 -5
  637. package/utility/componentNames.ts +12 -0
  638. package/utility/environment/environmentManager.ts +3 -7
  639. package/utility/environment/systemInformation.ts +7 -0
  640. package/utility/errors/commonErrors.ts +4 -0
  641. package/utility/errors/hdbError.ts +57 -0
  642. package/utility/hdbTerms.ts +19 -0
  643. package/utility/logging/harper_logger.ts +278 -26
  644. package/utility/logging/logGenerationCoordinator.ts +196 -0
  645. package/utility/logging/logRotation.ts +367 -0
  646. package/utility/logging/logRotator.ts +213 -81
  647. package/utility/nodeIdentity.ts +45 -0
  648. package/utility/npmUtilities.ts +12 -8
  649. package/utility/operationPermissions.ts +18 -1
  650. package/utility/operation_authorization.ts +231 -42
  651. package/utility/watchPath.ts +63 -0
  652. package/utility/watcherFallback.ts +148 -0
  653. package/validation/configValidator.ts +215 -75
  654. package/validation/installValidator.ts +15 -0
  655. package/validation/validationWrapper.ts +18 -4
@@ -1,11 +1,15 @@
1
1
  import {
2
2
  DatabaseTransaction,
3
+ closeWriteInstance,
4
+ validateWrite,
5
+ shouldSpareCommitPhase,
3
6
  transactionOpenTooLongError,
4
7
  type CommitOptions,
5
8
  type TransactionWrite,
6
9
  type CommitResolution,
7
10
  } from './DatabaseTransaction';
8
11
  import { cleanupUnusedBlobs, collectRetainedFileIds } from './blob.ts';
12
+ import { ServerError } from '../utility/errors/hdbError.ts';
9
13
  import { getNextMonotonicTime } from '../utility/lmdb/commonUtility.ts';
10
14
  import * as harperLogger from '../utility/logging/harper_logger.ts';
11
15
  import type { Context } from './ResourceInterface.ts';
@@ -35,10 +39,14 @@ export class LMDBTransaction extends DatabaseTransaction {
35
39
  declare overloadChecked: boolean;
36
40
  open = TRANSACTION_STATE.OPEN;
37
41
 
42
+ renewIdleTimeout(): void {
43
+ this.timeout = Math.max(txnExpiration, this.timeoutBudget ?? 0);
44
+ }
45
+
38
46
  getReadTxn(): any {
39
47
  // used optimistically
40
48
  this.readTxnRefCount = (this.readTxnRefCount || 0) + 1;
41
- this.timeout = txnExpiration; // reset the timeout
49
+ this.renewIdleTimeout();
42
50
  if (this.stale) this.stale = false;
43
51
  if (this.readTxn) {
44
52
  if ((this.readTxn as any).openTimer) (this.readTxn as any).openTimer = 0;
@@ -111,12 +119,14 @@ export class LMDBTransaction extends DatabaseTransaction {
111
119
 
112
120
  this.linkWrite(operation);
113
121
  this.writes.push(operation); // standard path, add to current transaction
122
+ (this.ownedWrites ??= new WeakSet()).add(operation);
114
123
  operation.stagedIn = this;
115
124
  }
116
125
 
117
126
  removeWrite(operation: TransactionWrite) {
118
127
  const index = this.writes.indexOf(operation);
119
128
  if (index > -1) this.writes[index] = null;
129
+ this.ownedWrites?.delete(operation);
120
130
  }
121
131
 
122
132
  /**
@@ -138,7 +148,11 @@ export class LMDBTransaction extends DatabaseTransaction {
138
148
  this.validated = this.writes.length;
139
149
  for (let i = start; i < this.validated; i++) {
140
150
  const write = this.writes[i];
141
- write?.validate?.(this.timestamp, this);
151
+ try {
152
+ if (write) validateWrite(write, this.timestamp, this);
153
+ } finally {
154
+ closeWriteInstance(write);
155
+ }
142
156
  }
143
157
  let hasBefore;
144
158
  for (let i = start; i < this.validated; i++) {
@@ -153,6 +167,9 @@ export class LMDBTransaction extends DatabaseTransaction {
153
167
  // source writes will finish (with right to refuse/abort) before proceeeding to less
154
168
  // canonical sources.
155
169
  if (hasBefore) {
170
+ // see `committing` in DatabaseTransaction (issue #2062)
171
+ this.setCommitPhase(true);
172
+ const stagedWrites = this.writes.length;
156
173
  return (async () => {
157
174
  try {
158
175
  for (let phase = 0; phase < 2; phase++) {
@@ -172,9 +189,15 @@ export class LMDBTransaction extends DatabaseTransaction {
172
189
  if (completion) await (completion.push ? Promise.all(completion) : completion);
173
190
  }
174
191
  } catch (error) {
192
+ this.setCommitPhase(false);
175
193
  this.abort();
176
194
  throw error;
177
195
  }
196
+ this.setCommitPhase(false);
197
+ // aborted underneath us while parked above — see DatabaseTransaction's twin guard
198
+ if (this.timedOut) throw transactionOpenTooLongError();
199
+ if (stagedWrites > 0 && this.writes.length === 0 && this.open === TRANSACTION_STATE.CLOSED)
200
+ throw new ServerError('Transaction was aborted while its commit was waiting on pre-commit work', 500);
178
201
  return this.commit(options);
179
202
  })();
180
203
  }
@@ -183,6 +206,7 @@ export class LMDBTransaction extends DatabaseTransaction {
183
206
  throw error;
184
207
  }
185
208
  }
209
+ if (!retries && this.writes.some((write) => write)) this.renewChainForNativeCommit();
186
210
  // release the read snapshot so we don't keep it open longer than necessary
187
211
  if (!retries) this.doneReadTxn();
188
212
  this.open = options?.doneWriting ? TRANSACTION_STATE.LINGERING : TRANSACTION_STATE.OPEN;
@@ -192,7 +216,12 @@ export class LMDBTransaction extends DatabaseTransaction {
192
216
  let writeIndex = 0;
193
217
  this.writes = this.writes.filter((write) => write); // filter out removed entries
194
218
  const doWrite = (write) => {
195
- const completion = write.commit(txnTime, write.entry, retries);
219
+ let completion;
220
+ try {
221
+ completion = write.commit(txnTime, write.entry, retries);
222
+ } finally {
223
+ closeWriteInstance(write);
224
+ }
196
225
  if (typeof completion?.then === 'function') {
197
226
  // the aggregating Promise.all is attached a turn or more later (after the conditional batch
198
227
  // or the exclusive transaction resolves), so handle rejection here to keep the gap from
@@ -372,10 +401,21 @@ let timer;
372
401
 
373
402
  function startMonitoringTxns() {
374
403
  timer = setInterval(function () {
404
+ const checkedCommitPhaseChains = new Set<DatabaseTransaction>();
375
405
  for (const txn of trackedTxns) {
406
+ const commitChainHead = txn.commitChainHead ?? txn;
376
407
  if (txn.timeout <= 0) {
377
408
  const url = (txn.getContext() as any)?.url;
378
- if (txn.hasPendingWrites() && !txn.sourceApply && !txn.isReplay) {
409
+ if (shouldSpareCommitPhase(txn, checkedCommitPhaseChains)) {
410
+ // see DatabaseTransaction's monitor (issue #2062)
411
+ harperLogger.warn?.(
412
+ `Transaction has been in its commit phase past the open-transaction limit, waiting on pre-commit work (e.g. a large blob write); letting it complete, from table: ${
413
+ (txn.db as any)?.name + (url ? ' path: ' + url : '')
414
+ }`,
415
+ ...(txn.startedFrom ? [`was started from ${txn.startedFrom.resourceName}.${txn.startedFrom.method}`] : [])
416
+ );
417
+ txn.timeout = Math.max(txnExpiration, txn.timeoutBudget ?? 0);
418
+ } else if (txn.hasPendingWrites() && !txn.sourceApply && !txn.isReplay) {
379
419
  // Abort and surface an error rather than force-committing a partial write set: silently
380
420
  // committing on the application's behalf breaks atomicity and can leave orphaned
381
421
  // secondary-index entries that only a full index rebuild repairs (issue #1407). The app
@@ -390,7 +430,7 @@ function startMonitoringTxns() {
390
430
  }`
391
431
  );
392
432
  try {
393
- txn.abortDueToTimeout();
433
+ commitChainHead.abortDueToTimeout();
394
434
  } catch (error) {
395
435
  harperLogger.debug?.(`Error aborting timed out transaction: ${error.message}`);
396
436
  }
@@ -408,7 +448,7 @@ function startMonitoringTxns() {
408
448
  } catch (error) {
409
449
  harperLogger.debug?.(`Error committing timed out transaction: ${error.message}`);
410
450
  }
411
- txn.timeout = txnExpiration;
451
+ txn.timeout = Math.max(txnExpiration, txn.timeoutBudget ?? 0);
412
452
  }
413
453
  } else {
414
454
  txn.timeout -= txnExpiration;
@@ -1,10 +1,10 @@
1
1
  import { trackReadRange } from './DatabaseTransaction.ts';
2
- import { RocksDatabase, type RocksDatabaseOptions, constants, type Store } from '@harperfast/rocksdb-js';
2
+ import { RocksDatabase, type RocksDatabaseOptions, constants, type Store, Transaction } from '@harperfast/rocksdb-js';
3
3
 
4
4
  const FRESH_VERSION_FLAG = constants.FRESH_VERSION_FLAG;
5
5
  import { WeakLRUCache } from 'weak-lru-cache';
6
6
  import { when } from '../utility/when.ts';
7
- import { assignStoredFields, entryMap, METADATA, type Entry } from './RecordEncoder.ts';
7
+ import { assignStoredFields, entryMap, METADATA, VERSION_REUSED, type Entry } from './RecordEncoder.ts';
8
8
 
9
9
  /**
10
10
  * RocksDatabase subclass that owns all primary-store behaviour for Harper tables:
@@ -42,6 +42,33 @@ export class PrimaryRocksDatabase extends RocksDatabase {
42
42
  }
43
43
  }
44
44
 
45
+ // Match LMDB's remove(key, version) contract without splitting the version check from the delete.
46
+ async removeIfVersion(id: any, version: number): Promise<boolean> {
47
+ let retried = false;
48
+ while (true) {
49
+ let transaction: Transaction | undefined;
50
+ try {
51
+ transaction = new Transaction(this.store);
52
+ const entry = this.#processEntry(super.getSync(id, { transaction }), id);
53
+ if (!entry || entry.version !== version) {
54
+ transaction.abort();
55
+ return false;
56
+ }
57
+ this.#cache?.delete(id);
58
+ super.removeSync(id, { transaction });
59
+ await transaction.commit();
60
+ return true;
61
+ } catch (error: any) {
62
+ try {
63
+ transaction?.abort();
64
+ } catch {}
65
+ // rocksdb-js reports a writer that committed between our read and our commit as ERR_BUSY
66
+ if (retried || error?.code !== 'ERR_BUSY') throw error;
67
+ retried = true;
68
+ }
69
+ }
70
+ }
71
+
45
72
  /**
46
73
  * Initialises encoder/decoder state. Must be called once after open() with the root
47
74
  * RocksDatabase. Equivalent to the RocksDB branch of handleLocalTimeForGets, but as
@@ -95,6 +122,13 @@ export class PrimaryRocksDatabase extends RocksDatabase {
95
122
  */
96
123
  getEntry(id: any, options?: any): any {
97
124
  this.readCount++;
125
+ // Commit-path base reads must reflect the caller's transaction snapshot; the cache vouch
126
+ // answers "latest committed" — the wrong question there, and wrong outright for a version a
127
+ // resequenced write reused — so read the store directly, touching neither cache nor VT.
128
+ if (options?.uncachedRead) {
129
+ if (options.async) return when(super.get(id, options), (result) => this.#processEntry(result, id));
130
+ return this.#processEntry(super.getSync(id, options), id);
131
+ }
98
132
  const cache = this.#cache;
99
133
  // The cache stores the record *value* (weakly, via setValue) rather than
100
134
  // the Entry: a WeakRef-wrapped value lets the LRFU expirer release it once
@@ -132,11 +166,16 @@ export class PrimaryRocksDatabase extends RocksDatabase {
132
166
  if (cache && cachedValue !== undefined) cache.delete(id);
133
167
  return undefined;
134
168
  }
135
- // Only object values can be weakly cached and mapped back to their Entry;
136
- // primitive/empty values fall through uncached (no fast path, still correct).
137
- if (entry.version != null && cache && entry.value != null && typeof entry.value === 'object') {
138
- entryMap.set(entry.value, entry);
139
- cache.setValue(id, entry.value, (entry.size ?? 0) >> 10);
169
+ if (entry.version != null && cache) {
170
+ // its version no longer identifies its value, so drop any copy already held
171
+ if (entry.metadataFlags & VERSION_REUSED) {
172
+ if (cachedValue !== undefined) cache.delete(id);
173
+ } else if (entry.value != null && typeof entry.value === 'object') {
174
+ // Only object values can be weakly cached and mapped back to their Entry;
175
+ // primitive/empty values fall through uncached (no fast path, still correct).
176
+ entryMap.set(entry.value, entry);
177
+ cache.setValue(id, entry.value, (entry.size ?? 0) >> 10);
178
+ }
140
179
  }
141
180
  return entry;
142
181
  });
@@ -30,7 +30,7 @@ import {
30
30
  } from './blob.ts';
31
31
  import { getThisNodeId } from './nodeIdMapping.ts';
32
32
  import { recordAction } from './analytics/write.ts';
33
- import { RocksDatabase } from '@harperfast/rocksdb-js';
33
+ import { constants, RocksDatabase } from '@harperfast/rocksdb-js';
34
34
  import { when } from '../utility/when.ts';
35
35
  import { CONFIG_PARAMS } from '../utility/hdbTerms.ts';
36
36
  import * as envMngr from '../utility/environment/environmentManager.js';
@@ -42,6 +42,33 @@ const StructonEncoder = createStructon(Encoder) as typeof Encoder;
42
42
  // monitoring; the store name is passed as the metric path.
43
43
  const MISSING_STRUCTURE_METRIC = 'decode-missing-structure';
44
44
 
45
+ // Reaching this bound is not an error: novel shapes fall back to plain msgpackr encoding, which
46
+ // round-trips correctly but gives up random-access field reads.
47
+ export const DEFAULT_MAX_TYPED_STRUCTURES = 256;
48
+
49
+ export interface StructureCounts {
50
+ typed: number;
51
+ classic: number;
52
+ typedLimit: number;
53
+ typedEnabled: boolean;
54
+ }
55
+
56
+ // Mirrors structon's onLoadedStructures over the four durable payload shapes it accepts. The
57
+ // bare-array and cbor-x forms carry named structures only; structon keeps the encoder's own typed
58
+ // dictionary for them, and they are written by the capped fallback path (msgpackr's base save
59
+ // overwriting the combined payload, before structon's best-effort re-save). That window is by
60
+ // definition a saturated store, so reporting zero there would show an empty dictionary at the one
61
+ // moment the count matters most.
62
+ function countDurableStructures(sharedData: any, localTyped: number): { typed: number; classic: number } {
63
+ if (!sharedData) return { typed: 0, classic: 0 };
64
+ if (sharedData instanceof Map)
65
+ return { typed: sharedData.get('typed')?.length ?? 0, classic: sharedData.get('named')?.length ?? 0 };
66
+ if (Array.isArray(sharedData)) return { typed: localTyped, classic: sharedData.length };
67
+ if (!sharedData.named && !sharedData.typed && Array.isArray(sharedData.structures))
68
+ return { typed: localTyped, classic: sharedData.structures.length };
69
+ return { typed: sharedData.typed?.length ?? 0, classic: sharedData.named?.length ?? 0 };
70
+ }
71
+
45
72
  // Terminal error messages msgpackr/structon throw when a record references a shared structure that
46
73
  // is not in this node's structures buffer. Both the typed (random-access) path (structon's
47
74
  // readStruct) and the classic path (msgpackr's createSecondByteReader) reload the structures from
@@ -77,6 +104,8 @@ export type Entry = {
77
104
  export const TIMESTAMP_PLACEHOLDER = new Uint8Array([1, 1, 1, 1, 4, 0x40, 0, 0]);
78
105
  // the first byte here indicates that we use the last timestamp
79
106
  export const LAST_TIMESTAMP_PLACEHOLDER = new Uint8Array([1, 1, 1, 1, 1, 0, 0, 0]);
107
+ // Never write this into an audit entry value: the substitution resolves to 2.0 when no previous time
108
+ // was recorded, and the audit format reads a leading byte other than 0x42 as "no field here". See harper#2247.
80
109
  export const PREVIOUS_TIMESTAMP_PLACEHOLDER = new Uint8Array([1, 1, 1, 1, 3, 0x40, 0, 0]);
81
110
  export const NEW_TIMESTAMP_PLACEHOLDER = new Uint8Array([1, 1, 1, 1, 0, 0x40, 0, 0]);
82
111
  export const LOCAL_TIMESTAMP = Symbol('local-timestamp');
@@ -95,6 +124,26 @@ export const HAS_NODE_ID = 64;
95
124
  export const PENDING_LOCAL_TIME = 1;
96
125
  export const HAS_STRUCTURE_UPDATE = 0x100;
97
126
  export const HAS_ADDITIONAL_AUDIT_REFS = 0x80;
127
+ // A resequenced write keeps the (newer) version it merged onto, so one version identifies two
128
+ // different stored values and version equality proves nothing. The metadata word this is set in is
129
+ // the same header word the VerificationTable reads at value offset 8 — ACTION_32_BIT is the tag
130
+ // byte its predicate requires — so the bit is what stops the native layer vouching for the version.
131
+ export const VERSION_REUSED = constants.VERSION_NOT_UNIQUE_FLAG;
132
+ // The bit is persisted in every resequenced record, so a rocksdb-js that moved it down onto one of
133
+ // the flags above (or up into the tag byte) would silently change what records already on disk
134
+ // mean: it must stay a single bit strictly between them.
135
+ if (
136
+ typeof VERSION_REUSED !== 'number' ||
137
+ (VERSION_REUSED & (VERSION_REUSED - 1)) !== 0 ||
138
+ VERSION_REUSED < 0x10000 ||
139
+ VERSION_REUSED > 0x800000
140
+ )
141
+ throw new Error(
142
+ `rocksdb-js VERSION_NOT_UNIQUE_FLAG (${VERSION_REUSED}) is not a single bit in the range Harper record metadata reserves for it — requires @harperfast/rocksdb-js >= 2.8.0`
143
+ );
144
+ function versionIsReused(newVersion: number, existingEntry: { version?: number } | undefined): boolean {
145
+ return existingEntry?.version != null && newVersion <= existingEntry.version;
146
+ }
98
147
 
99
148
  const TRACKED_WRITE_TYPES = new Set(['put', 'patch', 'delete', 'message', 'publish']);
100
149
  // For now we use this as the private property mechanism for mapping records to entries.
@@ -197,6 +246,10 @@ export class RecordEncoder extends StructonEncoder {
197
246
  declare saveStructures: any;
198
247
  declare getStructures: any;
199
248
  declare _writeStruct: any;
249
+ declare typedStructs: any[];
250
+ declare structures: any[];
251
+ declare maxOwnStructures: number;
252
+ randomAccessStructure = false;
200
253
  structureUpdate?: any;
201
254
  // Set by TableResource.updatedAttributes. Undefined for a store with no resolved attributes, which
202
255
  // is what keeps the projection off that store's writes entirely.
@@ -217,7 +270,7 @@ export class RecordEncoder extends StructonEncoder {
217
270
  // Bound the per-encoder typed-structure dictionary. It is append-only and pinned on the
218
271
  // long-lived primary store, so a wide/sparse schema (whose records vary by per-field value
219
272
  // width) can grow it unbounded and exhaust memory. Caller-overridable; default caps it.
220
- options.maxOwnStructures ??= 256;
273
+ options.maxOwnStructures ??= DEFAULT_MAX_TYPED_STRUCTURES;
221
274
  /**
222
275
  * The base class for records that provides the read-only methods for accessing
223
276
  * metadata and will be assigned computed property getters. On its own, these instances
@@ -233,6 +286,7 @@ export class RecordEncoder extends StructonEncoder {
233
286
  // (e.g. __dbis__, useVersions=false) must not — see the encode hook + harper#1307. Default to
234
287
  // true when unspecified so an option that doesn't propagate can't silently strip prefixes.
235
288
  this.useVersions = options.useVersions !== false;
289
+ this.randomAccessStructure = options.randomAccessStructure === true;
236
290
  // structon (the StructonEncoder base) always installs the struct write hook. For DBIs
237
291
  // that don't opt into struct mode (non-primary, e.g. __dbis__), force it to bail (return
238
292
  // 0) so objects are written in plain msgpackr records mode — decodable by readers without
@@ -417,12 +471,14 @@ export class RecordEncoder extends StructonEncoder {
417
471
  // HAS_STRUCTURE_UPDATE in the audit log for a structure that was never persisted.
418
472
  if (committed === true) {
419
473
  this.structureUpdate = structures;
474
+ this.checkStructureCapacity();
420
475
  return true;
421
476
  }
422
477
  return false;
423
478
  } else {
424
479
  const result = superSaveStructures.call(this, structures, isCompatible);
425
480
  this.structureUpdate = structures;
481
+ if (result !== false) this.checkStructureCapacity();
426
482
  return result;
427
483
  }
428
484
  };
@@ -432,10 +488,51 @@ export class RecordEncoder extends StructonEncoder {
432
488
  const buffer = this.rootStore.getBinarySync(sharedStructuresKey);
433
489
  return buffer ? this.decode(buffer) : undefined;
434
490
  } else {
435
- return superGetStructures.call(this);
491
+ // msgpackr only defines getStructures when the option was supplied; a store with no
492
+ // shared-structures mechanism has no durable dictionary to report.
493
+ return superGetStructures?.call(this);
436
494
  }
437
495
  };
438
496
  }
497
+ /**
498
+ * Sizes of this store's record-structure dictionaries. Read from the durable payload, not this
499
+ * encoder's arrays: each worker owns an encoder and loads lazily, so a worker that never encoded
500
+ * for this store holds empty arrays for a store whose dictionary is full.
501
+ */
502
+ getStructureCounts(): StructureCounts {
503
+ const { typed, classic } = countDurableStructures(this.getStructures(), this.typedStructs?.length ?? 0);
504
+ return {
505
+ typed,
506
+ classic,
507
+ typedLimit: this.maxOwnStructures ?? DEFAULT_MAX_TYPED_STRUCTURES,
508
+ typedEnabled: this.randomAccessStructure,
509
+ };
510
+ }
511
+ /**
512
+ * Warn once per encoder when its typed dictionary is at the bound. Fires from the post-save path,
513
+ * so it needs some structure save to happen -- any save, including a classic one -- and it needs
514
+ * this encoder to have loaded the dictionary. Several workers can therefore each warn for one
515
+ * table, and a worker that never saves after loading a full dictionary stays silent. The counts
516
+ * above are the reliable signal; this is the heads-up. It must not escape into the save's return
517
+ * value -- the structures are committed.
518
+ */
519
+ #reportedStructureSaturation = false;
520
+ checkStructureCapacity() {
521
+ if (this.#reportedStructureSaturation) return;
522
+ const typedLimit = this.maxOwnStructures ?? DEFAULT_MAX_TYPED_STRUCTURES;
523
+ if ((this.typedStructs?.length ?? 0) < typedLimit) return;
524
+ this.#reportedStructureSaturation = true;
525
+ try {
526
+ harperLogger.warn(
527
+ `Typed-structure dictionary for store ${this.name ?? this.rootStore?.name ?? '(unnamed)'} reached its limit of ` +
528
+ `${typedLimit} structures; new record shapes will be stored without random-access field ` +
529
+ 'encoding. Dictionary size follows the variety of shapes written -- field set, key order, ' +
530
+ 'and per-field value width -- not column count (see HarperFast/harper#2220).'
531
+ );
532
+ } catch {
533
+ // the structures are already committed; a failing sink must not fail the write
534
+ }
535
+ }
439
536
  decode(buffer, options) {
440
537
  lastMetadata = null;
441
538
  const start = options?.start || 0;
@@ -809,6 +906,10 @@ export function recordUpdater(store, tableId, auditStore) {
809
906
  if (expiresAt >= 0) assignMetadata |= HAS_EXPIRATION;
810
907
  metadataInNextEncoding = assignMetadata;
811
908
  expiresAtNextEncoding = expiresAt;
909
+ // Math.max normalizes the -1 "no metadata word" sentinel to 0 first: OR-ing the flag into
910
+ // -1 directly would stay -1 and silently drop the metadata word (and the flag with it).
911
+ if (isRocksDB && record !== undefined && versionIsReused(newVersion, existingEntry))
912
+ metadataInNextEncoding = Math.max(metadataInNextEncoding, 0) | VERSION_REUSED;
812
913
  const putOptions: {
813
914
  version: number;
814
915
  instructedWrite?: boolean;
@@ -829,9 +930,9 @@ export function recordUpdater(store, tableId, auditStore) {
829
930
  metadataInNextEncoding |= HAS_RESIDENCY_ID;
830
931
  extendedType |= HAS_CURRENT_RESIDENCY_ID;
831
932
  } else residencyIdAtNextEncoding = 0;
832
- const nodeId = options?.nodeId ?? (audit ? getThisNodeId(auditStore) : undefined);
833
- if (nodeId >= 0) {
834
- nodeIdAtNextEncoding = nodeId;
933
+ const recordNodeId = options?.recordNodeId ?? options?.nodeId ?? (audit ? getThisNodeId(auditStore) : undefined);
934
+ if (recordNodeId >= 0) {
935
+ nodeIdAtNextEncoding = recordNodeId;
835
936
  metadataInNextEncoding |= HAS_NODE_ID;
836
937
  } else nodeIdAtNextEncoding = -1;
837
938
  const additionalAuditRefs = options?.additionalAuditRefs;
@@ -904,8 +1005,22 @@ export function recordUpdater(store, tableId, auditStore) {
904
1005
  const nodeId = options?.nodeId ?? getThisNodeId(auditStore) ?? 0;
905
1006
  const viaNodeId = options?.viaNodeId ?? nodeId;
906
1007
  if (resolveRecord && existingEntry?.localTime) {
907
- const replacingId = existingEntry?.localTime;
908
- const replacingEntry = auditStore.get(replacingId, tableId, id);
1008
+ let replacingId = existingEntry.localTime;
1009
+ let replacingEntry;
1010
+ if (isRocksDB && existingEntry.additionalAuditRefs) {
1011
+ for (const ref of existingEntry.additionalAuditRefs) {
1012
+ const candidate = auditStore.get(ref.version, tableId, id, ref.nodeId);
1013
+ if (
1014
+ candidate?.version === existingEntry.version &&
1015
+ (candidate.nodeId ?? 0) === (existingEntry.nodeId ?? 0)
1016
+ ) {
1017
+ replacingId = ref.version;
1018
+ replacingEntry = candidate;
1019
+ break;
1020
+ }
1021
+ }
1022
+ }
1023
+ replacingEntry ??= auditStore.get(replacingId, tableId, id, existingEntry.nodeId);
909
1024
  if (replacingEntry) {
910
1025
  const previousVersion = replacingEntry.previousVersion;
911
1026
  result = auditStore[isRocksDB ? 'putSync' : 'put'](
@@ -934,7 +1049,7 @@ export function recordUpdater(store, tableId, auditStore) {
934
1049
  result = auditStore[isRocksDB ? 'putSync' : 'put'](
935
1050
  record === undefined ? NEW_TIMESTAMP_PLACEHOLDER : LAST_TIMESTAMP_PLACEHOLDER,
936
1051
  {
937
- version: newVersion,
1052
+ version: options?.recordVersion ?? newVersion,
938
1053
  tableId,
939
1054
  recordId: id,
940
1055
  previousVersion: isRocksDB ? existingEntry?.version : existingEntry?.localTime,
@@ -36,6 +36,8 @@ export class RequestTarget extends URLSearchParams {
36
36
  declare select?: Select;
37
37
  /** Return an explanation of the query order */
38
38
  declare explain?: boolean;
39
+ /** Request a total count of matching records for pagination (REST `Prefer: count=exact|estimated`). */
40
+ declare count?: 'exact' | 'estimated';
39
41
  /** Force the query to be executed in the order of conditions */
40
42
  declare enforceExecutionOrder?: boolean;
41
43
  declare lazy?: boolean;
@@ -147,20 +147,55 @@ export class Resource<Record extends object = any> implements ResourceInterface<
147
147
  static put = transactional(
148
148
  function (resource: any, query: RequestTarget, request: Context, data: any) {
149
149
  if (Array.isArray(data) && resource.#isCollection && resource.constructor.loadAsInstance !== false) {
150
+ const resourceClass = resource.constructor;
151
+ const primaryKey = resourceClass.primaryKey;
152
+ // Before dispatching anything: a malformed body must not race a sibling's write, and a bare
153
+ // dereference below would reach the client as a 500 carrying V8's wording. A primitive is
154
+ // malformed for the same reason — only a Table's own key validation rejects one later, so a
155
+ // plain Resource's override would otherwise be handed it.
156
+ for (let index = 0; index < data.length; index++) {
157
+ const element = data[index];
158
+ if (element == null || typeof element !== 'object')
159
+ throw new ClientError(
160
+ `Array element at index ${index} is ${element === null ? 'null' : typeof element}, expected a record`
161
+ );
162
+ }
163
+ const elementTarget = elementTargetFactory(query);
150
164
  const results = [];
151
- for (const element of data) {
152
- const resourceClass = resource.constructor;
153
- const id = element[resourceClass.primaryKey];
154
- let target = new RequestTarget();
155
- target.id = id;
156
- const elementResource = resourceClass.getResource(target, request, {
157
- async: true,
165
+ try {
166
+ for (let index = 0; index < data.length; index++) {
167
+ const element = data[index];
168
+ const target = elementTarget(element[primaryKey]);
169
+ const elementResource = resourceClass.getResource(target, request, {
170
+ async: true,
171
+ });
172
+ // `target`, not `request`: this is the target slot, and Table's back-compat shift only
173
+ // recognizes a RequestTarget there — a context is taken for the record.
174
+ // `missingMethod` rather than an unguarded call, so a resource without `put` answers 405
175
+ // here exactly as it does on the single-record path below.
176
+ if (typeof elementResource.then === 'function')
177
+ results.push(
178
+ elementResource.then((resource) =>
179
+ resource.put ? resource.put(element, target) : missingMethod(resource, 'put')
180
+ )
181
+ );
182
+ else if (elementResource.put) results.push(elementResource.put(element, target));
183
+ else missingMethod(elementResource, 'put');
184
+ }
185
+ } catch (error) {
186
+ // Settle the already-dispatched elements or a rejection among them goes unhandled. Then
187
+ // report the earliest-index failure, the same rule `settleElements` applies: every sibling
188
+ // already dispatched precedes the element that threw, so the batch reports the same failure
189
+ // whether that element's dispatch resolved synchronously or asynchronously.
190
+ return Promise.allSettled(results).then((settled) => {
191
+ const failed = settled.find((sibling) => sibling.status === 'rejected');
192
+ if (!failed) throw error;
193
+ throw (failed as PromiseRejectedResult).reason;
158
194
  });
159
- if (typeof elementResource.then === 'function')
160
- results.push(elementResource.then((resource) => resource.put(element, request)));
161
- else results.push(elementResource.put(element, query));
162
195
  }
163
- return Promise.all(results);
196
+ // Not `Promise.all`: settling the batch while a slower element is still resolving lets that
197
+ // element commit outside the failed batch — a partially applied array PUT.
198
+ return settleElements(results);
164
199
  }
165
200
  return resource.put
166
201
  ? resource.constructor.loadAsInstance === false
@@ -571,6 +606,53 @@ export function snakeCase(camelCase: string) {
571
606
  );
572
607
  }
573
608
 
609
+ /**
610
+ * Mint per-element targets for a fanned-out batch write. Each element needs its own object — its id
611
+ * must not be visible to a sibling whose dispatch resolves later — but it also needs the request's
612
+ * query and route metadata, which a `put()` override can read. The request's contribution is
613
+ * therefore resolved once here rather than deep-cloned per element, and nested metadata is shared
614
+ * rather than copied. `checkPermission` never carries: the collection receiver's single verdict is
615
+ * authoritative and per-element dispatch must not re-arm it.
616
+ */
617
+ function elementTargetFactory(query: any): (id: any) => RequestTarget {
618
+ const base = cloneRequestTarget(query);
619
+ const search = base.toString();
620
+ // Only what the element target would not otherwise have. `RequestTarget` declares `pathname`,
621
+ // `search` and `sort` without `declare`, so they are own properties on every instance and would
622
+ // otherwise always be "carried" — overwriting what this element's own construction just derived,
623
+ // and making the skip below dead. A nullish own value carries no metadata either.
624
+ const carried: Record<string, any> = {};
625
+ for (const key of Object.keys(base)) {
626
+ if (key === 'id' || key === 'isCollection' || key === 'pathname' || key === 'search') continue;
627
+ const value = (base as any)[key];
628
+ if (value != null) carried[key] = value;
629
+ }
630
+ const hasCarried = Object.keys(carried).length > 0;
631
+ return (id) => {
632
+ const target = search ? new RequestTarget(search) : new RequestTarget();
633
+ if (hasCarried) Object.assign(target, carried);
634
+ target.id = id;
635
+ target.isCollection = false;
636
+ return target;
637
+ };
638
+ }
639
+
640
+ /**
641
+ * Report a fanned-out batch write's outcome only once every element has staged or failed its own
642
+ * write, so the enclosing transaction cannot unwind under one. Rejects with the first failure.
643
+ */
644
+ function settleElements(results: any[]): Promise<any[]> {
645
+ return Promise.allSettled(results).then((settled) => {
646
+ const values = new Array(settled.length);
647
+ for (let index = 0; index < settled.length; index++) {
648
+ const element = settled[index];
649
+ if (element.status === 'rejected') throw element.reason;
650
+ values[index] = element.value;
651
+ }
652
+ return values;
653
+ });
654
+ }
655
+
574
656
  /**
575
657
  * This is responsible for arranging arguments in the main static methods and creating the appropriate context and default transaction wrapping
576
658
  * @param action
@@ -733,8 +815,10 @@ function transactional(
733
815
  if (!query) {
734
816
  query = new RequestTarget();
735
817
  query.id = id;
736
- if (isCollection && options.method === 'put' && Array.isArray(data) && this.loadAsInstance === false)
737
- query.isCollection = true;
818
+ // The collection inferred from the batch's null id has to survive onto the synthesized target,
819
+ // or the array is dispatched as one record update against a null primary key. Array-only:
820
+ // a null id on non-array data means auto-generate, not collection.
821
+ if (isCollection && options.method === 'put' && Array.isArray(data)) query.isCollection = true;
738
822
  }
739
823
  if (options.method === 'query' && data && typeof data === 'object') {
740
824
  // QUERY executes the independently parsed body target. Make its requested projection part of
@@ -963,15 +1047,23 @@ function registerLiveSubscriptionForContext(subscription: any, resource: any, ad
963
1047
  // JWT exp of the bearer credential (set by the auth layer); undefined for password/mTLS/session.
964
1048
  authExpiresAt: user.authExpiresAt,
965
1049
  recheck: async () => {
966
- // Re-fetch current user state — the user/role cache is rebuilt on mutations — so a dropped or
967
- // role-stripped user no longer authorizes.
968
- const { findAndValidateUser } = require('../security/user');
969
- const fresh: any = await findAndValidateUser(username, undefined, false);
970
- if (!fresh?.role) return false;
971
- // Advance the subscription's context to the fresh user so downstream checks — context.user
972
- // and getCurrentUser() (which reads the resource's context) — evaluate against current state,
973
- // not the stale user captured at subscribe time.
974
- if (context) (context as any).user = fresh;
1050
+ let fresh: any;
1051
+ if (user._scopedToken) {
1052
+ // A scoped token's identity IS its embedded role — never re-resolve its attribution
1053
+ // username against hdb_user (it may not exist, or may name an unrelated principal
1054
+ // created later). Expiry (authExpiresAt above) is its only revocation.
1055
+ fresh = user;
1056
+ } else {
1057
+ // Re-fetch current user state — the user/role cache is rebuilt on mutations — so a dropped or
1058
+ // role-stripped user no longer authorizes.
1059
+ const { findAndValidateUser } = require('../security/user');
1060
+ fresh = await findAndValidateUser(username, undefined, false);
1061
+ if (!fresh?.role) return false;
1062
+ // Advance the subscription's context to the fresh user so downstream checks — context.user
1063
+ // and getCurrentUser() (which reads the resource's context) — evaluate against current state,
1064
+ // not the stale user captured at subscribe time.
1065
+ if (context) (context as any).user = fresh;
1066
+ }
975
1067
  // Re-run the same operation-level allowRead that granted the subscription.
976
1068
  const reTarget: any = cloneRequestTarget(admittedTarget);
977
1069
  reTarget.checkPermission = fresh.role?.permission;