@harperfast/harper 5.2.10 → 5.3.0-alpha.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 (554) hide show
  1. package/agent/mcpTools.ts +1 -1
  2. package/agent/session.ts +25 -14
  3. package/bin/cliOperations.ts +46 -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 +59 -3
  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 +2414 -242
  14. package/components/ApplicationScope.ts +8 -0
  15. package/components/EntryHandler.ts +59 -39
  16. package/components/OptionsWatcher.ts +150 -74
  17. package/components/RuntimeModuleTracker.ts +38 -7
  18. package/components/Scope.ts +37 -15
  19. package/components/awaitRestart.ts +84 -0
  20. package/components/componentLoader.ts +340 -30
  21. package/components/componentPreparationLock.ts +16 -5
  22. package/components/mcp/adapters/harperHttp.ts +4 -0
  23. package/components/mcp/listChanged.ts +4 -0
  24. package/components/mcp/toolRegistry.ts +2 -0
  25. package/components/mcp/tools/operations.ts +9 -0
  26. package/components/mcp/tools/schemas/operationDescriptions.ts +2 -2
  27. package/components/operations.js +270 -78
  28. package/components/operationsValidation.js +49 -1
  29. package/components/status/ComponentStatusRegistry.ts +59 -0
  30. package/config/RootConfigWatcher.ts +80 -34
  31. package/config/configUtils.ts +291 -34
  32. package/config/harperConfigEnvVars.ts +170 -27
  33. package/config-root.schema.json +29 -0
  34. package/dataLayer/blobBackup.ts +160 -50
  35. package/dataLayer/delete.ts +6 -1
  36. package/dataLayer/harperBridge/ResourceBridge.ts +52 -8
  37. package/dataLayer/harperBridge/lmdbBridge/lmdbMethods/DeleteAuditLogsBeforeResults.js +3 -1
  38. package/dataLayer/hdbInfoController.ts +34 -1
  39. package/dataLayer/insert.ts +44 -1
  40. package/dataLayer/rocksdbBackup.ts +53 -10
  41. package/dataLayer/schema.ts +11 -1
  42. package/dataLayer/schemaDescribe.ts +8 -1
  43. package/dist/agent/mcpTools.js +1 -1
  44. package/dist/agent/mcpTools.js.map +1 -1
  45. package/dist/agent/session.d.ts +22 -0
  46. package/dist/agent/session.js +26 -15
  47. package/dist/agent/session.js.map +1 -1
  48. package/dist/bin/cliOperations.js +48 -9
  49. package/dist/bin/cliOperations.js.map +1 -1
  50. package/dist/bin/copyDb.d.ts +12 -1
  51. package/dist/bin/copyDb.js +248 -60
  52. package/dist/bin/copyDb.js.map +1 -1
  53. package/dist/bin/deploySetup.d.ts +2 -0
  54. package/dist/bin/deploySetup.js +11 -3
  55. package/dist/bin/deploySetup.js.map +1 -1
  56. package/dist/bin/harper.js +1 -1
  57. package/dist/bin/harper.js.map +1 -1
  58. package/dist/bin/help.js +4 -1
  59. package/dist/bin/help.js.map +1 -1
  60. package/dist/bin/lite.js +4 -1
  61. package/dist/bin/lite.js.map +1 -1
  62. package/dist/bin/restart.js +54 -5
  63. package/dist/bin/restart.js.map +1 -1
  64. package/dist/bin/run.js +4 -10
  65. package/dist/bin/run.js.map +1 -1
  66. package/dist/bin/upgrade.js +4 -3
  67. package/dist/bin/upgrade.js.map +1 -1
  68. package/dist/bin/workloadIdentity.d.ts +18 -0
  69. package/dist/bin/workloadIdentity.js +100 -0
  70. package/dist/bin/workloadIdentity.js.map +1 -0
  71. package/dist/components/Application.d.ts +139 -16
  72. package/dist/components/Application.js +2215 -267
  73. package/dist/components/Application.js.map +1 -1
  74. package/dist/components/ApplicationScope.d.ts +8 -0
  75. package/dist/components/ApplicationScope.js +7 -0
  76. package/dist/components/ApplicationScope.js.map +1 -1
  77. package/dist/components/EntryHandler.js +26 -10
  78. package/dist/components/EntryHandler.js.map +1 -1
  79. package/dist/components/OptionsWatcher.d.ts +1 -0
  80. package/dist/components/OptionsWatcher.js +141 -74
  81. package/dist/components/OptionsWatcher.js.map +1 -1
  82. package/dist/components/RuntimeModuleTracker.js +40 -6
  83. package/dist/components/RuntimeModuleTracker.js.map +1 -1
  84. package/dist/components/Scope.js +38 -13
  85. package/dist/components/Scope.js.map +1 -1
  86. package/dist/components/awaitRestart.d.ts +33 -0
  87. package/dist/components/awaitRestart.js +61 -0
  88. package/dist/components/awaitRestart.js.map +1 -0
  89. package/dist/components/componentLoader.d.ts +38 -1
  90. package/dist/components/componentLoader.js +279 -22
  91. package/dist/components/componentLoader.js.map +1 -1
  92. package/dist/components/componentPreparationLock.d.ts +5 -0
  93. package/dist/components/componentPreparationLock.js +14 -6
  94. package/dist/components/componentPreparationLock.js.map +1 -1
  95. package/dist/components/mcp/adapters/harperHttp.js +4 -0
  96. package/dist/components/mcp/adapters/harperHttp.js.map +1 -1
  97. package/dist/components/mcp/listChanged.js +5 -0
  98. package/dist/components/mcp/listChanged.js.map +1 -1
  99. package/dist/components/mcp/toolRegistry.d.ts +1 -0
  100. package/dist/components/mcp/toolRegistry.js.map +1 -1
  101. package/dist/components/mcp/tools/operations.d.ts +5 -0
  102. package/dist/components/mcp/tools/operations.js +9 -0
  103. package/dist/components/mcp/tools/operations.js.map +1 -1
  104. package/dist/components/mcp/tools/schemas/operationDescriptions.js +2 -2
  105. package/dist/components/mcp/tools/schemas/operationDescriptions.js.map +1 -1
  106. package/dist/components/operations.js +231 -77
  107. package/dist/components/operations.js.map +1 -1
  108. package/dist/components/operationsValidation.js +49 -1
  109. package/dist/components/operationsValidation.js.map +1 -1
  110. package/dist/components/status/ComponentStatusRegistry.d.ts +0 -4
  111. package/dist/components/status/ComponentStatusRegistry.js +63 -0
  112. package/dist/components/status/ComponentStatusRegistry.js.map +1 -1
  113. package/dist/config/RootConfigWatcher.d.ts +7 -1
  114. package/dist/config/RootConfigWatcher.js +64 -27
  115. package/dist/config/RootConfigWatcher.js.map +1 -1
  116. package/dist/config/configUtils.d.ts +9 -1
  117. package/dist/config/configUtils.js +254 -33
  118. package/dist/config/configUtils.js.map +1 -1
  119. package/dist/config/harperConfigEnvVars.d.ts +16 -0
  120. package/dist/config/harperConfigEnvVars.js +162 -25
  121. package/dist/config/harperConfigEnvVars.js.map +1 -1
  122. package/dist/dataLayer/blobBackup.d.ts +49 -20
  123. package/dist/dataLayer/blobBackup.js +139 -50
  124. package/dist/dataLayer/blobBackup.js.map +1 -1
  125. package/dist/dataLayer/delete.js +1 -1
  126. package/dist/dataLayer/delete.js.map +1 -1
  127. package/dist/dataLayer/harperBridge/ResourceBridge.d.ts +14 -1
  128. package/dist/dataLayer/harperBridge/ResourceBridge.js +51 -10
  129. package/dist/dataLayer/harperBridge/ResourceBridge.js.map +1 -1
  130. package/dist/dataLayer/harperBridge/lmdbBridge/lmdbMethods/DeleteAuditLogsBeforeResults.d.ts +3 -1
  131. package/dist/dataLayer/harperBridge/lmdbBridge/lmdbMethods/DeleteAuditLogsBeforeResults.js +3 -1
  132. package/dist/dataLayer/harperBridge/lmdbBridge/lmdbMethods/DeleteAuditLogsBeforeResults.js.map +1 -1
  133. package/dist/dataLayer/hdbInfoController.d.ts +10 -0
  134. package/dist/dataLayer/hdbInfoController.js +30 -1
  135. package/dist/dataLayer/hdbInfoController.js.map +1 -1
  136. package/dist/dataLayer/insert.d.ts +9 -1
  137. package/dist/dataLayer/insert.js +30 -0
  138. package/dist/dataLayer/insert.js.map +1 -1
  139. package/dist/dataLayer/rocksdbBackup.d.ts +2 -2
  140. package/dist/dataLayer/rocksdbBackup.js +45 -8
  141. package/dist/dataLayer/rocksdbBackup.js.map +1 -1
  142. package/dist/dataLayer/schema.js +8 -0
  143. package/dist/dataLayer/schema.js.map +1 -1
  144. package/dist/dataLayer/schemaDescribe.js +8 -1
  145. package/dist/dataLayer/schemaDescribe.js.map +1 -1
  146. package/dist/index.d.ts +1 -1
  147. package/dist/index.js.map +1 -1
  148. package/dist/json/systemSchema.json +52 -0
  149. package/dist/resources/DatabaseTransaction.d.ts +55 -3
  150. package/dist/resources/DatabaseTransaction.js +460 -134
  151. package/dist/resources/DatabaseTransaction.js.map +1 -1
  152. package/dist/resources/LMDBTransaction.js +22 -4
  153. package/dist/resources/LMDBTransaction.js.map +1 -1
  154. package/dist/resources/PrimaryRocksDatabase.d.ts +1 -0
  155. package/dist/resources/PrimaryRocksDatabase.js +30 -2
  156. package/dist/resources/PrimaryRocksDatabase.js.map +1 -1
  157. package/dist/resources/RecordEncoder.d.ts +20 -0
  158. package/dist/resources/RecordEncoder.js +94 -9
  159. package/dist/resources/RecordEncoder.js.map +1 -1
  160. package/dist/resources/RequestTarget.d.ts +2 -0
  161. package/dist/resources/RequestTarget.js.map +1 -1
  162. package/dist/resources/Resource.js +20 -11
  163. package/dist/resources/Resource.js.map +1 -1
  164. package/dist/resources/ResourceInterface.d.ts +20 -1
  165. package/dist/resources/ResourceInterface.js.map +1 -1
  166. package/dist/resources/RocksIndexStore.d.ts +6 -1
  167. package/dist/resources/RocksIndexStore.js +24 -10
  168. package/dist/resources/RocksIndexStore.js.map +1 -1
  169. package/dist/resources/RocksTransactionLogStore.d.ts +14 -1
  170. package/dist/resources/RocksTransactionLogStore.js +57 -17
  171. package/dist/resources/RocksTransactionLogStore.js.map +1 -1
  172. package/dist/resources/Table.d.ts +112 -8
  173. package/dist/resources/Table.js +1071 -197
  174. package/dist/resources/Table.js.map +1 -1
  175. package/dist/resources/auditStore.d.ts +11 -2
  176. package/dist/resources/auditStore.js +183 -18
  177. package/dist/resources/auditStore.js.map +1 -1
  178. package/dist/resources/blob.d.ts +129 -9
  179. package/dist/resources/blob.js +992 -114
  180. package/dist/resources/blob.js.map +1 -1
  181. package/dist/resources/branchDatabase.d.ts +48 -0
  182. package/dist/resources/branchDatabase.js +892 -0
  183. package/dist/resources/branchDatabase.js.map +1 -0
  184. package/dist/resources/crdt.js +50 -12
  185. package/dist/resources/crdt.js.map +1 -1
  186. package/dist/resources/databases.d.ts +150 -9
  187. package/dist/resources/databases.js +1247 -525
  188. package/dist/resources/databases.js.map +1 -1
  189. package/dist/resources/defineTable.d.ts +10 -2
  190. package/dist/resources/defineTable.js +9 -1
  191. package/dist/resources/defineTable.js.map +1 -1
  192. package/dist/resources/graphql.d.ts +1 -1
  193. package/dist/resources/graphql.js +50 -15
  194. package/dist/resources/graphql.js.map +1 -1
  195. package/dist/resources/indexes/HierarchicalNavigableSmallWorld.d.ts +1 -0
  196. package/dist/resources/indexes/HierarchicalNavigableSmallWorld.js +6 -2
  197. package/dist/resources/indexes/HierarchicalNavigableSmallWorld.js.map +1 -1
  198. package/dist/resources/longLivedTransactions.d.ts +71 -0
  199. package/dist/resources/longLivedTransactions.js +358 -0
  200. package/dist/resources/longLivedTransactions.js.map +1 -0
  201. package/dist/resources/models/backendRegistry.d.ts +26 -0
  202. package/dist/resources/models/backendRegistry.js +60 -2
  203. package/dist/resources/models/backendRegistry.js.map +1 -1
  204. package/dist/resources/models/bootstrap.d.ts +33 -1
  205. package/dist/resources/models/bootstrap.js +416 -31
  206. package/dist/resources/models/bootstrap.js.map +1 -1
  207. package/dist/resources/recordLock.d.ts +80 -0
  208. package/dist/resources/recordLock.js +184 -0
  209. package/dist/resources/recordLock.js.map +1 -0
  210. package/dist/resources/replayLogs.d.ts +14 -1
  211. package/dist/resources/replayLogs.js +152 -24
  212. package/dist/resources/replayLogs.js.map +1 -1
  213. package/dist/resources/replayLogsGuards.d.ts +94 -7
  214. package/dist/resources/replayLogsGuards.js +111 -7
  215. package/dist/resources/replayLogsGuards.js.map +1 -1
  216. package/dist/resources/search.js +158 -32
  217. package/dist/resources/search.js.map +1 -1
  218. package/dist/resources/transactionBroadcast.d.ts +1 -1
  219. package/dist/resources/transactionBroadcast.js +2 -2
  220. package/dist/resources/transactionBroadcast.js.map +1 -1
  221. package/dist/security/auth.js +61 -30
  222. package/dist/security/auth.js.map +1 -1
  223. package/dist/security/authn/oidc/claims.d.ts +22 -0
  224. package/dist/security/authn/oidc/claims.js +71 -0
  225. package/dist/security/authn/oidc/claims.js.map +1 -0
  226. package/dist/security/authn/oidc/identityToken.d.ts +27 -0
  227. package/dist/security/authn/oidc/identityToken.js +111 -0
  228. package/dist/security/authn/oidc/identityToken.js.map +1 -0
  229. package/dist/security/authn/oidc/jwks.d.ts +25 -0
  230. package/dist/security/authn/oidc/jwks.js +261 -0
  231. package/dist/security/authn/oidc/jwks.js.map +1 -0
  232. package/dist/security/authn/oidc/providers/generic.d.ts +13 -0
  233. package/dist/security/authn/oidc/providers/generic.js +34 -0
  234. package/dist/security/authn/oidc/providers/generic.js.map +1 -0
  235. package/dist/security/authn/oidc/providers/githubActions.d.ts +11 -0
  236. package/dist/security/authn/oidc/providers/githubActions.js +129 -0
  237. package/dist/security/authn/oidc/providers/githubActions.js.map +1 -0
  238. package/dist/security/authn/oidc/providers/index.d.ts +37 -0
  239. package/dist/security/authn/oidc/providers/index.js +24 -0
  240. package/dist/security/authn/oidc/providers/index.js.map +1 -0
  241. package/dist/security/authn/oidc/tokenExchange.d.ts +12 -0
  242. package/dist/security/authn/oidc/tokenExchange.js +306 -0
  243. package/dist/security/authn/oidc/tokenExchange.js.map +1 -0
  244. package/dist/security/authn/oidc/trustPolicyOperations.d.ts +49 -0
  245. package/dist/security/authn/oidc/trustPolicyOperations.js +358 -0
  246. package/dist/security/authn/oidc/trustPolicyOperations.js.map +1 -0
  247. package/dist/security/authn/oidc/types.d.ts +38 -0
  248. package/dist/security/authn/oidc/types.js +6 -0
  249. package/dist/security/authn/oidc/types.js.map +1 -0
  250. package/dist/security/certificateVerification/index.js +40 -11
  251. package/dist/security/certificateVerification/index.js.map +1 -1
  252. package/dist/security/certificateVerification/trustedIssuers.d.ts +24 -0
  253. package/dist/security/certificateVerification/trustedIssuers.js +79 -0
  254. package/dist/security/certificateVerification/trustedIssuers.js.map +1 -0
  255. package/dist/security/certificateVerification/types.d.ts +1 -0
  256. package/dist/security/credentialProvenance.d.ts +35 -0
  257. package/dist/security/credentialProvenance.js +51 -0
  258. package/dist/security/credentialProvenance.js.map +1 -0
  259. package/dist/security/credentialRejection.d.ts +4 -0
  260. package/dist/security/credentialRejection.js +24 -0
  261. package/dist/security/credentialRejection.js.map +1 -0
  262. package/dist/security/deferredAuthentication.d.ts +36 -0
  263. package/dist/security/deferredAuthentication.js +70 -0
  264. package/dist/security/deferredAuthentication.js.map +1 -0
  265. package/dist/security/impersonation.d.ts +21 -0
  266. package/dist/security/impersonation.js +108 -9
  267. package/dist/security/impersonation.js.map +1 -1
  268. package/dist/security/jsLoader.d.ts +6 -0
  269. package/dist/security/jsLoader.js +75 -15
  270. package/dist/security/jsLoader.js.map +1 -1
  271. package/dist/security/keys.js +301 -71
  272. package/dist/security/keys.js.map +1 -1
  273. package/dist/security/operationScope.d.ts +21 -0
  274. package/dist/security/operationScope.js +36 -0
  275. package/dist/security/operationScope.js.map +1 -0
  276. package/dist/security/permissionsTranslator.js +21 -0
  277. package/dist/security/permissionsTranslator.js.map +1 -1
  278. package/dist/security/tokenAuthentication.d.ts +19 -1
  279. package/dist/security/tokenAuthentication.js +191 -10
  280. package/dist/security/tokenAuthentication.js.map +1 -1
  281. package/dist/security/user.js +4 -3
  282. package/dist/security/user.js.map +1 -1
  283. package/dist/server/DurableSubscriptionsSession.d.ts +2 -2
  284. package/dist/server/DurableSubscriptionsSession.js +67 -10
  285. package/dist/server/DurableSubscriptionsSession.js.map +1 -1
  286. package/dist/server/REST.js +73 -0
  287. package/dist/server/REST.js.map +1 -1
  288. package/dist/server/graphqlQuerying.js +4 -0
  289. package/dist/server/graphqlQuerying.js.map +1 -1
  290. package/dist/server/http.d.ts +11 -0
  291. package/dist/server/http.js +88 -15
  292. package/dist/server/http.js.map +1 -1
  293. package/dist/server/itc/serverHandlers.js +8 -1
  294. package/dist/server/itc/serverHandlers.js.map +1 -1
  295. package/dist/server/jobs/jobProcess.js +6 -2
  296. package/dist/server/jobs/jobProcess.js.map +1 -1
  297. package/dist/server/jobs/jobs.js +4 -1
  298. package/dist/server/jobs/jobs.js.map +1 -1
  299. package/dist/server/liveSubscriptionAuth.d.ts +26 -4
  300. package/dist/server/liveSubscriptionAuth.js +105 -39
  301. package/dist/server/liveSubscriptionAuth.js.map +1 -1
  302. package/dist/server/loadRootComponents.js +49 -10
  303. package/dist/server/loadRootComponents.js.map +1 -1
  304. package/dist/server/mqtt.d.ts +2 -0
  305. package/dist/server/mqtt.js +165 -30
  306. package/dist/server/mqtt.js.map +1 -1
  307. package/dist/server/nodeName.d.ts +2 -0
  308. package/dist/server/nodeName.js +107 -23
  309. package/dist/server/nodeName.js.map +1 -1
  310. package/dist/server/serverHelpers/Headers.d.ts +25 -0
  311. package/dist/server/serverHelpers/Headers.js +139 -1
  312. package/dist/server/serverHelpers/Headers.js.map +1 -1
  313. package/dist/server/serverHelpers/contentTypes.d.ts +9 -0
  314. package/dist/server/serverHelpers/contentTypes.js +32 -25
  315. package/dist/server/serverHelpers/contentTypes.js.map +1 -1
  316. package/dist/server/serverHelpers/deployValidationState.d.ts +3 -0
  317. package/dist/server/serverHelpers/deployValidationState.js +9 -19
  318. package/dist/server/serverHelpers/deployValidationState.js.map +1 -1
  319. package/dist/server/serverHelpers/operationAuthorizationState.d.ts +12 -0
  320. package/dist/server/serverHelpers/operationAuthorizationState.js +24 -2
  321. package/dist/server/serverHelpers/operationAuthorizationState.js.map +1 -1
  322. package/dist/server/serverHelpers/registeredOperations.d.ts +5 -4
  323. package/dist/server/serverHelpers/registeredOperations.js +74 -21
  324. package/dist/server/serverHelpers/registeredOperations.js.map +1 -1
  325. package/dist/server/serverHelpers/requestSanitization.d.ts +11 -0
  326. package/dist/server/serverHelpers/requestSanitization.js +20 -0
  327. package/dist/server/serverHelpers/requestSanitization.js.map +1 -0
  328. package/dist/server/serverHelpers/serverHandlers.js +6 -3
  329. package/dist/server/serverHelpers/serverHandlers.js.map +1 -1
  330. package/dist/server/serverHelpers/serverUtilities.d.ts +18 -0
  331. package/dist/server/serverHelpers/serverUtilities.js +90 -20
  332. package/dist/server/serverHelpers/serverUtilities.js.map +1 -1
  333. package/dist/server/serverHelpers/sharedMessageEncoding.d.ts +67 -0
  334. package/dist/server/serverHelpers/sharedMessageEncoding.js +280 -0
  335. package/dist/server/serverHelpers/sharedMessageEncoding.js.map +1 -0
  336. package/dist/server/serverHelpers/uwsServer.js +19 -1
  337. package/dist/server/serverHelpers/uwsServer.js.map +1 -1
  338. package/dist/server/static.js +24 -28
  339. package/dist/server/static.js.map +1 -1
  340. package/dist/server/storageReclamation.d.ts +5 -0
  341. package/dist/server/storageReclamation.js +16 -0
  342. package/dist/server/storageReclamation.js.map +1 -1
  343. package/dist/server/threads/itc.d.ts +7 -2
  344. package/dist/server/threads/itc.js +5 -1
  345. package/dist/server/threads/itc.js.map +1 -1
  346. package/dist/server/threads/manageThreads.d.ts +26 -4
  347. package/dist/server/threads/manageThreads.js +517 -72
  348. package/dist/server/threads/manageThreads.js.map +1 -1
  349. package/dist/server/threads/socketRouter.js +89 -26
  350. package/dist/server/threads/socketRouter.js.map +1 -1
  351. package/dist/server/threads/threadHeapMemory.d.ts +2 -0
  352. package/dist/server/threads/threadHeapMemory.js +31 -0
  353. package/dist/server/threads/threadHeapMemory.js.map +1 -0
  354. package/dist/server/threads/threadServer.js +46 -15
  355. package/dist/server/threads/threadServer.js.map +1 -1
  356. package/dist/sqlEngine/config.d.ts +1 -3
  357. package/dist/sqlEngine/config.js +19 -16
  358. package/dist/sqlEngine/config.js.map +1 -1
  359. package/dist/sqlTranslator/index.d.ts +1 -1
  360. package/dist/sqlTranslator/index.js +30 -7
  361. package/dist/sqlTranslator/index.js.map +1 -1
  362. package/dist/upgrade/directives/5-3-0.d.ts +7 -0
  363. package/dist/upgrade/directives/5-3-0.js +148 -0
  364. package/dist/upgrade/directives/5-3-0.js.map +1 -0
  365. package/dist/upgrade/directives/directivesController.js +2 -1
  366. package/dist/upgrade/directives/directivesController.js.map +1 -1
  367. package/dist/utility/OperationFunctionCaller.js +2 -1
  368. package/dist/utility/OperationFunctionCaller.js.map +1 -1
  369. package/dist/utility/common_utils.d.ts +16 -0
  370. package/dist/utility/common_utils.js +32 -6
  371. package/dist/utility/common_utils.js.map +1 -1
  372. package/dist/utility/componentNames.d.ts +8 -0
  373. package/dist/utility/componentNames.js +12 -1
  374. package/dist/utility/componentNames.js.map +1 -1
  375. package/dist/utility/environment/environmentManager.js +3 -6
  376. package/dist/utility/environment/environmentManager.js.map +1 -1
  377. package/dist/utility/environment/systemInformation.d.ts +1 -0
  378. package/dist/utility/environment/systemInformation.js +1 -0
  379. package/dist/utility/environment/systemInformation.js.map +1 -1
  380. package/dist/utility/errors/commonErrors.d.ts +2 -0
  381. package/dist/utility/errors/commonErrors.js +2 -0
  382. package/dist/utility/errors/commonErrors.js.map +1 -1
  383. package/dist/utility/errors/hdbError.d.ts +17 -0
  384. package/dist/utility/errors/hdbError.js +30 -1
  385. package/dist/utility/errors/hdbError.js.map +1 -1
  386. package/dist/utility/globalSchema.d.ts +18 -0
  387. package/dist/utility/hdbTerms.d.ts +16 -0
  388. package/dist/utility/hdbTerms.js +18 -2
  389. package/dist/utility/hdbTerms.js.map +1 -1
  390. package/dist/utility/logging/harper_logger.d.ts +2 -0
  391. package/dist/utility/logging/harper_logger.js +91 -14
  392. package/dist/utility/logging/harper_logger.js.map +1 -1
  393. package/dist/utility/logging/logRotator.js +76 -49
  394. package/dist/utility/logging/logRotator.js.map +1 -1
  395. package/dist/utility/nodeIdentity.d.ts +9 -0
  396. package/dist/utility/nodeIdentity.js +58 -0
  397. package/dist/utility/nodeIdentity.js.map +1 -0
  398. package/dist/utility/npmUtilities.js +9 -7
  399. package/dist/utility/npmUtilities.js.map +1 -1
  400. package/dist/utility/operationPermissions.d.ts +3 -1
  401. package/dist/utility/operationPermissions.js +16 -1
  402. package/dist/utility/operationPermissions.js.map +1 -1
  403. package/dist/utility/operation_authorization.d.ts +10 -7
  404. package/dist/utility/operation_authorization.js +212 -41
  405. package/dist/utility/operation_authorization.js.map +1 -1
  406. package/dist/utility/watchPath.d.ts +29 -0
  407. package/dist/utility/watchPath.js +68 -0
  408. package/dist/utility/watchPath.js.map +1 -0
  409. package/dist/utility/watcherFallback.d.ts +86 -0
  410. package/dist/utility/watcherFallback.js +278 -1
  411. package/dist/utility/watcherFallback.js.map +1 -1
  412. package/dist/validation/configValidator.d.ts +12 -0
  413. package/dist/validation/configValidator.js +199 -72
  414. package/dist/validation/configValidator.js.map +1 -1
  415. package/dist/validation/installValidator.js +12 -0
  416. package/dist/validation/installValidator.js.map +1 -1
  417. package/dist/validation/validationWrapper.d.ts +11 -0
  418. package/dist/validation/validationWrapper.js +16 -3
  419. package/dist/validation/validationWrapper.js.map +1 -1
  420. package/index.ts +2 -0
  421. package/json/systemSchema.json +52 -0
  422. package/npm-shrinkwrap.json +235 -233
  423. package/package.json +8 -7
  424. package/resources/DESIGN.md +114 -52
  425. package/resources/DatabaseTransaction.ts +522 -130
  426. package/resources/LMDBTransaction.ts +26 -4
  427. package/resources/PrimaryRocksDatabase.ts +29 -3
  428. package/resources/RecordEncoder.ts +103 -8
  429. package/resources/RequestTarget.ts +2 -0
  430. package/resources/Resource.ts +17 -9
  431. package/resources/ResourceInterface.ts +23 -0
  432. package/resources/RocksIndexStore.ts +30 -10
  433. package/resources/RocksTransactionLogStore.ts +78 -21
  434. package/resources/Table.ts +1107 -164
  435. package/resources/auditStore.ts +187 -21
  436. package/resources/blob.ts +1029 -110
  437. package/resources/branchDatabase.ts +941 -0
  438. package/resources/crdt.ts +76 -12
  439. package/resources/databases.ts +1354 -522
  440. package/resources/defineTable.ts +18 -2
  441. package/resources/graphql.ts +70 -16
  442. package/resources/indexes/HierarchicalNavigableSmallWorld.ts +6 -2
  443. package/resources/longLivedTransactions.ts +360 -0
  444. package/resources/models/backendRegistry.ts +84 -2
  445. package/resources/models/bootstrap.ts +473 -28
  446. package/resources/recordLock.ts +253 -0
  447. package/resources/replayLogs.ts +147 -26
  448. package/resources/replayLogsGuards.ts +171 -8
  449. package/resources/search.ts +154 -32
  450. package/resources/transactionBroadcast.ts +3 -3
  451. package/security/auth.ts +68 -29
  452. package/security/authn/oidc/claims.ts +72 -0
  453. package/security/authn/oidc/identityToken.ts +129 -0
  454. package/security/authn/oidc/jwks.ts +260 -0
  455. package/security/authn/oidc/providers/generic.ts +40 -0
  456. package/security/authn/oidc/providers/githubActions.ts +137 -0
  457. package/security/authn/oidc/providers/index.ts +52 -0
  458. package/security/authn/oidc/tokenExchange.ts +300 -0
  459. package/security/authn/oidc/trustPolicyOperations.ts +343 -0
  460. package/security/authn/oidc/types.ts +41 -0
  461. package/security/certificateVerification/index.ts +54 -13
  462. package/security/certificateVerification/trustedIssuers.ts +76 -0
  463. package/security/certificateVerification/types.ts +1 -0
  464. package/security/credentialProvenance.ts +47 -0
  465. package/security/credentialRejection.ts +22 -0
  466. package/security/deferredAuthentication.ts +71 -0
  467. package/security/impersonation.ts +117 -12
  468. package/security/jsLoader.ts +81 -18
  469. package/security/keys.ts +298 -72
  470. package/security/operationScope.ts +33 -0
  471. package/security/permissionsTranslator.js +23 -0
  472. package/security/tokenAuthentication.ts +233 -12
  473. package/security/user.ts +4 -3
  474. package/server/DESIGN.md +183 -16
  475. package/server/DurableSubscriptionsSession.ts +71 -11
  476. package/server/REST.ts +79 -1
  477. package/server/graphqlQuerying.ts +4 -0
  478. package/server/http.ts +99 -18
  479. package/server/itc/serverHandlers.js +8 -1
  480. package/server/jobs/jobProcess.ts +8 -2
  481. package/server/jobs/jobs.ts +4 -1
  482. package/server/liveSubscriptionAuth.ts +129 -46
  483. package/server/loadRootComponents.js +50 -8
  484. package/server/mqtt.ts +179 -38
  485. package/server/nodeName.ts +103 -21
  486. package/server/serverHelpers/Headers.ts +131 -0
  487. package/server/serverHelpers/contentTypes.ts +29 -21
  488. package/server/serverHelpers/deployValidationState.ts +24 -13
  489. package/server/serverHelpers/operationAuthorizationState.ts +34 -3
  490. package/server/serverHelpers/registeredOperations.ts +79 -22
  491. package/server/serverHelpers/requestSanitization.ts +15 -0
  492. package/server/serverHelpers/serverHandlers.js +6 -3
  493. package/server/serverHelpers/serverUtilities.ts +104 -31
  494. package/server/serverHelpers/sharedMessageEncoding.ts +307 -0
  495. package/server/serverHelpers/uwsServer.ts +17 -2
  496. package/server/static.ts +23 -29
  497. package/server/storageReclamation.ts +13 -0
  498. package/server/threads/itc.js +11 -1
  499. package/server/threads/manageThreads.js +526 -63
  500. package/server/threads/socketRouter.ts +81 -26
  501. package/server/threads/threadHeapMemory.ts +26 -0
  502. package/server/threads/threadServer.js +43 -15
  503. package/sqlTranslator/index.ts +31 -8
  504. package/studio/web/assets/{Chat-Br06zdMA.js → Chat-BnCBegQz.js} +1 -1
  505. package/studio/web/assets/{FloatingChat-BWImX5fA.js → FloatingChat-CoDW1ySS.js} +3 -3
  506. package/studio/web/assets/{apiToken-DN0nmDsq.js → apiToken-Bwk5BLXW.js} +1 -1
  507. package/studio/web/assets/{applications-kSxVoyeU.js → applications-DHxGi7JH.js} +1 -1
  508. package/studio/web/assets/{cssMode-C1JeufH5.js → cssMode-s0cWI-_M.js} +1 -1
  509. package/studio/web/assets/{editor-qoo9CrGO.js → editor-DNcRHK54.js} +1 -1
  510. package/studio/web/assets/{html-Dt4IIy04.js → html-Bdssedlg.js} +1 -1
  511. package/studio/web/assets/{htmlMode-DXgKKr4C.js → htmlMode-CoDlJ3fw.js} +1 -1
  512. package/studio/web/assets/{index-6onkYFOG.js → index-D6sxmFLR.js} +5 -5
  513. package/studio/web/assets/{index.lazy-BrCFnpNJ.js → index.lazy-tmU5BS8s.js} +1 -1
  514. package/studio/web/assets/{javascript-DNCQGUBc.js → javascript-B8meVSTH.js} +1 -1
  515. package/studio/web/assets/{jsonMode-CR6HWruP.js → jsonMode-DpIPd35T.js} +1 -1
  516. package/studio/web/assets/{languageServices-BM4fI4rS.js → languageServices-C_5FMJzQ.js} +1 -1
  517. package/studio/web/assets/{lspLanguageFeatures-DSa1ttcD.js → lspLanguageFeatures-BIzNBzPK.js} +1 -1
  518. package/studio/web/assets/{notifications-BHXLnh6x.js → notifications-CQf18QKb.js} +1 -1
  519. package/studio/web/assets/{notifications-CUmtIA6z.js → notifications-CvZivSbh.js} +1 -1
  520. package/studio/web/assets/{profile-8BeFSF3j.js → profile-DdOwtntb.js} +1 -1
  521. package/studio/web/assets/{regions-C8qR0HhD.js → regions-n69fwagr.js} +1 -1
  522. package/studio/web/assets/{register-B4n5i0SD.js → register-PfWTCXWB.js} +2 -2
  523. package/studio/web/assets/{setComponentFile-g0_B0lgX.js → setComponentFile-Bg6O7X0S.js} +1 -1
  524. package/studio/web/assets/{setup-B0CTj_Q5.js → setup-CUx_aUDl.js} +2 -2
  525. package/studio/web/assets/{status-C6Yib7-K.js → status-D7BVKqX9.js} +1 -1
  526. package/studio/web/assets/{toggleHighContrast-Dgta7bVi.js → toggleHighContrast-DBSyXzMr.js} +1 -1
  527. package/studio/web/assets/{tsMode-CH_jHvU-.js → tsMode-BByKCjBS.js} +1 -1
  528. package/studio/web/assets/{typescript-Co9LCXd5.js → typescript-DDLnLpw9.js} +1 -1
  529. package/studio/web/assets/{useEntityRestURL-wKC8NsC_.js → useEntityRestURL-31CHGaHk.js} +1 -1
  530. package/studio/web/assets/{workers-CWeLxCXA.js → workers-pR3jRY9D.js} +1 -1
  531. package/studio/web/assets/{xml-BadC-0Rk.js → xml-2iRnMhQO.js} +1 -1
  532. package/studio/web/assets/{yaml-BiUfxPbC.js → yaml-Bf92gJpd.js} +1 -1
  533. package/studio/web/index.html +1 -1
  534. package/upgrade/directives/5-3-0.ts +132 -0
  535. package/upgrade/directives/directivesController.ts +2 -1
  536. package/utility/OperationFunctionCaller.ts +2 -1
  537. package/utility/common_utils.ts +30 -5
  538. package/utility/componentNames.ts +12 -0
  539. package/utility/environment/environmentManager.ts +3 -7
  540. package/utility/environment/systemInformation.ts +7 -0
  541. package/utility/errors/commonErrors.ts +4 -0
  542. package/utility/errors/hdbError.ts +29 -0
  543. package/utility/hdbTerms.ts +17 -0
  544. package/utility/logging/harper_logger.ts +87 -14
  545. package/utility/logging/logRotator.ts +72 -45
  546. package/utility/nodeIdentity.ts +45 -0
  547. package/utility/npmUtilities.ts +10 -8
  548. package/utility/operationPermissions.ts +18 -1
  549. package/utility/operation_authorization.ts +231 -42
  550. package/utility/watchPath.ts +63 -0
  551. package/utility/watcherFallback.ts +270 -0
  552. package/validation/configValidator.ts +209 -72
  553. package/validation/installValidator.ts +15 -0
  554. package/validation/validationWrapper.ts +18 -4
