nx 22.7.11 → 22.7.12
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/src/daemon/client/client.d.ts +2 -0
- package/dist/src/daemon/client/client.js +73 -7
- package/dist/src/daemon/client/daemon-environment.d.ts +11 -0
- package/dist/src/daemon/client/daemon-environment.js +20 -0
- package/dist/src/daemon/client/daemon-socket-messenger.d.ts +1 -1
- package/dist/src/daemon/client/daemon-socket-messenger.js +8 -3
- package/dist/src/daemon/is-v8-serializer-enabled.d.ts +2 -2
- package/dist/src/daemon/is-v8-serializer-enabled.js +2 -2
- package/dist/src/daemon/server/client-socket-context.d.ts +1 -1
- package/dist/src/daemon/server/client-socket-context.js +2 -3
- package/dist/src/daemon/server/handle-hash-tasks.js +1 -1
- package/dist/src/daemon/server/project-graph-incremental-recomputation.d.ts +1 -4
- package/dist/src/daemon/server/project-graph-incremental-recomputation.js +1 -14
- package/dist/src/daemon/server/server.js +17 -24
- package/dist/src/daemon/server/shutdown-utils.d.ts +1 -1
- package/dist/src/daemon/server/shutdown-utils.js +2 -2
- package/dist/src/daemon/socket-utils.d.ts +26 -7
- package/dist/src/daemon/socket-utils.js +45 -31
- package/dist/src/hasher/create-task-hasher.js +1 -1
- package/dist/src/native/nx.wasm32-wasi.debug.wasm +0 -0
- package/dist/src/native/nx.wasm32-wasi.wasm +0 -0
- package/dist/src/project-graph/build-project-graph.d.ts +2 -4
- package/dist/src/project-graph/build-project-graph.js +2 -7
- package/dist/src/project-graph/file-map-utils.d.ts +2 -4
- package/dist/src/project-graph/file-map-utils.js +0 -3
- package/dist/src/project-graph/plugins/isolation/isolated-plugin.js +18 -2
- package/dist/src/project-graph/plugins/isolation/messaging.js +1 -3
- package/dist/src/project-graph/plugins/isolation/plugin-worker.js +9 -1
- package/dist/src/project-graph/project-graph.js +4 -4
- package/dist/src/project-graph/utils/retrieve-workspace-files.d.ts +1 -2
- package/dist/src/project-graph/utils/retrieve-workspace-files.js +1 -3
- package/dist/src/tasks-runner/pseudo-ipc.js +14 -13
- package/dist/src/utils/consume-messages-from-socket.d.ts +42 -7
- package/dist/src/utils/consume-messages-from-socket.js +272 -38
- package/dist/src/utils/params.js +1 -1
- package/package.json +11 -11
|
@@ -33,10 +33,12 @@ export declare class DaemonClient {
|
|
|
33
33
|
private _daemonReady;
|
|
34
34
|
private fileWatcherMessenger;
|
|
35
35
|
private fileWatcherReconnecting;
|
|
36
|
+
private fileWatcherFramingFailures;
|
|
36
37
|
private fileWatcherCallbacks;
|
|
37
38
|
private fileWatcherConfigs;
|
|
38
39
|
private projectGraphListenerMessenger;
|
|
39
40
|
private projectGraphListenerReconnecting;
|
|
41
|
+
private projectGraphListenerFramingFailures;
|
|
40
42
|
private projectGraphListenerCallbacks;
|
|
41
43
|
enabled(): boolean;
|
|
42
44
|
reset(): void;
|
|
@@ -54,6 +54,11 @@ const WAIT_FOR_SERVER_CONFIG = {
|
|
|
54
54
|
delayMs: 10,
|
|
55
55
|
maxAttempts: 6000, // 6000 * 10ms = 60 seconds
|
|
56
56
|
};
|
|
57
|
+
/**
|
|
58
|
+
* A framing failure repeats on every redial, so the watcher channels stop
|
|
59
|
+
* re-dialing once this many land back to back without a message in between.
|
|
60
|
+
*/
|
|
61
|
+
const MAX_CONSECUTIVE_FRAMING_FAILURES = 3;
|
|
57
62
|
class DaemonClient {
|
|
58
63
|
constructor() {
|
|
59
64
|
// Tracks the spinner owned by the in-flight request so streamed
|
|
@@ -65,9 +70,11 @@ class DaemonClient {
|
|
|
65
70
|
this._waitForDaemonReady = null;
|
|
66
71
|
this._daemonReady = null;
|
|
67
72
|
this.fileWatcherReconnecting = false;
|
|
73
|
+
this.fileWatcherFramingFailures = 0;
|
|
68
74
|
this.fileWatcherCallbacks = new Map();
|
|
69
75
|
this.fileWatcherConfigs = new Map();
|
|
70
76
|
this.projectGraphListenerReconnecting = false;
|
|
77
|
+
this.projectGraphListenerFramingFailures = 0;
|
|
71
78
|
this.projectGraphListenerCallbacks = new Map();
|
|
72
79
|
this.envReflectionSent = false;
|
|
73
80
|
try {
|
|
@@ -220,6 +227,8 @@ class DaemonClient {
|
|
|
220
227
|
this.fileWatcherMessenger = new daemon_socket_messenger_1.DaemonSocketMessenger((0, net_1.connect)(socketPath)).listen((message) => {
|
|
221
228
|
try {
|
|
222
229
|
const parsedMessage = (0, consume_messages_from_socket_1.parseMessage)(message);
|
|
230
|
+
// A delivered message means the stream is healthy again.
|
|
231
|
+
this.fileWatcherFramingFailures = 0;
|
|
223
232
|
// Notify all callbacks
|
|
224
233
|
for (const cb of this.fileWatcherCallbacks.values()) {
|
|
225
234
|
cb(null, parsedMessage);
|
|
@@ -248,6 +257,12 @@ class DaemonClient {
|
|
|
248
257
|
for (const cb of this.fileWatcherCallbacks.values()) {
|
|
249
258
|
cb(err, null);
|
|
250
259
|
}
|
|
260
|
+
if (err instanceof consume_messages_from_socket_1.MessageFramingError) {
|
|
261
|
+
this.fileWatcherFramingFailures++;
|
|
262
|
+
}
|
|
263
|
+
// Close so 'close' fires and the reconnect path runs; a framing
|
|
264
|
+
// failure would otherwise leave this channel silent forever.
|
|
265
|
+
this.fileWatcherMessenger?.close();
|
|
251
266
|
});
|
|
252
267
|
this.fileWatcherMessenger.sendMessage({
|
|
253
268
|
type: 'REGISTER_FILE_WATCHER',
|
|
@@ -270,6 +285,19 @@ class DaemonClient {
|
|
|
270
285
|
if (this.fileWatcherReconnecting) {
|
|
271
286
|
return;
|
|
272
287
|
}
|
|
288
|
+
// The concurrency guard above is cleared before this method recurses, so it
|
|
289
|
+
// bounds overlap rather than iterations. A framing failure is deterministic
|
|
290
|
+
// — re-dialing replays it — so without this the channel would reconnect and
|
|
291
|
+
// re-fail forever. Reaching a payload over NX_MAX_MESSAGE_SIZE does exactly
|
|
292
|
+
// that on every notification.
|
|
293
|
+
if (this.fileWatcherFramingFailures >= MAX_CONSECUTIVE_FRAMING_FAILURES) {
|
|
294
|
+
logger_1.clientLogger.log(`[FileWatcher] Giving up after ${this.fileWatcherFramingFailures} consecutive framing failures`);
|
|
295
|
+
this.fileWatcherReconnecting = false;
|
|
296
|
+
for (const cb of this.fileWatcherCallbacks.values()) {
|
|
297
|
+
cb('closed', null);
|
|
298
|
+
}
|
|
299
|
+
return;
|
|
300
|
+
}
|
|
273
301
|
if (this.fileWatcherCallbacks.size === 0) {
|
|
274
302
|
return; // No callbacks to reconnect
|
|
275
303
|
}
|
|
@@ -306,6 +334,8 @@ class DaemonClient {
|
|
|
306
334
|
this.fileWatcherMessenger = new daemon_socket_messenger_1.DaemonSocketMessenger((0, net_1.connect)(socketPath)).listen((message) => {
|
|
307
335
|
try {
|
|
308
336
|
const parsedMessage = (0, consume_messages_from_socket_1.parseMessage)(message);
|
|
337
|
+
// A delivered message means the stream is healthy again.
|
|
338
|
+
this.fileWatcherFramingFailures = 0;
|
|
309
339
|
for (const cb of this.fileWatcherCallbacks.values()) {
|
|
310
340
|
cb(null, parsedMessage);
|
|
311
341
|
}
|
|
@@ -331,7 +361,12 @@ class DaemonClient {
|
|
|
331
361
|
}
|
|
332
362
|
process.exit(1);
|
|
333
363
|
}
|
|
334
|
-
|
|
364
|
+
if (err instanceof consume_messages_from_socket_1.MessageFramingError) {
|
|
365
|
+
this.fileWatcherFramingFailures++;
|
|
366
|
+
}
|
|
367
|
+
// The retry loop is driven by 'close', which a framing failure does
|
|
368
|
+
// not emit, so close explicitly to hand off to it.
|
|
369
|
+
this.fileWatcherMessenger?.close();
|
|
335
370
|
});
|
|
336
371
|
// Re-register all stored configs
|
|
337
372
|
for (const cfg of this.fileWatcherConfigs.values()) {
|
|
@@ -374,6 +409,8 @@ class DaemonClient {
|
|
|
374
409
|
this.projectGraphListenerMessenger = new daemon_socket_messenger_1.DaemonSocketMessenger((0, net_1.connect)(socketPath)).listen((message) => {
|
|
375
410
|
try {
|
|
376
411
|
const parsedMessage = (0, consume_messages_from_socket_1.parseMessage)(message);
|
|
412
|
+
// A delivered message means the stream is healthy again.
|
|
413
|
+
this.projectGraphListenerFramingFailures = 0;
|
|
377
414
|
// Notify all callbacks
|
|
378
415
|
for (const cb of this.projectGraphListenerCallbacks.values()) {
|
|
379
416
|
cb(null, parsedMessage);
|
|
@@ -402,6 +439,10 @@ class DaemonClient {
|
|
|
402
439
|
for (const cb of this.projectGraphListenerCallbacks.values()) {
|
|
403
440
|
cb(err, null);
|
|
404
441
|
}
|
|
442
|
+
if (err instanceof consume_messages_from_socket_1.MessageFramingError) {
|
|
443
|
+
this.projectGraphListenerFramingFailures++;
|
|
444
|
+
}
|
|
445
|
+
this.projectGraphListenerMessenger?.close();
|
|
405
446
|
});
|
|
406
447
|
this.projectGraphListenerMessenger.sendMessage({
|
|
407
448
|
type: register_project_graph_listener_1.REGISTER_PROJECT_GRAPH_LISTENER,
|
|
@@ -422,6 +463,17 @@ class DaemonClient {
|
|
|
422
463
|
if (this.projectGraphListenerReconnecting) {
|
|
423
464
|
return;
|
|
424
465
|
}
|
|
466
|
+
// See reconnectFileWatcher: a framing failure repeats on every redial, so
|
|
467
|
+
// the concurrency guard alone cannot bound it.
|
|
468
|
+
if (this.projectGraphListenerFramingFailures >=
|
|
469
|
+
MAX_CONSECUTIVE_FRAMING_FAILURES) {
|
|
470
|
+
logger_1.clientLogger.log(`[ProjectGraphListener] Giving up after ${this.projectGraphListenerFramingFailures} consecutive framing failures`);
|
|
471
|
+
this.projectGraphListenerReconnecting = false;
|
|
472
|
+
for (const cb of this.projectGraphListenerCallbacks.values()) {
|
|
473
|
+
cb('closed', null);
|
|
474
|
+
}
|
|
475
|
+
return;
|
|
476
|
+
}
|
|
425
477
|
if (this.projectGraphListenerCallbacks.size === 0) {
|
|
426
478
|
return; // No callbacks to reconnect
|
|
427
479
|
}
|
|
@@ -458,6 +510,8 @@ class DaemonClient {
|
|
|
458
510
|
this.projectGraphListenerMessenger = new daemon_socket_messenger_1.DaemonSocketMessenger((0, net_1.connect)(socketPath)).listen((message) => {
|
|
459
511
|
try {
|
|
460
512
|
const parsedMessage = (0, consume_messages_from_socket_1.parseMessage)(message);
|
|
513
|
+
// A delivered message means the stream is healthy again.
|
|
514
|
+
this.projectGraphListenerFramingFailures = 0;
|
|
461
515
|
for (const cb of this.projectGraphListenerCallbacks.values()) {
|
|
462
516
|
cb(null, parsedMessage);
|
|
463
517
|
}
|
|
@@ -483,7 +537,12 @@ class DaemonClient {
|
|
|
483
537
|
}
|
|
484
538
|
process.exit(1);
|
|
485
539
|
}
|
|
486
|
-
|
|
540
|
+
if (err instanceof consume_messages_from_socket_1.MessageFramingError) {
|
|
541
|
+
this.projectGraphListenerFramingFailures++;
|
|
542
|
+
}
|
|
543
|
+
// The retry loop is driven by 'close', which a framing failure does
|
|
544
|
+
// not emit, so close explicitly to hand off to it.
|
|
545
|
+
this.projectGraphListenerMessenger?.close();
|
|
487
546
|
});
|
|
488
547
|
// Re-register
|
|
489
548
|
this.projectGraphListenerMessenger.sendMessage({
|
|
@@ -768,8 +827,14 @@ class DaemonClient {
|
|
|
768
827
|
this.handleConnectionError(daemonProcessException('Daemon process terminated and closed the connection'));
|
|
769
828
|
}
|
|
770
829
|
}, (err) => {
|
|
830
|
+
// Every recovery path below is keyed on the socket 'close' event, and a
|
|
831
|
+
// framing failure emits neither 'close' nor 'error'. Without the
|
|
832
|
+
// teardown at the end of this handler the connection stays open and
|
|
833
|
+
// permanently deaf, and the next request waits out the keep-alive.
|
|
771
834
|
if (!err.message) {
|
|
772
|
-
|
|
835
|
+
this.currentReject(daemonProcessException(err.toString()));
|
|
836
|
+
this.socketMessenger?.close();
|
|
837
|
+
return;
|
|
773
838
|
}
|
|
774
839
|
let error;
|
|
775
840
|
if (err.message.startsWith('connect ENOENT')) {
|
|
@@ -790,6 +855,7 @@ class DaemonClient {
|
|
|
790
855
|
error = daemonProcessException(err.toString());
|
|
791
856
|
}
|
|
792
857
|
this.currentReject(error);
|
|
858
|
+
this.socketMessenger?.close();
|
|
793
859
|
});
|
|
794
860
|
}
|
|
795
861
|
async handleConnectionError(error) {
|
|
@@ -960,9 +1026,9 @@ class DaemonClient {
|
|
|
960
1026
|
}
|
|
961
1027
|
}
|
|
962
1028
|
catch (e) {
|
|
963
|
-
const endOfResponse = serializedResult
|
|
964
|
-
|
|
965
|
-
|
|
1029
|
+
const endOfResponse = (0, consume_messages_from_socket_1.describeMessage)(serializedResult, {
|
|
1030
|
+
from: 'end',
|
|
1031
|
+
});
|
|
966
1032
|
this.currentReject(daemonProcessException([
|
|
967
1033
|
'Could not deserialize response from Nx daemon.',
|
|
968
1034
|
`Message: ${e.message}`,
|
|
@@ -999,7 +1065,7 @@ class DaemonClient {
|
|
|
999
1065
|
detached: true,
|
|
1000
1066
|
windowsHide: true,
|
|
1001
1067
|
shell: false,
|
|
1002
|
-
env: (0, daemon_environment_1.
|
|
1068
|
+
env: (0, daemon_environment_1.getDaemonSpawnEnv)(),
|
|
1003
1069
|
});
|
|
1004
1070
|
// The child now owns dup'd copies of the descriptors, so release ours.
|
|
1005
1071
|
(0, node_fs_1.closeSync)(outFd);
|
|
@@ -2,3 +2,14 @@ export declare function getDaemonEnv(): NodeJS.ProcessEnv & {
|
|
|
2
2
|
NX_PROJECT_GLOB_CACHE: string;
|
|
3
3
|
NX_CACHE_PROJECTS_CONFIG: string;
|
|
4
4
|
};
|
|
5
|
+
/**
|
|
6
|
+
* Env for spawning the daemon process. On top of the reflected env it re-adds
|
|
7
|
+
* NX_MAX_MESSAGE_SIZE, which is excluded from reflection (see above) so one
|
|
8
|
+
* client cannot change the ceiling for another. The value here therefore holds
|
|
9
|
+
* for the daemon's whole lifetime, and changing it needs a daemon restart
|
|
10
|
+
* (`nx reset`).
|
|
11
|
+
*/
|
|
12
|
+
export declare function getDaemonSpawnEnv(): NodeJS.ProcessEnv & {
|
|
13
|
+
NX_PROJECT_GLOB_CACHE: string;
|
|
14
|
+
NX_CACHE_PROJECTS_CONFIG: string;
|
|
15
|
+
};
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
3
|
exports.getDaemonEnv = getDaemonEnv;
|
|
4
|
+
exports.getDaemonSpawnEnv = getDaemonSpawnEnv;
|
|
4
5
|
const DAEMON_ENV_REQUIRED_SETTINGS = {
|
|
5
6
|
NX_PROJECT_GLOB_CACHE: 'false',
|
|
6
7
|
NX_CACHE_PROJECTS_CONFIG: 'false',
|
|
@@ -16,6 +17,11 @@ const DAEMON_ENV_OVERRIDABLE_SETTINGS = {
|
|
|
16
17
|
* (e.g. PATH, JAVA_HOME, GRADLE_HOME) should be allowed through.
|
|
17
18
|
*/
|
|
18
19
|
const DAEMON_ENV_VARS_EXCLUSIONS = new Set([
|
|
20
|
+
// The message-size ceiling is read when a connection is accepted, before the
|
|
21
|
+
// client's env could apply, so reflecting it would hand one client's value to
|
|
22
|
+
// the next client's connection. Pinned at daemon spawn instead; see
|
|
23
|
+
// getDaemonSpawnEnv.
|
|
24
|
+
'NX_MAX_MESSAGE_SIZE',
|
|
19
25
|
// Nx task-scoped vars
|
|
20
26
|
'NX_TASK_TARGET_CONFIGURATION',
|
|
21
27
|
'NX_TASK_TARGET_PROJECT',
|
|
@@ -119,3 +125,17 @@ function getDaemonEnv() {
|
|
|
119
125
|
}
|
|
120
126
|
return Object.assign(env, DAEMON_ENV_REQUIRED_SETTINGS);
|
|
121
127
|
}
|
|
128
|
+
/**
|
|
129
|
+
* Env for spawning the daemon process. On top of the reflected env it re-adds
|
|
130
|
+
* NX_MAX_MESSAGE_SIZE, which is excluded from reflection (see above) so one
|
|
131
|
+
* client cannot change the ceiling for another. The value here therefore holds
|
|
132
|
+
* for the daemon's whole lifetime, and changing it needs a daemon restart
|
|
133
|
+
* (`nx reset`).
|
|
134
|
+
*/
|
|
135
|
+
function getDaemonSpawnEnv() {
|
|
136
|
+
const env = getDaemonEnv();
|
|
137
|
+
if (process.env.NX_MAX_MESSAGE_SIZE !== undefined) {
|
|
138
|
+
env.NX_MAX_MESSAGE_SIZE = process.env.NX_MAX_MESSAGE_SIZE;
|
|
139
|
+
}
|
|
140
|
+
return env;
|
|
141
|
+
}
|
|
@@ -7,6 +7,6 @@ export declare class DaemonSocketMessenger {
|
|
|
7
7
|
private socket;
|
|
8
8
|
constructor(socket: Socket);
|
|
9
9
|
sendMessage<T extends DaemonMessage>(messageToDaemon: T, force?: 'v8' | 'json'): void;
|
|
10
|
-
listen(onData: (message:
|
|
10
|
+
listen(onData: (message: Buffer) => void, onClose?: () => void, onError?: (err: Error) => void): DaemonSocketMessenger;
|
|
11
11
|
close(): void;
|
|
12
12
|
}
|
|
@@ -30,9 +30,7 @@ class DaemonSocketMessenger {
|
|
|
30
30
|
const serialized = (0, socket_utils_1.serialize)(messageToDaemon, force);
|
|
31
31
|
perf_hooks_1.performance.mark('daemon-message-serialization-end-' + messageToDaemon.type);
|
|
32
32
|
perf_hooks_1.performance.measure('daemon-message-serialization-' + messageToDaemon.type, 'daemon-message-serialization-start-' + messageToDaemon.type, 'daemon-message-serialization-end-' + messageToDaemon.type);
|
|
33
|
-
this.socket
|
|
34
|
-
// send EOT to indicate that the message has been fully written
|
|
35
|
-
this.socket.write(consume_messages_from_socket_1.MESSAGE_END_SEQ);
|
|
33
|
+
(0, consume_messages_from_socket_1.writeMessage)(this.socket, serialized);
|
|
36
34
|
logger_1.clientLogger.log('[Messenger] Message sent');
|
|
37
35
|
}
|
|
38
36
|
listen(onData, onClose = () => { }, onError = () => { }) {
|
|
@@ -45,6 +43,13 @@ class DaemonSocketMessenger {
|
|
|
45
43
|
this.socket.on('data', (0, consume_messages_from_socket_1.consumeMessagesFromSocket)(async (message) => {
|
|
46
44
|
logger_1.clientLogger.log('[Messenger] Received message, length:', message.length);
|
|
47
45
|
onData(message);
|
|
46
|
+
},
|
|
47
|
+
// A framing failure leaves the socket open and writable, so nothing
|
|
48
|
+
// else settles the in-flight request. Route it to the same handler as
|
|
49
|
+
// a socket error rather than waiting for the keep-alive timeout.
|
|
50
|
+
(err) => {
|
|
51
|
+
logger_1.clientLogger.log('[Messenger] Framing error:', err.message);
|
|
52
|
+
onError(err);
|
|
48
53
|
}));
|
|
49
54
|
logger_1.clientLogger.log('[Messenger] listen() complete');
|
|
50
55
|
return this;
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
/**
|
|
2
|
-
*
|
|
3
|
-
*
|
|
2
|
+
* Off by default; set `NX_USE_V8_SERIALIZER=true` to opt in. Governs every Nx
|
|
3
|
+
* socket channel: the daemon client and server, plugin workers, and pseudo-IPC.
|
|
4
4
|
*/
|
|
5
5
|
export declare function isV8SerializerEnabled(): boolean;
|
|
@@ -2,8 +2,8 @@
|
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
3
|
exports.isV8SerializerEnabled = isV8SerializerEnabled;
|
|
4
4
|
/**
|
|
5
|
-
*
|
|
6
|
-
*
|
|
5
|
+
* Off by default; set `NX_USE_V8_SERIALIZER=true` to opt in. Governs every Nx
|
|
6
|
+
* socket channel: the daemon client and server, plugin workers, and pseudo-IPC.
|
|
7
7
|
*/
|
|
8
8
|
function isV8SerializerEnabled() {
|
|
9
9
|
return process.env.NX_USE_V8_SERIALIZER === 'true';
|
|
@@ -7,7 +7,7 @@ export declare function getTopicSubscribers(topic: ProgressTopic): Set<Socket>;
|
|
|
7
7
|
export declare function assertOnDaemon(helperName: string): void;
|
|
8
8
|
/**
|
|
9
9
|
* Writes a streaming message over the given socket using the daemon's
|
|
10
|
-
* configured serialization format
|
|
10
|
+
* configured serialization format, length-prefixed like any other message.
|
|
11
11
|
* Errors are logged to the daemon's stdout (redirected to the daemon
|
|
12
12
|
* log) rather than propagated — a disconnected client shouldn't tear
|
|
13
13
|
* down the current request handler or other subscribers.
|
|
@@ -7,7 +7,6 @@ exports.assertOnDaemon = assertOnDaemon;
|
|
|
7
7
|
exports.writeStreamingMessage = writeStreamingMessage;
|
|
8
8
|
exports.sendProgressMessageToTopic = sendProgressMessageToTopic;
|
|
9
9
|
exports.sendEmitLogMessageToTopic = sendEmitLogMessageToTopic;
|
|
10
|
-
const consume_messages_from_socket_1 = require("../../utils/consume-messages-from-socket");
|
|
11
10
|
const is_on_daemon_1 = require("../is-on-daemon");
|
|
12
11
|
const logger_1 = require("../logger");
|
|
13
12
|
const streaming_messages_1 = require("../message-types/streaming-messages");
|
|
@@ -43,7 +42,7 @@ function assertOnDaemon(helperName) {
|
|
|
43
42
|
}
|
|
44
43
|
/**
|
|
45
44
|
* Writes a streaming message over the given socket using the daemon's
|
|
46
|
-
* configured serialization format
|
|
45
|
+
* configured serialization format, length-prefixed like any other message.
|
|
47
46
|
* Errors are logged to the daemon's stdout (redirected to the daemon
|
|
48
47
|
* log) rather than propagated — a disconnected client shouldn't tear
|
|
49
48
|
* down the current request handler or other subscribers.
|
|
@@ -51,7 +50,7 @@ function assertOnDaemon(helperName) {
|
|
|
51
50
|
function writeStreamingMessage(socket, payload, description) {
|
|
52
51
|
try {
|
|
53
52
|
logger_1.serverLogger.log('Streaming message to client:', description);
|
|
54
|
-
|
|
53
|
+
(0, socket_utils_1.sendMessage)(socket, payload, undefined, (err) => {
|
|
55
54
|
if (err) {
|
|
56
55
|
console.log(`Streaming message write error (client likely disconnected): ${err.message}`);
|
|
57
56
|
}
|
|
@@ -11,7 +11,7 @@ const configuration_1 = require("../../config/configuration");
|
|
|
11
11
|
let storedProjectGraph = null;
|
|
12
12
|
let storedHasher = null;
|
|
13
13
|
async function handleHashTasks(payload) {
|
|
14
|
-
const { error, projectGraph,
|
|
14
|
+
const { error, projectGraph, rustReferences } = await (0, project_graph_incremental_recomputation_1.getCachedSerializedProjectGraphPromise)();
|
|
15
15
|
if (error) {
|
|
16
16
|
throw error;
|
|
17
17
|
}
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { Socket } from 'net';
|
|
2
|
-
import {
|
|
2
|
+
import { FileMap, ProjectGraph } from '../../config/project-graph';
|
|
3
3
|
import { NxWorkspaceFilesExternals } from '../../native';
|
|
4
4
|
import { FileMapCache } from '../../project-graph/nx-deps-cache';
|
|
5
5
|
import { ConfigurationSourceMaps } from '../../project-graph/utils/project-configuration/source-maps';
|
|
@@ -7,8 +7,6 @@ interface SerializedProjectGraph {
|
|
|
7
7
|
error: Error | null;
|
|
8
8
|
projectGraph: ProjectGraph | null;
|
|
9
9
|
projectFileMapCache: FileMapCache | null;
|
|
10
|
-
fileMap: FileMap | null;
|
|
11
|
-
allWorkspaceFiles: FileData[] | null;
|
|
12
10
|
serializedProjectGraph: string | null;
|
|
13
11
|
serializedSourceMaps: string | null;
|
|
14
12
|
sourceMaps: ConfigurationSourceMaps | null;
|
|
@@ -16,7 +14,6 @@ interface SerializedProjectGraph {
|
|
|
16
14
|
}
|
|
17
15
|
export declare let fileMapWithFiles: {
|
|
18
16
|
fileMap: FileMap;
|
|
19
|
-
allWorkspaceFiles: FileData[];
|
|
20
17
|
rustReferences: NxWorkspaceFilesExternals;
|
|
21
18
|
} | undefined;
|
|
22
19
|
export declare let currentProjectFileMapCache: FileMapCache | undefined;
|
|
@@ -113,8 +113,6 @@ async function getCachedSerializedProjectGraphPromise(socket) {
|
|
|
113
113
|
sourceMaps: null,
|
|
114
114
|
projectGraph: null,
|
|
115
115
|
projectFileMapCache: null,
|
|
116
|
-
fileMap: null,
|
|
117
|
-
allWorkspaceFiles: null,
|
|
118
116
|
rustReferences: null,
|
|
119
117
|
};
|
|
120
118
|
}
|
|
@@ -277,9 +275,7 @@ async function processFilesAndCreateAndSerializeProjectGraph(plugins) {
|
|
|
277
275
|
error: g.error,
|
|
278
276
|
projectGraph: null,
|
|
279
277
|
projectFileMapCache: null,
|
|
280
|
-
fileMap: null,
|
|
281
278
|
rustReferences: null,
|
|
282
|
-
allWorkspaceFiles: null,
|
|
283
279
|
serializedProjectGraph: null,
|
|
284
280
|
serializedSourceMaps: null,
|
|
285
281
|
sourceMaps: null,
|
|
@@ -291,9 +287,7 @@ async function processFilesAndCreateAndSerializeProjectGraph(plugins) {
|
|
|
291
287
|
error: new error_types_1.DaemonProjectGraphError(errors, g.projectGraph, projectConfigurationsResult.sourceMaps),
|
|
292
288
|
projectGraph: null,
|
|
293
289
|
projectFileMapCache: null,
|
|
294
|
-
fileMap: null,
|
|
295
290
|
rustReferences: null,
|
|
296
|
-
allWorkspaceFiles: null,
|
|
297
291
|
serializedProjectGraph: null,
|
|
298
292
|
serializedSourceMaps: null,
|
|
299
293
|
sourceMaps: null,
|
|
@@ -308,9 +302,7 @@ async function processFilesAndCreateAndSerializeProjectGraph(plugins) {
|
|
|
308
302
|
error: err,
|
|
309
303
|
projectGraph: null,
|
|
310
304
|
projectFileMapCache: null,
|
|
311
|
-
fileMap: null,
|
|
312
305
|
rustReferences: null,
|
|
313
|
-
allWorkspaceFiles: null,
|
|
314
306
|
serializedProjectGraph: null,
|
|
315
307
|
serializedSourceMaps: null,
|
|
316
308
|
sourceMaps: null,
|
|
@@ -334,9 +326,8 @@ async function createAndSerializeProjectGraph({ projects, sourceMaps, }) {
|
|
|
334
326
|
try {
|
|
335
327
|
perf_hooks_1.performance.mark('create-project-graph-start');
|
|
336
328
|
const fileMap = copyFileMap(exports.fileMapWithFiles.fileMap);
|
|
337
|
-
const allWorkspaceFiles = copyFileData(exports.fileMapWithFiles.allWorkspaceFiles);
|
|
338
329
|
const rustReferences = exports.fileMapWithFiles.rustReferences;
|
|
339
|
-
const { projectGraph, projectFileMapCache } = await (0, build_project_graph_1.buildProjectGraphUsingProjectFileMap)(projects, knownExternalNodes, fileMap,
|
|
330
|
+
const { projectGraph, projectFileMapCache } = await (0, build_project_graph_1.buildProjectGraphUsingProjectFileMap)(projects, knownExternalNodes, fileMap, rustReferences, exports.currentProjectFileMapCache || (0, nx_deps_cache_1.readFileMapCache)(), await (0, get_plugins_1.getPlugins)(), sourceMaps);
|
|
340
331
|
exports.currentProjectFileMapCache = projectFileMapCache;
|
|
341
332
|
exports.currentProjectGraph = projectGraph;
|
|
342
333
|
exports.currentSourceMaps = sourceMaps;
|
|
@@ -351,8 +342,6 @@ async function createAndSerializeProjectGraph({ projects, sourceMaps, }) {
|
|
|
351
342
|
error: null,
|
|
352
343
|
projectGraph,
|
|
353
344
|
projectFileMapCache,
|
|
354
|
-
fileMap,
|
|
355
|
-
allWorkspaceFiles,
|
|
356
345
|
serializedProjectGraph,
|
|
357
346
|
serializedSourceMaps,
|
|
358
347
|
sourceMaps,
|
|
@@ -365,8 +354,6 @@ async function createAndSerializeProjectGraph({ projects, sourceMaps, }) {
|
|
|
365
354
|
error: e,
|
|
366
355
|
projectGraph: null,
|
|
367
356
|
projectFileMapCache: null,
|
|
368
|
-
fileMap: null,
|
|
369
|
-
allWorkspaceFiles: null,
|
|
370
357
|
serializedProjectGraph: null,
|
|
371
358
|
serializedSourceMaps: null,
|
|
372
359
|
sourceMaps: null,
|
|
@@ -6,7 +6,6 @@ exports.startServer = startServer;
|
|
|
6
6
|
const fs_1 = require("fs");
|
|
7
7
|
const net_1 = require("net");
|
|
8
8
|
const path_1 = require("path");
|
|
9
|
-
const v8_1 = require("v8");
|
|
10
9
|
const analytics_1 = require("../../analytics");
|
|
11
10
|
const file_hasher_1 = require("../../hasher/file-hasher");
|
|
12
11
|
const native_1 = require("../../native");
|
|
@@ -76,7 +75,16 @@ const server = (0, net_1.createServer)(async (socket) => {
|
|
|
76
75
|
logger_1.serverLogger.log(`Established a connection. Number of open connections: ${numberOfOpenConnections}`);
|
|
77
76
|
(0, shutdown_utils_1.resetInactivityTimeout)(handleInactivityTimeout);
|
|
78
77
|
socket.on('data', (0, consume_messages_from_socket_1.consumeMessagesFromSocket)(async (message) => {
|
|
79
|
-
|
|
78
|
+
// A rejection here would otherwise be unhandled and take the daemon
|
|
79
|
+
// down with it, failing every other client's request too.
|
|
80
|
+
await handleMessage(socket, message).catch(async (e) => {
|
|
81
|
+
await (0, shutdown_utils_1.respondWithError)(socket, 'Error handling message', e);
|
|
82
|
+
});
|
|
83
|
+
}, (err) => {
|
|
84
|
+
logger_1.serverLogger.log(`Framing error: ${err.message}`);
|
|
85
|
+
// The stream cannot resynchronize, so close it and let the client
|
|
86
|
+
// observe the disconnect instead of waiting on a reply.
|
|
87
|
+
(0, shutdown_utils_1.respondWithError)(socket, 'Malformed message', err).finally(() => socket.destroy());
|
|
80
88
|
}));
|
|
81
89
|
socket.on('error', (e) => {
|
|
82
90
|
logger_1.serverLogger.log(`Socket error: ${e.message}`);
|
|
@@ -99,21 +107,14 @@ async function handleMessage(socket, data) {
|
|
|
99
107
|
(0, shutdown_utils_1.resetInactivityTimeout)(handleInactivityTimeout);
|
|
100
108
|
const unparsedPayload = data;
|
|
101
109
|
let payload;
|
|
102
|
-
|
|
110
|
+
// Reply in the format the client used.
|
|
111
|
+
const mode = (0, consume_messages_from_socket_1.isJsonMessage)(unparsedPayload) ? 'json' : 'v8';
|
|
103
112
|
logger_1.serverLogger.log(`Received raw message of length ${unparsedPayload.length}`);
|
|
104
113
|
try {
|
|
105
|
-
|
|
106
|
-
if ((0, consume_messages_from_socket_1.isJsonMessage)(unparsedPayload)) {
|
|
107
|
-
payload = JSON.parse(unparsedPayload);
|
|
108
|
-
}
|
|
109
|
-
else {
|
|
110
|
-
// V8 Serialized Message
|
|
111
|
-
payload = (0, v8_1.deserialize)(Buffer.from(unparsedPayload, 'binary'));
|
|
112
|
-
mode = 'v8';
|
|
113
|
-
}
|
|
114
|
+
payload = (0, consume_messages_from_socket_1.parseMessage)(unparsedPayload);
|
|
114
115
|
}
|
|
115
116
|
catch (e) {
|
|
116
|
-
await (0, shutdown_utils_1.respondWithErrorAndExit)(socket, `Invalid payload from the client`, new Error(`Unsupported payload sent to daemon server: ${unparsedPayload}`));
|
|
117
|
+
await (0, shutdown_utils_1.respondWithErrorAndExit)(socket, `Invalid payload from the client`, new Error(`Unsupported payload sent to daemon server: ${(0, consume_messages_from_socket_1.describeMessage)(unparsedPayload, { maxBytes: 200 })}`));
|
|
117
118
|
}
|
|
118
119
|
logger_1.serverLogger.log(`Received ${mode} message of type ${payload.type}`);
|
|
119
120
|
// A mismatch means the client reached the wrong daemon (e.g. a shared
|
|
@@ -233,7 +234,7 @@ async function handleMessage(socket, data) {
|
|
|
233
234
|
await handleResult(socket, configure_ai_agents_1.RESET_CONFIGURE_AI_AGENTS_STATUS, () => (0, handle_configure_ai_agents_1.handleResetConfigureAiAgentsStatus)(), mode);
|
|
234
235
|
}
|
|
235
236
|
else {
|
|
236
|
-
await (0, shutdown_utils_1.respondWithErrorAndExit)(socket, `Invalid payload from the client`, new Error(`Unsupported payload sent to daemon server: ${unparsedPayload}`));
|
|
237
|
+
await (0, shutdown_utils_1.respondWithErrorAndExit)(socket, `Invalid payload from the client`, new Error(`Unsupported payload sent to daemon server: ${(0, consume_messages_from_socket_1.describeMessage)(unparsedPayload, { maxBytes: 200 })}`));
|
|
237
238
|
}
|
|
238
239
|
}
|
|
239
240
|
async function handleResult(socket, type, hrFn, mode) {
|
|
@@ -252,8 +253,8 @@ async function handleResult(socket, type, hrFn, mode) {
|
|
|
252
253
|
else {
|
|
253
254
|
logger_1.serverLogger.log(`Serializing response for ${type} message in ${mode} mode`);
|
|
254
255
|
const response = typeof hr.response === 'string'
|
|
255
|
-
? hr.response
|
|
256
|
-
:
|
|
256
|
+
? Buffer.from(hr.response, 'utf8')
|
|
257
|
+
: (0, socket_utils_1.serializeWithFallback)(hr.response, mode);
|
|
257
258
|
logger_1.serverLogger.log(`Responding to ${type} message`);
|
|
258
259
|
await (0, shutdown_utils_1.respondToClient)(socket, response, hr.description);
|
|
259
260
|
}
|
|
@@ -531,11 +532,3 @@ function forwardEnvToPluginWorkers(env) {
|
|
|
531
532
|
// Plugins may not be loaded yet — env will be picked up on next load
|
|
532
533
|
});
|
|
533
534
|
}
|
|
534
|
-
function serializeUnserializedResult(response, mode) {
|
|
535
|
-
if (mode === 'json') {
|
|
536
|
-
return JSON.stringify(response);
|
|
537
|
-
}
|
|
538
|
-
else {
|
|
539
|
-
return (0, v8_1.serialize)(response).toString('binary');
|
|
540
|
-
}
|
|
541
|
-
}
|
|
@@ -13,7 +13,7 @@ interface HandleServerProcessTerminationParams {
|
|
|
13
13
|
export declare function handleServerProcessTermination({ server, reason, sockets, }: HandleServerProcessTerminationParams): Promise<void>;
|
|
14
14
|
export declare function handleServerProcessTerminationWithRestart({ server, reason, sockets, }: HandleServerProcessTerminationParams): Promise<void>;
|
|
15
15
|
export declare function resetInactivityTimeout(cb: () => void): void;
|
|
16
|
-
export declare function respondToClient(socket: Socket, response:
|
|
16
|
+
export declare function respondToClient(socket: Socket, response: Buffer, description: string): Promise<unknown>;
|
|
17
17
|
/**
|
|
18
18
|
* Send an error to the client without terminating the daemon. Use this when the
|
|
19
19
|
* daemon should keep running for other clients after refusing a request.
|
|
@@ -127,7 +127,7 @@ function respondToClient(socket, response, description) {
|
|
|
127
127
|
if (description) {
|
|
128
128
|
logger_1.serverLogger.requestLog(`Responding to the client.`, description);
|
|
129
129
|
}
|
|
130
|
-
|
|
130
|
+
(0, consume_messages_from_socket_1.writeMessage)(socket, response, (err) => {
|
|
131
131
|
if (err) {
|
|
132
132
|
logger_1.serverLogger.log(`Socket write error (client likely disconnected): ${err.message}`);
|
|
133
133
|
}
|
|
@@ -148,7 +148,7 @@ async function respondWithError(socket, description, error) {
|
|
|
148
148
|
logger_1.serverLogger.requestLog(`Responding to the client with an error.`, description, normalizedError.message);
|
|
149
149
|
console.error(normalizedError.stack);
|
|
150
150
|
// Respond with the original error
|
|
151
|
-
await respondToClient(socket, (0, socket_utils_1.serializeResult)(error, null, null), null);
|
|
151
|
+
await respondToClient(socket, Buffer.from((0, socket_utils_1.serializeResult)(error, null, null), 'utf8'), null);
|
|
152
152
|
}
|
|
153
153
|
async function respondWithErrorAndExit(socket, description, error) {
|
|
154
154
|
await respondWithError(socket, description, error);
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import type { Socket } from 'net';
|
|
1
2
|
export declare const isWindows: boolean;
|
|
2
3
|
/**
|
|
3
4
|
* For IPC with the daemon server we use unix sockets or windows named pipes, depending on the user's operating system.
|
|
@@ -12,13 +13,31 @@ export declare function getPluginSocketFileName(id: string): string;
|
|
|
12
13
|
export declare function killSocketOrPath(): void;
|
|
13
14
|
export declare function serializeResult(error: Error | null, serializedProjectGraph: string | null, serializedSourceMaps: string | null): string | null;
|
|
14
15
|
/**
|
|
15
|
-
*
|
|
16
|
-
*
|
|
17
|
-
*
|
|
18
|
-
* by the client it is communicating with.
|
|
16
|
+
* Serialize using `preferred`, falling back to the other format when it throws.
|
|
17
|
+
* Neither format subsumes the other: JSON cannot represent a BigInt and hits the
|
|
18
|
+
* max string length far sooner, while v8 cannot clone a function.
|
|
19
19
|
*
|
|
20
20
|
* @param data Data to serialize
|
|
21
|
-
* @param
|
|
22
|
-
* @returns Serialized data as a
|
|
21
|
+
* @param preferred Format to attempt first
|
|
22
|
+
* @returns Serialized data as bytes ready to be framed onto a socket
|
|
23
23
|
*/
|
|
24
|
-
export declare function
|
|
24
|
+
export declare function serializeWithFallback(data: any, preferred: 'v8' | 'json'): Buffer;
|
|
25
|
+
/**
|
|
26
|
+
* Serialize data for IPC using the format the user configured.
|
|
27
|
+
*
|
|
28
|
+
* @param data Data to serialize
|
|
29
|
+
* @param force Use this format without falling back. For callers whose data is
|
|
30
|
+
* known to be unrepresentable in the other format, where a fallback
|
|
31
|
+
* would only swap one failure for a less obvious one.
|
|
32
|
+
* @returns Serialized data as bytes ready to be framed onto a socket
|
|
33
|
+
*/
|
|
34
|
+
export declare function serialize(data: any, force?: 'v8' | 'json'): Buffer;
|
|
35
|
+
/**
|
|
36
|
+
* Serialize `data` and write it as one framed message.
|
|
37
|
+
*
|
|
38
|
+
* Lives here rather than in `writeMessage` so the framing stays a byte-level
|
|
39
|
+
* primitive: `utils/consume-messages-from-socket` is shared by callers that
|
|
40
|
+
* already hold bytes, and having it reach back into the daemon's serializer
|
|
41
|
+
* would invert the dependency.
|
|
42
|
+
*/
|
|
43
|
+
export declare function sendMessage(socket: Socket, data: any, force?: 'v8' | 'json', callback?: (err?: Error) => void): void;
|