@harperfast/harper 5.2.9 → 5.3.0-alpha.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (668) hide show
  1. package/agent/mcpTools.ts +1 -1
  2. package/agent/session.ts +25 -14
  3. package/bin/cliOperations.ts +46 -9
  4. package/bin/copyDb.ts +282 -59
  5. package/bin/deploySetup.ts +16 -5
  6. package/bin/harper.ts +1 -1
  7. package/bin/help.ts +4 -1
  8. package/bin/lite.ts +4 -1
  9. package/bin/restart.ts +59 -3
  10. package/bin/run.ts +6 -11
  11. package/bin/upgrade.js +7 -3
  12. package/bin/workloadIdentity.ts +119 -0
  13. package/components/Application.ts +2414 -242
  14. package/components/ApplicationScope.ts +8 -0
  15. package/components/EntryHandler.ts +59 -39
  16. package/components/OptionsWatcher.ts +150 -74
  17. package/components/RuntimeModuleTracker.ts +38 -7
  18. package/components/Scope.ts +37 -15
  19. package/components/awaitRestart.ts +84 -0
  20. package/components/componentLoader.ts +340 -30
  21. package/components/componentPreparationLock.ts +16 -5
  22. package/components/mcp/adapters/harperHttp.ts +4 -0
  23. package/components/mcp/listChanged.ts +4 -0
  24. package/components/mcp/toolRegistry.ts +2 -0
  25. package/components/mcp/tools/operations.ts +9 -0
  26. package/components/mcp/tools/schemas/operationDescriptions.ts +2 -2
  27. package/components/operations.js +270 -78
  28. package/components/operationsValidation.js +49 -1
  29. package/components/status/ComponentStatusRegistry.ts +59 -0
  30. package/config/RootConfigWatcher.ts +80 -34
  31. package/config/configUtils.ts +291 -34
  32. package/config/harperConfigEnvVars.ts +170 -27
  33. package/config-root.schema.json +29 -0
  34. package/dataLayer/blobBackup.ts +160 -50
  35. package/dataLayer/delete.ts +6 -1
  36. package/dataLayer/harperBridge/ResourceBridge.ts +52 -8
  37. package/dataLayer/harperBridge/lmdbBridge/lmdbMethods/DeleteAuditLogsBeforeResults.js +3 -1
  38. package/dataLayer/hdbInfoController.ts +34 -1
  39. package/dataLayer/insert.ts +44 -1
  40. package/dataLayer/rocksdbBackup.ts +53 -10
  41. package/dataLayer/schema.ts +11 -1
  42. package/dataLayer/schemaDescribe.ts +8 -1
  43. package/dist/agent/mcpTools.js +1 -1
  44. package/dist/agent/mcpTools.js.map +1 -1
  45. package/dist/agent/session.d.ts +22 -0
  46. package/dist/agent/session.js +26 -15
  47. package/dist/agent/session.js.map +1 -1
  48. package/dist/bin/cliOperations.js +48 -9
  49. package/dist/bin/cliOperations.js.map +1 -1
  50. package/dist/bin/copyDb.d.ts +12 -1
  51. package/dist/bin/copyDb.js +248 -60
  52. package/dist/bin/copyDb.js.map +1 -1
  53. package/dist/bin/deploySetup.d.ts +2 -0
  54. package/dist/bin/deploySetup.js +11 -3
  55. package/dist/bin/deploySetup.js.map +1 -1
  56. package/dist/bin/harper.js +1 -1
  57. package/dist/bin/harper.js.map +1 -1
  58. package/dist/bin/help.js +4 -1
  59. package/dist/bin/help.js.map +1 -1
  60. package/dist/bin/lite.js +4 -1
  61. package/dist/bin/lite.js.map +1 -1
  62. package/dist/bin/restart.js +54 -5
  63. package/dist/bin/restart.js.map +1 -1
  64. package/dist/bin/run.js +4 -10
  65. package/dist/bin/run.js.map +1 -1
  66. package/dist/bin/upgrade.js +4 -3
  67. package/dist/bin/upgrade.js.map +1 -1
  68. package/dist/bin/workloadIdentity.d.ts +18 -0
  69. package/dist/bin/workloadIdentity.js +100 -0
  70. package/dist/bin/workloadIdentity.js.map +1 -0
  71. package/dist/components/Application.d.ts +139 -16
  72. package/dist/components/Application.js +2215 -267
  73. package/dist/components/Application.js.map +1 -1
  74. package/dist/components/ApplicationScope.d.ts +8 -0
  75. package/dist/components/ApplicationScope.js +7 -0
  76. package/dist/components/ApplicationScope.js.map +1 -1
  77. package/dist/components/EntryHandler.js +26 -10
  78. package/dist/components/EntryHandler.js.map +1 -1
  79. package/dist/components/OptionsWatcher.d.ts +1 -0
  80. package/dist/components/OptionsWatcher.js +141 -74
  81. package/dist/components/OptionsWatcher.js.map +1 -1
  82. package/dist/components/RuntimeModuleTracker.js +40 -6
  83. package/dist/components/RuntimeModuleTracker.js.map +1 -1
  84. package/dist/components/Scope.js +38 -13
  85. package/dist/components/Scope.js.map +1 -1
  86. package/dist/components/awaitRestart.d.ts +33 -0
  87. package/dist/components/awaitRestart.js +61 -0
  88. package/dist/components/awaitRestart.js.map +1 -0
  89. package/dist/components/componentLoader.d.ts +38 -1
  90. package/dist/components/componentLoader.js +279 -22
  91. package/dist/components/componentLoader.js.map +1 -1
  92. package/dist/components/componentPreparationLock.d.ts +5 -0
  93. package/dist/components/componentPreparationLock.js +14 -6
  94. package/dist/components/componentPreparationLock.js.map +1 -1
  95. package/dist/components/mcp/adapters/harperHttp.js +4 -0
  96. package/dist/components/mcp/adapters/harperHttp.js.map +1 -1
  97. package/dist/components/mcp/listChanged.js +5 -0
  98. package/dist/components/mcp/listChanged.js.map +1 -1
  99. package/dist/components/mcp/toolRegistry.d.ts +1 -0
  100. package/dist/components/mcp/toolRegistry.js.map +1 -1
  101. package/dist/components/mcp/tools/operations.d.ts +5 -0
  102. package/dist/components/mcp/tools/operations.js +9 -0
  103. package/dist/components/mcp/tools/operations.js.map +1 -1
  104. package/dist/components/mcp/tools/schemas/operationDescriptions.js +2 -2
  105. package/dist/components/mcp/tools/schemas/operationDescriptions.js.map +1 -1
  106. package/dist/components/operations.js +231 -77
  107. package/dist/components/operations.js.map +1 -1
  108. package/dist/components/operationsValidation.js +49 -1
  109. package/dist/components/operationsValidation.js.map +1 -1
  110. package/dist/components/status/ComponentStatusRegistry.d.ts +0 -4
  111. package/dist/components/status/ComponentStatusRegistry.js +63 -0
  112. package/dist/components/status/ComponentStatusRegistry.js.map +1 -1
  113. package/dist/config/RootConfigWatcher.d.ts +7 -1
  114. package/dist/config/RootConfigWatcher.js +64 -27
  115. package/dist/config/RootConfigWatcher.js.map +1 -1
  116. package/dist/config/configUtils.d.ts +9 -1
  117. package/dist/config/configUtils.js +254 -33
  118. package/dist/config/configUtils.js.map +1 -1
  119. package/dist/config/harperConfigEnvVars.d.ts +16 -0
  120. package/dist/config/harperConfigEnvVars.js +162 -25
  121. package/dist/config/harperConfigEnvVars.js.map +1 -1
  122. package/dist/dataLayer/blobBackup.d.ts +49 -20
  123. package/dist/dataLayer/blobBackup.js +139 -50
  124. package/dist/dataLayer/blobBackup.js.map +1 -1
  125. package/dist/dataLayer/delete.js +1 -1
  126. package/dist/dataLayer/delete.js.map +1 -1
  127. package/dist/dataLayer/harperBridge/ResourceBridge.d.ts +14 -1
  128. package/dist/dataLayer/harperBridge/ResourceBridge.js +51 -10
  129. package/dist/dataLayer/harperBridge/ResourceBridge.js.map +1 -1
  130. package/dist/dataLayer/harperBridge/lmdbBridge/lmdbMethods/DeleteAuditLogsBeforeResults.d.ts +3 -1
  131. package/dist/dataLayer/harperBridge/lmdbBridge/lmdbMethods/DeleteAuditLogsBeforeResults.js +3 -1
  132. package/dist/dataLayer/harperBridge/lmdbBridge/lmdbMethods/DeleteAuditLogsBeforeResults.js.map +1 -1
  133. package/dist/dataLayer/hdbInfoController.d.ts +10 -0
  134. package/dist/dataLayer/hdbInfoController.js +30 -1
  135. package/dist/dataLayer/hdbInfoController.js.map +1 -1
  136. package/dist/dataLayer/insert.d.ts +9 -1
  137. package/dist/dataLayer/insert.js +30 -0
  138. package/dist/dataLayer/insert.js.map +1 -1
  139. package/dist/dataLayer/rocksdbBackup.d.ts +2 -2
  140. package/dist/dataLayer/rocksdbBackup.js +45 -8
  141. package/dist/dataLayer/rocksdbBackup.js.map +1 -1
  142. package/dist/dataLayer/schema.js +8 -0
  143. package/dist/dataLayer/schema.js.map +1 -1
  144. package/dist/dataLayer/schemaDescribe.js +8 -1
  145. package/dist/dataLayer/schemaDescribe.js.map +1 -1
  146. package/dist/index.d.ts +1 -1
  147. package/dist/index.js.map +1 -1
  148. package/dist/json/systemSchema.json +52 -0
  149. package/dist/resources/DatabaseTransaction.d.ts +55 -0
  150. package/dist/resources/DatabaseTransaction.js +459 -45
  151. package/dist/resources/DatabaseTransaction.js.map +1 -1
  152. package/dist/resources/LMDBTransaction.js +22 -4
  153. package/dist/resources/LMDBTransaction.js.map +1 -1
  154. package/dist/resources/PrimaryRocksDatabase.d.ts +1 -0
  155. package/dist/resources/PrimaryRocksDatabase.js +29 -0
  156. package/dist/resources/PrimaryRocksDatabase.js.map +1 -1
  157. package/dist/resources/RecordEncoder.d.ts +20 -0
  158. package/dist/resources/RecordEncoder.js +94 -9
  159. package/dist/resources/RecordEncoder.js.map +1 -1
  160. package/dist/resources/RequestTarget.d.ts +2 -0
  161. package/dist/resources/RequestTarget.js.map +1 -1
  162. package/dist/resources/Resource.js +20 -11
  163. package/dist/resources/Resource.js.map +1 -1
  164. package/dist/resources/ResourceInterface.d.ts +20 -1
  165. package/dist/resources/ResourceInterface.js.map +1 -1
  166. package/dist/resources/RocksIndexStore.d.ts +6 -1
  167. package/dist/resources/RocksIndexStore.js +24 -9
  168. package/dist/resources/RocksIndexStore.js.map +1 -1
  169. package/dist/resources/RocksTransactionLogStore.d.ts +14 -1
  170. package/dist/resources/RocksTransactionLogStore.js +57 -17
  171. package/dist/resources/RocksTransactionLogStore.js.map +1 -1
  172. package/dist/resources/Table.d.ts +112 -8
  173. package/dist/resources/Table.js +1073 -191
  174. package/dist/resources/Table.js.map +1 -1
  175. package/dist/resources/auditStore.d.ts +11 -2
  176. package/dist/resources/auditStore.js +325 -71
  177. package/dist/resources/auditStore.js.map +1 -1
  178. package/dist/resources/blob.d.ts +129 -9
  179. package/dist/resources/blob.js +992 -114
  180. package/dist/resources/blob.js.map +1 -1
  181. package/dist/resources/branchDatabase.d.ts +48 -0
  182. package/dist/resources/branchDatabase.js +892 -0
  183. package/dist/resources/branchDatabase.js.map +1 -0
  184. package/dist/resources/crdt.js +50 -12
  185. package/dist/resources/crdt.js.map +1 -1
  186. package/dist/resources/databases.d.ts +150 -0
  187. package/dist/resources/databases.js +1200 -239
  188. package/dist/resources/databases.js.map +1 -1
  189. package/dist/resources/defineTable.d.ts +10 -2
  190. package/dist/resources/defineTable.js +9 -1
  191. package/dist/resources/defineTable.js.map +1 -1
  192. package/dist/resources/graphql.d.ts +1 -1
  193. package/dist/resources/graphql.js +50 -15
  194. package/dist/resources/graphql.js.map +1 -1
  195. package/dist/resources/indexes/HierarchicalNavigableSmallWorld.d.ts +1 -0
  196. package/dist/resources/indexes/HierarchicalNavigableSmallWorld.js +6 -2
  197. package/dist/resources/indexes/HierarchicalNavigableSmallWorld.js.map +1 -1
  198. package/dist/resources/longLivedTransactions.d.ts +71 -0
  199. package/dist/resources/longLivedTransactions.js +358 -0
  200. package/dist/resources/longLivedTransactions.js.map +1 -0
  201. package/dist/resources/models/backendRegistry.d.ts +26 -0
  202. package/dist/resources/models/backendRegistry.js +60 -2
  203. package/dist/resources/models/backendRegistry.js.map +1 -1
  204. package/dist/resources/models/bootstrap.d.ts +33 -1
  205. package/dist/resources/models/bootstrap.js +416 -31
  206. package/dist/resources/models/bootstrap.js.map +1 -1
  207. package/dist/resources/recordLock.d.ts +80 -0
  208. package/dist/resources/recordLock.js +184 -0
  209. package/dist/resources/recordLock.js.map +1 -0
  210. package/dist/resources/replayLogs.d.ts +14 -1
  211. package/dist/resources/replayLogs.js +152 -24
  212. package/dist/resources/replayLogs.js.map +1 -1
  213. package/dist/resources/replayLogsGuards.d.ts +94 -7
  214. package/dist/resources/replayLogsGuards.js +111 -7
  215. package/dist/resources/replayLogsGuards.js.map +1 -1
  216. package/dist/resources/search.js +158 -6
  217. package/dist/resources/search.js.map +1 -1
  218. package/dist/resources/transactionBroadcast.d.ts +1 -1
  219. package/dist/resources/transactionBroadcast.js +2 -2
  220. package/dist/resources/transactionBroadcast.js.map +1 -1
  221. package/dist/security/auth.js +61 -30
  222. package/dist/security/auth.js.map +1 -1
  223. package/dist/security/authn/oidc/claims.d.ts +22 -0
  224. package/dist/security/authn/oidc/claims.js +71 -0
  225. package/dist/security/authn/oidc/claims.js.map +1 -0
  226. package/dist/security/authn/oidc/identityToken.d.ts +27 -0
  227. package/dist/security/authn/oidc/identityToken.js +111 -0
  228. package/dist/security/authn/oidc/identityToken.js.map +1 -0
  229. package/dist/security/authn/oidc/jwks.d.ts +25 -0
  230. package/dist/security/authn/oidc/jwks.js +261 -0
  231. package/dist/security/authn/oidc/jwks.js.map +1 -0
  232. package/dist/security/authn/oidc/providers/generic.d.ts +13 -0
  233. package/dist/security/authn/oidc/providers/generic.js +34 -0
  234. package/dist/security/authn/oidc/providers/generic.js.map +1 -0
  235. package/dist/security/authn/oidc/providers/githubActions.d.ts +11 -0
  236. package/dist/security/authn/oidc/providers/githubActions.js +129 -0
  237. package/dist/security/authn/oidc/providers/githubActions.js.map +1 -0
  238. package/dist/security/authn/oidc/providers/index.d.ts +37 -0
  239. package/dist/security/authn/oidc/providers/index.js +24 -0
  240. package/dist/security/authn/oidc/providers/index.js.map +1 -0
  241. package/dist/security/authn/oidc/tokenExchange.d.ts +12 -0
  242. package/dist/security/authn/oidc/tokenExchange.js +306 -0
  243. package/dist/security/authn/oidc/tokenExchange.js.map +1 -0
  244. package/dist/security/authn/oidc/trustPolicyOperations.d.ts +49 -0
  245. package/dist/security/authn/oidc/trustPolicyOperations.js +358 -0
  246. package/dist/security/authn/oidc/trustPolicyOperations.js.map +1 -0
  247. package/dist/security/authn/oidc/types.d.ts +38 -0
  248. package/dist/security/authn/oidc/types.js +6 -0
  249. package/dist/security/authn/oidc/types.js.map +1 -0
  250. package/dist/security/certificateVerification/index.js +40 -11
  251. package/dist/security/certificateVerification/index.js.map +1 -1
  252. package/dist/security/certificateVerification/trustedIssuers.d.ts +24 -0
  253. package/dist/security/certificateVerification/trustedIssuers.js +79 -0
  254. package/dist/security/certificateVerification/trustedIssuers.js.map +1 -0
  255. package/dist/security/certificateVerification/types.d.ts +1 -0
  256. package/dist/security/credentialProvenance.d.ts +35 -0
  257. package/dist/security/credentialProvenance.js +51 -0
  258. package/dist/security/credentialProvenance.js.map +1 -0
  259. package/dist/security/credentialRejection.d.ts +4 -0
  260. package/dist/security/credentialRejection.js +24 -0
  261. package/dist/security/credentialRejection.js.map +1 -0
  262. package/dist/security/deferredAuthentication.d.ts +36 -0
  263. package/dist/security/deferredAuthentication.js +70 -0
  264. package/dist/security/deferredAuthentication.js.map +1 -0
  265. package/dist/security/impersonation.d.ts +21 -0
  266. package/dist/security/impersonation.js +108 -9
  267. package/dist/security/impersonation.js.map +1 -1
  268. package/dist/security/jsLoader.d.ts +6 -0
  269. package/dist/security/jsLoader.js +75 -15
  270. package/dist/security/jsLoader.js.map +1 -1
  271. package/dist/security/keys.js +301 -71
  272. package/dist/security/keys.js.map +1 -1
  273. package/dist/security/operationScope.d.ts +21 -0
  274. package/dist/security/operationScope.js +36 -0
  275. package/dist/security/operationScope.js.map +1 -0
  276. package/dist/security/permissionsTranslator.js +21 -0
  277. package/dist/security/permissionsTranslator.js.map +1 -1
  278. package/dist/security/tokenAuthentication.d.ts +19 -1
  279. package/dist/security/tokenAuthentication.js +191 -10
  280. package/dist/security/tokenAuthentication.js.map +1 -1
  281. package/dist/security/user.js +4 -3
  282. package/dist/security/user.js.map +1 -1
  283. package/dist/server/DurableSubscriptionsSession.d.ts +2 -2
  284. package/dist/server/DurableSubscriptionsSession.js +67 -10
  285. package/dist/server/DurableSubscriptionsSession.js.map +1 -1
  286. package/dist/server/REST.js +73 -0
  287. package/dist/server/REST.js.map +1 -1
  288. package/dist/server/graphqlQuerying.js +4 -0
  289. package/dist/server/graphqlQuerying.js.map +1 -1
  290. package/dist/server/http.d.ts +11 -0
  291. package/dist/server/http.js +88 -15
  292. package/dist/server/http.js.map +1 -1
  293. package/dist/server/itc/serverHandlers.js +8 -1
  294. package/dist/server/itc/serverHandlers.js.map +1 -1
  295. package/dist/server/jobs/jobProcess.js +6 -2
  296. package/dist/server/jobs/jobProcess.js.map +1 -1
  297. package/dist/server/jobs/jobs.js +4 -1
  298. package/dist/server/jobs/jobs.js.map +1 -1
  299. package/dist/server/liveSubscriptionAuth.d.ts +26 -4
  300. package/dist/server/liveSubscriptionAuth.js +105 -39
  301. package/dist/server/liveSubscriptionAuth.js.map +1 -1
  302. package/dist/server/loadRootComponents.js +49 -10
  303. package/dist/server/loadRootComponents.js.map +1 -1
  304. package/dist/server/mqtt.d.ts +2 -0
  305. package/dist/server/mqtt.js +165 -30
  306. package/dist/server/mqtt.js.map +1 -1
  307. package/dist/server/nodeName.d.ts +2 -0
  308. package/dist/server/nodeName.js +107 -23
  309. package/dist/server/nodeName.js.map +1 -1
  310. package/dist/server/serverHelpers/Headers.d.ts +25 -0
  311. package/dist/server/serverHelpers/Headers.js +139 -1
  312. package/dist/server/serverHelpers/Headers.js.map +1 -1
  313. package/dist/server/serverHelpers/contentTypes.d.ts +9 -0
  314. package/dist/server/serverHelpers/contentTypes.js +32 -25
  315. package/dist/server/serverHelpers/contentTypes.js.map +1 -1
  316. package/dist/server/serverHelpers/deployValidationState.d.ts +3 -0
  317. package/dist/server/serverHelpers/deployValidationState.js +9 -19
  318. package/dist/server/serverHelpers/deployValidationState.js.map +1 -1
  319. package/dist/server/serverHelpers/operationAuthorizationState.d.ts +12 -0
  320. package/dist/server/serverHelpers/operationAuthorizationState.js +24 -2
  321. package/dist/server/serverHelpers/operationAuthorizationState.js.map +1 -1
  322. package/dist/server/serverHelpers/registeredOperations.d.ts +5 -4
  323. package/dist/server/serverHelpers/registeredOperations.js +74 -21
  324. package/dist/server/serverHelpers/registeredOperations.js.map +1 -1
  325. package/dist/server/serverHelpers/requestSanitization.d.ts +11 -0
  326. package/dist/server/serverHelpers/requestSanitization.js +20 -0
  327. package/dist/server/serverHelpers/requestSanitization.js.map +1 -0
  328. package/dist/server/serverHelpers/serverHandlers.js +6 -3
  329. package/dist/server/serverHelpers/serverHandlers.js.map +1 -1
  330. package/dist/server/serverHelpers/serverUtilities.d.ts +18 -0
  331. package/dist/server/serverHelpers/serverUtilities.js +90 -20
  332. package/dist/server/serverHelpers/serverUtilities.js.map +1 -1
  333. package/dist/server/serverHelpers/sharedMessageEncoding.d.ts +67 -0
  334. package/dist/server/serverHelpers/sharedMessageEncoding.js +280 -0
  335. package/dist/server/serverHelpers/sharedMessageEncoding.js.map +1 -0
  336. package/dist/server/serverHelpers/uwsServer.js +19 -1
  337. package/dist/server/serverHelpers/uwsServer.js.map +1 -1
  338. package/dist/server/static.js +24 -28
  339. package/dist/server/static.js.map +1 -1
  340. package/dist/server/storageReclamation.d.ts +12 -0
  341. package/dist/server/storageReclamation.js +26 -0
  342. package/dist/server/storageReclamation.js.map +1 -1
  343. package/dist/server/threads/itc.d.ts +7 -2
  344. package/dist/server/threads/itc.js +5 -1
  345. package/dist/server/threads/itc.js.map +1 -1
  346. package/dist/server/threads/manageThreads.d.ts +26 -3
  347. package/dist/server/threads/manageThreads.js +517 -65
  348. package/dist/server/threads/manageThreads.js.map +1 -1
  349. package/dist/server/threads/socketRouter.js +89 -26
  350. package/dist/server/threads/socketRouter.js.map +1 -1
  351. package/dist/server/threads/threadHeapMemory.d.ts +2 -0
  352. package/dist/server/threads/threadHeapMemory.js +31 -0
  353. package/dist/server/threads/threadHeapMemory.js.map +1 -0
  354. package/dist/server/threads/threadServer.js +46 -15
  355. package/dist/server/threads/threadServer.js.map +1 -1
  356. package/dist/sqlEngine/config.d.ts +1 -3
  357. package/dist/sqlEngine/config.js +19 -16
  358. package/dist/sqlEngine/config.js.map +1 -1
  359. package/dist/sqlTranslator/index.d.ts +1 -1
  360. package/dist/sqlTranslator/index.js +30 -7
  361. package/dist/sqlTranslator/index.js.map +1 -1
  362. package/dist/upgrade/directives/5-3-0.d.ts +7 -0
  363. package/dist/upgrade/directives/5-3-0.js +148 -0
  364. package/dist/upgrade/directives/5-3-0.js.map +1 -0
  365. package/dist/upgrade/directives/directivesController.js +2 -1
  366. package/dist/upgrade/directives/directivesController.js.map +1 -1
  367. package/dist/utility/OperationFunctionCaller.js +2 -1
  368. package/dist/utility/OperationFunctionCaller.js.map +1 -1
  369. package/dist/utility/common_utils.d.ts +16 -0
  370. package/dist/utility/common_utils.js +32 -6
  371. package/dist/utility/common_utils.js.map +1 -1
  372. package/dist/utility/componentNames.d.ts +8 -0
  373. package/dist/utility/componentNames.js +12 -1
  374. package/dist/utility/componentNames.js.map +1 -1
  375. package/dist/utility/environment/environmentManager.js +3 -6
  376. package/dist/utility/environment/environmentManager.js.map +1 -1
  377. package/dist/utility/environment/systemInformation.d.ts +1 -0
  378. package/dist/utility/environment/systemInformation.js +1 -0
  379. package/dist/utility/environment/systemInformation.js.map +1 -1
  380. package/dist/utility/errors/commonErrors.d.ts +2 -0
  381. package/dist/utility/errors/commonErrors.js +2 -0
  382. package/dist/utility/errors/commonErrors.js.map +1 -1
  383. package/dist/utility/errors/hdbError.d.ts +17 -0
  384. package/dist/utility/errors/hdbError.js +30 -1
  385. package/dist/utility/errors/hdbError.js.map +1 -1
  386. package/dist/utility/globalSchema.d.ts +18 -0
  387. package/dist/utility/hdbTerms.d.ts +16 -0
  388. package/dist/utility/hdbTerms.js +18 -2
  389. package/dist/utility/hdbTerms.js.map +1 -1
  390. package/dist/utility/logging/harper_logger.d.ts +2 -0
  391. package/dist/utility/logging/harper_logger.js +91 -14
  392. package/dist/utility/logging/harper_logger.js.map +1 -1
  393. package/dist/utility/logging/logRotator.js +76 -49
  394. package/dist/utility/logging/logRotator.js.map +1 -1
  395. package/dist/utility/nodeIdentity.d.ts +9 -0
  396. package/dist/utility/nodeIdentity.js +58 -0
  397. package/dist/utility/nodeIdentity.js.map +1 -0
  398. package/dist/utility/npmUtilities.js +9 -7
  399. package/dist/utility/npmUtilities.js.map +1 -1
  400. package/dist/utility/operationPermissions.d.ts +3 -1
  401. package/dist/utility/operationPermissions.js +16 -1
  402. package/dist/utility/operationPermissions.js.map +1 -1
  403. package/dist/utility/operation_authorization.d.ts +10 -7
  404. package/dist/utility/operation_authorization.js +212 -41
  405. package/dist/utility/operation_authorization.js.map +1 -1
  406. package/dist/utility/watchPath.d.ts +29 -0
  407. package/dist/utility/watchPath.js +68 -0
  408. package/dist/utility/watchPath.js.map +1 -0
  409. package/dist/utility/watcherFallback.d.ts +86 -0
  410. package/dist/utility/watcherFallback.js +278 -1
  411. package/dist/utility/watcherFallback.js.map +1 -1
  412. package/dist/validation/configValidator.d.ts +12 -0
  413. package/dist/validation/configValidator.js +199 -72
  414. package/dist/validation/configValidator.js.map +1 -1
  415. package/dist/validation/installValidator.js +12 -0
  416. package/dist/validation/installValidator.js.map +1 -1
  417. package/dist/validation/validationWrapper.d.ts +11 -0
  418. package/dist/validation/validationWrapper.js +16 -3
  419. package/dist/validation/validationWrapper.js.map +1 -1
  420. package/index.ts +2 -0
  421. package/json/systemSchema.json +52 -0
  422. package/npm-shrinkwrap.json +196 -194
  423. package/package.json +7 -6
  424. package/resources/DESIGN.md +114 -52
  425. package/resources/DatabaseTransaction.ts +521 -43
  426. package/resources/LMDBTransaction.ts +26 -4
  427. package/resources/PrimaryRocksDatabase.ts +28 -1
  428. package/resources/RecordEncoder.ts +103 -8
  429. package/resources/RequestTarget.ts +2 -0
  430. package/resources/Resource.ts +17 -9
  431. package/resources/ResourceInterface.ts +23 -0
  432. package/resources/RocksIndexStore.ts +30 -9
  433. package/resources/RocksTransactionLogStore.ts +78 -21
  434. package/resources/Table.ts +1110 -159
  435. package/resources/auditStore.ts +331 -73
  436. package/resources/blob.ts +1029 -110
  437. package/resources/branchDatabase.ts +941 -0
  438. package/resources/crdt.ts +76 -12
  439. package/resources/databases.ts +1310 -252
  440. package/resources/defineTable.ts +18 -2
  441. package/resources/graphql.ts +70 -16
  442. package/resources/indexes/HierarchicalNavigableSmallWorld.ts +6 -2
  443. package/resources/longLivedTransactions.ts +360 -0
  444. package/resources/models/backendRegistry.ts +84 -2
  445. package/resources/models/bootstrap.ts +473 -28
  446. package/resources/recordLock.ts +253 -0
  447. package/resources/replayLogs.ts +147 -26
  448. package/resources/replayLogsGuards.ts +171 -8
  449. package/resources/search.ts +154 -7
  450. package/resources/transactionBroadcast.ts +3 -3
  451. package/security/auth.ts +68 -29
  452. package/security/authn/oidc/claims.ts +72 -0
  453. package/security/authn/oidc/identityToken.ts +129 -0
  454. package/security/authn/oidc/jwks.ts +260 -0
  455. package/security/authn/oidc/providers/generic.ts +40 -0
  456. package/security/authn/oidc/providers/githubActions.ts +137 -0
  457. package/security/authn/oidc/providers/index.ts +52 -0
  458. package/security/authn/oidc/tokenExchange.ts +300 -0
  459. package/security/authn/oidc/trustPolicyOperations.ts +343 -0
  460. package/security/authn/oidc/types.ts +41 -0
  461. package/security/certificateVerification/index.ts +54 -13
  462. package/security/certificateVerification/trustedIssuers.ts +76 -0
  463. package/security/certificateVerification/types.ts +1 -0
  464. package/security/credentialProvenance.ts +47 -0
  465. package/security/credentialRejection.ts +22 -0
  466. package/security/deferredAuthentication.ts +71 -0
  467. package/security/impersonation.ts +117 -12
  468. package/security/jsLoader.ts +81 -18
  469. package/security/keys.ts +298 -72
  470. package/security/operationScope.ts +33 -0
  471. package/security/permissionsTranslator.js +23 -0
  472. package/security/tokenAuthentication.ts +233 -12
  473. package/security/user.ts +4 -3
  474. package/server/DESIGN.md +183 -16
  475. package/server/DurableSubscriptionsSession.ts +71 -11
  476. package/server/REST.ts +79 -1
  477. package/server/graphqlQuerying.ts +4 -0
  478. package/server/http.ts +99 -18
  479. package/server/itc/serverHandlers.js +8 -1
  480. package/server/jobs/jobProcess.ts +8 -2
  481. package/server/jobs/jobs.ts +4 -1
  482. package/server/liveSubscriptionAuth.ts +129 -46
  483. package/server/loadRootComponents.js +50 -8
  484. package/server/mqtt.ts +179 -38
  485. package/server/nodeName.ts +103 -21
  486. package/server/serverHelpers/Headers.ts +131 -0
  487. package/server/serverHelpers/contentTypes.ts +29 -21
  488. package/server/serverHelpers/deployValidationState.ts +24 -13
  489. package/server/serverHelpers/operationAuthorizationState.ts +34 -3
  490. package/server/serverHelpers/registeredOperations.ts +79 -22
  491. package/server/serverHelpers/requestSanitization.ts +15 -0
  492. package/server/serverHelpers/serverHandlers.js +6 -3
  493. package/server/serverHelpers/serverUtilities.ts +104 -31
  494. package/server/serverHelpers/sharedMessageEncoding.ts +307 -0
  495. package/server/serverHelpers/uwsServer.ts +17 -2
  496. package/server/static.ts +23 -29
  497. package/server/storageReclamation.ts +23 -0
  498. package/server/threads/itc.js +11 -1
  499. package/server/threads/manageThreads.js +526 -56
  500. package/server/threads/socketRouter.ts +81 -26
  501. package/server/threads/threadHeapMemory.ts +26 -0
  502. package/server/threads/threadServer.js +43 -15
  503. package/sqlTranslator/index.ts +31 -8
  504. package/studio/web/assets/Chat-BnCBegQz.js +2267 -0
  505. package/studio/web/assets/FloatingChat-CoDW1ySS.js +23 -0
  506. package/studio/web/assets/{abnfDiagram-VCTEODGH-C0_BAZyO.js → abnfDiagram-VCTEODGH-B0BebmD2.js} +1 -1
  507. package/studio/web/assets/{alertDialog-DIHt7Z0r.js → alertDialog-CQyAJJhl.js} +1 -1
  508. package/studio/web/assets/{apiToken-c3Rd-w6g.js → apiToken-Bwk5BLXW.js} +1 -1
  509. package/studio/web/assets/applications-DHxGi7JH.js +296 -0
  510. package/studio/web/assets/architecture-7GRP2DOG-LB-MLAAb.js +1 -0
  511. package/studio/web/assets/{architectureDiagram-5GKGNRK7-BWzrASgm.js → architectureDiagram-5GKGNRK7-7SW3GD-K.js} +1 -1
  512. package/studio/web/assets/authStore-C3Nfubqr.js +3 -0
  513. package/studio/web/assets/{blockDiagram-NRAW4CY4-BdJX9Khj.js → blockDiagram-I7D4REHJ-BqguiadH.js} +2 -2
  514. package/studio/web/assets/{button-DhiX-njv.js → button-BIsUKRZq.js} +2 -2
  515. package/studio/web/assets/{c4Diagram-UCG6FXSJ-CI6MzGmQ.js → c4Diagram-7LVT6UL2-LBNf8t_X.js} +1 -1
  516. package/studio/web/assets/channel-yictG-U-.js +1 -0
  517. package/studio/web/assets/{chevron-up-Df2c8uoU.js → chevron-up-DtKGqDn3.js} +1 -1
  518. package/studio/web/assets/{chunk-TEH6E4GO-P87k5mNi.js → chunk-4HAMMTFA-DWtTut21.js} +1 -1
  519. package/studio/web/assets/{chunk-75Z2AOVW-BT8tVmks.js → chunk-75Z2AOVW-QGQD6th2.js} +1 -1
  520. package/studio/web/assets/{chunk-DU6HZSFF-9kAOOmI6.js → chunk-DU6HZSFF-Chq20Ba5.js} +1 -1
  521. package/studio/web/assets/{chunk-F27PBJKO-BW7ao8AY.js → chunk-F27PBJKO-BVA5EPhV.js} +1 -1
  522. package/studio/web/assets/{chunk-GMAD6QVW-BNyXpoQO.js → chunk-GMAD6QVW-BeS7S07A.js} +1 -1
  523. package/studio/web/assets/{chunk-OBVCFTLP-D4wWOqDQ.js → chunk-GVQU2GXP-sbwVIQ8i.js} +1 -1
  524. package/studio/web/assets/{chunk-G27WJ6UU-COyLMcgK.js → chunk-IMKFNOWR-Bnh3tAVd.js} +1 -1
  525. package/studio/web/assets/{chunk-JQ64N6SF-Cyz1IeLf.js → chunk-L3NEJ4N5-COfUyKII.js} +1 -1
  526. package/studio/web/assets/chunk-OSK3NFVY-ByciRftO.js +10 -0
  527. package/studio/web/assets/{chunk-P2QGCYS3-DmIFY4d7.js → chunk-P2QGCYS3-CP1VhG_c.js} +1 -1
  528. package/studio/web/assets/{chunk-POPQ4Y6H-BPrvMyKz.js → chunk-POPQ4Y6H-ClWhhkwW.js} +1 -1
  529. package/studio/web/assets/{chunk-PWAF6VOD-2zB6IW9i.js → chunk-PWAF6VOD-1z1THyS5.js} +1 -1
  530. package/studio/web/assets/{chunk-RHFEMEQ7-2FgyI8YU.js → chunk-SHT3W25Y-LpQkMsah.js} +2 -2
  531. package/studio/web/assets/{chunk-SVP7TREG-FwtbH2QC.js → chunk-SVP7TREG-jtdAHw0S.js} +1 -1
  532. package/studio/web/assets/{chunk-LCL6LL3I-HOzK_ppE.js → chunk-TICWLB2K-VOwzetX-.js} +1 -1
  533. package/studio/web/assets/classDiagram-ZZMXUADV-VaEwSy_g.js +1 -0
  534. package/studio/web/assets/classDiagram-v2-VYDZK3BY-VaEwSy_g.js +1 -0
  535. package/studio/web/assets/{createLucideIcon-BKGPfjm2.js → createLucideIcon-CzW9508A.js} +1 -1
  536. package/studio/web/assets/{cssMode-CEN2mzSA.js → cssMode-s0cWI-_M.js} +1 -1
  537. package/studio/web/assets/{cynefin-OW5HDTMX-BRkpLFQV.js → cynefin-OW5HDTMX-BbdbCvub.js} +1 -1
  538. package/studio/web/assets/{cynefinDiagram-5FMLGOSQ-CHT1DaX6.js → cynefinDiagram-5FMLGOSQ-TP-aIqbt.js} +1 -1
  539. package/studio/web/assets/{dagre-3AP2YEHR-DpUXBh63.js → dagre-GXQ25YYZ-DShnGpGo.js} +1 -1
  540. package/studio/web/assets/{diagram-S7CK7UJ4-BuymVFZT.js → diagram-S7CK7UJ4-aoCVTtcy.js} +1 -1
  541. package/studio/web/assets/{diagram-UQ7AKVKN-CyP148RM.js → diagram-UQ7AKVKN-DglXtQ6x.js} +1 -1
  542. package/studio/web/assets/{diagram-VSXAHHWV-CoCAg3M9.js → diagram-VSXAHHWV-fhEdmkwM.js} +1 -1
  543. package/studio/web/assets/{diagram-VX7I27RA-BpOqCFca.js → diagram-VX7I27RA-DccVJet6.js} +1 -1
  544. package/studio/web/assets/{diagram-Z3DM3KII-Bfpw7Vbj.js → diagram-Z3DM3KII-D-RyJJb7.js} +1 -1
  545. package/studio/web/assets/{dialog-CBf0Mr1d.js → dialog-Cn2uWgD4.js} +1 -1
  546. package/studio/web/assets/{dist-lkA3O3eM.js → dist-DP8UjMB_.js} +1 -1
  547. package/studio/web/assets/{download-BtTOBem-.js → download-B5T5r7ss.js} +1 -1
  548. package/studio/web/assets/{ebnfDiagram-PWID7BFC-DS_6aWqL.js → ebnfDiagram-PWID7BFC-DJGpIpz_.js} +1 -1
  549. package/studio/web/assets/{editor-D8oDeCTL.js → editor-DNcRHK54.js} +1 -1
  550. package/studio/web/assets/{erDiagram-SSCWMZ5O-DJNk6Fgw.js → erDiagram-RLTQ6QDP-CIfNlgkC.js} +1 -1
  551. package/studio/web/assets/eventmodeling-NTZA5JFV-CLxnp2CR.js +1 -0
  552. package/studio/web/assets/flowDiagram-HODETNUW-BIbhmz9f.js +1 -0
  553. package/studio/web/assets/{ganttDiagram-EL5Y4UJY-2pOExxMY.js → ganttDiagram-EL5Y4UJY-BxToTzzD.js} +1 -1
  554. package/studio/web/assets/{getAnalytics-D4LKGeVy.js → getAnalytics-GHK8ORfM.js} +1 -1
  555. package/studio/web/assets/{gitGraph-4MIJSDKK-CH5ZxwzF.js → gitGraph-4MIJSDKK-D2s2w8lE.js} +1 -1
  556. package/studio/web/assets/{gitGraphDiagram-WWUBYQGX-DVIsIhbO.js → gitGraphDiagram-WWUBYQGX-Dwntd4-x.js} +1 -1
  557. package/studio/web/assets/{html-u3vOg7LJ.js → html-Bdssedlg.js} +1 -1
  558. package/studio/web/assets/{htmlMode-DyO31v-P.js → htmlMode-CoDlJ3fw.js} +1 -1
  559. package/studio/web/assets/{index-Cxj2_wsl.css → index-7RMEgVG1.css} +1 -1
  560. package/studio/web/assets/index-D6sxmFLR.js +824 -0
  561. package/studio/web/assets/index.lazy-tmU5BS8s.js +2 -0
  562. package/studio/web/assets/{info-A6RAGUB7-CPQfTnaG.js → info-A6RAGUB7-DYjkvb0C.js} +1 -1
  563. package/studio/web/assets/{infoDiagram-RXCK75RN-DlwLYlwm.js → infoDiagram-27XIBGKW-Bnp1FJE5.js} +1 -1
  564. package/studio/web/assets/{ishikawaDiagram-5VMMS53U-BRXRp29U.js → ishikawaDiagram-5VMMS53U-D9Xh2r6X.js} +1 -1
  565. package/studio/web/assets/{javascript-CUvxOyTC.js → javascript-B8meVSTH.js} +1 -1
  566. package/studio/web/assets/{journeyDiagram-EYS64GPL-B0ou8k0n.js → journeyDiagram-3NMN7TZE-CokIi6ll.js} +2 -2
  567. package/studio/web/assets/{jsonMode-f_IwbF3D.js → jsonMode-DpIPd35T.js} +1 -1
  568. package/studio/web/assets/{kanban-definition-3QL26DDD-uYg7iYzp.js → kanban-definition-UXKFOSKX-CukSFJfX.js} +1 -1
  569. package/studio/web/assets/{languageServices-DXtZ6rEF.js → languageServices-C_5FMJzQ.js} +1 -1
  570. package/studio/web/assets/{lspLanguageFeatures-B4pCF1zO.js → lspLanguageFeatures-BIzNBzPK.js} +1 -1
  571. package/studio/web/assets/{mermaid-parser.core-Ck-fC8b7.js → mermaid-parser.core-BlEsOWNO.js} +3 -3
  572. package/studio/web/assets/{mermaid.core-CP8aNNYm.js → mermaid.core-BlkGaMIH.js} +5 -5
  573. package/studio/web/assets/{mindmap-definition-FBJOCRG2-CgTZ-rit.js → mindmap-definition-YA3MSWOX-IprMc_0j.js} +1 -1
  574. package/studio/web/assets/{notifications-D3tIQ4sg.js → notifications-CQf18QKb.js} +1 -1
  575. package/studio/web/assets/notifications-CvZivSbh.js +1 -0
  576. package/studio/web/assets/{packet-AYTQ26CC-DEyoPtPb.js → packet-AYTQ26CC-Bi3V04Zi.js} +1 -1
  577. package/studio/web/assets/{pegDiagram-XKGWAZYB-DrD-7sD9.js → pegDiagram-XKGWAZYB-BNuPDLZY.js} +1 -1
  578. package/studio/web/assets/{pie-WAS4IAKB-wjj-EI1d.js → pie-WAS4IAKB-_6DoDbng.js} +1 -1
  579. package/studio/web/assets/{pieDiagram-E7YTZNPT-GntqDCzv.js → pieDiagram-E7YTZNPT-DqNb6Ht2.js} +1 -1
  580. package/studio/web/assets/{profile-DZWU7MgT.js → profile-DdOwtntb.js} +1 -1
  581. package/studio/web/assets/{quadrantDiagram-AXDQQJYC-0UeqXQGd.js → quadrantDiagram-AXDQQJYC-BGH9E2YR.js} +1 -1
  582. package/studio/web/assets/{radar-RG4KPBEZ-DFSA5h7k.js → radar-RG4KPBEZ-DDdVczcL.js} +1 -1
  583. package/studio/web/assets/{railroad-74A4TZTK-CaOUG9wR.js → railroad-74A4TZTK-BJUP4Jds.js} +1 -1
  584. package/studio/web/assets/railroad-abnf-HS5TGJTU-Bm3L1L0h.js +1 -0
  585. package/studio/web/assets/railroad-ebnf-LZEXJU2U-CKzLGlkw.js +1 -0
  586. package/studio/web/assets/railroad-peg-WCYAUIDC-S8xLjslx.js +1 -0
  587. package/studio/web/assets/{railroadDiagram-O6MQD6OU-yHUELZaV.js → railroadDiagram-O6MQD6OU-DGTPh2KZ.js} +1 -1
  588. package/studio/web/assets/{regions-CkyurXzE.js → regions-n69fwagr.js} +1 -1
  589. package/studio/web/assets/{register-BUyhWjBO.js → register-PfWTCXWB.js} +3 -3
  590. package/studio/web/assets/{requirementDiagram-EFPCY7ZU-DNEGFjuW.js → requirementDiagram-BXWQKSXE-BJnO6uLz.js} +1 -1
  591. package/studio/web/assets/{sankeyDiagram-P5KCCOFB-DpyAmSVR.js → sankeyDiagram-P5KCCOFB-0vSOdymH.js} +1 -1
  592. package/studio/web/assets/{sequenceDiagram-WJ2MYXX4-TyaT7xNk.js → sequenceDiagram-WJ2MYXX4-hETizDWE.js} +1 -1
  593. package/studio/web/assets/{setComponentFile-CeyKSZAa.js → setComponentFile-Bg6O7X0S.js} +1 -1
  594. package/studio/web/assets/{setup-D2kn7cAA.js → setup-CUx_aUDl.js} +2 -2
  595. package/studio/web/assets/{stateDiagram-HBIQ2CUA-CeEdTArZ.js → stateDiagram-D77RDMKH-CdYQ_KtC.js} +1 -1
  596. package/studio/web/assets/stateDiagram-v2-MP3YSRHH-CdKuzQMT.js +1 -0
  597. package/studio/web/assets/status-D7BVKqX9.js +61 -0
  598. package/studio/web/assets/{swimlanes-XN3QIQJK-B54FmF46.js → swimlanes-42K2YHIH-B8cHIpU4.js} +1 -1
  599. package/studio/web/assets/swimlanesDiagram-VR7AAH4N-DmOSJwaH.js +8 -0
  600. package/studio/web/assets/{tabs-B_G5zscN.js → tabs-BrHu7gJi.js} +1 -1
  601. package/studio/web/assets/{timeline-definition-24CTP7MA-D-a9ujbo.js → timeline-definition-24CTP7MA-BJWYSXqF.js} +1 -1
  602. package/studio/web/assets/{toggleHighContrast-C0UW6rI2.js → toggleHighContrast-DBSyXzMr.js} +1 -1
  603. package/studio/web/assets/{treeView-Q6P3EWNA-CrW_6JnS.js → treeView-Q6P3EWNA-qxe_v6CQ.js} +1 -1
  604. package/studio/web/assets/{treemap-WGGIJYW6-BxyYLdP_.js → treemap-WGGIJYW6-dDo97XXF.js} +1 -1
  605. package/studio/web/assets/{tsMode-DBC0zmDx.js → tsMode-BByKCjBS.js} +1 -1
  606. package/studio/web/assets/{typescript-DApRQir3.js → typescript-DDLnLpw9.js} +1 -1
  607. package/studio/web/assets/{useEntityRestURL-DB6JStU1.js → useEntityRestURL-31CHGaHk.js} +1 -1
  608. package/studio/web/assets/{useLocalStorage-Dtj1QS8_.js → useLocalStorage-BqMR3D8_.js} +1 -1
  609. package/studio/web/assets/vendor-core-c2JRRJpV.js +58 -0
  610. package/studio/web/assets/vendor-datadog-CLUcJXOo.js +6 -0
  611. package/studio/web/assets/{vendor-react-Dyj4O3HE.js → vendor-react-CJV_K1u4.js} +1 -1
  612. package/studio/web/assets/vendor-tanstack-DxzraizX.js +1 -0
  613. package/studio/web/assets/{vendor-ui-vhu-UHhF.js → vendor-ui-BUjK0h8a.js} +2 -2
  614. package/studio/web/assets/{vennDiagram-4TSXK5OY-Cy7s7Mpy.js → vennDiagram-4TSXK5OY-A3i-lCdl.js} +1 -1
  615. package/studio/web/assets/{wardley-WFR3VGLG-BeBL35g2.js → wardley-WFR3VGLG-B0ik-_6g.js} +1 -1
  616. package/studio/web/assets/{wardleyDiagram-VM6X3IG4-BylmIGSg.js → wardleyDiagram-VM6X3IG4-CjrkKWUR.js} +1 -1
  617. package/studio/web/assets/{workers-C0bFIedw.js → workers-pR3jRY9D.js} +1 -1
  618. package/studio/web/assets/x-DIzaLEdK.js +1 -0
  619. package/studio/web/assets/{xml-HWd01lU-.js → xml-2iRnMhQO.js} +1 -1
  620. package/studio/web/assets/{xychartDiagram-S5SC5T6Z-CoKALMXr.js → xychartDiagram-S5SC5T6Z-Biok4GYV.js} +1 -1
  621. package/studio/web/assets/{yaml-CIH0Nt-h.js → yaml-Bf92gJpd.js} +1 -1
  622. package/studio/web/index.html +14 -14
  623. package/upgrade/directives/5-3-0.ts +132 -0
  624. package/upgrade/directives/directivesController.ts +2 -1
  625. package/utility/OperationFunctionCaller.ts +2 -1
  626. package/utility/common_utils.ts +30 -5
  627. package/utility/componentNames.ts +12 -0
  628. package/utility/environment/environmentManager.ts +3 -7
  629. package/utility/environment/systemInformation.ts +7 -0
  630. package/utility/errors/commonErrors.ts +4 -0
  631. package/utility/errors/hdbError.ts +29 -0
  632. package/utility/hdbTerms.ts +17 -0
  633. package/utility/logging/harper_logger.ts +87 -14
  634. package/utility/logging/logRotator.ts +72 -45
  635. package/utility/nodeIdentity.ts +45 -0
  636. package/utility/npmUtilities.ts +10 -8
  637. package/utility/operationPermissions.ts +18 -1
  638. package/utility/operation_authorization.ts +231 -42
  639. package/utility/watchPath.ts +63 -0
  640. package/utility/watcherFallback.ts +270 -0
  641. package/validation/configValidator.ts +209 -72
  642. package/validation/installValidator.ts +15 -0
  643. package/validation/validationWrapper.ts +18 -4
  644. package/studio/web/assets/Chat-JpO8EtUu.js +0 -2067
  645. package/studio/web/assets/FloatingChat-Bcj3xSZu.js +0 -23
  646. package/studio/web/assets/applications-ByqLRKyZ.js +0 -296
  647. package/studio/web/assets/architecture-7GRP2DOG-DNdx5tEU.js +0 -1
  648. package/studio/web/assets/authStore-qKmCZcaf.js +0 -3
  649. package/studio/web/assets/channel-DtCV8PTL.js +0 -1
  650. package/studio/web/assets/chunk-R7TYR2AO-Irip67yr.js +0 -10
  651. package/studio/web/assets/classDiagram-DTDB5LWJ-DbO_dCNE.js +0 -1
  652. package/studio/web/assets/classDiagram-v2-JRS7N3AN-DbO_dCNE.js +0 -1
  653. package/studio/web/assets/eventmodeling-NTZA5JFV-5jbe4A5P.js +0 -1
  654. package/studio/web/assets/flowDiagram-A5DVABFB-Dp9Ezlow.js +0 -1
  655. package/studio/web/assets/index-aSt5tY-L.js +0 -824
  656. package/studio/web/assets/index.lazy-B9jiPwT8.js +0 -2
  657. package/studio/web/assets/notifications-CUoYgU98.js +0 -1
  658. package/studio/web/assets/railroad-abnf-HS5TGJTU-Bc0Qi0WH.js +0 -1
  659. package/studio/web/assets/railroad-ebnf-LZEXJU2U-G8rVVZ2C.js +0 -1
  660. package/studio/web/assets/railroad-peg-WCYAUIDC-CrehKBhC.js +0 -1
  661. package/studio/web/assets/stateDiagram-v2-4QOOHH4V-D4tuw9Su.js +0 -1
  662. package/studio/web/assets/status-D7Xn5ePA.js +0 -61
  663. package/studio/web/assets/swimlanesDiagram-VK2B7HYN-XOhmNEvq.js +0 -8
  664. package/studio/web/assets/vendor-core-RCcadM3e.js +0 -73
  665. package/studio/web/assets/vendor-datadog-BRv-mOv1.js +0 -6
  666. package/studio/web/assets/vendor-tanstack-BiFWSB3W.js +0 -1
  667. package/studio/web/assets/x-B9o9hsep.js +0 -1
  668. /package/studio/web/assets/{sizeCapture-X5ZJPWSS-B0uUizjq.js → sizeCapture-INFHLROL-B0uUizjq.js} +0 -0
