@harperfast/harper-pro 5.0.25 → 5.1.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 (927) hide show
  1. package/analytics/profile.ts +4 -0
  2. package/core/AGENTS.md +92 -6
  3. package/core/DESIGN.md +24 -0
  4. package/core/README.md +11 -10
  5. package/core/agent/agent.ts +203 -0
  6. package/core/agent/loop.ts +205 -0
  7. package/core/agent/operations.ts +148 -0
  8. package/core/agent/session.ts +187 -0
  9. package/core/agent/tools/fsTools.ts +276 -0
  10. package/core/agent/tools/httpFetchTool.ts +112 -0
  11. package/core/agent/tools/scheduleTool.ts +68 -0
  12. package/core/agent/toolset.ts +43 -0
  13. package/core/agent/types.ts +85 -0
  14. package/core/benchmarks/hnsw-search.js +157 -0
  15. package/core/benchmarks/ycsb/README.md +144 -0
  16. package/core/benchmarks/ycsb/analyze-profile.mts +120 -0
  17. package/core/benchmarks/ycsb/app/config.yaml +3 -0
  18. package/core/benchmarks/ycsb/app/schema.graphql +17 -0
  19. package/core/benchmarks/ycsb/harness.mts +328 -0
  20. package/core/benchmarks/ycsb/restClient.mts +120 -0
  21. package/core/benchmarks/ycsb/run-single-node.mts +97 -0
  22. package/core/benchmarks/ycsb/to-benchmark-json.mts +58 -0
  23. package/core/benchmarks/ycsb/workload.mts +499 -0
  24. package/core/benchmarks/ycsb/workload.test.mts +174 -0
  25. package/core/bin/{BinObjects.js → BinObjects.ts} +4 -5
  26. package/core/bin/cliCredentials.ts +133 -0
  27. package/core/bin/cliOperations.ts +339 -0
  28. package/core/bin/copyDb.ts +10 -10
  29. package/core/bin/deployRenderer.ts +196 -0
  30. package/core/bin/{harper.js → harper.ts} +48 -24
  31. package/core/bin/{install.js → install.ts} +3 -3
  32. package/core/bin/lite.ts +2 -0
  33. package/core/bin/login.ts +168 -0
  34. package/core/bin/logout.ts +11 -0
  35. package/core/bin/mcp/client.ts +407 -0
  36. package/core/bin/mcp/doctor.ts +189 -0
  37. package/core/bin/mcp/index.ts +80 -0
  38. package/core/bin/mcp/options.ts +122 -0
  39. package/core/bin/mcp/printConfig.ts +89 -0
  40. package/core/bin/multipartBuilder.ts +74 -0
  41. package/core/bin/{restart.js → restart.ts} +31 -32
  42. package/core/bin/{run.js → run.ts} +57 -46
  43. package/core/bin/sseConsumer.ts +126 -0
  44. package/core/bin/{status.js → status.ts} +10 -10
  45. package/core/bin/stop.ts +21 -0
  46. package/core/bin/upgrade.js +6 -6
  47. package/core/components/Application.ts +144 -18
  48. package/core/components/ApplicationScope.ts +2 -2
  49. package/core/components/ComponentV1.ts +2 -2
  50. package/core/components/EntryHandler.ts +159 -9
  51. package/core/components/OptionsWatcher.ts +75 -11
  52. package/core/components/Scope.ts +125 -15
  53. package/core/components/anthropic/index.ts +547 -0
  54. package/core/components/bedrock/index.ts +823 -0
  55. package/core/components/componentLoader.ts +63 -32
  56. package/core/components/deployLifecycle.ts +161 -0
  57. package/core/components/deploymentOperations.ts +173 -0
  58. package/core/components/deploymentRecorder.ts +402 -0
  59. package/core/components/deriveURLPath.ts +4 -4
  60. package/core/components/mcp/adapters/fastify.ts +87 -0
  61. package/core/components/mcp/adapters/harperHttp.ts +103 -0
  62. package/core/components/mcp/audit.ts +75 -0
  63. package/core/components/mcp/index.ts +134 -0
  64. package/core/components/mcp/jsonrpc.ts +134 -0
  65. package/core/components/mcp/lifecycle.ts +105 -0
  66. package/core/components/mcp/listChanged.ts +270 -0
  67. package/core/components/mcp/rateLimit.ts +217 -0
  68. package/core/components/mcp/resources.ts +593 -0
  69. package/core/components/mcp/session.ts +151 -0
  70. package/core/components/mcp/sessionRegistry.ts +140 -0
  71. package/core/components/mcp/toolRegistry.ts +292 -0
  72. package/core/components/mcp/tools/application.ts +603 -0
  73. package/core/components/mcp/tools/operations.ts +283 -0
  74. package/core/components/mcp/tools/schemas/derive.ts +256 -0
  75. package/core/components/mcp/tools/schemas/operations.ts +245 -0
  76. package/core/components/mcp/transport.ts +517 -0
  77. package/core/components/ollama/index.ts +316 -0
  78. package/core/components/openai/index.ts +563 -0
  79. package/core/components/operations.js +211 -60
  80. package/core/components/operationsValidation.js +3 -3
  81. package/core/components/packageComponent.ts +97 -29
  82. package/core/components/requestRestart.ts +17 -2
  83. package/core/components/status/crossThread.ts +14 -5
  84. package/core/components/status/errors.ts +1 -1
  85. package/core/config/RootConfigWatcher.ts +56 -2
  86. package/core/config/configUtils.js +29 -8
  87. package/core/config/harperConfigEnvVars.ts +1 -1
  88. package/core/dataLayer/{CreateAttributeObject.js → CreateAttributeObject.ts} +4 -3
  89. package/core/dataLayer/{CreateTableObject.js → CreateTableObject.ts} +2 -1
  90. package/core/dataLayer/{DataLayerObjects.js → DataLayerObjects.ts} +17 -9
  91. package/core/dataLayer/{DeleteBeforeObject.js → DeleteBeforeObject.ts} +2 -1
  92. package/core/dataLayer/{DeleteObject.js → DeleteObject.ts} +3 -2
  93. package/core/dataLayer/{DropAttributeObject.js → DropAttributeObject.ts} +2 -1
  94. package/core/dataLayer/{GetBackupObject.js → GetBackupObject.ts} +3 -2
  95. package/core/dataLayer/{InsertObject.js → InsertObject.ts} +3 -2
  96. package/core/dataLayer/{ReadAuditLogObject.js → ReadAuditLogObject.ts} +3 -2
  97. package/core/dataLayer/{SQLSearch.js → SQLSearch.ts} +97 -43
  98. package/core/dataLayer/{SearchByConditionsObject.js → SearchByConditionsObject.ts} +5 -6
  99. package/core/dataLayer/{SearchByHashObject.js → SearchByHashObject.ts} +2 -1
  100. package/core/dataLayer/{SearchObject.js → SearchObject.ts} +2 -1
  101. package/core/dataLayer/{SqlSearchObject.js → SqlSearchObject.ts} +2 -1
  102. package/core/dataLayer/{UpdateObject.js → UpdateObject.ts} +3 -2
  103. package/core/dataLayer/{UpsertObject.js → UpsertObject.ts} +3 -2
  104. package/core/dataLayer/{bulkLoad.js → bulkLoad.ts} +40 -49
  105. package/core/dataLayer/{delete.js → delete.ts} +21 -26
  106. package/core/dataLayer/{export.js → export.ts} +22 -26
  107. package/core/dataLayer/{getBackup.js → getBackup.ts} +7 -9
  108. package/core/dataLayer/harperBridge/BridgeMethods.ts +102 -0
  109. package/core/dataLayer/harperBridge/ResourceBridge.ts +27 -26
  110. package/core/dataLayer/harperBridge/TableSizeObject.ts +1 -0
  111. package/core/dataLayer/harperBridge/bridgeUtility/insertUpdateValidate.js +4 -4
  112. package/core/dataLayer/harperBridge/{harperBridge.js → harperBridge.ts} +3 -3
  113. package/core/dataLayer/harperBridge/lmdbBridge/lmdbMethods/lmdbCreateAttribute.js +8 -6
  114. package/core/dataLayer/harperBridge/lmdbBridge/lmdbMethods/lmdbCreateRecords.js +4 -4
  115. package/core/dataLayer/harperBridge/lmdbBridge/lmdbMethods/lmdbCreateSchema.js +1 -1
  116. package/core/dataLayer/harperBridge/lmdbBridge/lmdbMethods/lmdbCreateTable.js +6 -4
  117. package/core/dataLayer/harperBridge/lmdbBridge/lmdbMethods/lmdbDeleteAuditLogsBefore.js +5 -4
  118. package/core/dataLayer/harperBridge/lmdbBridge/lmdbMethods/lmdbDeleteRecords.js +4 -4
  119. package/core/dataLayer/harperBridge/lmdbBridge/lmdbMethods/lmdbDropAttribute.js +6 -5
  120. package/core/dataLayer/harperBridge/lmdbBridge/lmdbMethods/lmdbDropSchema.js +5 -4
  121. package/core/dataLayer/harperBridge/lmdbBridge/lmdbMethods/lmdbDropTable.js +5 -5
  122. package/core/dataLayer/harperBridge/lmdbBridge/lmdbMethods/lmdbFlush.js +1 -1
  123. package/core/dataLayer/harperBridge/lmdbBridge/lmdbMethods/lmdbGetBackup.js +3 -3
  124. package/core/dataLayer/harperBridge/lmdbBridge/lmdbMethods/lmdbGetDataByHash.js +1 -1
  125. package/core/dataLayer/harperBridge/lmdbBridge/lmdbMethods/lmdbGetDataByValue.js +3 -2
  126. package/core/dataLayer/harperBridge/lmdbBridge/lmdbMethods/lmdbReadAuditLog.js +5 -5
  127. package/core/dataLayer/harperBridge/lmdbBridge/lmdbMethods/lmdbSearchByConditions.js +10 -8
  128. package/core/dataLayer/harperBridge/lmdbBridge/lmdbMethods/lmdbSearchByHash.js +1 -1
  129. package/core/dataLayer/harperBridge/lmdbBridge/lmdbMethods/lmdbSearchByValue.js +4 -3
  130. package/core/dataLayer/harperBridge/lmdbBridge/lmdbMethods/lmdbUpdateRecords.js +3 -3
  131. package/core/dataLayer/harperBridge/lmdbBridge/lmdbMethods/lmdbUpsertRecords.js +6 -5
  132. package/core/dataLayer/harperBridge/lmdbBridge/lmdbUtility/LMDBCreateAttributeObject.js +2 -1
  133. package/core/dataLayer/harperBridge/lmdbBridge/lmdbUtility/initializeHashSearch.js +3 -2
  134. package/core/dataLayer/harperBridge/lmdbBridge/lmdbUtility/initializePaths.js +2 -2
  135. package/core/dataLayer/harperBridge/lmdbBridge/lmdbUtility/lmdbCheckForNewAttributes.js +5 -4
  136. package/core/dataLayer/harperBridge/lmdbBridge/lmdbUtility/lmdbCreateTransactionsAuditEnvironment.js +6 -3
  137. package/core/dataLayer/harperBridge/lmdbBridge/lmdbUtility/lmdbGetTableSize.ts +1 -1
  138. package/core/dataLayer/harperBridge/lmdbBridge/lmdbUtility/lmdbProcessRows.js +4 -4
  139. package/core/dataLayer/harperBridge/lmdbBridge/lmdbUtility/lmdbSearch.js +5 -5
  140. package/core/dataLayer/harperBridge/lmdbBridge/lmdbUtility/lmdbWriteTransaction.js +4 -4
  141. package/core/dataLayer/{hdbInfoController.js → hdbInfoController.ts} +20 -26
  142. package/core/dataLayer/{insert.js → insert.ts} +24 -24
  143. package/core/dataLayer/{readAuditLog.js → readAuditLog.ts} +8 -10
  144. package/core/dataLayer/{schema.js → schema.ts} +32 -44
  145. package/core/dataLayer/{schemaDescribe.js → schemaDescribe.ts} +23 -26
  146. package/core/dataLayer/{search.js → search.ts} +9 -15
  147. package/core/dataLayer/{transaction.js → transaction.ts} +2 -5
  148. package/core/dataLayer/{update.js → update.ts} +19 -22
  149. package/core/index.ts +5 -0
  150. package/core/json/systemSchema.json +65 -0
  151. package/core/launchServiceScripts/utility/checkNodeVersion.js +2 -0
  152. package/core/package-lock.json +9051 -4318
  153. package/core/resources/DESIGN.md +98 -0
  154. package/core/resources/DatabaseTransaction.ts +58 -30
  155. package/core/resources/ErrorResource.ts +2 -1
  156. package/core/resources/LMDBTransaction.ts +38 -29
  157. package/core/resources/RecordEncoder.ts +12 -4
  158. package/core/resources/RequestTarget.ts +2 -0
  159. package/core/resources/Resource.ts +89 -56
  160. package/core/resources/ResourceInterface.ts +44 -21
  161. package/core/resources/Resources.ts +8 -8
  162. package/core/resources/RocksIndexStore.ts +3 -0
  163. package/core/resources/RocksTransactionLogStore.ts +47 -28
  164. package/core/resources/Table.ts +386 -214
  165. package/core/resources/analytics/metadata.ts +1 -0
  166. package/core/resources/analytics/read.ts +24 -6
  167. package/core/resources/analytics/write.ts +240 -17
  168. package/core/resources/auditStore.ts +28 -19
  169. package/core/resources/blob.ts +53 -53
  170. package/core/resources/dataLoader.ts +4 -4
  171. package/core/resources/databases.ts +190 -71
  172. package/core/resources/graphql.ts +227 -164
  173. package/core/resources/indexes/HierarchicalNavigableSmallWorld.ts +294 -65
  174. package/core/resources/indexes/vector.ts +17 -0
  175. package/core/resources/loadEnv.ts +21 -17
  176. package/core/resources/login.ts +5 -3
  177. package/core/resources/models/Models.ts +304 -0
  178. package/core/resources/models/TestBackend.ts +83 -0
  179. package/core/resources/models/agentLoop.ts +895 -0
  180. package/core/resources/models/analyticsTable.ts +199 -0
  181. package/core/resources/models/backendHelpers.ts +116 -0
  182. package/core/resources/models/backendRegistry.ts +66 -0
  183. package/core/resources/models/bootstrap.ts +135 -0
  184. package/core/resources/models/embedHook.ts +138 -0
  185. package/core/resources/models/types.ts +296 -0
  186. package/core/resources/replayLogs.ts +15 -7
  187. package/core/resources/roles.ts +62 -67
  188. package/core/resources/search.ts +355 -135
  189. package/core/resources/tracked.ts +8 -8
  190. package/core/resources/transaction.ts +8 -8
  191. package/core/resources/transactionBroadcast.ts +3 -3
  192. package/core/security/auth.ts +35 -26
  193. package/core/security/certificateVerification/crlVerification.ts +11 -4
  194. package/core/security/{cryptoHash.js → cryptoHash.ts} +3 -8
  195. package/core/security/data_objects/{PermissionAttributeResponseObject.js → PermissionAttributeResponseObject.ts} +4 -4
  196. package/core/security/data_objects/{PermissionResponseObject.js → PermissionResponseObject.ts} +12 -11
  197. package/core/security/data_objects/{PermissionTableResponseObject.js → PermissionTableResponseObject.ts} +6 -4
  198. package/core/security/{fastifyAuth.js → fastifyAuth.ts} +93 -20
  199. package/core/security/impersonation.ts +3 -3
  200. package/core/security/jsLoader.ts +22 -8
  201. package/core/security/{keys.js → keys.ts} +113 -121
  202. package/core/security/permissionsTranslator.js +2 -2
  203. package/core/security/{role.js → role.ts} +26 -33
  204. package/core/security/tokenAuthentication.ts +34 -7
  205. package/core/security/user.ts +26 -22
  206. package/core/server/DESIGN.md +139 -0
  207. package/core/server/DurableSubscriptionsSession.ts +67 -50
  208. package/core/server/REST.ts +120 -107
  209. package/core/server/Server.ts +31 -12
  210. package/core/server/fastifyRoutes/helpers/getCORSOptions.js +1 -1
  211. package/core/server/fastifyRoutes/helpers/getHeaderTimeoutConfig.js +1 -1
  212. package/core/server/fastifyRoutes/helpers/getServerOptions.js +1 -1
  213. package/core/server/fastifyRoutes.ts +37 -33
  214. package/core/server/graphqlQuerying.ts +6 -5
  215. package/core/server/http.ts +517 -26
  216. package/core/server/itc/serverHandlers.js +75 -14
  217. package/core/server/jobs/{JobObject.js → JobObject.ts} +13 -6
  218. package/core/server/jobs/{jobProcess.js → jobProcess.ts} +20 -16
  219. package/core/server/jobs/{jobRunner.js → jobRunner.ts} +20 -21
  220. package/core/server/jobs/{jobs.js → jobs.ts} +41 -44
  221. package/core/server/loadRootComponents.js +1 -1
  222. package/core/server/middlewareChain.ts +270 -0
  223. package/core/server/mqtt.ts +35 -26
  224. package/core/server/nodeName.ts +2 -1
  225. package/core/server/operationsServer.ts +85 -10
  226. package/core/server/serverHelpers/Headers.ts +10 -8
  227. package/core/server/serverHelpers/JSONStream.ts +15 -5
  228. package/core/server/serverHelpers/Request.ts +370 -13
  229. package/core/server/serverHelpers/contentTypes.ts +42 -11
  230. package/core/server/serverHelpers/multipartParser.ts +152 -0
  231. package/core/server/serverHelpers/progressEmitter.ts +110 -0
  232. package/core/server/serverHelpers/serverHandlers.js +43 -7
  233. package/core/server/serverHelpers/serverUtilities.ts +40 -30
  234. package/core/server/static.ts +9 -6
  235. package/core/server/status/index.ts +2 -2
  236. package/core/server/storageReclamation.ts +39 -2
  237. package/core/server/threads/itc.js +7 -4
  238. package/core/server/threads/manageThreads.js +100 -26
  239. package/core/server/threads/socketRouter.ts +12 -275
  240. package/core/server/threads/threadServer.js +345 -105
  241. package/core/server/threads/workerProcessGuard.ts +93 -0
  242. package/core/server/throttle.ts +18 -0
  243. package/core/sqlTranslator/{SelectValidator.js → SelectValidator.ts} +41 -49
  244. package/core/sqlTranslator/{alasqlFunctionImporter.js → alasqlFunctionImporter.ts} +5 -5
  245. package/core/sqlTranslator/{deleteTranslator.js → deleteTranslator.ts} +13 -18
  246. package/core/sqlTranslator/{index.js → index.ts} +30 -33
  247. package/core/sqlTranslator/{sql_statement_bucket.js → sql_statement_bucket.ts} +49 -20
  248. package/core/static/README.md +10 -9
  249. package/core/system/000004.log +0 -0
  250. package/core/system/CURRENT +1 -0
  251. package/core/system/IDENTITY +1 -0
  252. package/core/system/LOCK +0 -0
  253. package/core/system/LOG +1351 -0
  254. package/core/system/MANIFEST-000005 +0 -0
  255. package/core/system/OPTIONS-000013 +607 -0
  256. package/core/system/OPTIONS-000015 +734 -0
  257. package/core/upgrade/{UpgradeObjects.js → UpgradeObjects.ts} +3 -6
  258. package/core/upgrade/directives/5-2-0.ts +49 -0
  259. package/core/upgrade/directives/{directivesController.js → directivesController.ts} +16 -16
  260. package/core/upgrade/{directivesManager.js → directivesManager.ts} +7 -11
  261. package/core/upgrade/{upgradePrompt.js → upgradePrompt.ts} +8 -14
  262. package/core/upgrade/{upgradeUtilities.js → upgradeUtilities.ts} +3 -7
  263. package/core/utility/{OperationFunctionCaller.js → OperationFunctionCaller.ts} +7 -7
  264. package/core/utility/{assignCmdEnvVariables.js → assignCmdEnvVariables.ts} +6 -8
  265. package/core/utility/{common_utils.js → common_utils.ts} +113 -139
  266. package/core/utility/environment/{environmentManager.js → environmentManager.ts} +34 -33
  267. package/core/utility/environment/systemInformation.ts +18 -4
  268. package/core/utility/errors/{commonErrors.js → commonErrors.ts} +9 -9
  269. package/core/utility/errors/{hdbError.js → hdbError.ts} +39 -45
  270. package/core/utility/expandEnvVar.ts +110 -0
  271. package/core/utility/functions/geo.js +2 -2
  272. package/core/utility/functions/sql/alaSQLExtension.js +1 -1
  273. package/core/utility/globalSchema.ts +30 -0
  274. package/core/utility/hdbTerms.ts +56 -0
  275. package/core/utility/install/checkJWTTokensExist.js +1 -1
  276. package/core/utility/install/{installer.js → installer.ts} +58 -59
  277. package/core/utility/installation.ts +2 -2
  278. package/core/utility/lmdb/{DBIDefinition.js → DBIDefinition.ts} +4 -1
  279. package/core/utility/lmdb/{DeleteRecordsResponseObject.js → DeleteRecordsResponseObject.ts} +2 -1
  280. package/core/utility/lmdb/{InsertRecordsResponseObject.js → InsertRecordsResponseObject.ts} +2 -1
  281. package/core/utility/lmdb/OpenDBIObject.ts +43 -0
  282. package/core/utility/lmdb/{OpenEnvironmentObject.js → OpenEnvironmentObject.ts} +19 -6
  283. package/core/utility/lmdb/{UpdateRecordsResponseObject.js → UpdateRecordsResponseObject.ts} +2 -1
  284. package/core/utility/lmdb/{UpsertRecordsResponseObject.js → UpsertRecordsResponseObject.ts} +2 -1
  285. package/core/utility/lmdb/{cleanLMDBMap.js → cleanLMDBMap.ts} +5 -5
  286. package/core/utility/lmdb/{commonUtility.js → commonUtility.ts} +13 -21
  287. package/core/utility/lmdb/{deleteUtility.js → deleteUtility.ts} +8 -12
  288. package/core/utility/lmdb/{environmentUtility.js → environmentUtility.ts} +43 -52
  289. package/core/utility/lmdb/{searchCursorFunctions.js → searchCursorFunctions.ts} +12 -26
  290. package/core/utility/lmdb/{searchUtility.js → searchUtility.ts} +75 -64
  291. package/core/utility/lmdb/{terms.js → terms.ts} +10 -23
  292. package/core/utility/lmdb/{writeUtility.js → writeUtility.ts} +37 -22
  293. package/core/utility/logging/{harper_logger.js → harper_logger.ts} +136 -89
  294. package/core/utility/logging/{logRotator.js → logRotator.ts} +13 -13
  295. package/core/utility/logging/logger.ts +1 -1
  296. package/core/utility/logging/{readLog.js → readLog.ts} +19 -19
  297. package/core/utility/logging/{transactionLog.js → transactionLog.ts} +10 -14
  298. package/core/utility/{mount_hdb.js → mount_hdb.ts} +15 -16
  299. package/core/utility/{npmUtilities.js → npmUtilities.ts} +14 -17
  300. package/core/utility/{operation_authorization.js → operation_authorization.ts} +173 -124
  301. package/core/utility/packageUtils.js +7 -16
  302. package/core/utility/password.ts +1 -1
  303. package/core/utility/processManagement/processManagement.js +2 -2
  304. package/core/utility/processManagement/servicesConfig.js +1 -1
  305. package/core/utility/{signalling.js → signalling.ts} +6 -11
  306. package/core/utility/watcherFallback.ts +74 -0
  307. package/core/validation/analyticsValidator.ts +43 -0
  308. package/core/validation/{bulkDeleteValidator.js → bulkDeleteValidator.ts} +5 -5
  309. package/core/validation/{check_permissions.js → check_permissions.ts} +3 -3
  310. package/core/validation/{common_validators.js → common_validators.ts} +12 -24
  311. package/core/validation/{configValidator.js → configValidator.ts} +114 -18
  312. package/core/validation/{deleteValidator.js → deleteValidator.ts} +5 -5
  313. package/core/validation/{fileLoadValidator.js → fileLoadValidator.ts} +12 -19
  314. package/core/validation/{insertValidator.js → insertValidator.ts} +5 -5
  315. package/core/validation/{installValidator.js → installValidator.ts} +8 -8
  316. package/core/validation/{readLogValidator.js → readLogValidator.ts} +10 -10
  317. package/core/validation/{role_validation.js → role_validation.ts} +26 -32
  318. package/core/validation/{schemaMetadataValidator.js → schemaMetadataValidator.ts} +5 -11
  319. package/core/validation/{searchValidator.js → searchValidator.ts} +12 -11
  320. package/core/validation/statusValidator.ts +1 -1
  321. package/core/validation/{transactionLogValidator.js → transactionLogValidator.ts} +4 -9
  322. package/core/validation/{user_validation.js → user_validation.ts} +4 -10
  323. package/core/validation/{validationWrapper.js → validationWrapper.ts} +3 -9
  324. package/dist/analytics/profile.js +4 -0
  325. package/dist/analytics/profile.js.map +1 -1
  326. package/dist/cloneNode/cloneNode.js +224 -12
  327. package/dist/cloneNode/cloneNode.js.map +1 -1
  328. package/dist/core/agent/agent.js +175 -0
  329. package/dist/core/agent/agent.js.map +1 -0
  330. package/dist/core/agent/loop.js +176 -0
  331. package/dist/core/agent/loop.js.map +1 -0
  332. package/dist/core/agent/operations.js +137 -0
  333. package/dist/core/agent/operations.js.map +1 -0
  334. package/dist/core/agent/session.js +182 -0
  335. package/dist/core/agent/session.js.map +1 -0
  336. package/dist/core/agent/tools/fsTools.js +286 -0
  337. package/dist/core/agent/tools/fsTools.js.map +1 -0
  338. package/dist/core/agent/tools/httpFetchTool.js +116 -0
  339. package/dist/core/agent/tools/httpFetchTool.js.map +1 -0
  340. package/dist/core/agent/tools/scheduleTool.js +54 -0
  341. package/dist/core/agent/tools/scheduleTool.js.map +1 -0
  342. package/dist/core/agent/toolset.js +33 -0
  343. package/dist/core/agent/toolset.js.map +1 -0
  344. package/dist/core/agent/types.js +10 -0
  345. package/dist/core/agent/types.js.map +1 -0
  346. package/dist/core/bin/BinObjects.js +6 -3
  347. package/dist/core/bin/BinObjects.js.map +1 -1
  348. package/dist/core/bin/cliCredentials.js +130 -0
  349. package/dist/core/bin/cliCredentials.js.map +1 -0
  350. package/dist/core/bin/cliOperations.js +254 -40
  351. package/dist/core/bin/cliOperations.js.map +1 -1
  352. package/dist/core/bin/copyDb.js +16 -16
  353. package/dist/core/bin/copyDb.js.map +1 -1
  354. package/dist/core/bin/deployRenderer.js +185 -0
  355. package/dist/core/bin/deployRenderer.js.map +1 -0
  356. package/dist/core/bin/harper.js +92 -31
  357. package/dist/core/bin/harper.js.map +1 -1
  358. package/dist/core/bin/install.js +41 -4
  359. package/dist/core/bin/install.js.map +1 -1
  360. package/dist/core/bin/lite.js +3 -4
  361. package/dist/core/bin/lite.js.map +1 -1
  362. package/dist/core/bin/login.js +158 -0
  363. package/dist/core/bin/login.js.map +1 -0
  364. package/dist/core/bin/logout.js +16 -0
  365. package/dist/core/bin/logout.js.map +1 -0
  366. package/dist/core/bin/mcp/client.js +395 -0
  367. package/dist/core/bin/mcp/client.js.map +1 -0
  368. package/dist/core/bin/mcp/doctor.js +193 -0
  369. package/dist/core/bin/mcp/doctor.js.map +1 -0
  370. package/dist/core/bin/mcp/index.js +81 -0
  371. package/dist/core/bin/mcp/index.js.map +1 -0
  372. package/dist/core/bin/mcp/options.js +113 -0
  373. package/dist/core/bin/mcp/options.js.map +1 -0
  374. package/dist/core/bin/mcp/printConfig.js +85 -0
  375. package/dist/core/bin/mcp/printConfig.js.map +1 -0
  376. package/dist/core/bin/multipartBuilder.js +55 -0
  377. package/dist/core/bin/multipartBuilder.js.map +1 -0
  378. package/dist/core/bin/restart.js +85 -48
  379. package/dist/core/bin/restart.js.map +1 -1
  380. package/dist/core/bin/run.js +123 -77
  381. package/dist/core/bin/run.js.map +1 -1
  382. package/dist/core/bin/sseConsumer.js +127 -0
  383. package/dist/core/bin/sseConsumer.js.map +1 -0
  384. package/dist/core/bin/status.js +48 -11
  385. package/dist/core/bin/status.js.map +1 -1
  386. package/dist/core/bin/stop.js +44 -7
  387. package/dist/core/bin/stop.js.map +1 -1
  388. package/dist/core/bin/upgrade.js +6 -6
  389. package/dist/core/components/Application.js +134 -28
  390. package/dist/core/components/Application.js.map +1 -1
  391. package/dist/core/components/ApplicationScope.js +2 -2
  392. package/dist/core/components/ComponentV1.js +5 -5
  393. package/dist/core/components/ComponentV1.js.map +1 -1
  394. package/dist/core/components/EntryHandler.js +153 -13
  395. package/dist/core/components/EntryHandler.js.map +1 -1
  396. package/dist/core/components/OptionsWatcher.js +72 -10
  397. package/dist/core/components/OptionsWatcher.js.map +1 -1
  398. package/dist/core/components/Scope.js +105 -9
  399. package/dist/core/components/Scope.js.map +1 -1
  400. package/dist/core/components/anthropic/index.js +428 -0
  401. package/dist/core/components/anthropic/index.js.map +1 -0
  402. package/dist/core/components/bedrock/index.js +734 -0
  403. package/dist/core/components/bedrock/index.js.map +1 -0
  404. package/dist/core/components/componentLoader.js +63 -38
  405. package/dist/core/components/componentLoader.js.map +1 -1
  406. package/dist/core/components/deployLifecycle.js +156 -0
  407. package/dist/core/components/deployLifecycle.js.map +1 -0
  408. package/dist/core/components/deploymentOperations.js +185 -0
  409. package/dist/core/components/deploymentOperations.js.map +1 -0
  410. package/dist/core/components/deploymentRecorder.js +401 -0
  411. package/dist/core/components/deploymentRecorder.js.map +1 -0
  412. package/dist/core/components/deriveURLPath.js +2 -2
  413. package/dist/core/components/deriveURLPath.js.map +1 -1
  414. package/dist/core/components/mcp/adapters/fastify.js +66 -0
  415. package/dist/core/components/mcp/adapters/fastify.js.map +1 -0
  416. package/dist/core/components/mcp/adapters/harperHttp.js +78 -0
  417. package/dist/core/components/mcp/adapters/harperHttp.js.map +1 -0
  418. package/dist/core/components/mcp/audit.js +73 -0
  419. package/dist/core/components/mcp/audit.js.map +1 -0
  420. package/dist/core/components/mcp/index.js +109 -0
  421. package/dist/core/components/mcp/index.js.map +1 -0
  422. package/dist/core/components/mcp/jsonrpc.js +93 -0
  423. package/dist/core/components/mcp/jsonrpc.js.map +1 -0
  424. package/dist/core/components/mcp/lifecycle.js +79 -0
  425. package/dist/core/components/mcp/lifecycle.js.map +1 -0
  426. package/dist/core/components/mcp/listChanged.js +257 -0
  427. package/dist/core/components/mcp/listChanged.js.map +1 -0
  428. package/dist/core/components/mcp/rateLimit.js +226 -0
  429. package/dist/core/components/mcp/rateLimit.js.map +1 -0
  430. package/dist/core/components/mcp/resources.js +515 -0
  431. package/dist/core/components/mcp/resources.js.map +1 -0
  432. package/dist/core/components/mcp/session.js +170 -0
  433. package/dist/core/components/mcp/session.js.map +1 -0
  434. package/dist/core/components/mcp/sessionRegistry.js +124 -0
  435. package/dist/core/components/mcp/sessionRegistry.js.map +1 -0
  436. package/dist/core/components/mcp/toolRegistry.js +176 -0
  437. package/dist/core/components/mcp/toolRegistry.js.map +1 -0
  438. package/dist/core/components/mcp/tools/application.js +549 -0
  439. package/dist/core/components/mcp/tools/application.js.map +1 -0
  440. package/dist/core/components/mcp/tools/operations.js +303 -0
  441. package/dist/core/components/mcp/tools/operations.js.map +1 -0
  442. package/dist/core/components/mcp/tools/schemas/derive.js +216 -0
  443. package/dist/core/components/mcp/tools/schemas/derive.js.map +1 -0
  444. package/dist/core/components/mcp/tools/schemas/operations.js +243 -0
  445. package/dist/core/components/mcp/tools/schemas/operations.js.map +1 -0
  446. package/dist/core/components/mcp/transport.js +467 -0
  447. package/dist/core/components/mcp/transport.js.map +1 -0
  448. package/dist/core/components/ollama/index.js +239 -0
  449. package/dist/core/components/ollama/index.js.map +1 -0
  450. package/dist/core/components/openai/index.js +475 -0
  451. package/dist/core/components/openai/index.js.map +1 -0
  452. package/dist/core/components/operations.js +198 -52
  453. package/dist/core/components/operations.js.map +1 -1
  454. package/dist/core/components/operationsValidation.js +3 -3
  455. package/dist/core/components/packageComponent.js +87 -26
  456. package/dist/core/components/packageComponent.js.map +1 -1
  457. package/dist/core/components/requestRestart.js +12 -1
  458. package/dist/core/components/requestRestart.js.map +1 -1
  459. package/dist/core/components/status/crossThread.js +12 -5
  460. package/dist/core/components/status/crossThread.js.map +1 -1
  461. package/dist/core/components/status/errors.js +7 -7
  462. package/dist/core/config/RootConfigWatcher.js +52 -1
  463. package/dist/core/config/RootConfigWatcher.js.map +1 -1
  464. package/dist/core/config/configUtils.js +31 -8
  465. package/dist/core/config/configUtils.js.map +1 -1
  466. package/dist/core/config/harperConfigEnvVars.js +1 -1
  467. package/dist/core/config/harperConfigEnvVars.js.map +1 -1
  468. package/dist/core/dataLayer/CreateAttributeObject.js +4 -3
  469. package/dist/core/dataLayer/CreateAttributeObject.js.map +1 -1
  470. package/dist/core/dataLayer/CreateTableObject.js +2 -1
  471. package/dist/core/dataLayer/CreateTableObject.js.map +1 -1
  472. package/dist/core/dataLayer/DataLayerObjects.js +19 -5
  473. package/dist/core/dataLayer/DataLayerObjects.js.map +1 -1
  474. package/dist/core/dataLayer/DeleteBeforeObject.js +2 -1
  475. package/dist/core/dataLayer/DeleteBeforeObject.js.map +1 -1
  476. package/dist/core/dataLayer/DeleteObject.js +4 -3
  477. package/dist/core/dataLayer/DeleteObject.js.map +1 -1
  478. package/dist/core/dataLayer/DropAttributeObject.js +2 -1
  479. package/dist/core/dataLayer/DropAttributeObject.js.map +1 -1
  480. package/dist/core/dataLayer/GetBackupObject.js +4 -3
  481. package/dist/core/dataLayer/GetBackupObject.js.map +1 -1
  482. package/dist/core/dataLayer/InsertObject.js +4 -3
  483. package/dist/core/dataLayer/InsertObject.js.map +1 -1
  484. package/dist/core/dataLayer/ReadAuditLogObject.js +4 -3
  485. package/dist/core/dataLayer/ReadAuditLogObject.js.map +1 -1
  486. package/dist/core/dataLayer/SQLSearch.js +140 -78
  487. package/dist/core/dataLayer/SQLSearch.js.map +1 -1
  488. package/dist/core/dataLayer/SearchByConditionsObject.js +5 -7
  489. package/dist/core/dataLayer/SearchByConditionsObject.js.map +1 -1
  490. package/dist/core/dataLayer/SearchByHashObject.js +2 -1
  491. package/dist/core/dataLayer/SearchByHashObject.js.map +1 -1
  492. package/dist/core/dataLayer/SearchObject.js +2 -1
  493. package/dist/core/dataLayer/SearchObject.js.map +1 -1
  494. package/dist/core/dataLayer/SqlSearchObject.js +2 -1
  495. package/dist/core/dataLayer/SqlSearchObject.js.map +1 -1
  496. package/dist/core/dataLayer/UpdateObject.js +4 -3
  497. package/dist/core/dataLayer/UpdateObject.js.map +1 -1
  498. package/dist/core/dataLayer/UpsertObject.js +4 -3
  499. package/dist/core/dataLayer/UpsertObject.js.map +1 -1
  500. package/dist/core/dataLayer/bulkLoad.js +122 -88
  501. package/dist/core/dataLayer/bulkLoad.js.map +1 -1
  502. package/dist/core/dataLayer/delete.js +74 -39
  503. package/dist/core/dataLayer/delete.js.map +1 -1
  504. package/dist/core/dataLayer/export.js +90 -55
  505. package/dist/core/dataLayer/export.js.map +1 -1
  506. package/dist/core/dataLayer/getBackup.js +43 -11
  507. package/dist/core/dataLayer/getBackup.js.map +1 -1
  508. package/dist/core/dataLayer/harperBridge/BridgeMethods.js +41 -20
  509. package/dist/core/dataLayer/harperBridge/BridgeMethods.js.map +1 -1
  510. package/dist/core/dataLayer/harperBridge/ResourceBridge.js +26 -25
  511. package/dist/core/dataLayer/harperBridge/ResourceBridge.js.map +1 -1
  512. package/dist/core/dataLayer/harperBridge/TableSizeObject.js.map +1 -1
  513. package/dist/core/dataLayer/harperBridge/bridgeUtility/insertUpdateValidate.js +4 -4
  514. package/dist/core/dataLayer/harperBridge/harperBridge.js +38 -4
  515. package/dist/core/dataLayer/harperBridge/harperBridge.js.map +1 -1
  516. package/dist/core/dataLayer/harperBridge/lmdbBridge/lmdbMethods/lmdbCreateAttribute.js +7 -6
  517. package/dist/core/dataLayer/harperBridge/lmdbBridge/lmdbMethods/lmdbCreateAttribute.js.map +1 -1
  518. package/dist/core/dataLayer/harperBridge/lmdbBridge/lmdbMethods/lmdbCreateRecords.js +4 -4
  519. package/dist/core/dataLayer/harperBridge/lmdbBridge/lmdbMethods/lmdbCreateRecords.js.map +1 -1
  520. package/dist/core/dataLayer/harperBridge/lmdbBridge/lmdbMethods/lmdbCreateSchema.js +1 -1
  521. package/dist/core/dataLayer/harperBridge/lmdbBridge/lmdbMethods/lmdbCreateSchema.js.map +1 -1
  522. package/dist/core/dataLayer/harperBridge/lmdbBridge/lmdbMethods/lmdbCreateTable.js +5 -4
  523. package/dist/core/dataLayer/harperBridge/lmdbBridge/lmdbMethods/lmdbCreateTable.js.map +1 -1
  524. package/dist/core/dataLayer/harperBridge/lmdbBridge/lmdbMethods/lmdbDeleteAuditLogsBefore.js +4 -4
  525. package/dist/core/dataLayer/harperBridge/lmdbBridge/lmdbMethods/lmdbDeleteAuditLogsBefore.js.map +1 -1
  526. package/dist/core/dataLayer/harperBridge/lmdbBridge/lmdbMethods/lmdbDeleteRecords.js +4 -4
  527. package/dist/core/dataLayer/harperBridge/lmdbBridge/lmdbMethods/lmdbDropAttribute.js +5 -5
  528. package/dist/core/dataLayer/harperBridge/lmdbBridge/lmdbMethods/lmdbDropAttribute.js.map +1 -1
  529. package/dist/core/dataLayer/harperBridge/lmdbBridge/lmdbMethods/lmdbDropSchema.js +4 -4
  530. package/dist/core/dataLayer/harperBridge/lmdbBridge/lmdbMethods/lmdbDropSchema.js.map +1 -1
  531. package/dist/core/dataLayer/harperBridge/lmdbBridge/lmdbMethods/lmdbDropTable.js +5 -5
  532. package/dist/core/dataLayer/harperBridge/lmdbBridge/lmdbMethods/lmdbDropTable.js.map +1 -1
  533. package/dist/core/dataLayer/harperBridge/lmdbBridge/lmdbMethods/lmdbFlush.js +1 -1
  534. package/dist/core/dataLayer/harperBridge/lmdbBridge/lmdbMethods/lmdbGetBackup.js +3 -3
  535. package/dist/core/dataLayer/harperBridge/lmdbBridge/lmdbMethods/lmdbGetDataByHash.js +1 -1
  536. package/dist/core/dataLayer/harperBridge/lmdbBridge/lmdbMethods/lmdbGetDataByValue.js +2 -2
  537. package/dist/core/dataLayer/harperBridge/lmdbBridge/lmdbMethods/lmdbGetDataByValue.js.map +1 -1
  538. package/dist/core/dataLayer/harperBridge/lmdbBridge/lmdbMethods/lmdbReadAuditLog.js +5 -5
  539. package/dist/core/dataLayer/harperBridge/lmdbBridge/lmdbMethods/lmdbSearchByConditions.js +8 -8
  540. package/dist/core/dataLayer/harperBridge/lmdbBridge/lmdbMethods/lmdbSearchByConditions.js.map +1 -1
  541. package/dist/core/dataLayer/harperBridge/lmdbBridge/lmdbMethods/lmdbSearchByHash.js +1 -1
  542. package/dist/core/dataLayer/harperBridge/lmdbBridge/lmdbMethods/lmdbSearchByValue.js +3 -3
  543. package/dist/core/dataLayer/harperBridge/lmdbBridge/lmdbMethods/lmdbSearchByValue.js.map +1 -1
  544. package/dist/core/dataLayer/harperBridge/lmdbBridge/lmdbMethods/lmdbUpdateRecords.js +3 -3
  545. package/dist/core/dataLayer/harperBridge/lmdbBridge/lmdbMethods/lmdbUpsertRecords.js +5 -5
  546. package/dist/core/dataLayer/harperBridge/lmdbBridge/lmdbMethods/lmdbUpsertRecords.js.map +1 -1
  547. package/dist/core/dataLayer/harperBridge/lmdbBridge/lmdbUtility/LMDBCreateAttributeObject.js +1 -1
  548. package/dist/core/dataLayer/harperBridge/lmdbBridge/lmdbUtility/LMDBCreateAttributeObject.js.map +1 -1
  549. package/dist/core/dataLayer/harperBridge/lmdbBridge/lmdbUtility/initializeHashSearch.js +2 -2
  550. package/dist/core/dataLayer/harperBridge/lmdbBridge/lmdbUtility/initializeHashSearch.js.map +1 -1
  551. package/dist/core/dataLayer/harperBridge/lmdbBridge/lmdbUtility/initializePaths.js +2 -2
  552. package/dist/core/dataLayer/harperBridge/lmdbBridge/lmdbUtility/lmdbCheckForNewAttributes.js +4 -4
  553. package/dist/core/dataLayer/harperBridge/lmdbBridge/lmdbUtility/lmdbCheckForNewAttributes.js.map +1 -1
  554. package/dist/core/dataLayer/harperBridge/lmdbBridge/lmdbUtility/lmdbCreateTransactionsAuditEnvironment.js +5 -3
  555. package/dist/core/dataLayer/harperBridge/lmdbBridge/lmdbUtility/lmdbCreateTransactionsAuditEnvironment.js.map +1 -1
  556. package/dist/core/dataLayer/harperBridge/lmdbBridge/lmdbUtility/lmdbGetTableSize.js +2 -2
  557. package/dist/core/dataLayer/harperBridge/lmdbBridge/lmdbUtility/lmdbProcessRows.js +4 -4
  558. package/dist/core/dataLayer/harperBridge/lmdbBridge/lmdbUtility/lmdbProcessRows.js.map +1 -1
  559. package/dist/core/dataLayer/harperBridge/lmdbBridge/lmdbUtility/lmdbSearch.js +5 -5
  560. package/dist/core/dataLayer/harperBridge/lmdbBridge/lmdbUtility/lmdbWriteTransaction.js +4 -4
  561. package/dist/core/dataLayer/hdbInfoController.js +66 -31
  562. package/dist/core/dataLayer/hdbInfoController.js.map +1 -1
  563. package/dist/core/dataLayer/insert.js +63 -28
  564. package/dist/core/dataLayer/insert.js.map +1 -1
  565. package/dist/core/dataLayer/readAuditLog.js +45 -13
  566. package/dist/core/dataLayer/readAuditLog.js.map +1 -1
  567. package/dist/core/dataLayer/schema.js +124 -89
  568. package/dist/core/dataLayer/schema.js.map +1 -1
  569. package/dist/core/dataLayer/schemaDescribe.js +78 -41
  570. package/dist/core/dataLayer/schemaDescribe.js.map +1 -1
  571. package/dist/core/dataLayer/search.js +12 -13
  572. package/dist/core/dataLayer/search.js.map +1 -1
  573. package/dist/core/dataLayer/transaction.js +3 -4
  574. package/dist/core/dataLayer/transaction.js.map +1 -1
  575. package/dist/core/dataLayer/update.js +53 -18
  576. package/dist/core/dataLayer/update.js.map +1 -1
  577. package/dist/core/globals.js +1 -0
  578. package/dist/core/globals.js.map +1 -1
  579. package/dist/core/index.js +4 -1
  580. package/dist/core/index.js.map +1 -1
  581. package/dist/core/json/systemSchema.json +65 -0
  582. package/dist/core/launchServiceScripts/utility/checkNodeVersion.js +3 -0
  583. package/dist/core/launchServiceScripts/utility/checkNodeVersion.js.map +1 -1
  584. package/dist/core/resources/DatabaseTransaction.js +39 -15
  585. package/dist/core/resources/DatabaseTransaction.js.map +1 -1
  586. package/dist/core/resources/ErrorResource.js +3 -1
  587. package/dist/core/resources/ErrorResource.js.map +1 -1
  588. package/dist/core/resources/LMDBTransaction.js +18 -7
  589. package/dist/core/resources/LMDBTransaction.js.map +1 -1
  590. package/dist/core/resources/RecordEncoder.js +5 -2
  591. package/dist/core/resources/RecordEncoder.js.map +1 -1
  592. package/dist/core/resources/RequestTarget.js.map +1 -1
  593. package/dist/core/resources/Resource.js +37 -10
  594. package/dist/core/resources/Resource.js.map +1 -1
  595. package/dist/core/resources/ResourceInterface.js +20 -0
  596. package/dist/core/resources/ResourceInterface.js.map +1 -1
  597. package/dist/core/resources/Resources.js +7 -6
  598. package/dist/core/resources/Resources.js.map +1 -1
  599. package/dist/core/resources/RocksIndexStore.js +3 -0
  600. package/dist/core/resources/RocksIndexStore.js.map +1 -1
  601. package/dist/core/resources/RocksTransactionLogStore.js +46 -27
  602. package/dist/core/resources/RocksTransactionLogStore.js.map +1 -1
  603. package/dist/core/resources/Table.js +267 -107
  604. package/dist/core/resources/Table.js.map +1 -1
  605. package/dist/core/resources/analytics/metadata.js +1 -0
  606. package/dist/core/resources/analytics/metadata.js.map +1 -1
  607. package/dist/core/resources/analytics/read.js +16 -5
  608. package/dist/core/resources/analytics/read.js.map +1 -1
  609. package/dist/core/resources/analytics/write.js +232 -20
  610. package/dist/core/resources/analytics/write.js.map +1 -1
  611. package/dist/core/resources/auditStore.js +13 -8
  612. package/dist/core/resources/auditStore.js.map +1 -1
  613. package/dist/core/resources/blob.js +8 -8
  614. package/dist/core/resources/blob.js.map +1 -1
  615. package/dist/core/resources/dataLoader.js +13 -13
  616. package/dist/core/resources/databases.js +160 -52
  617. package/dist/core/resources/databases.js.map +1 -1
  618. package/dist/core/resources/graphql.js +224 -174
  619. package/dist/core/resources/graphql.js.map +1 -1
  620. package/dist/core/resources/indexes/HierarchicalNavigableSmallWorld.js +308 -59
  621. package/dist/core/resources/indexes/HierarchicalNavigableSmallWorld.js.map +1 -1
  622. package/dist/core/resources/indexes/vector.js +14 -0
  623. package/dist/core/resources/indexes/vector.js.map +1 -1
  624. package/dist/core/resources/loadEnv.js +21 -18
  625. package/dist/core/resources/loadEnv.js.map +1 -1
  626. package/dist/core/resources/login.js +5 -4
  627. package/dist/core/resources/login.js.map +1 -1
  628. package/dist/core/resources/models/Models.js +265 -0
  629. package/dist/core/resources/models/Models.js.map +1 -0
  630. package/dist/core/resources/models/TestBackend.js +71 -0
  631. package/dist/core/resources/models/TestBackend.js.map +1 -0
  632. package/dist/core/resources/models/agentLoop.js +746 -0
  633. package/dist/core/resources/models/agentLoop.js.map +1 -0
  634. package/dist/core/resources/models/analyticsTable.js +166 -0
  635. package/dist/core/resources/models/analyticsTable.js.map +1 -0
  636. package/dist/core/resources/models/backendHelpers.js +109 -0
  637. package/dist/core/resources/models/backendHelpers.js.map +1 -0
  638. package/dist/core/resources/models/backendRegistry.js +54 -0
  639. package/dist/core/resources/models/backendRegistry.js.map +1 -0
  640. package/dist/core/resources/models/bootstrap.js +112 -0
  641. package/dist/core/resources/models/bootstrap.js.map +1 -0
  642. package/dist/core/resources/models/embedHook.js +127 -0
  643. package/dist/core/resources/models/embedHook.js.map +1 -0
  644. package/dist/core/resources/models/types.js +11 -0
  645. package/dist/core/resources/models/types.js.map +1 -0
  646. package/dist/core/resources/replayLogs.js +7 -1
  647. package/dist/core/resources/replayLogs.js.map +1 -1
  648. package/dist/core/resources/roles.js +67 -71
  649. package/dist/core/resources/roles.js.map +1 -1
  650. package/dist/core/resources/search.js +349 -140
  651. package/dist/core/resources/search.js.map +1 -1
  652. package/dist/core/resources/tracked.js +14 -14
  653. package/dist/core/resources/tracked.js.map +1 -1
  654. package/dist/core/resources/transaction.js +1 -1
  655. package/dist/core/resources/transaction.js.map +1 -1
  656. package/dist/core/resources/transactionBroadcast.js.map +1 -1
  657. package/dist/core/security/auth.js +34 -25
  658. package/dist/core/security/auth.js.map +1 -1
  659. package/dist/core/security/certificateVerification/crlVerification.js +7 -1
  660. package/dist/core/security/certificateVerification/crlVerification.js.map +1 -1
  661. package/dist/core/security/cryptoHash.js +37 -5
  662. package/dist/core/security/cryptoHash.js.map +1 -1
  663. package/dist/core/security/data_objects/PermissionAttributeResponseObject.js +4 -1
  664. package/dist/core/security/data_objects/PermissionAttributeResponseObject.js.map +1 -1
  665. package/dist/core/security/data_objects/PermissionResponseObject.js +15 -8
  666. package/dist/core/security/data_objects/PermissionResponseObject.js.map +1 -1
  667. package/dist/core/security/data_objects/PermissionTableResponseObject.js +6 -1
  668. package/dist/core/security/data_objects/PermissionTableResponseObject.js.map +1 -1
  669. package/dist/core/security/fastifyAuth.js +131 -22
  670. package/dist/core/security/fastifyAuth.js.map +1 -1
  671. package/dist/core/security/impersonation.js +15 -15
  672. package/dist/core/security/jsLoader.js +18 -5
  673. package/dist/core/security/jsLoader.js.map +1 -1
  674. package/dist/core/security/keys.js +160 -114
  675. package/dist/core/security/keys.js.map +1 -1
  676. package/dist/core/security/permissionsTranslator.js +2 -2
  677. package/dist/core/security/role.js +70 -35
  678. package/dist/core/security/role.js.map +1 -1
  679. package/dist/core/security/tokenAuthentication.js +57 -27
  680. package/dist/core/security/tokenAuthentication.js.map +1 -1
  681. package/dist/core/security/user.js +74 -38
  682. package/dist/core/security/user.js.map +1 -1
  683. package/dist/core/server/DurableSubscriptionsSession.js +71 -55
  684. package/dist/core/server/DurableSubscriptionsSession.js.map +1 -1
  685. package/dist/core/server/REST.js +17 -16
  686. package/dist/core/server/REST.js.map +1 -1
  687. package/dist/core/server/Server.js +1 -1
  688. package/dist/core/server/Server.js.map +1 -1
  689. package/dist/core/server/fastifyRoutes/helpers/getCORSOptions.js +1 -1
  690. package/dist/core/server/fastifyRoutes/helpers/getHeaderTimeoutConfig.js +1 -1
  691. package/dist/core/server/fastifyRoutes/helpers/getServerOptions.js +1 -1
  692. package/dist/core/server/fastifyRoutes.js +34 -30
  693. package/dist/core/server/fastifyRoutes.js.map +1 -1
  694. package/dist/core/server/graphqlQuerying.js +8 -7
  695. package/dist/core/server/graphqlQuerying.js.map +1 -1
  696. package/dist/core/server/http.js +523 -47
  697. package/dist/core/server/http.js.map +1 -1
  698. package/dist/core/server/itc/serverHandlers.js +66 -15
  699. package/dist/core/server/itc/serverHandlers.js.map +1 -1
  700. package/dist/core/server/jobs/JobObject.js +53 -7
  701. package/dist/core/server/jobs/JobObject.js.map +1 -1
  702. package/dist/core/server/jobs/jobProcess.js +64 -24
  703. package/dist/core/server/jobs/jobProcess.js.map +1 -1
  704. package/dist/core/server/jobs/jobRunner.js +68 -30
  705. package/dist/core/server/jobs/jobRunner.js.map +1 -1
  706. package/dist/core/server/jobs/jobs.js +93 -61
  707. package/dist/core/server/jobs/jobs.js.map +1 -1
  708. package/dist/core/server/loadRootComponents.js +1 -1
  709. package/dist/core/server/middlewareChain.js +252 -0
  710. package/dist/core/server/middlewareChain.js.map +1 -0
  711. package/dist/core/server/mqtt.js +22 -17
  712. package/dist/core/server/mqtt.js.map +1 -1
  713. package/dist/core/server/nodeName.js +44 -13
  714. package/dist/core/server/nodeName.js.map +1 -1
  715. package/dist/core/server/operationsServer.js +106 -33
  716. package/dist/core/server/operationsServer.js.map +1 -1
  717. package/dist/core/server/serverHelpers/Headers.js +2 -0
  718. package/dist/core/server/serverHelpers/Headers.js.map +1 -1
  719. package/dist/core/server/serverHelpers/JSONStream.js +12 -3
  720. package/dist/core/server/serverHelpers/JSONStream.js.map +1 -1
  721. package/dist/core/server/serverHelpers/Request.js +370 -9
  722. package/dist/core/server/serverHelpers/Request.js.map +1 -1
  723. package/dist/core/server/serverHelpers/contentTypes.js +36 -7
  724. package/dist/core/server/serverHelpers/contentTypes.js.map +1 -1
  725. package/dist/core/server/serverHelpers/multipartParser.js +142 -0
  726. package/dist/core/server/serverHelpers/multipartParser.js.map +1 -0
  727. package/dist/core/server/serverHelpers/progressEmitter.js +103 -0
  728. package/dist/core/server/serverHelpers/progressEmitter.js.map +1 -0
  729. package/dist/core/server/serverHelpers/serverHandlers.js +38 -7
  730. package/dist/core/server/serverHelpers/serverHandlers.js.map +1 -1
  731. package/dist/core/server/serverHelpers/serverUtilities.js +97 -93
  732. package/dist/core/server/serverHelpers/serverUtilities.js.map +1 -1
  733. package/dist/core/server/static.js +8 -5
  734. package/dist/core/server/static.js.map +1 -1
  735. package/dist/core/server/status/index.js +3 -3
  736. package/dist/core/server/storageReclamation.js +68 -9
  737. package/dist/core/server/storageReclamation.js.map +1 -1
  738. package/dist/core/server/threads/itc.js +7 -4
  739. package/dist/core/server/threads/itc.js.map +1 -1
  740. package/dist/core/server/threads/manageThreads.js +110 -26
  741. package/dist/core/server/threads/manageThreads.js.map +1 -1
  742. package/dist/core/server/threads/socketRouter.js +8 -271
  743. package/dist/core/server/threads/socketRouter.js.map +1 -1
  744. package/dist/core/server/threads/threadServer.js +360 -118
  745. package/dist/core/server/threads/threadServer.js.map +1 -1
  746. package/dist/core/server/threads/workerProcessGuard.js +114 -0
  747. package/dist/core/server/threads/workerProcessGuard.js.map +1 -0
  748. package/dist/core/server/throttle.js +17 -0
  749. package/dist/core/server/throttle.js.map +1 -1
  750. package/dist/core/sqlTranslator/SelectValidator.js +86 -47
  751. package/dist/core/sqlTranslator/SelectValidator.js.map +1 -1
  752. package/dist/core/sqlTranslator/alasqlFunctionImporter.js +40 -3
  753. package/dist/core/sqlTranslator/alasqlFunctionImporter.js.map +1 -1
  754. package/dist/core/sqlTranslator/deleteTranslator.js +48 -14
  755. package/dist/core/sqlTranslator/deleteTranslator.js.map +1 -1
  756. package/dist/core/sqlTranslator/index.js +69 -30
  757. package/dist/core/sqlTranslator/index.js.map +1 -1
  758. package/dist/core/sqlTranslator/sql_statement_bucket.js +55 -13
  759. package/dist/core/sqlTranslator/sql_statement_bucket.js.map +1 -1
  760. package/dist/core/upgrade/UpgradeObjects.js +37 -4
  761. package/dist/core/upgrade/UpgradeObjects.js.map +1 -1
  762. package/dist/core/upgrade/directives/5-2-0.js +77 -0
  763. package/dist/core/upgrade/directives/5-2-0.js.map +1 -0
  764. package/dist/core/upgrade/directives/directivesController.js +52 -11
  765. package/dist/core/upgrade/directives/directivesController.js.map +1 -1
  766. package/dist/core/upgrade/directivesManager.js +53 -18
  767. package/dist/core/upgrade/directivesManager.js.map +1 -1
  768. package/dist/core/upgrade/upgradePrompt.js +65 -30
  769. package/dist/core/upgrade/upgradePrompt.js.map +1 -1
  770. package/dist/core/upgrade/upgradeUtilities.js +37 -5
  771. package/dist/core/upgrade/upgradeUtilities.js.map +1 -1
  772. package/dist/core/utility/OperationFunctionCaller.js +45 -10
  773. package/dist/core/utility/OperationFunctionCaller.js.map +1 -1
  774. package/dist/core/utility/assignCmdEnvVariables.js +8 -4
  775. package/dist/core/utility/assignCmdEnvVariables.js.map +1 -1
  776. package/dist/core/utility/common_utils.js +140 -79
  777. package/dist/core/utility/common_utils.js.map +1 -1
  778. package/dist/core/utility/environment/environmentManager.js +75 -29
  779. package/dist/core/utility/environment/environmentManager.js.map +1 -1
  780. package/dist/core/utility/environment/systemInformation.js +27 -16
  781. package/dist/core/utility/environment/systemInformation.js.map +1 -1
  782. package/dist/core/utility/errors/commonErrors.js +49 -18
  783. package/dist/core/utility/errors/commonErrors.js.map +1 -1
  784. package/dist/core/utility/errors/hdbError.js +65 -26
  785. package/dist/core/utility/errors/hdbError.js.map +1 -1
  786. package/dist/core/utility/expandEnvVar.js +113 -0
  787. package/dist/core/utility/expandEnvVar.js.map +1 -0
  788. package/dist/core/utility/functions/geo.js +2 -2
  789. package/dist/core/utility/functions/sql/alaSQLExtension.js +1 -1
  790. package/dist/core/utility/globalSchema.js +14 -11
  791. package/dist/core/utility/globalSchema.js.map +1 -1
  792. package/dist/core/utility/hdbTerms.js +56 -0
  793. package/dist/core/utility/hdbTerms.js.map +1 -1
  794. package/dist/core/utility/install/checkJWTTokensExist.js +1 -1
  795. package/dist/core/utility/install/installer.js +106 -70
  796. package/dist/core/utility/install/installer.js.map +1 -1
  797. package/dist/core/utility/installation.js +3 -3
  798. package/dist/core/utility/lmdb/DBIDefinition.js +5 -1
  799. package/dist/core/utility/lmdb/DBIDefinition.js.map +1 -1
  800. package/dist/core/utility/lmdb/DeleteRecordsResponseObject.js +2 -1
  801. package/dist/core/utility/lmdb/DeleteRecordsResponseObject.js.map +1 -1
  802. package/dist/core/utility/lmdb/InsertRecordsResponseObject.js +2 -1
  803. package/dist/core/utility/lmdb/InsertRecordsResponseObject.js.map +1 -1
  804. package/dist/core/utility/lmdb/OpenDBIObject.js +54 -6
  805. package/dist/core/utility/lmdb/OpenDBIObject.js.map +1 -1
  806. package/dist/core/utility/lmdb/OpenEnvironmentObject.js +52 -4
  807. package/dist/core/utility/lmdb/OpenEnvironmentObject.js.map +1 -1
  808. package/dist/core/utility/lmdb/UpdateRecordsResponseObject.js +2 -1
  809. package/dist/core/utility/lmdb/UpdateRecordsResponseObject.js.map +1 -1
  810. package/dist/core/utility/lmdb/UpsertRecordsResponseObject.js +2 -1
  811. package/dist/core/utility/lmdb/UpsertRecordsResponseObject.js.map +1 -1
  812. package/dist/core/utility/lmdb/cleanLMDBMap.js +44 -7
  813. package/dist/core/utility/lmdb/cleanLMDBMap.js.map +1 -1
  814. package/dist/core/utility/lmdb/commonUtility.js +46 -17
  815. package/dist/core/utility/lmdb/commonUtility.js.map +1 -1
  816. package/dist/core/utility/lmdb/deleteUtility.js +51 -16
  817. package/dist/core/utility/lmdb/deleteUtility.js.map +1 -1
  818. package/dist/core/utility/lmdb/environmentUtility.js +91 -51
  819. package/dist/core/utility/lmdb/environmentUtility.js.map +1 -1
  820. package/dist/core/utility/lmdb/searchCursorFunctions.js +46 -14
  821. package/dist/core/utility/lmdb/searchCursorFunctions.js.map +1 -1
  822. package/dist/core/utility/lmdb/searchUtility.js +91 -55
  823. package/dist/core/utility/lmdb/searchUtility.js.map +1 -1
  824. package/dist/core/utility/lmdb/terms.js +12 -22
  825. package/dist/core/utility/lmdb/terms.js.map +1 -1
  826. package/dist/core/utility/lmdb/writeUtility.js +61 -28
  827. package/dist/core/utility/lmdb/writeUtility.js.map +1 -1
  828. package/dist/core/utility/logging/harper_logger.js +176 -81
  829. package/dist/core/utility/logging/harper_logger.js.map +1 -1
  830. package/dist/core/utility/logging/logRotator.js +65 -28
  831. package/dist/core/utility/logging/logRotator.js.map +1 -1
  832. package/dist/core/utility/logging/logger.js +4 -4
  833. package/dist/core/utility/logging/readLog.js +54 -17
  834. package/dist/core/utility/logging/readLog.js.map +1 -1
  835. package/dist/core/utility/logging/transactionLog.js +51 -16
  836. package/dist/core/utility/logging/transactionLog.js.map +1 -1
  837. package/dist/core/utility/mount_hdb.js +54 -17
  838. package/dist/core/utility/mount_hdb.js.map +1 -1
  839. package/dist/core/utility/npmUtilities.js +54 -19
  840. package/dist/core/utility/npmUtilities.js.map +1 -1
  841. package/dist/core/utility/operation_authorization.js +135 -86
  842. package/dist/core/utility/operation_authorization.js.map +1 -1
  843. package/dist/core/utility/packageUtils.js +7 -17
  844. package/dist/core/utility/packageUtils.js.map +1 -1
  845. package/dist/core/utility/password.js +2 -2
  846. package/dist/core/utility/processManagement/processManagement.js +2 -2
  847. package/dist/core/utility/processManagement/servicesConfig.js +1 -1
  848. package/dist/core/utility/signalling.js +51 -16
  849. package/dist/core/utility/signalling.js.map +1 -1
  850. package/dist/core/utility/watcherFallback.js +73 -0
  851. package/dist/core/utility/watcherFallback.js.map +1 -0
  852. package/dist/core/validation/analyticsValidator.js +79 -0
  853. package/dist/core/validation/analyticsValidator.js.map +1 -0
  854. package/dist/core/validation/bulkDeleteValidator.js +49 -11
  855. package/dist/core/validation/bulkDeleteValidator.js.map +1 -1
  856. package/dist/core/validation/check_permissions.js +38 -3
  857. package/dist/core/validation/check_permissions.js.map +1 -1
  858. package/dist/core/validation/common_validators.js +62 -31
  859. package/dist/core/validation/common_validators.js.map +1 -1
  860. package/dist/core/validation/configValidator.js +189 -54
  861. package/dist/core/validation/configValidator.js.map +1 -1
  862. package/dist/core/validation/deleteValidator.js +49 -11
  863. package/dist/core/validation/deleteValidator.js.map +1 -1
  864. package/dist/core/validation/fileLoadValidator.js +67 -32
  865. package/dist/core/validation/fileLoadValidator.js.map +1 -1
  866. package/dist/core/validation/insertValidator.js +48 -10
  867. package/dist/core/validation/insertValidator.js.map +1 -1
  868. package/dist/core/validation/installValidator.js +47 -10
  869. package/dist/core/validation/installValidator.js.map +1 -1
  870. package/dist/core/validation/readLogValidator.js +60 -22
  871. package/dist/core/validation/readLogValidator.js.map +1 -1
  872. package/dist/core/validation/role_validation.js +55 -19
  873. package/dist/core/validation/role_validation.js.map +1 -1
  874. package/dist/core/validation/schemaMetadataValidator.js +11 -12
  875. package/dist/core/validation/schemaMetadataValidator.js.map +1 -1
  876. package/dist/core/validation/searchValidator.js +82 -43
  877. package/dist/core/validation/searchValidator.js.map +1 -1
  878. package/dist/core/validation/transactionLogValidator.js +52 -17
  879. package/dist/core/validation/transactionLogValidator.js.map +1 -1
  880. package/dist/core/validation/user_validation.js +38 -6
  881. package/dist/core/validation/user_validation.js.map +1 -1
  882. package/dist/core/validation/validationWrapper.js +4 -5
  883. package/dist/core/validation/validationWrapper.js.map +1 -1
  884. package/dist/licensing/usageLicensing.js +30 -21
  885. package/dist/licensing/usageLicensing.js.map +1 -1
  886. package/dist/replication/knownNodes.js +173 -39
  887. package/dist/replication/knownNodes.js.map +1 -1
  888. package/dist/replication/replicationConnection.js +441 -85
  889. package/dist/replication/replicationConnection.js.map +1 -1
  890. package/dist/replication/replicator.js +44 -26
  891. package/dist/replication/replicator.js.map +1 -1
  892. package/dist/replication/setNode.js +22 -2
  893. package/dist/replication/setNode.js.map +1 -1
  894. package/dist/replication/subscriptionManager.js +121 -9
  895. package/dist/replication/subscriptionManager.js.map +1 -1
  896. package/dist/security/certificate.js +41 -6
  897. package/dist/security/certificate.js.map +1 -1
  898. package/dist/security/sshKeyOperations.js +35 -2
  899. package/dist/security/sshKeyOperations.js.map +1 -1
  900. package/licensing/usageLicensing.ts +32 -37
  901. package/npm-shrinkwrap.json +8994 -4305
  902. package/package.json +16 -11
  903. package/replication/DESIGN.md +139 -0
  904. package/replication/knownNodes.ts +166 -43
  905. package/replication/replicationConnection.ts +475 -92
  906. package/replication/replicator.ts +42 -25
  907. package/replication/setNode.ts +28 -9
  908. package/replication/subscriptionManager.ts +138 -14
  909. package/security/certificate.ts +8 -4
  910. package/security/sshKeyOperations.ts +1 -1
  911. package/static/defaultConfig.yaml +1 -0
  912. package/studio/web/assets/{index-CmtPP0YO.js → index-COfIkCT-.js} +5 -5
  913. package/studio/web/assets/index-COfIkCT-.js.map +1 -0
  914. package/studio/web/assets/{index.lazy-C8jvGtlu.js → index.lazy-CIvl7Fj9.js} +2 -2
  915. package/studio/web/assets/{index.lazy-C8jvGtlu.js.map → index.lazy-CIvl7Fj9.js.map} +1 -1
  916. package/studio/web/assets/{profile-O0DYlJUv.js → profile-B2ZVB--r.js} +2 -2
  917. package/studio/web/assets/{profile-O0DYlJUv.js.map → profile-B2ZVB--r.js.map} +1 -1
  918. package/studio/web/assets/{status-BIlJkJby.js → status-Db6WBmhf.js} +2 -2
  919. package/studio/web/assets/{status-BIlJkJby.js.map → status-Db6WBmhf.js.map} +1 -1
  920. package/studio/web/index.html +1 -1
  921. package/core/bin/cliOperations.js +0 -159
  922. package/core/bin/lite.js +0 -5
  923. package/core/bin/stop.js +0 -21
  924. package/core/dataLayer/harperBridge/BridgeMethods.js +0 -85
  925. package/core/utility/globalSchema.js +0 -35
  926. package/core/utility/lmdb/OpenDBIObject.js +0 -31
  927. package/studio/web/assets/index-CmtPP0YO.js.map +0 -1