@@ -132,16 +132,66 @@ export function shouldAbortSlowReplay(totalElapsedMs: number, timeLimitMs = REPL
132
132
  }
133
133
 
134
134
  /**
135
- * Wraps a transaction-log query iterator so a corrupt/torn frame ends that log's iteration
136
- * cleanly instead of escaping as an uncaughtException. rocksdb-js throws a bounded RangeError
137
- * when an entry's framing is broken; framing loss means the next entry can't be located, so the
138
- * frame marks end-of-log (entries before it were already yielded) and startup replay /
139
- * replication broadcast continue. `onCorruptFrame` fires once, latched — kept a callback (not a
140
- * direct log) so this module stays out of the Harper module graph and is unit-testable.
135
+ * A corrupt transaction-log frame from rocksdb-js. `resyncPosition` is set only when intact entries
136
+ * follow the break, which is what distinguishes lost entries from a merely truncated tail; its
137
+ * absence is also all that any rocksdb-js predating that field can report.
138
+ */
139
+ export type CorruptFrameError = RangeError & {
140
+ logId?: number | null;
141
+ position?: number | null;
142
+ resyncPosition?: number | null;
143
+ unreadableBytes?: number | null;
144
+ };
145
+
146
+ /**
147
+ * Corrupt frames hit by one `getRange` call, shared by its per-log iterators.
148
+ *
149
+ * `truncatedVersions` is what a consumer has to act on: a break destroys the framing after the last
150
+ * entry the broken log yielded, and whether the entries it swallowed continued that entry's version
151
+ * is exactly what can no longer be read — UNLESS that last entry's own `endTxn` already closed its
152
+ * transaction, in which case nothing of it was left open to swallow. That version's transaction is
153
+ * therefore incomplete only when it wasn't already known-closed, and replay must discard it rather
154
+ * than commit the part of it that was still readable. A log that broke before yielding anything
155
+ * truncates no transaction.
156
+ *
157
+ * `midLogBreak` is coarser: true once any break in this range left intact entries unreadable behind
158
+ * it (see {@link isMidLogBreak}), regardless of which version they belonged to. A consumer that must
159
+ * never serve a range with lost entries — an elected branch replay publishing itself as a trustworthy
160
+ * point-in-time copy — rejects on this rather than on `truncatedVersions`, since a mid-log break can
161
+ * strand entries whose version this range never even attributes to one log (harper#2016, #2063).
162
+ */
163
+ export interface CorruptFrameStop {
164
+ breaks: number;
165
+ truncatedVersions: Set<number>;
166
+ midLogBreak: boolean;
167
+ }
168
+
169
+ /**
170
+ * Whether a corrupt frame is a mid-log break — intact entries followed it, so entries were lost
171
+ * rather than the log merely being torn at its tail. Shared by the reporter below and by
172
+ * `RocksTransactionLogStore.getRange`'s `CorruptFrameStop.midLogBreak`, so the two can't drift on
173
+ * what counts as a mid-log break.
174
+ */
175
+ export function isMidLogBreak(error: CorruptFrameError): boolean {
176
+ return error.resyncPosition != null;
177
+ }
178
+
179
+ /**
180
+ * Wraps a transaction-log query iterator so a corrupt/torn frame ends that log's iteration cleanly
181
+ * instead of escaping as an uncaughtException.
182
+ *
183
+ * Iteration stops at the break whether or not intact entries follow it. Resuming past a mid-log
184
+ * break would recover those entries, but a transaction log carries no frame-level transaction
185
+ * boundaries: replay groups equal-version entries into one source transaction, so skipping a frame
186
+ * inside such a group applies and checkpoints the surviving subset of a transaction that never
187
+ * committed that way at the source. Availability loss is visible and recoverable; a silently torn
188
+ * transaction is neither. Recovery is deferred until the engine can resume at a proven boundary
189
+ * (harper#2016, harper#2063); `resyncPosition` is used only to report the break as lost entries
190
+ * rather than as a truncated tail.
141
191
  */
