@harperfast/harper 5.3.0-beta.3 → 5.3.0-beta.4

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 (311) hide show
  1. package/bin/cliOperations.ts +25 -3
  2. package/bin/run.ts +18 -0
  3. package/components/Application.ts +52 -78
  4. package/components/DESIGN.md +76 -3
  5. package/components/Scope.ts +10 -4
  6. package/components/anthropic/index.ts +10 -1
  7. package/components/bedrock/index.ts +10 -1
  8. package/components/componentLoader.ts +23 -14
  9. package/components/mcp/tools/operations.ts +3 -1
  10. package/components/ollama/index.ts +10 -1
  11. package/components/openai/index.ts +265 -18
  12. package/components/operations.js +40 -1
  13. package/config/configUtils.ts +3 -19
  14. package/dataLayer/DESIGN.md +29 -5
  15. package/dataLayer/harperBridge/ResourceBridge.ts +39 -3
  16. package/dataLayer/restoreMarker.ts +250 -14
  17. package/dataLayer/schema.ts +2 -7
  18. package/dataLayer/schemaDescribe.ts +10 -0
  19. package/dist/bin/cliOperations.js +28 -3
  20. package/dist/bin/cliOperations.js.map +1 -1
  21. package/dist/bin/run.js +19 -0
  22. package/dist/bin/run.js.map +1 -1
  23. package/dist/components/Application.d.ts +8 -2
  24. package/dist/components/Application.js +43 -69
  25. package/dist/components/Application.js.map +1 -1
  26. package/dist/components/Scope.d.ts +1 -1
  27. package/dist/components/Scope.js +9 -4
  28. package/dist/components/Scope.js.map +1 -1
  29. package/dist/components/anthropic/index.d.ts +0 -10
  30. package/dist/components/anthropic/index.js +9 -1
  31. package/dist/components/anthropic/index.js.map +1 -1
  32. package/dist/components/bedrock/index.d.ts +0 -12
  33. package/dist/components/bedrock/index.js +9 -1
  34. package/dist/components/bedrock/index.js.map +1 -1
  35. package/dist/components/componentLoader.js +23 -14
  36. package/dist/components/componentLoader.js.map +1 -1
  37. package/dist/components/mcp/tools/operations.js +1 -1
  38. package/dist/components/mcp/tools/operations.js.map +1 -1
  39. package/dist/components/ollama/index.d.ts +0 -13
  40. package/dist/components/ollama/index.js +9 -1
  41. package/dist/components/ollama/index.js.map +1 -1
  42. package/dist/components/openai/index.d.ts +11 -15
  43. package/dist/components/openai/index.js +224 -12
  44. package/dist/components/openai/index.js.map +1 -1
  45. package/dist/components/operations.d.ts +9 -0
  46. package/dist/components/operations.js +32 -2
  47. package/dist/components/operations.js.map +1 -1
  48. package/dist/config/configUtils.d.ts +1 -2
  49. package/dist/config/configUtils.js +7 -22
  50. package/dist/config/configUtils.js.map +1 -1
  51. package/dist/dataLayer/harperBridge/ResourceBridge.js +29 -2
  52. package/dist/dataLayer/harperBridge/ResourceBridge.js.map +1 -1
  53. package/dist/dataLayer/restoreMarker.d.ts +25 -3
  54. package/dist/dataLayer/restoreMarker.js +213 -13
  55. package/dist/dataLayer/restoreMarker.js.map +1 -1
  56. package/dist/dataLayer/schema.js +1 -4
  57. package/dist/dataLayer/schema.js.map +1 -1
  58. package/dist/dataLayer/schemaDescribe.js +10 -0
  59. package/dist/dataLayer/schemaDescribe.js.map +1 -1
  60. package/dist/index.d.ts +1 -1
  61. package/dist/index.js.map +1 -1
  62. package/dist/json/systemSchema.json +28 -0
  63. package/dist/resources/DatabaseTransaction.d.ts +21 -1
  64. package/dist/resources/DatabaseTransaction.js +147 -8
  65. package/dist/resources/DatabaseTransaction.js.map +1 -1
  66. package/dist/resources/PrimaryRocksDatabase.js +1 -1
  67. package/dist/resources/PrimaryRocksDatabase.js.map +1 -1
  68. package/dist/resources/Table.d.ts +31 -3
  69. package/dist/resources/Table.js +397 -79
  70. package/dist/resources/Table.js.map +1 -1
  71. package/dist/resources/auditStore.js +16 -10
  72. package/dist/resources/auditStore.js.map +1 -1
  73. package/dist/resources/blob.d.ts +1 -0
  74. package/dist/resources/blob.js +14 -5
  75. package/dist/resources/blob.js.map +1 -1
  76. package/dist/resources/branchDatabase.js +3 -3
  77. package/dist/resources/branchDatabase.js.map +1 -1
  78. package/dist/resources/databaseDropPreparation.d.ts +20 -0
  79. package/dist/resources/databaseDropPreparation.js +106 -0
  80. package/dist/resources/databaseDropPreparation.js.map +1 -0
  81. package/dist/resources/databases.d.ts +26 -21
  82. package/dist/resources/databases.js +1040 -204
  83. package/dist/resources/databases.js.map +1 -1
  84. package/dist/resources/derivedIndexRegistry.d.ts +23 -0
  85. package/dist/resources/derivedIndexRegistry.js +92 -0
  86. package/dist/resources/derivedIndexRegistry.js.map +1 -1
  87. package/dist/resources/derivedIndexRuntime.d.ts +10 -0
  88. package/dist/resources/derivedIndexRuntime.js +136 -25
  89. package/dist/resources/derivedIndexRuntime.js.map +1 -1
  90. package/dist/resources/derivedIndexes.d.ts +39 -0
  91. package/dist/resources/derivedIndexes.js +733 -0
  92. package/dist/resources/derivedIndexes.js.map +1 -0
  93. package/dist/resources/fullTextSchema.d.ts +8 -0
  94. package/dist/resources/fullTextSchema.js +42 -0
  95. package/dist/resources/fullTextSchema.js.map +1 -1
  96. package/dist/resources/fullTextSchemaLifecycle.d.ts +0 -5
  97. package/dist/resources/fullTextSchemaLifecycle.js +0 -9
  98. package/dist/resources/fullTextSchemaLifecycle.js.map +1 -1
  99. package/dist/resources/graphql.js +101 -9
  100. package/dist/resources/graphql.js.map +1 -1
  101. package/dist/resources/indexes/hnswDerivedIndex.d.ts +0 -10
  102. package/dist/resources/indexes/hnswDerivedIndex.js +0 -230
  103. package/dist/resources/indexes/hnswDerivedIndex.js.map +1 -1
  104. package/dist/resources/indexes/nativeFullTextDerivedIndexLifecycle.d.ts +3 -0
  105. package/dist/resources/indexes/nativeFullTextDerivedIndexLifecycle.js +57 -8
  106. package/dist/resources/indexes/nativeFullTextDerivedIndexLifecycle.js.map +1 -1
  107. package/dist/resources/models/Models.d.ts +26 -3
  108. package/dist/resources/models/Models.js +256 -13
  109. package/dist/resources/models/Models.js.map +1 -1
  110. package/dist/resources/models/TestBackend.d.ts +3 -1
  111. package/dist/resources/models/TestBackend.js +35 -1
  112. package/dist/resources/models/TestBackend.js.map +1 -1
  113. package/dist/resources/models/analyticsTable.d.ts +2 -2
  114. package/dist/resources/models/analyticsTable.js +4 -2
  115. package/dist/resources/models/analyticsTable.js.map +1 -1
  116. package/dist/resources/models/backendHelpers.d.ts +24 -0
  117. package/dist/resources/models/backendHelpers.js +18 -1
  118. package/dist/resources/models/backendHelpers.js.map +1 -1
  119. package/dist/resources/models/backendRegistry.d.ts +10 -21
  120. package/dist/resources/models/backendRegistry.js +74 -18
  121. package/dist/resources/models/backendRegistry.js.map +1 -1
  122. package/dist/resources/models/bootstrap.d.ts +7 -0
  123. package/dist/resources/models/bootstrap.js +36 -4
  124. package/dist/resources/models/bootstrap.js.map +1 -1
  125. package/dist/resources/models/decideHook.d.ts +33 -0
  126. package/dist/resources/models/decideHook.js +105 -0
  127. package/dist/resources/models/decideHook.js.map +1 -0
  128. package/dist/resources/models/decision.d.ts +64 -0
  129. package/dist/resources/models/decision.js +507 -0
  130. package/dist/resources/models/decision.js.map +1 -0
  131. package/dist/resources/models/decisionStore.d.ts +134 -0
  132. package/dist/resources/models/decisionStore.js +350 -0
  133. package/dist/resources/models/decisionStore.js.map +1 -0
  134. package/dist/resources/models/embedHook.d.ts +66 -8
  135. package/dist/resources/models/embedHook.js +228 -61
  136. package/dist/resources/models/embedHook.js.map +1 -1
  137. package/dist/resources/models/generativeDecision.d.ts +58 -0
  138. package/dist/resources/models/generativeDecision.js +315 -0
  139. package/dist/resources/models/generativeDecision.js.map +1 -0
  140. package/dist/resources/models/types.d.ts +232 -4
  141. package/dist/security/authn/oidc/tokenExchange.js +3 -33
  142. package/dist/security/authn/oidc/tokenExchange.js.map +1 -1
  143. package/dist/security/authn/oidc/tokenUseTable.d.ts +9 -0
  144. package/dist/security/authn/oidc/tokenUseTable.js +28 -0
  145. package/dist/security/authn/oidc/tokenUseTable.js.map +1 -0
  146. package/dist/security/certificateVerification/certificateVerificationSource.d.ts +0 -1
  147. package/dist/security/certificateVerification/certificateVerificationSource.js +3 -3
  148. package/dist/security/certificateVerification/certificateVerificationSource.js.map +1 -1
  149. package/dist/security/certificateVerification/crlVerification.js +77 -165
  150. package/dist/security/certificateVerification/crlVerification.js.map +1 -1
  151. package/dist/security/certificateVerification/types.d.ts +0 -3
  152. package/dist/security/certificateVerification/verificationTables.d.ts +19 -0
  153. package/dist/security/certificateVerification/verificationTables.js +125 -0
  154. package/dist/security/certificateVerification/verificationTables.js.map +1 -0
  155. package/dist/security/certificateVerification/verificationUtils.d.ts +1 -1
  156. package/dist/security/certificateVerification/verificationUtils.js +6 -31
  157. package/dist/security/certificateVerification/verificationUtils.js.map +1 -1
  158. package/dist/security/jsLoader.d.ts +5 -0
  159. package/dist/security/jsLoader.js +8 -0
  160. package/dist/security/jsLoader.js.map +1 -1
  161. package/dist/server/Server.d.ts +1 -0
  162. package/dist/server/Server.js.map +1 -1
  163. package/dist/server/http.js +2 -2
  164. package/dist/server/http.js.map +1 -1
  165. package/dist/server/itc/serverHandlers.js +21 -2
  166. package/dist/server/itc/serverHandlers.js.map +1 -1
  167. package/dist/server/jobs/jobOwnership.d.ts +25 -0
  168. package/dist/server/jobs/jobOwnership.js +127 -0
  169. package/dist/server/jobs/jobOwnership.js.map +1 -0
  170. package/dist/server/jobs/jobProcess.js +9 -2
  171. package/dist/server/jobs/jobProcess.js.map +1 -1
  172. package/dist/server/jobs/jobs.js +18 -5
  173. package/dist/server/jobs/jobs.js.map +1 -1
  174. package/dist/server/loadRootComponents.js +10 -0
  175. package/dist/server/loadRootComponents.js.map +1 -1
  176. package/dist/server/serverHelpers/contentTypes.js +11 -1
  177. package/dist/server/serverHelpers/contentTypes.js.map +1 -1
  178. package/dist/server/serverHelpers/serverHandlers.js +12 -2
  179. package/dist/server/serverHelpers/serverHandlers.js.map +1 -1
  180. package/dist/server/threads/itc.d.ts +4 -1
  181. package/dist/server/threads/itc.js +29 -6
  182. package/dist/server/threads/itc.js.map +1 -1
  183. package/dist/server/threads/manageThreads.d.ts +12 -4
  184. package/dist/server/threads/manageThreads.js +241 -119
  185. package/dist/server/threads/manageThreads.js.map +1 -1
  186. package/dist/server/threads/threadServer.d.ts +1 -0
  187. package/dist/server/threads/threadServer.js +15 -1
  188. package/dist/server/threads/threadServer.js.map +1 -1
  189. package/dist/server/threads/windowsProcessTree.d.ts +91 -0
  190. package/dist/server/threads/windowsProcessTree.js +361 -0
  191. package/dist/server/threads/windowsProcessTree.js.map +1 -0
  192. package/dist/server/throttle.d.ts +1 -1
  193. package/dist/server/throttle.js +8 -1
  194. package/dist/server/throttle.js.map +1 -1
  195. package/dist/upgrade/directives/5-3-0.js +53 -46
  196. package/dist/upgrade/directives/5-3-0.js.map +1 -1
  197. package/dist/utility/errors/hdbError.d.ts +10 -1
  198. package/dist/utility/errors/hdbError.js +55 -11
  199. package/dist/utility/errors/hdbError.js.map +1 -1
  200. package/dist/utility/fsync.d.ts +2 -0
  201. package/dist/utility/fsync.js +20 -0
  202. package/dist/utility/fsync.js.map +1 -0
  203. package/dist/utility/globalSchema.d.ts +18 -0
  204. package/dist/utility/hdbTerms.d.ts +3 -0
  205. package/dist/utility/hdbTerms.js +3 -0
  206. package/dist/utility/hdbTerms.js.map +1 -1
  207. package/dist/utility/signalling.d.ts +7 -1
  208. package/dist/utility/signalling.js +27 -3
  209. package/dist/utility/signalling.js.map +1 -1
  210. package/dist/utility/when.d.ts +1 -0
  211. package/dist/utility/when.js +21 -0
  212. package/dist/utility/when.js.map +1 -1
  213. package/dist/validation/configValidator.js +30 -3
  214. package/dist/validation/configValidator.js.map +1 -1
  215. package/index.ts +13 -0
  216. package/json/systemSchema.json +28 -0
  217. package/npm-shrinkwrap.json +151 -55
  218. package/package.json +14 -13
  219. package/resources/DESIGN.md +80 -100
  220. package/resources/DatabaseTransaction.ts +157 -10
  221. package/resources/PrimaryRocksDatabase.ts +2 -2
  222. package/resources/Table.ts +494 -101
  223. package/resources/auditStore.ts +18 -10
  224. package/resources/blob.ts +13 -6
  225. package/resources/branchDatabase.ts +3 -3
  226. package/resources/databaseDropPreparation.ts +120 -0
  227. package/resources/databases.ts +1155 -205
  228. package/resources/derivedIndexRegistry.ts +122 -0
  229. package/resources/derivedIndexRuntime.ts +157 -30
  230. package/resources/derivedIndexes.ts +856 -0
  231. package/resources/fullTextSchema.ts +55 -0
  232. package/resources/fullTextSchemaLifecycle.ts +0 -15
  233. package/resources/graphql.ts +107 -12
  234. package/resources/indexes/DESIGN.md +7 -0
  235. package/resources/indexes/hnswDerivedIndex.ts +0 -260
  236. package/resources/indexes/nativeFullTextDerivedIndexLifecycle.ts +72 -9
  237. package/resources/models/DESIGN.md +39 -0
  238. package/resources/models/Models.ts +361 -18
  239. package/resources/models/TestBackend.ts +56 -1
  240. package/resources/models/analyticsTable.ts +6 -4
  241. package/resources/models/backendHelpers.ts +17 -0
  242. package/resources/models/backendRegistry.ts +76 -21
  243. package/resources/models/bootstrap.ts +57 -7
  244. package/resources/models/decideHook.ts +135 -0
  245. package/resources/models/decision.ts +505 -0
  246. package/resources/models/decisionStore.ts +419 -0
  247. package/resources/models/embedHook.ts +272 -55
  248. package/resources/models/generativeDecision.ts +459 -0
  249. package/resources/models/types.ts +231 -4
  250. package/security/DESIGN.md +22 -2
  251. package/security/authn/oidc/tokenExchange.ts +6 -34
  252. package/security/authn/oidc/tokenUseTable.ts +27 -0
  253. package/security/certificateVerification/certificateVerificationSource.ts +2 -3
  254. package/security/certificateVerification/crlVerification.ts +96 -174
  255. package/security/certificateVerification/types.ts +0 -3
  256. package/security/certificateVerification/verificationTables.ts +124 -0
  257. package/security/certificateVerification/verificationUtils.ts +8 -32
  258. package/security/jsLoader.ts +9 -1
  259. package/server/DESIGN.md +27 -1
  260. package/server/Server.ts +2 -1
  261. package/server/http.ts +4 -2
  262. package/server/itc/serverHandlers.js +29 -1
  263. package/server/jobs/jobOwnership.ts +89 -0
  264. package/server/jobs/jobProcess.ts +8 -2
  265. package/server/jobs/jobs.ts +15 -4
  266. package/server/loadRootComponents.js +11 -0
  267. package/server/serverHelpers/contentTypes.ts +13 -1
  268. package/server/serverHelpers/serverHandlers.js +11 -2
  269. package/server/threads/itc.js +32 -6
  270. package/server/threads/manageThreads.js +268 -122
  271. package/server/threads/threadServer.js +13 -1
  272. package/server/threads/windowsProcessTree.ts +409 -0
  273. package/server/throttle.ts +11 -1
  274. package/studio/web/assets/{Chat-CveGbxe6.js → Chat-Bq60AO-g.js} +1 -1
  275. package/studio/web/assets/{FloatingChat-IpCFgYWL.js → FloatingChat-D4560T0p.js} +3 -3
  276. package/studio/web/assets/{apiToken-CS8fFTgL.js → apiToken-BEvbFPMc.js} +1 -1
  277. package/studio/web/assets/{applications-D7F4Jopl.js → applications-CSUVTH4E.js} +1 -1
  278. package/studio/web/assets/{cssMode-Daw_oLPq.js → cssMode-DJyc9mHj.js} +1 -1
  279. package/studio/web/assets/{editor-B3Bs1zWX.js → editor-DDIsQfj6.js} +1 -1
  280. package/studio/web/assets/{html-CDaQwWiE.js → html-g1BJHxLy.js} +1 -1
  281. package/studio/web/assets/{htmlMode-Cb6emO4U.js → htmlMode-Vh5IglCQ.js} +1 -1
  282. package/studio/web/assets/{index-B4g2Fu_2.js → index-DGRAzvO4.js} +5 -5
  283. package/studio/web/assets/{index.lazy-NwhvF1q1.js → index.lazy-vgSic7et.js} +1 -1
  284. package/studio/web/assets/{javascript-2_86gB9Q.js → javascript-5VIolqf7.js} +1 -1
  285. package/studio/web/assets/{jsonMode-DX-i86uQ.js → jsonMode-DG4zQraI.js} +1 -1
  286. package/studio/web/assets/{languageServices-B85WN-Ej.js → languageServices-t5FBpX-i.js} +1 -1
  287. package/studio/web/assets/{lspLanguageFeatures-JCWHIsKg.js → lspLanguageFeatures-DgVnNPh-.js} +1 -1
  288. package/studio/web/assets/{notifications-D9ejzt52.js → notifications-BeWcX04m.js} +1 -1
  289. package/studio/web/assets/{notifications-B7hjKhai.js → notifications-DUGWrWoO.js} +1 -1
  290. package/studio/web/assets/{profile-D8B6No3F.js → profile-BvUEDBzW.js} +1 -1
  291. package/studio/web/assets/{regions-DFazIfTK.js → regions-CB-_2m7M.js} +1 -1
  292. package/studio/web/assets/{register-CwFLviLF.js → register-1w1Zr3b2.js} +2 -2
  293. package/studio/web/assets/{setComponentFile-VnJ80SSJ.js → setComponentFile-z4-qJCbA.js} +1 -1
  294. package/studio/web/assets/{setup-BYQxFy-C.js → setup-CS50uJ4x.js} +2 -2
  295. package/studio/web/assets/{status-Cn5XjmsL.js → status-CI0wOdOE.js} +1 -1
  296. package/studio/web/assets/{toggleHighContrast-g9xIh6TX.js → toggleHighContrast-BsAE4jPe.js} +1 -1
  297. package/studio/web/assets/{tsMode-EC-n0Wqc.js → tsMode-D83Gg3cq.js} +1 -1
  298. package/studio/web/assets/{typescript-DHmM9JCO.js → typescript-Dfp-GrNq.js} +1 -1
  299. package/studio/web/assets/{useEntityRestURL-BUdnVSVv.js → useEntityRestURL-BKetlINf.js} +1 -1
  300. package/studio/web/assets/{workers-BVl9bBB-.js → workers-CCvmDLH3.js} +1 -1
  301. package/studio/web/assets/{xml-C4_sqOkX.js → xml-06pZp00U.js} +1 -1
  302. package/studio/web/assets/{yaml-CVkbDqtW.js → yaml-iSnWMVlV.js} +1 -1
  303. package/studio/web/index.html +1 -1
  304. package/upgrade/directives/5-3-0.ts +61 -54
  305. package/utility/DESIGN.md +5 -1
  306. package/utility/errors/hdbError.ts +56 -8
  307. package/utility/fsync.ts +16 -0
  308. package/utility/hdbTerms.ts +3 -0
  309. package/utility/signalling.ts +33 -4
  310. package/utility/when.ts +22 -0
  311. package/validation/configValidator.ts +36 -7