@@ -7,7 +7,7 @@ const { realExit } = require('./workerProcessGuard.ts');
7
7
 
8
8
  const { Worker, MessageChannel, parentPort, isMainThread, threadId, workerData } = require('worker_threads');
9
9
  const { spawn, spawnSync } = require('node:child_process');
10
- const { readFileSync } = require('node:fs');
10
+ const { readdirSync, readFileSync, readlinkSync } = require('node:fs');
11
11
  const { setTimeout: delay } = require('node:timers/promises');
12
12
  const { join, isAbsolute, extname } = require('path');
13
13
  const { pathToFileURL } = require('url');
@@ -21,7 +21,16 @@ const { randomBytes } = require('crypto');
21
21
  const { _assignPackageExport } = require('../../globals.js');
22
22
  const { PACKAGE_ROOT } = require('../../utility/packageUtils.js');
23
23
  const { resolvePreloadModules } = require('./resolvePreload.ts');
24
+ const { resolveThreadHeapMemoryMb } = require('./threadHeapMemory.ts');
24
25
  const { getConfigPath } = require('../../config/configUtils.ts');
26
+ const { resolveWatchTarget } = require('../../utility/watchPath.ts');
27
+ const {
28
+ DIRECTORY_POLLING_FALLBACK_OPTIONS,
29
+ claimLostNativeWatchError,
30
+ guardedWatch,
31
+ isWatcherExhaustionError,
32
+ warnWatcherFallback,
33
+ } = require('../../utility/watcherFallback.ts');
25
34
  let importModules;