@@ -31,10 +31,11 @@ import {
31
31
  urlToNodeName,
32
32
  } from './replicator.ts';
33
33
  import { getThisNodeName } from '../core/server/nodeName.ts';
34
- import env from '../core/utility/environment/environmentManager.js';
34
+ import * as env from '../core/utility/environment/environmentManager.js';
35
35
  import { CONFIG_PARAMS } from '../core/utility/hdbTerms.ts';
36
36
  import { HAS_STRUCTURE_UPDATE, lastMetadata, lastValueEncoding, METADATA } from '../core/resources/RecordEncoder.ts';
37
37
  import { decode, encode, Packr } from 'msgpackr';
38
+ import { createStructon } from 'structon';
38
39
  import { WebSocket } from 'ws';
39
40
  import { threadId } from 'worker_threads';
40
41
  import harperLogger from '../core/utility/logging/harper_logger.js';
@@ -43,11 +44,17 @@ import { disconnectedFromNode, connectedToNode, ensureNode } from './subscriptio
43
44
  import { EventEmitter } from 'events';
44
45
  import { createTLSSelector } from '../core/security/keys.js';
45
46
  import * as tls from 'node:tls';
46
- import { getHDBNodeTable, getNodeURL, getReplicationSharedStatus, type Node } from './knownNodes.ts';
47
+ import {
48
+ getHDBNodeTable,
49
+ getNodeURL,
50
+ getReplicationSharedStatus,
51
+ getExcludedTablesForRouteEntries,
52
+ } from './knownNodes.ts';
47
53
  import * as process from 'node:process';