@@ -46,6 +46,7 @@ exports.acquireUpdateAttributesLock = acquireUpdateAttributesLock;
46
46
  exports.tryUpdateAttributesLock = tryUpdateAttributesLock;
47
47
  exports.releaseUpdateAttributesLock = releaseUpdateAttributesLock;
48
48
  exports.withUpdateAttributesLock = withUpdateAttributesLock;
49
+ exports.withUpdateAttributesLockNonBlocking = withUpdateAttributesLockNonBlocking;
49
50
  exports.ignoreAlreadyDropped = ignoreAlreadyDropped;
50
51
  exports.freezeRecord = freezeRecord;
51
52
  exports.frozenRecordView = frozenRecordView;
@@ -67,6 +68,7 @@ const recordLock_ts_1 = require("./recordLock.js");
67
68
  const nodeName_ts_1 = require("../server/nodeName.js");
68
69
  const envMngr = __importStar(require("../utility/environment/environmentManager.js"));
69
70
  const transactionBroadcast_ts_1 = require("./transactionBroadcast.js");
71
+ const databaseDropPreparation_ts_1 = require("./databaseDropPreparation.js");
70
72
  const hdbError_ts_1 = require("../utility/errors/hdbError.js");
71
73
  const signalling = __importStar(require("../utility/signalling.js"));