26
35
  function getImportModules() {
27
36
  if (importModules === undefined)
@@ -42,7 +51,6 @@ function getRequireModules() {
42
51
  );
43
52
  return requireModules;
44
53
  }
45
- const chokidar = require('chokidar');
46
54
  const isBun = typeof globalThis.Bun !== 'undefined';
47
55
  const MB = 1024 * 1024;
48
56
  const workers = []; // these are our child workers that we are managing
@@ -54,8 +62,11 @@ const MAX_UNEXPECTED_RESTARTS = 50;
54
62
  // down mid-disposal crashes the process, so give that teardown more room before the forced backstop.
55
63
  let threadTerminationTimeout = process.env.DEV_MODE === 'true' || process.env.DEV_MODE === '1' ? 30000 : 10000;
56
64
  const RESTART_TYPE = 'restart';
65
+ const RESTART_PROGRESS_HEARTBEAT_MS = 15000;
57
66
  const REQUEST_THREAD_INFO = 'request_thread_info';
58
67
  const RESOURCE_REPORT = 'resource_report';
68
+ const OS_THREAD_ID = 'os-thread-id';
69
+ const STUCK_WORKER_REPORT = 'stuck-worker-report';
59
70
  const THREAD_INFO = 'thread_info';
60
71
  const ADDED_PORT = 'added-port';
