@harperfast/harper 5.2.13 → 5.3.0-beta.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (655) hide show
  1. package/agent/mcpTools.ts +1 -1
  2. package/agent/session.ts +25 -14
  3. package/bin/cliOperations.ts +58 -9
  4. package/bin/copyDb.ts +282 -59
  5. package/bin/deploySetup.ts +16 -5
  6. package/bin/harper.ts +1 -1
  7. package/bin/help.ts +4 -1
  8. package/bin/lite.ts +4 -1
  9. package/bin/restart.ts +120 -4
  10. package/bin/run.ts +6 -11
  11. package/bin/upgrade.js +7 -3
  12. package/bin/workloadIdentity.ts +119 -0
  13. package/components/Application.ts +3436 -242
  14. package/components/ApplicationScope.ts +8 -0
  15. package/components/EntryHandler.ts +59 -39
  16. package/components/OptionsWatcher.ts +440 -98
  17. package/components/RuntimeModuleTracker.ts +38 -7
  18. package/components/Scope.ts +56 -15
  19. package/components/awaitRestart.ts +84 -0
  20. package/components/componentLoader.ts +381 -32
  21. package/components/componentPreparationLock.ts +16 -5
  22. package/components/deploymentOperations.ts +4 -1
  23. package/components/deploymentRecorder.ts +9 -2
  24. package/components/mcp/adapters/harperHttp.ts +4 -0
  25. package/components/mcp/listChanged.ts +4 -0
  26. package/components/mcp/toolRegistry.ts +2 -0
  27. package/components/mcp/tools/operations.ts +9 -0
  28. package/components/mcp/tools/schemas/operationDescriptions.ts +2 -2
  29. package/components/operations.js +537 -113
  30. package/components/operationsValidation.js +98 -3
  31. package/components/packageComponent.ts +25 -1
  32. package/components/requestRestart.ts +11 -0
  33. package/components/status/ComponentStatusRegistry.ts +59 -0
  34. package/config/RootConfigWatcher.ts +240 -40
  35. package/config/configReadRetry.ts +62 -0
  36. package/config/configUtils.ts +357 -48
  37. package/config/harperConfigEnvVars.ts +170 -27
  38. package/config/parseConfigFile.ts +34 -0
  39. package/config/readConfigFileSync.ts +44 -0
  40. package/config/watcherArming.ts +59 -0
  41. package/config-root.schema.json +33 -0
  42. package/dataLayer/blobBackup.ts +160 -50
  43. package/dataLayer/delete.ts +6 -1
  44. package/dataLayer/harperBridge/ResourceBridge.ts +80 -10
  45. package/dataLayer/harperBridge/lmdbBridge/lmdbMethods/DeleteAuditLogsBeforeResults.js +3 -1
  46. package/dataLayer/hdbInfoController.ts +34 -1
  47. package/dataLayer/insert.ts +44 -1
  48. package/dataLayer/rocksdbBackup.ts +53 -10
  49. package/dataLayer/schema.ts +11 -1
  50. package/dataLayer/schemaDescribe.ts +8 -1
  51. package/dist/agent/mcpTools.js +1 -1
  52. package/dist/agent/mcpTools.js.map +1 -1
  53. package/dist/agent/session.d.ts +22 -0
  54. package/dist/agent/session.js +26 -15
  55. package/dist/agent/session.js.map +1 -1
  56. package/dist/bin/cliOperations.js +61 -9
  57. package/dist/bin/cliOperations.js.map +1 -1
  58. package/dist/bin/copyDb.d.ts +12 -1
  59. package/dist/bin/copyDb.js +248 -60
  60. package/dist/bin/copyDb.js.map +1 -1
  61. package/dist/bin/deploySetup.d.ts +2 -0
  62. package/dist/bin/deploySetup.js +11 -3
  63. package/dist/bin/deploySetup.js.map +1 -1
  64. package/dist/bin/harper.js +1 -1
  65. package/dist/bin/harper.js.map +1 -1
  66. package/dist/bin/help.js +4 -1
  67. package/dist/bin/help.js.map +1 -1
  68. package/dist/bin/lite.js +4 -1
  69. package/dist/bin/lite.js.map +1 -1
  70. package/dist/bin/restart.js +91 -6
  71. package/dist/bin/restart.js.map +1 -1
  72. package/dist/bin/run.js +4 -10
  73. package/dist/bin/run.js.map +1 -1
  74. package/dist/bin/upgrade.js +4 -3
  75. package/dist/bin/upgrade.js.map +1 -1
  76. package/dist/bin/workloadIdentity.d.ts +18 -0
  77. package/dist/bin/workloadIdentity.js +100 -0
  78. package/dist/bin/workloadIdentity.js.map +1 -0
  79. package/dist/components/Application.d.ts +235 -17
  80. package/dist/components/Application.js +3038 -238
  81. package/dist/components/Application.js.map +1 -1
  82. package/dist/components/ApplicationScope.d.ts +8 -0
  83. package/dist/components/ApplicationScope.js +7 -0
  84. package/dist/components/ApplicationScope.js.map +1 -1
  85. package/dist/components/EntryHandler.js +26 -10
  86. package/dist/components/EntryHandler.js.map +1 -1
  87. package/dist/components/OptionsWatcher.d.ts +4 -0
  88. package/dist/components/OptionsWatcher.js +440 -99
  89. package/dist/components/OptionsWatcher.js.map +1 -1
  90. package/dist/components/RuntimeModuleTracker.js +40 -6
  91. package/dist/components/RuntimeModuleTracker.js.map +1 -1
  92. package/dist/components/Scope.js +52 -13
  93. package/dist/components/Scope.js.map +1 -1
  94. package/dist/components/awaitRestart.d.ts +33 -0
  95. package/dist/components/awaitRestart.js +61 -0
  96. package/dist/components/awaitRestart.js.map +1 -0
  97. package/dist/components/componentLoader.d.ts +38 -1
  98. package/dist/components/componentLoader.js +313 -24
  99. package/dist/components/componentLoader.js.map +1 -1
  100. package/dist/components/componentPreparationLock.d.ts +5 -0
  101. package/dist/components/componentPreparationLock.js +14 -6
  102. package/dist/components/componentPreparationLock.js.map +1 -1
  103. package/dist/components/deploymentOperations.js +4 -1
  104. package/dist/components/deploymentOperations.js.map +1 -1
  105. package/dist/components/deploymentRecorder.d.ts +4 -2
  106. package/dist/components/deploymentRecorder.js +1 -0
  107. package/dist/components/deploymentRecorder.js.map +1 -1
  108. package/dist/components/mcp/adapters/harperHttp.js +4 -0
  109. package/dist/components/mcp/adapters/harperHttp.js.map +1 -1
  110. package/dist/components/mcp/listChanged.js +5 -0
  111. package/dist/components/mcp/listChanged.js.map +1 -1
  112. package/dist/components/mcp/toolRegistry.d.ts +1 -0
  113. package/dist/components/mcp/toolRegistry.js.map +1 -1
  114. package/dist/components/mcp/tools/operations.d.ts +5 -0
  115. package/dist/components/mcp/tools/operations.js +9 -0
  116. package/dist/components/mcp/tools/operations.js.map +1 -1
  117. package/dist/components/mcp/tools/schemas/operationDescriptions.js +2 -2
  118. package/dist/components/mcp/tools/schemas/operationDescriptions.js.map +1 -1
  119. package/dist/components/operations.d.ts +28 -0
  120. package/dist/components/operations.js +476 -113
  121. package/dist/components/operations.js.map +1 -1
  122. package/dist/components/operationsValidation.js +97 -3
  123. package/dist/components/operationsValidation.js.map +1 -1
  124. package/dist/components/packageComponent.js +24 -0
  125. package/dist/components/packageComponent.js.map +1 -1
  126. package/dist/components/requestRestart.d.ts +1 -0
  127. package/dist/components/requestRestart.js +7 -0
  128. package/dist/components/requestRestart.js.map +1 -1
  129. package/dist/components/status/ComponentStatusRegistry.d.ts +0 -4
  130. package/dist/components/status/ComponentStatusRegistry.js +63 -0
  131. package/dist/components/status/ComponentStatusRegistry.js.map +1 -1
  132. package/dist/config/RootConfigWatcher.d.ts +9 -1
  133. package/dist/config/RootConfigWatcher.js +228 -37
  134. package/dist/config/RootConfigWatcher.js.map +1 -1
  135. package/dist/config/configReadRetry.d.ts +8 -0
  136. package/dist/config/configReadRetry.js +62 -0
  137. package/dist/config/configReadRetry.js.map +1 -0
  138. package/dist/config/configUtils.d.ts +11 -2
  139. package/dist/config/configUtils.js +304 -47
  140. package/dist/config/configUtils.js.map +1 -1
  141. package/dist/config/harperConfigEnvVars.d.ts +16 -0
  142. package/dist/config/harperConfigEnvVars.js +162 -25
  143. package/dist/config/harperConfigEnvVars.js.map +1 -1
  144. package/dist/config/parseConfigFile.d.ts +4 -0
  145. package/dist/config/parseConfigFile.js +35 -0
  146. package/dist/config/parseConfigFile.js.map +1 -0
  147. package/dist/config/readConfigFileSync.d.ts +1 -0
  148. package/dist/config/readConfigFileSync.js +47 -0
  149. package/dist/config/readConfigFileSync.js.map +1 -0
  150. package/dist/config/watcherArming.d.ts +15 -0
  151. package/dist/config/watcherArming.js +59 -0
  152. package/dist/config/watcherArming.js.map +1 -0
  153. package/dist/dataLayer/blobBackup.d.ts +49 -20
  154. package/dist/dataLayer/blobBackup.js +139 -50
  155. package/dist/dataLayer/blobBackup.js.map +1 -1
  156. package/dist/dataLayer/delete.js +1 -1
  157. package/dist/dataLayer/delete.js.map +1 -1
  158. package/dist/dataLayer/harperBridge/ResourceBridge.d.ts +14 -1
  159. package/dist/dataLayer/harperBridge/ResourceBridge.js +72 -12
  160. package/dist/dataLayer/harperBridge/ResourceBridge.js.map +1 -1
  161. package/dist/dataLayer/harperBridge/lmdbBridge/lmdbMethods/DeleteAuditLogsBeforeResults.d.ts +3 -1
  162. package/dist/dataLayer/harperBridge/lmdbBridge/lmdbMethods/DeleteAuditLogsBeforeResults.js +3 -1
  163. package/dist/dataLayer/harperBridge/lmdbBridge/lmdbMethods/DeleteAuditLogsBeforeResults.js.map +1 -1
  164. package/dist/dataLayer/hdbInfoController.d.ts +10 -0
  165. package/dist/dataLayer/hdbInfoController.js +30 -1
  166. package/dist/dataLayer/hdbInfoController.js.map +1 -1
  167. package/dist/dataLayer/insert.d.ts +9 -1
  168. package/dist/dataLayer/insert.js +30 -0
  169. package/dist/dataLayer/insert.js.map +1 -1
  170. package/dist/dataLayer/rocksdbBackup.d.ts +2 -2
  171. package/dist/dataLayer/rocksdbBackup.js +45 -8
  172. package/dist/dataLayer/rocksdbBackup.js.map +1 -1
  173. package/dist/dataLayer/schema.js +8 -0
  174. package/dist/dataLayer/schema.js.map +1 -1
  175. package/dist/dataLayer/schemaDescribe.js +8 -1
  176. package/dist/dataLayer/schemaDescribe.js.map +1 -1
  177. package/dist/index.d.ts +2 -1
  178. package/dist/index.js +4 -1
  179. package/dist/index.js.map +1 -1
  180. package/dist/json/systemSchema.json +55 -0
  181. package/dist/resources/DatabaseTransaction.d.ts +77 -0
  182. package/dist/resources/DatabaseTransaction.js +586 -54
  183. package/dist/resources/DatabaseTransaction.js.map +1 -1
  184. package/dist/resources/LMDBTransaction.d.ts +2 -1
  185. package/dist/resources/LMDBTransaction.js +43 -6
  186. package/dist/resources/LMDBTransaction.js.map +1 -1
  187. package/dist/resources/PrimaryRocksDatabase.d.ts +1 -0
  188. package/dist/resources/PrimaryRocksDatabase.js +49 -5
  189. package/dist/resources/PrimaryRocksDatabase.js.map +1 -1
  190. package/dist/resources/RecordEncoder.d.ts +20 -0
  191. package/dist/resources/RecordEncoder.js +110 -9
  192. package/dist/resources/RecordEncoder.js.map +1 -1
  193. package/dist/resources/RequestTarget.d.ts +2 -0
  194. package/dist/resources/RequestTarget.js.map +1 -1
  195. package/dist/resources/Resource.js +117 -24
  196. package/dist/resources/Resource.js.map +1 -1
  197. package/dist/resources/ResourceInterface.d.ts +28 -1
  198. package/dist/resources/ResourceInterface.js.map +1 -1
  199. package/dist/resources/RocksIndexStore.d.ts +6 -1
  200. package/dist/resources/RocksIndexStore.js +24 -9
  201. package/dist/resources/RocksIndexStore.js.map +1 -1
  202. package/dist/resources/RocksTransactionLogStore.d.ts +24 -1
  203. package/dist/resources/RocksTransactionLogStore.js +160 -49
  204. package/dist/resources/RocksTransactionLogStore.js.map +1 -1
  205. package/dist/resources/Table.d.ts +165 -10
  206. package/dist/resources/Table.js +1805 -303
  207. package/dist/resources/Table.js.map +1 -1
  208. package/dist/resources/analytics/write.js +10 -3
  209. package/dist/resources/analytics/write.js.map +1 -1
  210. package/dist/resources/auditStore.d.ts +181 -2
  211. package/dist/resources/auditStore.js +640 -29
  212. package/dist/resources/auditStore.js.map +1 -1
  213. package/dist/resources/blob.d.ts +129 -9
  214. package/dist/resources/blob.js +992 -114
  215. package/dist/resources/blob.js.map +1 -1
  216. package/dist/resources/branchDatabase.d.ts +48 -0
  217. package/dist/resources/branchDatabase.js +892 -0
  218. package/dist/resources/branchDatabase.js.map +1 -0
  219. package/dist/resources/crdt.js +50 -12
  220. package/dist/resources/crdt.js.map +1 -1
  221. package/dist/resources/dataLoader.js +3 -4
  222. package/dist/resources/dataLoader.js.map +1 -1
  223. package/dist/resources/databases.d.ts +157 -14
  224. package/dist/resources/databases.js +1462 -325
  225. package/dist/resources/databases.js.map +1 -1
  226. package/dist/resources/defineTable.d.ts +10 -2
  227. package/dist/resources/defineTable.js +9 -1
  228. package/dist/resources/defineTable.js.map +1 -1
  229. package/dist/resources/derivedIndexRegistry.d.ts +5 -0
  230. package/dist/resources/derivedIndexRegistry.js +68 -0
  231. package/dist/resources/derivedIndexRegistry.js.map +1 -0
  232. package/dist/resources/derivedIndexRuntime.d.ts +215 -0
  233. package/dist/resources/derivedIndexRuntime.js +2027 -0
  234. package/dist/resources/derivedIndexRuntime.js.map +1 -0
  235. package/dist/resources/graphql.d.ts +1 -1
  236. package/dist/resources/graphql.js +52 -16
  237. package/dist/resources/graphql.js.map +1 -1
  238. package/dist/resources/indexes/HierarchicalNavigableSmallWorld.d.ts +103 -9
  239. package/dist/resources/indexes/HierarchicalNavigableSmallWorld.js +854 -41
  240. package/dist/resources/indexes/HierarchicalNavigableSmallWorld.js.map +1 -1
  241. package/dist/resources/indexes/hnswDerivedIndex.d.ts +67 -0
  242. package/dist/resources/indexes/hnswDerivedIndex.js +464 -0
  243. package/dist/resources/indexes/hnswDerivedIndex.js.map +1 -0
  244. package/dist/resources/indexes/hnswPlaneBinding.d.ts +65 -0
  245. package/dist/resources/indexes/hnswPlaneBinding.js +91 -0
  246. package/dist/resources/indexes/hnswPlaneBinding.js.map +1 -0
  247. package/dist/resources/longLivedTransactions.d.ts +71 -0
  248. package/dist/resources/longLivedTransactions.js +358 -0
  249. package/dist/resources/longLivedTransactions.js.map +1 -0
  250. package/dist/resources/models/backendRegistry.d.ts +26 -0
  251. package/dist/resources/models/backendRegistry.js +60 -2
  252. package/dist/resources/models/backendRegistry.js.map +1 -1
  253. package/dist/resources/models/bootstrap.d.ts +33 -1
  254. package/dist/resources/models/bootstrap.js +416 -31
  255. package/dist/resources/models/bootstrap.js.map +1 -1
  256. package/dist/resources/nodeIdMapping.d.ts +5 -0
  257. package/dist/resources/nodeIdMapping.js +49 -0
  258. package/dist/resources/nodeIdMapping.js.map +1 -1
  259. package/dist/resources/recordLock.d.ts +123 -0
  260. package/dist/resources/recordLock.js +315 -0
  261. package/dist/resources/recordLock.js.map +1 -0
  262. package/dist/resources/recordLockCoordinator.d.ts +557 -0
  263. package/dist/resources/recordLockCoordinator.js +2565 -0
  264. package/dist/resources/recordLockCoordinator.js.map +1 -0
  265. package/dist/resources/replayLogs.d.ts +14 -1
  266. package/dist/resources/replayLogs.js +157 -24
  267. package/dist/resources/replayLogs.js.map +1 -1
  268. package/dist/resources/replayLogsGuards.d.ts +94 -7
  269. package/dist/resources/replayLogsGuards.js +111 -7
  270. package/dist/resources/replayLogsGuards.js.map +1 -1
  271. package/dist/resources/replicatedApplyFailure.d.ts +16 -0
  272. package/dist/resources/replicatedApplyFailure.js +63 -0
  273. package/dist/resources/replicatedApplyFailure.js.map +1 -0
  274. package/dist/resources/scheduler/scheduler.js +3 -3
  275. package/dist/resources/scheduler/scheduler.js.map +1 -1
  276. package/dist/resources/search.d.ts +10 -4
  277. package/dist/resources/search.js +283 -37
  278. package/dist/resources/search.js.map +1 -1
  279. package/dist/resources/tracked.d.ts +5 -1
  280. package/dist/resources/tracked.js +74 -23
  281. package/dist/resources/tracked.js.map +1 -1
  282. package/dist/resources/transactionBroadcast.d.ts +1 -1
  283. package/dist/resources/transactionBroadcast.js +2 -2
  284. package/dist/resources/transactionBroadcast.js.map +1 -1
  285. package/dist/security/auth.js +61 -30
  286. package/dist/security/auth.js.map +1 -1
  287. package/dist/security/authn/oidc/claims.d.ts +22 -0
  288. package/dist/security/authn/oidc/claims.js +71 -0
  289. package/dist/security/authn/oidc/claims.js.map +1 -0
  290. package/dist/security/authn/oidc/identityToken.d.ts +27 -0
  291. package/dist/security/authn/oidc/identityToken.js +111 -0
  292. package/dist/security/authn/oidc/identityToken.js.map +1 -0
  293. package/dist/security/authn/oidc/jwks.d.ts +25 -0
  294. package/dist/security/authn/oidc/jwks.js +261 -0
  295. package/dist/security/authn/oidc/jwks.js.map +1 -0
  296. package/dist/security/authn/oidc/providers/generic.d.ts +13 -0
  297. package/dist/security/authn/oidc/providers/generic.js +34 -0
  298. package/dist/security/authn/oidc/providers/generic.js.map +1 -0
  299. package/dist/security/authn/oidc/providers/githubActions.d.ts +11 -0
  300. package/dist/security/authn/oidc/providers/githubActions.js +129 -0
  301. package/dist/security/authn/oidc/providers/githubActions.js.map +1 -0
  302. package/dist/security/authn/oidc/providers/index.d.ts +37 -0
  303. package/dist/security/authn/oidc/providers/index.js +24 -0
  304. package/dist/security/authn/oidc/providers/index.js.map +1 -0
  305. package/dist/security/authn/oidc/tokenExchange.d.ts +12 -0
  306. package/dist/security/authn/oidc/tokenExchange.js +306 -0
  307. package/dist/security/authn/oidc/tokenExchange.js.map +1 -0
  308. package/dist/security/authn/oidc/trustPolicyOperations.d.ts +49 -0
  309. package/dist/security/authn/oidc/trustPolicyOperations.js +358 -0
  310. package/dist/security/authn/oidc/trustPolicyOperations.js.map +1 -0
  311. package/dist/security/authn/oidc/types.d.ts +38 -0
  312. package/dist/security/authn/oidc/types.js +6 -0
  313. package/dist/security/authn/oidc/types.js.map +1 -0
  314. package/dist/security/certificateVerification/index.js +40 -11
  315. package/dist/security/certificateVerification/index.js.map +1 -1
  316. package/dist/security/certificateVerification/trustedIssuers.d.ts +24 -0
  317. package/dist/security/certificateVerification/trustedIssuers.js +79 -0
  318. package/dist/security/certificateVerification/trustedIssuers.js.map +1 -0
  319. package/dist/security/certificateVerification/types.d.ts +1 -0
  320. package/dist/security/credentialProvenance.d.ts +35 -0
  321. package/dist/security/credentialProvenance.js +51 -0
  322. package/dist/security/credentialProvenance.js.map +1 -0
  323. package/dist/security/credentialRejection.d.ts +4 -0
  324. package/dist/security/credentialRejection.js +24 -0
  325. package/dist/security/credentialRejection.js.map +1 -0
  326. package/dist/security/deferredAuthentication.d.ts +36 -0
  327. package/dist/security/deferredAuthentication.js +70 -0
  328. package/dist/security/deferredAuthentication.js.map +1 -0
  329. package/dist/security/impersonation.d.ts +21 -0
  330. package/dist/security/impersonation.js +108 -9
  331. package/dist/security/impersonation.js.map +1 -1
  332. package/dist/security/jsLoader.d.ts +6 -0
  333. package/dist/security/jsLoader.js +77 -15
  334. package/dist/security/jsLoader.js.map +1 -1
  335. package/dist/security/keys.js +301 -71
  336. package/dist/security/keys.js.map +1 -1
  337. package/dist/security/operationScope.d.ts +21 -0
  338. package/dist/security/operationScope.js +36 -0
  339. package/dist/security/operationScope.js.map +1 -0
  340. package/dist/security/permissionsTranslator.js +21 -0
  341. package/dist/security/permissionsTranslator.js.map +1 -1
  342. package/dist/security/tokenAuthentication.d.ts +19 -1
  343. package/dist/security/tokenAuthentication.js +191 -10
  344. package/dist/security/tokenAuthentication.js.map +1 -1
  345. package/dist/security/user.js +4 -3
  346. package/dist/security/user.js.map +1 -1
  347. package/dist/server/DurableSubscriptionsSession.d.ts +2 -2
  348. package/dist/server/DurableSubscriptionsSession.js +67 -10
  349. package/dist/server/DurableSubscriptionsSession.js.map +1 -1
  350. package/dist/server/REST.js +106 -2
  351. package/dist/server/REST.js.map +1 -1
  352. package/dist/server/graphqlQuerying.js +4 -0
  353. package/dist/server/graphqlQuerying.js.map +1 -1
  354. package/dist/server/http.d.ts +16 -1
  355. package/dist/server/http.js +122 -17
  356. package/dist/server/http.js.map +1 -1
  357. package/dist/server/itc/serverHandlers.js +8 -1
  358. package/dist/server/itc/serverHandlers.js.map +1 -1
  359. package/dist/server/jobs/jobProcess.js +6 -2
  360. package/dist/server/jobs/jobProcess.js.map +1 -1
  361. package/dist/server/jobs/jobs.js +4 -1
  362. package/dist/server/jobs/jobs.js.map +1 -1
  363. package/dist/server/liveSubscriptionAuth.d.ts +26 -4
  364. package/dist/server/liveSubscriptionAuth.js +105 -39
  365. package/dist/server/liveSubscriptionAuth.js.map +1 -1
  366. package/dist/server/loadRootComponents.js +49 -10
  367. package/dist/server/loadRootComponents.js.map +1 -1
  368. package/dist/server/mqtt.d.ts +2 -0
  369. package/dist/server/mqtt.js +165 -30
  370. package/dist/server/mqtt.js.map +1 -1
  371. package/dist/server/nodeName.d.ts +2 -0
  372. package/dist/server/nodeName.js +107 -23
  373. package/dist/server/nodeName.js.map +1 -1
  374. package/dist/server/serverHelpers/Headers.d.ts +27 -0
  375. package/dist/server/serverHelpers/Headers.js +145 -1
  376. package/dist/server/serverHelpers/Headers.js.map +1 -1
  377. package/dist/server/serverHelpers/NodeAdapterResponse.d.ts +48 -0
  378. package/dist/server/serverHelpers/NodeAdapterResponse.js +220 -0
  379. package/dist/server/serverHelpers/NodeAdapterResponse.js.map +1 -0
  380. package/dist/server/serverHelpers/Request.d.ts +5 -10
  381. package/dist/server/serverHelpers/Request.js +38 -136
  382. package/dist/server/serverHelpers/Request.js.map +1 -1
  383. package/dist/server/serverHelpers/contentTypes.d.ts +11 -0
  384. package/dist/server/serverHelpers/contentTypes.js +221 -40
  385. package/dist/server/serverHelpers/contentTypes.js.map +1 -1
  386. package/dist/server/serverHelpers/deployValidationState.d.ts +3 -0
  387. package/dist/server/serverHelpers/deployValidationState.js +9 -19
  388. package/dist/server/serverHelpers/deployValidationState.js.map +1 -1
  389. package/dist/server/serverHelpers/operationAuthorizationState.d.ts +12 -0
  390. package/dist/server/serverHelpers/operationAuthorizationState.js +24 -2
  391. package/dist/server/serverHelpers/operationAuthorizationState.js.map +1 -1
  392. package/dist/server/serverHelpers/registeredOperations.d.ts +5 -4
  393. package/dist/server/serverHelpers/registeredOperations.js +74 -21
  394. package/dist/server/serverHelpers/registeredOperations.js.map +1 -1
  395. package/dist/server/serverHelpers/requestSanitization.d.ts +11 -0
  396. package/dist/server/serverHelpers/requestSanitization.js +20 -0
  397. package/dist/server/serverHelpers/requestSanitization.js.map +1 -0
  398. package/dist/server/serverHelpers/serverHandlers.js +6 -3
  399. package/dist/server/serverHelpers/serverHandlers.js.map +1 -1
  400. package/dist/server/serverHelpers/serverUtilities.d.ts +18 -0
  401. package/dist/server/serverHelpers/serverUtilities.js +178 -29
  402. package/dist/server/serverHelpers/serverUtilities.js.map +1 -1
  403. package/dist/server/serverHelpers/sharedMessageEncoding.d.ts +67 -0
  404. package/dist/server/serverHelpers/sharedMessageEncoding.js +280 -0
  405. package/dist/server/serverHelpers/sharedMessageEncoding.js.map +1 -0
  406. package/dist/server/serverHelpers/uwsServer.js +19 -1
  407. package/dist/server/serverHelpers/uwsServer.js.map +1 -1
  408. package/dist/server/static.js +24 -28
  409. package/dist/server/static.js.map +1 -1
  410. package/dist/server/storageReclamation.d.ts +5 -0
  411. package/dist/server/storageReclamation.js +17 -1
  412. package/dist/server/storageReclamation.js.map +1 -1
  413. package/dist/server/threads/isolatedApplications.d.ts +47 -0
  414. package/dist/server/threads/isolatedApplications.js +171 -0
  415. package/dist/server/threads/isolatedApplications.js.map +1 -0
  416. package/dist/server/threads/itc.d.ts +7 -2
  417. package/dist/server/threads/itc.js +5 -1
  418. package/dist/server/threads/itc.js.map +1 -1
  419. package/dist/server/threads/logRotationTransport.d.ts +1 -0
  420. package/dist/server/threads/logRotationTransport.js +33 -0
  421. package/dist/server/threads/logRotationTransport.js.map +1 -0
  422. package/dist/server/threads/manageThreads.d.ts +83 -3
  423. package/dist/server/threads/manageThreads.js +769 -75
  424. package/dist/server/threads/manageThreads.js.map +1 -1
  425. package/dist/server/threads/socketRouter.d.ts +1 -0
  426. package/dist/server/threads/socketRouter.js +277 -31
  427. package/dist/server/threads/socketRouter.js.map +1 -1
  428. package/dist/server/threads/threadHeapMemory.d.ts +2 -0
  429. package/dist/server/threads/threadHeapMemory.js +31 -0
  430. package/dist/server/threads/threadHeapMemory.js.map +1 -0
  431. package/dist/server/threads/threadServer.js +76 -22
  432. package/dist/server/threads/threadServer.js.map +1 -1
  433. package/dist/sqlEngine/config.d.ts +1 -3
  434. package/dist/sqlEngine/config.js +19 -16
  435. package/dist/sqlEngine/config.js.map +1 -1
  436. package/dist/sqlTranslator/index.d.ts +1 -1
  437. package/dist/sqlTranslator/index.js +30 -7
  438. package/dist/sqlTranslator/index.js.map +1 -1
  439. package/dist/upgrade/directives/5-3-0.d.ts +7 -0
  440. package/dist/upgrade/directives/5-3-0.js +148 -0
  441. package/dist/upgrade/directives/5-3-0.js.map +1 -0
  442. package/dist/upgrade/directives/directivesController.js +2 -1
  443. package/dist/upgrade/directives/directivesController.js.map +1 -1
  444. package/dist/utility/OperationFunctionCaller.js +2 -1
  445. package/dist/utility/OperationFunctionCaller.js.map +1 -1
  446. package/dist/utility/common_utils.d.ts +16 -0
  447. package/dist/utility/common_utils.js +32 -6
  448. package/dist/utility/common_utils.js.map +1 -1
  449. package/dist/utility/componentNames.d.ts +8 -0
  450. package/dist/utility/componentNames.js +12 -1
  451. package/dist/utility/componentNames.js.map +1 -1
  452. package/dist/utility/environment/environmentManager.js +3 -6
  453. package/dist/utility/environment/environmentManager.js.map +1 -1
  454. package/dist/utility/environment/systemInformation.d.ts +1 -0
  455. package/dist/utility/environment/systemInformation.js +1 -0
  456. package/dist/utility/environment/systemInformation.js.map +1 -1
  457. package/dist/utility/errors/commonErrors.d.ts +2 -0
  458. package/dist/utility/errors/commonErrors.js +2 -0
  459. package/dist/utility/errors/commonErrors.js.map +1 -1
  460. package/dist/utility/errors/hdbError.d.ts +33 -0
  461. package/dist/utility/errors/hdbError.js +58 -1
  462. package/dist/utility/errors/hdbError.js.map +1 -1
  463. package/dist/utility/globalSchema.d.ts +18 -0
  464. package/dist/utility/hdbTerms.d.ts +18 -0
  465. package/dist/utility/hdbTerms.js +20 -2
  466. package/dist/utility/hdbTerms.js.map +1 -1
  467. package/dist/utility/logging/harper_logger.d.ts +2 -0
  468. package/dist/utility/logging/harper_logger.js +286 -30
  469. package/dist/utility/logging/harper_logger.js.map +1 -1
  470. package/dist/utility/logging/logGenerationCoordinator.d.ts +35 -0
  471. package/dist/utility/logging/logGenerationCoordinator.js +184 -0
  472. package/dist/utility/logging/logGenerationCoordinator.js.map +1 -0
  473. package/dist/utility/logging/logRotation.d.ts +46 -0
  474. package/dist/utility/logging/logRotation.js +365 -0
  475. package/dist/utility/logging/logRotation.js.map +1 -0
  476. package/dist/utility/logging/logRotator.d.ts +1 -1
  477. package/dist/utility/logging/logRotator.js +192 -85
  478. package/dist/utility/logging/logRotator.js.map +1 -1
  479. package/dist/utility/nodeIdentity.d.ts +9 -0
  480. package/dist/utility/nodeIdentity.js +58 -0
  481. package/dist/utility/nodeIdentity.js.map +1 -0
  482. package/dist/utility/npmUtilities.js +11 -7
  483. package/dist/utility/npmUtilities.js.map +1 -1
  484. package/dist/utility/operationPermissions.d.ts +3 -1
  485. package/dist/utility/operationPermissions.js +16 -1
  486. package/dist/utility/operationPermissions.js.map +1 -1
  487. package/dist/utility/operation_authorization.d.ts +10 -7
  488. package/dist/utility/operation_authorization.js +212 -41
  489. package/dist/utility/operation_authorization.js.map +1 -1
  490. package/dist/utility/watchPath.d.ts +29 -0
  491. package/dist/utility/watchPath.js +68 -0
  492. package/dist/utility/watchPath.js.map +1 -0
  493. package/dist/utility/watcherFallback.d.ts +41 -0
  494. package/dist/utility/watcherFallback.js +153 -0
  495. package/dist/utility/watcherFallback.js.map +1 -1
  496. package/dist/validation/configValidator.d.ts +12 -0
  497. package/dist/validation/configValidator.js +205 -75
  498. package/dist/validation/configValidator.js.map +1 -1
  499. package/dist/validation/installValidator.js +12 -0
  500. package/dist/validation/installValidator.js.map +1 -1
  501. package/dist/validation/validationWrapper.d.ts +11 -0
  502. package/dist/validation/validationWrapper.js +16 -3
  503. package/dist/validation/validationWrapper.js.map +1 -1
  504. package/index.ts +8 -0
  505. package/json/systemSchema.json +55 -0
  506. package/npm-shrinkwrap.json +286 -194
  507. package/package.json +15 -7
  508. package/resources/DESIGN.md +219 -52
  509. package/resources/DatabaseTransaction.ts +657 -52
  510. package/resources/LMDBTransaction.ts +46 -6
  511. package/resources/PrimaryRocksDatabase.ts +46 -7
  512. package/resources/RecordEncoder.ts +124 -9
  513. package/resources/RequestTarget.ts +2 -0
  514. package/resources/Resource.ts +114 -22
  515. package/resources/ResourceInterface.ts +31 -0
  516. package/resources/RocksIndexStore.ts +30 -9
  517. package/resources/RocksTransactionLogStore.ts +188 -51
  518. package/resources/Table.ts +1960 -301
  519. package/resources/analytics/write.ts +10 -3
  520. package/resources/auditStore.ts +647 -32
  521. package/resources/blob.ts +1029 -110
  522. package/resources/branchDatabase.ts +941 -0
  523. package/resources/crdt.ts +76 -12
  524. package/resources/dataLoader.ts +3 -4
  525. package/resources/databases.ts +1584 -320
  526. package/resources/defineTable.ts +18 -2
  527. package/resources/derivedIndexRegistry.ts +56 -0
  528. package/resources/derivedIndexRuntime.ts +2292 -0
  529. package/resources/graphql.ts +72 -17
  530. package/resources/indexes/HierarchicalNavigableSmallWorld.ts +911 -48
  531. package/resources/indexes/hnswDerivedIndex.ts +531 -0
  532. package/resources/indexes/hnswPlaneBinding.ts +174 -0
  533. package/resources/longLivedTransactions.ts +360 -0
  534. package/resources/models/backendRegistry.ts +84 -2
  535. package/resources/models/bootstrap.ts +473 -28
  536. package/resources/nodeIdMapping.ts +50 -0
  537. package/resources/recordLock.ts +419 -0
  538. package/resources/recordLockCoordinator.ts +3043 -0
  539. package/resources/replayLogs.ts +152 -26
  540. package/resources/replayLogsGuards.ts +171 -8
  541. package/resources/replicatedApplyFailure.ts +77 -0
  542. package/resources/scheduler/scheduler.ts +4 -4
  543. package/resources/search.ts +288 -46
  544. package/resources/tracked.ts +73 -22
  545. package/resources/transactionBroadcast.ts +3 -3
  546. package/security/auth.ts +68 -29
  547. package/security/authn/oidc/claims.ts +72 -0
  548. package/security/authn/oidc/identityToken.ts +129 -0
  549. package/security/authn/oidc/jwks.ts +260 -0
  550. package/security/authn/oidc/providers/generic.ts +40 -0
  551. package/security/authn/oidc/providers/githubActions.ts +137 -0
  552. package/security/authn/oidc/providers/index.ts +52 -0
  553. package/security/authn/oidc/tokenExchange.ts +300 -0
  554. package/security/authn/oidc/trustPolicyOperations.ts +343 -0
  555. package/security/authn/oidc/types.ts +41 -0
  556. package/security/certificateVerification/index.ts +54 -13
  557. package/security/certificateVerification/trustedIssuers.ts +76 -0
  558. package/security/certificateVerification/types.ts +1 -0
  559. package/security/credentialProvenance.ts +47 -0
  560. package/security/credentialRejection.ts +22 -0
  561. package/security/deferredAuthentication.ts +71 -0
  562. package/security/impersonation.ts +117 -12
  563. package/security/jsLoader.ts +83 -18
  564. package/security/keys.ts +298 -72
  565. package/security/operationScope.ts +33 -0
  566. package/security/permissionsTranslator.js +23 -0
  567. package/security/tokenAuthentication.ts +233 -12
  568. package/security/user.ts +4 -3
  569. package/server/DESIGN.md +194 -16
  570. package/server/DurableSubscriptionsSession.ts +71 -11
  571. package/server/REST.ts +115 -4
  572. package/server/graphqlQuerying.ts +4 -0
  573. package/server/http.ts +133 -20
  574. package/server/itc/serverHandlers.js +8 -1
  575. package/server/jobs/jobProcess.ts +8 -2
  576. package/server/jobs/jobs.ts +4 -1
  577. package/server/liveSubscriptionAuth.ts +129 -46
  578. package/server/loadRootComponents.js +50 -8
  579. package/server/mqtt.ts +179 -38
  580. package/server/nodeName.ts +103 -21
  581. package/server/serverHelpers/Headers.ts +135 -0
  582. package/server/serverHelpers/NodeAdapterResponse.ts +221 -0
  583. package/server/serverHelpers/Request.ts +33 -131
  584. package/server/serverHelpers/contentTypes.ts +217 -36
  585. package/server/serverHelpers/deployValidationState.ts +24 -13
  586. package/server/serverHelpers/operationAuthorizationState.ts +34 -3
  587. package/server/serverHelpers/registeredOperations.ts +79 -22
  588. package/server/serverHelpers/requestSanitization.ts +15 -0
  589. package/server/serverHelpers/serverHandlers.js +6 -3
  590. package/server/serverHelpers/serverUtilities.ts +232 -40
  591. package/server/serverHelpers/sharedMessageEncoding.ts +307 -0
  592. package/server/serverHelpers/uwsServer.ts +17 -2
  593. package/server/static.ts +23 -29
  594. package/server/storageReclamation.ts +15 -2
  595. package/server/threads/isolatedApplications.ts +157 -0
  596. package/server/threads/itc.js +11 -1
  597. package/server/threads/logRotationTransport.ts +40 -0
  598. package/server/threads/manageThreads.js +771 -66
  599. package/server/threads/socketRouter.ts +291 -30
  600. package/server/threads/threadHeapMemory.ts +26 -0
  601. package/server/threads/threadServer.js +73 -22
  602. package/sqlTranslator/index.ts +31 -8
  603. package/studio/web/assets/{Chat-4RrB5134.js → Chat-D3j-1yY1.js} +1 -1
  604. package/studio/web/assets/{FloatingChat-omlNMDcJ.js → FloatingChat-BxJGYcfB.js} +3 -3
  605. package/studio/web/assets/{apiToken-Bke3wvfZ.js → apiToken-CT55oWOe.js} +1 -1
  606. package/studio/web/assets/{applications-DvFDYJgK.js → applications-D9Ct9_vm.js} +1 -1
  607. package/studio/web/assets/{cssMode-C1vRa7zh.js → cssMode-DV8H7VwA.js} +1 -1
  608. package/studio/web/assets/{editor-3XRWEDWX.js → editor-uatc0unt.js} +1 -1
  609. package/studio/web/assets/{html-CTY5tdMr.js → html-Bm6D6paN.js} +1 -1
  610. package/studio/web/assets/{htmlMode-CG1vSD9t.js → htmlMode-CEn7tpLG.js} +1 -1
  611. package/studio/web/assets/{index-Dfpeofdu.js → index-BIXW6Pu4.js} +5 -5
  612. package/studio/web/assets/{index.lazy-B1VOIv-t.js → index.lazy-UI7L-Vrk.js} +1 -1
  613. package/studio/web/assets/{javascript-CJeJzGnI.js → javascript-CJ0G3AFZ.js} +1 -1
  614. package/studio/web/assets/{jsonMode-CYPBwM82.js → jsonMode-DQADAYEa.js} +1 -1
  615. package/studio/web/assets/{languageServices-IKH4GUHl.js → languageServices-CAQJXWcI.js} +1 -1
  616. package/studio/web/assets/{lspLanguageFeatures-BC8_gxKG.js → lspLanguageFeatures-CCQ8P5sY.js} +1 -1
  617. package/studio/web/assets/{notifications-BfKBpYcq.js → notifications-BbxTU6Aw.js} +1 -1
  618. package/studio/web/assets/{notifications-DFbArTfC.js → notifications-Cvb3P1lB.js} +1 -1
  619. package/studio/web/assets/{profile-Q4-T6c-S.js → profile-Yyb7gsvL.js} +1 -1
  620. package/studio/web/assets/{regions-CtkV0xje.js → regions-OgjGHlU5.js} +1 -1
  621. package/studio/web/assets/{register-1ZZuMsiA.js → register-6qwNEOY3.js} +2 -2
  622. package/studio/web/assets/{setComponentFile-ZWLcWv5X.js → setComponentFile-BilDMtgB.js} +1 -1
  623. package/studio/web/assets/{setup-DWprJyJy.js → setup-J6qJ7OIU.js} +2 -2
  624. package/studio/web/assets/{status--aNm8isn.js → status-0RWGcfyD.js} +1 -1
  625. package/studio/web/assets/{toggleHighContrast-Cq_lt3XD.js → toggleHighContrast-BIn-vErT.js} +1 -1
  626. package/studio/web/assets/{tsMode-pjgytARx.js → tsMode-DgUXku4d.js} +1 -1
  627. package/studio/web/assets/{typescript-Cdg0mqUh.js → typescript-C9orXcsM.js} +1 -1
  628. package/studio/web/assets/{useEntityRestURL-RZhaY8Rn.js → useEntityRestURL-BEoXXbUB.js} +1 -1
  629. package/studio/web/assets/{workers-BgoXIqQe.js → workers-JVzSDmgx.js} +1 -1
  630. package/studio/web/assets/{xml-BwSeDMiP.js → xml-Cq-S8S4X.js} +1 -1
  631. package/studio/web/assets/{yaml-DotCUG5l.js → yaml-sfoRdh1M.js} +1 -1
  632. package/studio/web/index.html +1 -1
  633. package/upgrade/directives/5-3-0.ts +132 -0
  634. package/upgrade/directives/directivesController.ts +2 -1
  635. package/utility/OperationFunctionCaller.ts +2 -1
  636. package/utility/common_utils.ts +30 -5
  637. package/utility/componentNames.ts +12 -0
  638. package/utility/environment/environmentManager.ts +3 -7
  639. package/utility/environment/systemInformation.ts +7 -0
  640. package/utility/errors/commonErrors.ts +4 -0
  641. package/utility/errors/hdbError.ts +57 -0
  642. package/utility/hdbTerms.ts +19 -0
  643. package/utility/logging/harper_logger.ts +278 -26
  644. package/utility/logging/logGenerationCoordinator.ts +196 -0
  645. package/utility/logging/logRotation.ts +367 -0
  646. package/utility/logging/logRotator.ts +213 -81
  647. package/utility/nodeIdentity.ts +45 -0
  648. package/utility/npmUtilities.ts +12 -8
  649. package/utility/operationPermissions.ts +18 -1
  650. package/utility/operation_authorization.ts +231 -42
  651. package/utility/watchPath.ts +63 -0
  652. package/utility/watcherFallback.ts +148 -0
  653. package/validation/configValidator.ts +215 -75
  654. package/validation/installValidator.ts +15 -0
  655. package/validation/validationWrapper.ts +18 -4