72
74
  const itc_js_1 = require("../server/threads/itc.js");
@@ -83,6 +85,7 @@ const auditStore_ts_1 = require("./auditStore.js");
83
85
  const derivedIndexRegistry_ts_1 = require("./derivedIndexRegistry.js");
84
86
  const recordLockCoordinator_ts_1 = require("./recordLockCoordinator.js");
85
87
  const embedHook_ts_1 = require("./models/embedHook.js");
88
+ const decideHook_ts_1 = require("./models/decideHook.js");
86
89
  const common_utils_ts_1 = require("../utility/common_utils.js");
87
90
  const RecordEncoder_ts_1 = require("./RecordEncoder.js");
88
91
  const write_ts_1 = require("./analytics/write.js");
@@ -98,6 +101,7 @@ const rocksdb_js_1 = require("@harperfast/rocksdb-js");
98
101
  const LMDBTransaction_1 = require("./LMDBTransaction");
99
102
  const contentTypes_1 = require("../server/serverHelpers/contentTypes");
100
103
  const jsonSchemaTypes_ts_1 = require("./jsonSchemaTypes.js");
104
+ const fullTextSchema_ts_1 = require("./fullTextSchema.js");
101
105
  const { sortBy } = lodash_1.default;
102
106
  const { validateAttribute } = lmdbProcessRows_js_1.default;
103
107
  const NULL_WITH_TIMESTAMP = new Uint8Array(9);