142
192
  export function endIteratorOnCorruptFrame<T>(
143
193
  iterator: Iterator<T>,
144
- onCorruptFrame: (error: RangeError) => void
194
+ onCorruptFrame: (error: CorruptFrameError) => void
145
195
  ): IterableIterator<T> {
146
196
  let stopped = false;
147
197
  return {
@@ -157,7 +207,7 @@ export function endIteratorOnCorruptFrame<T>(
157
207
  // version-dependent (1.4.2 added hex offsets). Anything else re-throws.
158
208
  if (!(error instanceof RangeError)) throw error;
159
209
  stopped = true;
160
- onCorruptFrame(error);
210
+ onCorruptFrame(error as CorruptFrameError);
161
211
  return { done: true, value: undefined };
162
212
  }
163
213
  },
@@ -175,3 +225,116 @@ export function endIteratorOnCorruptFrame<T>(
175
225
  },
176
226
  };
177
227
  }
228
+
229
+ /**
230
+ * A corrupt frame, accumulated for the life of the process. The same frame is re-encountered by
231
+ * every reader until each consumer's resume cursor has passed it, so this is deduplicated by
232
+ * location and the repeats become a count.
233
+ */
234
+ export interface CorruptFrameReport {
235
+ log: string;
236
+ logId?: number;
237
+ position?: number;
238
+ /** Intact entries followed the break, so entries were lost rather than merely truncated. */
239
+ midLog: boolean;
240
+ /** Extent of the unreadable region, when the engine reports it; 0 when nothing valid follows it. */
241
+ unreadableBytes: number;
242
+ firstSeen: number;
243
+ lastSeen: number;
244
+ occurrences: number;
245
+ }
246
+
247
+ /** Distinct break sites retained. One physical corruption yields one site, so this is generous. */
248
+ export const MAX_CORRUPT_FRAME_REPORTS = 256;
249
+
250
+ // Re-encounters move a site to the end, so the first key is the least recently seen. Full means
251
+ // evicting that one rather than refusing a new site, which could not then be deduplicated.
252
+ const corruptFrameReports = new Map<string, CorruptFrameReport>();
253
+ let evictedCorruptFrameReports = 0;
254
+
255
+ /**
256
+ * Every corrupt frame seen by this worker. This is not yet consumed by cluster/health status;
257
+ * callers must aggregate it across worker threads before exposing a node-wide signal.
258
+ *
259
+ * Mid-log breaks are immediately logged at error level even without that consumer.
260
+ */
261
+ export function getCorruptFrameReports(): CorruptFrameReport[] {
262
+ return [...corruptFrameReports.values()];
263
+ }
264
+
265
+ /** Break sites evicted because {@link MAX_CORRUPT_FRAME_REPORTS} was reached. */
266
+ export function getEvictedCorruptFrameReportCount(): number {
267
+ return evictedCorruptFrameReports;
268
+ }
269
+
270
+ export function clearCorruptFrameReports() {
271
+ corruptFrameReports.clear();
272
+ evictedCorruptFrameReports = 0;
273
+ }
274
+
275
+ // `logId`/`position` are absent on any rocksdb-js predating the resync support, and every break on
276
+ // a stream would then collapse onto one key — folding genuinely different corruptions into a single
277
+ // count that only ever logs once. The message carries the offset and file in text, so it separates
278
+ // them when the fields can't.
279
+ function corruptFrameKey(logName: string, error: CorruptFrameError): string {
280
+ const { logId, position } = error;
281
+ return logId != null && position != null
282
+ ? `${logName}\u0000${logId}:${position}`
283
+ : `${logName}\u0000${error.message}`;
284
+ }
285
+
286
+ /**
287
+ * Records a corrupt frame and logs it once per distinct break.
288
+ *
289
+ * A mid-log break is an `error`, not a `warn`: entries after it were acknowledged and are now
290
+ * quarantined behind it. Severity follows the break's own shape, never this pass's outcome — every
291
+ * pass stops, so keying on that would report the #2063 signature with the benign torn-tail `warn`.
292
+ * A site first seen as a torn tail can escalate later, since only a rocksdb-js that reports
293
+ * `resyncPosition` can tell the two apart.
294
+ */
295
+ export function createCorruptFrameReporter(logger: {
296
+ warn: (message: string, error?: unknown) => void;
297
+ error: (message: string, error?: unknown) => void;
298
+ }) {
299
+ return (logName: string) => (error: CorruptFrameError) => {
300
+ const midLog = isMidLogBreak(error);
301
+ const unreadableBytes = error.unreadableBytes ?? 0;
302
+ const now = Date.now();
303
+ const key = corruptFrameKey(logName, error);
304
+ const existing = corruptFrameReports.get(key);
305
+ if (existing) {
306
+ corruptFrameReports.delete(key);
307
+ corruptFrameReports.set(key, existing);
308
+ existing.occurrences++;
309
+ existing.lastSeen = now;
310
+ if (!midLog || existing.midLog) return;
311
+ existing.midLog = true;
312
+ existing.unreadableBytes = unreadableBytes;
313
+ } else {
314
+ if (corruptFrameReports.size >= MAX_CORRUPT_FRAME_REPORTS) {
315
+ corruptFrameReports.delete(corruptFrameReports.keys().next().value);
316
+ evictedCorruptFrameReports++;
317
+ }
318
+ corruptFrameReports.set(key, {
319
+ log: logName,
320
+ logId: error.logId ?? undefined,
321
+ position: error.position ?? undefined,
322
+ midLog,
323
+ unreadableBytes,
324
+ firstSeen: now,
325
+ lastSeen: now,
326
+ occurrences: 1,
327
+ });
328
+ }
329
+ if (midLog) {
330
+ logger.error(
331
+ `Corrupt entry in transaction log "${logName}"; ${unreadableBytes} byte(s) are unreadable and the entries within them are lost. ` +
332
+ 'Intact entries follow the break, but reading stops there rather than skipping the frame, which could tear a source transaction: ' +
333
+ 'they are quarantined until the log is repaired or this node is re-cloned, and they are neither replayed nor replicated meanwhile.',
334
+ error
335
+ );
336
+ } else {
337
+ logger.warn(`Stopping transaction log "${logName}" at a corrupt entry during replay`, error);
338
+ }
339
+ };
340
+ }
@@ -1,11 +1,12 @@
1
1
  import { ClientError, IndexRebuildingError, Violation } from '../utility/errors/hdbError.ts';
