@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,13 +1,15 @@
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, getReadTransactionGuard } from './DatabaseTransaction.ts';
9
10
  import { recordAction } from './analytics/write';
10
11
  import { RocksDatabase } from '@harperfast/rocksdb-js';
12
+ import { appendHeader } from '../server/serverHelpers/Headers.ts';
11
13
 
12
14
  // these are ratios/percentages of overall table size
13
15
  const OPEN_RANGE_ESTIMATE = 0.3;
@@ -140,16 +142,13 @@ export function executeConditions(
140
142
  filtered
141
143
  // recordAccess intentionally omitted: guards run once, at the top level (see above).
142
144
  );
143
- return searchByIndex(
144
- condition,
145
- txn,
146
- condition.descending || request.reverse === true,
147
- table,
148
- request.allowFullScan,
145
+ return searchByIndex(condition, txn, condition.descending || request.reverse === true, table, {
146
+ allowFullScan: request.allowFullScan,
149
147
  filtered,
150
148
  context,
151
- request.limit !== undefined ? (request.offset || 0) + request.limit : undefined
152
- );
149
+ minResults:
150
+ request.limit === 0 ? 0 : request.limit !== undefined ? (request.offset || 0) + request.limit : undefined,
151
+ });
153
152
  }
154
153
  function mapConditionsToFilters(conditions, intersection, estimatedIncomingCount) {
155
154
  return conditions
@@ -233,27 +232,70 @@ function composeRecordFilter(recordFilters, table, context): (primaryKey: Id) =>
233
232
  }
234
233
 
235
234
  /**
236
- * Search for records or keys, based on the search condition, using an index if available
235
+ * A record's index entries are not adjacent — the composite `[indexedValue, primaryKey]` key sorts
236
+ * on the indexed value first — so comparing neighbours cannot collapse them and the scan has to
237
+ * remember what it already yielded (#2434).
238
+ *
239
+ * Heap cost is one key per distinct record in the scanned range, held for the life of the scan, and
240
+ * a page window does not cap it: sibling-condition and row filters run downstream, so a selective
241
+ * filter keeps the scan running and the set growing while the page fills. On a multi-million-row
242
+ * range that is hundreds of MB where main streamed at O(1). Accepted for #2434 over the bounded
243
+ * alternative, which costs a primary read per index entry.
244
+ */
245
+ function distinctRecords(entries: any): AsyncIterable<Id> {
246
+ const distinct = new ExtendedIterable();
247
+ (distinct as any).iterate = (options) => {
248
+ // A non-scalar key decodes to a fresh instance per entry, so identity never matches it;
249
+ // `writeKeyId` is the store's own key equality. Its bytes get their own set because a scalar
250
+ // string id can legitimately carry exactly those bytes, and one set would fold the two —
251
+ // the collision `flattenKey` has, and the reason Table.ts prefixes its key ids.
252
+ const yieldedKeys = new Set();
253
+ const yieldedEncodedKeys = new Set();
254
+ // map rather than filter: filter clears `continueOnRecoverableError` for everything below it
255
+ return entries
256
+ .map((primaryKey) => {
257
+ const isEncoded = typeof primaryKey === 'object' && primaryKey !== null;
258
+ const yielded = isEncoded ? yieldedEncodedKeys : yieldedKeys;
259
+ const identity = isEncoded ? writeKeyId(primaryKey) : primaryKey;
260
+ if (yielded.has(identity)) return SKIP;
261
+ yielded.add(identity);
262
+ return primaryKey;
263
+ })
264
+ .iterate(options);
265
+ };
266
+ return distinct as any;
267
+ }
268
+
269
+ /**
270
+ * Search for records or keys, based on the search condition, using an index if available. The four
271
+ * leading parameters are required at every call site; everything optional is named, so a new
272
+ * capability can be added without shifting positions at callers that don't use it.
237
273
  * @param searchCondition
238
274
  * @param transaction
239
275
  * @param reverse
240
276
  * @param Table
241
- * @param allowFullScan
242
- * @param filtered
277
+ * @param options
243
278
  */