48
54
  import { isIP } from 'node:net';
49
55
  import { recordAction } from '../core/resources/analytics/write.ts';
50
56
  import {
57
+ createBlob,
51
58
  decodeBlobsWithWrites,
52
59
  decodeFromDatabase,
53
60
  decodeWithBlobCallback,
@@ -59,6 +66,14 @@ import { PassThrough } from 'node:stream';
59
66
  import { getLastVersion } from 'lmdb';
60
67
  const logger = forComponent('replication').conditional as Logger;
61
68
 
69
+ // msgpackr v2 removed the built-in `randomAccessStructure` option; that random-access
70
+ // struct support now lives in the `structon` package (the same wrapper core's
71
+ // RecordEncoder uses). Replication decoders must be structon-wrapped so they install
72
+ // the `_readStruct` hook and can decode typed-struct records — without it, struct
73
+ // marker bytes (0x20–0x3f) decode as plain integers and the decode aborts with
74
+ // "Data read, but end of buffer not reached".
75
+ const StructonPackr = createStructon(Packr);
76
+
62
77
  // these are the codes we use for the different commands
63
78
  const SUBSCRIPTION_REQUEST = 129;
64
79
  const NODE_NAME = 140;
@@ -75,6 +90,8 @@ const COMMITTED_UPDATE = 144;
75
90
  const DB_SCHEMA = 145;
76
91
  const BLOB_CHUNK = 146;
77
92
  const SUBSCRIPTION_UPDATE = 147;
93
+ const COPY_START = 148; // leader -> follower: a bulk table copy is starting; carries copyStartTime
94
+ const COPY_COMPLETE = 149; // leader -> follower: the bulk table copy finished; follower clears its resume cursor
78
95
  export const CONFIRMATION_STATUS_POSITION = 0;
79
96
  export const RECEIVED_VERSION_POSITION = 1;
80
97
  export const RECEIVED_TIME_POSITION = 2;
@@ -91,6 +108,16 @@ const MAX_PAYLOAD = env.get('replication_maxPayload') ?? 100_000_000;
91
108
  // heap past its limit. If the local replicator queue grows beyond this threshold we pause
92
109
  // the WS connection and wait for it to drain before continuing the decode loop.
93
110
  const RECEIVE_EVENT_HIGH_WATER_MARK = env.get('replication_receiveEventHighWaterMark') ?? 100;
111
+ // Even when the consumer keeps up (queue below the high-water mark), a single large inbound message
112
+ // would otherwise decode thousands of records in one synchronous turn — pegging the worker, blocking
113
+ // replication ping responses, and tripping core's "JavaScript execution has taken too long" monitor
114
+ // (MAX_EVENT_DELAY_TIME = 3 s). Yield the event loop at least this often (ms) while decoding so the
115
+ // worker stays responsive during a bulk copy/clone.
116
+ const RECEIVE_YIELD_INTERVAL = env.get('replication_receiveYieldInterval') ?? 100;
117
+ // During a bulk clone copy the leader flushes a checkpoint transaction every this many records so the
118
+ // follower commits incrementally and persists a resume cursor. On reconnect the copy resumes from that
119
+ // cursor instead of restarting from zero. Larger = less overhead but coarser resume granularity.
120
+ const COPY_CHECKPOINT_RECORDS = env.get('replication_copyCheckpointRecords') ?? 1000;
94
121
 
95
122
  export const tableUpdateListeners = new Map();
96
123
  // This a map of the database name to the subscription object, for the subscriptions from our tables to the replication module
@@ -104,7 +131,95 @@ const SKIPPED_MESSAGE_SEQUENCE_UPDATE_DELAY = 300;
104
131
  // We want it be fairly quick so we can let the sending node know that we have received and committed the update.
105
132
  // (but still allow for batching so we aren't sending out a message for every update under load)
106
133
  const COMMITTED_UPDATE_DELAY = 2;
107
- const PING_INTERVAL = 30000;
134
+ const PING_INTERVAL = env.get(CONFIG_PARAMS.REPLICATION_PINGINTERVAL) ?? 30000;
135
+ // Time (ms) without any socket activity before a connection is treated as dead.
136
+ const PING_TIMEOUT = env.get(CONFIG_PARAMS.REPLICATION_PINGTIMEOUT) ?? PING_INTERVAL * 2;
137
+ // The receive-side watchdog fires after this much silence on a replication WS. Both client and
138
+ // server arm it: the client also runs an active 30s sendPing tick that should normally catch a
139
+ // silent peer first, but if that tick is missed (event-loop stall, ws.terminate() not propagating
140
+ // to 'close', etc.) this timer-based watchdog is the belt-and-suspenders that forces the
141
+ // reconnect — see harper-pro#233.
142
+ const RECEIVE_SILENCE_THRESHOLD_MS = PING_TIMEOUT;
143
+
144
+ /**
145
+ * Decide whether an idle replication connection should be terminated as dead.
146
+ *
147
+ * Liveness is measured from the last observed socket byte movement (in either direction), not from a
148
+ * single ping interval. A bulk transfer — notably the initial clone copy of a large table — makes
149
+ * slow but real progress: the sender's socket buffer drains in bursts as the peer consumes, so bytes
150
+ * keep moving within the timeout window even while it is otherwise stalled. A genuinely dead or
151
+ * unreachable peer moves no bytes at all, so it still trips the timeout. We deliberately do NOT exempt
152
+ * the sender's `isPausedForBackPressure` drain-wait here: if the peer dies after we have filled our
153
+ * socket buffer, the drain event never fires, and exempting it would hang the connection forever.
154
+ *
155
+ * The one exemption is `pauseReasons > 0`: the receiver has intentionally stopped reading to drain its
156
+ * own queue. That stall is local and self-clearing (it does not depend on the peer), so it is never a
157
+ * death signal; the caller keeps liveness fresh while paused and resumes normal detection afterward.
158
+ */
159
+ export function shouldTerminateIdlePing(idleMs: number, pingTimeout: number, pauseReasons: number): boolean {
160
+ return pauseReasons === 0 && idleMs >= pingTimeout;
161
+ }
162
+
163
+ /**
164
+ * Receive-side silence watchdog. Arm with `reset()` whenever incoming activity is observed
165
+ * (peer ping, pong, message). If `intervalMs` elapses with the underlying socket's `bytesRead`
166
+ * unchanged, `onSilence` is invoked exactly once.
167
+ *
168
+ * Only `bytesRead` is checked — `bytesWritten` reflects our own outbound traffic (pings, blob
169
+ * sends) and is not proof that the peer is alive. Including it would let our own keepalive
170
+ * pings suppress the watchdog in the exact missed-sendPing scenario this is meant to recover.
171
+ *
172
+ * Callers should suspend the watchdog (via `stop()`) when the underlying WS is intentionally
173
+ * paused for backpressure: `bytesRead` is frozen by design while paused and would otherwise
174
+ * cause a spurious termination of a healthy connection.
175
+ *
176
+ * Exported so the timer logic can be exercised in isolation by `unitTests/replication/
177
+ * receiveWatchdog.test.mjs` — production callers go through `replicateOverWS`.
178
+ */
179
+ export function createReceiveWatchdog(opts: {
180
+ intervalMs: number;
181
+ getBytesRead: () => number;
182
+ onSilence: () => void;
183
+ }): { reset: () => void; stop: () => void } {
184
+ let timer: NodeJS.Timeout | undefined;
185
+ let bytesReadAtArm = 0;
186
+ let lastResetAt = 0;
187
+ // Coalesce rapid reset() calls (e.g. message frames arriving thousands of times per second
188
+ // during a large copy) so we do not churn setTimeout/clearTimeout per frame. Granularity loss
189
+ // is small relative to intervalMs — at worst the watchdog fires this much earlier or later.
190
+ const throttleMs = Math.min(1000, Math.max(100, opts.intervalMs / 30));
191
+ function check() {
192
+ const current = opts.getBytesRead();
193
+ if (current === bytesReadAtArm) {
194
+ timer = undefined;
195
+ opts.onSilence();
196
+ return;
197
+ }
198
+ // Bytes advanced since the last arm — but the activity may have been swallowed by the
199
+ // reset() throttle, so we cannot rely on an external caller to re-arm us. Re-arm from
200
+ // the new baseline; otherwise a throttled-reset-then-silence sequence would leave the
201
+ // watchdog permanently inactive (see PR #234 review).
202
+ bytesReadAtArm = current;
203
+ lastResetAt = Date.now();
204
+ timer = setTimeout(check, opts.intervalMs).unref();
205
+ }
206
+ return {
207
+ reset() {
208
+ const now = Date.now();
209
+ if (timer && now - lastResetAt < throttleMs) return;
210
+ lastResetAt = now;
211
+ if (timer) clearTimeout(timer);
212
+ bytesReadAtArm = opts.getBytesRead();
213
+ timer = setTimeout(check, opts.intervalMs).unref();
214
+ },
215
+ stop() {
216
+ if (timer) {
217
+ clearTimeout(timer);
218
+ timer = undefined;
219
+ }
220
+ },
221
+ };
222
+ }
108
223
  let secureContexts: Map<string, tls.SecureContext>;
109
224
  /**
110
225
  * Handles reconnection, and requesting catch-up
@@ -161,7 +276,7 @@ export async function createWebSocket(
161
276
  );
162
277
  }
163
278
  }
164
- const headers = {};
279
+ const headers: Record<string, string> = {};
165
280
  if (authorization) {
166
281
  headers.Authorization = authorization;
167
282
  }
@@ -222,6 +337,7 @@ export class NodeReplicationConnection extends EventEmitter {
222
337
  databaseName: string;
223
338
  nodeName?: string;
224
339
  authorization?: string;
340
+ tentativeNode?: any;
225
341
  constructor(url: string, subscription: any, databaseName: string, nodeName?: string, authorization?: string) {
226
342
  super();
227
343
  this.url = url;
@@ -232,6 +348,7 @@ export class NodeReplicationConnection extends EventEmitter {
232
348
  }
233
349
 
234
350
  async connect() {
351
+ if (this.intentionallyUnsubscribed) return;
235
352
  if (!this.session) this.resetSession();
236
353
  // TODO: Need to do this specifically for each node
237
354
  this.socket = await createWebSocket(this.url, { serverName: this.nodeName, authorization: this.authorization });
@@ -357,7 +474,7 @@ export class NodeReplicationConnection extends EventEmitter {
357
474
  }
358
475
  unsubscribe() {
359
476
  this.intentionallyUnsubscribed = true;
360
- this.socket.close(1008, 'No longer subscribed');
477
+ this.socket?.close(1008, 'No longer subscribed');
361
478
  }
362
479
 
363
480
  getRecord(request) {
@@ -370,7 +487,7 @@ export class NodeReplicationConnection extends EventEmitter {
370
487
  /**
371
488
  * This handles both incoming and outgoing WS allowing either one to issue a subscription and get replication and/or handle subscription requests
372
489
  */
373
- export function replicateOverWS(ws: WebSocket, options: any, authorization: Promise<Node | undefined>) {
490
+ export function replicateOverWS(ws: WebSocket, options: any, authorization: any) {
374
491
  const p = options.port || options.securePort;
375
492
  const connectionId =
376
493
  (process.pid % 1000) +
@@ -387,7 +504,7 @@ export function replicateOverWS(ws: WebSocket, options: any, authorization: Prom
387
504
  let databaseName = options.database;
388
505
  const dbSubscriptions = options.databaseSubscriptions || databaseSubscriptions;
389
506
  let auditStore: any;
390
- let auditLogIterable: Iterable<AuditRecord>; // reusable iterator for a subscription
507
+ let auditLogIterable: Iterable<AuditRecord> & { removeLog?: (name: string) => void; addLog?: (name: string) => void }; // reusable iterator for a subscription
391
508
  let replicationSharedStatus: Float64Array;
392
509
  // this is the subscription that the local table makes to this replicator, and incoming messages
393
510
  // are sent to this subscription queue:
@@ -405,7 +522,33 @@ export function replicateOverWS(ws: WebSocket, options: any, authorization: Prom
405
522
  remoteNodeName = authorization.name;
406
523
  if (remoteNodeName && options.connection) options.connection.nodeName = remoteNodeName;
407
524
  let lastSequenceIdReceived, lastSequenceIdCommitted;
408
- let sendPingInterval, receivePingTimer, lastPingTime, skippedMessageSequenceUpdateTimer;
525
+ // Bulk-copy resume state (receiver side). While inCopyMode, each committed batch persists a cursor
526
+ // (copyStartTime + last committed table/key) so an interrupted copy can resume instead of restarting.
527
+ let inCopyMode = false;
528
+ let copyModeStartTime = 0;
529
+ let copyFromNodeId; // local id of the node we are copying from — the key for the persisted cursor
530
+ let copyCompleteReceived = false;
531
+ // Finish the copy — leave copy mode and remove the resume cursor — only once COPY_COMPLETE has been
532
+ // received AND every copied batch has committed (outstandingCommits drained, which includes the final
533
+ // end_txn that advances the resume seqId to copyStartTime). We deliberately stay in copy mode until
534
+ // then so batches still committing keep advancing the cursor (onCommit). Finishing earlier — e.g.
535
+ // synchronously when COPY_COMPLETE is decoded while batches are still queued — would freeze the cursor
536
+ // and risk a crash that loses both the cursor and the not-yet-durable rows, leaving the next start to
537
+ // resume from seqId with gaps.
538
+ function maybeFinishCopy() {
539
+ if (copyCompleteReceived && outstandingCommits === 0) {
540
+ // guard only the cursor removal on a known node id; ALWAYS exit copy mode, otherwise a
541
+ // COPY_START whose getIdOfRemoteNode returned undefined would strand the node in copy mode
542
+ // (received-version watermark suppressed) and it could never reach Available.
543
+ if (copyFromNodeId !== undefined)
544
+ tableSubscriptionToReplicator?.dbisDB?.remove([Symbol.for('copyCursor'), copyFromNodeId]);
545
+ inCopyMode = false;
546
+ copyCompleteReceived = false;
547
+ copyFromNodeId = undefined;
548
+ }
549
+ }
550
+ let sendPingInterval, lastPingTime, skippedMessageSequenceUpdateTimer;
551
+ let receiveWatchdog: { reset: () => void; stop: () => void } | undefined;
409
552
  let blobsTimer;
410
553
  const DELAY_CLOSE_TIME = 60000; // amount of time to wait before closing the connection if we haven't any activity and there are no subscriptions
411
554
  let delayedClose: NodeJS.Timeout;
@@ -413,41 +556,78 @@ export function replicateOverWS(ws: WebSocket, options: any, authorization: Prom
413
556
  // track bytes read and written so we can verify if a connection is really dead on pings
414
557
  let bytesRead = 0;
415
558
  let bytesWritten = 0;
559
+ // wall-clock time of the last observed socket activity (bytes moved in either direction); the
560
+ // keep-alive timeout is measured from this so a legitimately slow-but-progressing transfer stays
561
+ // alive while a truly idle/dead peer is still terminated.
562
+ let lastByteActivity = performance.now();
563
+ // Multiple independent conditions can ask to pause receive on this WS (commit backlog, consumer
564
+ // queue full, blob write backpressure). We refcount the reasons so that resuming one does not race
565
+ // ahead of another that still wants the WS paused. Declared before the ping setup below because the
566
+ // immediate sendPing() reads it.
567
+ let pauseReasons = 0;
416
568
  const blobTimeout = env.get(CONFIG_PARAMS.REPLICATION_BLOBTIMEOUT) ?? 120000;
417
569
  const blobsInFlight = new Map();
418
570
  const outstandingBlobsToFinish: Promise<void>[] = [];
419
571
  let outstandingBlobsBeingSent = 0;
420
572
  let blobSentCallback: (v?: any) => void;
573
+ // Refresh the keep-alive liveness clock from observed socket byte movement. If the underlying
574
+ // _socket isn't observable (test mocks, pre-connect, or a change in the ws library internals),
575
+ // bytesRead/bytesWritten read as undefined; we can't measure activity, so treat the connection as
576
+ // live rather than let the keep-alive falsely terminate a healthy peer.
577
+ function noteByteActivity(): void {
578
+ const read = ws._socket?.bytesRead;
579
+ const written = ws._socket?.bytesWritten;
580
+ if (read === undefined || written === undefined || read !== bytesRead || written !== bytesWritten) {
581
+ lastByteActivity = performance.now();
582
+ }
583
+ }
421
584
  if (options.url) {
422
585
  const sendPing = () => {
423
- // if we have not received a message in the last ping interval, we should terminate the connection (but check to make sure we aren't just waiting for other data to flow)
424
- if (lastPingTime && bytesRead === ws._socket?.bytesRead && bytesWritten === ws._socket?.bytesWritten)
425
- ws.terminate(); // timeout
426
- else {
427
- lastPingTime = performance.now();
428
- ws.ping();
429
- bytesRead = ws._socket?.bytesRead;
430
- bytesWritten = ws._socket?.bytesWritten;
586
+ // Note any socket activity since the last interval (incoming pong/data or our send buffer
587
+ // draining as the peer consumes) either proves the peer is still alive.
588
+ noteByteActivity();
589
+ if (shouldTerminateIdlePing(performance.now() - lastByteActivity, PING_TIMEOUT, pauseReasons)) {
590
+ ws.terminate(); // no socket activity within the timeout — peer is gone
591
+ return;
431
592
  }
593
+ // While paused for receiver backpressure, keep our own liveness fresh: the stall is local and
594
+ // self-clearing (it doesn't depend on the peer), so we must not time the peer out for it.
595
+ if (pauseReasons > 0) lastByteActivity = performance.now();
596
+ // Always send the keep-alive ping. ws.pause() only stops reads, not writes, and the accepted
597
+ // peer relies on our pings to keep its own receive timer alive even when it has no data to send
598
+ // us. Record byte counts AFTER the ping so the ping's own bytes aren't later mistaken for peer
599
+ // activity.
600
+ lastPingTime = performance.now();
601
+ ws.ping();
602
+ bytesRead = ws._socket?.bytesRead;
603
+ bytesWritten = ws._socket?.bytesWritten;
432
604
  };
433
605
  sendPingInterval = setInterval(sendPing, PING_INTERVAL).unref();
434
606
  sendPing(); // send the first ping immediately so we can measure latency
435
- } else {
436
- resetPingTimer();
437
607
  }
608
+ // Both client and server arm the receive watchdog. On the client this is independent of the
609
+ // sendPing tick above: if that tick is missed or its ws.terminate() does not propagate a
610
+ // 'close' event, the watchdog forces the reconnect path. See harper-pro#233 for the failure
611
+ // modes observed in the field.
612
+ receiveWatchdog = createReceiveWatchdog({
613
+ intervalMs: RECEIVE_SILENCE_THRESHOLD_MS,
614
+ getBytesRead: () => ws._socket?.bytesRead ?? 0,
615
+ onSilence: () => {
616
+ // Warn-level: if the active sendPing was healthy this watchdog should not have fired,
617
+ // so it is a signal that something is wrong upstream (event-loop stall, keepalive timer
618
+ // misbehaving, peer accepting bytes but not progressing the protocol). Surface it so
619
+ // operators have something to grep for.
620
+ const dbContext = databaseName ? ` (db: "${databaseName}")` : '';
621
+ const direction = options.url ? 'no activity from' : 'no ping from';
622
+ logger.warn?.(
623
+ `Receive watchdog: ${direction} ${remoteNodeName}${dbContext} for ${RECEIVE_SILENCE_THRESHOLD_MS}ms — terminating connection and reconnecting`
624
+ );
625
+ ws.terminate();
626
+ },
627
+ });
628
+ const resetPingTimer = receiveWatchdog.reset;
629
+ resetPingTimer();
438
630
  ws._socket?.setMaxListeners(200); // we should allow a lot of drain listeners for concurrent blob streams
439
- function resetPingTimer() {
440
- clearTimeout(receivePingTimer);
441
- bytesRead = ws._socket?.bytesRead;
442
- bytesWritten = ws._socket?.bytesWritten;
443
- receivePingTimer = setTimeout(() => {
444
- // double check to make sure we aren't just waiting for other data to flow
445
- if (bytesRead === ws._socket?.bytesRead && bytesWritten === ws._socket?.bytesWritten) {
446
- logger.warn?.(`Timeout waiting for ping from ${remoteNodeName}, terminating connection and reconnecting`);
447
- ws.terminate();
448
- }
449
- }, PING_INTERVAL * 2).unref();
450
- }
451
631
  let ratioOfBackPressureTime = 0;
452
632
  let lastBackPressureCheck = 0;
453
633
  let isPausedForBackPressure = false;
@@ -489,23 +669,32 @@ export function replicateOverWS(ws: WebSocket, options: any, authorization: Prom
489
669
  const MAX_OUTSTANDING_BLOBS_BEING_SENT = env.get(CONFIG_PARAMS.REPLICATION_BLOBCONCURRENCY) ?? 5;
490
670
  let outstandingCommits = 0;
491
671
  let lastStructureLength = 0;
492
- // Multiple independent conditions can ask to pause receive on this WS (commit backlog,
493
- // consumer queue full, blob write backpressure). We refcount the reasons so that resuming
494
- // one does not race ahead of another that still wants the WS paused.
495
- let pauseReasons = 0;
496
672
  let commitBacklogPaused = false;
497
673
  function addPauseReason(): void {
498
- if (pauseReasons === 0) ws.pause();
674
+ if (pauseReasons === 0) {
675
+ ws.pause();
676
+ // Suspend the receive watchdog while the socket is intentionally paused — `bytesRead`
677
+ // is frozen by `ws.pause()` so the byte check cannot tell legitimate backpressure
678
+ // from peer silence, and firing here would terminate a healthy mid-ingest connection.
679
+ receiveWatchdog?.stop();
680
+ }
499
681
  pauseReasons++;
500
682
  }
501
683
  function removePauseReason(): void {
502
684
  if (pauseReasons === 0) return;
503
685
  pauseReasons--;
504
- if (pauseReasons === 0) ws.resume();
686
+ if (pauseReasons === 0) {
687
+ ws.resume();
688
+ // Restart the silence window from the resume point — we deliberately do not penalize
689
+ // the connection for the time it spent paused.
690
+ receiveWatchdog?.reset();
691
+ }
505
692
  }
506
693
  let subscriptionRequest, auditSubscription;
507
694
  let nodeSubscriptions;
508
695
  let excludedNodes: string[]; // list of nodes to exclude from this subscription
696
+ // undefined = not yet computed; null = computed, no exclusions; Set = tables to drop on receive
697
+ let receiveExcludedTables: Set<string> | null | undefined;
509
698
  let remoteShortIdToLocalId: Map<number, number>;
510
699
  let subscribedNodeIds: Array<boolean | { startTime: number; endTime?: number }> | undefined; // map of node IDs to their subscription time ranges
511
700
  // Serialize message handling so that async backpressure inside onWSMessage doesn't allow
@@ -515,6 +704,9 @@ export function replicateOverWS(ws: WebSocket, options: any, authorization: Prom
515
704
  let messageProcessing: Promise<void> = Promise.resolve();
516
705
  let wsClosed = false;
517
706
  ws.on('message', (body: Buffer) => {
707
+ // Reset the receive watchdog synchronously on every frame — async processing below may
708
+ // take a long time and we want a single late frame to count as proof of life immediately.
709
+ resetPingTimer();
518
710
  messageProcessing = messageProcessing.then(
519
711
  () => (wsClosed ? undefined : onWSMessage(body)),
520
712
  () => (wsClosed ? undefined : onWSMessage(body))
@@ -554,7 +746,7 @@ export function replicateOverWS(ws: WebSocket, options: any, authorization: Prom
554
746
  // of an array that begins with the command code
555
747
  lastMessageTime = performance.now();
556
748
  try {
557
- const decoder = (body.dataView = new Decoder(body.buffer, body.byteOffset, body.byteLength));
749
+ const decoder = ((body as any).dataView = new Decoder(body.buffer, body.byteOffset, body.byteLength));
558
750
  if (body[0] > 127) {
559
751
  // not a transaction, special message
560
752
  const message = decode(body);
@@ -720,13 +912,12 @@ export function replicateOverWS(ws: WebSocket, options: any, authorization: Prom
720
912
  // of the table id to the decoder so we can decode the binary data for each table.
721
913
  tableDecoders[tableId] = {
722
914
  name: tableName,
723
- decoder: new Packr({
915
+ decoder: new StructonPackr({
724
916
  useBigIntExtension: true,
725
- randomAccessStructure: true,
726
917
  freezeData: true,
727
918
  typedStructs: data.typedStructs,
728
919
  structures: data.structures,
729
- }),
920
+ } as any),
730
921
  getEntry(id) {
731
922
  return table.primaryStore.getEntry(id);
732
923
  },
@@ -765,7 +956,21 @@ export function replicateOverWS(ws: WebSocket, options: any, authorization: Prom
765
956
  'replication',
766
957
  'committed-update'
767
958
  );
768
- getSharedStatus().buffer.notify();
959
+ (getSharedStatus().buffer as any).notify();
960
+ break;
961
+ case COPY_START:
962
+ // the leader is (re)starting a bulk copy; track a resume cursor for it
963
+ inCopyMode = true;
964
+ copyModeStartTime = data; // copyStartTime anchor chosen by the leader
965
+ copyFromNodeId = getIdOfRemoteNode(remoteNodeName, auditStore);
966
+ logger.debug?.(connectionId, 'bulk copy starting from', remoteNodeName, new Date(copyModeStartTime));
967
+ break;
968
+ case COPY_COMPLETE:
969
+ // Copy signalled complete. Stay in copy mode so batches still committing keep advancing the
970
+ // cursor; maybeFinishCopy exits copy mode and clears the cursor once those commits drain.
971
+ copyCompleteReceived = true;
972
+ maybeFinishCopy();
973
+ logger.debug?.(connectionId, 'bulk copy complete from', remoteNodeName);
769
974
  break;
770
975
  case SEQUENCE_ID_UPDATE:
771
976
  // we need to record the sequence number that the remote node has received
@@ -833,20 +1038,36 @@ export function replicateOverWS(ws: WebSocket, options: any, authorization: Prom
833
1038
  );
834
1039
  } else stream.end(blobBody);
835
1040
  if (stream.connectedToBlob) blobsInFlight.delete(fileId);
1041
+ } else if (stream.destroyed || stream.writableEnded) {
1042
+ // The stream was already torn down before this mid-blob chunk arrived —
1043
+ // typically because saveBlob's pipeline failed (e.g. ENOENT on
1044
+ // createWriteStream) and destroyed the PassThrough source, firing 'close'.
1045
+ // We must NOT fall into the backpressure branch below: writing to a dead
1046
+ // stream returns false, and pausing to wait for a 'drain'/'close' that has
1047
+ // already fired strands the pause reason forever, wedging the entire
1048
+ // receive loop (observed in prod: receiver goes silent, sender stuck
1049
+ // reconnecting, replication never recovers). Drop the orphaned chunk and
1050
+ // forget the stream instead.
1051
+ blobsInFlight.delete(fileId);
836
1052
  } else if (!stream.write(blobBody)) {
837
1053
  // The PassThrough's internal queue is over its HWM, meaning the downstream
838
1054
  // file write (via pipeline in saveBlob) can't keep up. Pause the WS until the
839
1055
  // stream drains so blob chunks don't accumulate in memory faster than they
840
- // can be flushed to disk. Also listen for 'close' so a destroyed stream
841
- // (e.g. saveBlob error) doesn't strand the pause reason.
842
- addPauseReason();
843
- const release = () => {
844
- stream.off('drain', release);
845
- stream.off('close', release);
846
- removePauseReason();
847
- };
848
- stream.on('drain', release);
849
- stream.on('close', release);
1056
+ // can be flushed to disk.
1057
+ if (stream.destroyed || stream.writableEnded) {
1058
+ // write() itself may have torn the stream down (e.g. a late error). If so,
1059
+ // 'drain'/'close' won't arrive skip pausing rather than strand the reason.
1060
+ blobsInFlight.delete(fileId);
1061
+ } else {
1062
+ addPauseReason();
1063
+ const release = () => {
1064
+ stream.off('drain', release);
1065
+ stream.off('close', release);
1066
+ removePauseReason();
1067
+ };
1068
+ stream.on('drain', release);
1069
+ stream.on('close', release);
1070
+ }
850
1071
  }
851
1072
  } catch (error) {
852
1073
  logger.error?.(
@@ -1097,6 +1318,11 @@ export function replicateOverWS(ws: WebSocket, options: any, authorization: Prom
1097
1318
  let tableById;
1098
1319
  let currentSequenceId = Infinity; // the last sequence number in the audit log that we have processed, set this with a finite number from the subscriptions
1099
1320
  let sentSequenceId; // the last sequence number we have sent
1321
+ // Tables excluded from outgoing replication to this peer+database (from sendsTo config)
1322
+ const sendExcludedTables =
1323
+ authorization?.replicates && typeof authorization.replicates === 'object'
1324
+ ? getExcludedTablesForRouteEntries(authorization.replicates.sendsTo, remoteNodeName, databaseName)
1325
+ : null;
1100
1326
  const sendAuditRecord = (auditRecord, localTime) => {
1101
1327
  if (auditRecord.type === 'end_txn') {
1102
1328
  if (currentTransaction.txnTime) {
@@ -1127,6 +1353,9 @@ export function replicateOverWS(ws: WebSocket, options: any, authorization: Prom
1127
1353
  }
1128
1354
  }
1129
1355
  const table = tableEntry.table;
1356
+ if (sendExcludedTables?.has(table.tableName)) {
1357
+ return skipAuditRecord();
1358
+ }
1130
1359
  const primaryStore = table.primaryStore;
1131
1360
  const encoder = primaryStore.encoder;
1132
1361
  // Force a reload the first time this connection touches each table:
@@ -1154,7 +1383,9 @@ export function replicateOverWS(ws: WebSocket, options: any, authorization: Prom
1154
1383
  const matchesSubscription =
1155
1384
  (excludedNodes && timeRange === undefined) ||
1156
1385
  // if it is in the list, we check the timestamps to verify it matches
1157
- (timeRange && timeRange.startTime < localTime && (!timeRange.endTime || timeRange.endTime > localTime));
1386
+ (timeRange &&
1387
+ (timeRange as any).startTime < localTime &&
1388
+ (!(timeRange as any).endTime || (timeRange as any).endTime > localTime));
1158
1389
  if (!matchesSubscription) {
1159
1390
  if (DEBUG_MODE)
1160
1391
  logger.trace?.(
@@ -1386,8 +1617,11 @@ export function replicateOverWS(ws: WebSocket, options: any, authorization: Prom
1386
1617
  subscriptionToHdbNodes?.end();
1387
1618
  });
1388
1619
  // find the earliest start time of the subscriptions
1389
- for (const { startTime } of nodeSubscriptions) {
1390
- if (startTime < currentSequenceId) currentSequenceId = startTime;
1620
+ let copyResume: { copyStartTime: number; currentTable: string; afterKey: any } | undefined;
1621
+ for (const subscription of nodeSubscriptions) {
1622
+ if (subscription.startTime < currentSequenceId) currentSequenceId = subscription.startTime;
1623
+ // a follower resuming an interrupted bulk copy sends back where it left off
1624
+ if (subscription.copyResume) copyResume = subscription.copyResume;
1391
1625
  }
1392
1626
 
1393
1627
  // wait for internal subscription, might be waiting for a table to be registered
@@ -1457,16 +1691,51 @@ export function replicateOverWS(ws: WebSocket, options: any, authorization: Prom
1457
1691
  isFirst = false;
1458
1692
  if (currentSequenceId === 0) {
1459
1693
  logger.info?.('Replicating all tables to', remoteNodeName);
1460
- let lastSequenceId = Date.now(); // we need at least a default time point in case there are no records
1694
+ // Capture the resume point BEFORE iterating. The bulk copy walks the primary store in
1695
+ // key order (snapshot: false), but the follower resumes replication from the audit log in
1696
+ // time order. Using copyStartTime — not max(localTime) of the copied records — guarantees
1697
+ // the post-copy audit replay re-delivers every write committed during the copy, including
1698
+ // ones to keys we already passed; resuming from max(localTime) would skip those (data loss).
1699
+ // When the follower is resuming an interrupted copy, keep the original copy start time so
1700
+ // the post-copy resume point stays anchored to when the copy first began (see safety note).
1701
+ const copyStartTime = copyResume?.copyStartTime ?? Date.now();
1461
1702
  const nodeId = getThisNodeId(auditStore);
1703
+ // Tell the follower a bulk copy is starting and its anchor time, so it tracks a resume cursor.
1704
+ ws.send(encode([COPY_START, copyStartTime]));
1705
+ let recordsSinceCheckpoint = 0;
1706
+ // If resuming, the follower already committed every table before currentTable (records commit
1707
+ // in stable iteration order), so skip to currentTable and continue after its last committed key.
1708
+ let reachedResumeTable = !copyResume;
1709
+ // currentTable must be one the loop below will actually visit (present in `tables` AND passing
1710
+ // the same replication filter); otherwise the skip loop never reaches it and would omit every
1711
+ // later table. Mirror the loop's own check so a dropped/unreplicated cursor table forces a restart.
1712
+ if (copyResume && !tableToTableEntry(tables[copyResume.currentTable])) {
1713
+ // cursor table is gone or no longer replicated — the skip loop would never reach it and
1714
+ // would omit every later table, so recopy from scratch (idempotent puts, copyStartTime reset)
1715
+ logger.warn?.(
1716
+ 'Copy-resume table missing or unreplicated, restarting full copy',
1717
+ copyResume.currentTable
1718
+ );
1719
+ copyResume = undefined;
1720
+ reachedResumeTable = true;
1721
+ }
1722
+ const resumeCurrentTable = copyResume?.currentTable;
1723
+ const resumeAfterKey = copyResume?.afterKey;
1462
1724
  for (const tableName in tables) {
1463
- if (!tableToTableEntry(tableName)) continue; // if we aren't replicating this table, skip it
1464
1725
  const table = tables[tableName];
1465
- for (const entry of table.primaryStore.getRange({
1466
- snapshot: false,
1467
- versions: true,
1468
- // values: false, // TODO: eventually, we don't want to decode, we want to use fast binary transfer
1469
- })) {
1726
+ if (!tableToTableEntry(table)) continue; // if we aren't replicating this table, skip it
1727
+ if (!reachedResumeTable) {
1728
+ if (tableName !== resumeCurrentTable) continue; // already committed on the follower
1729
+ reachedResumeTable = true;
1730
+ }
1731
+ const rangeOptions: any = { snapshot: false, versions: true };
1732
+ // values: false, // TODO: eventually, we don't want to decode, we want to use fast binary transfer
1733
+ if (tableName === resumeCurrentTable) {
1734
+ // resume this table after the last key the follower committed
1735
+ rangeOptions.start = resumeAfterKey;
1736
+ rangeOptions.exclusiveStart = true;
1737
+ }
1738
+ for (const entry of table.primaryStore.getRange(rangeOptions)) {
1470
1739
  if (closed) return;
1471
1740
  logger.trace?.(
1472
1741
  connectionId,
@@ -1476,7 +1745,6 @@ export function replicateOverWS(ws: WebSocket, options: any, authorization: Prom
1476
1745
  entry.key,
1477
1746
  entry.localTime
1478
1747
  );
1479
- lastSequenceId = Math.max(entry.localTime ?? 1, lastSequenceId);
1480
1748
  getSharedStatus()[SENDING_TIME_POSITION] = 1;
1481
1749
  const encoded = createAuditEntry({
1482
1750
  version: entry.version,
@@ -1496,7 +1764,7 @@ export function replicateOverWS(ws: WebSocket, options: any, authorization: Prom
1496
1764
  residencyId: entry.residencyId,
1497
1765
  previousResidencyId: null,
1498
1766
  expiresAt: entry.expiresAt,
1499
- });
1767
+ } as any);
1500
1768
  await sendAuditRecord(
1501
1769
  {
1502
1770
  // make it look like an audit record
@@ -1521,26 +1789,46 @@ export function replicateOverWS(ws: WebSocket, options: any, authorization: Prom
1521
1789
  encoded.length,
1522
1790
  encoded.slice(0, 10)
1523
1791
  );
1792
+ // Periodically flush the accumulated records as a message so the follower commits this
1793
+ // batch and advances its resume cursor. This is a plain flush with NO sequence update:
1794
+ // emitting an end_txn here would advance the follower's received-version watermark to
1795
+ // copyStartTime mid-copy, which monitorSync could read as "caught up" and mark the clone
1796
+ // Available/cloned with rows still uncopied. (Records with differing versions already flush
1797
+ // naturally above; this also bounds same-version bulk data into committable batches.) The
1798
+ // watermark is only advanced to copyStartTime by the single end_txn after the whole copy.
1799
+ if (++recordsSinceCheckpoint >= COPY_CHECKPOINT_RECORDS && position - encodingStart > 8) {
1800
+ recordsSinceCheckpoint = 0;
1801
+ sendQueuedData();
1802
+ encodingStart = position;
1803
+ currentTransaction.txnTime = 0;
1804
+ }
1524
1805
  }
1525
1806
  logger.info?.('Finished copy table', tableName, remoteNodeName);
1526
1807
  }
1527
- currentSequenceId = lastSequenceId;
1808
+ currentSequenceId = copyStartTime;
1528
1809
  if (!currentTransaction.txnTime) {
1529
- // if we haven't sent any records, force a txn start and end so the subscribers records a timestamp
1530
- currentTransaction.txnTime = lastSequenceId;
1531
- writeFloat64(lastSequenceId);
1532
- }
1533
- if (position - encodingStart > 8) {
1534
- // if we have any queued transactions to send, send them now
1535
- sendAuditRecord(
1536
- {
1537
- type: 'end_txn',
1538
- },
1539
- currentSequenceId
1540
- );
1810
+ // no records pending (none sent, or the last batch landed on a checkpoint flush):
1811
+ // force a txn so the end_txn below still carries the sequence update
1812
+ currentTransaction.txnTime = copyStartTime;
1813
+ encodingStart = position;
1814
+ writeFloat64(copyStartTime);
1541
1815
  }
1816
+ // ALWAYS emit the final end_txn at copyStartTime. It carries the REMOTE_SEQUENCE_UPDATE
1817
+ // that advances the follower's seqId and received-version watermark to copyStartTime —
1818
+ // the sole signal that the copy is synced (per-record watermark advance is suppressed
1819
+ // during the copy). Skipping it when the last rows landed exactly on a checkpoint flush
1820
+ // would leave the clone unable to ever reach Available.
1821
+ sendAuditRecord(
1822
+ {
1823
+ type: 'end_txn',
1824
+ },
1825
+ currentSequenceId
1826
+ );
1827
+ // The full copy is done — tell the follower to clear its resume cursor and fall back to
1828
+ // normal audit-log replication from the persisted seqId (which is copyStartTime).
1829
+ ws.send(encode([COPY_COMPLETE]));
1542
1830
  getSharedStatus()[SENDING_TIME_POSITION] = 0;
1543
- currentSequenceId = lastSequenceId;
1831
+ currentSequenceId = copyStartTime;
1544
1832
  }
1545
1833
  }
1546
1834
  const logName = subscribedNodeName === getThisNodeName() ? 'local' : subscribedNodeName;
@@ -1599,6 +1887,7 @@ export function replicateOverWS(ws: WebSocket, options: any, authorization: Prom
1599
1887
  let beginTxn = true;
1600
1888
  let event; // could also get txnTime from decoder.getFloat64(0);
1601
1889
  let sequenceIdReceived;
1890
+ let lastYieldTime = performance.now();
1602
1891
  do {
1603
1892
  getSharedStatus();
1604
1893
  const eventLength = decoder.readInt();
@@ -1627,6 +1916,30 @@ export function replicateOverWS(ws: WebSocket, options: any, authorization: Prom
1627
1916
  if (!tableDecoder) {
1628
1917
  logger.error?.(`No table found with an id of ${auditRecord.tableId}`);
1629
1918
  }
1919
+ // Lazily compute receive-side exclusions once remoteNodeName is known.
1920
+ // Prefer routeReplicates from the subscriber-side connection; fall back to
1921
+ // authorization.replicates when this is the server-side handler.
1922
+ if (receiveExcludedTables === undefined) {
1923
+ const firstNode = options.connection?.nodeSubscriptions?.[0];
1924
+ const receivesFromEntries =
1925
+ firstNode?.routeReplicates?.receivesFrom ??
1926
+ (authorization?.replicates && typeof authorization.replicates === 'object'
1927
+ ? authorization.replicates.receivesFrom
1928
+ : undefined);
1929
+ receiveExcludedTables =
1930
+ getExcludedTablesForRouteEntries(receivesFromEntries, remoteNodeName, databaseName) ?? null;
1931
+ }
1932
+ if (tableDecoder && receiveExcludedTables?.has(tableDecoder.name)) {
1933
+ logger.trace?.(
1934
+ connectionId,
1935
+ 'dropping incoming replication for excluded table',
1936
+ databaseName + '.' + tableDecoder.name,
1937
+ 'from',
1938
+ remoteNodeName
1939
+ );
1940
+ decoder.position = start + eventLength;
1941
+ continue;
1942
+ }
1630
1943
  let residencyList;
1631
1944
  if (auditRecord.residencyId) {
1632
1945
  residencyList = receivedResidencyLists[auditRecord.residencyId];
@@ -1640,6 +1953,7 @@ export function replicateOverWS(ws: WebSocket, options: any, authorization: Prom
1640
1953
  }
1641
1954
  const id = auditRecord.recordId;
1642
1955
  event = undefined; // reset before each decode attempt
1956
+ let receivedBlobs: any[] | undefined;
1643
1957
  try {
1644
1958
  decodeBlobsWithWrites(
1645
1959
  () => {
@@ -1658,7 +1972,11 @@ export function replicateOverWS(ws: WebSocket, options: any, authorization: Prom
1658
1972
  };
1659
1973
  },
1660
1974
  auditStore?.rootStore,
1661
- (blob) => receiveBlobs(blob, id)
1975
+ (blob) => {
1976
+ const localBlob = receiveBlobs(blob, id);
1977
+ (receivedBlobs ??= []).push(localBlob);
1978
+ return localBlob;
1979
+ }
1662
1980
  );
1663
1981
  } catch (error) {
1664
1982
  logger.error?.(
@@ -1671,11 +1989,23 @@ export function replicateOverWS(ws: WebSocket, options: any, authorization: Prom
1671
1989
  error
1672
1990
  );
1673
1991
  }
1674
- replicationSharedStatus[RECEIVED_VERSION_POSITION] = Math.max(
1675
- // ensure monotonicity
1676
- auditRecord.version,
1677
- replicationSharedStatus[RECEIVED_VERSION_POSITION]
1678
- );
1992
+ if (!event && receivedBlobs) {
1993
+ // decode failed mid-message; the blobs that were already accepted will never be referenced. Give in-flight reads
1994
+ // a window to complete, then unlink the files. (mirrors the pattern at the relocate path above.)
1995
+ setTimeout(() => receivedBlobs.forEach(deleteBlob), 60000).unref();
1996
+ }
1997
+ // During a bulk copy, do NOT advance the received-version watermark per copied record:
1998
+ // records arrive in primary-key order carrying their original (possibly newest) versions, so a
1999
+ // single record at the leader's latest timestamp would otherwise let checkSyncStatus mark the
2000
+ // clone Available with rows still uncopied. The watermark is advanced to copyStartTime by the
2001
+ // single end_txn after the whole copy (the REMOTE_SEQUENCE_UPDATE branch above).
2002
+ if (!inCopyMode) {
2003
+ replicationSharedStatus[RECEIVED_VERSION_POSITION] = Math.max(
2004
+ // ensure monotonicity
2005
+ auditRecord.version,
2006
+ replicationSharedStatus[RECEIVED_VERSION_POSITION]
2007
+ );
2008
+ }
1679
2009
  replicationSharedStatus[RECEIVED_TIME_POSITION] = Date.now();
1680
2010
  replicationSharedStatus[RECEIVING_STATUS_POSITION] = RECEIVING_STATUS_RECEIVING;
1681
2011
 
@@ -1706,6 +2036,13 @@ export function replicateOverWS(ws: WebSocket, options: any, authorization: Prom
1706
2036
  } finally {
1707
2037
  removePauseReason();
1708
2038
  }
2039
+ lastYieldTime = performance.now();
2040
+ } else if (performance.now() - lastYieldTime >= RECEIVE_YIELD_INTERVAL) {
2041
+ // The high-water-mark pause only fires under heap pressure. When the consumer keeps
2042
+ // up, yield on a time budget anyway so a large message doesn't decode in one
2043
+ // synchronous turn and stall ping responses (see RECEIVE_YIELD_INTERVAL).
2044
+ await new Promise(setImmediate);
2045
+ lastYieldTime = performance.now();
1709
2046
  }
1710
2047
  }
1711
2048
  decoder.position = start + eventLength;
@@ -1727,6 +2064,9 @@ export function replicateOverWS(ws: WebSocket, options: any, authorization: Prom
1727
2064
  `Commit backlog causing replication back-pressure, requesting that ${remoteNodeName} pause replication`
1728
2065
  );
1729
2066
  }
2067
+ // Is this a bulk-copy frame? Only frames received before COPY_COMPLETE are part of the
2068
+ // primary-key copy; later audit-replay frames must not be recorded as the resume cursor.
2069
+ const isCopyFrame = inCopyMode && !copyCompleteReceived;
1730
2070
  tableSubscriptionToReplicator.send({
1731
2071
  type: 'end_txn',
1732
2072
  localTime: lastSequenceIdReceived,
@@ -1754,6 +2094,18 @@ export function replicateOverWS(ws: WebSocket, options: any, authorization: Prom
1754
2094
  // we correctly resend the blobs)
1755
2095
  if (outstandingBlobsToFinish.length > 0) await Promise.all(outstandingBlobsToFinish);
1756
2096
  logger.trace?.('All blobs finished');
2097
+ // Persist/clear the resume cursor only AFTER this batch's blobs are durable too. The cursor
2098
+ // means "fully copied through this key"; advancing it before blob writes finish would let a
2099
+ // crash skip re-requesting an unfinished blob, leaving the record pointing at missing data.
2100
+ if (isCopyFrame && event && copyFromNodeId !== undefined) {
2101
+ tableSubscriptionToReplicator?.dbisDB?.put([Symbol.for('copyCursor'), copyFromNodeId], {
2102
+ copyStartTime: copyModeStartTime,
2103
+ currentTable: event.table,
2104
+ afterKey: event.id,
2105
+ });
2106
+ }
2107
+ // once the last copied batch (incl. its blobs) is durable, it's safe to drop the cursor
2108
+ maybeFinishCopy();
1757
2109
  if (!lastSequenceIdCommitted && sequenceIdReceived) {
1758
2110
  logger.trace?.(connectionId, 'queuing confirmation of a commit at', sequenceIdReceived);
1759
2111
  setTimeout(() => {
@@ -1773,6 +2125,7 @@ export function replicateOverWS(ws: WebSocket, options: any, authorization: Prom
1773
2125
  }
1774
2126
  }
1775
2127
  ws.on('ping', resetPingTimer);
2128
+ ws.on('pong', resetPingTimer);
1776
2129
  ws.on('pong', () => {
1777
2130
  if (options.connection) {
1778
2131
  // every pong we can use to update our connection information (and latency)
@@ -1797,7 +2150,7 @@ export function replicateOverWS(ws: WebSocket, options: any, authorization: Prom
1797
2150
  // cleanup
1798
2151
  wsClosed = true;
1799
2152
  clearInterval(sendPingInterval);
1800
- clearTimeout(receivePingTimer);
2153
+ receiveWatchdog?.stop();
1801
2154
  clearInterval(blobsTimer);
1802
2155
  if (auditSubscription) auditSubscription.emit('close');
1803
2156
  if (subscriptionRequest) subscriptionRequest.end();
@@ -1855,7 +2208,7 @@ export function replicateOverWS(ws: WebSocket, options: any, authorization: Prom
1855
2208
  ])
1856
2209
  );
1857
2210
  }
1858
- lastBuffer = buffer;
2211
+ lastBuffer = buffer as Buffer;
1859
2212
  if (ws._socket.writableNeedDrain) {
1860
2213
  logger.debug?.('draining', id);
1861
2214
  await new Promise((resolve) => ws._socket.once('drain', resolve));
@@ -1911,7 +2264,7 @@ export function replicateOverWS(ws: WebSocket, options: any, authorization: Prom
1911
2264
  stream.connectedToBlob = true;
1912
2265
  stream.lastChunk = Date.now();
1913
2266
  stream.recordId = id;
1914
- if (remoteBlob.size === undefined && stream.expectedSize) remoteBlob.size = stream.expectedSize;
2267
+ if (remoteBlob.size === undefined && stream.expectedSize) (remoteBlob as any).size = stream.expectedSize;
1915
2268
  const localBlob = stream.blob ?? createBlob(stream, remoteBlob);
1916
2269
  stream.blob = localBlob; // record the blob so we can reuse it if another request uses the same blob
1917
2270
 
@@ -1935,7 +2288,7 @@ export function replicateOverWS(ws: WebSocket, options: any, authorization: Prom
1935
2288
  const index = outstandingBlobsToFinish.indexOf(tracked);
1936
2289
  if (index > -1) outstandingBlobsToFinish.splice(index, 1);
1937
2290
  });
1938
- tracked.blobId = blobId;
2291
+ (tracked as any).blobId = blobId;
1939
2292
  outstandingBlobsToFinish.push(tracked);
1940
2293
  }
1941
2294
  return localBlob;
@@ -2027,14 +2380,20 @@ export function replicateOverWS(ws: WebSocket, options: any, authorization: Prom
2027
2380
  const nodeSubscriptions = options.connection?.nodeSubscriptions.map((node: any) => {
2028
2381
  const tableSubs = [];
2029
2382
  let { replicateByDefault } = node;
2383
+ // Tables excluded by this node's receivesFrom config for this peer+database
2384
+ const receiverExcludedTables = getExcludedTablesForRouteEntries(
2385
+ node.routeReplicates?.receivesFrom,
2386
+ node.name,
2387
+ databaseName
2388
+ );
2030
2389
  if (node.subscriptions) {
2031
2390
  // if the node has explicit subscriptions, we need to use that to determine subscriptions
2032
2391
  for (const subscription of node.subscriptions) {
2033
2392
  // if there is an explicit subscription listed
2034
2393
  if (subscription.subscribe && (subscription.schema || subscription.database) === databaseName) {
2035
2394
  const tableName = subscription.table;
2036
- if (tables?.[tableName]?.replicate !== false)
2037
- // if replication is enabled for this table
2395
+ if (tables?.[tableName]?.replicate !== false && !receiverExcludedTables?.has(tableName))
2396
+ // if replication is enabled for this table and not excluded
2038
2397
  tableSubs.push(tableName);
2039
2398
  }
2040
2399
  }
@@ -2042,15 +2401,26 @@ export function replicateOverWS(ws: WebSocket, options: any, authorization: Prom
2042
2401
  } else {
2043
2402
  // note that if replicateByDefault is enabled, we are listing the *excluded* tables
2044
2403
  for (const tableName in tables) {
2045
- if (replicateByDefault ? tables[tableName].replicate === false : tables[tableName].replicate) {
2404
+ if (
2405
+ replicateByDefault
2406
+ ? tables[tableName].replicate === false || receiverExcludedTables?.has(tableName)
2407
+ : tables[tableName].replicate && !receiverExcludedTables?.has(tableName)
2408
+ ) {
2046
2409
  tableSubs.push(tableName);
2047
2410
  }
2048
2411
  }
2049
2412
  }
2050
2413
 
2051
2414
  const nodeId = auditStore && getIdOfRemoteNode(node.name, auditStore);
2052
- auditStore.ensureLogExists?.(node.name);
2415
+ auditStore?.ensureLogExists?.(node.name);
2053
2416
  const sequenceEntry = tableSubscriptionToReplicator?.dbisDB?.get([Symbol.for('seq'), nodeId]) ?? 1;
2417
+ // A persisted copy cursor means a bulk copy from this node was interrupted mid-stream. We must
2418
+ // resume that copy (not treat the persisted seqId as a normal start point — the un-copied table
2419
+ // data predates copyStartTime and would never be delivered by an audit-log resume).
2420
+ const copyCursor =
2421
+ nodeId === undefined
2422
+ ? undefined
2423
+ : tableSubscriptionToReplicator?.dbisDB?.get([Symbol.for('copyCursor'), nodeId]);
2054
2424
  // if we are connected directly to the node, we start from the last sequence number we received at the top level
2055
2425
  let startTime = Math.max(
2056
2426
  sequenceEntry?.seqId ?? 1,
@@ -2097,6 +2467,18 @@ export function replicateOverWS(ws: WebSocket, options: any, authorization: Prom
2097
2467
  startTime = Date.now() - 60000;
2098
2468
  }
2099
2469
  }
2470
+ let copyResume;
2471
+ if (copyCursor) {
2472
+ startTime = 0; // request a copy; the cursor tells the leader where to resume
2473
+ copyResume = {
2474
+ copyStartTime: copyCursor.copyStartTime,
2475
+ currentTable: copyCursor.currentTable,
2476
+ afterKey: copyCursor.afterKey,
2477
+ };
2478
+ logger.warn?.(
2479
+ `Resuming interrupted copy of database ${databaseName} from ${getNodeURL(node)} at table ${copyCursor.currentTable}`
2480
+ );
2481
+ }
2100
2482
  logger.trace?.(connectionId, 'defining subscription request', node.name, databaseName, new Date(startTime));
2101
2483
  return {
2102
2484
  name: node.name,
@@ -2105,6 +2487,7 @@ export function replicateOverWS(ws: WebSocket, options: any, authorization: Prom
2105
2487
  startTime,
2106
2488
  isLeader: node.isLeader,
2107
2489
  endTime: node.endTime,
2490
+ copyResume, // present only when resuming an interrupted bulk copy
2108
2491
  };
2109
2492
  });
2110
2493
  let excluded: string[];
@@ -2203,7 +2586,7 @@ export function replicateOverWS(ws: WebSocket, options: any, authorization: Prom
2203
2586
  const thisNodeName = getThisNodeName();
2204
2587
  if (thisNodeName === remoteNodeName) {
2205
2588
  if (!thisNodeName) throw new Error('Node name not defined');
2206
- else throw new Error('Should not connect to self', thisNodeName);
2589
+ else throw new Error('Should not connect to self: ' + thisNodeName);
2207
2590
  }
2208
2591
  sendNodeDBName(thisNodeName, databaseName);
2209
2592
  return true;