@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
@@ -5,7 +5,7 @@
5
5
  const { realExit } = require("./workerProcessGuard.js");
6
6
  const { Worker, MessageChannel, parentPort, isMainThread, threadId, workerData } = require('worker_threads');
7
7
  const { spawn, spawnSync } = require('node:child_process');
8
- const { readFileSync } = require('node:fs');
8
+ const { readdirSync, readFileSync, readlinkSync } = require('node:fs');
9
9
  const { setTimeout: delay } = require('node:timers/promises');
10
10
  const { join, isAbsolute, extname } = require('path');
11
11
  const { pathToFileURL } = require('url');
@@ -19,7 +19,10 @@ const { randomBytes } = require('crypto');
19
19
  const { _assignPackageExport } = require('../../globals.js');
20
20
  const { PACKAGE_ROOT } = require('../../utility/packageUtils.js');
21
21
  const { resolvePreloadModules } = require("./resolvePreload.js");
22
+ const { resolveThreadHeapMemoryMb } = require("./threadHeapMemory.js");
22
23
  const { getConfigPath } = require("../../config/configUtils.js");
24
+ const { resolveWatchTarget } = require("../../utility/watchPath.js");
25
+ const { DIRECTORY_POLLING_FALLBACK_OPTIONS, claimLostNativeWatchError, guardedWatch, isWatcherExhaustionError, warnWatcherFallback, } = require("../../utility/watcherFallback.js");
23
26
  let importModules;