@@ -189,8 +193,28 @@ function tryUpdateAttributesLock(rootStore) {
189
193
  function releaseUpdateAttributesLock(rootStore) {
190
194
  rootStore.unlock(updateAttributesLockKey);
191
195
  }
192
- function withUpdateAttributesLock(rootStore, scopeDescription, callback) {
193
- acquireUpdateAttributesLock(rootStore, scopeDescription);
196
+ async function acquireUpdateAttributesLockAsync(rootStore, scopeDescription, timeout = exports.UPDATE_ATTRIBUTES_LOCK_TIMEOUT) {
197
+ if (rootStore.tryLock(updateAttributesLockKey))
198
+ return;
199
+ const startTime = node_perf_hooks_1.performance.now();
200
+ let waitTime = 1;
201
+ while (!rootStore.tryLock(updateAttributesLockKey)) {
202
+ const elapsed = node_perf_hooks_1.performance.now() - startTime;
203
+ if (elapsed >= timeout) {
204
+ throw new hdbError_ts_1.UpdateAttributesLockTimeoutError(`Timed out after ${Math.round(elapsed)}ms waiting for the exclusive '${UPDATE_ATTRIBUTES_LOCK}' lock on ${scopeDescription}; the lock holder did not release it before the deadline, so this schema/attribute update cannot proceed`);
205
+ }
206
+ await new Promise((resolve) => setTimeout(resolve, Math.min(waitTime, timeout - elapsed)));
207
+ if (waitTime < 16)
208
+ waitTime *= 2;
209
+ }
210
+ const waited = node_perf_hooks_1.performance.now() - startTime;
211
+ if (waited >= exports.UPDATE_ATTRIBUTES_LOCK_SLOW_WAIT)
212
+ try {
213
+ logger_ts_1.logger.warn?.(`Acquired the exclusive '${UPDATE_ATTRIBUTES_LOCK}' lock on ${scopeDescription} after waiting ${Math.round(waited)}ms`);
214
+ }
215
+ catch { }
216
+ }
217
+ function runWithUpdateAttributesLock(rootStore, scopeDescription, callback) {
194
218
  try {
195
219
  const result = callback();
196
220
  if (typeof result?.then === 'function') {
@@ -203,6 +227,15 @@ function withUpdateAttributesLock(rootStore, scopeDescription, callback) {
203
227
  releaseUpdateAttributesLock(rootStore);
204
228
  }
205
229
  }
230
+ function withUpdateAttributesLock(rootStore, scopeDescription, callback) {
231
+ acquireUpdateAttributesLock(rootStore, scopeDescription);
232
+ return runWithUpdateAttributesLock(rootStore, scopeDescription, callback);
233
+ }
234
+ function withUpdateAttributesLockNonBlocking(rootStore, scopeDescription, callback) {
235
+ if (rootStore.tryLock(updateAttributesLockKey))
236
+ return runWithUpdateAttributesLock(rootStore, scopeDescription, callback);
237
+ return acquireUpdateAttributesLockAsync(rootStore, scopeDescription).then(() => runWithUpdateAttributesLock(rootStore, scopeDescription, callback));
238
+ }
206
239
  // Tolerate a redundant column family drop. Drops are broadcast to every worker
207
240
  // thread and each holds its own handle to the same underlying family, so a
208
241
  // concurrent worker may already have dropped it; the storage engine reports
@@ -521,7 +554,9 @@ function isPlainOptions(value) {
521
554
  });
522
555
  function makeTable(options) {
523
556
  var _a;
524
- const { primaryKey, indices, tableId, tableName, primaryStore, databasePath, databaseName, auditStore, schemaDefined, dbisDB: dbisDb, sealed, splitSegments, replicate, description, hidden, cacheControl, isBranch, fullTextIndexes = [], } = options;
557
+ const { primaryKey, indices, tableId, tableName, primaryStore, databasePath, databaseName, auditStore, schemaDefined, dbisDB: dbisDb, sealed, splitSegments, replicate, description, hidden, cacheControl, isBranch, fullTextIndexes = [], fullTextIndexGenerations = Object.create(null), fullTextIndexRetirements = [], } = options;
558
+ const tableRootStore = primaryStore.rootStore;
559
+ const tableRootPath = tableRootStore.path;
525
560
  let { expirationMS: expirationMs, evictionMS: evictionMs, audit, trackDeletes } = options;
526
561
  // Set when the TTL exists only on this thread: either application code configured it at runtime, or
527
562
  // an isolated application's schema was declared here. Hydrating persisted metadata does not set it:
@@ -548,6 +583,9 @@ function makeTable(options) {
548
583
  let hasSourceGet;
549
584
  let primaryKeyAttribute;
550
585
  let lastEvictionCompletion = Promise.resolve();
586
+ let recordExpirationCompletion = Promise.resolve();
587
+ const maintenanceCommits = new Set();
588
+ let maintenanceClosed = false;
551
589
  let droppingTable = false;
552
590
  let createdTimeProperty, updatedTimeProperty, expiresAtProperty;
553
591
  for (const attribute of attributes) {
@@ -761,9 +799,10 @@ function makeTable(options) {
761
799
  }
762
800
  return { txnLogKey: version, nodeId };
763
801
  }
764
- // Canonical-source applies (sourceApply), replay and replication notifications are never shed;
765
- // dropping one would advance the source cursor past a write that never landed.
802
+ // Teardown rejects every producer; source/replay paths only bypass derived-index lag shedding.
766
803
  function assertDerivedIndexAdmission(options, transaction) {
804
+ if ((0, databaseDropPreparation_ts_1.databaseDropPrepared)(tableRootPath) || (0, DatabaseTransaction_ts_1.databaseCommitsSuspended)(tableRootStore))
805
+ throw new hdbError_ts_1.DatabaseClosingError(databaseName, !transaction?.root && !transaction?.snapshotFree);
767
806
  if (options?.isNotification || transaction?.sourceApply || transaction?.isReplay)
768
807
  return;
769
808
  const reason = (0, derivedIndexRegistry_ts_1.derivedIndexWriteRejection)(auditStore, tableId);
@@ -788,6 +827,17 @@ function makeTable(options) {
788
827
  error.statusCode = 404;
789
828
  throw error;
790
829
  }
830
+ function currentFullTextDescriptor() {
831
+ const namedKey = `${tableName}/${primaryKey}`;
832
+ const namedDescriptor = dbisDb.getSync(namedKey);
833
+ const key = namedDescriptor?.isPrimaryKey ? namedKey : `${tableName}/`;
834
+ const descriptor = dbisDb.getSync(key);
835
+ if (!descriptor ||
836
+ (descriptor.tableId != null && descriptor.tableId !== tableId) ||
837
+ descriptor.generation !== tableGeneration)
838
+ return;
839
+ return { key, descriptor };
840
+ }
791
841
  class TableResource extends Resource_ts_1.Resource {
792
842
  #record; // the stored/frozen record from the database and stored in the cache (should not be modified directly)
793
843
  #changes; // the changes to the record that have been made (should not be modified directly)
@@ -840,6 +890,38 @@ function makeTable(options) {
840
890
  static derivedIndexRuntime;
841
891
  static audit = audit;
842
892
  static fullTextIndexes = fullTextIndexes;
893
+ static fullTextIndexGenerations = fullTextIndexGenerations;
894
+ static fullTextIndexRetirements = fullTextIndexRetirements;
895
+ static hasCurrentFullTextIndexRetirements(names) {
896
+ if (names.length === 0 || !(primaryStore.rootStore instanceof rocksdb_js_1.RocksDatabase))
897
+ return false;
898
+ return withUpdateAttributesLockNonBlocking(primaryStore.rootStore, `verify full-text retirement for '${databaseName}.${tableName}'`, () => {
899
+ const current = currentFullTextDescriptor();
900
+ if (!current)
901
+ return false;
902
+ const pending = new Set((0, fullTextSchema_ts_1.persistedFullTextIndexNames)(current.descriptor.fullTextIndexRetirements));
903
+ return names.every((name) => pending.has(name));
904
+ });
905
+ }
906
+ static completeFullTextIndexRetirements(names) {
907
+ if (names.length === 0 || !(primaryStore.rootStore instanceof rocksdb_js_1.RocksDatabase))
908
+ return;
909
+ const completed = new Set(names);
910
+ return withUpdateAttributesLockNonBlocking(primaryStore.rootStore, `complete full-text retirement for '${databaseName}.${tableName}'`, () => {
911
+ const current = currentFullTextDescriptor();
912
+ if (!current)
913
+ return;
914
+ const { key, descriptor } = current;
915
+ const remaining = (0, fullTextSchema_ts_1.persistedFullTextIndexNames)(descriptor.fullTextIndexRetirements).filter((name) => !completed.has(name));
916
+ const updated = { ...descriptor };
917
+ if (remaining.length > 0)
918
+ updated.fullTextIndexRetirements = remaining.map((name) => ({ name }));
919
+ else
920
+ delete updated.fullTextIndexRetirements;
921
+ dbisDb.putSync(key, updated);
922
+ this.fullTextIndexRetirements = remaining;
923
+ });
924
+ }
843
925
  static databasePath = databasePath;
844
926
  static databaseName = databaseName;
845
927
  static attributes = attributes;
@@ -863,6 +945,9 @@ function makeTable(options) {
863
945
  static userEmbedders = {};
864
946
  static userSetEmbedders = new Set();
865
947
  static embedAttributes = attributes.filter((a) => a?.embed);
948
+ static userDeciders = {};
949
+ static userSetDeciders = new Set();
950
+ static decideAttributes = attributes.filter((a) => a?.decide);
866
951
  static source;
867
952
  static getResidencyById;
868
953
  static get expirationMS() {
@@ -1224,7 +1309,7 @@ function makeTable(options) {
1224
1309
  catch { }
1225
1310
  throw error;
1226
1311
  }
1227
- return seqTransaction.commit().catch((error) => {
1312
+ return (0, DatabaseTransaction_ts_1.commitTrackedRocksTransaction)(seqTransaction, dbisDb, primaryStore.rootStore).catch((error) => {
1228
1313
  // A rejected commit leaves the handle open too, so release it here as well —
1229
1314
  // same reason as the staging failure above, and the same shape as the
1230
1315
  // eviction paths' commit failures (see evict/commitItems below).
@@ -1453,6 +1538,8 @@ function makeTable(options) {
1453
1538
  * @returns
1454
1539
  */
1455
1540
  static getResource(target, request, resourceOptions) {
1541
+ if ((0, databaseDropPreparation_ts_1.databaseDropPrepared)(tableRootPath) || (0, DatabaseTransaction_ts_1.databaseCommitsSuspended)(tableRootStore))
1542
+ throw new hdbError_ts_1.DatabaseClosingError(databaseName, !request?.transaction?.root && !request?.transaction?.snapshotFree);
1456
1543
  const resource = super.getResource(target, request, resourceOptions);
1457
1544
  if (this.loadAsInstance !== false) {
1458
1545
  return resource._loadRecord(target, request, resourceOptions);
@@ -1842,14 +1929,25 @@ function makeTable(options) {
1842
1929
  // same-name recreate must not race an owner still applying to the old generation.
1843
1930
  const derivedIndexRuntime = _a.derivedIndexRuntime;
1844
1931
  const restoreDerivedIndexesAfterFailedDrop = () => {
1932
+ _a.resumeMaintenance();
1845
1933
  try {
1846
- _a.derivedIndexRuntime = derivedIndexRuntime?.restoreAfterFailedDrop?.();
1934
+ const restored = derivedIndexRuntime?.restoreAfterFailedDrop?.();
1935
+ if (_a.derivedIndexRuntime === derivedIndexRuntime)
1936
+ _a.derivedIndexRuntime = restored;
1847
1937
  }
1848
1938
  catch (restoreError) {
1849
- _a.derivedIndexRuntime = undefined;
1939
+ if (_a.derivedIndexRuntime === derivedIndexRuntime)
1940
+ _a.derivedIndexRuntime = undefined;
1850
1941
  logger_ts_1.logger.error?.(`Could not restore derived indexes after failed drop of ${databaseName}.${_a.tableName}`, restoreError);
1851
1942
  }
1852
1943
  };
1944
+ try {
1945
+ await _a.closeMaintenance();
1946
+ }
1947
+ catch (error) {
1948
+ _a.resumeMaintenance();
1949
+ throw error;
1950
+ }
1853
1951
  try {
1854
1952
  await derivedIndexRuntime?.close(true);
1855
1953
  }
@@ -1857,13 +1955,23 @@ function makeTable(options) {
1857
1955
  restoreDerivedIndexesAfterFailedDrop();
1858
1956
  throw error;
1859
1957
  }
1958
+ let releaseFullTextRetirementFence;
1959
+ const releaseFullTextRetirement = () => {
1960
+ const release = releaseFullTextRetirementFence;
1961
+ releaseFullTextRetirementFence = undefined;
1962
+ release?.();
1963
+ };
1860
1964
  const abortStaleDrop = () => {
1965
+ releaseFullTextRetirement();
1861
1966
  derivedIndexRuntime?.completeDrop?.(false);
1862
1967
  _a.derivedIndexRuntime = undefined;
1863
1968
  _a.cleanup();
1864
1969
  if (databases_ts_1.databases[databaseName]?.[tableName] === _a)
1865
1970
  delete databases_ts_1.databases[databaseName][tableName];
1866
1971
  };
1972
+ let fullTextDefinitionsForRetirement = [
1973
+ ..._a.fullTextIndexes,
1974
+ ];
1867
1975
  let dropIdentityConfirmed = databaseName !== databasePath;
1868
1976
  let primaryCatalogKey = _a.tableName + '/';
1869
1977
  let storeGeneration;
@@ -1892,6 +2000,23 @@ function makeTable(options) {
1892
2000
  return false;
1893
2001
  dropGeneration = primaryMeta.dropGeneration;
1894
2002
  storeGeneration = primaryMeta.generation;
2003
+ const durableFullTextDefinitions = rootStore instanceof rocksdb_js_1.RocksDatabase
2004
+ ? [
2005
+ ...(0, fullTextSchema_ts_1.persistedFullTextIndexNames)(primaryMeta.fullTextIndexes),
2006
+ ...(0, fullTextSchema_ts_1.persistedFullTextIndexNames)(primaryMeta.fullTextIndexRetirements),
2007
+ ].map((name) => ({ name }))
2008
+ : [];
2009
+ const attachedFullTextDefinitions = derivedIndexRuntime?.fullTextDefinitions?.() ?? [];
2010
+ const definitionsByName = new Map([...durableFullTextDefinitions, ...attachedFullTextDefinitions].map((definition) => [
2011
+ definition.name,
2012
+ definition,
2013
+ ]));
2014
+ fullTextDefinitionsForRetirement = [...definitionsByName.values()];
2015
+ if (fullTextDefinitionsForRetirement.length > 0 && !releaseFullTextRetirementFence) {
2016
+ releaseFullTextRetirementFence = (0, derivedIndexRegistry_ts_1.acquireFullTextRetirementFence)(rootStore, _a.tableName);
2017
+ if (!releaseFullTextRetirementFence)
2018
+ throw new hdbError_ts_1.ClientError(`Cannot drop '${databaseName}.${_a.tableName}' while its full-text storage is being retired`, 409);
2019
+ }
1895
2020
  if (primaryMeta.dropping)
1896
2021
  return true;
1897
2022
  primaryMeta.dropping = true;
@@ -1923,11 +2048,13 @@ function makeTable(options) {
1923
2048
  }
1924
2049
  }
1925
2050
  catch (error) {
2051
+ releaseFullTextRetirement();
1926
2052
  restoreDerivedIndexesAfterFailedDrop();
1927
2053
  throw error;
1928
2054
  }
1929
2055
  }
1930
2056
  if (!dropIdentityConfirmed) {
2057
+ releaseFullTextRetirement();
1931
2058
  abortStaleDrop();
1932
2059
  return;
1933
2060
  }
@@ -1951,13 +2078,20 @@ function makeTable(options) {
1951
2078
  try {
1952
2079
  if (!dropGeneration)
1953
2080
  throw new Error(`Cannot drop ${databaseName}.${tableName}: its catalog tombstone has no drop generation`);
1954
- await retireRocksStores(storeGeneration, dropGeneration);
2081
+ const retired = await retireRocksStores(storeGeneration, dropGeneration);
2082
+ if (!retired) {
2083
+ derivedIndexRuntime?.completeDrop?.();
2084
+ releaseFullTextRetirement();
2085
+ return;
2086
+ }
1955
2087
  }
1956
2088
  catch (error) {
2089
+ releaseFullTextRetirement();
1957
2090
  derivedIndexRuntime?.completeDrop?.();
1958
2091
  throw error;
1959
2092
  }
1960
2093
  derivedIndexRuntime?.completeDrop?.();
2094
+ releaseFullTextRetirement();
1961
2095
  return;
1962
2096
  }
1963
2097
  try {
@@ -1968,6 +2102,7 @@ function makeTable(options) {
1968
2102
  }
1969
2103
  }
1970
2104
  catch (error) {
2105
+ releaseFullTextRetirement();
1971
2106
  derivedIndexRuntime?.completeDrop?.();
1972
2107
  throw error;
1973
2108
  }
@@ -2009,6 +2144,7 @@ function makeTable(options) {
2009
2144
  await dbisDb.committed;
2010
2145
  }
2011
2146
  catch (error) {
2147
+ releaseFullTextRetirement();
2012
2148
  derivedIndexRuntime?.completeDrop?.();
2013
2149
  throw error;
2014
2150
  }
@@ -2029,14 +2165,31 @@ function makeTable(options) {
2029
2165
  node_fs_1.default.unlinkSync(primaryStore.path);
2030
2166
  }
2031
2167
  catch (error) {
2168
+ releaseFullTextRetirement();
2032
2169
  derivedIndexRuntime?.completeDrop?.();
2033
2170
  throw error;
2034
2171
  }
2035
2172
  }
2036
- derivedIndexRuntime?.completeDrop?.();
2037
- const message = new itc_js_1.SchemaEventMsg(process.pid, hdbTerms_ts_1.OPERATIONS_ENUM.DROP_TABLE, databaseName, tableName);
2038
- message.dropTableId = tableId;
2039
- signalling.signalSchemaChange(message);
2173
+ try {
2174
+ const message = new itc_js_1.SchemaEventMsg(process.pid, hdbTerms_ts_1.OPERATIONS_ENUM.DROP_TABLE, databaseName, tableName);
2175
+ message.dropTableId = tableId;
2176
+ await signalling.signalSchemaChange(message);
2177
+ await retireFullTextStorage();
2178
+ derivedIndexRuntime?.completeDrop?.();
2179
+ }
2180
+ finally {
2181
+ releaseFullTextRetirement();
2182
+ }
2183
+ async function retireFullTextStorage() {
2184
+ if (fullTextDefinitionsForRetirement.length === 0)
2185
+ return true;
2186
+ if (derivedIndexRuntime?.retireAfterConfirmedDrop)
2187
+ return derivedIndexRuntime.retireAfterConfirmedDrop(fullTextDefinitionsForRetirement);
2188
+ else {
2189
+ const { retireFullTextIndexes } = await import("./derivedIndexes.js");
2190
+ return retireFullTextIndexes(_a, fullTextDefinitionsForRetirement);
2191
+ }
2192
+ }
2040
2193
  async function retireRocksStores(generation, dropGeneration) {
2041
2194
  const releaseDropMark = (0, databases_ts_1.markDropInProgress)(dropGeneration);
2042
2195
  try {
@@ -2044,6 +2197,10 @@ function makeTable(options) {
2044
2197
  message.dropGeneration = dropGeneration;
2045
2198
  message.dropTableId = tableId;
2046
2199
  await signalling.signalSchemaChange(message);
2200
+ // Keep the tombstone's native index names durable until every peer writer
2201
+ // has stopped and the wrapper has retired their storage.
2202
+ if (!(await retireFullTextStorage()))
2203
+ return false;
2047
2204
  const removed = withUpdateAttributesLock(rootStore, `table '${databaseName}.${tableName}'`, () => {
2048
2205
  const stores = (0, databases_ts_1.storeNamesFor)(dbisDb, tableName, generation);
2049
2206
  const retiredStores = (0, databases_ts_1.recordRetiredGeneration)(dbisDb, tableName, dropGeneration, stores, primaryStore.name ?? (0, databases_ts_1.storeNameFor)(primaryCatalogKey, generation));
@@ -2092,6 +2249,7 @@ function makeTable(options) {
2092
2249
  await (0, databases_ts_1.sweepDroppedTableBlobs)(primaryStore, label);
2093
2250
  if (!settled)
2094
2251
  finishDroppedTableBlobSweep(rootStore, primaryStore, label);
2252
+ return true;
2095
2253
  }
2096
2254
  finally {
2097
2255
  releaseDropMark();
@@ -2724,6 +2882,8 @@ function makeTable(options) {
2724
2882
  * Evicting a record will remove it from a caching table. This is not considered a canonical data change, and it is assumed that retrieving this record from the source will still yield the same record, this is only removing the local copy of the record.
2725
2883
  */
2726
2884
  static evict(id, existingRecord, existingVersion) {
2885
+ if (maintenanceClosed)
2886
+ return Promise.resolve();
2727
2887
  let entry;
2728
2888
  const lmdbTransaction = txnForContext({ transaction: new DatabaseTransaction_ts_1.DatabaseTransaction() });
2729
2889
  let transaction = lmdbTransaction.getReadTxn();
@@ -2775,15 +2935,15 @@ function makeTable(options) {
2775
2935
  // a plain resolution object rather than a promise — return the store's write promises instead, so
2776
2936
  // the caller gets a real thenable that resolves once the removal is durable.
2777
2937
  lmdbTransaction.commit();
2778
- return Promise.resolve(lmdbCompletion).catch((error) => {
2938
+ return trackMaintenanceCommit(Promise.resolve(lmdbCompletion).catch((error) => {
2779
2939
  logger_ts_1.logger.warn?.('Error evicting record', id, error);
2780
- });
2940
+ }));
2781
2941
  }
2782
2942
  // RocksDB: eviction writes went directly into the raw transaction via options; commit it directly,
2783
2943
  // as DatabaseTransaction.commit() would abort it (no tracked writes). The raw commit bypasses
2784
2944
  // DatabaseTransaction's ERR_BUSY retry, so a concurrent-write conflict rejects here — swallow it
2785
2945
  // (abandon the eviction) and log anything unexpected, rather than letting it crash the process.
2786
- return transaction.commit().catch((error) => {
2946
+ return trackMaintenanceCommit((0, DatabaseTransaction_ts_1.commitTrackedRocksTransaction)(transaction, primaryStore).catch((error) => {
2787
2947
  // The commit failed, so the read-snapshot/transaction handle is still open — release it, as the
2788
2948
  // batched-eviction path does on its own commit failures. committed===true skips the finally abort.
2789
2949
  try {
@@ -2794,7 +2954,7 @@ function makeTable(options) {
2794
2954
  logger_ts_1.logger.trace?.('Abandoned eviction of busy record', id);
2795
2955
  else
2796
2956
  logger_ts_1.logger.warn?.('Error evicting record', id, error);
2797
- });
2957
+ }));
2798
2958
  }
2799
2959
  finally {
2800
2960
  if (!committed) {
@@ -4076,23 +4236,26 @@ function makeTable(options) {
4076
4236
  },
4077
4237
  };
4078
4238
  this.#savingOperation = write;
4079
- // `@embed` hook must run before `addWrite` so the embedder's vector is on the
4080
- // record when `commit` runs. (The txn `before` slot runs after commit, which
4081
- // suits blob writes but not embedding, where the vector must be present at commit.)
4082
- // Known limitation of this write-time placement (a validate-time alternative was
4083
- // tried and reverted as a Harper-foreign pattern): the embedder sees this write's
4084
- // payload, before table validation — so a write that later fails validation still
4085
- // calls the backend, and a tracked-instance mutation (update(id,{}); row.source=…;
4086
- // save()) that sets the source via accessors after update() won't re-embed. A
4087
- // resource-layer re-embed is the proper fix; tracked as a follow-up.
4088
- const embedBefore = (0, embedHook_ts_1.buildEmbedBefore)(recordUpdate, context, options, _a.embedAttributes, _a.userEmbedders);
4239
+ // The hooks run before `addWrite` so the derived values are on the record at commit (the
4240
+ // txn `before` slot runs after commit). They see the payload before table validation, and a
4241
+ // tracked-instance mutation that sets the source via accessors after update() is not seen.
4242
+ let modelHooksBefore;
4243
+ try {
4244
+ modelHooksBefore =
4245
+ (_a.embedAttributes.length || _a.decideAttributes.length) && !(0, databases_ts_1.isReadOnlyMode)()
4246
+ ? (0, embedHook_ts_1.combineWriteHooks)((0, embedHook_ts_1.buildEmbedBefore)(recordUpdate, context, options, _a.embedAttributes, _a.userEmbedders), (0, decideHook_ts_1.buildDecideBefore)(recordUpdate, context, options, _a.decideAttributes, _a.userDeciders))
4247
+ : undefined;
4248
+ }
4249
+ catch (err) {
4250
+ return Promise.reject(err);
4251
+ }
4089
4252
  const proceed = () => {
4090
4253
  // On a source/replication apply (`isNotification`), the record's already-saved blobs were
4091
4254
  // received out-of-band for THIS write, so track them for skip/abort cleanup (harper-pro#406).
4092
4255
  write.beforeIntermediate = preCommitBlobsForRecordBefore(write, recordUpdate, undefined, undefined, options?.isNotification);
4093
4256
  return transaction.addWrite(write);
4094
4257
  };
4095
- return embedBefore ? embedBefore().then(proceed) : proceed();
4258
+ return modelHooksBefore ? modelHooksBefore(context?.signal).then(proceed) : proceed();
4096
4259
  }
4097
4260
  async delete(target) {
4098
4261
  if (isSearchTarget(target)) {
@@ -5326,6 +5489,10 @@ function makeTable(options) {
5326
5489
  // messages during this loop because the snapshot:false cursor will pick them up itself.
5327
5490
  pendingRealTimeQueue = null;
5328
5491
  dropDuringReplay = true;
5492
+ // subscription.startTime is the resume cursor (exclusive) and RocksDB gives every record of a
5493
+ // transaction the same txnLogKey, so it only moves to a key once all of that key's records are
5494
+ // handled; an early return leaves it before a partly delivered transaction.
5495
+ let handledTxnLogKey;
5329
5496
  try {
5330
5497
  for (const auditRecord of auditStore.getRange({
5331
5498
  start: startTime,
@@ -5342,20 +5509,25 @@ function makeTable(options) {
5342
5509
  continue;
5343
5510
  if ((0, auditStore_ts_1.isLockControlType)(auditRecord.type))
5344
5511
  continue;
5512
+ if (handledTxnLogKey !== undefined && auditRecord.txnLogKey !== handledTxnLogKey) {
5513
+ subscription.startTime = handledTxnLogKey;
5514
+ }
5345
5515
  const id = auditRecord.recordId;
5346
- subscription.startTime = auditRecord.txnLogKey;
5347
5516
  if (thisId == null || isDescendantId(thisId, id)) {
5348
5517
  const event = eventFromAudit(id, auditRecord, auditRecord.txnLogKey);
5349
- if (!event)
5350
- continue;
5351
- if (!send(event))
5352
- return;
5353
- if (subscription.queue?.length > EVENT_HIGH_WATER_MARK) {
5354
- if ((await subscription.waitForDrain()) === false)
5518
+ if (event) {
5519
+ if (!send(event))
5355
5520
  return;
5521
+ if (subscription.queue?.length > EVENT_HIGH_WATER_MARK) {
5522
+ if ((await subscription.waitForDrain()) === false)
5523
+ return;
5524
+ }
5356
5525
  }
5357
5526
  }
5527
+ handledTxnLogKey = auditRecord.txnLogKey;
5358
5528
  }
5529
+ if (handledTxnLogKey !== undefined)
5530
+ subscription.startTime = handledTxnLogKey;
5359
5531
  }
5360
5532
  finally {
5361
5533
  // replay is done, we can start sending real-time messages again
@@ -6318,10 +6490,19 @@ function makeTable(options) {
6318
6490
  static updatedAttributes() {
6319
6491
  // Refresh on every call: schema reload mutates `attributes` in place, so the
6320
6492
  // class-construction snapshot would otherwise go stale.
6493
+ // Declarations are refused before they are saved; here a descriptor an earlier build
6494
+ // accepted must still load, so a violation is logged and the table keeps working.
6495
+ try {
6496
+ (0, embedHook_ts_1.assertDerivedFieldOwnership)(this.attributes);
6497
+ }
6498
+ catch (error) {
6499
+ console.error(`Derived attributes of table "${tableName}" conflict: ${error.message}`);
6500
+ }
6321
6501
  this.embedAttributes = this.attributes.filter((a) => a?.embed);
6502
+ this.decideAttributes = this.attributes.filter((a) => a?.decide);
6322
6503
  expiresAtProperty = this.attributes.find((attribute) => attribute.expiresAt);
6323
- // Drop registry entries for attributes that are no longer `@embed`, so a dropped
6324
- // directive doesn't leave a stale embedder or block a default refresh on re-add.
6504
+ // Drop registry entries for attributes that are no longer `@embed` / `@decide`, so a dropped
6505
+ // directive doesn't leave a stale hook or block a default refresh on re-add.
6325
6506
  const embedNames = new Set(this.embedAttributes.map((a) => a.name));
6326
6507
  for (const name of Object.keys(this.userEmbedders))
6327
6508
  if (!embedNames.has(name))
@@ -6329,6 +6510,13 @@ function makeTable(options) {
6329
6510
  for (const name of this.userSetEmbedders)
6330
6511
  if (!embedNames.has(name))
6331
6512
  this.userSetEmbedders.delete(name);
6513
+ const decideNames = new Set(this.decideAttributes.map((a) => a.name));
6514
+ for (const name of Object.keys(this.userDeciders))
6515
+ if (!decideNames.has(name))
6516
+ delete this.userDeciders[name];
6517
+ for (const name of this.userSetDeciders)
6518
+ if (!decideNames.has(name))
6519
+ this.userSetDeciders.delete(name);
6332
6520
  propertyResolvers = this.propertyResolvers = {
6333
6521
  $id: (object, context, entry) => ({ value: entry.key }),
6334
6522
  $updatedtime: (object, context, entry) => entry.version,
@@ -6368,9 +6556,12 @@ function makeTable(options) {
6368
6556
  const computed = attribute.computed;
6369
6557
  // Register the default embedder unless an author override is set. Sits outside
6370
6558
  // the resolver chain below so `@embed` fields still flow through auto-HNSW indexing.
6371
- if (attribute.embed && !_a.userSetEmbedders.has(attribute.name)) {
6559
+ if (attribute.embed && !this.userSetEmbedders.has(attribute.name)) {
6372
6560
  this.userEmbedders[attribute.name] = (0, embedHook_ts_1.createDefaultEmbedder)(attribute.embed);
6373
6561
  }
6562
+ if (attribute.decide && !this.userSetDeciders.has(attribute.name)) {
6563
+ this.userDeciders[attribute.name] = (0, decideHook_ts_1.createDefaultDecider)(attribute.decide);
6564
+ }
6374
6565
  if (relationship) {
6375
6566
  if (attribute.indexed) {
6376
6567
  console.error(`A relationship property can not be directly indexed, (but you may want to index the foreign key attribute)`);
@@ -6597,6 +6788,26 @@ function makeTable(options) {
6597
6788
  this.userEmbedders[attribute_name] = embedder;
6598
6789
  this.userSetEmbedders.add(attribute_name);
6599
6790
  }
6791
+ /**
6792
+ * Override the default decider for a `@decide` attribute. Return `{ value, probability }`
6793
+ * to store at the attribute and its confidence attribute, or `null` to clear both. The
6794
+ * value must be one the directive allows, and the probability is required when the
6795
+ * directive names a confidence attribute. Like an embedder, the decider receives the write
6796
+ * payload, not the post-merge record, and a `signal` that aborts when a sibling hook fails.
6797
+ */
6798
+ static setDecideAttribute(attribute_name, decider) {
6799
+ const attribute = (0, search_ts_1.findAttribute)(attributes, attribute_name);
6800
+ if (!attribute) {
6801
+ console.error(`The attribute "${attribute_name}" does not exist in the table "${tableName}"`);
6802
+ return;
6803
+ }
6804
+ if (!attribute.decide) {
6805
+ console.error(`The attribute "${attribute_name}" is not declared with @decide in the table "${tableName}"`);
6806
+ return;
6807
+ }
6808
+ this.userDeciders[attribute_name] = decider;
6809
+ this.userSetDeciders.add(attribute_name);
6810
+ }
6600
6811
  static async deleteHistory(endTime = 0, cleanupDeletedRecords = false) {
6601
6812
  const maxConcurrentRemovals = isRocksDB ? MAX_CONCURRENT_HISTORY_REMOVALS : MAX_CONCURRENT_LMDB_HISTORY_REMOVALS;
6602
6813
  const inFlightRemovals = new Set();
@@ -6760,31 +6971,105 @@ function makeTable(options) {
6760
6971
  } while (count < 1000 && nextVersion);
6761
6972
  return history.reverse();
6762
6973
  }
6763
- static clear() {
6764
- // clear the primary store and every secondary index dbi (same pattern used by
6765
- // runIndexing when rebuilding from scratch), so clear() doesn't leave stale
6766
- // index entries pointing at records that no longer exist.
6767
- const promises = [primaryStore.clear()];
6768
- for (const key in indices) {
6769
- const index = indices[key];
6770
- index.customIndex?.resetDerivedStorage?.();
6771
- promises.push(index.clearAsync ? index.clearAsync() : index.clear());
6974
+ static async clear() {
6975
+ const rootStore = primaryStore.rootStore;
6976
+ const assertNoFullTextDeclaration = () => {
6977
+ const namedPrimaryDescriptor = dbisDb.getSync(`${tableName}/${primaryKey}`);
6978
+ const primaryDescriptor = namedPrimaryDescriptor?.isPrimaryKey
6979
+ ? namedPrimaryDescriptor
6980
+ : dbisDb.getSync(`${tableName}/`);
6981
+ if (_a.fullTextIndexes.length > 0 ||
6982
+ (rootStore instanceof rocksdb_js_1.RocksDatabase &&
6983
+ (0, fullTextSchema_ts_1.persistedFullTextIndexNames)(primaryDescriptor?.fullTextIndexes).length > 0))
6984
+ throw new hdbError_ts_1.ClientError(`Table.clear() is not supported on full-text table '${databaseName}.${tableName}' until whole-table invalidation is crash-safe`, 501);
6985
+ };
6986
+ let releaseFullTextClearFence;
6987
+ let waitForExistingClear;
6988
+ let admission;
6989
+ if (rootStore instanceof rocksdb_js_1.RocksDatabase) {
6990
+ admission = withUpdateAttributesLockNonBlocking(rootStore, `clear table '${databaseName}.${tableName}'`, () => {
6991
+ assertNoFullTextDeclaration();
6992
+ releaseFullTextClearFence = (0, derivedIndexRegistry_ts_1.acquireFullTextClearFence)(rootStore, tableId);
6993
+ if (!releaseFullTextClearFence)
6994
+ waitForExistingClear = (0, derivedIndexRegistry_ts_1.waitForFullTextClear)(rootStore, tableId);
6995
+ });
6772
6996
  }
6773
- return Promise.all(promises);
6997
+ else
6998
+ assertNoFullTextDeclaration();
6999
+ const startClear = () => {
7000
+ if (waitForExistingClear)
7001
+ return waitForExistingClear.then(() => _a.clear());
7002
+ // clear the primary store and every secondary index dbi (same pattern used by
7003
+ // runIndexing when rebuilding from scratch), so clear() doesn't leave stale
7004
+ // index entries pointing at records that no longer exist.
7005
+ const promises = [];
7006
+ const settleStartedClears = (synchronousFailure) => Promise.allSettled(promises)
7007
+ .then((results) => {
7008
+ if (synchronousFailure)
7009
+ throw synchronousFailure.error;
7010
+ const values = [];
7011
+ for (const result of results) {
7012
+ if (result.status === 'rejected')
7013
+ throw result.reason;
7014
+ values.push(result.value);
7015
+ }
7016
+ return values;
7017
+ })
7018
+ .finally(releaseFullTextClearFence);
7019
+ try {
7020
+ promises.push(primaryStore.clear());
7021
+ for (const key in indices) {
7022
+ const index = indices[key];
7023
+ index.customIndex?.resetDerivedStorage?.();
7024
+ promises.push(index.clearAsync ? index.clearAsync() : index.clear());
7025
+ }
7026
+ return settleStartedClears();
7027
+ }
7028
+ catch (error) {
7029
+ if (promises.length > 0)
7030
+ return settleStartedClears({ error });
7031
+ releaseFullTextClearFence?.();
7032
+ throw error;
7033
+ }
7034
+ };
7035
+ return admission instanceof Promise ? admission.then(startClear) : startClear();
6774
7036
  }
6775
7037
  /** Release everything makeTable() registered process-wide; the class must not be used afterwards. */
6776
7038
  static cleanup() {
6777
7039
  disposed = true;
6778
7040
  _a.getResource = unavailableResource;
6779
- _a.derivedIndexRuntime
7041
+ void _a.derivedIndexRuntime
6780
7042
  ?.close()
6781
7043
  .catch((error) => logger_ts_1.logger.warn?.(`Derived index shutdown failed for ${databaseName}.${tableName}`, error));
6782
- clearTimeout(cleanupTimer);
6783
- settlePendingCleanup();
6784
- clearInterval(recordExpirationInterval);
7044
+ stopMaintenance();
6785
7045
  deleteCallbackHandle?.remove();
6786
7046
  (0, storageReclamation_ts_1.removeStorageReclamationHandler)(primaryStore.path, reclamationHandler);
6787
7047
  }
7048
+ static async closeMaintenance(deadline) {
7049
+ const startedAt = Date.now();
7050
+ const defaultTimeout = (0, DatabaseTransaction_ts_1.getDatabaseCommitDrainTimeoutMilliseconds)();
7051
+ const timeoutMilliseconds = deadline === undefined ? defaultTimeout : Math.max(0, deadline - startedAt);
7052
+ const resolvedDeadline = deadline ?? startedAt + timeoutMilliseconds;
7053
+ stopMaintenance();
7054
+ const timeoutError = () => new hdbError_ts_1.DatabaseDrainTimeoutError(databaseName, timeoutMilliseconds);
7055
+ await (0, when_ts_1.settleBeforeDeadline)([lastEvictionCompletion, recordExpirationCompletion], resolvedDeadline, timeoutError);
7056
+ for (;;) {
7057
+ const pending = [...maintenanceCommits];
7058
+ if (pending.length === 0)
7059
+ return;
7060
+ await (0, when_ts_1.settleBeforeDeadline)(pending, resolvedDeadline, timeoutError);
7061
+ }
7062
+ }
7063
+ static resumeMaintenance() {
7064
+ if (disposed || !maintenanceClosed)
7065
+ return;
7066
+ maintenanceClosed = false;
7067
+ lastCleanupInterval = undefined;
7068
+ if (expirationScanScheduled || evictionMs)
7069
+ scheduleCleanup();
7070
+ if (expiresAtProperty && !recordExpirationInterval)
7071
+ runRecordExpirationEviction();
7072
+ }
6788
7073
  static _readTxnForContext(context) {
6789
7074
  return txnForContext(context).getReadTxn();
6790
7075
  }
@@ -6802,7 +7087,7 @@ function makeTable(options) {
6802
7087
  }
6803
7088
  }, () => {
6804
7089
  throw new hdbError_ts_1.ServerError('Service unavailable, exceeded request queue limit for resolving cache record', 503);
6805
- });
7090
+ }, undefined, `cache resolution for ${tableName}`);
6806
7091
  try {
6807
7092
  TableResource.updatedAttributes(); // on creation, update accessors as well
6808
7093
  if (expirationMs) {
@@ -7445,7 +7730,8 @@ function makeTable(options) {
7445
7730
  replacingVersion: existingVersion,
7446
7731
  // Once dropTable() has started, no new source-fill write may begin; still resolve the
7447
7732
  // caller's read with fresh source data, just don't cache it into a table that's going away.
7448
- noCacheStore: droppingTable,
7733
+ // No write can commit on a read-only node, so a fill there is served but never staged.
7734
+ noCacheStore: droppingTable || (0, databases_ts_1.isReadOnlyMode)(),
7449
7735
  source: null,
7450
7736
  transaction: undefined,
7451
7737
  expiresAt: undefined,
@@ -7769,14 +8055,11 @@ function makeTable(options) {
7769
8055
  }
7770
8056
  },
7771
8057
  };
7772
- // The cache-from-source write bypasses `_writeUpdate`, so wire the embed hook here
7773
- // too (always the originating node). It runs after the client GET has resolved with
7774
- // fresh source data, so it's a background commit: an embedder failure aborts the cache
7775
- // write via the outer error handler (row re-embeds next read) and never reaches the
7776
- // caller. Source-resolution errors are handled earlier, with the stale-data fallback.
7777
- const embedBefore = (0, embedHook_ts_1.buildEmbedBefore)(updatedRecord, sourceContext, undefined, TableResource.embedAttributes, TableResource.userEmbedders);
7778
- if (embedBefore)
7779
- await embedBefore();
8058
+ // The fill is shared by every reader, so it takes no request signal; a hook failure aborts
8059
+ // only the cache write.
8060
+ const modelHooksBefore = (0, embedHook_ts_1.combineWriteHooks)((0, embedHook_ts_1.buildEmbedBefore)(updatedRecord, sourceContext, undefined, TableResource.embedAttributes, TableResource.userEmbedders), (0, decideHook_ts_1.buildDecideBefore)(updatedRecord, sourceContext, undefined, TableResource.decideAttributes, TableResource.userDeciders));
8061
+ if (modelHooksBefore)
8062
+ await modelHooksBefore();
7780
8063
  if (droppingTable) {
7781
8064
  // Re-check right before staging the write: dropTable() may have started
7782
8065
  // while we were awaiting the embed step above (harper#1381).
@@ -7811,6 +8094,19 @@ function makeTable(options) {
7811
8094
  throw new hdbError_ts_1.ClientError('Can not specify replication confirmation without super user permissions', 403);
7812
8095
  return true;
7813
8096
  }
8097
+ function trackMaintenanceCommit(commit) {
8098
+ const tracked = commit.finally(() => maintenanceCommits.delete(tracked));
8099
+ maintenanceCommits.add(tracked);
8100
+ return tracked;
8101
+ }
8102
+ function stopMaintenance() {
8103
+ maintenanceClosed = true;
8104
+ clearTimeout(cleanupTimer);
8105
+ cleanupTimer = undefined;
8106
+ settlePendingCleanup();
8107
+ clearInterval(recordExpirationInterval);
8108
+ recordExpirationInterval = undefined;
8109
+ }
7814
8110
  // RocksDB-only: coalesces eviction/tombstone removals into shared transactions so the cleanup
7815
8111
  // scan pays one commit per batch instead of one per record. Descriptors hold only the decoded
7816
8112
  // primary key and the version seen during the scan (both stable primitives — the scanned record
@@ -7875,7 +8171,7 @@ function makeTable(options) {
7875
8171
  return;
7876
8172
  }
7877
8173
  try {
7878
- await transaction.commit();
8174
+ await (0, DatabaseTransaction_ts_1.commitTrackedRocksTransaction)(transaction, primaryStore);
7879
8175
  return;
7880
8176
  }
7881
8177
  catch (error) {
@@ -7926,7 +8222,7 @@ function makeTable(options) {
7926
8222
  }
7927
8223
  function scheduleCleanup(priority) {
7928
8224
  // a reclamation run may still hold this class's handler after cleanup(); a promise here would never settle
7929
- if (disposed)
8225
+ if (disposed || maintenanceClosed)
7930
8226
  return;
7931
8227
  let runImmediately = false;
7932
8228
  if (priority) {
@@ -7938,7 +8234,10 @@ function makeTable(options) {
7938
8234
  // Periodically evict expired records and deleted records searching for records who expiresAt timestamp is before now
7939
8235
  if (cleanupInterval === lastCleanupInterval && !runImmediately)
7940
8236
  return;
7941
- lastCleanupInterval = cleanupInterval;
8237
+ // Left unrecorded by a thread with no worker index yet: under threads: 0 the main thread loads tables
8238
+ // before it becomes worker 0, and its next call for this interval has to arm the scan then.
8239
+ if ((0, manageThreads_js_1.getWorkerIndex)() !== undefined)
8240
+ lastCleanupInterval = cleanupInterval;
7942
8241
  if ((0, manageThreads_js_1.ownsStoreMaintenance)(primaryStore.path) || (ttlConfiguredByApplication && (0, manageThreads_js_1.isDedicatedWorker)())) {
7943
8242
  // run on the last thread so we aren't overloading lower-numbered threads
7944
8243
  if (cleanupTimer)
@@ -7968,11 +8267,13 @@ function makeTable(options) {
7968
8267
  ? Date.now()
7969
8268
  : Math.ceil((Date.now() - startOfYear.getTime()) / nextInterval) * nextInterval + startOfYear.getTime();
7970
8269
  const startNextTimer = (nextScheduled) => {
7971
- if (disposed)
8270
+ if (disposed || maintenanceClosed)
7972
8271
  return;
7973
8272
  logger_ts_1.logger.trace?.(`Scheduled next cleanup scan at ${new Date(nextScheduled)}`);
7974
8273
  // noinspection JSVoidFunctionReturnValueUsed
7975
8274
  cleanupTimer = setTimeout(() => (lastEvictionCompletion = lastEvictionCompletion.then(async () => {
8275
+ if (disposed || maintenanceClosed)
8276
+ return;
7976
8277
  // schedule the next run for when the next cleanup interval should occur (or now if it is in the past)
7977
8278
  startNextTimer(Math.max(nextScheduled + cleanupInterval, Date.now()));
7978
8279
  const rootStore = primaryStore.rootStore;
@@ -8022,6 +8323,8 @@ function makeTable(options) {
8022
8323
  versions: true,
8023
8324
  lazy: true, // only want to access metadata most of the time
8024
8325
  })) {
8326
+ if (maintenanceClosed)
8327
+ break;
8025
8328
  const { key, value: record, version, expiresAt, metadataFlags } = entry;
8026
8329
  // if there is no auditing cleanup and we are tracking deletion, need to do cleanup of
8027
8330
  // these deletion entries (LMDB audit cleanup has its own scheduled job for this)
@@ -8055,6 +8358,7 @@ function makeTable(options) {
8055
8358
  }
8056
8359
  await rest();
8057
8360
  }
8361
+ await Promise.all(outstandingCleanupOperations.filter(Boolean));
8058
8362
  if (batcher)
8059
8363
  await batcher.drain();
8060
8364
  logger_ts_1.logger.debug?.(`Finished cleanup scan for ${tableName}, evicted ${count} entries`);
@@ -8083,45 +8387,59 @@ function makeTable(options) {
8083
8387
  // Periodically evict expired records, searching for records who expiresAt timestamp is before now
8084
8388
  if ((0, manageThreads_js_1.ownsStoreExpiration)(primaryStore.path) || (ttlConfiguredByApplication && (0, manageThreads_js_1.isDedicatedWorker)())) {
8085
8389
  // we want to run the pruning of expired records on only one thread so we don't have conflicts in evicting
8086
- recordExpirationInterval = setInterval(async () => {
8390
+ recordExpirationInterval = setInterval(() => {
8087
8391
  // go through each database and table and then search for expired entries
8088
8392
  // find any entries that are set to expire before now
8089
8393
  // updatedAttributes() clears expiresAtProperty when a live redeclaration drops the directive,
8090
8394
  // and there is nothing left for this interval to scan by
8091
- if (disposed || runningRecordExpiration || !expiresAtProperty)
8395
+ if (disposed || maintenanceClosed || runningRecordExpiration || !expiresAtProperty)
8092
8396
  return;
8093
8397
  runningRecordExpiration = true;
8094
- try {
8398
+ recordExpirationCompletion = (async () => {
8095
8399
  const expiresAtName = expiresAtProperty.name;
8096
8400
  const index = indices[expiresAtName];
8097
8401
  if (!index)
8098
8402
  throw new Error(`expiresAt attribute ${expiresAtProperty} must be indexed`);
8099
- for (const key of index.getRange({
8403
+ const inFlight = new Set();
8404
+ const trackInFlight = (operation) => {
8405
+ const tracked = operation.finally(() => inFlight.delete(tracked));
8406
+ inFlight.add(tracked);
8407
+ if (inFlight.size >= 50)
8408
+ return Promise.race(inFlight);
8409
+ };
8410
+ expirationScan: for (const key of index.getRange({
8100
8411
  start: true,
8101
8412
  values: false,
8102
8413
  end: Date.now(),
8103
8414
  snapshot: false,
8104
8415
  })) {
8105
8416
  for (const id of index.getValues(key)) {
8417
+ if (maintenanceClosed)
8418
+ break expirationScan;
8106
8419
  const recordEntry = primaryStore.getEntry(id);
8107
8420
  if (!recordEntry?.value) {
8108
8421
  // cleanup the index if the record is gone
8109
- primaryStore.ifVersion(id, recordEntry?.version, () => index.remove(key, id));
8422
+ const repair = trackMaintenanceCommit(Promise.resolve(primaryStore.ifVersion(id, recordEntry?.version, () => index.remove(key, id))).catch((error) => logger_ts_1.logger.warn?.('Error removing stale expiration index entry', id, error)));
8423
+ const backpressure = trackInFlight(repair);
8424
+ if (backpressure)
8425
+ await backpressure;
8110
8426
  }
8111
8427
  else if (recordEntry.value[expiresAtName] < Date.now()) {
8112
8428
  // make sure the record hasn't changed and won't change while removing
8113
- TableResource.evict(id, recordEntry.value, recordEntry.version);
8429
+ const eviction = TableResource.evict(id, recordEntry.value, recordEntry.version);
8430
+ if (eviction) {
8431
+ const backpressure = trackInFlight(eviction);
8432
+ if (backpressure)
8433
+ await backpressure;
8434
+ }
8114
8435
  }
8115
8436
  }
8116
8437
  await rest();
8117
8438
  }
8118
- }
8119
- catch (error) {
8120
- logger_ts_1.logger.error?.('Error in evicting old records', error);
8121
- }
8122
- finally {
8123
- runningRecordExpiration = false;
8124
- }
8439
+ await Promise.all(inFlight);
8440
+ })()
8441
+ .catch((error) => logger_ts_1.logger.error?.('Error in evicting old records', error))
8442
+ .finally(() => (runningRecordExpiration = false));
8125
8443
  }, RECORD_PRUNING_INTERVAL).unref();
8126
8444
  }
8127
8445
  }