244
279
  export function searchByIndex(
245
280
  searchCondition: DirectCondition,
246
281
  transaction: any,
247
282
  reverse: boolean,
248
283
  Table: any,
249
- allowFullScan?: boolean,
250
- filtered?: any,
251
- context?: any,
252
- // How many rows the query will ultimately consume (offset + limit), when it is bounded. An
253
- // approximate index returns a fixed-size candidate list, so without this a query asking for more
254
- // rows than that list holds silently gets a short result set. Only custom indexes read it.
255
- minResults?: number
284
+ options: {
285
+ allowFullScan?: boolean;
286
+ filtered?: any;
287
+ context?: any;
288
+ // How many rows the query will ultimately consume (offset + limit), when it is bounded. An
289
+ // approximate index returns a fixed-size candidate list, so without this a query asking for more
290
+ // rows than that list holds silently gets a short result set. Only custom indexes read it.
291
+ minResults?: number;
292
+ } = {}
256
293
  ): AsyncIterable<Id | { key: Id; value: any }> {
294
+ // A stale positional caller passes `allowFullScan` here. Type checking only covers .ts callers, so
295
+ // fail loud rather than silently reading every option as undefined.
296
+ if (typeof options !== 'object' || options === null)
297
+ throw new TypeError('searchByIndex: the 5th argument is an options object (#2165), not a positional value');
298
+ const { allowFullScan, filtered, context, minResults } = options;
257
299
  let attribute_name = searchCondition[0] ?? searchCondition.attribute;
258
300
  let value = searchCondition[1] ?? searchCondition.value;
259
301
  const comparator = searchCondition.comparator;
@@ -261,6 +303,9 @@ export function searchByIndex(
261
303
  throw new ClientError(`Search condition for ${attribute_name} must have a value`);
262
304
  }
263
305
  let needFullScan;
306
+ // `[indexedValue, primaryKey]` is unique, so a scan that stays inside one indexed value cannot
307
+ // reach a record twice and skips the collapse below
308
+ let scansOneIndexedValue;
264
309
  if (Array.isArray(attribute_name)) {
265
310
  const firstAttributeName = attribute_name[0];
266
311
  // get the potential relationship attribute
@@ -284,8 +329,7 @@ export function searchByIndex(
284
329
  transaction,
285
330
  reverse,
286
331
  relatedTable,
287
- allowFullScan,
288
- joined
332
+ { allowFullScan, filtered: joined }
289
333
  );
290
334
  if (attribute.relationship.to) {
291
335
  // this is one-to-many or many-to-many, so we need to track the filtering of related entries that match
@@ -302,8 +346,7 @@ export function searchByIndex(
302
346
  transaction,
303
347
  reverse,
304
348
  Table,
305
- allowFullScan,
306
- joined
349
+ { allowFullScan, filtered: joined }
307
350
  );
308
351
  };
309
352
  if (attribute.elements) {
@@ -388,6 +431,7 @@ export function searchByIndex(
388
431
  start = value;
389
432
  end = value;
390
433
  inclusiveEnd = true;
434
+ scansOneIndexedValue = true;
391
435
  break;
392
436
  case 'in':
393
437
  // Phase 1: route through filter — index-merge optimization is a Phase 2 follow-up.
@@ -510,28 +554,119 @@ export function searchByIndex(
510
554
  // exploring until it has enough MATCHING results, rather than post-filtering an under-filled
511
555
  // candidate set. Only indexes that opt in (filteredSearch) receive it; others post-filter as before.
512
556
  const recordFilter = index.customIndex.filteredSearch ? searchCondition.recordFilter : undefined;
513
- const loaded = index.customIndex.search(searchCondition, context, recordFilter, minResults).map((entry) => {
514
- // if the custom index returns an entry with metadata, merge it with the loaded entry
515
- if (typeof entry === 'object' && entry) {
516
- const { key, ...otherProps } = entry;
517
- if (key == null) return SKIP; // primaryKey missing from HNSW node — skip rather than crash
518
- const loadedEntry = Table.primaryStore.getEntry(key, {
519
- transaction: context && Table._readTxnForContext(context),
520
- });
521
- if (!loadedEntry) return SKIP; // record was deleted/expired or not yet visible
522
- freezeRecord(loadedEntry?.value);
523
- recordRead(loadedEntry);
524
- return { ...otherProps, ...loadedEntry };
525
- }
526
- return entry;
557
+ const waiting = index.customIndex.filePrimary && searchCondition.waitForIndexMilliseconds > 0;
558
+ const start = waiting ? Promise.withResolvers<void>() : undefined;
559
+ const controller = waiting ? new AbortController() : undefined;
560
+ const checkActive = waiting ? getReadTransactionGuard(transaction) : undefined;
561
+ const signal = waiting
562
+ ? context.signal
563
+ ? AbortSignal.any([context.signal, controller.signal])
564
+ : controller.signal
565
+ : undefined;
566
+ const searchContext = waiting
567
+ ? Object.create(context, {
568
+ signal: { value: signal },
569
+ indexSearchStart: { value: start.promise.then(() => checkActive?.()) },
570
+ })
571
+ : context;
572
+ const searched = index.customIndex.search(searchCondition, searchContext, {
573
+ filter:
574
+ waiting && recordFilter
575
+ ? (id) => {
576
+ if (signal.aborted) return false;
577
+ checkActive?.();
578
+ return recordFilter(id);
579
+ }
580
+ : recordFilter,
581
+ minResults,
527
582
  });
528
- if (index.customIndex.rescoreResults) {
529
- const rescored = index.customIndex.rescoreResults(loaded, searchCondition, comparator, attribute_name);
530
- if (rescored != null) return rescored as any;
583
+ const coverage = (searched as any).indexCoverage;
584
+ if (!waiting && coverage && context?.responseHeaders) {
585
+ appendHeader(
586
+ context.responseHeaders,
587
+ 'Harper-Index-Coverage',
588
+ `${coverage.state}; lag=${coverage.lagUpperBoundMilliseconds}; tolerance=${coverage.maxLagMilliseconds}`,
589
+ true
590
+ );
591
+ appendHeader(context.responseHeaders, 'Access-Control-Expose-Headers', 'Harper-Index-Coverage', true);
531
592
  }
532
- return loaded;
593
+ const processEntries = (entries: any[]) => {
594
+ const loaded = entries
595
+ .map((entry) => {
596
+ // if the custom index returns an entry with metadata, merge it with the loaded entry
597
+ if (typeof entry === 'object' && entry) {
598
+ const { key, ...otherProps } = entry;
599
+ if (key == null) return SKIP; // primaryKey missing from HNSW node — skip rather than crash
600
+ const loadedEntry = Table.primaryStore.getEntry(key, {
601
+ transaction: waiting ? transaction : context && Table._readTxnForContext(context),
602
+ });
603
+ if (!loadedEntry) return SKIP; // record was deleted/expired or not yet visible
604
+ freezeRecord(loadedEntry?.value);
605
+ recordRead(loadedEntry);
606
+ return { ...otherProps, ...loadedEntry };
607
+ }
608
+ return entry;
609
+ })
610
+ .filter((entry) => entry !== SKIP);
611
+ if (index.customIndex.rescoreResults) {
612
+ const rescored = index.customIndex.rescoreResults(loaded, searchCondition, comparator, attribute_name);
613
+ if (rescored != null) return rescored as any;
614
+ }
615
+ return loaded;
616
+ };
617
+ if (typeof (searched as any)?.then === 'function') {
618
+ let settled = false;
619
+ const pending = waiting
620
+ ? (searched as Promise<any[]>)
621
+ .then((entries) => {
622
+ if (controller.signal.aborted) return [];
623
+ if (signal.aborted) throw signal.reason ?? new Error('Index search aborted', { cause: signal.reason });
624
+ checkActive?.();
625
+ return processEntries(entries);
626
+ })
627
+ .finally(() => {
628
+ settled = true;
629
+ })
630
+ : (searched as Promise<any[]>).then(processEntries);
631
+ // A consumer may abandon this lazy iterable without calling next().
632
+ pending.catch(() => {});
633
+ const results: any = new ExtendedIterable();
634
+ results.iterate = (options?: { async?: boolean }) => {
635
+ if (!options?.async) {
636
+ throw new Error(
637
+ 'This index resolves search results asynchronously; the results must be consumed with async iteration'
638
+ );
639
+ }
640
+ // Overlapping next() calls share one cursor and cannot duplicate its first entry.
641
+ const iteratorPromise = pending.then((entries) => entries[Symbol.iterator]());
642
+ iteratorPromise.catch(() => {});
643
+ let closed = false;
644
+ return {
645
+ next() {
646
+ if (closed) return Promise.resolve({ done: true, value: undefined });
647
+ start?.resolve();
648
+ return iteratorPromise.then((inner) => {
649
+ if (closed) return { done: true, value: undefined };
650
+ checkActive?.();
651
+ return inner.next();
652
+ });
653
+ },
654
+ return(value?: any) {
655
+ closed = true;
656
+ if (!settled) controller?.abort();
657
+ iteratorPromise.then(
658
+ (inner) => (inner as any).return?.(value),
659
+ () => {}
660
+ );
661
+ return Promise.resolve({ done: true, value });
662
+ },
663
+ };
664
+ };
665
+ return results;
666
+ }
667
+ return processEntries(searched);
533
668
  }
534
- return index.getRange(rangeOptions).map(
669
+ const scanned = index.getRange(rangeOptions).map(
535
670
  filter
536
671
  ? function ({ key, value }) {
537
672
  let recordMatcher: any;
@@ -554,6 +689,14 @@ export function searchByIndex(
554
689
  }
555
690
  : ({ value }) => value
556
691
  );
692
+ // Collapsing AFTER the map is required, not incidental: the condition's own filter tests one
693
+ // element at a time, so a record whose first entry fails it and whose second passes must
694
+ // still be reached. Collapsing first would settle that record on the failing entry and drop
695
+ // it. Scoped to attributes the schema declares multi-valued; a runtime array under an
696
+ // undeclared attribute repeats the same way but stays outside this boundary.
697
+ return scansOneIndexedValue || !findAttribute(Table.attributes, attribute_name)?.elements
698
+ ? scanned
699
+ : distinctRecords(scanned);
557
700
  } else {
558
701
  return Table.primaryStore
559
702
  .getRange(reverse ? { end: true, transaction, reverse: true } : { start: true, transaction })
@@ -1112,6 +1255,91 @@ export function filterByType(searchCondition, Table, context, filtered, isPrimar
1112
1255
  }
1113
1256
  }
1114
1257
 
1258
+ /**
1259
+ * Estimates a range comparator's entry count from the store's statistical estimate, blended with
1260
+ * the table-fraction heuristic by the estimate's confidence so low-confidence estimates degrade
1261
+ * to the old behavior. Returns undefined (caller falls back to the heuristic) when the store
1262
+ * cannot estimate, the shape is unexpected, or the executed range wouldn't match this one.
1263
+ */
1264
+ function estimateRangeCondition(table, condition, searchType, fraction) {
1265
+ const attributeName = condition[0] ?? condition.attribute;
1266
+ const isPrimaryKey = attributeName === table.primaryKey;
1267
+ const store = isPrimaryKey ? table.primaryStore : usableIndex(table, attributeName);
1268
+ // LMDB-backed and custom index stores do not implement estimateCount
1269
+ if (typeof store?.estimateCount !== 'function') return undefined;
1270
+ let value = condition[1] ?? condition.value;
1271
+ if (value instanceof Date) value = value.getTime();
1272
+ let range;
1273
+ switch (searchType) {
1274
+ case 'lt':
1275
+ // `start: true` mirrors searchByIndex: `true` sorts above `null`, so an indexNulls
1276
+ // index's `[null, primaryKey]` entries are outside the executed range
1277
+ range = { start: true, end: value };
1278
+ break;
1279
+ case 'le':
1280
+ range = { start: true, end: value, inclusiveEnd: true };
1281
+ break;
1282
+ case 'gt':
1283
+ range = { start: value, exclusiveStart: true };
1284
+ break;
1285
+ case 'ge':
1286
+ range = { start: value };
1287
+ break;
1288
+ case 'between':
1289
+ case 'gele':
1290
+ case 'gelt':
1291
+ case 'gtlt':
1292
+ case 'gtle': {
1293
+ if (!Array.isArray(value)) return undefined;
1294
+ let [start, end] = value;
1295
+ if (start instanceof Date) start = start.getTime();
1296
+ if (end instanceof Date) end = end.getTime();
1297
+ range = {
1298
+ start,
1299
+ end,
1300
+ inclusiveEnd: searchType === 'between' || searchType === 'gele' || searchType === 'gtle',
1301
+ exclusiveStart: searchType === 'gtlt' || searchType === 'gtle',
1302
+ };
1303
+ break;
1304
+ }
1305
+ case 'starts_with': {
1306
+ const prefix = value?.toString();
1307
+ if (!prefix) return undefined;
1308
+ range = { start: prefix, end: getStringPrefixUpperBound(prefix) };
1309
+ break;
1310
+ }
1311
+ case 'prefix': {
1312
+ let start = Array.isArray(value) ? value : [value, null];
1313
+ if (start[start.length - 1] != null) start = start.concat(null);
1314
+ const end = start.slice(0);
1315
+ end[end.length - 1] = MAXIMUM_KEY;
1316
+ range = { start, end };
1317
+ break;
1318
+ }
1319
+ default:
1320
+ return undefined;
1321
+ }
1322
+ // Long string bounds get truncated + filtered at execution (searchByIndex), so the
1323
+ // executed range is wider than this one; don't estimate what won't be iterated.
1324
+ if (
1325
+ (typeof range.start === 'string' && range.start.length > MAX_SEARCH_KEY_LENGTH) ||
1326
+ (typeof range.end === 'string' && range.end.length > MAX_SEARCH_KEY_LENGTH)
1327
+ ) {
1328
+ return undefined;
1329
+ }
1330
+ let count, confidence;
1331
+ try {
1332
+ ({ count, confidence } = store.estimateCount(range) ?? {});
1333
+ } catch {
1334
+ // a concurrently closing/dropped store must degrade the plan, not fail the query
1335
+ return undefined;
1336
+ }
1337
+ if (!Number.isFinite(count) || count < 0 || !Number.isFinite(confidence) || confidence < 0 || confidence > 1)
1338
+ return undefined;
1339
+ const heuristic = fraction * estimatedEntryCount(table.primaryStore) + 1;
1340
+ return Math.max(1, Math.round(confidence * count + (1 - confidence) * heuristic));
1341
+ }
1342
+
1115
1343
  /** The index a condition can be driven by; searchByIndex refuses a rebuilding one, so it reads as absent. */
1116
1344
  function usableIndex(table, attributeName): any {
1117
1345
  const index = attributeName == null ? undefined : table.indices[attributeName];
@@ -1166,6 +1394,13 @@ export function estimateCondition(table) {
1166
1394
  // before comparator dispatch: several branches fall back to a finite table-fraction heuristic
1167
1395
  if (drivesRebuildingIndex(table, condition[0] ?? condition.attribute)) {
1168
1396
  condition.estimated_count = Infinity;
1397
+ } else if (condition.negated) {
1398
+ // a negated condition always executes as a full scan (searchByIndex forces
1399
+ // needFullScan), so follow the filter-only convention used by contains/ends_with:
1400
+ // estimate Infinity so its positive-range estimate can never win the driving-condition
1401
+ // ordering. Short-circuit here rather than computing (and discarding) a positive-range
1402
+ // estimate that would cross the native FFI boundary for nothing.
1403
+ condition.estimated_count = Infinity;
1169
1404
  } else if (searchType === SEARCH_TYPES.EQUALS || !searchType) {
1170
1405
  const attribute_name = condition[0] ?? condition.attribute;
1171
1406
  if (attribute_name == null || attribute_name === table.primaryKey) condition.estimated_count = 1;
@@ -1217,11 +1452,14 @@ export function estimateCondition(table) {
1217
1452
  } else if (Array.isArray(condition.value)) {
1218
1453
  condition.estimated_count = Infinity;
1219
1454
  } else condition.estimated_count = Infinity;
1220
- // for range queries (betweens, startsWith, greater, etc.), just arbitrarily guess
1221
1455
  } else if (searchType === 'starts_with' || searchType === 'prefix')
1222
- condition.estimated_count = STARTS_WITH_ESTIMATE * estimatedEntryCount(table.primaryStore) + 1;
1456
+ condition.estimated_count =
1457
+ estimateRangeCondition(table, condition, searchType, STARTS_WITH_ESTIMATE) ??
1458
+ STARTS_WITH_ESTIMATE * estimatedEntryCount(table.primaryStore) + 1;
1223
1459
  else if (searchType === 'between')
1224
- condition.estimated_count = BETWEEN_ESTIMATE * estimatedEntryCount(table.primaryStore) + 1;
1460
+ condition.estimated_count =
1461
+ estimateRangeCondition(table, condition, searchType, BETWEEN_ESTIMATE) ??
1462
+ BETWEEN_ESTIMATE * estimatedEntryCount(table.primaryStore) + 1;
1225
1463
  else if (searchType === 'sort') {
1226
1464
  const attribute_name = condition[0] ?? condition.attribute;
1227
1465
  const index = usableIndex(table, attribute_name);
@@ -1236,7 +1474,10 @@ export function estimateCondition(table) {
1236
1474
  if (index?.customIndex?.estimateCount)
1237
1475
  // allow custom index to define its own estimation of counts
1238
1476
  condition.estimated_count = index.customIndex.estimateCount(condition.value);
1239
- else condition.estimated_count = OPEN_RANGE_ESTIMATE * estimatedEntryCount(table.primaryStore) + 1;
1477
+ else
1478
+ condition.estimated_count =
1479
+ estimateRangeCondition(table, condition, searchType, OPEN_RANGE_ESTIMATE) ??
1480
+ OPEN_RANGE_ESTIMATE * estimatedEntryCount(table.primaryStore) + 1;
1240
1481
  }
1241
1482
  // we give a condition significantly more weight/preference if we will be ordering by it
1242
1483
  if (typeof condition.descending === 'boolean') condition.estimated_count /= 2;
@@ -1422,6 +1663,7 @@ function parseBlock(query, expectedEnd) {
1422
1663
  break;
1423
1664
  case 'group-by':
1424
1665
  recordError('group by is not implemented yet');
1666
+ break;
1425
1667
  case 'sort':
1426
1668
  query.sort = toSortObject(args);
1427
1669
  break;
@@ -3,7 +3,15 @@ import * as crdtOperations from './crdt.ts';
3
3
  import { Blob } from './blob.ts';
4
4
  import * as harperLogger from '../utility/logging/harper_logger.ts';
5
5
 
6
+ export const ASSERT_TRACKED_WRITABLE = Symbol('assert-tracked-writable');
7
+ export const GET_TRACKED_WRITE_GENERATION = Symbol('get-tracked-write-generation');
8
+
9
+ function assertWritable(target, generation?) {
10
+ target[ASSERT_TRACKED_WRITABLE]?.(generation);
11
+ }
12
+
6
13
  function getChanges(target) {
14
+ assertWritable(target);
7
15
  let changes = target.getChanges();
8
16
  if (!changes) {
9
17
  changes = Object.create(null);
@@ -49,6 +57,7 @@ export function assignTrackedAccessors(Target, typeDef, useFullPropertyProxy = f
49
57
  // still load), this is a user assignment to a resolved attribute, and a read-only resolver
50
58
  // (a scalar @computed has no attribute.set) has nowhere to put it (harper#2359).
51
59
  if (!attribute.set) throw new ClientError(`${name} is a computed attribute and can not be assigned`);
60
+ assertWritable(this);
52
61
  return attribute.set(this, related);
53
62
  },
54
63
  configurable: true,
@@ -188,7 +197,7 @@ export function assignTrackedAccessors(Target, typeDef, useFullPropertyProxy = f
188
197
  }
189
198
  const sourceValue = this.getRecord()?.[name];
190
199
  if (sourceValue && typeof sourceValue === 'object') {
191
- const updatedValue = trackObject(sourceValue, attribute);
200
+ const updatedValue = trackObject(sourceValue, attribute, this);
192
201
  if (updatedValue) {
193
202
  if (!changes) {
194
203
  this._setChanges((changes = Object.create(null)));
@@ -288,7 +297,7 @@ function getProxiedProperty(target, name, receiver) {
288
297
  }
289
298
  const sourceValue = receiver.getRecord?.()?.[name];
290
299
  if (sourceValue && typeof sourceValue === 'object') {
291
- const updatedValue = trackObject(sourceValue);
300
+ const updatedValue = trackObject(sourceValue, undefined, receiver);
292
301
  if (updatedValue) {
293
302
  if (!changes) {
294
303
  changes = Object.create(null);
@@ -303,6 +312,7 @@ function getProxiedProperty(target, name, receiver) {
303
312
  }
304
313
  function setProxiedProperty(target: any, name: string | symbol, value, receiver) {
305
314
  if (typeof name === 'string') {
315
+ assertWritable(receiver);
306
316
  let changes = receiver.getChanges?.();
307
317
  if (!changes) {
308
318
  changes = {};
@@ -315,7 +325,7 @@ function setProxiedProperty(target: any, name: string | symbol, value, receiver)
315
325
  return true;
316
326
  }
317
327
 
318
- function trackObject(sourceObject: any, typeDef?: any) {
328
+ function trackObject(sourceObject: any, typeDef?: any, writableOwner?: any) {
319
329
  // lazily instantiate in case of recursive structures
320
330
  let TrackedObject;
321
331
  switch (sourceObject.constructor) {
@@ -326,18 +336,41 @@ function trackObject(sourceObject: any, typeDef?: any) {
326
336
  typeDef.TrackedObject = TrackedObject = class extends GenericTrackedObject {};
327
337
  assignTrackedAccessors(TrackedObject, typeDef);
328
338
  }
329
- return new TrackedObject(sourceObject);
339
+ return new TrackedObject(sourceObject, writableOwner);
330
340
  } else {
331
- return new GenericTrackedObject(sourceObject);
341
+ return new GenericTrackedObject(sourceObject, writableOwner);
332
342
  }
333
343
  case Array:
334
- const trackedArray = new TrackedArray(sourceObject.length, sourceObject);
344
+ const trackedArray = new TrackedArray(sourceObject.length, sourceObject, writableOwner);
335
345
  for (let i = 0, l = sourceObject.length; i < l; i++) {
336
346
  let element = sourceObject[i];
337
- if (element && typeof element === 'object') element = trackObject(element, typeDef?.elements);
347
+ if (element && typeof element === 'object') element = trackObject(element, typeDef?.elements, trackedArray);
338
348
  trackedArray[i] = element;
339
349
  }
340
- return trackedArray;
350
+ if (!writableOwner) return trackedArray;
351
+ return new Proxy(trackedArray, {
352
+ set(target, name, value) {
353
+ if (typeof name === 'string') {
354
+ assertWritable(target);
355
+ target[HAS_ARRAY_CHANGES] = true;
356
+ }
357
+ return Reflect.set(target, name, value, target);
358
+ },
359
+ defineProperty(target, name, descriptor) {
360
+ if (typeof name === 'string') {
361
+ assertWritable(target);
362
+ target[HAS_ARRAY_CHANGES] = true;
363
+ }
364
+ return Reflect.defineProperty(target, name, descriptor);
365
+ },
366
+ deleteProperty(target, name) {
367
+ if (typeof name === 'string') {
368
+ assertWritable(target);
369
+ target[HAS_ARRAY_CHANGES] = true;
370
+ }
371
+ return Reflect.deleteProperty(target, name);
372
+ },
373
+ });
341
374
  // any other objects (like Date) are left unchanged
342
375
  default:
343
376
  return sourceObject;
@@ -346,10 +379,20 @@ function trackObject(sourceObject: any, typeDef?: any) {
346
379
  export class GenericTrackedObject<T extends object = any> {
347
380
  #record: T;
348
381
  #changes: Partial<T>;
349
- constructor(sourceObject?: GenericTrackedObject<T> | T) {
382
+ #writableOwner: any;
383
+ #writableGeneration: any;
384
+ constructor(sourceObject?: GenericTrackedObject<T> | T, writableOwner?: any) {
350
385
  if ((sourceObject as GenericTrackedObject<T>)?.getRecord)
351
386
  throw new Error('Can not track an already tracked object, check for circular references');
352
387
  this.#record = sourceObject as any;
388
+ this.#writableOwner = writableOwner;
389
+ this.#writableGeneration = writableOwner?.[GET_TRACKED_WRITE_GENERATION]?.();
390
+ }
391
+ [ASSERT_TRACKED_WRITABLE]() {
392
+ this.#writableOwner?.[ASSERT_TRACKED_WRITABLE]?.(this.#writableGeneration);
393
+ }
394
+ [GET_TRACKED_WRITE_GENERATION]() {
395
+ return this.#writableGeneration;
353
396
  }
354
397
  getRecord(): T {
355
398
  return this.#record;
@@ -404,18 +447,13 @@ export function updateAndFreeze(target, changes = target.getChanges?.()) {
404
447
  let mergedUpdatedObject: any;
405
448
  if (!target) return changes;
406
449
  if (target.getRecord && target.constructor === Array && !Object.isFrozen(target)) {
407
- // a tracked array, by default we can freeze the tracked array itself
408
- mergedUpdatedObject = target;
450
+ if (!hasChanges(target)) return (target as any).getRecord();
451
+ // Materialize a plain array so the stored value retains neither the mutation proxy nor its owner.
452
+ mergedUpdatedObject = new Array(target.length);
409
453
  for (let i = 0, l = target.length; i < l; i++) {
410
454
  let value = target[i];
411
455
  if (value && typeof value === 'object') {
412
- const newValue = updateAndFreeze(value);
413
- if (newValue !== value && mergedUpdatedObject === target) {
414
- // if we need to make any changes to the user's array, we make a copy so we don't modify
415
- // an array that the user may be using with transient properties
416
- mergedUpdatedObject = target.slice(0);
417
- }
418
- value = newValue;
456
+ value = updateAndFreeze(value);
419
457
  }
420
458
  mergedUpdatedObject[i] = value;
421
459
  }
@@ -493,15 +531,28 @@ export function hasChanges(target) {
493
531
  }
494
532
 
495
533
  const HAS_ARRAY_CHANGES = Symbol.for('has-array-changes');
534
+ const TRACKED_ARRAY_RECORD = Symbol('tracked-array-record');
535
+ const TRACKED_WRITABLE_OWNER = Symbol('tracked-writable-owner');
536
+ const TRACKED_WRITABLE_GENERATION = Symbol('tracked-writable-generation');
496
537
  class TrackedArray extends Array {
497
- #record: any;
538
+ [TRACKED_ARRAY_RECORD]: any;
539
+ [TRACKED_WRITABLE_OWNER]: any;
540
+ [TRACKED_WRITABLE_GENERATION]: any;
498
541
  [HAS_ARRAY_CHANGES]: boolean;
499
- constructor(length, record) {
542
+ constructor(length, record, writableOwner) {
500
543
  super(length);
501
- this.#record = record;
544
+ this[TRACKED_ARRAY_RECORD] = record;
545
+ this[TRACKED_WRITABLE_OWNER] = writableOwner;
546
+ this[TRACKED_WRITABLE_GENERATION] = writableOwner?.[GET_TRACKED_WRITE_GENERATION]?.();
547
+ }
548
+ [ASSERT_TRACKED_WRITABLE]() {
549
+ this[TRACKED_WRITABLE_OWNER]?.[ASSERT_TRACKED_WRITABLE]?.(this[TRACKED_WRITABLE_GENERATION]);
550
+ }
551
+ [GET_TRACKED_WRITE_GENERATION]() {
552
+ return this[TRACKED_WRITABLE_GENERATION];
502
553
  }
503
554
  getRecord() {
504
- return this.#record;
555
+ return this[TRACKED_ARRAY_RECORD];
505
556
  }
506
557
  splice(...args) {
507
558
  this[HAS_ARRAY_CHANGES] = true;
@@ -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];