@@ -1,19 +1,19 @@
1
1
  "use strict";
2
- var __importDefault = (this && this.__importDefault) || function (mod) {
3
- return (mod && mod.__esModule) ? mod : { "default": mod };
4
- };
5
2
  Object.defineProperty(exports, "__esModule", { value: true });
6
3
  exports.OptionsWatcher = exports.CannotSetPropertyError = exports.KeyDoesNotExistError = exports.InvalidValueTypeError = exports.InvariantUninitializedOptionsWatcherError = exports.UninitializedOptionsWatcherError = exports.OptionsWatcherConfigFileError = void 0;
7
4
  const harper_logger_ts_1 = require("../utility/logging/harper_logger.js");
8
5
  const events_1 = require("events");
9
- const yaml_1 = __importDefault(require("yaml"));
10
- const chokidar_1 = __importDefault(require("chokidar"));
11
6
  const promises_1 = require("node:fs/promises");
12
7
  const util_1 = require("util");
13
8
  const DEFAULT_CONFIG_ts_1 = require("./DEFAULT_CONFIG.js");
14
9
  const lodash_1 = require("lodash");
15
10
  const watcherFallback_ts_1 = require("../utility/watcherFallback.js");
11
+ const watchPath_ts_1 = require("../utility/watchPath.js");
16
12
  const harperConfigEnvVars_ts_1 = require("../config/harperConfigEnvVars.js");