61
72
  const ACKNOWLEDGEMENT = 'ack';
@@ -154,8 +165,11 @@ module.exports = {
154
165
  beginProcessShutdown,
155
166
  registerWorkerDataProvider,
156
167
  onThreadExit,
168
+ hasThreadExited,
169
+ notifyThreadExit,
157
170
  registerProcessGroup,
158
171
  unregisterProcessGroup,
172
+ isProcessGroupAlive,
159
173
  isThreadRunning,
160
174
  waitUntilConfirmedGone,
161
175
  restartNumber: workerData?.restartNumber || 1,
@@ -297,6 +311,15 @@ if (!parentPort) {
297
311
  onMessageByType(RESOURCE_REPORT, (message, worker) => {
298
312
  if (worker) recordResourceReport(worker, message);
299
313
  });
314
+ onMessageByType(OS_THREAD_ID, (message, worker) => {
315
+ if (worker) worker.osThreadId = message.osThreadId;
316
+ });
317
+ onMessageByType(STUCK_WORKER_REPORT, (message) => {
318
+ for (const threadId of message.threadIds) {
319
+ const worker = workers.find((worker) => worker.threadId === threadId);
320
+ if (worker) logStuckWorkerDiagnostics(worker);
321
+ }
322
+ });
300
323
  onMessageByType(AWAIT_PROCESS_GROUP_TERMINATION, async (message, worker) => {
301
324
  if (!worker) return;
302
325
  await (pendingProcessGroupTerminations.get(message.ownerThreadId) ?? Promise.resolve());
@@ -308,6 +331,7 @@ if (!parentPort) {
308
331
  }
309
332
  // postMessage type listeners that are registered in other ways or can be registered later
310
333
  listenersByType.set(hdbTerms.ITC_EVENT_TYPES.CHILD_STARTED, null);
334
+ listenersByType.set(hdbTerms.ITC_EVENT_TYPES.CHILD_STARTUP_PHASE, null);
311
335
  listenersByType.set(hdbTerms.ITC_EVENT_TYPES.SCHEMA, null);
312
336
  listenersByType.set(hdbTerms.ITC_EVENT_TYPES.USER, null);
313
337
  listenersByType.set(hdbTerms.ITC_EVENT_TYPES.COMPONENT_STATUS_REQUEST, null);
@@ -345,7 +369,7 @@ function startWorker(path, options = {}) {
345
369
  // and lower than total memory
346
370
  availableMemory = Math.min(availableMemory, totalmem(), 20000 * MB);
347
371
  const maxOldMemory =
348
- envMgr.get(hdbTerms.CONFIG_PARAMS.THREADS_MAXHEAPMEMORY) ??
372
+ resolveThreadHeapMemoryMb(envMgr.get(hdbTerms.CONFIG_PARAMS.THREADS_MAXHEAPMEMORY)) ??
349
373
  Math.max(Math.floor(availableMemory / MB / (10 + (options.threadCount || 1) / 4)), 512);
350
374
  // Max young memory space (semi-space for scavenger) is 1/128 of max memory (limited to 16-64). For most of our m5
351
375
  // machines this will be 64MB (less for t3's). This is based on recommendations from:
@@ -447,7 +471,10 @@ function startWorker(path, options = {}) {
447
471
  if (worker.unexpectedRestarts < MAX_UNEXPECTED_RESTARTS) {
448
472
  options.unexpectedRestarts = worker.unexpectedRestarts + 1;
449
473
  startWorker(path, options);
450
- } else harperLogger.error(`Thread has been restarted ${worker.restarts} times and will not be restarted`);
474
+ } else {
475
+ harperLogger.error(`Thread has been restarted ${worker.unexpectedRestarts} times and will not be restarted`);
476
+ options.onRestartExhausted?.(worker);
477
+ }
451
478
  }
452
479
  });
453
480
  workers.push(worker);
@@ -467,16 +494,24 @@ const OVERLAPPING_RESTART_TYPES = [hdbTerms.THREAD_TYPES.HTTP];
467
494
  * threads at the same time we shutdown new ones. However, we usually want to limit how many we do at once to avoid
468
495
  * excessive load and to keep things responsive. This parameter throttles the restarts to minimize load from
469
496
  * thread startups.
470
- * @returns {Promise<void>}
497
+ * @param onProgress Called each time a worker has been replaced (or its replacement has been given
498
+ * up on), so a caller waiting on a wide pool can tell a slow restart from a stalled one.
499
+ * @returns {Promise<{workersKeptOnOldCode: number, replacementsNotStarted: number}|{declined: true}|undefined>}
500
+ * from the main thread, how many workers were left running the old code and how many replacements
501
+ * never reported that they started — or `{declined: true}` when the process is already shutting down;
502
+ * from a worker, nothing — the restart is handed to the main thread.
471
503
  */
472
504
 
473
505
  async function restartWorkers(
474
506
  name = null,
475
507
  maxWorkersDown = Math.max(Math.floor(workerCount / 8), 1), // restart 1/8 of the threads at a time, but at least 1
476
- startReplacementThreads = true
508
+ startReplacementThreads = true,
509
+ onProgress = null
477
510
  ) {
478
511
  if (isMainThread) {
479
- if (processShuttingDown && startReplacementThreads) return;
512
+ // Declining is not the same as delegating: a caller reporting on the restart must not read this
513
+ // as "another thread is completing it".
514
+ if (processShuttingDown && startReplacementThreads) return { declined: true };
480
515
  try {
481
516
  // we do this because it is possible for a component to chdir to itself, get re-deployed and then the cwd
482
517
  // inode link is invalid and it can cause a lot of problems. But process.cwd() still returns the path, for
@@ -491,7 +526,16 @@ async function restartWorkers(
491
526
  // This is here to prevent circular dependencies
492
527
  if (startReplacementThreads) {
493
528
  const { loadRootComponents } = require('../loadRootComponents.js');
494
- await loadRootComponents();
529
+ // Installing and loading every root component reports nothing and can outlast a caller's idle
530
+ // window on its own (a cold npm cache, a large dependency graph), so beat while it runs. The
531
+ // caller's absolute ceiling is what bounds a load that never finishes.
532
+ const loading = setInterval(() => onProgress?.(), RESTART_PROGRESS_HEARTBEAT_MS).unref();
533
+ try {
534
+ await loadRootComponents();
535
+ } finally {
536
+ clearInterval(loading);
537
+ }
538
+ onProgress?.();
495
539
  }
496
540
 
497
541
  module.exports.restartNumber++;
@@ -501,7 +545,17 @@ async function restartWorkers(
501
545
  maxWorkersDown = maxWorkersDown * workers.length;
502
546
  }
503
547
  // make a copy of the workers before iterating them, as the workers array mutates a lot during this
504
- let waitingToFinish = []; // promises for workers we have shut down and are waiting to exit
548
+ let waitingToFinish = []; // promises for workers we are replacing, spliced as each is replaced
549
+ // Every replacement that was started without being awaited first, so this function can still
550
+ // resolve only once each one is accepting connections.
551
+ let replacementsStarting = [];
552
+ // A replacement that never came up leaves the pool in one of two very different states, and a
553
+ // caller waiting on this restart needs them apart: the pre-start path keeps the *old* worker
554
+ // serving old code, while a replacement that exits after its predecessor is already gone only
555
+ // costs capacity until startWorker's auto-restart brings a fresh one up.
556
+ let workersKeptOnOldCode = 0;
557
+ let replacementsNotStarted = 0;
558
+ let replacementsFailedToStart = 0;
505
559
  // We can only start the replacement *before* the old worker releases its port when the OS lets
506
560
  // both listen on the same port at once (SO_REUSEPORT). Without that — Windows (no SO_REUSEPORT),
507
561
  // macOS (unreliable SO_REUSEPORT, so workers bind exclusively), and Bun — the replacement can't
@@ -513,7 +567,9 @@ async function restartWorkers(
513
567
  // thread keeps serving the HTTP ports throughout. This ordering is also what lets
514
568
  // listenOnPorts() treat a dedicated listener's EADDRINUSE as an external conflict.
515
569
  const canPreStartReplacement = process.platform !== 'win32' && process.platform !== 'darwin' && !isBun;
516
- for (let worker of workers.slice(0)) {
570
+ const restarting = workers.slice(0);
571
+ for (let index = 0; index < restarting.length; index++) {
572
+ const worker = restarting[index];
517
573
  // Terminal shutdown: stop replacing workers mid-loop — the guard for every replacement start below.
518
574
  if (processShuttingDown && startReplacementThreads) break;
519
575
  if ((name && worker.name !== name) || worker.wasShutdown) continue; // filter by type, if specified
@@ -580,6 +636,8 @@ async function restartWorkers(
580
636
  // Replacement didn't come up — keep the existing worker serving. Restore its auto-restart
581
637
  // protection if it is still alive (it may have exited on its own during the wait).
582
638
  if (workers.includes(worker)) worker.wasShutdown = false;
639
+ workersKeptOnOldCode++;
640
+ onProgress?.();
583
641
  continue;
584
642
  }
585
643
  }
@@ -600,7 +658,16 @@ async function restartWorkers(
600
658
  // Overlapping types we couldn't pre-start (Windows/Bun): start the replacement now that the old
601
659
  // worker is releasing its port. server.close() stops accepting immediately, so the port frees up
602
660
  // well before the replacement finishes booting and binds.
603
- if (overlapping && startReplacementThreads && !canPreStartReplacement && !processShuttingDown) worker.startCopy();
661
+ let replacementStarting;
662
+ if (overlapping && startReplacementThreads && !canPreStartReplacement && !processShuttingDown) {
663
+ replacementStarting = whenWorkerStarted(worker.startCopy()).then((started) => {
664
+ if (!started) replacementsFailedToStart++;
665
+ onProgress?.();
666
+ return started;
667
+ });
668
+ replacementsStarting.push(replacementStarting);
669
+ }
670
+
604
671
  let whenDone = new Promise((resolve) => {
605
672
  // in case the exit inside the thread doesn't timeout, force it from the outside
606
673
  const armTerminate = (delay) =>
@@ -627,29 +694,62 @@ async function restartWorkers(
627
694
  // buggy/rogue message can't defer the force-kill unboundedly; a shrink (drain-done reset)
628
695
  // passes through untouched. See boundedTerminateDelay for the arithmetic + its unit tests.
629
696
  const { boundedTerminateDelay, getShutdownDrainCeilingMs } = require('../../components/shutdownDrain.ts');
630
- timeout = armTerminate(
631
- boundedTerminateDelay(deadlineMs, Date.now(), threadTerminationTimeout * 2, getShutdownDrainCeilingMs())
697
+ const delay = boundedTerminateDelay(
698
+ deadlineMs,
699
+ Date.now(),
700
+ threadTerminationTimeout * 2,
701
+ getShutdownDrainCeilingMs()
632
702
  );
703
+ timeout = armTerminate(delay);
704
+ // The worker is telling us it has work still moving and how long it may take, so pass that
705
+ // on: a caller waiting on the restart must not treat a live drain as a stalled one.
706
+ onProgress?.(Date.now() + delay);
633
707
  };
634
708
  worker.on('exit', () => {
635
709
  clearTimeout(timeout);
710
+ onProgress?.();
636
711
  worker.extendTerminateDeadline = undefined;
637
- const index = waitingToFinish.indexOf(whenDone);
638
- if (index > -1) waitingToFinish.splice(index, 1);
639
712
  // non-overlapping types have no advance replacement, so start it once the old one is gone
640
713
  if (!overlapping && startReplacementThreads && !processShuttingDown) worker.startCopy();
641
714
  resolve();
642
715
  });
643
716
  });
644
- waitingToFinish.push(whenDone);
717
+ // A worker counts as replaced only once its replacement is accepting connections, not merely
718
+ // once it has exited. This promise is held unawaited between throttle points, so it must not
719
+ // be able to reject.
720
+ const replaced = (replacementStarting ? Promise.all([whenDone, replacementStarting]) : whenDone)
721
+ .catch((error) => harperLogger.warn('Error waiting for a worker to be replaced', error))
722
+ .then(() => {
723
+ const at = waitingToFinish.indexOf(replaced);
724
+ if (at > -1) waitingToFinish.splice(at, 1);
725
+ });
726
+ waitingToFinish.push(replaced);
645
727
  if (waitingToFinish.length >= maxWorkersDown) {
646
- // throttle how many workers are draining/down at once to limit load
728
+ // throttle how many workers are down at once to limit load
647
729
  await Promise.race(waitingToFinish);
648
730
  }
731
+ // Readiness throttling bounds how many workers are down at once, but not how many *fail*: with
732
+ // replacements that never come up, walking the rest of the pool would leave nothing serving.
733
+ // The workers not yet touched are still running the old code, which beats none running at all.
734
+ if (replacementsFailedToStart >= maxWorkersDown) {
735
+ const untouched = restarting
736
+ .slice(index + 1)
737
+ // a worker that exited on its own mid-restart is spliced out of `workers` and
738
+ // auto-restarted onto the new code (see the exit handler above); it is not still on
739
+ // the previous code even though this loop never got to it.
740
+ .filter((other) => (!name || other.name === name) && !other.wasShutdown && workers.includes(other)).length;
741
+ harperLogger.error(
742
+ `${replacementsFailedToStart} replacement worker thread(s) did not start; stopping this restart with ${untouched} worker(s) still on the previous code`
743
+ );
744
+ workersKeptOnOldCode += untouched;
745
+ break;
746
+ }
649
747
  }
650
- // seems appropriate to wait for this to finish, but the API doesn't actually wait for this function
651
- // to finish, so not that important
652
748
  await Promise.all(waitingToFinish);
749
+ // A caller awaiting this needs it to mean "the pool is serving the new code", so wait out the
750
+ // replacements that could only be started once their predecessor released its exclusive ports.
751
+ replacementsNotStarted = (await Promise.all(replacementsStarting)).filter((started) => !started).length;
752
+ return { workersKeptOnOldCode, replacementsNotStarted };
653
753
  } else {
654
754
  parentPort.postMessage({
655
755
  type: RESTART_TYPE,
@@ -657,6 +757,51 @@ async function restartWorkers(
657
757
  });
658
758
  }
659
759
  }
760
+ /**
761
+ * Resolve once a newly started worker reports that it is accepting connections, or gives up on it.
762
+ * There is no old worker left to fall back on here, so a replacement that fails to start is left to
763
+ * startWorker's own auto-restart handling; this only stops waiting on it.
764
+ * @param newWorker The replacement worker
765
+ * @returns {Promise<boolean>} whether the worker reported that it started
766
+ */
767
+ function whenWorkerStarted(newWorker) {
768
+ return new Promise((resolve) => {
769
+ const cleanup = () => {
770
+ clearTimeout(timeout);
771
+ newWorker.off('message', startListener);
772
+ newWorker.off('exit', exitListener);
773
+ };
774
+ const timeout = setTimeout(
775
+ () => {
776
+ harperLogger.error('Replacement worker did not start in time', newWorker.threadId);
777
+ cleanup();
778
+ // Its predecessor is already gone, so a replacement wedged in boot is a worker slot serving
779
+ // nothing until the process restarts. Stop it and let startWorker's exit handling replace it.
780
+ if (isBun) {
781
+ // terminate() triggers a NAPI segfault in Bun; ask the worker to self-exit instead.
782
+ try {
783
+ newWorker.postMessage({ type: FORCE_EXIT });
784
+ } catch {}
785
+ } else newWorker.terminate();
786
+ resolve(false);
787
+ },
788
+ Math.max(threadTerminationTimeout * 2, 60000)
789
+ ).unref();
790
+ const startListener = (message) => {
791
+ if (message.type === hdbTerms.ITC_EVENT_TYPES.CHILD_STARTED) {
792
+ cleanup();
793
+ resolve(true);
794
+ }
795
+ };
796
+ const exitListener = () => {
797
+ harperLogger.warn('Replacement worker exited before starting', newWorker.threadId);
798
+ cleanup();
799
+ resolve(false);
800
+ };
801
+ newWorker.on('message', startListener);
802
+ newWorker.on('exit', exitListener);
803
+ });
804
+ }
660
805
  function shutdownWorkers(name) {
661
806
  return restartWorkers(name, Infinity, false);
662
807
  }
@@ -784,18 +929,154 @@ function broadcastWithAcknowledgement(message, timeout = DEFAULT_ACK_TIMEOUT_MS)
784
929
  timer = undefined;
785
930
  const stuck = [];
786
931
  for (let ackHandler of [...pending]) {
787
- stuck.push(ackHandler.port?.threadId);
932
+ stuck.push(ackHandler.port);
788
933
  ackHandler(); // same cleanup path as an ack/close; drives waitingCount to 0 and resolves
789
934
  }
790
935
  harperLogger.warn(
791
- `ITC broadcast (type ${message.type}) not acknowledged by worker thread(s) ${stuck.join(', ')} within ${timeout}ms; proceeding best-effort`
936
+ `ITC broadcast (type ${message.type}) not acknowledged by worker thread(s) ${stuck.map((port) => port?.threadId).join(', ')} within ${timeout}ms; proceeding best-effort`
792
937
  );
938
+ if (isMainThread) for (let port of stuck) logStuckWorkerDiagnostics(port);
939
+ else if (parentPort) {
940
+ // Only main holds the Worker objects (and tids), so a worker-originated timeout is sampled there.
941
+ const threadIds = stuck.map((port) => port?.threadId).filter((threadId) => threadId > 0);
942
+ if (threadIds.length > 0) parentPort.postMessage({ type: STUCK_WORKER_REPORT, threadIds });
943
+ }
793
944
  }, timeout);
794
945
  timer.unref?.();
795
946
  }
796
947
  });
797
948
  }
798
949
 
950
+ // Linux only: /proc/thread-self resolves to <pid>/task/<tid> for the calling thread.
951
+ function getOsThreadId() {
952
+ try {
953
+ const tid = Number(readlinkSync('/proc/thread-self').split('/').pop());
954
+ return Number.isInteger(tid) && tid > 0 ? tid : undefined;
955
+ } catch {
956
+ return undefined;
957
+ }
958
+ }
959
+
960
+ function readTaskFile(tid, name) {
961
+ try {
962
+ return readFileSync(`/proc/self/task/${tid}/${name}`, 'utf8').trim();
963
+ } catch {
964
+ return undefined;
965
+ }
966
+ }
967
+
968
+ // Kernel-side view of one thread. Every field is best-effort and reported individually, since
969
+ // wchan/syscall need ptrace read access that a hardened container may deny while stat is open.
970
+ function finiteOrUndefined(value) {
971
+ const number = Number(value);
972
+ return Number.isFinite(number) ? number : undefined;
973
+ }
974
+
975
+ function readOsThreadState(tid) {
976
+ const thread = { tid };
977
+ const stat = readTaskFile(tid, 'stat');
978
+ if (stat !== undefined) {
979
+ // Fields after the parenthesized comm, so state is [0], utime/stime [11]/[12], starttime [19].
980
+ const fields = stat.slice(stat.lastIndexOf(')') + 2).split(' ');
981
+ thread.state = fields[0];
982
+ const utime = finiteOrUndefined(fields[11]);
983
+ const stime = finiteOrUndefined(fields[12]);
984
+ if (utime !== undefined && stime !== undefined) thread.cpuTicks = utime + stime;
985
+ thread.startTime = fields[19];
986
+ }
987
+ const wchan = readTaskFile(tid, 'wchan');
988
+ if (wchan !== undefined) thread.wchan = wchan;
989
+ // First token only: the syscall number, "running", or -1 (blocked outside a syscall). The
990
+ // remainder is argument registers and the stack/instruction pointers, which don't belong in a log.
991
+ const syscall = readTaskFile(tid, 'syscall');
992
+ if (syscall !== undefined) thread.syscall = syscall.split(' ')[0];
993
+ const status = readTaskFile(tid, 'status');
994
+ if (status !== undefined) {
995
+ thread.voluntarySwitches = finiteOrUndefined(/^voluntary_ctxt_switches:\s*(\d+)/m.exec(status)?.[1]);
996
+ thread.nonvoluntarySwitches = finiteOrUndefined(/^nonvoluntary_ctxt_switches:\s*(\d+)/m.exec(status)?.[1]);
997
+ }
998
+ return thread;
999
+ }
1000
+
1001
+ function snapshotWorkerThread(worker) {
1002
+ const snapshot = { at: Date.now() };
1003
+ if (worker.resources?.updated) snapshot.sinceResourceReport = snapshot.at - worker.resources.updated;
1004
+ const eventLoop = worker.performance?.eventLoopUtilization?.();
1005
+ if (eventLoop) snapshot.eventLoop = { idle: eventLoop.idle, active: eventLoop.active };
1006
+ if (worker.osThreadId) snapshot.osThread = readOsThreadState(worker.osThreadId);
1007
+ return snapshot;
1008
+ }
1009
+
1010
+ function describeThreadState(thread) {
1011
+ return `state=${thread.state ?? '?'} wchan=${thread.wchan ?? '?'} syscall=${thread.syscall ?? '?'}`;
1012
+ }
1013
+
1014
+ function describeSnapshot(snapshot) {
1015
+ const parts = [
1016
+ snapshot.sinceResourceReport === undefined
1017
+ ? 'no resource report received'
1018
+ : `last resource report ${snapshot.sinceResourceReport}ms ago`,
1019
+ ];
1020
+ if (snapshot.eventLoop)
1021
+ parts.push(
1022
+ `event loop active ${Math.round(snapshot.eventLoop.active)}ms idle ${Math.round(snapshot.eventLoop.idle)}ms`
1023
+ );
1024
+ const thread = snapshot.osThread;
1025
+ if (!thread) parts.push('os thread state unavailable');
1026
+ else
1027
+ parts.push(
1028
+ `os tid ${thread.tid} ${describeThreadState(thread)} cpuTicks=${thread.cpuTicks ?? '?'} ctxtSwitches=${thread.voluntarySwitches ?? '?'}/${thread.nonvoluntarySwitches ?? '?'}`
1029
+ );
1030
+ return parts.join('; ');
1031
+ }
1032
+
1033
+ function describeDelta(before, after) {
1034
+ return Number.isFinite(before) && Number.isFinite(after) ? `+${after - before}` : '?';
1035
+ }
1036
+
1037
+ function describeProgress(first, second) {
1038
+ const parts = [];
1039
+ if (first.eventLoop && second.eventLoop)
1040
+ parts.push(
1041
+ `event loop active +${Math.round(second.eventLoop.active - first.eventLoop.active)}ms idle +${Math.round(second.eventLoop.idle - first.eventLoop.idle)}ms`
1042
+ );
1043
+ const before = first.osThread;
1044
+ const after = second.osThread;
1045
+ if (before && after)
1046
+ parts.push(
1047
+ `cpuTicks ${describeDelta(before.cpuTicks, after.cpuTicks)} ctxtSwitches ${describeDelta(before.voluntarySwitches, after.voluntarySwitches)}/${describeDelta(before.nonvoluntarySwitches, after.nonvoluntarySwitches)} ${describeThreadState(after)}`
1048
+ );
1049
+ return parts.join('; ');
1050
+ }
1051
+
1052
+ // A blocked event loop cannot report itself; two kernel-state samples a second apart separate
1053
+ // "parked on a lock" (no CPU ticks, no context switches) from "spinning".
1054
+ const STUCK_WORKER_SAMPLE_INTERVAL_MS = 1000;
1055
+ const STUCK_WORKER_DIAGNOSTIC_COOLDOWN_MS = 30000;
1056
+ function logStuckWorkerDiagnostics(worker) {
1057
+ if (!worker || !workers.includes(worker)) return;
1058
+ const now = Date.now();
1059
+ if (worker.stuckDiagnosticAt !== undefined && now - worker.stuckDiagnosticAt < STUCK_WORKER_DIAGNOSTIC_COOLDOWN_MS)
1060
+ return;
1061
+ worker.stuckDiagnosticAt = now;
1062
+ const threadId = worker.threadId; // Node resets it to -1 once the worker exits
1063
+ const first = snapshotWorkerThread(worker);
1064
+ harperLogger.warn(`Worker thread ${threadId} at ack timeout: ${describeSnapshot(first)}`);
1065
+ if (!first.eventLoop && !first.osThread) return;
1066
+ setTimeout(() => {
1067
+ if (!workers.includes(worker)) {
1068
+ harperLogger.warn(`Worker thread ${threadId} exited before its follow-up sample`);
1069
+ return;
1070
+ }
1071
+ const second = snapshotWorkerThread(worker);
1072
+ // A recycled tid after a thread exit would attribute another thread's activity to this worker.
1073
+ if (second.osThread && second.osThread.startTime !== first.osThread?.startTime) second.osThread = undefined;
1074
+ harperLogger.warn(
1075
+ `Worker thread ${threadId} over the next ${second.at - first.at}ms: ${describeProgress(first, second) || 'no further state available'}`
1076
+ );
1077
+ }, STUCK_WORKER_SAMPLE_INTERVAL_MS).unref();
1078
+ }
1079
+
799
1080
  function sendThreadInfo(targetWorker) {
800
1081
  targetWorker.postMessage({
801
1082
  type: THREAD_INFO,
@@ -868,6 +1149,8 @@ if (parentPort && workerData?.addPorts) {
868
1149
  // parentPort so sendToThread(0, ...) and similar lookups can route back to main.
869
1150
  parentPort.threadId = 0;
870
1151
  addPort(parentPort);
1152
+ const osThreadId = getOsThreadId();
1153
+ if (osThreadId !== undefined) parentPort.postMessage({ type: OS_THREAD_ID, osThreadId });
871
1154
  for (let i = 0, l = workerData.addPorts.length; i < l; i++) {
872
1155
  let port = workerData.addPorts[i];
873
1156
  port.threadId = workerData.addThreadIds[i];
@@ -916,17 +1199,23 @@ if (parentPort && workerData?.addPorts) {
916
1199
  }
917
1200
  });
918
1201
  let awaitTerminationRequestId = 0;
919
- awaitProcessGroupTermination = (ownerThreadId) =>
1202
+ awaitProcessGroupTermination = (ownerThreadId, signal) =>
920
1203
  new Promise((resolve) => {
921
1204
  // Deliberately no timeout: a contender must not reclaim a dead owner's lock while its
922
1205
  // process group might still be alive and mutating files, so this mirrors the unbounded
923
- // wait Application.ts's waitForConfirmedTermination uses for the same reason.
1206
+ // wait Application.ts's waitForConfirmedTermination uses for the same reason. A caller
1207
+ // that needs its own bound (isThreadRunning) passes `signal` so this listener still gets
1208
+ // torn down when that caller gives up, instead of leaking for the life of the worker.
924
1209
  const requestId = ++awaitTerminationRequestId;
925
1210
  parentPort.on('message', receiveConfirmation);
1211
+ signal?.addEventListener('abort', cleanup, { once: true });
926
1212
  parentPort.postMessage({ type: AWAIT_PROCESS_GROUP_TERMINATION, ownerThreadId, requestId });
1213
+ function cleanup() {
1214
+ parentPort.off('message', receiveConfirmation);
1215
+ }
927
1216
  function receiveConfirmation(message) {
928
1217
  if (message.type === PROCESS_GROUP_TERMINATION_CONFIRMED && message.requestId === requestId) {
929
- parentPort.off('message', receiveConfirmation);
1218
+ cleanup();
930
1219
  resolve();
931
1220
  }
932
1221
  }
@@ -959,33 +1248,142 @@ const processGroupsByThread = new Map();
959
1248
  // window component-preparation locking exists to close.
960
1249
  const pendingProcessGroupTerminations = new Map();
961
1250
  const PROCESS_GROUP_TERMINATION_POLL_MS = 25;
1251
+ const ZOMBIE_GROUP_MEMBER_SCAN_INTERVAL_MS = 1000;
1252
+ const PROCESS_GROUP_LIVENESS_WARNING_MS = 30000;
1253
+ // Bounds isThreadRunning's own wait (below); Application.ts's waitForConfirmedTermination stays
1254
+ // deliberately unbounded.
1255
+ const THREAD_RUNNING_TERMINATION_BACKSTOP_MS = PROCESS_GROUP_LIVENESS_WARNING_MS;
1256
+ const zombieGroupScanTimes = new Map();
1257
+ const processGroupLivenessStates = new Map();
1258
+
1259
+ function processGroupExists(processGroupId) {
1260
+ try {
1261
+ process.kill(-processGroupId, 0);
1262
+ return true;
1263
+ } catch (error) {
1264
+ return error.code === 'EPERM';
1265
+ }
1266
+ }
1267
+
1268
+ function processProbeError(error) {
1269
+ return error?.code ?? error?.message ?? String(error ?? 'unknown error');
1270
+ }
962
1271
 
963
- // A process-group leader spawned by a worker thread's own event loop is normally reaped by that
964
- // same loop when it dies. But the only caller of this check (waitForProcessGroupExit, for a
965
- // *dead* owner thread) runs after that loop is already gone, so nothing will ever reap it here:
966
- // kill(pid, 0) keeps succeeding forever against the zombie's still-allocated PID slot. A zombie
967
- // can no longer touch the filesystem, so treat a confirmed zombie as dead rather than poll
968
- // forever for a reap that will never come. Reproduced by killing a worker mid-install: the
969
- // installer's process-group leader becomes a permanent `Z` in /proc without this check.
970
- function isZombieProcessGroupLeader(processGroupId) {
971
- if (process.platform !== 'linux') return false;
1272
+ function processGroupLeaderState(processGroupId, platform, readStat) {
1273
+ if (platform !== 'linux') {
1274
+ return { state: 'unknown', reason: `zombie-process detection is unavailable on ${platform}` };
1275
+ }
972
1276
  let stat;
973
1277
  try {
974
- stat = readFileSync(`/proc/${processGroupId}/stat`, 'utf8');
975
- } catch {
976
- return false;
1278
+ stat = readStat(`/proc/${processGroupId}/stat`, 'utf8');
1279
+ } catch (error) {
1280
+ if (error.code === 'ENOENT' || error.code === 'ESRCH') return { state: 'missing' };
1281
+ return {
1282
+ state: 'unknown',
1283
+ reason: `reading /proc/${processGroupId}/stat failed (${processProbeError(error)})`,
1284
+ };
977
1285
  }
978
- // Format is "pid (comm) state ..."; comm can contain spaces/parens, so anchor on the last ')'.
979
- return stat[stat.lastIndexOf(')') + 2] === 'Z';
1286
+ const fields = stat.slice(stat.lastIndexOf(')') + 2).split(' ');
1287
+ if (Number(fields[2]) !== processGroupId) return { state: 'missing' };
1288
+ return { state: fields[0] === 'Z' ? 'zombie' : 'alive' };
980
1289
  }
981
1290
 
982
- function processGroupIsAlive(processGroupId) {
1291
+ function scanLinuxProcessGroup(processGroupId, readDirectory, readStat) {
1292
+ let processIds;
983
1293
  try {
984
- process.kill(-processGroupId, 0);
1294
+ processIds = readDirectory('/proc');
985
1295
  } catch (error) {
986
- return error.code === 'EPERM';
1296
+ return {
1297
+ isAlive: null,
1298
+ reason: `reading /proc failed (${processProbeError(error)})`,
1299
+ };
987
1300
  }
988
- return !isZombieProcessGroupLeader(processGroupId);
1301
+ for (const processId of processIds) {
1302
+ if (!/^\d+$/.test(processId)) continue;
1303
+ let stat;
1304
+ try {
1305
+ stat = readStat(`/proc/${processId}/stat`, 'utf8');
1306
+ } catch (error) {
1307
+ if (error.code === 'ENOENT' || error.code === 'ESRCH') continue; // already gone
1308
+ // EACCES/EPERM (e.g. hidepid/ProtectProc) can only happen on a pid we don't own — we
1309
+ // spawned our own group's members as this same user, so their stat is always readable —
1310
+ // so it's not one of ours. Anything else is genuinely unknown, not "not ours".
1311
+ if (error.code === 'EACCES' || error.code === 'EPERM') continue;
1312
+ return {
1313
+ isAlive: null,
1314
+ reason: `reading /proc/${processId}/stat failed (${processProbeError(error)})`,
1315
+ };
1316
+ }
1317
+ const fields = stat.slice(stat.lastIndexOf(')') + 2).split(' ');
1318
+ const pgrp = Number(fields[2]);
1319
+ if (!Number.isFinite(pgrp)) {
1320
+ return { isAlive: null, reason: `/proc/${processId}/stat had an unparseable process-group field` };
1321
+ }
1322
+ if (pgrp !== processGroupId) continue;
1323
+ if (fields[0] !== 'Z') return { isAlive: true, reason: `a live member was observed at pid ${processId}` };
1324
+ }
1325
+ return { isAlive: false };
1326
+ }
1327
+
1328
+ function keepProcessGroupAlive(processGroupId, reason, observationTime, warn) {
1329
+ let livenessState = processGroupLivenessStates.get(processGroupId);
1330
+ if (!livenessState) {
1331
+ livenessState = { observedAt: observationTime, reason, warned: false };
1332
+ processGroupLivenessStates.set(processGroupId, livenessState);
1333
+ } else {
1334
+ livenessState.reason = reason;
1335
+ }
1336
+ if (!livenessState.warned && observationTime - livenessState.observedAt >= PROCESS_GROUP_LIVENESS_WARNING_MS) {
1337
+ livenessState.warned = true;
1338
+ warn(
1339
+ `Process group ${processGroupId} termination remains unconfirmed after ${PROCESS_GROUP_LIVENESS_WARNING_MS}ms: ${livenessState.reason}`
1340
+ );
1341
+ }
1342
+ return true;
1343
+ }
1344
+
1345
+ function clearProcessGroupLivenessState(processGroupId) {
1346
+ zombieGroupScanTimes.delete(processGroupId);
1347
+ processGroupLivenessStates.delete(processGroupId);
1348
+ }
1349
+
1350
+ function isProcessGroupAlive(processGroupId, options) {
1351
+ const platform = options?.platform ?? process.platform;
1352
+ const groupExists = options?.processGroupExists ?? processGroupExists;
1353
+ const readDirectory = options?.readDirectory ?? readdirSync;
1354
+ const readStat = options?.readStat ?? readFileSync;
1355
+ const observationTime = options?.now?.() ?? performance.now();
1356
+ const warn = options?.warn ?? ((message) => harperLogger.warn(message));
1357
+ if (!groupExists(processGroupId)) {
1358
+ clearProcessGroupLivenessState(processGroupId);
1359
+ return false;
1360
+ }
1361
+ const leaderState = processGroupLeaderState(processGroupId, platform, readStat);
1362
+ if (leaderState.state === 'alive')
1363
+ return keepProcessGroupAlive(processGroupId, 'the process-group leader is still alive', observationTime, warn);
1364
+ if (leaderState.state === 'unknown')
1365
+ return keepProcessGroupAlive(processGroupId, leaderState.reason, observationTime, warn);
1366
+ const scanTime = observationTime;
1367
+ const lastScan = zombieGroupScanTimes.get(processGroupId);
1368
+ if (lastScan !== undefined && scanTime >= lastScan && scanTime - lastScan < ZOMBIE_GROUP_MEMBER_SCAN_INTERVAL_MS) {
1369
+ const reason = processGroupLivenessStates.get(processGroupId)?.reason ?? 'the previous Linux scan is still current';
1370
+ return keepProcessGroupAlive(processGroupId, reason, observationTime, warn);
1371
+ }
1372
+ zombieGroupScanTimes.set(processGroupId, scanTime);
1373
+ let scanResult = scanLinuxProcessGroup(processGroupId, readDirectory, readStat);
1374
+ if (scanResult.isAlive !== false)
1375
+ return keepProcessGroupAlive(processGroupId, scanResult.reason, observationTime, warn);
1376
+ // A second snapshot catches a member omitted after forking inside the first readdir/stat window.
1377
+ // It narrows rather than closes the race because the second synchronous pass has the same gap.
1378
+ scanResult = scanLinuxProcessGroup(processGroupId, readDirectory, readStat);
1379
+ if (scanResult.isAlive !== false)
1380
+ return keepProcessGroupAlive(processGroupId, scanResult.reason, observationTime, warn);
1381
+ clearProcessGroupLivenessState(processGroupId);
1382
+ return false;
1383
+ }
1384
+
1385
+ function processGroupIsAlive(processGroupId) {
1386
+ return isProcessGroupAlive(processGroupId);
989
1387
  }
990
1388
 
991
1389
  async function waitForProcessGroupExit(processGroupId) {
@@ -1067,6 +1465,7 @@ function addProcessGroup(ownerThreadId, processGroupId) {
1067
1465
  }
1068
1466
 
1069
1467
  function removeProcessGroup(ownerThreadId, processGroupId) {
1468
+ clearProcessGroupLivenessState(processGroupId);
1070
1469
  const processGroups = processGroupsByThread.get(ownerThreadId);
1071
1470
  if (!processGroups) return;
1072
1471
  processGroups.delete(processGroupId);
@@ -1118,12 +1517,48 @@ function unregisterProcessGroup(processGroupId) {
1118
1517
  else parentPort?.postMessage({ type: UNREGISTER_PROCESS_GROUP, processGroupId });
1119
1518
  }
1120
1519
 
1520
+ class ProcessGroupTerminationUnconfirmedError extends Error {
1521
+ code = 'ERR_PROCESS_GROUP_TERMINATION_UNCONFIRMED';
1522
+ constructor(ownerThreadId, timeoutMs) {
1523
+ super(`Thread ${ownerThreadId}'s process-group termination could not be confirmed after ${timeoutMs}ms`);
1524
+ this.name = 'ProcessGroupTerminationUnconfirmedError';
1525
+ }
1526
+ }
1527
+
1528
+ // Bounds awaitProcessGroupTermination for isThreadRunning below. Throws rather than resolving to
1529
+ // "not running": componentPreparationLock's ownerIsAlive/ownerLivenessConfirmed already treat a
1530
+ // throwing isOwnerAlive as "can't confirm — don't steal the claim, don't renew the deadline", so
1531
+ // the lock's own bounded wait is what eventually fails the waiter.
1532
+ async function awaitConfirmedProcessGroupTermination(ownerThreadId) {
1533
+ const abortController = new AbortController();
1534
+ let timedOut = false;
1535
+ const timer = setTimeout(() => {
1536
+ timedOut = true;
1537
+ abortController.abort();
1538
+ }, THREAD_RUNNING_TERMINATION_BACKSTOP_MS);
1539
+ timer.unref?.();
1540
+ try {
1541
+ await Promise.race([
1542
+ awaitProcessGroupTermination(ownerThreadId, abortController.signal),
1543
+ new Promise((resolve) => abortController.signal.addEventListener('abort', resolve, { once: true })),
1544
+ ]);
1545
+ } finally {
1546
+ clearTimeout(timer);
1547
+ }
1548
+ if (timedOut) {
1549
+ harperLogger.warn(
1550
+ `Thread ${ownerThreadId}'s process-group termination could not be confirmed after ${THREAD_RUNNING_TERMINATION_BACKSTOP_MS}ms`
1551
+ );
1552
+ throw new ProcessGroupTerminationUnconfirmedError(ownerThreadId, THREAD_RUNNING_TERMINATION_BACKSTOP_MS);
1553
+ }
1554
+ }
1555
+
1121
1556
  async function isThreadRunning(ownerThreadId, timeoutMs = THREAD_INFO_REQUEST_TIMEOUT_MS) {
1122
1557
  if (ownerThreadId === threadId || ownerThreadId === 0) return true;
1123
1558
  if ((await getThreadInfo(timeoutMs)).some((worker) => worker.threadId === ownerThreadId)) return true;
1124
1559
  // The thread itself is gone, but it may still own process groups whose forced termination is
1125
1560
  // in flight — wait for that to be confirmed before reporting the owner as reclaimable.
1126
- await awaitProcessGroupTermination(ownerThreadId);
1561
+ await awaitConfirmedProcessGroupTermination(ownerThreadId);
1127
1562
  return false;
1128
1563
  }
1129
1564
 
@@ -1133,6 +1568,15 @@ if (isMainThread) {
1133
1568
  });
1134
1569
  }
1135
1570
 
1571
+ /**
1572
+ * Whether a thread has already been reported dead. Sync, unlike `isThreadRunning`, because it only
1573
+ * reads the tombstone `notifyThreadExit` records below — callers on the exit path need an answer
1574
+ * without awaiting process-group confirmation.
1575
+ */
1576
+ function hasThreadExited(threadId) {
1577
+ return notifiedDeadThreadIds.has(threadId);
1578
+ }
1579
+
1136
1580
  function notifyThreadExit(deadThreadId) {
1137
1581
  if (deadThreadId == null || notifiedDeadThreadIds.has(deadThreadId)) return;
1138
1582
  notifiedDeadThreadIds.add(deadThreadId);
@@ -1240,23 +1684,49 @@ if (isMainThread) {
1240
1684
  const ignoredPaths = ['node_modules', '.git'];
1241
1685
  const watchDir = async (dir, beforeRestartCallback) => {
1242
1686
  if (beforeRestartCallback) beforeRestart = beforeRestartCallback;
1243
- chokidar
1244
- .watch(dir, {
1687
+ const watchTarget = resolveWatchTarget(dir);
1688
+ let usingPolling = watchTarget.mustPoll;
1689
+ let liveWatcher;
1690
+ const openWatcher = () => {
1691
+ const opened = (liveWatcher = guardedWatch(watchTarget.path, {
1245
1692
  persistent: false,
1693
+ ...(usingPolling ? DIRECTORY_POLLING_FALLBACK_OPTIONS : {}),
1246
1694
  ignored: (path) => {
1247
1695
  return ignoredPaths.some((ignoredPath) => path.includes(ignoredPath));
1248
1696
  },
1249
- })
1250
- .on('change', (path) => {
1251
- changedFiles.add(path);
1252
- if (queuedRestart) clearTimeout(queuedRestart);
1253
- queuedRestart = setTimeout(async () => {
1254
- if (beforeRestart) await beforeRestart();
1255
- await restartWorkers();
1256
- console.log('Reloaded Harper components, changed files:', Array.from(changedFiles));
1257
- changedFiles.clear();
1258
- }, 100);
1259
- });
1697
+ }));
1698
+ opened
1699
+ // This runs on the thread that owns every worker, and chokidar emits 'error' unguarded for
1700
+ // anything but ENOENT/ENOTDIR.
1701
+ .on('error', (error) => {
1702
+ if (claimLostNativeWatchError(error)) return;
1703
+ if (isWatcherExhaustionError(error)) {
1704
+ if (usingPolling || liveWatcher !== opened) return;
1705
+ warnWatcherFallback(dir);
1706
+ usingPolling = true;
1707
+ Promise.resolve()
1708
+ .then(() => opened.close())
1709
+ .catch(() => {})
1710
+ .then(openWatcher)
1711
+ .catch((reopenError) =>
1712
+ console.error(`Could not reopen the ${dir} component-reload watch on polling:`, reopenError)
1713
+ );
1714
+ return;
1715
+ }
1716
+ console.error(`Error watching ${dir} for component reloads:`, error);
1717
+ })
1718
+ .on('change', (path) => {
1719
+ changedFiles.add(path);
1720
+ if (queuedRestart) clearTimeout(queuedRestart);
1721
+ queuedRestart = setTimeout(async () => {
1722
+ if (beforeRestart) await beforeRestart();
1723
+ await restartWorkers();
1724
+ console.log('Reloaded Harper components, changed files:', Array.from(changedFiles));
1725
+ changedFiles.clear();
1726
+ }, 100);
1727
+ });
1728
+ };
1729
+ openWatcher();
1260
1730
  };
1261
1731
  module.exports.watchDir = watchDir;
1262
1732
  if (process.env.WATCH_DIR) watchDir(process.env.WATCH_DIR);