2
2
  import { OVERFLOW_MARKER, MAX_SEARCH_KEY_LENGTH, SEARCH_TYPES } from '../utility/lmdb/terms.ts';
3
3
  import { compareKeys, MAXIMUM_KEY, writeKey } from 'ordered-binary';
4
- import { SKIP } from '@harperfast/extended-iterable';
4
+ import { SKIP, ExtendedIterable } from '@harperfast/extended-iterable';
5
5
  import { INVALIDATED, EVICTED, freezeRecord } from './Table.ts';
6
6
  import type { DirectCondition, Id } from './ResourceInterface.ts';
7
7
  import { RequestTarget } from './RequestTarget.ts';
8
8
  import { lastMetadata } from './RecordEncoder.ts';
9
+ import { writeKeyId } from './DatabaseTransaction.ts';
9
10
  import { recordAction } from './analytics/write';
10
11
  import { RocksDatabase } from '@harperfast/rocksdb-js';
11
12
 
@@ -232,6 +233,41 @@ function composeRecordFilter(recordFilters, table, context): (primaryKey: Id) =>
232
233
  };
233
234
  }
234
235
 
236
+ /**
237
+ * A record's index entries are not adjacent — the composite `[indexedValue, primaryKey]` key sorts
238
+ * on the indexed value first — so comparing neighbours cannot collapse them and the scan has to
239
+ * remember what it already yielded (#2434).
240
+ *
241
+ * Heap cost is one key per distinct record in the scanned range, held for the life of the scan, and
242
+ * a page window does not cap it: sibling-condition and row filters run downstream, so a selective
243
+ * filter keeps the scan running and the set growing while the page fills. On a multi-million-row
244
+ * range that is hundreds of MB where main streamed at O(1). Accepted for #2434 over the bounded
245
+ * alternative, which costs a primary read per index entry.
246
+ */
247
+ function distinctRecords(entries: any): AsyncIterable<Id> {
248
+ const distinct = new ExtendedIterable();
249
+ (distinct as any).iterate = (options) => {
250
+ // A non-scalar key decodes to a fresh instance per entry, so identity never matches it;
251
+ // `writeKeyId` is the store's own key equality. Its bytes get their own set because a scalar
252
+ // string id can legitimately carry exactly those bytes, and one set would fold the two —
253
+ // the collision `flattenKey` has, and the reason Table.ts prefixes its key ids.
254
+ const yieldedKeys = new Set();
255
+ const yieldedEncodedKeys = new Set();
256
+ // map rather than filter: filter clears `continueOnRecoverableError` for everything below it
257
+ return entries
258
+ .map((primaryKey) => {
259
+ const isEncoded = typeof primaryKey === 'object' && primaryKey !== null;
260
+ const yielded = isEncoded ? yieldedEncodedKeys : yieldedKeys;
261
+ const identity = isEncoded ? writeKeyId(primaryKey) : primaryKey;
262
+ if (yielded.has(identity)) return SKIP;
263
+ yielded.add(identity);
264
+ return primaryKey;
265
+ })
266
+ .iterate(options);
267
+ };
268
+ return distinct as any;
269
+ }
270
+
235
271
  /**
236
272
  * Search for records or keys, based on the search condition, using an index if available
237
273
  * @param searchCondition
@@ -261,6 +297,9 @@ export function searchByIndex(
261
297
  throw new ClientError(`Search condition for ${attribute_name} must have a value`);
262
298
  }
263
299
  let needFullScan;
300
+ // `[indexedValue, primaryKey]` is unique, so a scan that stays inside one indexed value cannot
301
+ // reach a record twice and skips the collapse below
302
+ let scansOneIndexedValue;
264
303
  if (Array.isArray(attribute_name)) {
265
304
  const firstAttributeName = attribute_name[0];
266
305
  // get the potential relationship attribute
@@ -388,6 +427,7 @@ export function searchByIndex(
388
427
  start = value;
389
428
  end = value;
390
429
  inclusiveEnd = true;
430
+ scansOneIndexedValue = true;
391
431
  break;
392
432
  case 'in':
393
433
  // Phase 1: route through filter — index-merge optimization is a Phase 2 follow-up.
@@ -531,7 +571,7 @@ export function searchByIndex(
531
571
  }
532
572
  return loaded;
533
573
  }
534
- return index.getRange(rangeOptions).map(
574
+ const scanned = index.getRange(rangeOptions).map(
535
575
  filter
536
576
  ? function ({ key, value }) {
537
577
  let recordMatcher: any;
@@ -554,6 +594,14 @@ export function searchByIndex(
554
594
  }
555
595
  : ({ value }) => value
556
596
  );
597
+ // Collapsing AFTER the map is required, not incidental: the condition's own filter tests one
598
+ // element at a time, so a record whose first entry fails it and whose second passes must
599
+ // still be reached. Collapsing first would settle that record on the failing entry and drop
600
+ // it. Scoped to attributes the schema declares multi-valued; a runtime array under an
601
+ // undeclared attribute repeats the same way but stays outside this boundary.
602
+ return scansOneIndexedValue || !findAttribute(Table.attributes, attribute_name)?.elements
603
+ ? scanned
604
+ : distinctRecords(scanned);
557
605
  } else {
558
606
  return Table.primaryStore
559
607
  .getRange(reverse ? { end: true, transaction, reverse: true } : { start: true, transaction })
@@ -1055,7 +1103,7 @@ export function filterByType(searchCondition, Table, context, filtered, isPrimar
1055
1103
  canUseIndex =
1056
1104
  canUseIndex && // is it a comparator that makes sense to use index
1057
1105
  !isPrimaryKey && // no need to use index for primary keys, since we will be iterating over the primary keys
1058
- !!(Table && usableIndex(Table, attribute)) && // is there a usable (complete) index for this attribute
1106
+ Table?.indices[attribute] && // is there an index for this attribute
1059
1107
  estimatedIncomingCount > 3; // do we have a valid estimate of multiple incoming records (that is worth using an index for)
1060
1108
  if (canUseIndex) {
1061
1109
  if (searchCondition.estimated_count == undefined) estimateCondition(Table)(searchCondition);
@@ -1112,26 +1160,89 @@ export function filterByType(searchCondition, Table, context, filtered, isPrimar
1112
1160
  }
1113
1161
  }
1114
1162
 
1115
- /** The index a condition can be driven by; searchByIndex refuses a rebuilding one, so it reads as absent. */
1116
- function usableIndex(table, attributeName): any {
1117
- const index = attributeName == null ? undefined : table.indices[attributeName];
1118
- return index?.isIndexing ? undefined : index;
1119
- }
1120
-
1121
1163
  /**
1122
- * True when an index this attribute would have to be driven by is still being built, following a
1123
- * relationship path to the local join index and on to the related table's leaf index.
1164
+ * Estimates a range comparator's entry count from the store's statistical estimate, blended with
1165
+ * the table-fraction heuristic by the estimate's confidence so low-confidence estimates degrade
1166
+ * to the old behavior. Returns undefined (caller falls back to the heuristic) when the store
1167
+ * cannot estimate, the shape is unexpected, or the executed range wouldn't match this one.
1124
1168
  */