13
+ const readConfigFileSync_ts_1 = require("../config/readConfigFileSync.js");
14
+ const parseConfigFile_ts_1 = require("../config/parseConfigFile.js");
15
+ const configReadRetry_ts_1 = require("../config/configReadRetry.js");
16
+ const watcherArming_ts_1 = require("../config/watcherArming.js");
17
17
  // This is uniquely for errors coming from the chokidar watcher of the config file.
18
18
  class OptionsWatcherConfigFileError extends Error {
19
19
  constructor(configFilePath, error) {
@@ -68,35 +68,49 @@ exports.CannotSetPropertyError = CannotSetPropertyError;
68
68
  */
69
69
  class OptionsWatcher extends events_1.EventEmitter {
70
70
  #filePath;
71
+ #watchPath;
71
72
  #watcher;
72
73
  #scopedConfig;
73
74
  #rootConfig;
74
75
  #isRootConfig;
76
+ #synchronousRead;
77
+ #readRetry = new configReadRetry_ts_1.ConfigReadRetry();
78
+ #armGate = new watcherArming_ts_1.ArmGate();
75
79
  #name;
76
80
  #logger;
77
81
  #usingPolling;
78
82
  #closed;
79
83
  #openCount = 0;
84
+ #readCount = 0;
85
+ #readSequence = 0;
86
+ #appliedSequence = 0;
87
+ #scopeConfigured = false;
88
+ #armAbsence;
89
+ #readyEmitted = false;
90
+ #envComposeError;
80
91
  #pendingReads = new Set();
81
92
  ready;
82
93
  constructor(name, filePath, logger, isRootConfig) {
83
94
  super();
84
95
  this.#name = name;
85
96
  this.#filePath = filePath;
97
+ const watchTarget = (0, watchPath_ts_1.resolveWatchTarget)(filePath);
98
+ this.#watchPath = watchTarget.path;
86
99
  // Root-config watchers must see runtime env config (HARPER_SET_CONFIG et al.)
87
100
  // even when it hasn't been flushed to disk yet — see #handleChange (#1618).
88
101
  // Application scopes watch their own config.yaml and are never overlaid.
89
- this.#isRootConfig = isRootConfig ?? (0, harperConfigEnvVars_ts_1.isRootConfigFilename)(filePath);
102
+ const rootConfigFile = (0, harperConfigEnvVars_ts_1.isRootConfigFilename)(filePath);
103
+ this.#isRootConfig = isRootConfig ?? rootConfigFile;
104
+ this.#synchronousRead = this.#isRootConfig;
90
105
  this.#logger = logger || (0, harper_logger_ts_1.loggerWithTag)(name);
91
- this.#usingPolling = false;
106
+ this.#usingPolling = watchTarget.mustPoll;
92
107
  this.#closed = false;
93
108
  this.ready = (0, events_1.once)(this, 'ready');
94
109
  this.#openWatcher();
95
110
  }
96
111
  #openWatcher() {
97
- this.#openCount++;
98
- this.#watcher = chokidar_1.default
99
- .watch(this.#filePath, {
112
+ const generation = ++this.#openCount;
113
+ this.#watcher = (0, watcherFallback_ts_1.guardedWatch)(this.#watchPath, {
100
114
  persistent: false,
101
115
  ...(this.#usingPolling ? watcherFallback_ts_1.POLLING_FALLBACK_OPTIONS : {}),
102
116
  })
@@ -104,79 +118,292 @@ class OptionsWatcher extends events_1.EventEmitter {
104
118
  .on('change', this.#handleChange.bind(this))
105
119
  .on('error', this.#handleError.bind(this))
106
120
  .on('unlink', this.#handleUnlink.bind(this))
107
- .on('ready', this.#handleChange.bind(this));
121
+ // Generation-bound: `#armGate.reset()` runs before the failed watcher is closed, so a
122
+ // `ready` still queued on it would arm the gate on the replacement's behalf and the
123
+ // replacement's own `ready` would then be a no-op — leaving its scan window unre-read.
124
+ .on('ready', () => this.#handleArmed(generation));
125
+ }
126
+ // Every root-declared plugin gets its own root-config watcher and each reads synchronously, so
127
+ // each has the unarmed window DESIGN.md's "`ready` means the watcher is armed" describes. The
128
+ // write that lands in it is reported by no event, so only this re-read can deliver it.
129
+ #handleArmed(generation) {
130
+ if (this.#closed || generation !== this.#openCount)
131
+ return;
132
+ this.#armGate.arm(() => this.#read(true, true));
108
133
  }
109
134
  #handleChange() {
110
- const read = (0, promises_1.readFile)(this.#filePath, 'utf-8')
111
- .then((contents) => {
112
- let parsed = yaml_1.default.parse(contents);
113
- // The on-disk root config is not guaranteed to include runtime env config at
114
- // boot: the file flush races component loading, so a scope's boot-time reads
115
- // (e.g. an `enabled` gate in handleApplication) could observe pre-env values
116
- // the componentLoader itself never saw. Ask the config layer to overlay env
117
- // config onto EVERY root-config read so scope.options matches the resolved
118
- // view (#1618). Non-root scopes and the no-env-vars case are untouched
119
- // (overlayRootEnvConfig is a no-op there).
120
- if (this.#isRootConfig)
121
- parsed = (0, harperConfigEnvVars_ts_1.overlayRootEnvConfig)(parsed);
122
- this.#rootConfig = parsed && typeof parsed === 'object' ? parsed : undefined;
123
- // If the extension is in the config file
124
- if (this.#rootConfig && this.#name in this.#rootConfig) {
125
- // If a config object does not exist
126
- if (!this.#scopedConfig) {
127
- // set it
128
- this.#scopedConfig = this.#rootConfig[this.#name];
129
- // and emit a ready event
130
- this.emit('ready', this.#scopedConfig);
135
+ this.#read(true);
136
+ }
137
+ // While `ready` is outstanding the ladder is the only thing that can settle it, so its timer has
138
+ // to keep the thread alive; afterwards it must not, or a config file nobody is reading would
139
+ // hold a worker open.
140
+ #schedule() {
141
+ return this.#readRetry.schedule(() => this.#read(false), !this.#readyEmitted);
142
+ }
143
+ #read(waitForLock, arming = false) {
144
+ // A queued chokidar callback can still land after close(), and by then removeAllListeners()
145
+ // has run — emitting into an EventEmitter with no 'error' listener would throw out of it.
146
+ if (this.#closed)
147
+ return;
148
+ this.#readCount++;
149
+ if (this.#synchronousRead) {
150
+ try {
151
+ let contents;
152
+ try {
153
+ contents = (0, readConfigFileSync_ts_1.readConfigFileSync)(this.#filePath, waitForLock);
131
154
  }
132
- else {
133
- // Otherwise, merge the new config with the old config
134
- this.#merge(this.#rootConfig[this.#name], this.#scopedConfig);
155
+ catch (error) {
156
+ this.#handleReadError(error, arming);
157
+ return;
135
158
  }
159
+ this.#applyContents(contents);
136
160
  }
137
- else {
138
- // Otherwise, if the extension is not in the config file
139
- // This means the plugin was removed from the config file
140
- if (this.#scopedConfig) {
141
- // and a config exists, remove it
142
- this.#scopedConfig = undefined;
143
- this.emit('remove');
144
- }
145
- // Otherwise do nothing - the user may add the config back in later
161
+ catch (error) {
162
+ // A listener of what `#applyContents` emitted may have closed the watcher, after
163
+ // which `emit('error')` has no listener left and would throw out of here.
164
+ if (!this.#closed)
165
+ this.#surfaceFailure(error);
146
166
  }
167
+ return;
168
+ }
169
+ // The `#closed` check above cannot cover the completion: close() may land while this read is
170
+ // in flight, and by then its retry state is cancelled and its listeners are gone. Nothing
171
+ // orders these against each other either — chokidar does not await one read before starting
172
+ // the next — so an older one completing last would put the file's previous contents back
173
+ // with no event left to correct it.
174
+ const sequence = ++this.#readSequence;
175
+ // `<=`, not `<`: a deletion supersedes the reads already in flight without being a read of
176
+ // its own, and says so by claiming the sequence they were issued under.
177
+ const outranked = () => this.#closed || sequence <= this.#appliedSequence;
178
+ const read = (0, promises_1.readFile)(this.#filePath, 'utf-8')
179
+ .then((contents) => {
180
+ if (outranked())
181
+ return;
182
+ this.#appliedSequence = sequence;
183
+ this.#applyContents(contents);
184
+ }, (error) => {
185
+ if (outranked())
186
+ return;
187
+ this.#appliedSequence = sequence;
188
+ this.#handleReadError(error, arming);
147
189
  })
148
190
  .catch((error) => {
149
- // If the config file does not exist
150
- if (error instanceof Error && 'code' in error && error.code === 'ENOENT') {
151
- // A readFile ENOENT here is the install window (file not written yet) or a
152
- // transient read race — NOT a real deletion, which chokidar routes to
153
- // `#handleUnlink`. Env config is file-independent, so when it provides this
154
- // scope the missing file must not discard it (#1618). When it does not, fall
155
- // through to the original ENOENT handling with #rootConfig untouched, so a
156
- // first boot still emits `ready` (not `remove`, which nothing consumes at
157
- // boot → `ready` would hang forever).
158
- if (this.#applyEnvOnlyConfig())
159
- return;
160
- // And a config already exists, reset it to the default
161
- if (this.#rootConfig) {
162
- this.#resetConfig();
163
- this.emit('remove');
164
- }
165
- else {
166
- // Otherwise, if no config exists, then just set to default and emit ready
167
- this.#resetConfig();
168
- this.emit('ready');
169
- }
170
- return;
171
- }
172
- this.emit('error', error);
191
+ if (!this.#closed)
192
+ this.#surfaceFailure(error);
173
193
  })
174
194
  .finally(() => {
175
195
  this.#pendingReads.delete(read);
176
196
  });
177
197
  this.#pendingReads.add(read);
178
198
  }
199
+ #applyContents(contents) {
200
+ // An empty read is a writer's truncate window, not an emptied config, and falling through
201
+ // would emit `remove` — see DESIGN.md, "An empty read is a writer mid-write, not an empty
202
+ // config". Both read paths can land in that window; only the synchronous one is likely to.
203
+ if (!contents) {
204
+ if (this.#schedule())
205
+ return;
206
+ // Past the ladder the file is empty rather than mid-write, and an empty file carries no
207
+ // scope: keep what is already applied instead of reading it as a removal, and settle the
208
+ // boot barrier on the defaults rather than leaving `Scope.ready` pending forever.
209
+ this.#logger.warn?.(`Configuration file ${this.#filePath} is empty.`);
210
+ this.#settleUnconfigured();
211
+ return;
212
+ }
213
+ let parsed;
214
+ try {
215
+ parsed = (0, parseConfigFile_ts_1.parseConfigFile)(contents, this.#filePath);
216
+ }
217
+ catch (error) {
218
+ // A prefix of the document is as much a mid-write read as an empty one, and the event
219
+ // carrying the rest is the one chokidar throttles away — same ladder, same reason. Past it
220
+ // the file really is malformed, which `#read` routes to `#surfaceFailure`.
221
+ if (!this.#closed && this.#schedule())
222
+ return;
223
+ throw error;
224
+ }
225
+ // Judged on the file's own parse, before the overlay below: a truncated document, a lone
226
+ // `\n` and a file of nothing but comments all parse to `null` rather than throwing, and
227
+ // `overlayRootEnvConfig` turns any of them into a non-null object whenever a config env var
228
+ // is set — the norm in containers — so overlaying first would launder a half-written file
229
+ // into a valid-looking env-only config and wipe the file's own options. Past the ladder it
230
+ // is an empty file, which `#applyContents` already keeps rather than reads as a removal.
231
+ if (!parsed || typeof parsed !== 'object') {
232
+ if (this.#schedule())
233
+ return;
234
+ this.#logger.warn?.(`Configuration file ${this.#filePath} is empty.`);
235
+ this.#settleUnconfigured();
236
+ return;
237
+ }
238
+ this.#readRetry.reset();
239
+ // The on-disk root config is not guaranteed to include runtime env config at
240
+ // boot: the file flush races component loading, so a scope's boot-time reads
241
+ // (e.g. an `enabled` gate in handleApplication) could observe pre-env values
242
+ // the componentLoader itself never saw. Ask the config layer to overlay env
243
+ // config onto EVERY root-config read so scope.options matches the resolved
244
+ // view (#1618). Non-root scopes and the no-env-vars case are untouched
245
+ // (overlayRootEnvConfig is a no-op there).
246
+ if (this.#isRootConfig) {
247
+ try {
248
+ parsed = (0, harperConfigEnvVars_ts_1.overlayRootEnvConfig)(parsed);
249
+ }
250
+ catch (error) {
251
+ this.#envComposeError = error;
252
+ if (this.#readyEmitted)
253
+ this.#reportEnvComposeFailure();
254
+ else
255
+ this.#settleUnconfigured();
256
+ return;
257
+ }
258
+ }
259
+ this.#rootConfig = parsed && typeof parsed === 'object' ? parsed : undefined;
260
+ // If the extension is in the config file
261
+ if (this.#rootConfig && this.#name in this.#rootConfig) {
262
+ this.#applyScopedConfig(this.#rootConfig[this.#name]);
263
+ }
264
+ else {
265
+ // Otherwise, if the extension is not in the config file
266
+ // This means the plugin was removed from the config file
267
+ // Presence, not truthiness: `myPlugin:` with nothing under it is a configured scope, and
268
+ // deleting that block has to reach `Scope` as a removal like any other.
269
+ if (this.#scopeConfigured) {
270
+ this.#scopeConfigured = false;
271
+ this.#scopedConfig = undefined;
272
+ this.#emitRemove();
273
+ }
274
+ // The scope may be added back later, but this read is still a terminal outcome: with
275
+ // nothing ever applied, neither branch above emits, and `Scope.ready` would stay pending
276
+ // for a config that read perfectly well. The read succeeded, so only the scope falls back
277
+ // to its default — `#settleUnconfigured`'s full reset would discard the root config this
278
+ // very read produced.
279
+ if (!this.#readyEmitted) {
280
+ this.#scopedConfig = (0, lodash_1.cloneDeep)(DEFAULT_CONFIG_ts_1.DEFAULT_CONFIG[this.#name]);
281
+ this.#emitReady(this.#scopedConfig);
282
+ }
283
+ }
284
+ }
285
+ #handleReadError(error, arming = false) {
286
+ // If the config file does not exist
287
+ if (error instanceof Error && 'code' in error && error.code === 'ENOENT') {
288
+ // A readFile ENOENT here is the install window (file not written yet) or a
289
+ // transient read race — NOT a real deletion, which chokidar routes to
290
+ // `#handleUnlink`. Env config is file-independent, so when it provides this
291
+ // scope the missing file must not discard it (#1618). When it does not, fall
292
+ // through to the original ENOENT handling with #rootConfig untouched, so a
293
+ // first boot still emits `ready` (not `remove`, which nothing consumes at
294
+ // boot → `ready` would hang forever).
295
+ // A ladder armed by an earlier empty read is spent here, and every other terminal path
296
+ // clears its deadline; leaving it armed costs the next mid-write read its whole budget.
297
+ this.#readRetry.reset();
298
+ if (this.#applyEnvOnlyConfig())
299
+ return;
300
+ // And a config already exists, reset it to the default
301
+ if (this.#rootConfig) {
302
+ // The arm gate's job is the *write* that landed while the watch was unarmed, and
303
+ // answering its ENOENT with a removal reports the deletion ahead of the `unlink`
304
+ // that would confirm it — on a platform with an arming grace, ahead of chokidar
305
+ // having finished its own teardown, so a config recreated on the strength of that
306
+ // early `remove` lands where its `add` is not observed at all. It cannot simply be
307
+ // dropped either: the unarmed window is exactly where an `unlink` can go missing.
308
+ // So it goes back to the loop once, and chokidar's own `unlink` cancels it.
309
+ if (arming) {
310
+ // Every resolution of the deferral recomposes and reports the env state it finds,
311
+ // so a failure held across it is one that may no longer be true — and the next
312
+ // path to report would attach it to an unrelated event.
313
+ this.#envComposeError = undefined;
314
+ this.#deferAbsenceCheck();
315
+ return;
316
+ }
317
+ this.#resetConfig();
318
+ this.#emitRemove();
319
+ }
320
+ else {
321
+ // Otherwise, if no config exists, then just set to default and emit ready
322
+ this.#resetConfig();
323
+ this.#emitReady();
324
+ }
325
+ this.#reportEnvComposeFailure();
326
+ return;
327
+ }
328
+ // A failure that outlives the read emits no new watcher event when it clears, so without
329
+ // this the scope would hold a stale config until the next write. Both read paths: an
330
+ // application config on SMB or under an editor's replacement returns a transient
331
+ // EBUSY/EIO just the same, and only the read for it blocks — the ladder never does.
332
+ if (!this.#closed && this.#schedule())
333
+ return;
334
+ this.#surfaceFailure(error);
335
+ }
336
+ // A failure before anything has been applied is the boot window, where `error` alone strands the
337
+ // component: `Scope` logs it and componentLoader waits on `Scope.ready` with no timeout. Fall
338
+ // back to the defaults exactly as the ENOENT branch above does — and emit `ready` first, since
339
+ // `error` settles the `once(..., 'ready')` promises both of them await.
340
+ #surfaceFailure(error) {
341
+ this.#settleUnconfigured();
342
+ this.#emitError(error);
343
+ }
344
+ // Settling the barrier removes the `error` listener `once(this, 'ready')` attached, and an emit
345
+ // with none left throws the error back at the caller — here, chokidar's dispatch or a retry
346
+ // timer, where nothing can absorb it. A consumer that throws is no different.
347
+ #emitError(error) {
348
+ if (this.listenerCount('error') === 0) {
349
+ this.#logger.error?.(`The configuration for '${this.#name}' at ${this.#filePath} could not be read`, error);
350
+ return;
351
+ }
352
+ try {
353
+ this.emit('error', error);
354
+ }
355
+ catch (listenerError) {
356
+ this.#logger.error?.('A configuration error listener failed', listenerError);
357
+ }
358
+ }
359
+ #emitRemove() {
360
+ try {
361
+ this.emit('remove');
362
+ }
363
+ catch (error) {
364
+ this.#logger.error?.('A configuration removal listener failed', error);
365
+ }
366
+ }
367
+ // `#readyEmitted`, not the config's truthiness: a scope absent from a config that read fine
368
+ // leaves `#rootConfig` set with no `ready` behind it, and a later failure would return here
369
+ // with `Scope.ready` still pending.
370
+ #settleUnconfigured() {
371
+ if (this.#readyEmitted)
372
+ return;
373
+ // A file the watcher cannot use does not unset env config, exactly as on the ENOENT path
374
+ // (#1618) — but the barrier still has to settle, including when composing that env config is
375
+ // itself what failed.
376
+ if (this.#applyEnvOnlyConfig()) {
377
+ if (!this.#readyEmitted)
378
+ this.#emitReady(this.#scopedConfig);
379
+ }
380
+ else {
381
+ this.#resetConfig();
382
+ this.#emitReady(this.#scopedConfig);
383
+ }
384
+ // Also on the env-only branch: the caller may be `#handleChange`'s merge-shape failure,
385
+ // whose error composing `undefined` never reproduces.
386
+ this.#reportEnvComposeFailure();
387
+ }
388
+ #emitReady(...args) {
389
+ this.#readyEmitted = true;
390
+ try {
391
+ this.emit('ready', ...args);
392
+ }
393
+ catch (error) {
394
+ // The failure paths emit `ready` from a ladder timer and from chokidar's error dispatch,
395
+ // where a throwing listener is an uncaught exception that takes the worker down.
396
+ this.#logger.error?.('A configuration listener failed', error);
397
+ }
398
+ }
179
399
  #handleError(error) {
400
+ // A queued chokidar error can land after close(), which has dropped every listener.
401
+ if (this.#closed)
402
+ return;
403
+ // See EntryHandler.#handleWatcherError: a lost native watch handle is benign
404
+ // and must not be surfaced to consumers as a config-watch failure.
405
+ if ((0, watcherFallback_ts_1.claimLostNativeWatchError)(error))
406
+ return;
180
407
  if ((0, watcherFallback_ts_1.isWatcherExhaustionError)(error)) {
181
408
  // Swallow every exhaustion error — chokidar can emit several before the
182
409
  // failed native watcher closes, and we don't want a flurry of ENOSPC to
@@ -184,25 +411,51 @@ class OptionsWatcher extends events_1.EventEmitter {
184
411
  if (!this.#usingPolling) {
185
412
  (0, watcherFallback_ts_1.warnWatcherFallback)(this.#filePath);
186
413
  this.#usingPolling = true;
187
- // Close the failed native watcher and reopen with polling. Guard
188
- // against reopen-after-close: the caller may have invoked close()
189
- // while this teardown was in flight. The .catch is required because
190
- // `finally` would re-raise a teardown rejection as an unhandled one.
191
- this.#watcher
192
- .close()
414
+ // The generation that just failed no longer speaks for the watch; the replacement arms
415
+ // on its own scan, and re-reads then as the first one did.
416
+ this.#armGate.reset();
417
+ // Start close() from a microtask, not directly here, so a synchronous throw
418
+ // can't escape this 'error' listener as an uncaught exception.
419
+ Promise.resolve()
420
+ .then(() => this.#watcher.close())
193
421
  .catch(() => {
194
422
  // Teardown errors on an already-failed watcher are not actionable.
195
423
  })
196
- .finally(() => {
424
+ .then(() => {
197
425
  if (!this.#closed)
198
426
  this.#openWatcher();
199
- });
427
+ })
428
+ .catch((error) => this.#logger.error?.(`Could not reopen the ${this.#filePath} watch on polling:`, error));
429
+ }
430
+ else {
431
+ // Already polling — the replacement failed too, or the watch was polling from
432
+ // construction (`mustPoll`) and never had a fallback to take. Either way the branch
433
+ // above reopens only once, so no read will ever run: settle the barrier rather than
434
+ // leave `Scope.ready` pending forever.
435
+ this.#settleUnconfigured();
200
436
  }
201
437
  return;
202
438
  }
203
- this.emit('error', new OptionsWatcherConfigFileError(this.#filePath, error));
439
+ // Terminal for this scope: `componentLoader` awaits `Scope.ready` with no timeout, and an
440
+ // `error` emitted while it is pending rejects that barrier instead of settling it — the
441
+ // asymmetry with `RootConfigWatcher.handleError` the boot-barrier contract cannot afford.
442
+ const watcherError = new OptionsWatcherConfigFileError(this.#filePath, error);
443
+ this.#settleUnconfigured();
444
+ this.#emitError(watcherError);
204
445
  }
205
446
  #handleUnlink(path) {
447
+ // The deletion settles what a pending read was retrying, and a rung landing after this
448
+ // would find ENOENT and emit a second `remove` at consumers that treat it as teardown.
449
+ // Same for the arming re-read's deferred absence check: this is the event it was waiting on.
450
+ // Cancelling the ladder covers the rung not yet armed; a rung already in flight on the
451
+ // asynchronous path is outranked instead, or its ENOENT would report the same deletion again
452
+ // — after the settle below, as a `remove` that asks `Scope` to restart a scope that just
453
+ // booted on the defaults.
454
+ this.#readRetry.reset();
455
+ this.#appliedSequence = this.#readSequence;
456
+ if (this.#armAbsence)
457
+ clearImmediate(this.#armAbsence);
458
+ this.#armAbsence = undefined;
206
459
  // A real deletion still leaves env-var config in force: an env-defined scope must
207
460
  // survive it exactly as on the ENOENT read path — same fallback, same error routing
208
461
  // (#1618, #1726 review).
@@ -212,16 +465,24 @@ class OptionsWatcher extends events_1.EventEmitter {
212
465
  }
213
466
  this.#logger.warn?.(`Configuration file ${path} was deleted. Reverting to default configuration. Recreate it to restore the options watcher.`);
214
467
  this.#resetConfig();
215
- this.emit('remove');
468
+ // Before the first `ready` there is nothing to remove and nothing to hear it: `Scope.ready`
469
+ // is still pending, so a `remove` here asks for a restart of a scope that never booted while
470
+ // the `#readRetry.reset()` above cancelled the only thing left to settle the barrier. Same
471
+ // ruling as the ENOENT read path, which already boots on the defaults rather than reporting
472
+ // a removal `ready` would then wait behind forever.
473
+ if (this.#readyEmitted)
474
+ this.#emitRemove();
475
+ else
476
+ this.#emitReady(this.#scopedConfig);
477
+ this.#reportEnvComposeFailure();
216
478
  }
217
479
  /**
218
480
  * Shared fallback for the ENOENT read path and `#handleUnlink`: when config env vars
219
- * define this scope, apply the env-only overlay (first application → `ready`; already
220
- * configured → `merge`, never reset). Returns true when the event was handled —
221
- * including the malformed-env case, which routes to `error` like the file-read path
222
- * rather than an unhandled rejection. Returns false (root config untouched) when this
223
- * is not a root config or the env config does not provide the scope, so callers keep
224
- * their own reset semantics.
481
+ * define this scope, apply the env-only overlay (first source application → `ready`;
482
+ * already configured → `merge`, never reset). Returns false — root config untouched, so
483
+ * callers keep their own reset semantics — when this is not a root config, when the env
484
+ * config does not provide the scope, and when composing it threw, which is held for
485
+ * `#reportEnvComposeFailure` rather than emitted here.
225
486
  */
226
487
  #applyEnvOnlyConfig() {
227
488
  if (!this.#isRootConfig)
@@ -231,25 +492,71 @@ class OptionsWatcher extends events_1.EventEmitter {
231
492
  composed = (0, harperConfigEnvVars_ts_1.overlayRootEnvConfig)(undefined);
232
493
  }
233
494
  catch (composeError) {
234
- this.emit('error', composeError);
235
- return true;
495
+ // Env config that cannot be composed is not env config: `false` puts every caller on the
496
+ // path it already takes when there is none, so the barrier still settles on the defaults
497
+ // and a deletion still emits `remove`. The failure is held for `#reportEnvComposeFailure`,
498
+ // which callers run *after* settling — an `error` emitted first settles
499
+ // `once(this, 'ready')` by rejection instead.
500
+ this.#envComposeError = composeError;
501
+ return false;
236
502
  }
237
503
  if (!composed || !(this.#name in composed))
238
504
  return false;
239
505
  this.#rootConfig = composed;
240
- if (!this.#scopedConfig) {
241
- this.#scopedConfig = composed[this.#name];
242
- this.emit('ready', this.#scopedConfig);
506
+ this.#applyScopedConfig(composed[this.#name]);
507
+ return true;
508
+ }
509
+ #reportEnvComposeFailure() {
510
+ if (this.#envComposeError === undefined)
511
+ return;
512
+ const error = this.#envComposeError;
513
+ this.#envComposeError = undefined;
514
+ this.#emitError(error);
515
+ }
516
+ // A scope with no source config takes `ready`, one with a prior source value takes the granular `change`
517
+ // events `#merge` derives. `ready` is emitted more than once: a scope can go back to having no
518
+ // config of its own — see `Scope.#handleOptionsWatcherReady` for what a repeat means there.
519
+ #applyScopedConfig(next) {
520
+ if (!this.#scopeConfigured) {
521
+ this.#scopeConfigured = true;
522
+ this.#scopedConfig = next;
523
+ this.#emitReady(this.#scopedConfig);
524
+ return;
243
525
  }
244
- else {
245
- this.#merge(composed[this.#name], this.#scopedConfig);
526
+ if (this.#scopedConfig) {
527
+ return this.#merge(next, this.#scopedConfig);
246
528
  }
247
- return true;
529
+ // A falsy scope value is still a configured scope — `myPlugin:` with nothing under it is
530
+ // the idiomatic "enable with defaults" — and `#merge` cannot diff from one, because
531
+ // `#setValue` needs a value to walk. Re-reading it is not a transition (every ladder rung
532
+ // and rename-burst re-read would otherwise look like one), but *filling it in* is a change
533
+ // like any other, not the unconfigured → configured `ready` that `Scope` answers with a
534
+ // restart.
535
+ if ((0, util_1.isDeepStrictEqual)(next, this.#scopedConfig))
536
+ return;
537
+ this.#scopedConfig = next;
538
+ this.#emitChange([], next);
248
539
  }
540
+ // Cloned, never aliased: `#merge` writes into `#scopedConfig` in place, so a scope that starts
541
+ // on the defaults and is then configured would otherwise write the app's values into the
542
+ // module-level `DEFAULT_CONFIG` every later reset hands out. And not configured: the six scopes
543
+ // `DEFAULT_CONFIG` names would otherwise have the next read of an unchanged file look like the
544
+ // block being deleted.
249
545
  #resetConfig() {
250
- this.#rootConfig = DEFAULT_CONFIG_ts_1.DEFAULT_CONFIG;
546
+ this.#scopeConfigured = false;
547
+ this.#rootConfig = (0, lodash_1.cloneDeep)(DEFAULT_CONFIG_ts_1.DEFAULT_CONFIG);
251
548
  this.#scopedConfig = this.#rootConfig[this.#name];
252
549
  }
550
+ #deferAbsenceCheck() {
551
+ if (this.#armAbsence)
552
+ return;
553
+ this.#armAbsence = setImmediate(() => {
554
+ this.#armAbsence = undefined;
555
+ if (!this.#closed)
556
+ this.#read(true);
557
+ });
558
+ this.#armAbsence.unref?.();
559
+ }
253
560
  /**
254
561
  * This merge algorithm is best thought off as a diff and overwrite.
255
562
  * The new config object will completely overwrite the old config object,
@@ -331,7 +638,7 @@ class OptionsWatcher extends events_1.EventEmitter {
331
638
  }
332
639
  if (keys.length === 0) {
333
640
  this.#scopedConfig = value;
334
- this.emit('change', keys, value, this.#scopedConfig);
641
+ this.#emitChange(keys, value);
335
642
  return;
336
643
  }
337
644
  let obj = this.#scopedConfig;
@@ -345,7 +652,19 @@ class OptionsWatcher extends events_1.EventEmitter {
345
652
  throw new CannotSetPropertyError(keys);
346
653
  }
347
654
  obj[keys[keys.length - 1]] = value;
348
- this.emit('change', keys, value, this.#scopedConfig);
655
+ this.#emitChange(keys, value);
656
+ }
657
+ // `#merge` runs inside chokidar's own dispatch on the unlink and env-fallback paths, where a
658
+ // plugin's `change` handler throwing would leave the worker with an uncaught exception on a
659
+ // config event the watcher had just decided to survive. It still reaches consumers as `error`
660
+ // — a listener fault is not a read fault, which is why it must not reach `#handleReadError`.
661
+ #emitChange(keys, value) {
662
+ try {
663
+ this.emit('change', keys, value, this.#scopedConfig);
664
+ }
665
+ catch (error) {
666
+ this.#emitError(error);
667
+ }
349
668
  }
350
669
  // Test-only: simulate the underlying chokidar watcher emitting an error.
351
670
  // Exposed so the polling-fallback path can be exercised without triggering a
@@ -353,23 +672,45 @@ class OptionsWatcher extends events_1.EventEmitter {
353
672
  _simulateWatcherErrorForTests(error) {
354
673
  this.#handleError(error);
355
674
  }
356
- // Test-only: whether the watcher has fallen back to polling.
675
+ // Test-only: a real unlink cannot be ordered against a read already in flight — see DESIGN.md,
676
+ // "Root config watchers must read synchronously".
677
+ _simulateUnlinkForTests(path) {
678
+ this.#handleUnlink(path);
679
+ }
357
680
  get _usingPollingForTests() {
358
681
  return this.#usingPolling;
359
682
  }
360
- // Test-only: number of times the underlying watcher has been (re)opened.
361
- // Used to assert that a close()-during-fallback race didn't install a
362
- // replacement watcher.
683
+ // Used to assert that a close()-during-fallback race didn't install a replacement watcher.
363
684
  get _openCountForTests() {
364
685
  return this.#openCount;
365
686
  }
687
+ // Distinguishes a ladder rung from a watcher event.
688
+ get _readCountForTests() {
689
+ return this.#readCount;
690
+ }
691
+ get _armedForTests() {
692
+ return this.#armGate.armed;
693
+ }
694
+ // Test-only: read now, rather than at whatever granularity a chokidar event would arrive.
695
+ _refreshForTests(arming = false) {
696
+ this.#read(true, arming);
697
+ return Promise.allSettled([...this.#pendingReads]);
698
+ }
366
699
  /**
367
700
  * Closes the underlying file watcher and drains any pending config-file reads.
368
701
  * Emits `close` synchronously, removes all listeners, then returns a Promise that
369
702
  * resolves once the chokidar watcher has fully stopped and all in-flight reads settle.
370
703
  */
371
704
  close() {
705
+ // Terminal like every other outcome, and `Scope.ready` has no timeout. Before `#closed` and
706
+ // through `#emitReady`, so a listener that throws cannot skip the teardown below it.
707
+ this.#settleUnconfigured();
372
708
  this.#closed = true;
709
+ this.#readRetry.cancel();
710
+ this.#armGate.cancel();
711
+ if (this.#armAbsence)
712
+ clearImmediate(this.#armAbsence);
713
+ this.#armAbsence = undefined;
373
714
  const pendingReads = [...this.#pendingReads];
374
715
  const watcherClose = Promise.resolve(this.#watcher.close()).catch(() => { });
375
716
  this.emit('close');