24
27
  function getImportModules() {
25
28
  if (importModules === undefined)
@@ -32,7 +35,6 @@ function getRequireModules() {
32
35
  requireModules = resolvePreloadModules(envMgr.get(hdbTerms.CONFIG_PARAMS.THREADS_PRELOADREQUIRE), getConfigPath(hdbTerms.CONFIG_PARAMS.COMPONENTSROOT), 'threads.preloadRequire');
33
36
  return requireModules;
34
37
  }
35
- const chokidar = require('chokidar');
36
38
  const isBun = typeof globalThis.Bun !== 'undefined';
37
39
  const MB = 1024 * 1024;
38
40
  const workers = []; // these are our child workers that we are managing
@@ -44,8 +46,11 @@ const MAX_UNEXPECTED_RESTARTS = 50;
44
46
  // down mid-disposal crashes the process, so give that teardown more room before the forced backstop.
45
47
  let threadTerminationTimeout = process.env.DEV_MODE === 'true' || process.env.DEV_MODE === '1' ? 30000 : 10000;
46
48
  const RESTART_TYPE = 'restart';
49
+ const RESTART_PROGRESS_HEARTBEAT_MS = 15000;
47
50
  const REQUEST_THREAD_INFO = 'request_thread_info';
48
51
  const RESOURCE_REPORT = 'resource_report';
52
+ const OS_THREAD_ID = 'os-thread-id';
53
+ const STUCK_WORKER_REPORT = 'stuck-worker-report';
49
54
  const THREAD_INFO = 'thread_info';
50
55
  const ADDED_PORT = 'added-port';
51
56
  const ACKNOWLEDGEMENT = 'ack';
@@ -141,8 +146,11 @@ module.exports = {
141
146
  beginProcessShutdown,
142
147
  registerWorkerDataProvider,
143
148
  onThreadExit,
149
+ hasThreadExited,
150
+ notifyThreadExit,
144
151
  registerProcessGroup,
145
152
  unregisterProcessGroup,
153
+ isProcessGroupAlive,
146
154
  isThreadRunning,
147
155
  waitUntilConfirmedGone,
148
156
  restartNumber: workerData?.restartNumber || 1,
@@ -293,6 +301,17 @@ if (!parentPort) {
293
301
  if (worker)
294
302
  recordResourceReport(worker, message);
295
303
  });
304
+ onMessageByType(OS_THREAD_ID, (message, worker) => {
305
+ if (worker)
306
+ worker.osThreadId = message.osThreadId;
307
+ });
308
+ onMessageByType(STUCK_WORKER_REPORT, (message) => {
309
+ for (const threadId of message.threadIds) {
310
+ const worker = workers.find((worker) => worker.threadId === threadId);
311
+ if (worker)
312
+ logStuckWorkerDiagnostics(worker);
313
+ }
314
+ });
296
315
  onMessageByType(AWAIT_PROCESS_GROUP_TERMINATION, async (message, worker) => {
297
316
  if (!worker)
298
317
  return;
@@ -305,6 +324,7 @@ if (!parentPort) {
305
324
  }
306
325
  // postMessage type listeners that are registered in other ways or can be registered later
307
326
  listenersByType.set(hdbTerms.ITC_EVENT_TYPES.CHILD_STARTED, null);
327
+ listenersByType.set(hdbTerms.ITC_EVENT_TYPES.CHILD_STARTUP_PHASE, null);
308
328
  listenersByType.set(hdbTerms.ITC_EVENT_TYPES.SCHEMA, null);
309
329
  listenersByType.set(hdbTerms.ITC_EVENT_TYPES.USER, null);
310
330
  listenersByType.set(hdbTerms.ITC_EVENT_TYPES.COMPONENT_STATUS_REQUEST, null);
@@ -340,7 +360,7 @@ function startWorker(path, options = {}) {
340
360
  let availableMemory = process.constrainedMemory?.() || totalmem(); // used constrained memory if it is available
341
361
  // and lower than total memory
342
362
  availableMemory = Math.min(availableMemory, totalmem(), 20000 * MB);
343
- const maxOldMemory = envMgr.get(hdbTerms.CONFIG_PARAMS.THREADS_MAXHEAPMEMORY) ??
363
+ const maxOldMemory = resolveThreadHeapMemoryMb(envMgr.get(hdbTerms.CONFIG_PARAMS.THREADS_MAXHEAPMEMORY)) ??
344
364
  Math.max(Math.floor(availableMemory / MB / (10 + (options.threadCount || 1) / 4)), 512);
345
365
  // Max young memory space (semi-space for scavenger) is 1/128 of max memory (limited to 16-64). For most of our m5
346
366
  // machines this will be 64MB (less for t3's). This is based on recommendations from:
@@ -438,8 +458,10 @@ function startWorker(path, options = {}) {
438
458
  options.unexpectedRestarts = worker.unexpectedRestarts + 1;
439
459
  startWorker(path, options);
440
460
  }
441
- else
442
- harperLogger.error(`Thread has been restarted ${worker.restarts} times and will not be restarted`);
461
+ else {
462
+ harperLogger.error(`Thread has been restarted ${worker.unexpectedRestarts} times and will not be restarted`);
463
+ options.onRestartExhausted?.(worker);
464
+ }
443
465
  }
444
466
  });
445
467
  workers.push(worker);
@@ -458,13 +480,20 @@ const OVERLAPPING_RESTART_TYPES = [hdbTerms.THREAD_TYPES.HTTP];
458
480
  * threads at the same time we shutdown new ones. However, we usually want to limit how many we do at once to avoid
459
481
  * excessive load and to keep things responsive. This parameter throttles the restarts to minimize load from
460
482
  * thread startups.
461
- * @returns {Promise<void>}
483
+ * @param onProgress Called each time a worker has been replaced (or its replacement has been given
484
+ * up on), so a caller waiting on a wide pool can tell a slow restart from a stalled one.
485
+ * @returns {Promise<{workersKeptOnOldCode: number, replacementsNotStarted: number}|{declined: true}|undefined>}
486
+ * from the main thread, how many workers were left running the old code and how many replacements
487
+ * never reported that they started — or `{declined: true}` when the process is already shutting down;
488
+ * from a worker, nothing — the restart is handed to the main thread.
462
489
  */
463
490
  async function restartWorkers(name = null, maxWorkersDown = Math.max(Math.floor(workerCount / 8), 1), // restart 1/8 of the threads at a time, but at least 1
464
- startReplacementThreads = true) {
491
+ startReplacementThreads = true, onProgress = null) {
465
492
  if (isMainThread) {
493
+ // Declining is not the same as delegating: a caller reporting on the restart must not read this
494
+ // as "another thread is completing it".
466
495
  if (processShuttingDown && startReplacementThreads)
467
- return;
496
+ return { declined: true };
468
497
  try {
469
498
  // we do this because it is possible for a component to chdir to itself, get re-deployed and then the cwd
470
499
  // inode link is invalid and it can cause a lot of problems. But process.cwd() still returns the path, for
@@ -480,7 +509,17 @@ startReplacementThreads = true) {
480
509
  // This is here to prevent circular dependencies
481
510
  if (startReplacementThreads) {
482
511
  const { loadRootComponents } = require('../loadRootComponents.js');
483
- await loadRootComponents();
512
+ // Installing and loading every root component reports nothing and can outlast a caller's idle
513
+ // window on its own (a cold npm cache, a large dependency graph), so beat while it runs. The
514
+ // caller's absolute ceiling is what bounds a load that never finishes.
515
+ const loading = setInterval(() => onProgress?.(), RESTART_PROGRESS_HEARTBEAT_MS).unref();
516
+ try {
517
+ await loadRootComponents();
518
+ }
519
+ finally {
520
+ clearInterval(loading);
521
+ }
522
+ onProgress?.();
484
523
  }
485
524
  module.exports.restartNumber++;
486
525
  if (maxWorkersDown < 1) {
@@ -489,7 +528,17 @@ startReplacementThreads = true) {
489
528
  maxWorkersDown = maxWorkersDown * workers.length;
490
529
  }
491
530
  // make a copy of the workers before iterating them, as the workers array mutates a lot during this
492
- let waitingToFinish = []; // promises for workers we have shut down and are waiting to exit
531
+ let waitingToFinish = []; // promises for workers we are replacing, spliced as each is replaced
532
+ // Every replacement that was started without being awaited first, so this function can still
533
+ // resolve only once each one is accepting connections.
534
+ let replacementsStarting = [];
535
+ // A replacement that never came up leaves the pool in one of two very different states, and a
536
+ // caller waiting on this restart needs them apart: the pre-start path keeps the *old* worker
537
+ // serving old code, while a replacement that exits after its predecessor is already gone only
538
+ // costs capacity until startWorker's auto-restart brings a fresh one up.
539
+ let workersKeptOnOldCode = 0;
540
+ let replacementsNotStarted = 0;
541
+ let replacementsFailedToStart = 0;
493
542
  // We can only start the replacement *before* the old worker releases its port when the OS lets
494
543
  // both listen on the same port at once (SO_REUSEPORT). Without that — Windows (no SO_REUSEPORT),
495
544
  // macOS (unreliable SO_REUSEPORT, so workers bind exclusively), and Bun — the replacement can't
@@ -501,7 +550,9 @@ startReplacementThreads = true) {
501
550
  // thread keeps serving the HTTP ports throughout. This ordering is also what lets
502
551
  // listenOnPorts() treat a dedicated listener's EADDRINUSE as an external conflict.
503
552
  const canPreStartReplacement = process.platform !== 'win32' && process.platform !== 'darwin' && !isBun;
504
- for (let worker of workers.slice(0)) {
553
+ const restarting = workers.slice(0);
554
+ for (let index = 0; index < restarting.length; index++) {
555
+ const worker = restarting[index];
505
556
  // Terminal shutdown: stop replacing workers mid-loop — the guard for every replacement start below.
506
557
  if (processShuttingDown && startReplacementThreads)
507
558
  break;
@@ -562,6 +613,8 @@ startReplacementThreads = true) {
562
613
  // protection if it is still alive (it may have exited on its own during the wait).
563
614
  if (workers.includes(worker))
564
615
  worker.wasShutdown = false;
616
+ workersKeptOnOldCode++;
617
+ onProgress?.();
565
618
  continue;
566
619
  }
567
620
  }
@@ -584,8 +637,16 @@ startReplacementThreads = true) {
584
637
  // Overlapping types we couldn't pre-start (Windows/Bun): start the replacement now that the old
585
638
  // worker is releasing its port. server.close() stops accepting immediately, so the port frees up
586
639
  // well before the replacement finishes booting and binds.
587
- if (overlapping && startReplacementThreads && !canPreStartReplacement && !processShuttingDown)
588
- worker.startCopy();
640
+ let replacementStarting;
641
+ if (overlapping && startReplacementThreads && !canPreStartReplacement && !processShuttingDown) {
642
+ replacementStarting = whenWorkerStarted(worker.startCopy()).then((started) => {
643
+ if (!started)
644
+ replacementsFailedToStart++;
645
+ onProgress?.();
646
+ return started;
647
+ });
648
+ replacementsStarting.push(replacementStarting);
649
+ }
589
650
  let whenDone = new Promise((resolve) => {
590
651
  // in case the exit inside the thread doesn't timeout, force it from the outside
591
652
  const armTerminate = (delay) => setTimeout(() => {
@@ -613,29 +674,57 @@ startReplacementThreads = true) {
613
674
  // buggy/rogue message can't defer the force-kill unboundedly; a shrink (drain-done reset)
614
675
  // passes through untouched. See boundedTerminateDelay for the arithmetic + its unit tests.
615
676
  const { boundedTerminateDelay, getShutdownDrainCeilingMs } = require("../../components/shutdownDrain.js");
616
- timeout = armTerminate(boundedTerminateDelay(deadlineMs, Date.now(), threadTerminationTimeout * 2, getShutdownDrainCeilingMs()));
677
+ const delay = boundedTerminateDelay(deadlineMs, Date.now(), threadTerminationTimeout * 2, getShutdownDrainCeilingMs());
678
+ timeout = armTerminate(delay);
679
+ // The worker is telling us it has work still moving and how long it may take, so pass that
680
+ // on: a caller waiting on the restart must not treat a live drain as a stalled one.
681
+ onProgress?.(Date.now() + delay);
617
682
  };
618
683
  worker.on('exit', () => {
619
684
  clearTimeout(timeout);
685
+ onProgress?.();
620
686
  worker.extendTerminateDeadline = undefined;
621
- const index = waitingToFinish.indexOf(whenDone);
622
- if (index > -1)
623
- waitingToFinish.splice(index, 1);
624
687
  // non-overlapping types have no advance replacement, so start it once the old one is gone
625
688
  if (!overlapping && startReplacementThreads && !processShuttingDown)
626
689
  worker.startCopy();
627
690
  resolve();
628
691
  });
629
692
  });
630
- waitingToFinish.push(whenDone);
693
+ // A worker counts as replaced only once its replacement is accepting connections, not merely
694
+ // once it has exited. This promise is held unawaited between throttle points, so it must not
695
+ // be able to reject.
696
+ const replaced = (replacementStarting ? Promise.all([whenDone, replacementStarting]) : whenDone)
697
+ .catch((error) => harperLogger.warn('Error waiting for a worker to be replaced', error))
698
+ .then(() => {
699
+ const at = waitingToFinish.indexOf(replaced);
700
+ if (at > -1)
701
+ waitingToFinish.splice(at, 1);
702
+ });
703
+ waitingToFinish.push(replaced);
631
704
  if (waitingToFinish.length >= maxWorkersDown) {
632
- // throttle how many workers are draining/down at once to limit load
705
+ // throttle how many workers are down at once to limit load
633
706
  await Promise.race(waitingToFinish);
634
707
  }
708
+ // Readiness throttling bounds how many workers are down at once, but not how many *fail*: with
709
+ // replacements that never come up, walking the rest of the pool would leave nothing serving.
710
+ // The workers not yet touched are still running the old code, which beats none running at all.
711
+ if (replacementsFailedToStart >= maxWorkersDown) {
712
+ const untouched = restarting
713
+ .slice(index + 1)
714
+ // a worker that exited on its own mid-restart is spliced out of `workers` and
715
+ // auto-restarted onto the new code (see the exit handler above); it is not still on
716
+ // the previous code even though this loop never got to it.
717
+ .filter((other) => (!name || other.name === name) && !other.wasShutdown && workers.includes(other)).length;
718
+ harperLogger.error(`${replacementsFailedToStart} replacement worker thread(s) did not start; stopping this restart with ${untouched} worker(s) still on the previous code`);
719
+ workersKeptOnOldCode += untouched;
720
+ break;
721
+ }
635
722
  }
636
- // seems appropriate to wait for this to finish, but the API doesn't actually wait for this function
637
- // to finish, so not that important
638
723
  await Promise.all(waitingToFinish);
724
+ // A caller awaiting this needs it to mean "the pool is serving the new code", so wait out the
725
+ // replacements that could only be started once their predecessor released its exclusive ports.
726
+ replacementsNotStarted = (await Promise.all(replacementsStarting)).filter((started) => !started).length;
727
+ return { workersKeptOnOldCode, replacementsNotStarted };
639
728
  }
640
729
  else {
641
730
  parentPort.postMessage({
@@ -644,6 +733,51 @@ startReplacementThreads = true) {
644
733
  });
645
734
  }
646
735
  }
736
+ /**
737
+ * Resolve once a newly started worker reports that it is accepting connections, or gives up on it.
738
+ * There is no old worker left to fall back on here, so a replacement that fails to start is left to
739
+ * startWorker's own auto-restart handling; this only stops waiting on it.
740
+ * @param newWorker The replacement worker
741
+ * @returns {Promise<boolean>} whether the worker reported that it started
742
+ */
743
+ function whenWorkerStarted(newWorker) {
744
+ return new Promise((resolve) => {
745
+ const cleanup = () => {
746
+ clearTimeout(timeout);
747
+ newWorker.off('message', startListener);
748
+ newWorker.off('exit', exitListener);
749
+ };
750
+ const timeout = setTimeout(() => {
751
+ harperLogger.error('Replacement worker did not start in time', newWorker.threadId);
752
+ cleanup();
753
+ // Its predecessor is already gone, so a replacement wedged in boot is a worker slot serving
754
+ // nothing until the process restarts. Stop it and let startWorker's exit handling replace it.
755
+ if (isBun) {
756
+ // terminate() triggers a NAPI segfault in Bun; ask the worker to self-exit instead.
757
+ try {
758
+ newWorker.postMessage({ type: FORCE_EXIT });
759
+ }
760
+ catch { }
761
+ }
762
+ else
763
+ newWorker.terminate();
764
+ resolve(false);
765
+ }, Math.max(threadTerminationTimeout * 2, 60000)).unref();
766
+ const startListener = (message) => {
767
+ if (message.type === hdbTerms.ITC_EVENT_TYPES.CHILD_STARTED) {
768
+ cleanup();
769
+ resolve(true);
770
+ }
771
+ };
772
+ const exitListener = () => {
773
+ harperLogger.warn('Replacement worker exited before starting', newWorker.threadId);
774
+ cleanup();
775
+ resolve(false);
776
+ };
777
+ newWorker.on('message', startListener);
778
+ newWorker.on('exit', exitListener);
779
+ });
780
+ }
647
781
  function shutdownWorkers(name) {
648
782
  return restartWorkers(name, Infinity, false);
649
783
  }
@@ -777,15 +911,146 @@ function broadcastWithAcknowledgement(message, timeout = DEFAULT_ACK_TIMEOUT_MS)
777
911
  timer = undefined;
778
912
  const stuck = [];
779
913
  for (let ackHandler of [...pending]) {
780
- stuck.push(ackHandler.port?.threadId);
914
+ stuck.push(ackHandler.port);
781
915
  ackHandler(); // same cleanup path as an ack/close; drives waitingCount to 0 and resolves
782
916
  }
783
- harperLogger.warn(`ITC broadcast (type ${message.type}) not acknowledged by worker thread(s) ${stuck.join(', ')} within ${timeout}ms; proceeding best-effort`);
917
+ harperLogger.warn(`ITC broadcast (type ${message.type}) not acknowledged by worker thread(s) ${stuck.map((port) => port?.threadId).join(', ')} within ${timeout}ms; proceeding best-effort`);
918
+ if (isMainThread)
919
+ for (let port of stuck)
920
+ logStuckWorkerDiagnostics(port);
921
+ else if (parentPort) {
922
+ // Only main holds the Worker objects (and tids), so a worker-originated timeout is sampled there.
923
+ const threadIds = stuck.map((port) => port?.threadId).filter((threadId) => threadId > 0);
924
+ if (threadIds.length > 0)
925
+ parentPort.postMessage({ type: STUCK_WORKER_REPORT, threadIds });
926
+ }
784
927
  }, timeout);