1125
- function drivesRebuildingIndex(table, attributeName, relationshipOffset = 0): boolean {
1126
- if (!Array.isArray(attributeName))
1127
- return attributeName != null && attributeName !== table.primaryKey && !!table.indices[attributeName]?.isIndexing;
1128
- if (relationshipOffset >= attributeName.length - 1)
1129
- return drivesRebuildingIndex(table, attributeName[relationshipOffset]);
1130
- const attribute = findAttribute(table.attributes, attributeName[relationshipOffset]);
1131
- if (!attribute) return false;
1132
- if (table.indices[attribute.relationship?.from]?.isIndexing) return true;
1133
- const relatedTable = attribute.definition?.tableClass || attribute.elements?.definition?.tableClass;
1134
- return !!relatedTable && drivesRebuildingIndex(relatedTable, attributeName, relationshipOffset + 1);
1169
+ function estimateRangeCondition(table, condition, searchType, fraction) {
1170
+ const attributeName = condition[0] ?? condition.attribute;
1171
+ const isPrimaryKey = attributeName === table.primaryKey;
1172
+ const store = isPrimaryKey ? table.primaryStore : table.indices[attributeName];
1173
+ // LMDB-backed and custom index stores do not implement estimateCount
1174
+ if (typeof store?.estimateCount !== 'function') return undefined;
1175
+ let value = condition[1] ?? condition.value;
1176
+ if (value instanceof Date) value = value.getTime();
1177
+ let range;
1178
+ switch (searchType) {
1179
+ case 'lt':
1180
+ // `start: true` mirrors searchByIndex: `true` sorts above `null`, so an indexNulls
1181
+ // index's `[null, primaryKey]` entries are outside the executed range
1182
+ range = { start: true, end: value };
1183
+ break;
1184
+ case 'le':
1185
+ range = { start: true, end: value, inclusiveEnd: true };
1186
+ break;
1187
+ case 'gt':
1188
+ range = { start: value, exclusiveStart: true };
1189
+ break;
1190
+ case 'ge':
1191
+ range = { start: value };
1192
+ break;
1193
+ case 'between':
1194
+ case 'gele':
1195
+ case 'gelt':
1196
+ case 'gtlt':
1197
+ case 'gtle': {
1198
+ if (!Array.isArray(value)) return undefined;
1199
+ let [start, end] = value;
1200
+ if (start instanceof Date) start = start.getTime();
1201
+ if (end instanceof Date) end = end.getTime();
1202
+ range = {
1203
+ start,
1204
+ end,
1205
+ inclusiveEnd: searchType === 'between' || searchType === 'gele' || searchType === 'gtle',
1206
+ exclusiveStart: searchType === 'gtlt' || searchType === 'gtle',
1207
+ };
1208
+ break;
1209
+ }
1210
+ case 'starts_with': {
1211
+ const prefix = value?.toString();
1212
+ if (!prefix) return undefined;
1213
+ range = { start: prefix, end: getStringPrefixUpperBound(prefix) };
1214
+ break;
1215
+ }
1216
+ case 'prefix': {
1217
+ let start = Array.isArray(value) ? value : [value, null];
1218
+ if (start[start.length - 1] != null) start = start.concat(null);
1219
+ const end = start.slice(0);
1220
+ end[end.length - 1] = MAXIMUM_KEY;
1221
+ range = { start, end };
1222
+ break;
1223
+ }
1224
+ default:
1225
+ return undefined;
1226
+ }
1227
+ // Long string bounds get truncated + filtered at execution (searchByIndex), so the
1228
+ // executed range is wider than this one; don't estimate what won't be iterated.
1229
+ if (
1230
+ (typeof range.start === 'string' && range.start.length > MAX_SEARCH_KEY_LENGTH) ||
1231
+ (typeof range.end === 'string' && range.end.length > MAX_SEARCH_KEY_LENGTH)
1232
+ ) {
1233
+ return undefined;
1234
+ }
1235
+ let count, confidence;
1236
+ try {
1237
+ ({ count, confidence } = store.estimateCount(range) ?? {});
1238
+ } catch {
1239
+ // a concurrently closing/dropped store must degrade the plan, not fail the query
1240
+ return undefined;
1241
+ }
1242
+ if (!Number.isFinite(count) || count < 0 || !Number.isFinite(confidence) || confidence < 0 || confidence > 1)
1243
+ return undefined;
1244
+ const heuristic = fraction * estimatedEntryCount(table.primaryStore) + 1;
1245
+ return Math.max(1, Math.round(confidence * count + (1 - confidence) * heuristic));
1135
1246
  }
1136
1247
 
1137
1248
  export function estimateCondition(table) {
@@ -1163,8 +1274,12 @@ export function estimateCondition(table) {
1163
1274
  // skip if it is cached
1164
1275
  let searchType = condition.comparator || condition.search_type;
1165
1276
  searchType = ALTERNATE_COMPARATOR_NAMES[searchType] || searchType;
1166
- // before comparator dispatch: several branches fall back to a finite table-fraction heuristic
1167
- if (drivesRebuildingIndex(table, condition[0] ?? condition.attribute)) {
1277
+ if (condition.negated) {
1278
+ // a negated condition always executes as a full scan (searchByIndex forces
1279
+ // needFullScan), so follow the filter-only convention used by contains/ends_with:
1280
+ // estimate Infinity so its positive-range estimate can never win the driving-condition
1281
+ // ordering. Short-circuit here rather than computing (and discarding) a positive-range
1282
+ // estimate that would cross the native FFI boundary for nothing.
1168
1283
  condition.estimated_count = Infinity;
1169
1284
  } else if (searchType === SEARCH_TYPES.EQUALS || !searchType) {
1170
1285
  const attribute_name = condition[0] ?? condition.attribute;
@@ -1192,12 +1307,12 @@ export function estimateCondition(table) {
1192
1307
  }
1193
1308
  } else {
1194
1309
  // we only attempt to estimate count on equals operator because that's really all that LMDB supports (some other key-value stores like libmdbx could be considered if we need to do estimated counts of ranges at some point)
1195
- const index = usableIndex(table, attribute_name);
1310
+ const index = table.indices[attribute_name];
1196
1311
  condition.estimated_count = index ? index.getValuesCount(condition[1] ?? condition.value) : Infinity;
1197
1312
  }
1198
1313
  } else if (searchType === 'contains' || searchType === 'ends_with' || searchType === 'ne') {
1199
1314
  const attribute_name = condition[0] ?? condition.attribute;
1200
- const index = usableIndex(table, attribute_name);
1315
+ const index = table.indices[attribute_name];
1201
1316
  if (condition.value === null && searchType === 'ne') {
1202
1317
  condition.estimated_count = Math.max(
1203
1318
  estimatedEntryCount(table.primaryStore) - (index ? index.getValuesCount(null) : 0),
@@ -1206,7 +1321,7 @@ export function estimateCondition(table) {
1206
1321
  } else condition.estimated_count = Infinity;
1207
1322
  } else if (searchType === 'in') {
1208
1323
  const attribute_name = condition[0] ?? condition.attribute;
1209
- const index = usableIndex(table, attribute_name);
1324
+ const index = table.indices[attribute_name];
1210
1325
  if (Array.isArray(condition.value) && index) {
1211
1326
  // Sum of per-value matches (over-counts duplicates but is a fine ceiling)
1212
1327
  let estimate = 0;
@@ -1217,14 +1332,17 @@ export function estimateCondition(table) {
1217
1332
  } else if (Array.isArray(condition.value)) {
1218
1333
  condition.estimated_count = Infinity;
1219
1334
  } else condition.estimated_count = Infinity;
1220
- // for range queries (betweens, startsWith, greater, etc.), just arbitrarily guess
1221
1335
  } else if (searchType === 'starts_with' || searchType === 'prefix')
1222
- condition.estimated_count = STARTS_WITH_ESTIMATE * estimatedEntryCount(table.primaryStore) + 1;
1336
+ condition.estimated_count =
1337
+ estimateRangeCondition(table, condition, searchType, STARTS_WITH_ESTIMATE) ??
1338
+ STARTS_WITH_ESTIMATE * estimatedEntryCount(table.primaryStore) + 1;
1223
1339
  else if (searchType === 'between')
1224
- condition.estimated_count = BETWEEN_ESTIMATE * estimatedEntryCount(table.primaryStore) + 1;
1340
+ condition.estimated_count =
1341
+ estimateRangeCondition(table, condition, searchType, BETWEEN_ESTIMATE) ??
1342
+ BETWEEN_ESTIMATE * estimatedEntryCount(table.primaryStore) + 1;
1225
1343
  else if (searchType === 'sort') {
1226
1344
  const attribute_name = condition[0] ?? condition.attribute;
1227
- const index = usableIndex(table, attribute_name);
1345
+ const index = table.indices[attribute_name];
1228
1346
  if (index?.customIndex?.estimateCountAsSort)
1229
1347
  // allow custom index to define its own estimation of counts
1230
1348
  condition.estimated_count = index.customIndex.estimateCountAsSort(condition);
@@ -1232,11 +1350,14 @@ export function estimateCondition(table) {
1232
1350
  } else {
1233
1351
  // for the search types that use the broadest range, try do them last
1234
1352
  const attribute_name = condition[0] ?? condition.attribute;
1235
- const index = usableIndex(table, attribute_name);
1353
+ const index = table.indices[attribute_name];
1236
1354
  if (index?.customIndex?.estimateCount)
1237
1355
  // allow custom index to define its own estimation of counts
1238
1356
  condition.estimated_count = index.customIndex.estimateCount(condition.value);
1239
- else condition.estimated_count = OPEN_RANGE_ESTIMATE * estimatedEntryCount(table.primaryStore) + 1;
1357
+ else
1358
+ condition.estimated_count =
1359
+ estimateRangeCondition(table, condition, searchType, OPEN_RANGE_ESTIMATE) ??
1360
+ OPEN_RANGE_ESTIMATE * estimatedEntryCount(table.primaryStore) + 1;
1240
1361
  }
1241
1362
  // we give a condition significantly more weight/preference if we will be ordering by it
1242
1363
  if (typeof condition.descending === 'boolean') condition.estimated_count /= 2;
@@ -1422,6 +1543,7 @@ function parseBlock(query, expectedEnd) {
1422
1543
  break;
1423
1544
  case 'group-by':
1424
1545
  recordError('group by is not implemented yet');
1546
+ break;
1425
1547
  case 'sort':
1426
1548
  query.sort = toSortObject(args);
1427
1549
  break;
@@ -95,7 +95,7 @@ export function addSubscription(table, key, listener?: (key) => any, startTime?:
95
95
  * subscription and get the initial state.
96
96
  */
97
97
  class Subscription extends IterableEventQueue {
98
- listener: (recordId: Id, auditEntry: any, localTime: number, beginTxn: boolean) => void;
98
+ listener: (recordId: Id, auditEntry: any, txnLogKey: number, beginTxn: boolean) => void;
99
99
  subscriptions: any;
100
100
  startTime?: number;
101
101
  includeDescendants?: boolean;
@@ -178,10 +178,10 @@ function notifyFromTransactionData(subscriptions, auditLogIterable?, allowYield
178
178
  }
179
179
  if (result.done) break;
180
180
  const auditRecord = result.value;
181
- const timestamp: number = auditRecord.localTime ?? auditRecord.version;
181
+ const timestamp: number = auditRecord.txnLogKey;
182
182
  subscriptions.lastTxnTime = timestamp;
183
183
  // the transaction extent: RocksDB entries committed together share the log key (record
184
- // versions may differ); LMDB's localTime is a per-entry audit key, so version delimits there
184
+ // versions may differ); LMDB's transaction-log key is per-entry, so version delimits there
185
185
  const txnKey = auditStore.reusableIterable ? timestamp : auditRecord.version;
186
186
  if (ACTIONS_OF_INTEREST.includes(auditRecord.type)) {
187
187
  const tableSubscriptions = subscriptions[auditRecord.tableId];