@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
@@ -0,0 +1,892 @@
1
+ "use strict";
2
+ var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
3
+ if (k2 === undefined) k2 = k;
4
+ var desc = Object.getOwnPropertyDescriptor(m, k);
5
+ if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
6
+ desc = { enumerable: true, get: function() { return m[k]; } };
7
+ }
8
+ Object.defineProperty(o, k2, desc);
9
+ }) : (function(o, m, k, k2) {
10
+ if (k2 === undefined) k2 = k;
11
+ o[k2] = m[k];
12
+ }));
13
+ var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
14
+ Object.defineProperty(o, "default", { enumerable: true, value: v });
15
+ }) : function(o, v) {
16
+ o["default"] = v;
17
+ });
18
+ var __importStar = (this && this.__importStar) || (function () {
19
+ var ownKeys = function(o) {
20
+ ownKeys = Object.getOwnPropertyNames || function (o) {
21
+ var ar = [];
22
+ for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
23
+ return ar;
24
+ };
25
+ return ownKeys(o);
26
+ };
27
+ return function (mod) {
28
+ if (mod && mod.__esModule) return mod;
29
+ var result = {};
30
+ if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
31
+ __setModuleDefault(result, mod);
32
+ return result;
33
+ };
34
+ })();
35
+ var __importDefault = (this && this.__importDefault) || function (mod) {
36
+ return (mod && mod.__esModule) ? mod : { "default": mod };
37
+ };
38
+ Object.defineProperty(exports, "__esModule", { value: true });
39
+ exports.reportClaimProgress = reportClaimProgress;
40
+ exports.claimDeadlineFor = claimDeadlineFor;
41
+ exports.getOrCreateBranch = getOrCreateBranch;
42
+ exports.closeBranchAt = closeBranchAt;
43
+ exports.removeBranches = removeBranches;
44
+ exports.applicationHasBranchStorage = applicationHasBranchStorage;
45
+ exports.removeBranchesForApplication = removeBranchesForApplication;
46
+ exports.prepareBranches = prepareBranches;
47
+ const node_fs_1 = require("node:fs");
48
+ const promises_1 = require("node:fs/promises");
49
+ const node_path_1 = require("node:path");
50
+ const harper_logger_ts_1 = __importDefault(require("../utility/logging/harper_logger.js"));
51
+ const hdbTerms_ts_1 = require("../utility/hdbTerms.js");
52
+ const common_validators_ts_1 = require("../validation/common_validators.js");
53
+ const env = __importStar(require("../utility/environment/environmentManager.js"));
54
+ const blobBackup_ts_1 = require("../dataLayer/blobBackup.js");
55
+ const blob_ts_1 = require("./blob.js");
56
+ const rocksdb_js_1 = require("@harperfast/rocksdb-js");
57
+ const databases_ts_1 = require("./databases.js");
58
+ const replayLogs_ts_1 = require("./replayLogs.js");
59
+ /**
60
+ * Private per-application forks of a database, for running several variants of an application against
61
+ * isolated copies of the same data (harper#642).
62
+ *
63
+ * A fork is durable and its location is derived only from the application and database names, so it
64
+ * survives restarts and every node in a cluster names it identically — the identity the data needs
65
+ * to be addressed, and eventually replicated, cluster-wide.
66
+ *
67
+ * Every worker thread loads the same applications, so all of them ask for the same branch at once.
68
+ * Only one may take the checkpoint — a second `createCheckpoint` against a populated directory fails,
69
+ * and worse, two checkpoints would give two threads divergent private data for one application. The
70
+ * claim is settled through a state word in a buffer the base store shares across threads, which is
71
+ * the same mechanism the blob id allocator and the HNSW node counter already use.
72
+ */
73
+ const UNCLAIMED = 0n;
74
+ const CREATING = 1n;
75
+ const READY = 2n;
76
+ const CLAIM_STATE = 0;
77
+ const CLAIM_PROGRESS = 1;
78
+ const MAX_NAME_LENGTH = common_validators_ts_1.commonValidators.schema_length.maximum;
79
+ /** How long a loser waits on a winner that reports NO progress before giving up on the branch; the
80
+ * winner's replay budget is added on top where the deadline is built (`openOrCreate`). Progress is
81
+ * reported per checkpoint and per cloned file, so that is the granularity this bounds: a single
82
+ * unit that takes longer than the budget still expires it, which needs a byte copy (no hard links)
83
+ * of one blob tens of gigabytes large. */
84
+ const CLAIM_TIMEOUT_MS = 10 * 60 * 1000;
85
+ // Keyed on the in-flight open, not the finished one: within a thread the losers of the claim wake on
86
+ // READY at the same moment and would otherwise each try to open the directory the winner just opened.
87
+ const branchesByPath = new Map();
88
+ /**
89
+ * The claim word is process-local shared memory (in-process only, seeded UNCLAIMED each boot) and
90
+ * must stay that way: the winner's tail replay runs exactly once per boot because every boot starts
91
+ * from UNCLAIMED. A durable or cross-process claim would read READY on restart and silently skip
92
+ * recovery.
93
+ */
94
+ function claimStateFor(baseName, branchPath) {
95
+ const baseStore = (0, databases_ts_1.database)({ database: baseName, table: undefined });
96
+ const seed = new BigInt64Array([UNCLAIMED, 0n]);
97
+ return new BigInt64Array(baseStore.getUserSharedBuffer(`branch-claim:${branchPath}`, seed.buffer));
98
+ }
99
+ function reportClaimProgress(state) {
100
+ Atomics.add(state, CLAIM_PROGRESS, 1n);
101
+ }
102
+ /**
103
+ * A deadline that follows the winner's progress rather than the clock alone. The claim window covers a
104
+ * hard-link clone of the base's entire blob tree, so any fixed budget is wrong for a large enough base:
105
+ * every waiting thread's load fails while the winner is healthily copying, the winner then publishes,
106
+ * and the next deploy succeeds -- so a real outage reads as a flap. Restarting the budget on each unit
107
+ * of reported work bounds a winner that has STOPPED rather than one that is merely slow.
108
+ */
109
+ function claimDeadlineFor(state, budget) {
110
+ let seen = Atomics.load(state, CLAIM_PROGRESS);
111
+ let at = Date.now() + budget;
112
+ return () => {
113
+ const progress = Atomics.load(state, CLAIM_PROGRESS);
114
+ if (progress !== seen) {
115
+ seen = progress;
116
+ at = Date.now() + budget;
117
+ }
118
+ return at;
119
+ };
120
+ }
121
+ /**
122
+ * Reproduce the base's blob roots as the branch's own, hard-linking each file so the OS inode
123
+ * refcount does the reference counting: the branch and base share the bytes, each holds its own
124
+ * directory entry, and the data is freed only when the last link to it goes. That is what lets the
125
+ * branch keep its own ID allocator -- `getNextFileId` seeds from the highest filename already in its
126
+ * directory, and the clone carries the base's filenames -- with no shared allocator, no high-water
127
+ * mark, and no gated deletion (harper#644).
128
+ *
129
+ * A blob that is mid-write or already reclaimed is substituted with a marker rather than linked. A
130
+ * partially-written blob would otherwise be linked as a whole one, and worse, the abort that follows
131
+ * stamps PENDING onto that inode IN PLACE -- through the branch's link as well, since it is the same
132
+ * inode. The marker makes that blob fail loudly in the branch instead.
133
+ */
134
+ async function cloneBlobRoots(baseName, baseRoots, branchRoots, progress) {
135
+ let substituted = 0;
136
+ let copied = 0;
137
+ // A row's `storageIndex` is a position in the branch's own list, and its marker records every entry.
138
+ for (let index = 0; index < branchRoots.length; index++) {
139
+ const staging = `${branchRoots[index]}.staging`;
140
+ await (0, promises_1.rm)(staging, { recursive: true, force: true });
141
+ // Created up front rather than left to the walk: a base with no blobs at all copies nothing, and
142
+ // the branch still needs its own (empty) root to exist so its allocator starts where the base's
143
+ // would have.
144
+ await (0, promises_1.mkdir)(staging, { recursive: true });
145
+ const source = baseRoots[index];
146
+ if (source) {
147
+ const counts = await (0, blobBackup_ts_1.copyTree)(source, staging, true, {
148
+ gone: 'blob was already reclaimed from the base when this branch was created',
149
+ pending: 'blob was still being written to the base when this branch was created',
150
+ }, progress);
151
+ substituted += counts.substituted;
152
+ copied += counts.copied;
153
+ }
154
+ await (0, promises_1.rm)(branchRoots[index], { recursive: true, force: true });
155
+ await (0, promises_1.mkdir)((0, node_path_1.dirname)(branchRoots[index]), { recursive: true });
156
+ await (0, promises_1.rename)(staging, branchRoots[index]);
157
+ progress();
158
+ }
159
+ if (substituted > 0) {
160
+ harper_logger_ts_1.default.warn?.(`Branch of '${baseName}' substituted ${substituted} blob file(s) with markers because they were ` +
161
+ `mid-write or already reclaimed when it was created; reads of those blobs in the branch will fail`);
162
+ }
163
+ if (copied > 0) {
164
+ // The hard link is the whole design: without it the branch is a second full copy of the base's
165
+ // blobs, taken while the application is loading, and nothing else in the log would say so.
166
+ harper_logger_ts_1.default.warn?.(`Branch of '${baseName}' could not hard-link ${copied} blob file(s) and copied their bytes instead; ` +
167
+ `on a filesystem without hard links a branch costs a full copy of the base's blobs, in disk and ` +
168
+ `in the time every application load that creates one takes`);
169
+ }
170
+ }
171
+ /**
172
+ * Remove the given blob roots and any staging siblings left by an interrupted clone. The roots are
173
+ * passed in rather than re-derived, because a published branch owns exactly what its completion
174
+ * marker recorded: deriving them from current configuration would let a `storage.blobPaths` entry
175
+ * added since -- one that may already hold a `<storeName>` directory restored from elsewhere --
176
+ * be deleted by a branch that never wrote it.
177
+ *
178
+ * Reports whether every removal succeeded, because a root left behind still belongs to this
179
+ * identity and the caller must not hand the name back while it does.
180
+ */
181
+ async function removeBlobRoots(roots) {
182
+ let removed = true;
183
+ for (const root of roots) {
184
+ for (const target of [root, `${root}.staging`]) {
185
+ await (0, promises_1.rm)(target, { recursive: true, force: true }).catch((error) => {
186
+ removed = false;
187
+ harper_logger_ts_1.default.warn?.(`Could not remove branch blob root ${target}`, error);
188
+ });
189
+ }
190
+ }
191
+ return removed;
192
+ }
193
+ const COMPLETION_MARKER = '.branch-complete';
194
+ /**
195
+ * Does this directory hold database data? Not "does CURRENT exist" -- a store that lost CURRENT still
196
+ * has its MANIFEST and SSTs, and a single missing file cannot stand for "there is nothing here to
197
+ * lose". Anything that answers yes is never deleted, only refused.
198
+ */
199
+ function holdsStoreData(branchPath) {
200
+ let entries;
201
+ try {
202
+ entries = (0, node_fs_1.readdirSync)(branchPath);
203
+ }
204
+ catch {
205
+ return false;
206
+ }
207
+ return entries.some((name) => name === 'CURRENT' || /^(MANIFEST|OPTIONS)-/.test(name) || name.endsWith('.sst') || name.endsWith('.log'));
208
+ }
209
+ /**
210
+ * What a branch directory on disk actually is, and in particular whether it may be destroyed.
211
+ *
212
+ * A branch's blob roots live on the volumes named by `storage.blobPaths`, not inside the branch
213
+ * directory, so the two cannot be published by one atomic rename and nothing fsyncs either parent. A
214
+ * directory alone therefore proves nothing: trusting it would adopt a branch whose allocator restarts
215
+ * from an empty root and mints file ids its own checkpointed rows already reference. The marker,
216
+ * written inside staging before the single rename that publishes it, is what proves both halves
217
+ * landed -- and recording the roots keeps an operator adding a `blobPaths` entry from reading as
218
+ * damage, since the new root was never this branch's.
219
+ *
220
+ * Materialization cannot leave a marker-less branch directory, so anything marker-less that still
221
+ * looks like a store came from elsewhere -- a branch older than the marker, or one whose marker was
222
+ * lost -- and is carrying data. It is refused, never deleted.
223
+ */
224
+ function readBranchState(branchPath, storeName) {
225
+ if (!(0, node_fs_1.existsSync)(branchPath))
226
+ return { state: 'absent' };
227
+ const looksLikeAStore = holdsStoreData(branchPath);
228
+ let recorded;
229
+ try {
230
+ recorded = JSON.parse((0, node_fs_1.readFileSync)((0, node_path_1.join)(branchPath, COMPLETION_MARKER), 'utf8'));
231
+ }
232
+ catch {
233
+ return looksLikeAStore ? { state: 'unmarked', why: 'it has no readable completion marker' } : { state: 'debris' };
234
+ }
235
+ // `JSON.parse('null')` succeeds and yields null, so this cannot go straight to a property access.
236
+ // An empty list would pass the prefix check below and pin the branch to no roots at all, so the
237
+ // first blob write would fail on an undefined path instead of here, where it can say why.
238
+ if (recorded == null ||
239
+ !Array.isArray(recorded.blobRoots) ||
240
+ recorded.blobRoots.length === 0 ||
241
+ recorded.blobRoots.some((r) => typeof r !== 'string')) {
242
+ // Same split as an unreadable marker above: refuse anything carrying store data, but a directory
243
+ // holding nothing but a bad marker has nothing to lose and must stay clearable.
244
+ return looksLikeAStore ? { state: 'unmarked', why: 'its completion marker is malformed' } : { state: 'debris' };
245
+ }
246
+ const configured = (0, blob_ts_1.getBlobPathsForDatabaseName)(storeName);
247
+ // By index, not by set: `storageIndex` on a row is a position in this list, so a reordered
248
+ // `storage.blobPaths` would silently resolve every row through the wrong root. A configured list
249
+ // that only GREW leaves every recorded index exactly where it was, which is why an appended volume
250
+ // is compatible where a removed, reordered or replaced one is not -- the branch goes on resolving
251
+ // through what it recorded, so the appended root is simply never its.
252
+ if (recorded.blobRoots.length > configured.length || recorded.blobRoots.some((root, i) => root !== configured[i])) {
253
+ return {
254
+ state: 'damaged',
255
+ problem: `its blob roots no longer match the configured storage.blobPaths ` +
256
+ `(recorded ${JSON.stringify(recorded.blobRoots)}, configured ${JSON.stringify(configured)})`,
257
+ blobRoots: recorded.blobRoots,
258
+ };
259
+ }
260
+ const missing = recorded.blobRoots.filter((root) => !(0, node_fs_1.existsSync)(root));
261
+ return missing.length
262
+ ? { state: 'damaged', problem: `blob root(s) are missing: ${missing.join(', ')}`, blobRoots: recorded.blobRoots }
263
+ : { state: 'complete', blobRoots: recorded.blobRoots };
264
+ }
265
+ function warnAboutUnusedBlobRoots(branchPath, storeName, blobRoots) {
266
+ const configured = (0, blob_ts_1.getBlobPathsForDatabaseName)(storeName);
267
+ if (blobRoots.length < configured.length) {
268
+ harper_logger_ts_1.default.warn?.(`Branch database at ${branchPath} keeps the ${blobRoots.length} blob root(s) it was published ` +
269
+ `with; the ${configured.length - blobRoots.length} storage.blobPaths entry(ies) added since are ` +
270
+ `not used by it. Its rows address roots by position, so only a branch rebuilt from the base ` +
271
+ `can take the added capacity.`);
272
+ }
273
+ }
274
+ /**
275
+ * Take the checkpoint into a temporary sibling and rename it into place, so a crash mid-copy leaves
276
+ * debris rather than a half-populated directory that RocksDB would then refuse to open. Reports the
277
+ * blob roots the marker records, which is what the branch is then pinned to.
278
+ */
279
+ async function materializeBranch(baseName, branchPath, storeName, report) {
280
+ const staging = `${branchPath}.staging`;
281
+ const blobRoots = (0, blob_ts_1.getBlobPathsForDatabaseName)(storeName);
282
+ // Publishing an empty root list would record a marker no later load can accept, bricking the branch
283
+ // on every subsequent boot instead of failing here, where the configuration is the visible problem.
284
+ if (blobRoots.length === 0) {
285
+ throw new Error(`Cannot create the branch at ${branchPath}: storage.blobPaths is configured with no entries, so ` +
286
+ `the branch has nowhere of its own to keep blobs`);
287
+ }
288
+ const base = (0, databases_ts_1.database)({ database: baseName, table: undefined });
289
+ // What the base's own rows resolve through, which is not the configured list if `storage.blobPaths`
290
+ // changed while its store was open. Refuse before taking a checkpoint: the configuration already
291
+ // proves that publishing this clone would leave rows on an omitted volume unreadable.
292
+ const baseRoots = (0, blob_ts_1.getRootBlobPathsForDB)(base);
293
+ if (baseRoots.length > blobRoots.length) {
294
+ throw new Error(`Cannot create a branch of '${baseName}': the open base resolves through ${baseRoots.length} blob ` +
295
+ `root(s), but storage.blobPaths now provides ${blobRoots.length}; publishing the branch would ` +
296
+ `make rows on the omitted volume(s) unreadable`);
297
+ }
298
+ await (0, promises_1.rm)(staging, { recursive: true, force: true });
299
+ await (0, promises_1.mkdir)((0, node_path_1.dirname)(branchPath), { recursive: true });
300
+ try {
301
+ await base.createCheckpoint(staging);
302
+ report.progress();
303
+ await cloneBlobRoots(baseName, baseRoots, blobRoots, report.progress);
304
+ await (0, promises_1.writeFile)((0, node_path_1.join)(staging, COMPLETION_MARKER), JSON.stringify({ blobRoots }));
305
+ await (0, promises_1.rename)(staging, branchPath);
306
+ return blobRoots;
307
+ }
308
+ catch (error) {
309
+ await (0, promises_1.rm)(staging, { recursive: true, force: true }).catch(() => { });
310
+ // The blob roots too, staging siblings included: a clone that failed partway leaves the roots it
311
+ // already published and nothing owns them once this attempt is abandoned. Only safe because this
312
+ // branch never became complete -- one that did is never rebuilt, so this cannot remove the last
313
+ // copy of anything.
314
+ // Surviving roots still hold this identity's files, so the caller must not release the name.
315
+ if (!(await removeBlobRoots(blobRoots)))
316
+ report.strandedRoots();
317
+ throw error;
318
+ }
319
+ }
320
+ /**
321
+ * `Atomics.wait`/`notify` need shared memory, and `getUserSharedBuffer` hands back a plain
322
+ * `ArrayBuffer` when nothing else has asked for the key yet — a single-threaded process, or the
323
+ * first caller in. The state word still works there (load/store/compareExchange do not require
324
+ * sharing) and concurrent async callers within one thread still contend, so waiting falls back to
325
+ * polling rather than throwing.
326
+ */
327
+ function isShared(state) {
328
+ return typeof SharedArrayBuffer !== 'undefined' && state.buffer instanceof SharedArrayBuffer;
329
+ }
330
+ function wakeWaiters(state) {
331
+ if (isShared(state))
332
+ Atomics.notify(state, CLAIM_STATE);
333
+ }
334
+ /** Wait until the claim leaves CREATING, and report the state it settled on. */
335
+ async function awaitClaim(state, branchPath, deadline) {
336
+ // A progress-extended deadline has no ceiling, so a wait past the original budget must say why it is
337
+ // still waiting rather than reading as a hang with no thread to point at.
338
+ const patience = deadline();
339
+ let announced = false;
340
+ for (;;) {
341
+ const current = Atomics.load(state, CLAIM_STATE);
342
+ if (current !== CREATING)
343
+ return current;
344
+ const remaining = deadline() - Date.now();
345
+ if (remaining <= 0)
346
+ throw new Error(`Timed out waiting for another thread to create the branch at ${branchPath}`);
347
+ // After the timeout check, and only once the deadline itself has moved: on the clock alone this
348
+ // would claim the winner is alive on the very turn the wait gives up on it.
349
+ if (!announced && Date.now() > patience && deadline() > patience) {
350
+ announced = true;
351
+ harper_logger_ts_1.default.warn?.(`Still waiting for another thread to create the branch at ${branchPath}; it is reporting ` +
352
+ `progress, so this wait is extended for as long as it keeps doing so`);
353
+ }
354
+ const slice = Math.min(remaining, 1000);
355
+ if (isShared(state)) {
356
+ const wait = Atomics.waitAsync(state, CLAIM_STATE, CREATING, slice);
357
+ if (wait.async)
358
+ await wait.value;
359
+ }
360
+ else {
361
+ await new Promise((resolve) => setTimeout(resolve, Math.min(slice, 5)));
362
+ }
363
+ }
364
+ }
365
+ /**
366
+ * Get this process's branch of `baseName` for `appName`, creating it on first call. Concurrent
367
+ * callers across worker threads all receive the same branch.
368
+ */
369
+ async function getOrCreateBranch(baseName, appName) {
370
+ const branchPath = (0, databases_ts_1.resolveBranchPath)(baseName, appName);
371
+ let pending = branchesByPath.get(branchPath);
372
+ if (!pending) {
373
+ pending = openOrCreate(baseName, appName, branchPath);
374
+ branchesByPath.set(branchPath, pending);
375
+ // A failed attempt must not be cached, or the application can never load on a later retry.
376
+ pending.catch(() => branchesByPath.delete(branchPath));
377
+ }
378
+ return (await pending).branch;
379
+ }
380
+ /**
381
+ * Two (application, database) pairs must never name one store: blob file ids restart from each
382
+ * store's own counter, so a shared identity means two branches minting identical file paths. Joining
383
+ * with a separator is not injective -- ('a__b', 'c') and ('a', 'b__c') both give 'a__b__c' -- so the
384
+ * application name's length goes in front, the way the directory layout keeps them separate.
385
+ */
386
+ function branchStoreName(appName, baseName) {
387
+ return `${appName.length}_${appName}__${baseName}`;
388
+ }
389
+ async function openOrCreate(baseName, appName, branchPath) {
390
+ const claimState = claimStateFor(baseName, branchPath);
391
+ const storeName = branchStoreName(appName, baseName);
392
+ // Reserved before the claim, and so before materialization: cloning the blob tree REMOVES and
393
+ // replaces the root this identity resolves to, while `openBranchDatabase`'s own copy of this check
394
+ // runs only after that has happened. A database created while the checkpoint is still copying
395
+ // cannot take the same name.
396
+ (0, databases_ts_1.reserveBranchIdentity)(storeName);
397
+ let handedOver = false;
398
+ // An abandoned attempt whose roots could not be removed does not hand the name back to a DATABASE:
399
+ // those roots still hold this identity's files, and a database created under the name would resolve
400
+ // its own new ids onto them. The branch may still retake it, which is what repairs the condition.
401
+ let blobRootsStranded = false;
402
+ try {
403
+ // The claim's CREATING window now covers the winner's replay as well as its checkpoint, so
404
+ // waiters must outlast the replay budget too, or a merely slow (but live) recovery would time
405
+ // out every other thread's application load minutes before the winner publishes READY. The
406
+ // clone in between is unbounded in the base's size, which is why the budget follows progress.
407
+ const deadline = claimDeadlineFor(claimState, CLAIM_TIMEOUT_MS + (0, replayLogs_ts_1.replayTimeBudgetMs)());
408
+ for (;;) {
409
+ // The deadline bounds the whole protocol, not just a single wait: an unexpected state word --
410
+ // a future state, a buffer another version wrote -- must fail this load, never spin forever.
411
+ if (Date.now() > deadline())
412
+ throw new Error(`Timed out claiming the branch at ${branchPath}`);
413
+ const previous = Atomics.compareExchange(claimState, CLAIM_STATE, UNCLAIMED, CREATING);
414
+ if (previous === READY)
415
+ break;
416
+ if (previous === UNCLAIMED) {
417
+ let branch;
418
+ let blobRoots = [];
419
+ try {
420
+ // Adopt rather than recreate: the branch outlives the process that first made it, so on
421
+ // every restart after the first the directory is already here. What it is, though, is a
422
+ // question -- the blob roots are published separately from the directory, so existence
423
+ // alone proves nothing and the marker is what has to be read.
424
+ await finishInterruptedRemoval(branchPath, storeName, { identityHeld: true });
425
+ const existing = readBranchState(branchPath, storeName);
426
+ if (existing.state === 'damaged' || existing.state === 'unmarked') {
427
+ const problem = existing.state === 'damaged' ? existing.problem : existing.why;
428
+ throw new Error(`Branch database at ${branchPath} cannot be trusted: ${problem}. Refusing to serve or rebuild ` +
429
+ `it, because rebuilding re-forks from the base and would discard anything written to this ` +
430
+ `branch. If this branch's data is not needed, delete the directory (and its blob roots) to ` +
431
+ `have it recreated from the base on the next load.`);
432
+ }
433
+ if (existing.state === 'complete') {
434
+ blobRoots = existing.blobRoots;
435
+ warnAboutUnusedBlobRoots(branchPath, storeName, blobRoots);
436
+ }
437
+ else {
438
+ // Absent, or leftovers that are not a store: nothing here can be lost.
439
+ await (0, promises_1.rm)(branchPath, { recursive: true, force: true });
440
+ blobRoots = await materializeBranch(baseName, branchPath, storeName, {
441
+ progress: () => reportClaimProgress(claimState),
442
+ strandedRoots: () => (blobRootsStranded = true),
443
+ });
444
+ }
445
+ (0, databases_ts_1.releaseBranchIdentity)(storeName);
446
+ branch = (0, databases_ts_1.openBranchDatabase)(branchPath, baseName, storeName, blobRoots);
447
+ // The branch's column families write with the WAL disabled, so writes since its last
448
+ // memtable flush exist only in its own transaction log — which a process that died
449
+ // without the exit-time flush (a crash, a Windows hard kill) always leaves behind.
450
+ // Replay must finish before READY, because READY is what releases the other threads to
451
+ // serve reads. A fresh checkpoint carries no transaction_logs (verified: the native
452
+ // checkpoint copies only RocksDB files), so replay after materialize is a no-op; the
453
+ // read-only skip mirrors the base's boot replay.
454
+ if (!(0, databases_ts_1.isReadOnlyMode)())
455
+ await (0, replayLogs_ts_1.replayLogs)(branch.rootStore, branch.tables, true);
456
+ Atomics.store(claimState, CLAIM_STATE, READY);
457
+ }
458
+ catch (error) {
459
+ // Release rather than record the failure. A claim that stays un-releasable turns one
460
+ // transient error -- a full disk, a rename losing a race -- into a branch that can never
461
+ // be created again for the life of the process, because the buffer outlives every retry.
462
+ // The branch is closed first — it holds the path and store identity a retry needs — and
463
+ // a close failure must not leave the claim wedged in CREATING for the whole deadline.
464
+ try {
465
+ branch?.close();
466
+ }
467
+ catch (closeError) {
468
+ harper_logger_ts_1.default.warn(`Error closing branch at ${branchPath} after a failed open`, closeError);
469
+ }
470
+ Atomics.store(claimState, CLAIM_STATE, UNCLAIMED);
471
+ throw error;
472
+ }
473
+ finally {
474
+ wakeWaiters(claimState);
475
+ }
476
+ handedOver = true;
477
+ return { branch, claimState, blobRoots };
478
+ }
479
+ // Someone else holds it. A wait that settles on UNCLAIMED means they failed and released, so
480
+ // take another turn at being the one who creates it.
481
+ if ((await awaitClaim(claimState, branchPath, deadline)) === READY)
482
+ break;
483
+ }
484
+ // This thread never read the branch it is adopting, and a branch resolves blob ids through the
485
+ // roots its marker names, so the marker has to be read here too rather than pinned only on the
486
+ // thread that won the claim.
487
+ const published = readBranchState(branchPath, storeName);
488
+ if (published.state !== 'complete') {
489
+ throw new Error(`Branch database at ${branchPath} was published by another thread but no longer reads as ` +
490
+ `complete: ${published.state === 'damaged' ? published.problem : published.state === 'unmarked' ? published.why : published.state}`);
491
+ }
492
+ warnAboutUnusedBlobRoots(branchPath, storeName, published.blobRoots);
493
+ // Released immediately before the open, with no `await` in between, so nothing can slip into the
494
+ // gap -- and so `openBranchDatabase`'s check stays strict rather than being taught to ignore a
495
+ // reservation, which would also make it ignore a DIFFERENT branch holding the same name.
496
+ (0, databases_ts_1.releaseBranchIdentity)(storeName);
497
+ const adopted = (0, databases_ts_1.openBranchDatabase)(branchPath, baseName, storeName, published.blobRoots);
498
+ handedOver = true;
499
+ return { branch: adopted, claimState, blobRoots: published.blobRoots };
500
+ }
501
+ finally {
502
+ // `openBranchDatabase` takes the identity over for the life of the handle; anything short of
503
+ // that has to hand it back, or the application can never load again in this process.
504
+ if (!handedOver) {
505
+ if (blobRootsStranded)
506
+ (0, databases_ts_1.quarantineBranchIdentity)(storeName);
507
+ else
508
+ (0, databases_ts_1.releaseBranchIdentity)(storeName);
509
+ }
510
+ }
511
+ }
512
+ /** Remove the now-empty `<app>` directory a removed branch leaves behind. */
513
+ async function pruneEmptyParents(root, branchPath) {
514
+ let dir = (0, node_path_1.dirname)(branchPath);
515
+ // Stop at the branch root itself: it is shared, and `relative` going outside it means we walked past.
516
+ while (dir !== root && !(0, node_path_1.relative)(root, dir).startsWith('..')) {
517
+ try {
518
+ await (0, promises_1.rmdir)(dir);
519
+ }
520
+ catch {
521
+ return;
522
+ }
523
+ dir = (0, node_path_1.dirname)(dir);
524
+ }
525
+ }
526
+ function branchRootOf(branchPath) {
527
+ return (0, node_path_1.dirname)((0, node_path_1.dirname)(branchPath));
528
+ }
529
+ /**
530
+ * Release this thread's handle on a branch WITHOUT touching the directory. One branch directory is
531
+ * shared by every worker thread that loaded the application, so a thread that gives up its handle --
532
+ * a failed load, most often -- must not delete storage another thread is serving queries from.
533
+ */
534
+ async function closeBranchAt(branchPath) {
535
+ const pending = branchesByPath.get(branchPath);
536
+ branchesByPath.delete(branchPath);
537
+ const opened = await pending?.catch(() => null);
538
+ opened?.branch.close();
539
+ }
540
+ /**
541
+ * Open references to this branch's store anywhere in the process (rocksdb-js's registry spans worker
542
+ * threads). A registry read that fails propagates and refuses the removal rather than reading as zero.
543
+ */
544
+ function branchReferenceCount(branchPath) {
545
+ let resolved = branchPath;
546
+ try {
547
+ resolved = (0, node_fs_1.realpathSync)(branchPath);
548
+ }
549
+ catch (error) {
550
+ if (error.code === 'ENOENT')
551
+ return 0;
552
+ throw error;
553
+ }
554
+ return ((0, rocksdb_js_1.registryStatus)().find((entry) => entry.path === resolved || canonicalOrSelf(entry.path) === resolved)
555
+ ?.refCount ?? 0);
556
+ }
557
+ function canonicalOrSelf(path) {
558
+ try {
559
+ return (0, node_fs_1.realpathSync)(path);
560
+ }
561
+ catch {
562
+ return path;
563
+ }
564
+ }
565
+ /**
566
+ * Destroy a branch's storage: checkpoint, blob roots and claim, the way `dropDatabase` does for a real
567
+ * database. Only safe as the last step of the undeploy sequence -- component dropped, workers
568
+ * restarted -- and it does not take that on trust: any remaining open reference to the store refuses
569
+ * the removal before anything is unlinked.
570
+ *
571
+ * The branch is first renamed to its `removing` sibling so a removal cut short by a crash is finished
572
+ * later instead of being read as damage. `opened` is the handle this thread held, if any, already closed.
573
+ */
574
+ async function destroyBranchStorage(branchPath, opened) {
575
+ const held = branchReferenceCount(branchPath);
576
+ if (held > 0) {
577
+ throw new Error(`Cannot remove the branch at ${branchPath}: ${held} open reference(s) to its store remain in this ` +
578
+ `process. Restart the workers that loaded the application first; removing it now would unlink ` +
579
+ `storage out from under a live reader.`);
580
+ }
581
+ const storeName = branchStoreNameFor(branchPath);
582
+ const blobRoots = opened?.blobRoots ?? recordedBlobRootsAt(branchPath, storeName);
583
+ let holdsIdentity = true;
584
+ let blobRootsStranded = false;
585
+ if (opened) {
586
+ (0, databases_ts_1.retakeBranchIdentity)(storeName);
587
+ releaseClaim(opened.claimState);
588
+ }
589
+ else {
590
+ try {
591
+ (0, databases_ts_1.reserveBranchIdentity)(storeName);
592
+ }
593
+ catch (error) {
594
+ holdsIdentity = false;
595
+ harper_logger_ts_1.default.warn?.(`Leaving the blob roots of the branch at ${branchPath} in place: its identity is in use`, error);
596
+ }
597
+ }
598
+ branchesByPath.delete(branchPath);
599
+ const removing = `${branchPath}${databases_ts_1.BRANCH_REMOVING_SUFFIX}`;
600
+ try {
601
+ await (0, promises_1.rm)(removing, { recursive: true, force: true });
602
+ if ((0, node_fs_1.existsSync)(branchPath))
603
+ await (0, promises_1.rename)(branchPath, removing);
604
+ if (holdsIdentity && !(0, node_fs_1.existsSync)(branchPath)) {
605
+ if (!(await removeBlobRoots(blobRoots))) {
606
+ blobRootsStranded = true;
607
+ harper_logger_ts_1.default.warn?.(`Refusing '${storeName}' to new databases on this worker for the life of the process: the ` +
608
+ `branch at ${branchPath} left blob files behind and a database under that name would ` +
609
+ `resolve onto them`);
610
+ }
611
+ }
612
+ // only while the base exists: `claimStateFor` goes through `database()`, which recreates a dropped one
613
+ if (!opened && databases_ts_1.databases[(0, node_path_1.basename)(branchPath)]) {
614
+ try {
615
+ releaseClaim(claimStateFor((0, node_path_1.basename)(branchPath), branchPath));
616
+ }
617
+ catch (error) {
618
+ harper_logger_ts_1.default.warn?.(`Could not release the branch claim for ${branchPath}`, error);
619
+ }
620
+ }
621
+ if (blobRootsStranded) {
622
+ throw new Error(`The branch at ${branchPath} was not fully removed: one or more of its blob roots could not be ` +
623
+ `deleted. Its \`removing\` tombstone was kept; retry the removal once the cause is fixed.`);
624
+ }
625
+ await (0, promises_1.rm)(removing, { recursive: true, force: true });
626
+ await pruneEmptyParents(branchRootOf(branchPath), branchPath);
627
+ }
628
+ finally {
629
+ if (holdsIdentity) {
630
+ if (blobRootsStranded)
631
+ (0, databases_ts_1.quarantineBranchIdentity)(storeName);
632
+ else
633
+ (0, databases_ts_1.releaseBranchIdentity)(storeName);
634
+ }
635
+ }
636
+ }
637
+ /**
638
+ * Finish a removal that was interrupted; a `removing` sibling can only mean that.
639
+ *
640
+ * `identityHeld`: the caller already reserved the store identity (the load path does). Otherwise it is
641
+ * reserved here, and a database that already took the name owns the root it resolves to.
642
+ */
643
+ async function finishInterruptedRemoval(branchPath, storeName, { identityHeld }) {
644
+ const removing = `${branchPath}${databases_ts_1.BRANCH_REMOVING_SUFFIX}`;
645
+ if (!(0, node_fs_1.existsSync)(removing))
646
+ return;
647
+ harper_logger_ts_1.default.warn?.(`Finishing an interrupted removal of the branch at ${branchPath}`);
648
+ if (!identityHeld)
649
+ (0, databases_ts_1.reserveBranchIdentity)(storeName);
650
+ let stranded = false;
651
+ try {
652
+ // Only what the tombstone's own marker names. The marker goes with the final delete, after the
653
+ // roots have; a tombstone without one had its roots removed already, or lost the record of them
654
+ // -- and guessing from configuration would take a same-named directory on a volume this branch
655
+ // never wrote.
656
+ const published = readBranchState(removing, storeName);
657
+ if (published.state === 'complete' || published.state === 'damaged') {
658
+ const configured = (0, blob_ts_1.getBlobPathsForDatabaseName)(storeName);
659
+ if (!(await removeBlobRoots(published.blobRoots.filter((root) => configured.includes(root))))) {
660
+ stranded = true;
661
+ throw new Error(`Could not finish removing the branch at ${branchPath}: its blob roots could not be deleted; ` +
662
+ `the \`removing\` tombstone was kept`);
663
+ }
664
+ }
665
+ else {
666
+ stranded = true;
667
+ harper_logger_ts_1.default.warn?.(`The removal tombstone of the branch at ${branchPath} no longer names its blob roots; leaving any ` +
668
+ `that remain in place and refusing '${storeName}' to new databases`);
669
+ }
670
+ await (0, promises_1.rm)(removing, { recursive: true, force: true });
671
+ }
672
+ finally {
673
+ if (stranded)
674
+ (0, databases_ts_1.quarantineBranchIdentity)(storeName);
675
+ else if (!identityHeld)
676
+ (0, databases_ts_1.releaseBranchIdentity)(storeName);
677
+ }
678
+ }
679
+ /**
680
+ * Hand a branch's claim back, but only from READY: a CREATING word belongs to a loader that won the
681
+ * claim since, and must not be clobbered into making a second waiter the creator.
682
+ */
683
+ function releaseClaim(claimState) {
684
+ if (Atomics.compareExchange(claimState, CLAIM_STATE, READY, UNCLAIMED) === READY)
685
+ wakeWaiters(claimState);
686
+ }
687
+ /**
688
+ * Release this thread's handle, then destroy the storage. Deliberate removal only.
689
+ */
690
+ async function removeBranchAt(branchPath) {
691
+ const pending = branchesByPath.get(branchPath);
692
+ branchesByPath.delete(branchPath);
693
+ const opened = (await pending?.catch(() => null)) ?? null;
694
+ opened?.branch.close();
695
+ await destroyBranchStorage(branchPath, opened);
696
+ }
697
+ /**
698
+ * The roots a branch on disk recorded, falling back to configuration only for one that never
699
+ * published a usable marker -- an abandoned materialization, whose roots ARE the configured ones.
700
+ */
701
+ function recordedBlobRootsAt(branchPath, storeName) {
702
+ const published = readBranchState(branchPath, storeName);
703
+ const configured = (0, blob_ts_1.getBlobPathsForDatabaseName)(storeName);
704
+ // A marker that reads as damaged still names what this branch published, and only a branch with no
705
+ // usable marker at all leaves configuration as the best answer. Nothing outside this node's
706
+ // configured volumes is deleted either way: a branch directory carried over from a host configured
707
+ // differently names paths this node never wrote.
708
+ return published.state === 'complete' || published.state === 'damaged'
709
+ ? published.blobRoots.filter((root) => configured.includes(root))
710
+ : configured;
711
+ }
712
+ /** `<storage>/`branches`/<app>/<db>` — the same identity `branchStoreName` composed on the way in. */
713
+ function branchStoreNameFor(branchPath) {
714
+ return branchStoreName((0, node_path_1.basename)((0, node_path_1.dirname)(branchPath)), (0, node_path_1.basename)(branchPath));
715
+ }
716
+ /**
717
+ * Discard every branch this process has open, storage included. Deliberate removal only: a branch is
718
+ * durable and is meant to survive restarts, so no shutdown path calls this.
719
+ */
720
+ async function removeBranches() {
721
+ for (const branchPath of [...branchesByPath.keys()])
722
+ await removeBranchAt(branchPath);
723
+ }
724
+ /** Every branch directory and removal tombstone an application owns, under every storage root a database could have. */
725
+ function branchDirectoriesFor(appName) {
726
+ if (!appName || appName.includes('/') || appName.includes('\\') || appName === '.' || appName === '..') {
727
+ throw new Error(`Invalid application name for branch removal: ${JSON.stringify(appName)}`);
728
+ }
729
+ (0, databases_ts_1.getDatabases)();
730
+ const branches = [];
731
+ const tombstones = [];
732
+ const failures = [];
733
+ const fail = (what, error) => failures.push(`${what} (${error.message})`);
734
+ const baseNames = new Set([
735
+ ...Object.keys(databases_ts_1.databases),
736
+ hdbTerms_ts_1.DEFAULT_DATABASE_NAME,
737
+ // a dropped base with a dedicated path has left `databases`, not its branches
738
+ ...Object.keys(env.get(hdbTerms_ts_1.CONFIG_PARAMS.DATABASES) ?? {}),
739
+ ]);
740
+ const branchRoots = new Set();
741
+ for (const baseName of baseNames) {
742
+ try {
743
+ branchRoots.add((0, node_path_1.join)((0, databases_ts_1.resolveDatabaseStorageRoot)(baseName), databases_ts_1.BRANCH_ROOT_DIR));
744
+ }
745
+ catch (error) {
746
+ fail(`the storage root of database '${baseName}'`, error);
747
+ }
748
+ }
749
+ for (const root of branchRoots) {
750
+ const appDirectory = (0, node_path_1.join)(root, appName);
751
+ let entries;
752
+ try {
753
+ entries = (0, node_fs_1.readdirSync)(appDirectory, { withFileTypes: true });
754
+ }
755
+ catch (error) {
756
+ if (error.code !== 'ENOENT')
757
+ fail(appDirectory, error);
758
+ continue;
759
+ }
760
+ for (const entry of entries) {
761
+ if (!entry.isDirectory())
762
+ continue;
763
+ const entryPath = (0, node_path_1.join)(appDirectory, entry.name);
764
+ if (entry.name.endsWith(databases_ts_1.BRANCH_REMOVING_SUFFIX))
765
+ tombstones.push(entryPath.slice(0, -databases_ts_1.BRANCH_REMOVING_SUFFIX.length));
766
+ else if (entry.name.includes('`'))
767
+ fail(entryPath, new Error('not a branch directory and not a known control path'));
768
+ else
769
+ branches.push(entryPath);
770
+ }
771
+ }
772
+ return { branches, tombstones, failures };
773
+ }
774
+ /** Whether undeploying this application would leave branched database storage behind. */
775
+ function applicationHasBranchStorage(appName) {
776
+ const { branches, tombstones, failures } = branchDirectoriesFor(appName);
777
+ return branches.length > 0 || tombstones.length > 0 || failures.length > 0;
778
+ }
779
+ /**
780
+ * Discard every branch an application owns: what undeploying it means for its private data. A branch is
781
+ * durable, so if this does not happen nothing else ever will, and a redeploy under the same name would
782
+ * adopt the previous tenant's rows.
783
+ *
784
+ * Runs on the thread that saw the worker restart through, after it completed. Own handles are closed
785
+ * first; any reference held elsewhere refuses. Every failure is collected and thrown: an undeploy must
786
+ * not answer "dropped" with private data still on disk.
787
+ */
788
+ async function removeBranchesForApplication(appName) {
789
+ const { branches, tombstones, failures } = branchDirectoriesFor(appName);
790
+ const fail = (what, error) => failures.push(`${what} (${error.message})`);
791
+ for (const branchPath of tombstones) {
792
+ try {
793
+ await finishInterruptedRemoval(branchPath, branchStoreNameFor(branchPath), { identityHeld: false });
794
+ }
795
+ catch (error) {
796
+ fail(`${branchPath}${databases_ts_1.BRANCH_REMOVING_SUFFIX}`, error);
797
+ }
798
+ }
799
+ for (const branchPath of branches) {
800
+ try {
801
+ await removeBranchAt(branchPath);
802
+ }
803
+ catch (error) {
804
+ fail(branchPath, error);
805
+ }
806
+ }
807
+ if (failures.length) {
808
+ throw new Error(`Could not remove branch storage for application '${appName}': ${failures.join('; ')}`);
809
+ }
810
+ }
811
+ /**
812
+ * Create every branch an application declared, or fail its load. These checks need the live
813
+ * environment, so they cannot live in the static config validator: whether the database exists,
814
+ * which storage engine is in effect, and whether the application's loader mode can carry a scoped
815
+ * `databases` binding at all.
816
+ *
817
+ * Failing is always right here. Falling back to the base would hand the application the shared
818
+ * database it explicitly asked not to have, and nothing downstream could tell the difference.
819
+ */
820
+ async function prepareBranches(appName, branchedDatabases, loaderMode) {
821
+ const branches = new Map();
822
+ if (branchedDatabases === undefined)
823
+ return branches;
824
+ // The scoped `databases` binding is delivered through the module loader. Under `native` the
825
+ // loader hands back the process-wide exports, so a branch would be created and then never
826
+ // reached — the application would silently write the base.
827
+ if (loaderMode === 'native') {
828
+ throw new Error(`Application '${appName}' declares branchedDatabases but runs under the 'native' module loader, ` +
829
+ `which cannot carry a scoped databases binding; use the default loader or remove branchedDatabases`);
830
+ }
831
+ // An explicitly empty list branches nothing, so it needs no engine capability.
832
+ if (branchedDatabases !== true && !branchedDatabases.length)
833
+ return branches;
834
+ if ((process.env.HARPER_STORAGE_ENGINE || env.get(hdbTerms_ts_1.CONFIG_PARAMS.STORAGE_ENGINE)) === 'lmdb') {
835
+ throw new Error(`Application '${appName}' declares branchedDatabases, which requires the RocksDB storage engine`);
836
+ }
837
+ (0, databases_ts_1.getDatabases)();
838
+ if (branchedDatabases === true) {
839
+ // A snapshot, not a subscription: every database that exists at THIS load. One created afterward
840
+ // is not retroactively branched, and this application's own declarations into an unbranched
841
+ // database land in the base. `system` is excluded the same way an explicit declaration of it is
842
+ // refused (assertBranchedDatabases): it carries the instance's catalog, users and jobs.
843
+ branchedDatabases = Object.keys(databases_ts_1.databases).filter((name) => name !== 'system');
844
+ }
845
+ if (!branchedDatabases.length)
846
+ return branches;
847
+ for (const baseName of branchedDatabases) {
848
+ if (!databases_ts_1.databases[baseName]) {
849
+ throw new Error(`Application '${appName}' declares a branch of database '${baseName}', which does not exist`);
850
+ }
851
+ // Up front, not inside the open: two individually legal names can compose a store identity past
852
+ // the 250-character limit, and `openBranchDatabase` only checks it after the claim is taken and
853
+ // a potentially full-copy checkpoint is already on disk.
854
+ const storeName = branchStoreName(appName, baseName);
855
+ if (storeName.length > MAX_NAME_LENGTH) {
856
+ throw new Error(`Application '${appName}' cannot branch '${baseName}': the branch store identity would be ` +
857
+ `${storeName.length} characters, over the ${MAX_NAME_LENGTH} allowed`);
858
+ }
859
+ }
860
+ // Only the branches this call actually opened are this call's to give up. A branch handle is
861
+ // cached per path for the whole thread, so a later load of the same application -- a component
862
+ // reload, or a second component with the same appName -- must not close the branch the load
863
+ // already running is serving from.
864
+ const opened = [];
865
+ try {
866
+ for (const baseName of branchedDatabases) {
867
+ const branchPath = (0, databases_ts_1.resolveBranchPath)(baseName, appName);
868
+ const isNew = !branchesByPath.has(branchPath);
869
+ branches.set(baseName, await getOrCreateBranch(baseName, appName));
870
+ if (isNew)
871
+ opened.push(branchPath);
872
+ }
873
+ // Only now: a relationship whose target this application also branched has to resolve to that
874
+ // branch, and the whole set has to exist before any of them can resolve that way. The set stays
875
+ // on each branch for the reloads its own schema declarations trigger later.
876
+ for (const branch of branches.values()) {
877
+ branch.relatedBranches = branches;
878
+ (0, databases_ts_1.hydrateBranchRelationships)(branch, branches);
879
+ }
880
+ }
881
+ catch (error) {
882
+ // A partially branched application is worse than one that failed to load: some of its names
883
+ // would resolve to a branch and the rest to the base. Only this application's handles go, and
884
+ // only handles: another worker thread may have loaded the same application successfully and be
885
+ // serving queries out of the very directories this thread is giving up.
886
+ for (const branchPath of opened)
887
+ await closeBranchAt(branchPath).catch(() => { });
888
+ throw error;
889
+ }
890
+ return branches;
891
+ }
892
+ //# sourceMappingURL=branchDatabase.js.map