785
928
  timer.unref?.();
786
929
  }
787
930
  });
788
931
  }
932
+ // Linux only: /proc/thread-self resolves to <pid>/task/<tid> for the calling thread.
933
+ function getOsThreadId() {
934
+ try {
935
+ const tid = Number(readlinkSync('/proc/thread-self').split('/').pop());
936
+ return Number.isInteger(tid) && tid > 0 ? tid : undefined;
937
+ }
938
+ catch {
939
+ return undefined;
940
+ }
941
+ }
942
+ function readTaskFile(tid, name) {
943
+ try {
944
+ return readFileSync(`/proc/self/task/${tid}/${name}`, 'utf8').trim();
945
+ }
946
+ catch {
947
+ return undefined;
948
+ }
949
+ }
950
+ // Kernel-side view of one thread. Every field is best-effort and reported individually, since
951
+ // wchan/syscall need ptrace read access that a hardened container may deny while stat is open.
952
+ function finiteOrUndefined(value) {
953
+ const number = Number(value);
954
+ return Number.isFinite(number) ? number : undefined;
955
+ }
956
+ function readOsThreadState(tid) {
957
+ const thread = { tid };
958
+ const stat = readTaskFile(tid, 'stat');
959
+ if (stat !== undefined) {
960
+ // Fields after the parenthesized comm, so state is [0], utime/stime [11]/[12], starttime [19].
961
+ const fields = stat.slice(stat.lastIndexOf(')') + 2).split(' ');
962
+ thread.state = fields[0];
963
+ const utime = finiteOrUndefined(fields[11]);
964
+ const stime = finiteOrUndefined(fields[12]);
965
+ if (utime !== undefined && stime !== undefined)
966
+ thread.cpuTicks = utime + stime;
967
+ thread.startTime = fields[19];
968
+ }
969
+ const wchan = readTaskFile(tid, 'wchan');
970
+ if (wchan !== undefined)
971
+ thread.wchan = wchan;
972
+ // First token only: the syscall number, "running", or -1 (blocked outside a syscall). The
973
+ // remainder is argument registers and the stack/instruction pointers, which don't belong in a log.
974
+ const syscall = readTaskFile(tid, 'syscall');
975
+ if (syscall !== undefined)
976
+ thread.syscall = syscall.split(' ')[0];
977
+ const status = readTaskFile(tid, 'status');
978
+ if (status !== undefined) {
979
+ thread.voluntarySwitches = finiteOrUndefined(/^voluntary_ctxt_switches:\s*(\d+)/m.exec(status)?.[1]);
980
+ thread.nonvoluntarySwitches = finiteOrUndefined(/^nonvoluntary_ctxt_switches:\s*(\d+)/m.exec(status)?.[1]);
981
+ }
982
+ return thread;
983
+ }
984
+ function snapshotWorkerThread(worker) {
985
+ const snapshot = { at: Date.now() };
986
+ if (worker.resources?.updated)
987
+ snapshot.sinceResourceReport = snapshot.at - worker.resources.updated;
988
+ const eventLoop = worker.performance?.eventLoopUtilization?.();
989
+ if (eventLoop)
990
+ snapshot.eventLoop = { idle: eventLoop.idle, active: eventLoop.active };
991
+ if (worker.osThreadId)
992
+ snapshot.osThread = readOsThreadState(worker.osThreadId);
993
+ return snapshot;
994
+ }
995
+ function describeThreadState(thread) {
996
+ return `state=${thread.state ?? '?'} wchan=${thread.wchan ?? '?'} syscall=${thread.syscall ?? '?'}`;
997
+ }
998
+ function describeSnapshot(snapshot) {
999
+ const parts = [
1000
+ snapshot.sinceResourceReport === undefined
1001
+ ? 'no resource report received'
1002
+ : `last resource report ${snapshot.sinceResourceReport}ms ago`,
1003
+ ];
1004
+ if (snapshot.eventLoop)
1005
+ parts.push(`event loop active ${Math.round(snapshot.eventLoop.active)}ms idle ${Math.round(snapshot.eventLoop.idle)}ms`);
1006
+ const thread = snapshot.osThread;
1007
+ if (!thread)
1008
+ parts.push('os thread state unavailable');
1009
+ else
1010
+ parts.push(`os tid ${thread.tid} ${describeThreadState(thread)} cpuTicks=${thread.cpuTicks ?? '?'} ctxtSwitches=${thread.voluntarySwitches ?? '?'}/${thread.nonvoluntarySwitches ?? '?'}`);
1011
+ return parts.join('; ');
1012
+ }
1013
+ function describeDelta(before, after) {
1014
+ return Number.isFinite(before) && Number.isFinite(after) ? `+${after - before}` : '?';
1015
+ }
1016
+ function describeProgress(first, second) {
1017
+ const parts = [];
1018
+ if (first.eventLoop && second.eventLoop)
1019
+ parts.push(`event loop active +${Math.round(second.eventLoop.active - first.eventLoop.active)}ms idle +${Math.round(second.eventLoop.idle - first.eventLoop.idle)}ms`);
1020
+ const before = first.osThread;
1021
+ const after = second.osThread;
1022
+ if (before && after)
1023
+ parts.push(`cpuTicks ${describeDelta(before.cpuTicks, after.cpuTicks)} ctxtSwitches ${describeDelta(before.voluntarySwitches, after.voluntarySwitches)}/${describeDelta(before.nonvoluntarySwitches, after.nonvoluntarySwitches)} ${describeThreadState(after)}`);
1024
+ return parts.join('; ');
1025
+ }
1026
+ // A blocked event loop cannot report itself; two kernel-state samples a second apart separate
1027
+ // "parked on a lock" (no CPU ticks, no context switches) from "spinning".
1028
+ const STUCK_WORKER_SAMPLE_INTERVAL_MS = 1000;
1029
+ const STUCK_WORKER_DIAGNOSTIC_COOLDOWN_MS = 30000;
1030
+ function logStuckWorkerDiagnostics(worker) {
1031
+ if (!worker || !workers.includes(worker))
1032
+ return;
1033
+ const now = Date.now();
1034
+ if (worker.stuckDiagnosticAt !== undefined && now - worker.stuckDiagnosticAt < STUCK_WORKER_DIAGNOSTIC_COOLDOWN_MS)
1035
+ return;
1036
+ worker.stuckDiagnosticAt = now;
1037
+ const threadId = worker.threadId; // Node resets it to -1 once the worker exits
1038
+ const first = snapshotWorkerThread(worker);
1039
+ harperLogger.warn(`Worker thread ${threadId} at ack timeout: ${describeSnapshot(first)}`);
1040
+ if (!first.eventLoop && !first.osThread)
1041
+ return;
1042
+ setTimeout(() => {
1043
+ if (!workers.includes(worker)) {
1044
+ harperLogger.warn(`Worker thread ${threadId} exited before its follow-up sample`);
1045
+ return;
1046
+ }
1047
+ const second = snapshotWorkerThread(worker);
1048
+ // A recycled tid after a thread exit would attribute another thread's activity to this worker.
1049
+ if (second.osThread && second.osThread.startTime !== first.osThread?.startTime)
1050
+ second.osThread = undefined;
1051
+ harperLogger.warn(`Worker thread ${threadId} over the next ${second.at - first.at}ms: ${describeProgress(first, second) || 'no further state available'}`);
1052
+ }, STUCK_WORKER_SAMPLE_INTERVAL_MS).unref();
1053
+ }
789
1054
  function sendThreadInfo(targetWorker) {
790
1055
  targetWorker.postMessage({
791
1056
  type: THREAD_INFO,
@@ -857,6 +1122,9 @@ if (parentPort && workerData?.addPorts) {
857
1122
  // parentPort so sendToThread(0, ...) and similar lookups can route back to main.
858
1123
  parentPort.threadId = 0;
859
1124
  addPort(parentPort);
1125
+ const osThreadId = getOsThreadId();
1126
+ if (osThreadId !== undefined)
1127
+ parentPort.postMessage({ type: OS_THREAD_ID, osThreadId });
860
1128
  for (let i = 0, l = workerData.addPorts.length; i < l; i++) {
861
1129
  let port = workerData.addPorts[i];
862
1130
  port.threadId = workerData.addThreadIds[i];
@@ -906,16 +1174,22 @@ if (parentPort && workerData?.addPorts) {
906
1174
  }
907
1175
  });
908
1176
  let awaitTerminationRequestId = 0;
909
- awaitProcessGroupTermination = (ownerThreadId) => new Promise((resolve) => {
1177
+ awaitProcessGroupTermination = (ownerThreadId, signal) => new Promise((resolve) => {
910
1178
  // Deliberately no timeout: a contender must not reclaim a dead owner's lock while its
911
1179
  // process group might still be alive and mutating files, so this mirrors the unbounded
912
- // wait Application.ts's waitForConfirmedTermination uses for the same reason.
1180
+ // wait Application.ts's waitForConfirmedTermination uses for the same reason. A caller
1181
+ // that needs its own bound (isThreadRunning) passes `signal` so this listener still gets
1182
+ // torn down when that caller gives up, instead of leaking for the life of the worker.
913
1183
  const requestId = ++awaitTerminationRequestId;
914
1184
  parentPort.on('message', receiveConfirmation);
1185
+ signal?.addEventListener('abort', cleanup, { once: true });
915
1186
  parentPort.postMessage({ type: AWAIT_PROCESS_GROUP_TERMINATION, ownerThreadId, requestId });
1187
+ function cleanup() {
1188
+ parentPort.off('message', receiveConfirmation);
1189
+ }
916
1190
  function receiveConfirmation(message) {
917
1191
  if (message.type === PROCESS_GROUP_TERMINATION_CONFIRMED && message.requestId === requestId) {
918
- parentPort.off('message', receiveConfirmation);
1192
+ cleanup();
919
1193
  resolve();
920
1194
  }
921
1195
  }
@@ -946,34 +1220,144 @@ const processGroupsByThread = new Map();
946
1220
  // window component-preparation locking exists to close.
947
1221
  const pendingProcessGroupTerminations = new Map();
948
1222
  const PROCESS_GROUP_TERMINATION_POLL_MS = 25;
949
- // A process-group leader spawned by a worker thread's own event loop is normally reaped by that
950
- // same loop when it dies. But the only caller of this check (waitForProcessGroupExit, for a
951
- // *dead* owner thread) runs after that loop is already gone, so nothing will ever reap it here:
952
- // kill(pid, 0) keeps succeeding forever against the zombie's still-allocated PID slot. A zombie
953
- // can no longer touch the filesystem, so treat a confirmed zombie as dead rather than poll
954
- // forever for a reap that will never come. Reproduced by killing a worker mid-install: the
955
- // installer's process-group leader becomes a permanent `Z` in /proc without this check.
956
- function isZombieProcessGroupLeader(processGroupId) {
957
- if (process.platform !== 'linux')
958
- return false;
1223
+ const ZOMBIE_GROUP_MEMBER_SCAN_INTERVAL_MS = 1000;
1224
+ const PROCESS_GROUP_LIVENESS_WARNING_MS = 30000;
1225
+ // Bounds isThreadRunning's own wait (below); Application.ts's waitForConfirmedTermination stays
1226
+ // deliberately unbounded.
1227
+ const THREAD_RUNNING_TERMINATION_BACKSTOP_MS = PROCESS_GROUP_LIVENESS_WARNING_MS;
1228
+ const zombieGroupScanTimes = new Map();
1229
+ const processGroupLivenessStates = new Map();
1230
+ function processGroupExists(processGroupId) {
1231
+ try {
1232
+ process.kill(-processGroupId, 0);
1233
+ return true;
1234
+ }
1235
+ catch (error) {
1236
+ return error.code === 'EPERM';
1237
+ }
1238
+ }
1239
+ function processProbeError(error) {
1240
+ return error?.code ?? error?.message ?? String(error ?? 'unknown error');
1241
+ }
1242
+ function processGroupLeaderState(processGroupId, platform, readStat) {
1243
+ if (platform !== 'linux') {
1244
+ return { state: 'unknown', reason: `zombie-process detection is unavailable on ${platform}` };
1245
+ }
959
1246
  let stat;
960
1247
  try {
961
- stat = readFileSync(`/proc/${processGroupId}/stat`, 'utf8');
1248
+ stat = readStat(`/proc/${processGroupId}/stat`, 'utf8');
962
1249
  }
963
- catch {
964
- return false;
1250
+ catch (error) {
1251
+ if (error.code === 'ENOENT' || error.code === 'ESRCH')
1252
+ return { state: 'missing' };
1253
+ return {
1254
+ state: 'unknown',
1255
+ reason: `reading /proc/${processGroupId}/stat failed (${processProbeError(error)})`,
1256
+ };
965
1257
  }
966
- // Format is "pid (comm) state ..."; comm can contain spaces/parens, so anchor on the last ')'.
967
- return stat[stat.lastIndexOf(')') + 2] === 'Z';
1258
+ const fields = stat.slice(stat.lastIndexOf(')') + 2).split(' ');
1259
+ if (Number(fields[2]) !== processGroupId)
1260
+ return { state: 'missing' };
1261
+ return { state: fields[0] === 'Z' ? 'zombie' : 'alive' };
968
1262
  }
969
- function processGroupIsAlive(processGroupId) {
1263
+ function scanLinuxProcessGroup(processGroupId, readDirectory, readStat) {
1264
+ let processIds;
970
1265
  try {
971
- process.kill(-processGroupId, 0);
1266
+ processIds = readDirectory('/proc');
972
1267
  }
973
1268
  catch (error) {
974
- return error.code === 'EPERM';
1269
+ return {
1270
+ isAlive: null,
1271
+ reason: `reading /proc failed (${processProbeError(error)})`,
1272
+ };
1273
+ }
1274
+ for (const processId of processIds) {
1275
+ if (!/^\d+$/.test(processId))
1276
+ continue;
1277
+ let stat;
1278
+ try {
1279
+ stat = readStat(`/proc/${processId}/stat`, 'utf8');
1280
+ }
1281
+ catch (error) {
1282
+ if (error.code === 'ENOENT' || error.code === 'ESRCH')
1283
+ continue; // already gone
1284
+ // EACCES/EPERM (e.g. hidepid/ProtectProc) can only happen on a pid we don't own — we
1285
+ // spawned our own group's members as this same user, so their stat is always readable —
1286
+ // so it's not one of ours. Anything else is genuinely unknown, not "not ours".
1287
+ if (error.code === 'EACCES' || error.code === 'EPERM')
1288
+ continue;
1289
+ return {
1290
+ isAlive: null,
1291
+ reason: `reading /proc/${processId}/stat failed (${processProbeError(error)})`,
1292
+ };
1293
+ }
1294
+ const fields = stat.slice(stat.lastIndexOf(')') + 2).split(' ');
1295
+ const pgrp = Number(fields[2]);
1296
+ if (!Number.isFinite(pgrp)) {
1297
+ return { isAlive: null, reason: `/proc/${processId}/stat had an unparseable process-group field` };
1298
+ }
1299
+ if (pgrp !== processGroupId)
1300
+ continue;
1301
+ if (fields[0] !== 'Z')
1302
+ return { isAlive: true, reason: `a live member was observed at pid ${processId}` };
1303
+ }
1304
+ return { isAlive: false };
1305
+ }
1306
+ function keepProcessGroupAlive(processGroupId, reason, observationTime, warn) {
1307
+ let livenessState = processGroupLivenessStates.get(processGroupId);
1308
+ if (!livenessState) {
1309
+ livenessState = { observedAt: observationTime, reason, warned: false };
1310
+ processGroupLivenessStates.set(processGroupId, livenessState);
975
1311
  }
976
- return !isZombieProcessGroupLeader(processGroupId);
1312
+ else {
1313
+ livenessState.reason = reason;
1314
+ }
1315
+ if (!livenessState.warned && observationTime - livenessState.observedAt >= PROCESS_GROUP_LIVENESS_WARNING_MS) {
1316
+ livenessState.warned = true;
1317
+ warn(`Process group ${processGroupId} termination remains unconfirmed after ${PROCESS_GROUP_LIVENESS_WARNING_MS}ms: ${livenessState.reason}`);
1318
+ }
1319
+ return true;
1320
+ }
1321
+ function clearProcessGroupLivenessState(processGroupId) {
1322
+ zombieGroupScanTimes.delete(processGroupId);
1323
+ processGroupLivenessStates.delete(processGroupId);
1324
+ }
1325
+ function isProcessGroupAlive(processGroupId, options) {
1326
+ const platform = options?.platform ?? process.platform;
1327
+ const groupExists = options?.processGroupExists ?? processGroupExists;
1328
+ const readDirectory = options?.readDirectory ?? readdirSync;
1329
+ const readStat = options?.readStat ?? readFileSync;
1330
+ const observationTime = options?.now?.() ?? performance.now();
1331
+ const warn = options?.warn ?? ((message) => harperLogger.warn(message));
1332
+ if (!groupExists(processGroupId)) {
1333
+ clearProcessGroupLivenessState(processGroupId);
1334
+ return false;
1335
+ }
1336
+ const leaderState = processGroupLeaderState(processGroupId, platform, readStat);
1337
+ if (leaderState.state === 'alive')
1338
+ return keepProcessGroupAlive(processGroupId, 'the process-group leader is still alive', observationTime, warn);
1339
+ if (leaderState.state === 'unknown')
1340
+ return keepProcessGroupAlive(processGroupId, leaderState.reason, observationTime, warn);
1341
+ const scanTime = observationTime;
1342
+ const lastScan = zombieGroupScanTimes.get(processGroupId);
1343
+ if (lastScan !== undefined && scanTime >= lastScan && scanTime - lastScan < ZOMBIE_GROUP_MEMBER_SCAN_INTERVAL_MS) {
1344
+ const reason = processGroupLivenessStates.get(processGroupId)?.reason ?? 'the previous Linux scan is still current';
1345
+ return keepProcessGroupAlive(processGroupId, reason, observationTime, warn);
1346
+ }
1347
+ zombieGroupScanTimes.set(processGroupId, scanTime);
1348
+ let scanResult = scanLinuxProcessGroup(processGroupId, readDirectory, readStat);
1349
+ if (scanResult.isAlive !== false)
1350
+ return keepProcessGroupAlive(processGroupId, scanResult.reason, observationTime, warn);
1351
+ // A second snapshot catches a member omitted after forking inside the first readdir/stat window.
1352
+ // It narrows rather than closes the race because the second synchronous pass has the same gap.
1353
+ scanResult = scanLinuxProcessGroup(processGroupId, readDirectory, readStat);
1354
+ if (scanResult.isAlive !== false)
1355
+ return keepProcessGroupAlive(processGroupId, scanResult.reason, observationTime, warn);
1356
+ clearProcessGroupLivenessState(processGroupId);
1357
+ return false;
1358
+ }
1359
+ function processGroupIsAlive(processGroupId) {
1360
+ return isProcessGroupAlive(processGroupId);
977
1361
  }
978
1362
  async function waitForProcessGroupExit(processGroupId) {
979
1363
  while (processGroupIsAlive(processGroupId))
@@ -1047,6 +1431,7 @@ function addProcessGroup(ownerThreadId, processGroupId) {
1047
1431
  processGroups.add(processGroupId);
1048
1432
  }
1049
1433
  function removeProcessGroup(ownerThreadId, processGroupId) {
1434
+ clearProcessGroupLivenessState(processGroupId);
1050
1435
  const processGroups = processGroupsByThread.get(ownerThreadId);
1051
1436
  if (!processGroups)
1052
1437
  return;
@@ -1100,6 +1485,39 @@ function unregisterProcessGroup(processGroupId) {
1100
1485
  else
1101
1486
  parentPort?.postMessage({ type: UNREGISTER_PROCESS_GROUP, processGroupId });
1102
1487
  }
1488
+ class ProcessGroupTerminationUnconfirmedError extends Error {
1489
+ code = 'ERR_PROCESS_GROUP_TERMINATION_UNCONFIRMED';
1490
+ constructor(ownerThreadId, timeoutMs) {
1491
+ super(`Thread ${ownerThreadId}'s process-group termination could not be confirmed after ${timeoutMs}ms`);
1492
+ this.name = 'ProcessGroupTerminationUnconfirmedError';
1493
+ }
1494
+ }
1495
+ // Bounds awaitProcessGroupTermination for isThreadRunning below. Throws rather than resolving to
1496
+ // "not running": componentPreparationLock's ownerIsAlive/ownerLivenessConfirmed already treat a
1497
+ // throwing isOwnerAlive as "can't confirm — don't steal the claim, don't renew the deadline", so
1498
+ // the lock's own bounded wait is what eventually fails the waiter.
1499
+ async function awaitConfirmedProcessGroupTermination(ownerThreadId) {
1500
+ const abortController = new AbortController();
1501
+ let timedOut = false;
1502
+ const timer = setTimeout(() => {
1503
+ timedOut = true;
1504
+ abortController.abort();
1505
+ }, THREAD_RUNNING_TERMINATION_BACKSTOP_MS);
1506
+ timer.unref?.();
1507
+ try {
1508
+ await Promise.race([
1509
+ awaitProcessGroupTermination(ownerThreadId, abortController.signal),
1510
+ new Promise((resolve) => abortController.signal.addEventListener('abort', resolve, { once: true })),
1511
+ ]);
1512
+ }
1513
+ finally {
1514
+ clearTimeout(timer);
1515
+ }
1516
+ if (timedOut) {
1517
+ harperLogger.warn(`Thread ${ownerThreadId}'s process-group termination could not be confirmed after ${THREAD_RUNNING_TERMINATION_BACKSTOP_MS}ms`);
1518
+ throw new ProcessGroupTerminationUnconfirmedError(ownerThreadId, THREAD_RUNNING_TERMINATION_BACKSTOP_MS);
1519
+ }
1520
+ }
1103
1521
  async function isThreadRunning(ownerThreadId, timeoutMs = THREAD_INFO_REQUEST_TIMEOUT_MS) {
1104
1522
  if (ownerThreadId === threadId || ownerThreadId === 0)
1105
1523
  return true;
@@ -1107,7 +1525,7 @@ async function isThreadRunning(ownerThreadId, timeoutMs = THREAD_INFO_REQUEST_TI
1107
1525
  return true;
1108
1526
  // The thread itself is gone, but it may still own process groups whose forced termination is
1109
1527
  // in flight — wait for that to be confirmed before reporting the owner as reclaimable.
1110
- await awaitProcessGroupTermination(ownerThreadId);
1528
+ await awaitConfirmedProcessGroupTermination(ownerThreadId);
1111
1529
  return false;
1112
1530
  }
1113
1531
  if (isMainThread) {
@@ -1116,6 +1534,14 @@ if (isMainThread) {
1116
1534
  terminateProcessGroupsForThread(ownerThreadId);
1117
1535
  });
1118
1536
  }
1537
+ /**
1538
+ * Whether a thread has already been reported dead. Sync, unlike `isThreadRunning`, because it only
1539
+ * reads the tombstone `notifyThreadExit` records below — callers on the exit path need an answer
1540
+ * without awaiting process-group confirmation.
1541
+ */
1542
+ function hasThreadExited(threadId) {
1543
+ return notifiedDeadThreadIds.has(threadId);
1544
+ }
1119
1545
  function notifyThreadExit(deadThreadId) {
1120
1546
  if (deadThreadId == null || notifiedDeadThreadIds.has(deadThreadId))
1121
1547
  return;
@@ -1239,25 +1665,51 @@ if (isMainThread) {
1239
1665
  const watchDir = async (dir, beforeRestartCallback) => {
1240
1666
  if (beforeRestartCallback)
1241
1667
  beforeRestart = beforeRestartCallback;
1242
- chokidar
1243
- .watch(dir, {
1244
- persistent: false,
1245
- ignored: (path) => {
1246
- return ignoredPaths.some((ignoredPath) => path.includes(ignoredPath));
1247
- },
1248
- })
1249
- .on('change', (path) => {
1250
- changedFiles.add(path);
1251
- if (queuedRestart)
1252
- clearTimeout(queuedRestart);
1253
- queuedRestart = setTimeout(async () => {
1254
- if (beforeRestart)
1255
- await beforeRestart();
1256
- await restartWorkers();
1257
- console.log('Reloaded Harper components, changed files:', Array.from(changedFiles));
1258
- changedFiles.clear();
1259
- }, 100);
1260
- });
1668
+ const watchTarget = resolveWatchTarget(dir);
1669
+ let usingPolling = watchTarget.mustPoll;
1670
+ let liveWatcher;
1671
+ const openWatcher = () => {
1672
+ const opened = (liveWatcher = guardedWatch(watchTarget.path, {
1673
+ persistent: false,
1674
+ ...(usingPolling ? DIRECTORY_POLLING_FALLBACK_OPTIONS : {}),
1675
+ ignored: (path) => {
1676
+ return ignoredPaths.some((ignoredPath) => path.includes(ignoredPath));
1677
+ },
1678
+ }));
1679
+ opened
1680
+ // This runs on the thread that owns every worker, and chokidar emits 'error' unguarded for
1681
+ // anything but ENOENT/ENOTDIR.
1682
+ .on('error', (error) => {
1683
+ if (claimLostNativeWatchError(error))
1684
+ return;
1685
+ if (isWatcherExhaustionError(error)) {
1686
+ if (usingPolling || liveWatcher !== opened)
1687
+ return;
1688
+ warnWatcherFallback(dir);
1689
+ usingPolling = true;
1690
+ Promise.resolve()
1691
+ .then(() => opened.close())
1692
+ .catch(() => { })
1693
+ .then(openWatcher)
1694
+ .catch((reopenError) => console.error(`Could not reopen the ${dir} component-reload watch on polling:`, reopenError));
1695
+ return;
1696
+ }
1697
+ console.error(`Error watching ${dir} for component reloads:`, error);
1698
+ })
1699
+ .on('change', (path) => {
1700
+ changedFiles.add(path);
1701
+ if (queuedRestart)
1702
+ clearTimeout(queuedRestart);
1703
+ queuedRestart = setTimeout(async () => {
1704
+ if (beforeRestart)
1705
+ await beforeRestart();
1706
+ await restartWorkers();
1707
+ console.log('Reloaded Harper components, changed files:', Array.from(changedFiles));
1708
+ changedFiles.clear();
1709
+ }, 100);
1710
+ });
1711
+ };
1712
+ openWatcher();
1261
1713
  };
1262
1714
  module.exports.watchDir = watchDir;
1263
1715
  if (process.env.WATCH_DIR)