@shin1ohno/sage 0.10.0 → 0.11.0
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.
- package/dist/cli/mcp-handler.d.ts.map +1 -1
- package/dist/cli/mcp-handler.js +154 -6
- package/dist/cli/mcp-handler.js.map +1 -1
- package/dist/cli/signal-handler.d.ts +23 -0
- package/dist/cli/signal-handler.d.ts.map +1 -0
- package/dist/cli/signal-handler.js +51 -0
- package/dist/cli/signal-handler.js.map +1 -0
- package/dist/config/config-differ.d.ts +21 -0
- package/dist/config/config-differ.d.ts.map +1 -0
- package/dist/config/config-differ.js +194 -0
- package/dist/config/config-differ.js.map +1 -0
- package/dist/config/config-reload-service.d.ts +84 -0
- package/dist/config/config-reload-service.d.ts.map +1 -0
- package/dist/config/config-reload-service.js +234 -0
- package/dist/config/config-reload-service.js.map +1 -0
- package/dist/config/config-watcher.d.ts +78 -0
- package/dist/config/config-watcher.d.ts.map +1 -0
- package/dist/config/config-watcher.js +244 -0
- package/dist/config/config-watcher.js.map +1 -0
- package/dist/config/hot-reload-config.d.ts +16 -0
- package/dist/config/hot-reload-config.d.ts.map +1 -0
- package/dist/config/hot-reload-config.js +61 -0
- package/dist/config/hot-reload-config.js.map +1 -0
- package/dist/index.js +58 -0
- package/dist/index.js.map +1 -1
- package/dist/services/reloadable/calendar-source-manager-adapter.d.ts +45 -0
- package/dist/services/reloadable/calendar-source-manager-adapter.d.ts.map +1 -0
- package/dist/services/reloadable/calendar-source-manager-adapter.js +106 -0
- package/dist/services/reloadable/calendar-source-manager-adapter.js.map +1 -0
- package/dist/services/reloadable/index.d.ts +46 -0
- package/dist/services/reloadable/index.d.ts.map +1 -0
- package/dist/services/reloadable/index.js +88 -0
- package/dist/services/reloadable/index.js.map +1 -0
- package/dist/services/reloadable/notion-service-adapter.d.ts +45 -0
- package/dist/services/reloadable/notion-service-adapter.d.ts.map +1 -0
- package/dist/services/reloadable/notion-service-adapter.js +86 -0
- package/dist/services/reloadable/notion-service-adapter.js.map +1 -0
- package/dist/services/reloadable/priority-engine-adapter.d.ts +73 -0
- package/dist/services/reloadable/priority-engine-adapter.d.ts.map +1 -0
- package/dist/services/reloadable/priority-engine-adapter.js +105 -0
- package/dist/services/reloadable/priority-engine-adapter.js.map +1 -0
- package/dist/services/reloadable/reminder-manager-adapter.d.ts +45 -0
- package/dist/services/reloadable/reminder-manager-adapter.d.ts.map +1 -0
- package/dist/services/reloadable/reminder-manager-adapter.js +80 -0
- package/dist/services/reloadable/reminder-manager-adapter.js.map +1 -0
- package/dist/services/reloadable/todo-list-manager-adapter.d.ts +45 -0
- package/dist/services/reloadable/todo-list-manager-adapter.d.ts.map +1 -0
- package/dist/services/reloadable/todo-list-manager-adapter.js +80 -0
- package/dist/services/reloadable/todo-list-manager-adapter.js.map +1 -0
- package/dist/services/reloadable/working-cadence-adapter.d.ts +52 -0
- package/dist/services/reloadable/working-cadence-adapter.d.ts.map +1 -0
- package/dist/services/reloadable/working-cadence-adapter.js +77 -0
- package/dist/services/reloadable/working-cadence-adapter.js.map +1 -0
- package/dist/services/service-registry.d.ts +85 -0
- package/dist/services/service-registry.d.ts.map +1 -0
- package/dist/services/service-registry.js +166 -0
- package/dist/services/service-registry.js.map +1 -0
- package/dist/tools/config/index.d.ts +9 -0
- package/dist/tools/config/index.d.ts.map +1 -0
- package/dist/tools/config/index.js +8 -0
- package/dist/tools/config/index.js.map +1 -0
- package/dist/tools/config/reload-handler.d.ts +35 -0
- package/dist/tools/config/reload-handler.d.ts.map +1 -0
- package/dist/tools/config/reload-handler.js +63 -0
- package/dist/tools/config/reload-handler.js.map +1 -0
- package/dist/tools/integrations/handlers.d.ts +2 -0
- package/dist/tools/integrations/handlers.d.ts.map +1 -1
- package/dist/tools/integrations/handlers.js.map +1 -1
- package/dist/tools/setup/handlers.d.ts +2 -0
- package/dist/tools/setup/handlers.d.ts.map +1 -1
- package/dist/tools/setup/handlers.js +14 -0
- package/dist/tools/setup/handlers.js.map +1 -1
- package/dist/types/hot-reload.d.ts +131 -0
- package/dist/types/hot-reload.d.ts.map +1 -0
- package/dist/types/hot-reload.js +5 -0
- package/dist/types/hot-reload.js.map +1 -0
- package/dist/version.js +1 -1
- package/dist/version.js.map +1 -1
- package/package.json +1 -1
|
@@ -0,0 +1,84 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Configuration Reload Service
|
|
3
|
+
*
|
|
4
|
+
* Orchestrates configuration hot-reload functionality.
|
|
5
|
+
* Subscribes to ConfigWatcher events and coordinates service
|
|
6
|
+
* re-initialization through ServiceRegistry.
|
|
7
|
+
*/
|
|
8
|
+
import type { ReloadResult, ConfigReloadServiceOptions, ConfigWatcher } from '../types/hot-reload.js';
|
|
9
|
+
import type { UserConfig } from '../types/config.js';
|
|
10
|
+
import type { ServiceRegistry } from '../services/service-registry.js';
|
|
11
|
+
/**
|
|
12
|
+
* ConfigReloadService manages automatic configuration reloading
|
|
13
|
+
*
|
|
14
|
+
* Subscribes to file change events from ConfigWatcher, validates
|
|
15
|
+
* new configuration, calculates differences, and coordinates
|
|
16
|
+
* service re-initialization through ServiceRegistry.
|
|
17
|
+
*/
|
|
18
|
+
export declare class ConfigReloadService {
|
|
19
|
+
private readonly watcher;
|
|
20
|
+
private readonly serviceRegistry;
|
|
21
|
+
private readonly enableAutoReload;
|
|
22
|
+
private readonly onReloadCallback?;
|
|
23
|
+
private currentConfig;
|
|
24
|
+
private lastReloadResult;
|
|
25
|
+
private isReloading;
|
|
26
|
+
private pendingReload;
|
|
27
|
+
private changeHandler;
|
|
28
|
+
/**
|
|
29
|
+
* Create a new ConfigReloadService
|
|
30
|
+
*
|
|
31
|
+
* @param watcher - ConfigWatcher instance to subscribe to
|
|
32
|
+
* @param serviceRegistry - ServiceRegistry for coordinating service re-initialization
|
|
33
|
+
* @param options - Optional configuration options
|
|
34
|
+
*/
|
|
35
|
+
constructor(watcher: ConfigWatcher, serviceRegistry: ServiceRegistry, options?: ConfigReloadServiceOptions);
|
|
36
|
+
/**
|
|
37
|
+
* Start the reload service
|
|
38
|
+
*
|
|
39
|
+
* Loads initial configuration and subscribes to watcher events
|
|
40
|
+
* if auto-reload is enabled.
|
|
41
|
+
*/
|
|
42
|
+
start(): Promise<void>;
|
|
43
|
+
/**
|
|
44
|
+
* Stop the reload service
|
|
45
|
+
*
|
|
46
|
+
* Unsubscribes from watcher events and stops the watcher.
|
|
47
|
+
*/
|
|
48
|
+
stop(): void;
|
|
49
|
+
/**
|
|
50
|
+
* Check if auto-reload is enabled
|
|
51
|
+
*/
|
|
52
|
+
isAutoReloadEnabled(): boolean;
|
|
53
|
+
/**
|
|
54
|
+
* Get the result of the last reload operation
|
|
55
|
+
*/
|
|
56
|
+
getLastReloadResult(): ReloadResult | null;
|
|
57
|
+
/**
|
|
58
|
+
* Get the current configuration
|
|
59
|
+
*/
|
|
60
|
+
getCurrentConfig(): UserConfig | null;
|
|
61
|
+
/**
|
|
62
|
+
* Reload configuration manually or in response to file changes
|
|
63
|
+
*
|
|
64
|
+
* Implements reload lock mechanism to prevent concurrent reloads.
|
|
65
|
+
* If a reload is already in progress, queues the request and returns
|
|
66
|
+
* the same promise.
|
|
67
|
+
*
|
|
68
|
+
* @returns ReloadResult with details about the reload operation
|
|
69
|
+
*/
|
|
70
|
+
reload(): Promise<ReloadResult>;
|
|
71
|
+
/**
|
|
72
|
+
* Perform the actual reload operation
|
|
73
|
+
*/
|
|
74
|
+
private performReload;
|
|
75
|
+
/**
|
|
76
|
+
* Handle config file change events from watcher
|
|
77
|
+
*/
|
|
78
|
+
private handleConfigChange;
|
|
79
|
+
/**
|
|
80
|
+
* Invoke the onReload callback if configured
|
|
81
|
+
*/
|
|
82
|
+
private invokeCallback;
|
|
83
|
+
}
|
|
84
|
+
//# sourceMappingURL=config-reload-service.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"config-reload-service.d.ts","sourceRoot":"","sources":["../../src/config/config-reload-service.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAEH,OAAO,KAAK,EACV,YAAY,EACZ,0BAA0B,EAC1B,aAAa,EACd,MAAM,wBAAwB,CAAC;AAChC,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,oBAAoB,CAAC;AAGrD,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,iCAAiC,CAAC;AAKvE;;;;;;GAMG;AACH,qBAAa,mBAAmB;IAC9B,OAAO,CAAC,QAAQ,CAAC,OAAO,CAAgB;IACxC,OAAO,CAAC,QAAQ,CAAC,eAAe,CAAkB;IAClD,OAAO,CAAC,QAAQ,CAAC,gBAAgB,CAAU;IAC3C,OAAO,CAAC,QAAQ,CAAC,gBAAgB,CAAC,CAAiC;IAEnE,OAAO,CAAC,aAAa,CAA2B;IAChD,OAAO,CAAC,gBAAgB,CAA6B;IACrD,OAAO,CAAC,WAAW,CAAkB;IACrC,OAAO,CAAC,aAAa,CAAsC;IAC3D,OAAO,CAAC,aAAa,CAAyC;IAE9D;;;;;;OAMG;gBAED,OAAO,EAAE,aAAa,EACtB,eAAe,EAAE,eAAe,EAChC,OAAO,GAAE,0BAA+B;IAa1C;;;;;OAKG;IACG,KAAK,IAAI,OAAO,CAAC,IAAI,CAAC;IAuB5B;;;;OAIG;IACH,IAAI,IAAI,IAAI;IAeZ;;OAEG;IACH,mBAAmB,IAAI,OAAO;IAI9B;;OAEG;IACH,mBAAmB,IAAI,YAAY,GAAG,IAAI;IAI1C;;OAEG;IACH,gBAAgB,IAAI,UAAU,GAAG,IAAI;IAIrC;;;;;;;;OAQG;IACG,MAAM,IAAI,OAAO,CAAC,YAAY,CAAC;IA0BrC;;OAEG;YACW,aAAa;IAoG3B;;OAEG;IACH,OAAO,CAAC,kBAAkB;IAU1B;;OAEG;IACH,OAAO,CAAC,cAAc;CAYvB"}
|
|
@@ -0,0 +1,234 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Configuration Reload Service
|
|
3
|
+
*
|
|
4
|
+
* Orchestrates configuration hot-reload functionality.
|
|
5
|
+
* Subscribes to ConfigWatcher events and coordinates service
|
|
6
|
+
* re-initialization through ServiceRegistry.
|
|
7
|
+
*/
|
|
8
|
+
import { ConfigLoader } from './loader.js';
|
|
9
|
+
import { diffConfig, hasSignificantChanges } from './config-differ.js';
|
|
10
|
+
import { createLogger } from '../utils/logger.js';
|
|
11
|
+
const logger = createLogger('config-reload-service');
|
|
12
|
+
/**
|
|
13
|
+
* ConfigReloadService manages automatic configuration reloading
|
|
14
|
+
*
|
|
15
|
+
* Subscribes to file change events from ConfigWatcher, validates
|
|
16
|
+
* new configuration, calculates differences, and coordinates
|
|
17
|
+
* service re-initialization through ServiceRegistry.
|
|
18
|
+
*/
|
|
19
|
+
export class ConfigReloadService {
|
|
20
|
+
watcher;
|
|
21
|
+
serviceRegistry;
|
|
22
|
+
enableAutoReload;
|
|
23
|
+
onReloadCallback;
|
|
24
|
+
currentConfig = null;
|
|
25
|
+
lastReloadResult = null;
|
|
26
|
+
isReloading = false;
|
|
27
|
+
pendingReload = null;
|
|
28
|
+
changeHandler = null;
|
|
29
|
+
/**
|
|
30
|
+
* Create a new ConfigReloadService
|
|
31
|
+
*
|
|
32
|
+
* @param watcher - ConfigWatcher instance to subscribe to
|
|
33
|
+
* @param serviceRegistry - ServiceRegistry for coordinating service re-initialization
|
|
34
|
+
* @param options - Optional configuration options
|
|
35
|
+
*/
|
|
36
|
+
constructor(watcher, serviceRegistry, options = {}) {
|
|
37
|
+
this.watcher = watcher;
|
|
38
|
+
this.serviceRegistry = serviceRegistry;
|
|
39
|
+
this.enableAutoReload = options.enableAutoReload ?? true;
|
|
40
|
+
this.onReloadCallback = options.onReload;
|
|
41
|
+
logger.debug({ enableAutoReload: this.enableAutoReload }, 'ConfigReloadService initialized');
|
|
42
|
+
}
|
|
43
|
+
/**
|
|
44
|
+
* Start the reload service
|
|
45
|
+
*
|
|
46
|
+
* Loads initial configuration and subscribes to watcher events
|
|
47
|
+
* if auto-reload is enabled.
|
|
48
|
+
*/
|
|
49
|
+
async start() {
|
|
50
|
+
logger.info('Starting ConfigReloadService');
|
|
51
|
+
// Load initial configuration
|
|
52
|
+
try {
|
|
53
|
+
this.currentConfig = await ConfigLoader.load();
|
|
54
|
+
logger.debug('Initial configuration loaded');
|
|
55
|
+
}
|
|
56
|
+
catch (error) {
|
|
57
|
+
logger.error({ err: error }, 'Failed to load initial configuration');
|
|
58
|
+
throw error;
|
|
59
|
+
}
|
|
60
|
+
// Subscribe to watcher events if auto-reload is enabled
|
|
61
|
+
if (this.enableAutoReload) {
|
|
62
|
+
this.changeHandler = this.handleConfigChange.bind(this);
|
|
63
|
+
this.watcher.on('change', this.changeHandler);
|
|
64
|
+
logger.info('Subscribed to config watcher change events');
|
|
65
|
+
}
|
|
66
|
+
}
|
|
67
|
+
/**
|
|
68
|
+
* Stop the reload service
|
|
69
|
+
*
|
|
70
|
+
* Unsubscribes from watcher events and stops the watcher.
|
|
71
|
+
*/
|
|
72
|
+
stop() {
|
|
73
|
+
logger.info('Stopping ConfigReloadService');
|
|
74
|
+
// Unsubscribe from watcher events
|
|
75
|
+
if (this.changeHandler) {
|
|
76
|
+
this.watcher.off('change', this.changeHandler);
|
|
77
|
+
this.changeHandler = null;
|
|
78
|
+
logger.debug('Unsubscribed from config watcher change events');
|
|
79
|
+
}
|
|
80
|
+
// Stop the watcher
|
|
81
|
+
this.watcher.stop();
|
|
82
|
+
logger.info('ConfigReloadService stopped');
|
|
83
|
+
}
|
|
84
|
+
/**
|
|
85
|
+
* Check if auto-reload is enabled
|
|
86
|
+
*/
|
|
87
|
+
isAutoReloadEnabled() {
|
|
88
|
+
return this.enableAutoReload;
|
|
89
|
+
}
|
|
90
|
+
/**
|
|
91
|
+
* Get the result of the last reload operation
|
|
92
|
+
*/
|
|
93
|
+
getLastReloadResult() {
|
|
94
|
+
return this.lastReloadResult;
|
|
95
|
+
}
|
|
96
|
+
/**
|
|
97
|
+
* Get the current configuration
|
|
98
|
+
*/
|
|
99
|
+
getCurrentConfig() {
|
|
100
|
+
return this.currentConfig;
|
|
101
|
+
}
|
|
102
|
+
/**
|
|
103
|
+
* Reload configuration manually or in response to file changes
|
|
104
|
+
*
|
|
105
|
+
* Implements reload lock mechanism to prevent concurrent reloads.
|
|
106
|
+
* If a reload is already in progress, queues the request and returns
|
|
107
|
+
* the same promise.
|
|
108
|
+
*
|
|
109
|
+
* @returns ReloadResult with details about the reload operation
|
|
110
|
+
*/
|
|
111
|
+
async reload() {
|
|
112
|
+
// If already reloading, queue and return pending promise
|
|
113
|
+
if (this.isReloading && this.pendingReload) {
|
|
114
|
+
logger.debug('Reload already in progress, queueing request');
|
|
115
|
+
return this.pendingReload;
|
|
116
|
+
}
|
|
117
|
+
// Set reload lock
|
|
118
|
+
this.isReloading = true;
|
|
119
|
+
logger.info('Starting configuration reload');
|
|
120
|
+
const startTime = Date.now();
|
|
121
|
+
// Create and store the pending reload promise
|
|
122
|
+
this.pendingReload = this.performReload(startTime);
|
|
123
|
+
try {
|
|
124
|
+
const result = await this.pendingReload;
|
|
125
|
+
return result;
|
|
126
|
+
}
|
|
127
|
+
finally {
|
|
128
|
+
// Release reload lock
|
|
129
|
+
this.isReloading = false;
|
|
130
|
+
this.pendingReload = null;
|
|
131
|
+
}
|
|
132
|
+
}
|
|
133
|
+
/**
|
|
134
|
+
* Perform the actual reload operation
|
|
135
|
+
*/
|
|
136
|
+
async performReload(startTime) {
|
|
137
|
+
try {
|
|
138
|
+
// Load new configuration
|
|
139
|
+
logger.debug('Loading new configuration from disk');
|
|
140
|
+
const newConfig = await ConfigLoader.load();
|
|
141
|
+
// Calculate diff if we have a previous config
|
|
142
|
+
let changedSections = [];
|
|
143
|
+
if (this.currentConfig) {
|
|
144
|
+
const diff = diffConfig(this.currentConfig, newConfig);
|
|
145
|
+
logger.debug({
|
|
146
|
+
changedSections: diff.changedSections,
|
|
147
|
+
addedKeys: Object.keys(diff.addedKeys).length,
|
|
148
|
+
removedKeys: diff.removedKeys.length,
|
|
149
|
+
modifiedKeys: Object.keys(diff.modifiedKeys).length,
|
|
150
|
+
}, 'Configuration diff calculated');
|
|
151
|
+
// Check if there are significant changes
|
|
152
|
+
if (!hasSignificantChanges(diff)) {
|
|
153
|
+
logger.info('No significant configuration changes detected');
|
|
154
|
+
const result = {
|
|
155
|
+
success: true,
|
|
156
|
+
changedSections: [],
|
|
157
|
+
reinitializedServices: [],
|
|
158
|
+
timestamp: new Date().toISOString(),
|
|
159
|
+
durationMs: Date.now() - startTime,
|
|
160
|
+
};
|
|
161
|
+
this.lastReloadResult = result;
|
|
162
|
+
this.invokeCallback(result);
|
|
163
|
+
return result;
|
|
164
|
+
}
|
|
165
|
+
changedSections = diff.changedSections;
|
|
166
|
+
}
|
|
167
|
+
// Re-initialize affected services
|
|
168
|
+
logger.info({ changedSections }, 'Re-initializing services for changed sections');
|
|
169
|
+
const affectedServices = this.serviceRegistry.getServicesForSections(changedSections);
|
|
170
|
+
const reinitializedServices = affectedServices.map((s) => s.name);
|
|
171
|
+
await this.serviceRegistry.reinitializeForSections(changedSections, newConfig);
|
|
172
|
+
// Update current config
|
|
173
|
+
this.currentConfig = newConfig;
|
|
174
|
+
const durationMs = Date.now() - startTime;
|
|
175
|
+
logger.info({
|
|
176
|
+
changedSections,
|
|
177
|
+
reinitializedServices,
|
|
178
|
+
durationMs,
|
|
179
|
+
}, 'Configuration reload completed successfully');
|
|
180
|
+
const result = {
|
|
181
|
+
success: true,
|
|
182
|
+
changedSections,
|
|
183
|
+
reinitializedServices,
|
|
184
|
+
timestamp: new Date().toISOString(),
|
|
185
|
+
durationMs,
|
|
186
|
+
};
|
|
187
|
+
this.lastReloadResult = result;
|
|
188
|
+
this.invokeCallback(result);
|
|
189
|
+
return result;
|
|
190
|
+
}
|
|
191
|
+
catch (error) {
|
|
192
|
+
const durationMs = Date.now() - startTime;
|
|
193
|
+
const errorMessage = error instanceof Error ? error.message : String(error);
|
|
194
|
+
logger.error({
|
|
195
|
+
err: error,
|
|
196
|
+
durationMs,
|
|
197
|
+
}, 'Configuration reload failed');
|
|
198
|
+
const result = {
|
|
199
|
+
success: false,
|
|
200
|
+
changedSections: [],
|
|
201
|
+
reinitializedServices: [],
|
|
202
|
+
error: errorMessage,
|
|
203
|
+
timestamp: new Date().toISOString(),
|
|
204
|
+
durationMs,
|
|
205
|
+
};
|
|
206
|
+
this.lastReloadResult = result;
|
|
207
|
+
this.invokeCallback(result);
|
|
208
|
+
return result;
|
|
209
|
+
}
|
|
210
|
+
}
|
|
211
|
+
/**
|
|
212
|
+
* Handle config file change events from watcher
|
|
213
|
+
*/
|
|
214
|
+
handleConfigChange(path) {
|
|
215
|
+
logger.info({ path }, 'Config file change detected, triggering reload');
|
|
216
|
+
this.reload().catch((error) => {
|
|
217
|
+
logger.error({ err: error, path }, 'Failed to reload configuration after file change');
|
|
218
|
+
});
|
|
219
|
+
}
|
|
220
|
+
/**
|
|
221
|
+
* Invoke the onReload callback if configured
|
|
222
|
+
*/
|
|
223
|
+
invokeCallback(result) {
|
|
224
|
+
if (this.onReloadCallback) {
|
|
225
|
+
try {
|
|
226
|
+
this.onReloadCallback(result);
|
|
227
|
+
}
|
|
228
|
+
catch (error) {
|
|
229
|
+
logger.error({ err: error }, 'Error in onReload callback');
|
|
230
|
+
}
|
|
231
|
+
}
|
|
232
|
+
}
|
|
233
|
+
}
|
|
234
|
+
//# sourceMappingURL=config-reload-service.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"config-reload-service.js","sourceRoot":"","sources":["../../src/config/config-reload-service.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAQH,OAAO,EAAE,YAAY,EAAE,MAAM,aAAa,CAAC;AAC3C,OAAO,EAAE,UAAU,EAAE,qBAAqB,EAAE,MAAM,oBAAoB,CAAC;AAEvE,OAAO,EAAE,YAAY,EAAE,MAAM,oBAAoB,CAAC;AAElD,MAAM,MAAM,GAAG,YAAY,CAAC,uBAAuB,CAAC,CAAC;AAErD;;;;;;GAMG;AACH,MAAM,OAAO,mBAAmB;IACb,OAAO,CAAgB;IACvB,eAAe,CAAkB;IACjC,gBAAgB,CAAU;IAC1B,gBAAgB,CAAkC;IAE3D,aAAa,GAAsB,IAAI,CAAC;IACxC,gBAAgB,GAAwB,IAAI,CAAC;IAC7C,WAAW,GAAY,KAAK,CAAC;IAC7B,aAAa,GAAiC,IAAI,CAAC;IACnD,aAAa,GAAoC,IAAI,CAAC;IAE9D;;;;;;OAMG;IACH,YACE,OAAsB,EACtB,eAAgC,EAChC,UAAsC,EAAE;QAExC,IAAI,CAAC,OAAO,GAAG,OAAO,CAAC;QACvB,IAAI,CAAC,eAAe,GAAG,eAAe,CAAC;QACvC,IAAI,CAAC,gBAAgB,GAAG,OAAO,CAAC,gBAAgB,IAAI,IAAI,CAAC;QACzD,IAAI,CAAC,gBAAgB,GAAG,OAAO,CAAC,QAAQ,CAAC;QAEzC,MAAM,CAAC,KAAK,CACV,EAAE,gBAAgB,EAAE,IAAI,CAAC,gBAAgB,EAAE,EAC3C,iCAAiC,CAClC,CAAC;IACJ,CAAC;IAED;;;;;OAKG;IACH,KAAK,CAAC,KAAK;QACT,MAAM,CAAC,IAAI,CAAC,8BAA8B,CAAC,CAAC;QAE5C,6BAA6B;QAC7B,IAAI,CAAC;YACH,IAAI,CAAC,aAAa,GAAG,MAAM,YAAY,CAAC,IAAI,EAAE,CAAC;YAC/C,MAAM,CAAC,KAAK,CAAC,8BAA8B,CAAC,CAAC;QAC/C,CAAC;QAAC,OAAO,KAAK,EAAE,CAAC;YACf,MAAM,CAAC,KAAK,CACV,EAAE,GAAG,EAAE,KAAK,EAAE,EACd,sCAAsC,CACvC,CAAC;YACF,MAAM,KAAK,CAAC;QACd,CAAC;QAED,wDAAwD;QACxD,IAAI,IAAI,CAAC,gBAAgB,EAAE,CAAC;YAC1B,IAAI,CAAC,aAAa,GAAG,IAAI,CAAC,kBAAkB,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;YACxD,IAAI,CAAC,OAAO,CAAC,EAAE,CAAC,QAAQ,EAAE,IAAI,CAAC,aAAa,CAAC,CAAC;YAC9C,MAAM,CAAC,IAAI,CAAC,4CAA4C,CAAC,CAAC;QAC5D,CAAC;IACH,CAAC;IAED;;;;OAIG;IACH,IAAI;QACF,MAAM,CAAC,IAAI,CAAC,8BAA8B,CAAC,CAAC;QAE5C,kCAAkC;QAClC,IAAI,IAAI,CAAC,aAAa,EAAE,CAAC;YACvB,IAAI,CAAC,OAAO,CAAC,GAAG,CAAC,QAAQ,EAAE,IAAI,CAAC,aAAa,CAAC,CAAC;YAC/C,IAAI,CAAC,aAAa,GAAG,IAAI,CAAC;YAC1B,MAAM,CAAC,KAAK,CAAC,gDAAgD,CAAC,CAAC;QACjE,CAAC;QAED,mBAAmB;QACnB,IAAI,CAAC,OAAO,CAAC,IAAI,EAAE,CAAC;QACpB,MAAM,CAAC,IAAI,CAAC,6BAA6B,CAAC,CAAC;IAC7C,CAAC;IAED;;OAEG;IACH,mBAAmB;QACjB,OAAO,IAAI,CAAC,gBAAgB,CAAC;IAC/B,CAAC;IAED;;OAEG;IACH,mBAAmB;QACjB,OAAO,IAAI,CAAC,gBAAgB,CAAC;IAC/B,CAAC;IAED;;OAEG;IACH,gBAAgB;QACd,OAAO,IAAI,CAAC,aAAa,CAAC;IAC5B,CAAC;IAED;;;;;;;;OAQG;IACH,KAAK,CAAC,MAAM;QACV,yDAAyD;QACzD,IAAI,IAAI,CAAC,WAAW,IAAI,IAAI,CAAC,aAAa,EAAE,CAAC;YAC3C,MAAM,CAAC,KAAK,CAAC,8CAA8C,CAAC,CAAC;YAC7D,OAAO,IAAI,CAAC,aAAa,CAAC;QAC5B,CAAC;QAED,kBAAkB;QAClB,IAAI,CAAC,WAAW,GAAG,IAAI,CAAC;QACxB,MAAM,CAAC,IAAI,CAAC,+BAA+B,CAAC,CAAC;QAE7C,MAAM,SAAS,GAAG,IAAI,CAAC,GAAG,EAAE,CAAC;QAE7B,8CAA8C;QAC9C,IAAI,CAAC,aAAa,GAAG,IAAI,CAAC,aAAa,CAAC,SAAS,CAAC,CAAC;QAEnD,IAAI,CAAC;YACH,MAAM,MAAM,GAAG,MAAM,IAAI,CAAC,aAAa,CAAC;YACxC,OAAO,MAAM,CAAC;QAChB,CAAC;gBAAS,CAAC;YACT,sBAAsB;YACtB,IAAI,CAAC,WAAW,GAAG,KAAK,CAAC;YACzB,IAAI,CAAC,aAAa,GAAG,IAAI,CAAC;QAC5B,CAAC;IACH,CAAC;IAED;;OAEG;IACK,KAAK,CAAC,aAAa,CAAC,SAAiB;QAC3C,IAAI,CAAC;YACH,yBAAyB;YACzB,MAAM,CAAC,KAAK,CAAC,qCAAqC,CAAC,CAAC;YACpD,MAAM,SAAS,GAAG,MAAM,YAAY,CAAC,IAAI,EAAE,CAAC;YAE5C,8CAA8C;YAC9C,IAAI,eAAe,GAAa,EAAE,CAAC;YACnC,IAAI,IAAI,CAAC,aAAa,EAAE,CAAC;gBACvB,MAAM,IAAI,GAAG,UAAU,CAAC,IAAI,CAAC,aAAa,EAAE,SAAS,CAAC,CAAC;gBACvD,MAAM,CAAC,KAAK,CACV;oBACE,eAAe,EAAE,IAAI,CAAC,eAAe;oBACrC,SAAS,EAAE,MAAM,CAAC,IAAI,CAAC,IAAI,CAAC,SAAS,CAAC,CAAC,MAAM;oBAC7C,WAAW,EAAE,IAAI,CAAC,WAAW,CAAC,MAAM;oBACpC,YAAY,EAAE,MAAM,CAAC,IAAI,CAAC,IAAI,CAAC,YAAY,CAAC,CAAC,MAAM;iBACpD,EACD,+BAA+B,CAChC,CAAC;gBAEF,yCAAyC;gBACzC,IAAI,CAAC,qBAAqB,CAAC,IAAI,CAAC,EAAE,CAAC;oBACjC,MAAM,CAAC,IAAI,CAAC,+CAA+C,CAAC,CAAC;oBAC7D,MAAM,MAAM,GAAiB;wBAC3B,OAAO,EAAE,IAAI;wBACb,eAAe,EAAE,EAAE;wBACnB,qBAAqB,EAAE,EAAE;wBACzB,SAAS,EAAE,IAAI,IAAI,EAAE,CAAC,WAAW,EAAE;wBACnC,UAAU,EAAE,IAAI,CAAC,GAAG,EAAE,GAAG,SAAS;qBACnC,CAAC;oBACF,IAAI,CAAC,gBAAgB,GAAG,MAAM,CAAC;oBAC/B,IAAI,CAAC,cAAc,CAAC,MAAM,CAAC,CAAC;oBAC5B,OAAO,MAAM,CAAC;gBAChB,CAAC;gBAED,eAAe,GAAG,IAAI,CAAC,eAAe,CAAC;YACzC,CAAC;YAED,kCAAkC;YAClC,MAAM,CAAC,IAAI,CACT,EAAE,eAAe,EAAE,EACnB,+CAA+C,CAChD,CAAC;YAEF,MAAM,gBAAgB,GAAG,IAAI,CAAC,eAAe,CAAC,sBAAsB,CAAC,eAAe,CAAC,CAAC;YACtF,MAAM,qBAAqB,GAAG,gBAAgB,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC;YAElE,MAAM,IAAI,CAAC,eAAe,CAAC,uBAAuB,CAAC,eAAe,EAAE,SAAS,CAAC,CAAC;YAE/E,wBAAwB;YACxB,IAAI,CAAC,aAAa,GAAG,SAAS,CAAC;YAE/B,MAAM,UAAU,GAAG,IAAI,CAAC,GAAG,EAAE,GAAG,SAAS,CAAC;YAC1C,MAAM,CAAC,IAAI,CACT;gBACE,eAAe;gBACf,qBAAqB;gBACrB,UAAU;aACX,EACD,6CAA6C,CAC9C,CAAC;YAEF,MAAM,MAAM,GAAiB;gBAC3B,OAAO,EAAE,IAAI;gBACb,eAAe;gBACf,qBAAqB;gBACrB,SAAS,EAAE,IAAI,IAAI,EAAE,CAAC,WAAW,EAAE;gBACnC,UAAU;aACX,CAAC;YAEF,IAAI,CAAC,gBAAgB,GAAG,MAAM,CAAC;YAC/B,IAAI,CAAC,cAAc,CAAC,MAAM,CAAC,CAAC;YAC5B,OAAO,MAAM,CAAC;QAChB,CAAC;QAAC,OAAO,KAAK,EAAE,CAAC;YACf,MAAM,UAAU,GAAG,IAAI,CAAC,GAAG,EAAE,GAAG,SAAS,CAAC;YAC1C,MAAM,YAAY,GAAG,KAAK,YAAY,KAAK,CAAC,CAAC,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC;YAE5E,MAAM,CAAC,KAAK,CACV;gBACE,GAAG,EAAE,KAAK;gBACV,UAAU;aACX,EACD,6BAA6B,CAC9B,CAAC;YAEF,MAAM,MAAM,GAAiB;gBAC3B,OAAO,EAAE,KAAK;gBACd,eAAe,EAAE,EAAE;gBACnB,qBAAqB,EAAE,EAAE;gBACzB,KAAK,EAAE,YAAY;gBACnB,SAAS,EAAE,IAAI,IAAI,EAAE,CAAC,WAAW,EAAE;gBACnC,UAAU;aACX,CAAC;YAEF,IAAI,CAAC,gBAAgB,GAAG,MAAM,CAAC;YAC/B,IAAI,CAAC,cAAc,CAAC,MAAM,CAAC,CAAC;YAC5B,OAAO,MAAM,CAAC;QAChB,CAAC;IACH,CAAC;IAED;;OAEG;IACK,kBAAkB,CAAC,IAAY;QACrC,MAAM,CAAC,IAAI,CAAC,EAAE,IAAI,EAAE,EAAE,gDAAgD,CAAC,CAAC;QACxE,IAAI,CAAC,MAAM,EAAE,CAAC,KAAK,CAAC,CAAC,KAAK,EAAE,EAAE;YAC5B,MAAM,CAAC,KAAK,CACV,EAAE,GAAG,EAAE,KAAK,EAAE,IAAI,EAAE,EACpB,kDAAkD,CACnD,CAAC;QACJ,CAAC,CAAC,CAAC;IACL,CAAC;IAED;;OAEG;IACK,cAAc,CAAC,MAAoB;QACzC,IAAI,IAAI,CAAC,gBAAgB,EAAE,CAAC;YAC1B,IAAI,CAAC;gBACH,IAAI,CAAC,gBAAgB,CAAC,MAAM,CAAC,CAAC;YAChC,CAAC;YAAC,OAAO,KAAK,EAAE,CAAC;gBACf,MAAM,CAAC,KAAK,CACV,EAAE,GAAG,EAAE,KAAK,EAAE,EACd,4BAA4B,CAC7B,CAAC;YACJ,CAAC;QACH,CAAC;IACH,CAAC;CACF"}
|
|
@@ -0,0 +1,78 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Configuration File Watcher
|
|
3
|
+
* Watches user config and remote config files for changes
|
|
4
|
+
* and emits events with debouncing support.
|
|
5
|
+
*/
|
|
6
|
+
import { EventEmitter } from 'events';
|
|
7
|
+
import type { ConfigWatcherOptions, ConfigWatcherEvents } from '../types/hot-reload.js';
|
|
8
|
+
/**
|
|
9
|
+
* ConfigWatcher watches configuration files for changes
|
|
10
|
+
* and emits debounced 'change' events.
|
|
11
|
+
*/
|
|
12
|
+
export declare class ConfigWatcher extends EventEmitter {
|
|
13
|
+
private readonly debounceMs;
|
|
14
|
+
private readonly configPaths;
|
|
15
|
+
private watchers;
|
|
16
|
+
private debounceTimers;
|
|
17
|
+
private watching;
|
|
18
|
+
constructor(options?: ConfigWatcherOptions);
|
|
19
|
+
/**
|
|
20
|
+
* Start watching all configuration files
|
|
21
|
+
*/
|
|
22
|
+
start(): Promise<void>;
|
|
23
|
+
/**
|
|
24
|
+
* Stop watching all configuration files
|
|
25
|
+
*/
|
|
26
|
+
stop(): void;
|
|
27
|
+
/**
|
|
28
|
+
* Check if the watcher is currently active
|
|
29
|
+
*/
|
|
30
|
+
isWatching(): boolean;
|
|
31
|
+
/**
|
|
32
|
+
* Get the list of watched paths
|
|
33
|
+
*/
|
|
34
|
+
getWatchedPaths(): string[];
|
|
35
|
+
/**
|
|
36
|
+
* Watch a single file for changes
|
|
37
|
+
*/
|
|
38
|
+
private watchFile;
|
|
39
|
+
/**
|
|
40
|
+
* Watch for file creation when the file doesn't exist
|
|
41
|
+
*/
|
|
42
|
+
private watchForFileCreation;
|
|
43
|
+
/**
|
|
44
|
+
* Handle file change events
|
|
45
|
+
*/
|
|
46
|
+
private handleFileChange;
|
|
47
|
+
/**
|
|
48
|
+
* Handle potential file deletion
|
|
49
|
+
*/
|
|
50
|
+
private handlePotentialDeletion;
|
|
51
|
+
/**
|
|
52
|
+
* Emit debounced change event
|
|
53
|
+
*/
|
|
54
|
+
private emitDebouncedChange;
|
|
55
|
+
/**
|
|
56
|
+
* Handle watch errors
|
|
57
|
+
*/
|
|
58
|
+
private handleWatchError;
|
|
59
|
+
/**
|
|
60
|
+
* Check if a file exists
|
|
61
|
+
*/
|
|
62
|
+
private fileExists;
|
|
63
|
+
/**
|
|
64
|
+
* Get parent directory of a file path
|
|
65
|
+
*/
|
|
66
|
+
private getParentDirectory;
|
|
67
|
+
/**
|
|
68
|
+
* Get file name from a file path
|
|
69
|
+
*/
|
|
70
|
+
private getFileName;
|
|
71
|
+
/**
|
|
72
|
+
* Type-safe event emitter methods
|
|
73
|
+
*/
|
|
74
|
+
on<K extends keyof ConfigWatcherEvents>(event: K, listener: ConfigWatcherEvents[K]): this;
|
|
75
|
+
off<K extends keyof ConfigWatcherEvents>(event: K, listener: ConfigWatcherEvents[K]): this;
|
|
76
|
+
emit<K extends keyof ConfigWatcherEvents>(event: K, ...args: Parameters<ConfigWatcherEvents[K]>): boolean;
|
|
77
|
+
}
|
|
78
|
+
//# sourceMappingURL=config-watcher.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"config-watcher.d.ts","sourceRoot":"","sources":["../../src/config/config-watcher.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAEH,OAAO,EAAE,YAAY,EAAE,MAAM,QAAQ,CAAC;AAEtC,OAAO,KAAK,EAAE,oBAAoB,EAAE,mBAAmB,EAAE,MAAM,wBAAwB,CAAC;AAYxF;;;GAGG;AACH,qBAAa,aAAc,SAAQ,YAAY;IAC7C,OAAO,CAAC,QAAQ,CAAC,UAAU,CAAS;IACpC,OAAO,CAAC,QAAQ,CAAC,WAAW,CAAW;IACvC,OAAO,CAAC,QAAQ,CAAqC;IACrD,OAAO,CAAC,cAAc,CAA0C;IAChE,OAAO,CAAC,QAAQ,CAAkB;gBAEtB,OAAO,GAAE,oBAAyB;IAW9C;;OAEG;IACG,KAAK,IAAI,OAAO,CAAC,IAAI,CAAC;IAe5B;;OAEG;IACH,IAAI,IAAI,IAAI;IAwBZ;;OAEG;IACH,UAAU,IAAI,OAAO;IAIrB;;OAEG;IACH,eAAe,IAAI,MAAM,EAAE;IAI3B;;OAEG;YACW,SAAS;IA0BvB;;OAEG;YACW,oBAAoB;IAsClC;;OAEG;IACH,OAAO,CAAC,gBAAgB;IAaxB;;OAEG;YACW,uBAAuB;IAoBrC;;OAEG;IACH,OAAO,CAAC,mBAAmB;IAiB3B;;OAEG;IACH,OAAO,CAAC,gBAAgB;IAKxB;;OAEG;YACW,UAAU;IASxB;;OAEG;IACH,OAAO,CAAC,kBAAkB;IAK1B;;OAEG;IACH,OAAO,CAAC,WAAW;IAKnB;;OAEG;IACM,EAAE,CAAC,CAAC,SAAS,MAAM,mBAAmB,EAC7C,KAAK,EAAE,CAAC,EACR,QAAQ,EAAE,mBAAmB,CAAC,CAAC,CAAC,GAC/B,IAAI;IAIE,GAAG,CAAC,CAAC,SAAS,MAAM,mBAAmB,EAC9C,KAAK,EAAE,CAAC,EACR,QAAQ,EAAE,mBAAmB,CAAC,CAAC,CAAC,GAC/B,IAAI;IAIE,IAAI,CAAC,CAAC,SAAS,MAAM,mBAAmB,EAC/C,KAAK,EAAE,CAAC,EACR,GAAG,IAAI,EAAE,UAAU,CAAC,mBAAmB,CAAC,CAAC,CAAC,CAAC,GAC1C,OAAO;CAGX"}
|
|
@@ -0,0 +1,244 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Configuration File Watcher
|
|
3
|
+
* Watches user config and remote config files for changes
|
|
4
|
+
* and emits events with debouncing support.
|
|
5
|
+
*/
|
|
6
|
+
import { EventEmitter } from 'events';
|
|
7
|
+
import { watch, constants, promises as fsPromises } from 'fs';
|
|
8
|
+
import { ConfigLoader } from './loader.js';
|
|
9
|
+
import { DEFAULT_REMOTE_CONFIG_PATH } from '../cli/remote-config-loader.js';
|
|
10
|
+
import { createLogger } from '../utils/logger.js';
|
|
11
|
+
const logger = createLogger('config-watcher');
|
|
12
|
+
/**
|
|
13
|
+
* Default debounce delay in milliseconds
|
|
14
|
+
*/
|
|
15
|
+
const DEFAULT_DEBOUNCE_MS = 500;
|
|
16
|
+
/**
|
|
17
|
+
* ConfigWatcher watches configuration files for changes
|
|
18
|
+
* and emits debounced 'change' events.
|
|
19
|
+
*/
|
|
20
|
+
export class ConfigWatcher extends EventEmitter {
|
|
21
|
+
debounceMs;
|
|
22
|
+
configPaths;
|
|
23
|
+
watchers = new Map();
|
|
24
|
+
debounceTimers = new Map();
|
|
25
|
+
watching = false;
|
|
26
|
+
constructor(options = {}) {
|
|
27
|
+
super();
|
|
28
|
+
this.debounceMs = options.debounceMs ?? DEFAULT_DEBOUNCE_MS;
|
|
29
|
+
this.configPaths = options.configPaths ?? [
|
|
30
|
+
ConfigLoader.getConfigPath(),
|
|
31
|
+
DEFAULT_REMOTE_CONFIG_PATH,
|
|
32
|
+
];
|
|
33
|
+
logger.debug({ debounceMs: this.debounceMs, configPaths: this.configPaths }, 'ConfigWatcher initialized');
|
|
34
|
+
}
|
|
35
|
+
/**
|
|
36
|
+
* Start watching all configuration files
|
|
37
|
+
*/
|
|
38
|
+
async start() {
|
|
39
|
+
if (this.watching) {
|
|
40
|
+
logger.warn('ConfigWatcher is already watching');
|
|
41
|
+
return;
|
|
42
|
+
}
|
|
43
|
+
logger.info({ paths: this.configPaths }, 'Starting config file watcher');
|
|
44
|
+
for (const configPath of this.configPaths) {
|
|
45
|
+
await this.watchFile(configPath);
|
|
46
|
+
}
|
|
47
|
+
this.watching = true;
|
|
48
|
+
}
|
|
49
|
+
/**
|
|
50
|
+
* Stop watching all configuration files
|
|
51
|
+
*/
|
|
52
|
+
stop() {
|
|
53
|
+
if (!this.watching) {
|
|
54
|
+
logger.warn('ConfigWatcher is not watching');
|
|
55
|
+
return;
|
|
56
|
+
}
|
|
57
|
+
logger.info('Stopping config file watcher');
|
|
58
|
+
// Clear all debounce timers
|
|
59
|
+
Array.from(this.debounceTimers.values()).forEach((timer) => {
|
|
60
|
+
clearTimeout(timer);
|
|
61
|
+
});
|
|
62
|
+
this.debounceTimers.clear();
|
|
63
|
+
// Close all file watchers
|
|
64
|
+
Array.from(this.watchers.entries()).forEach(([watchPath, watcher]) => {
|
|
65
|
+
logger.debug({ path: watchPath }, 'Closing watcher');
|
|
66
|
+
watcher.close();
|
|
67
|
+
});
|
|
68
|
+
this.watchers.clear();
|
|
69
|
+
this.watching = false;
|
|
70
|
+
}
|
|
71
|
+
/**
|
|
72
|
+
* Check if the watcher is currently active
|
|
73
|
+
*/
|
|
74
|
+
isWatching() {
|
|
75
|
+
return this.watching;
|
|
76
|
+
}
|
|
77
|
+
/**
|
|
78
|
+
* Get the list of watched paths
|
|
79
|
+
*/
|
|
80
|
+
getWatchedPaths() {
|
|
81
|
+
return [...this.configPaths];
|
|
82
|
+
}
|
|
83
|
+
/**
|
|
84
|
+
* Watch a single file for changes
|
|
85
|
+
*/
|
|
86
|
+
async watchFile(filePath) {
|
|
87
|
+
try {
|
|
88
|
+
// Check if file exists before watching
|
|
89
|
+
const fileExists = await this.fileExists(filePath);
|
|
90
|
+
if (!fileExists) {
|
|
91
|
+
logger.warn({ path: filePath }, 'Config file does not exist, will watch for creation');
|
|
92
|
+
// Watch the parent directory for file creation
|
|
93
|
+
await this.watchForFileCreation(filePath);
|
|
94
|
+
return;
|
|
95
|
+
}
|
|
96
|
+
const watcher = watch(filePath, (_eventType, _filename) => {
|
|
97
|
+
this.handleFileChange(filePath, _eventType);
|
|
98
|
+
});
|
|
99
|
+
watcher.on('error', (error) => {
|
|
100
|
+
this.handleWatchError(filePath, error);
|
|
101
|
+
});
|
|
102
|
+
this.watchers.set(filePath, watcher);
|
|
103
|
+
logger.debug({ path: filePath }, 'Started watching config file');
|
|
104
|
+
}
|
|
105
|
+
catch (error) {
|
|
106
|
+
this.handleWatchError(filePath, error);
|
|
107
|
+
}
|
|
108
|
+
}
|
|
109
|
+
/**
|
|
110
|
+
* Watch for file creation when the file doesn't exist
|
|
111
|
+
*/
|
|
112
|
+
async watchForFileCreation(filePath) {
|
|
113
|
+
const parentDir = this.getParentDirectory(filePath);
|
|
114
|
+
const fileName = this.getFileName(filePath);
|
|
115
|
+
try {
|
|
116
|
+
// Check if parent directory exists
|
|
117
|
+
const parentExists = await this.fileExists(parentDir);
|
|
118
|
+
if (!parentExists) {
|
|
119
|
+
logger.warn({ path: parentDir }, 'Parent directory does not exist, cannot watch for file creation');
|
|
120
|
+
return;
|
|
121
|
+
}
|
|
122
|
+
const watcher = watch(parentDir, async (eventType, detectedFileName) => {
|
|
123
|
+
if (detectedFileName === fileName && eventType === 'rename') {
|
|
124
|
+
const fileNowExists = await this.fileExists(filePath);
|
|
125
|
+
if (fileNowExists) {
|
|
126
|
+
logger.info({ path: filePath }, 'Config file created, starting to watch');
|
|
127
|
+
// Close directory watcher and start file watcher
|
|
128
|
+
watcher.close();
|
|
129
|
+
this.watchers.delete(parentDir + ':' + fileName);
|
|
130
|
+
await this.watchFile(filePath);
|
|
131
|
+
// Emit change event for the newly created file
|
|
132
|
+
this.emitDebouncedChange(filePath);
|
|
133
|
+
}
|
|
134
|
+
}
|
|
135
|
+
});
|
|
136
|
+
watcher.on('error', (error) => {
|
|
137
|
+
logger.error({ path: parentDir, error: error.message }, 'Error watching parent directory');
|
|
138
|
+
});
|
|
139
|
+
this.watchers.set(parentDir + ':' + fileName, watcher);
|
|
140
|
+
logger.debug({ path: parentDir, fileName }, 'Watching parent directory for file creation');
|
|
141
|
+
}
|
|
142
|
+
catch (error) {
|
|
143
|
+
logger.error({ path: parentDir, error: error.message }, 'Failed to watch parent directory');
|
|
144
|
+
}
|
|
145
|
+
}
|
|
146
|
+
/**
|
|
147
|
+
* Handle file change events
|
|
148
|
+
*/
|
|
149
|
+
handleFileChange(filePath, eventType) {
|
|
150
|
+
logger.debug({ path: filePath, eventType }, 'File change detected');
|
|
151
|
+
if (eventType === 'rename') {
|
|
152
|
+
// File may have been deleted
|
|
153
|
+
this.handlePotentialDeletion(filePath);
|
|
154
|
+
return;
|
|
155
|
+
}
|
|
156
|
+
// Debounce the change event
|
|
157
|
+
this.emitDebouncedChange(filePath);
|
|
158
|
+
}
|
|
159
|
+
/**
|
|
160
|
+
* Handle potential file deletion
|
|
161
|
+
*/
|
|
162
|
+
async handlePotentialDeletion(filePath) {
|
|
163
|
+
const fileExists = await this.fileExists(filePath);
|
|
164
|
+
if (!fileExists) {
|
|
165
|
+
logger.warn({ path: filePath }, 'Config file was deleted, continuing with current config');
|
|
166
|
+
// Close the current watcher
|
|
167
|
+
const watcher = this.watchers.get(filePath);
|
|
168
|
+
if (watcher) {
|
|
169
|
+
watcher.close();
|
|
170
|
+
this.watchers.delete(filePath);
|
|
171
|
+
}
|
|
172
|
+
// Start watching for file recreation
|
|
173
|
+
await this.watchForFileCreation(filePath);
|
|
174
|
+
}
|
|
175
|
+
else {
|
|
176
|
+
// File was recreated or renamed back
|
|
177
|
+
logger.info({ path: filePath }, 'Config file recreated');
|
|
178
|
+
this.emitDebouncedChange(filePath);
|
|
179
|
+
}
|
|
180
|
+
}
|
|
181
|
+
/**
|
|
182
|
+
* Emit debounced change event
|
|
183
|
+
*/
|
|
184
|
+
emitDebouncedChange(filePath) {
|
|
185
|
+
// Clear existing timer for this path
|
|
186
|
+
const existingTimer = this.debounceTimers.get(filePath);
|
|
187
|
+
if (existingTimer) {
|
|
188
|
+
clearTimeout(existingTimer);
|
|
189
|
+
}
|
|
190
|
+
// Set new debounce timer
|
|
191
|
+
const timer = setTimeout(() => {
|
|
192
|
+
this.debounceTimers.delete(filePath);
|
|
193
|
+
logger.info({ path: filePath }, 'Emitting config change event');
|
|
194
|
+
this.emit('change', filePath);
|
|
195
|
+
}, this.debounceMs);
|
|
196
|
+
this.debounceTimers.set(filePath, timer);
|
|
197
|
+
}
|
|
198
|
+
/**
|
|
199
|
+
* Handle watch errors
|
|
200
|
+
*/
|
|
201
|
+
handleWatchError(filePath, error) {
|
|
202
|
+
logger.error({ path: filePath, error: error.message }, 'Error watching config file');
|
|
203
|
+
this.emit('error', error);
|
|
204
|
+
}
|
|
205
|
+
/**
|
|
206
|
+
* Check if a file exists
|
|
207
|
+
*/
|
|
208
|
+
async fileExists(filePath) {
|
|
209
|
+
try {
|
|
210
|
+
await fsPromises.access(filePath, constants.F_OK);
|
|
211
|
+
return true;
|
|
212
|
+
}
|
|
213
|
+
catch {
|
|
214
|
+
return false;
|
|
215
|
+
}
|
|
216
|
+
}
|
|
217
|
+
/**
|
|
218
|
+
* Get parent directory of a file path
|
|
219
|
+
*/
|
|
220
|
+
getParentDirectory(filePath) {
|
|
221
|
+
const lastSlash = filePath.lastIndexOf('/');
|
|
222
|
+
return lastSlash > 0 ? filePath.substring(0, lastSlash) : '/';
|
|
223
|
+
}
|
|
224
|
+
/**
|
|
225
|
+
* Get file name from a file path
|
|
226
|
+
*/
|
|
227
|
+
getFileName(filePath) {
|
|
228
|
+
const lastSlash = filePath.lastIndexOf('/');
|
|
229
|
+
return lastSlash >= 0 ? filePath.substring(lastSlash + 1) : filePath;
|
|
230
|
+
}
|
|
231
|
+
/**
|
|
232
|
+
* Type-safe event emitter methods
|
|
233
|
+
*/
|
|
234
|
+
on(event, listener) {
|
|
235
|
+
return super.on(event, listener);
|
|
236
|
+
}
|
|
237
|
+
off(event, listener) {
|
|
238
|
+
return super.off(event, listener);
|
|
239
|
+
}
|
|
240
|
+
emit(event, ...args) {
|
|
241
|
+
return super.emit(event, ...args);
|
|
242
|
+
}
|
|
243
|
+
}
|
|
244
|
+
//# sourceMappingURL=config-watcher.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"config-watcher.js","sourceRoot":"","sources":["../../src/config/config-watcher.ts"],"names":[],"mappings":"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"}
|