@harperfast/harper 5.2.0-alpha.5 → 5.2.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 (311) hide show
  1. package/agent/agent.ts +152 -10
  2. package/agent/bestPractices.ts +58 -0
  3. package/agent/mcpTools.ts +122 -0
  4. package/agent/operations.ts +10 -1
  5. package/agent/registryTools.ts +117 -0
  6. package/agent/session.ts +16 -7
  7. package/agent/tools/fsTools.ts +74 -27
  8. package/agent/tools/inspectorTool.ts +459 -0
  9. package/agent/toolset.ts +27 -7
  10. package/agent/types.ts +2 -0
  11. package/components/Application.ts +566 -85
  12. package/components/OptionsWatcher.ts +62 -2
  13. package/components/Scope.ts +31 -8
  14. package/components/componentLoader.ts +5 -1
  15. package/components/componentSecrets.ts +514 -55
  16. package/components/deploymentRecorder.ts +7 -6
  17. package/components/gitCredentialHelper.js +115 -0
  18. package/components/gitCredentialServer.ts +251 -0
  19. package/components/mcp/tools/application.ts +197 -23
  20. package/components/mcp/tools/operations.ts +1 -1
  21. package/components/mcp/tools/schemas/derive.ts +6 -2
  22. package/components/operations.js +21 -19
  23. package/components/operationsValidation.js +86 -21
  24. package/components/secretOperations.ts +110 -30
  25. package/config/harperConfigEnvVars.ts +90 -5
  26. package/config-root.schema.json +4 -0
  27. package/dataLayer/harperBridge/ResourceBridge.ts +7 -0
  28. package/dist/agent/agent.d.ts +24 -3
  29. package/dist/agent/agent.js +172 -10
  30. package/dist/agent/agent.js.map +1 -1
  31. package/dist/agent/bestPractices.d.ts +24 -0
  32. package/dist/agent/bestPractices.js +60 -0
  33. package/dist/agent/bestPractices.js.map +1 -0
  34. package/dist/agent/mcpTools.d.ts +24 -0
  35. package/dist/agent/mcpTools.js +115 -0
  36. package/dist/agent/mcpTools.js.map +1 -0
  37. package/dist/agent/operations.js +10 -1
  38. package/dist/agent/operations.js.map +1 -1
  39. package/dist/agent/registryTools.d.ts +45 -0
  40. package/dist/agent/registryTools.js +113 -0
  41. package/dist/agent/registryTools.js.map +1 -0
  42. package/dist/agent/session.js +16 -7
  43. package/dist/agent/session.js.map +1 -1
  44. package/dist/agent/tools/fsTools.js +70 -28
  45. package/dist/agent/tools/fsTools.js.map +1 -1
  46. package/dist/agent/tools/inspectorTool.d.ts +40 -0
  47. package/dist/agent/tools/inspectorTool.js +428 -0
  48. package/dist/agent/tools/inspectorTool.js.map +1 -0
  49. package/dist/agent/toolset.d.ts +16 -6
  50. package/dist/agent/toolset.js +17 -7
  51. package/dist/agent/toolset.js.map +1 -1
  52. package/dist/agent/types.d.ts +2 -0
  53. package/dist/components/Application.d.ts +73 -18
  54. package/dist/components/Application.js +475 -64
  55. package/dist/components/Application.js.map +1 -1
  56. package/dist/components/OptionsWatcher.d.ts +1 -1
  57. package/dist/components/OptionsWatcher.js +65 -2
  58. package/dist/components/OptionsWatcher.js.map +1 -1
  59. package/dist/components/Scope.d.ts +8 -6
  60. package/dist/components/Scope.js +22 -6
  61. package/dist/components/Scope.js.map +1 -1
  62. package/dist/components/componentLoader.js +5 -1
  63. package/dist/components/componentLoader.js.map +1 -1
  64. package/dist/components/componentSecrets.d.ts +33 -7
  65. package/dist/components/componentSecrets.js +473 -59
  66. package/dist/components/componentSecrets.js.map +1 -1
  67. package/dist/components/deploymentRecorder.d.ts +2 -2
  68. package/dist/components/deploymentRecorder.js +1 -1
  69. package/dist/components/deploymentRecorder.js.map +1 -1
  70. package/dist/components/gitCredentialHelper.d.ts +1 -0
  71. package/dist/components/gitCredentialHelper.js +113 -0
  72. package/dist/components/gitCredentialHelper.js.map +1 -0
  73. package/dist/components/gitCredentialServer.d.ts +33 -0
  74. package/dist/components/gitCredentialServer.js +236 -0
  75. package/dist/components/gitCredentialServer.js.map +1 -0
  76. package/dist/components/mcp/tools/application.d.ts +19 -0
  77. package/dist/components/mcp/tools/application.js +180 -22
  78. package/dist/components/mcp/tools/application.js.map +1 -1
  79. package/dist/components/mcp/tools/operations.d.ts +13 -0
  80. package/dist/components/mcp/tools/operations.js +1 -0
  81. package/dist/components/mcp/tools/operations.js.map +1 -1
  82. package/dist/components/mcp/tools/schemas/derive.js +6 -2
  83. package/dist/components/mcp/tools/schemas/derive.js.map +1 -1
  84. package/dist/components/operations.js +23 -21
  85. package/dist/components/operations.js.map +1 -1
  86. package/dist/components/operationsValidation.js +84 -21
  87. package/dist/components/operationsValidation.js.map +1 -1
  88. package/dist/components/secretOperations.d.ts +33 -11
  89. package/dist/components/secretOperations.js +90 -26
  90. package/dist/components/secretOperations.js.map +1 -1
  91. package/dist/config/harperConfigEnvVars.d.ts +21 -0
  92. package/dist/config/harperConfigEnvVars.js +95 -5
  93. package/dist/config/harperConfigEnvVars.js.map +1 -1
  94. package/dist/dataLayer/harperBridge/ResourceBridge.js +8 -0
  95. package/dist/dataLayer/harperBridge/ResourceBridge.js.map +1 -1
  96. package/dist/index.d.ts +4 -0
  97. package/dist/index.js +14 -1
  98. package/dist/index.js.map +1 -1
  99. package/dist/resources/Resource.d.ts +19 -0
  100. package/dist/resources/Resource.js +98 -4
  101. package/dist/resources/Resource.js.map +1 -1
  102. package/dist/resources/Table.d.ts +12 -1
  103. package/dist/resources/Table.js +269 -53
  104. package/dist/resources/Table.js.map +1 -1
  105. package/dist/resources/analytics/read.js +28 -25
  106. package/dist/resources/analytics/read.js.map +1 -1
  107. package/dist/resources/databases.js +21 -0
  108. package/dist/resources/databases.js.map +1 -1
  109. package/dist/resources/defineResource.d.ts +180 -0
  110. package/dist/resources/defineResource.js +505 -0
  111. package/dist/resources/defineResource.js.map +1 -0
  112. package/dist/resources/defineTable.d.ts +221 -0
  113. package/dist/resources/defineTable.js +227 -0
  114. package/dist/resources/defineTable.js.map +1 -0
  115. package/dist/resources/jsonSchemaTypes.d.ts +2 -0
  116. package/dist/resources/jsonSchemaTypes.js +12 -4
  117. package/dist/resources/jsonSchemaTypes.js.map +1 -1
  118. package/dist/resources/openApi.js +69 -13
  119. package/dist/resources/openApi.js.map +1 -1
  120. package/dist/resources/search.js +6 -8
  121. package/dist/resources/search.js.map +1 -1
  122. package/dist/server/graphqlQuerying.js +4 -2
  123. package/dist/server/graphqlQuerying.js.map +1 -1
  124. package/dist/server/http.d.ts +12 -0
  125. package/dist/server/http.js +35 -15
  126. package/dist/server/http.js.map +1 -1
  127. package/dist/server/serverHelpers/serverUtilities.js +8 -5
  128. package/dist/server/serverHelpers/serverUtilities.js.map +1 -1
  129. package/dist/server/serverHelpers/uwsServer.js +23 -0
  130. package/dist/server/serverHelpers/uwsServer.js.map +1 -1
  131. package/dist/sqlEngine/binder/bind.d.ts +72 -0
  132. package/dist/sqlEngine/binder/bind.js +289 -0
  133. package/dist/sqlEngine/binder/bind.js.map +1 -0
  134. package/dist/sqlEngine/config.d.ts +35 -0
  135. package/dist/sqlEngine/config.js +62 -0
  136. package/dist/sqlEngine/config.js.map +1 -0
  137. package/dist/sqlEngine/diff/differential.d.ts +23 -0
  138. package/dist/sqlEngine/diff/differential.js +90 -0
  139. package/dist/sqlEngine/diff/differential.js.map +1 -0
  140. package/dist/sqlEngine/errors.d.ts +22 -0
  141. package/dist/sqlEngine/errors.js +36 -0
  142. package/dist/sqlEngine/errors.js.map +1 -0
  143. package/dist/sqlEngine/executor/runMutation.d.ts +35 -0
  144. package/dist/sqlEngine/executor/runMutation.js +352 -0
  145. package/dist/sqlEngine/executor/runMutation.js.map +1 -0
  146. package/dist/sqlEngine/executor/runSelect.d.ts +7 -0
  147. package/dist/sqlEngine/executor/runSelect.js +14 -0
  148. package/dist/sqlEngine/executor/runSelect.js.map +1 -0
  149. package/dist/sqlEngine/expressions/compile.d.ts +24 -0
  150. package/dist/sqlEngine/expressions/compile.js +326 -0
  151. package/dist/sqlEngine/expressions/compile.js.map +1 -0
  152. package/dist/sqlEngine/functions/aggregates.d.ts +12 -0
  153. package/dist/sqlEngine/functions/aggregates.js +299 -0
  154. package/dist/sqlEngine/functions/aggregates.js.map +1 -0
  155. package/dist/sqlEngine/functions/registry.d.ts +36 -0
  156. package/dist/sqlEngine/functions/registry.js +33 -0
  157. package/dist/sqlEngine/functions/registry.js.map +1 -0
  158. package/dist/sqlEngine/functions/standard.d.ts +9 -0
  159. package/dist/sqlEngine/functions/standard.js +86 -0
  160. package/dist/sqlEngine/functions/standard.js.map +1 -0
  161. package/dist/sqlEngine/index.d.ts +29 -0
  162. package/dist/sqlEngine/index.js +62 -0
  163. package/dist/sqlEngine/index.js.map +1 -0
  164. package/dist/sqlEngine/logical/build.d.ts +20 -0
  165. package/dist/sqlEngine/logical/build.js +326 -0
  166. package/dist/sqlEngine/logical/build.js.map +1 -0
  167. package/dist/sqlEngine/logical/op.d.ts +105 -0
  168. package/dist/sqlEngine/logical/op.js +10 -0
  169. package/dist/sqlEngine/logical/op.js.map +1 -0
  170. package/dist/sqlEngine/optimizer/joinAnalysis.d.ts +37 -0
  171. package/dist/sqlEngine/optimizer/joinAnalysis.js +144 -0
  172. package/dist/sqlEngine/optimizer/joinAnalysis.js.map +1 -0
  173. package/dist/sqlEngine/optimizer/optimize.d.ts +13 -0
  174. package/dist/sqlEngine/optimizer/optimize.js +53 -0
  175. package/dist/sqlEngine/optimizer/optimize.js.map +1 -0
  176. package/dist/sqlEngine/optimizer/ruleEngine.d.ts +7 -0
  177. package/dist/sqlEngine/optimizer/ruleEngine.js +26 -0
  178. package/dist/sqlEngine/optimizer/ruleEngine.js.map +1 -0
  179. package/dist/sqlEngine/optimizer/rules/limitPushdown.d.ts +19 -0
  180. package/dist/sqlEngine/optimizer/rules/limitPushdown.js +55 -0
  181. package/dist/sqlEngine/optimizer/rules/limitPushdown.js.map +1 -0
  182. package/dist/sqlEngine/optimizer/rules/planJoins.d.ts +19 -0
  183. package/dist/sqlEngine/optimizer/rules/planJoins.js +62 -0
  184. package/dist/sqlEngine/optimizer/rules/planJoins.js.map +1 -0
  185. package/dist/sqlEngine/optimizer/rules/predicateNormalize.d.ts +18 -0
  186. package/dist/sqlEngine/optimizer/rules/predicateNormalize.js +80 -0
  187. package/dist/sqlEngine/optimizer/rules/predicateNormalize.js.map +1 -0
  188. package/dist/sqlEngine/optimizer/rules/predicatePushdown.d.ts +11 -0
  189. package/dist/sqlEngine/optimizer/rules/predicatePushdown.js +48 -0
  190. package/dist/sqlEngine/optimizer/rules/predicatePushdown.js.map +1 -0
  191. package/dist/sqlEngine/optimizer/rules/projectionPushdown.d.ts +23 -0
  192. package/dist/sqlEngine/optimizer/rules/projectionPushdown.js +160 -0
  193. package/dist/sqlEngine/optimizer/rules/projectionPushdown.js.map +1 -0
  194. package/dist/sqlEngine/optimizer/rules/sortFromIndex.d.ts +14 -0
  195. package/dist/sqlEngine/optimizer/rules/sortFromIndex.js +49 -0
  196. package/dist/sqlEngine/optimizer/rules/sortFromIndex.js.map +1 -0
  197. package/dist/sqlEngine/optimizer/rules/validateScannable.d.ts +15 -0
  198. package/dist/sqlEngine/optimizer/rules/validateScannable.js +60 -0
  199. package/dist/sqlEngine/optimizer/rules/validateScannable.js.map +1 -0
  200. package/dist/sqlEngine/optimizer/whereToConditions.d.ts +85 -0
  201. package/dist/sqlEngine/optimizer/whereToConditions.js +484 -0
  202. package/dist/sqlEngine/optimizer/whereToConditions.js.map +1 -0
  203. package/dist/sqlEngine/parser/ast.d.ts +139 -0
  204. package/dist/sqlEngine/parser/ast.js +10 -0
  205. package/dist/sqlEngine/parser/ast.js.map +1 -0
  206. package/dist/sqlEngine/parser/normalizer.d.ts +28 -0
  207. package/dist/sqlEngine/parser/normalizer.js +413 -0
  208. package/dist/sqlEngine/parser/normalizer.js.map +1 -0
  209. package/dist/sqlEngine/parser/parse.d.ts +14 -0
  210. package/dist/sqlEngine/parser/parse.js +20 -0
  211. package/dist/sqlEngine/parser/parse.js.map +1 -0
  212. package/dist/sqlEngine/physical/PhysicalDistinct.d.ts +9 -0
  213. package/dist/sqlEngine/physical/PhysicalDistinct.js +29 -0
  214. package/dist/sqlEngine/physical/PhysicalDistinct.js.map +1 -0
  215. package/dist/sqlEngine/physical/PhysicalFilter.d.ts +10 -0
  216. package/dist/sqlEngine/physical/PhysicalFilter.js +25 -0
  217. package/dist/sqlEngine/physical/PhysicalFilter.js.map +1 -0
  218. package/dist/sqlEngine/physical/PhysicalHashAggregate.d.ts +27 -0
  219. package/dist/sqlEngine/physical/PhysicalHashAggregate.js +100 -0
  220. package/dist/sqlEngine/physical/PhysicalHashAggregate.js.map +1 -0
  221. package/dist/sqlEngine/physical/PhysicalHashJoin.d.ts +29 -0
  222. package/dist/sqlEngine/physical/PhysicalHashJoin.js +93 -0
  223. package/dist/sqlEngine/physical/PhysicalHashJoin.js.map +1 -0
  224. package/dist/sqlEngine/physical/PhysicalIndexNestedLoopJoin.d.ts +36 -0
  225. package/dist/sqlEngine/physical/PhysicalIndexNestedLoopJoin.js +78 -0
  226. package/dist/sqlEngine/physical/PhysicalIndexNestedLoopJoin.js.map +1 -0
  227. package/dist/sqlEngine/physical/PhysicalIndexScan.d.ts +22 -0
  228. package/dist/sqlEngine/physical/PhysicalIndexScan.js +77 -0
  229. package/dist/sqlEngine/physical/PhysicalIndexScan.js.map +1 -0
  230. package/dist/sqlEngine/physical/PhysicalLimit.d.ts +5 -0
  231. package/dist/sqlEngine/physical/PhysicalLimit.js +28 -0
  232. package/dist/sqlEngine/physical/PhysicalLimit.js.map +1 -0
  233. package/dist/sqlEngine/physical/PhysicalNestedLoopJoin.d.ts +20 -0
  234. package/dist/sqlEngine/physical/PhysicalNestedLoopJoin.js +51 -0
  235. package/dist/sqlEngine/physical/PhysicalNestedLoopJoin.js.map +1 -0
  236. package/dist/sqlEngine/physical/PhysicalProject.d.ts +18 -0
  237. package/dist/sqlEngine/physical/PhysicalProject.js +117 -0
  238. package/dist/sqlEngine/physical/PhysicalProject.js.map +1 -0
  239. package/dist/sqlEngine/physical/PhysicalQualify.d.ts +13 -0
  240. package/dist/sqlEngine/physical/PhysicalQualify.js +29 -0
  241. package/dist/sqlEngine/physical/PhysicalQualify.js.map +1 -0
  242. package/dist/sqlEngine/physical/PhysicalSort.d.ts +12 -0
  243. package/dist/sqlEngine/physical/PhysicalSort.js +59 -0
  244. package/dist/sqlEngine/physical/PhysicalSort.js.map +1 -0
  245. package/dist/sqlEngine/physical/op.d.ts +11 -0
  246. package/dist/sqlEngine/physical/op.js +9 -0
  247. package/dist/sqlEngine/physical/op.js.map +1 -0
  248. package/dist/sqlEngine/physical/plan.d.ts +23 -0
  249. package/dist/sqlEngine/physical/plan.js +162 -0
  250. package/dist/sqlEngine/physical/plan.js.map +1 -0
  251. package/dist/sqlEngine/router.d.ts +26 -0
  252. package/dist/sqlEngine/router.js +48 -0
  253. package/dist/sqlEngine/router.js.map +1 -0
  254. package/dist/sqlEngine/types.d.ts +37 -0
  255. package/dist/sqlEngine/types.js +13 -0
  256. package/dist/sqlEngine/types.js.map +1 -0
  257. package/dist/sqlTranslator/index.js +7 -1
  258. package/dist/sqlTranslator/index.js.map +1 -1
  259. package/dist/utility/errors/hdbError.d.ts +21 -0
  260. package/dist/utility/errors/hdbError.js +23 -1
  261. package/dist/utility/errors/hdbError.js.map +1 -1
  262. package/dist/validation/configValidator.js +22 -6
  263. package/dist/validation/configValidator.js.map +1 -1
  264. package/dist/validation/deleteValidator.js +10 -2
  265. package/dist/validation/deleteValidator.js.map +1 -1
  266. package/index.ts +33 -0
  267. package/npm-shrinkwrap.json +9197 -15066
  268. package/package.json +3 -1
  269. package/resources/DESIGN.md +43 -15
  270. package/resources/Resource.ts +104 -4
  271. package/resources/Table.ts +298 -74
  272. package/resources/analytics/read.ts +30 -25
  273. package/resources/databases.ts +24 -0
  274. package/resources/defineResource.ts +651 -0
  275. package/resources/defineTable.ts +407 -0
  276. package/resources/jsonSchemaTypes.ts +12 -4
  277. package/resources/openApi.ts +68 -16
  278. package/resources/search.ts +5 -8
  279. package/server/graphqlQuerying.ts +4 -2
  280. package/server/http.ts +40 -16
  281. package/server/serverHelpers/serverUtilities.ts +19 -6
  282. package/server/serverHelpers/uwsServer.ts +24 -0
  283. package/sqlTranslator/index.ts +16 -6
  284. package/studio/web/assets/{Chat-Cv_2paZE.js → Chat-BZks8dVF.js} +2 -2
  285. package/studio/web/assets/{Chat-Cv_2paZE.js.map → Chat-BZks8dVF.js.map} +1 -1
  286. package/studio/web/assets/{FloatingChat-CPyPIcVR.js → FloatingChat-Dic8paVO.js} +4 -4
  287. package/studio/web/assets/{FloatingChat-CPyPIcVR.js.map → FloatingChat-Dic8paVO.js.map} +1 -1
  288. package/studio/web/assets/{applications-C0jT2vdZ.js → applications-uOXkeUIN.js} +2 -2
  289. package/studio/web/assets/{applications-C0jT2vdZ.js.map → applications-uOXkeUIN.js.map} +1 -1
  290. package/studio/web/assets/{index-D_GKOkhn.js → index-i-2wrKhv.js} +6 -6
  291. package/studio/web/assets/{index-D_GKOkhn.js.map → index-i-2wrKhv.js.map} +1 -1
  292. package/studio/web/assets/{index.lazy-CAmCIA65.js → index.lazy-Csk8eCoB.js} +4 -4
  293. package/studio/web/assets/{index.lazy-CAmCIA65.js.map → index.lazy-Csk8eCoB.js.map} +1 -1
  294. package/studio/web/assets/{profile-CaF-4aZe.js → profile-Sb3mGDl6.js} +2 -2
  295. package/studio/web/assets/{profile-CaF-4aZe.js.map → profile-Sb3mGDl6.js.map} +1 -1
  296. package/studio/web/assets/{setComponentFile-DLW1DHCt.js → setComponentFile-BgZcaPJ2.js} +2 -2
  297. package/studio/web/assets/{setComponentFile-DLW1DHCt.js.map → setComponentFile-BgZcaPJ2.js.map} +1 -1
  298. package/studio/web/assets/{setup-TGCErIhq.js → setup-DKtlLgmT.js} +2 -2
  299. package/studio/web/assets/{setup-TGCErIhq.js.map → setup-DKtlLgmT.js.map} +1 -1
  300. package/studio/web/assets/{status-DG0Maoao.js → status-B45iLeug.js} +2 -2
  301. package/studio/web/assets/{status-DG0Maoao.js.map → status-B45iLeug.js.map} +1 -1
  302. package/studio/web/assets/{swagger-ui-react-8T4SgQ1m.js → swagger-ui-react-Csu4026e.js} +2 -2
  303. package/studio/web/assets/{swagger-ui-react-8T4SgQ1m.js.map → swagger-ui-react-Csu4026e.js.map} +1 -1
  304. package/studio/web/assets/{tsMode-DJ6Sl24Q.js → tsMode-DVgxUr_l.js} +2 -2
  305. package/studio/web/assets/{tsMode-DJ6Sl24Q.js.map → tsMode-DVgxUr_l.js.map} +1 -1
  306. package/studio/web/assets/{useEntityRestURL-JO2mfWTQ.js → useEntityRestURL-yfDQMV1f.js} +2 -2
  307. package/studio/web/assets/{useEntityRestURL-JO2mfWTQ.js.map → useEntityRestURL-yfDQMV1f.js.map} +1 -1
  308. package/studio/web/index.html +1 -1
  309. package/utility/errors/hdbError.ts +32 -0
  310. package/validation/configValidator.ts +23 -6
  311. package/validation/deleteValidator.ts +11 -2
