@centeredgesoft/runtime-mock-routes 2.11.0 → 2.11.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.
- package/build/server.js +49 -26
- package/package.json +3 -2
- package/server.ts +50 -32
package/build/server.js
CHANGED
|
@@ -66,7 +66,8 @@ const runServer = (sdk) => {
|
|
|
66
66
|
.option("--tlsKey <tlsKeyPath>", "Path to the TLS key file", process.env.HTTPS_CERT_KEY_FILE)
|
|
67
67
|
.option("-s, --seed <filePath>", "File path to seed the application", process.env.RUNTIME_MOCK_ROUTES_FILE_PATH)
|
|
68
68
|
.option("-a, --appVersion <appVersion>", "App Version to use", AppVersion.v2)
|
|
69
|
-
.option("-w, --watch", "Watch the seed file for changes and reload routes without restarting", false)
|
|
69
|
+
.option("-w, --watch", "Watch the seed file for changes and reload routes without restarting", false)
|
|
70
|
+
.option("--watch-poll [interval]", "Use stat polling instead of fs.watch (useful for network drives / containers / bind mounts). Optionally specify poll interval in ms.", false);
|
|
70
71
|
commander_1.program.parse(process.argv);
|
|
71
72
|
const seedFilePath = commander_1.program.seed ? (0, path_1.resolve)(commander_1.program.seed) : null;
|
|
72
73
|
// Tracks all files transitively required by the seed so we can bust & re-watch them
|
|
@@ -122,7 +123,8 @@ const runServer = (sdk) => {
|
|
|
122
123
|
// Watch seed and all transitive dependencies for changes
|
|
123
124
|
if (commander_1.program.watch && seedFilePath) {
|
|
124
125
|
let watchDebounce = null;
|
|
125
|
-
|
|
126
|
+
const usePolling = commander_1.program.watchPoll !== false;
|
|
127
|
+
const pollInterval = typeof commander_1.program.watchPoll === 'string' ? parseInt(commander_1.program.watchPoll, 10) || 300 : 300;
|
|
126
128
|
function doReload() {
|
|
127
129
|
watchDebounce = null;
|
|
128
130
|
try {
|
|
@@ -135,37 +137,58 @@ const runServer = (sdk) => {
|
|
|
135
137
|
console.error('🔥[watch]: Failed to reload seed:', err);
|
|
136
138
|
}
|
|
137
139
|
}
|
|
140
|
+
function scheduleReload() {
|
|
141
|
+
if (watchDebounce)
|
|
142
|
+
clearTimeout(watchDebounce);
|
|
143
|
+
watchDebounce = setTimeout(doReload, 100);
|
|
144
|
+
}
|
|
145
|
+
let watchedFiles = new Set();
|
|
146
|
+
let depWatchers = [];
|
|
138
147
|
function rewatchDeps() {
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
148
|
+
const deps = [...trackedDeps].filter(d => fs_1.default.existsSync(d));
|
|
149
|
+
if (usePolling) {
|
|
150
|
+
// Stop polling files no longer in the dep set
|
|
151
|
+
for (const f of watchedFiles) {
|
|
152
|
+
if (!trackedDeps.has(f)) {
|
|
153
|
+
fs_1.default.unwatchFile(f);
|
|
154
|
+
watchedFiles.delete(f);
|
|
155
|
+
}
|
|
146
156
|
}
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
});
|
|
157
|
-
w.on('error', (err) => {
|
|
158
|
-
console.error(`🔥[watch]: Watcher error for ${dep}:`, err);
|
|
159
|
-
});
|
|
160
|
-
depWatchers.push(w);
|
|
157
|
+
// Start polling new deps
|
|
158
|
+
for (const dep of deps) {
|
|
159
|
+
if (!watchedFiles.has(dep)) {
|
|
160
|
+
fs_1.default.watchFile(dep, { persistent: true, interval: pollInterval }, (curr, prev) => {
|
|
161
|
+
if (curr.mtimeMs !== prev.mtimeMs)
|
|
162
|
+
scheduleReload();
|
|
163
|
+
});
|
|
164
|
+
watchedFiles.add(dep);
|
|
165
|
+
}
|
|
161
166
|
}
|
|
162
|
-
|
|
163
|
-
|
|
167
|
+
}
|
|
168
|
+
else {
|
|
169
|
+
// Native fs.watch path
|
|
170
|
+
for (const w of depWatchers)
|
|
171
|
+
w.close();
|
|
172
|
+
depWatchers = [];
|
|
173
|
+
for (const dep of deps) {
|
|
174
|
+
try {
|
|
175
|
+
const w = fs_1.default.watch(dep, { persistent: true }, (eventType) => {
|
|
176
|
+
if (eventType !== 'change' && eventType !== 'rename')
|
|
177
|
+
return;
|
|
178
|
+
scheduleReload();
|
|
179
|
+
});
|
|
180
|
+
w.on('error', (err) => console.error(`🔥[watch]: Watcher error for ${dep}:`, err));
|
|
181
|
+
depWatchers.push(w);
|
|
182
|
+
}
|
|
183
|
+
catch (_a) {
|
|
184
|
+
// File may have been removed; skip
|
|
185
|
+
}
|
|
164
186
|
}
|
|
165
187
|
}
|
|
166
188
|
}
|
|
167
189
|
rewatchDeps();
|
|
168
|
-
|
|
190
|
+
const watchMode = usePolling ? `stat polling (${pollInterval}ms)` : 'fs.watch';
|
|
191
|
+
console.log(`🔥[watch]: Watching seed and ${trackedDeps.size} dep(s) via ${watchMode} — ${seedFilePath}`);
|
|
169
192
|
}
|
|
170
193
|
// Register signal handlers
|
|
171
194
|
process.on('SIGINT', () => gracefulShutdown('SIGINT'));
|
package/package.json
CHANGED
|
@@ -1,13 +1,14 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@centeredgesoft/runtime-mock-routes",
|
|
3
|
-
"version": "2.11.
|
|
3
|
+
"version": "2.11.1",
|
|
4
4
|
"description": "",
|
|
5
5
|
"main": "build/index.js",
|
|
6
6
|
"scripts": {
|
|
7
7
|
"build": "tsc --project ./",
|
|
8
8
|
"start": "nodemon -r dotenv/config index.ts",
|
|
9
9
|
"test": "jest",
|
|
10
|
-
"serve": "ts-node run.ts --watch
|
|
10
|
+
"serve": "ts-node run.ts --watch --seed ./example/seed.js",
|
|
11
|
+
"serve.poll": "ts-node run.ts --watch --watch-poll=5000 --seed ./example/seed.js"
|
|
11
12
|
},
|
|
12
13
|
"bin": {
|
|
13
14
|
"runtime-mock-routes": "./build/run.js"
|
package/server.ts
CHANGED
|
@@ -22,6 +22,7 @@ const runServer = (sdk: NodeSDK) => {
|
|
|
22
22
|
.option("-s, --seed <filePath>", "File path to seed the application", process.env.RUNTIME_MOCK_ROUTES_FILE_PATH)
|
|
23
23
|
.option("-a, --appVersion <appVersion>", "App Version to use", AppVersion.v2)
|
|
24
24
|
.option("-w, --watch", "Watch the seed file for changes and reload routes without restarting", false)
|
|
25
|
+
.option("--watch-poll [interval]", "Use stat polling instead of fs.watch (useful for network drives / containers / bind mounts). Optionally specify poll interval in ms.", false)
|
|
25
26
|
|
|
26
27
|
program.parse(process.argv);
|
|
27
28
|
|
|
@@ -85,16 +86,18 @@ const runServer = (sdk: NodeSDK) => {
|
|
|
85
86
|
httpServer.listen(program.port, () => {
|
|
86
87
|
console.log(`⚡️[server]: Server is running at http://localhost:${program.port}`);
|
|
87
88
|
});
|
|
89
|
+
|
|
88
90
|
if (httpsServer) {
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
91
|
+
httpsServer.listen(program.httpsPort, () => {
|
|
92
|
+
console.log(`⚡️[server]: Server is running at https://localhost:${program.httpsPort}`);
|
|
93
|
+
});
|
|
92
94
|
}
|
|
93
95
|
|
|
94
96
|
// Watch seed and all transitive dependencies for changes
|
|
95
97
|
if (program.watch && seedFilePath) {
|
|
96
98
|
let watchDebounce: ReturnType<typeof setTimeout> | null = null;
|
|
97
|
-
|
|
99
|
+
const usePolling = program.watchPoll !== false;
|
|
100
|
+
const pollInterval = typeof program.watchPoll === 'string' ? parseInt(program.watchPoll, 10) || 300 : 300;
|
|
98
101
|
|
|
99
102
|
function doReload() {
|
|
100
103
|
watchDebounce = null;
|
|
@@ -108,42 +111,57 @@ const runServer = (sdk: NodeSDK) => {
|
|
|
108
111
|
}
|
|
109
112
|
}
|
|
110
113
|
|
|
111
|
-
function
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
114
|
+
function scheduleReload() {
|
|
115
|
+
if (watchDebounce) clearTimeout(watchDebounce);
|
|
116
|
+
watchDebounce = setTimeout(doReload, 100);
|
|
117
|
+
}
|
|
115
118
|
|
|
116
|
-
|
|
119
|
+
let watchedFiles = new Set<string>();
|
|
120
|
+
let depWatchers: fs.FSWatcher[] = [];
|
|
117
121
|
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
122
|
+
function rewatchDeps() {
|
|
123
|
+
const deps = [...trackedDeps].filter(d => fs.existsSync(d));
|
|
124
|
+
|
|
125
|
+
if (usePolling) {
|
|
126
|
+
// Stop polling files no longer in the dep set
|
|
127
|
+
for (const f of watchedFiles) {
|
|
128
|
+
if (!trackedDeps.has(f)) {
|
|
129
|
+
fs.unwatchFile(f);
|
|
130
|
+
watchedFiles.delete(f);
|
|
131
|
+
}
|
|
121
132
|
}
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
}
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
133
|
+
// Start polling new deps
|
|
134
|
+
for (const dep of deps) {
|
|
135
|
+
if (!watchedFiles.has(dep)) {
|
|
136
|
+
fs.watchFile(dep, { persistent: true, interval: pollInterval }, (curr, prev) => {
|
|
137
|
+
if (curr.mtimeMs !== prev.mtimeMs) scheduleReload();
|
|
138
|
+
});
|
|
139
|
+
watchedFiles.add(dep);
|
|
140
|
+
}
|
|
141
|
+
}
|
|
142
|
+
} else {
|
|
143
|
+
// Native fs.watch path
|
|
144
|
+
for (const w of depWatchers) w.close();
|
|
145
|
+
depWatchers = [];
|
|
146
|
+
|
|
147
|
+
for (const dep of deps) {
|
|
148
|
+
try {
|
|
149
|
+
const w = fs.watch(dep, { persistent: true }, (eventType) => {
|
|
150
|
+
if (eventType !== 'change' && eventType !== 'rename') return;
|
|
151
|
+
scheduleReload();
|
|
152
|
+
});
|
|
153
|
+
w.on('error', (err) => console.error(`🔥[watch]: Watcher error for ${dep}:`, err));
|
|
154
|
+
depWatchers.push(w);
|
|
155
|
+
} catch {
|
|
156
|
+
// File may have been removed; skip
|
|
157
|
+
}
|
|
141
158
|
}
|
|
142
159
|
}
|
|
143
160
|
}
|
|
144
161
|
|
|
145
162
|
rewatchDeps();
|
|
146
|
-
|
|
163
|
+
const watchMode = usePolling ? `stat polling (${pollInterval}ms)` : 'fs.watch';
|
|
164
|
+
console.log(`🔥[watch]: Watching seed and ${trackedDeps.size} dep(s) via ${watchMode} — ${seedFilePath}`);
|
|
147
165
|
}
|
|
148
166
|
|
|
149
167
|
// Register signal handlers
|