@@ -8,6 +8,7 @@ import { isDeepStrictEqual } from 'util';
8
8
  import { DEFAULT_CONFIG } from './DEFAULT_CONFIG.ts';
9
9
  import { cloneDeep } from 'lodash';
10
10
  import { POLLING_FALLBACK_OPTIONS, isWatcherExhaustionError, warnWatcherFallback } from '../utility/watcherFallback.ts';
11
+ import { overlayRootEnvConfig, isRootConfigFilename } from '../config/harperConfigEnvVars.ts';
11
12
 
12
13
  export interface Config {
13
14
  [key: string]: ConfigValue;
@@ -86,6 +87,7 @@ export class OptionsWatcher extends EventEmitter<OptionsWatcherEventMap> {
86
87
  #watcher!: FSWatcher;
87
88
  #scopedConfig?: ConfigValue;
88
89
  #rootConfig?: Config;
90
+ #isRootConfig: boolean;
89
91
  #name: string;
90
92
  #logger: Logger;
91
93
  #usingPolling: boolean;
@@ -94,10 +96,14 @@ export class OptionsWatcher extends EventEmitter<OptionsWatcherEventMap> {
94
96
  #pendingReads: Set<Promise<void>> = new Set();
95
97
  ready: Promise<any[]>;
96
98
 
97
- constructor(name: string, filePath: string, logger?: Logger) {
99
+ constructor(name: string, filePath: string, logger?: Logger, isRootConfig?: boolean) {
98
100
  super();
99
101
  this.#name = name;
100
102
  this.#filePath = filePath;
103
+ // Root-config watchers must see runtime env config (HARPER_SET_CONFIG et al.)
104
+ // even when it hasn't been flushed to disk yet — see #handleChange (#1618).
105
+ // Application scopes watch their own config.yaml and are never overlaid.
106
+ this.#isRootConfig = isRootConfig ?? isRootConfigFilename(filePath);
101
107
  this.#logger = logger || loggerWithTag(name);
102
108
  this.#usingPolling = false;
103
109
  this.#closed = false;
@@ -122,7 +128,15 @@ export class OptionsWatcher extends EventEmitter<OptionsWatcherEventMap> {
122
128
  #handleChange() {
123
129
  const read: Promise<void> = readFile(this.#filePath, 'utf-8')
124
130
  .then((contents) => {
125
- const parsed = yaml.parse(contents);
131
+ let parsed = yaml.parse(contents);
132
+ // The on-disk root config is not guaranteed to include runtime env config at
133
+ // boot: the file flush races component loading, so a scope's boot-time reads
134
+ // (e.g. an `enabled` gate in handleApplication) could observe pre-env values
135
+ // the componentLoader itself never saw. Ask the config layer to overlay env
136
+ // config onto EVERY root-config read so scope.options matches the resolved
137
+ // view (#1618). Non-root scopes and the no-env-vars case are untouched
138
+ // (overlayRootEnvConfig is a no-op there).
139
+ if (this.#isRootConfig) parsed = overlayRootEnvConfig(parsed);
126
140
  this.#rootConfig = parsed && typeof parsed === 'object' ? parsed : undefined;
127
141
  // If the extension is in the config file
128
142
  if (this.#rootConfig && this.#name in this.#rootConfig) {
@@ -150,6 +164,14 @@ export class OptionsWatcher extends EventEmitter<OptionsWatcherEventMap> {
150
164
  .catch((error) => {
151
165
  // If the config file does not exist
152
166
  if (error instanceof Error && 'code' in error && error.code === 'ENOENT') {
167
+ // A readFile ENOENT here is the install window (file not written yet) or a
168
+ // transient read race — NOT a real deletion, which chokidar routes to
169
+ // `#handleUnlink`. Env config is file-independent, so when it provides this
170
+ // scope the missing file must not discard it (#1618). When it does not, fall
171
+ // through to the original ENOENT handling with #rootConfig untouched, so a
172
+ // first boot still emits `ready` (not `remove`, which nothing consumes at
173
+ // boot → `ready` would hang forever).
174
+ if (this.#applyEnvOnlyConfig()) return;
153
175
  // And a config already exists, reset it to the default
154
176
  if (this.#rootConfig) {
155
177
  this.#resetConfig();
@@ -196,6 +218,15 @@ export class OptionsWatcher extends EventEmitter<OptionsWatcherEventMap> {
196
218
  }
197
219
 
198
220
  #handleUnlink(path: string) {
221
+ // A real deletion still leaves env-var config in force: an env-defined scope must
222
+ // survive it exactly as on the ENOENT read path — same fallback, same error routing
223
+ // (#1618, #1726 review).
224
+ if (this.#applyEnvOnlyConfig()) {
225
+ this.#logger.warn?.(
226
+ `Configuration file ${path} was deleted; the env-var-defined configuration for '${this.#name}' remains in effect.`
227
+ );
228
+ return;
229
+ }
199
230
  this.#logger.warn?.(
200
231
  `Configuration file ${path} was deleted. Reverting to default configuration. Recreate it to restore the options watcher.`
201
232
  );
@@ -203,6 +234,35 @@ export class OptionsWatcher extends EventEmitter<OptionsWatcherEventMap> {
203
234
  this.emit('remove');
204
235
  }
205
236
 
237
+ /**
238
+ * Shared fallback for the ENOENT read path and `#handleUnlink`: when config env vars
239
+ * define this scope, apply the env-only overlay (first application → `ready`; already
240
+ * configured → `merge`, never reset). Returns true when the event was handled —
241
+ * including the malformed-env case, which routes to `error` like the file-read path
242
+ * rather than an unhandled rejection. Returns false (root config untouched) when this
243
+ * is not a root config or the env config does not provide the scope, so callers keep
244
+ * their own reset semantics.
245
+ */
246
+ #applyEnvOnlyConfig(): boolean {
247
+ if (!this.#isRootConfig) return false;
248
+ let composed: Config | undefined;
249
+ try {
250
+ composed = overlayRootEnvConfig(undefined) as Config | undefined;
251
+ } catch (composeError) {
252
+ this.emit('error', composeError);
253
+ return true;
254
+ }
255
+ if (!composed || !(this.#name in composed)) return false;
256
+ this.#rootConfig = composed;
257
+ if (!this.#scopedConfig) {
258
+ this.#scopedConfig = composed[this.#name];
259
+ this.emit('ready', this.#scopedConfig);
260
+ } else {
261
+ this.#merge(composed[this.#name], this.#scopedConfig);
262
+ }
263
+ return true;
264
+ }
265
+
206
266
  #resetConfig() {
207
267
  this.#rootConfig = DEFAULT_CONFIG;
208
268
  this.#scopedConfig = this.#rootConfig[this.#name];
@@ -12,7 +12,12 @@ import { FilesOption } from './deriveGlobOptions.ts';
12
12
  import { requestRestart } from './requestRestart.ts';
13
13
  import { resolveBaseURLPath } from './resolveBaseURLPath.ts';
14
14
  import { ApplicationScope } from './ApplicationScope.ts';
15
- import { getSecretsForComponent } from './componentSecrets.ts';
15
+ import {
16
+ getSecretsForComponent,
17
+ retainComponentSubscriptions,
18
+ releaseComponentSubscriptions,
19
+ } from './componentSecrets.ts';
20
+ import type { SecretsView } from './componentSecrets.ts';
16
21
  import { deployLifecycle } from './deployLifecycle.ts';
17
22
 
18
23
  export class MissingDefaultFilesOptionError extends Error {
@@ -54,6 +59,7 @@ export class Scope extends EventEmitter<ScopeEventsMap> {
54
59
  #entryHandler?: EntryHandler;
55
60
  #entryHandlers: EntryHandler[];
56
61
  #logger: Logger;
62
+ #secretsReleased = false;
57
63
  #pendingInitialLoads: Set<Promise<void>>;
58
64
  #deployStartHandler: (name: string) => void;
59
65
  #deployEndHandler: (name: string) => void;
@@ -77,7 +83,8 @@ export class Scope extends EventEmitter<ScopeEventsMap> {
77
83
  directory: string,
78
84
  configFilePath: string,
79
85
  applicationScope: ApplicationScope,
80
- origin: string = appName
86
+ origin: string = appName,
87
+ isRootConfig?: boolean
81
88
  ) {
82
89
  super();
83
90
 
@@ -90,6 +97,9 @@ export class Scope extends EventEmitter<ScopeEventsMap> {
90
97
 
91
98
  this.databaseEvents = databaseEventsEmitter;
92
99
  this.applicationScope = applicationScope;
100
+ // Hold this identity's live secret subscriptions (#1776) for as long as this Scope is open, so a
101
+ // throwaway deploy-validation Scope closing can't tear down a sibling/running Scope's streams.
102
+ retainComponentSubscriptions(applicationScope?.name ?? appName);
93
103
  this.resources = applicationScope?.resources ?? resources;
94
104
  this.models = modelsSingleton;
95
105
 
@@ -125,7 +135,10 @@ export class Scope extends EventEmitter<ScopeEventsMap> {
125
135
  this.ready = once(this, 'ready');
126
136
 
127
137
  // Create the options instance for the scope immediately
128
- this.options = new OptionsWatcher(pluginName, configFilePath, this.#logger)
138
+ // isRootConfig is the loader's authoritative isRoot signal — it decides whether the
139
+ // watcher overlays runtime env config (#1618). When a caller doesn't provide it,
140
+ // OptionsWatcher falls back to its root-config filename heuristic.
141
+ this.options = new OptionsWatcher(pluginName, configFilePath, this.#logger, isRootConfig)
129
142
  .on('error', this.#handleError.bind(this))
130
143
  .on('change', this.#optionsWatcherChangeListener.bind(this)())
131
144
  .on('ready', this.#handleOptionsWatcherReady.bind(this));
@@ -149,12 +162,13 @@ export class Scope extends EventEmitter<ScopeEventsMap> {
149
162
 
150
163
  /**
151
164
  * The application's secrets view (#1550): hdb_secret rows granted to this application plus its
152
- * declared global-tier env names. Frozen, enumerable, values decrypted at component load.
153
- * Keyed by the ApplicationScope's name (the application directory name the identity grants
154
- * and env declarations use, and the same binding `import { secrets } from 'harper'` resolves);
155
- * `#appName` can differ on paths like RUN_HDB_APP, where it is the full directory path.
165
+ * declared global-tier env names. A live, read-only view (#1776) — scoped values reflect the latest
166
+ * store state on each read and it carries the reserved `subscribe(name)` method. Keyed by the
167
+ * ApplicationScope's name (the application directory name the identity grants and env declarations
168
+ * use, and the same binding `import { secrets } from 'harper'` resolves); `#appName` can differ on
169
+ * paths like RUN_HDB_APP, where it is the full directory path.
156
170
  */
157
- get secrets(): Readonly<Record<string, string>> {
171
+ get secrets(): SecretsView {
158
172
  return getSecretsForComponent(this.applicationScope?.name ?? this.#appName);
159
173
  }
160
174
 
@@ -223,6 +237,15 @@ export class Scope extends EventEmitter<ScopeEventsMap> {
223
237
 
224
238
  this.removeAllListeners();
225
239
 
240
+ // Release this identity's subscription hold (#1776), AFTER the close listeners (so a subscription a
241
+ // close listener created is still accounted for). Streams end only when the LAST holder of the
242
+ // identity releases — so a discarded validation-load Scope can't kill a still-running app's streams.
243
+ // Guarded so a double close() can't underflow the refcount.
244
+ if (!this.#secretsReleased) {
245
+ this.#secretsReleased = true;
246
+ releaseComponentSubscriptions(this.applicationScope?.name ?? this.#appName);
247
+ }
248
+
226
249
  return this;
227
250
  }
228
251
 
@@ -548,7 +548,11 @@ export async function loadComponent(
548
548
  componentDirectory,
549
549
  configPath,
550
550
  applicationScope,
551
- origin
551
+ origin,
552
+ // authoritative root-ness: only root-load scopes watch THE root config and
553
+ // get the runtime env-config overlay (#1618) — an app component that happens
554
+ // to ship a root-named config file does not
555
+ isRoot
552
556
  );
553
557
 
554
558
  if (options.collectScopes) {