@indigoai-us/hq-cloud 6.18.22-alpha.702 → 6.18.22
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/push-scan-exclusions.test.js +12 -0
- package/dist/cli/push-scan-exclusions.test.js.map +1 -1
- package/dist/cli/sync.d.ts +0 -6
- package/dist/cli/sync.d.ts.map +1 -1
- package/dist/cli/sync.js +613 -724
- package/dist/cli/sync.js.map +1 -1
- package/dist/cli/sync.test.js +0 -253
- package/dist/cli/sync.test.js.map +1 -1
- package/dist/watch-roots.d.ts +14 -12
- package/dist/watch-roots.d.ts.map +1 -1
- package/dist/watch-roots.js +35 -22
- package/dist/watch-roots.js.map +1 -1
- package/dist/watch-roots.test.js +64 -2
- package/dist/watch-roots.test.js.map +1 -1
- package/dist/watcher.d.ts +37 -5
- package/dist/watcher.d.ts.map +1 -1
- package/dist/watcher.js +148 -17
- package/dist/watcher.js.map +1 -1
- package/dist/watcher.test.js +201 -3
- package/dist/watcher.test.js.map +1 -1
- package/package.json +1 -1
- package/dist/kms10-etag-safe-pull.d.ts +0 -20
- package/dist/kms10-etag-safe-pull.d.ts.map +0 -1
- package/dist/kms10-etag-safe-pull.js +0 -37
- package/dist/kms10-etag-safe-pull.js.map +0 -1
- package/dist/kms10-etag-safe-pull.test.d.ts +0 -2
- package/dist/kms10-etag-safe-pull.test.d.ts.map +0 -1
- package/dist/kms10-etag-safe-pull.test.js +0 -53
- package/dist/kms10-etag-safe-pull.test.js.map +0 -1
package/dist/watcher.js
CHANGED
|
@@ -355,9 +355,9 @@ function errorPath(err) {
|
|
|
355
355
|
/**
|
|
356
356
|
* Which backend a platform gets.
|
|
357
357
|
*
|
|
358
|
-
* - macOS (FSEvents)
|
|
359
|
-
*
|
|
360
|
-
*
|
|
358
|
+
* - macOS (FSEvents) uses one recursive root handle and filters events
|
|
359
|
+
* before they reach TreeWatcher. Windows uses the exact scoped recursive
|
|
360
|
+
* plan over ReadDirectoryChangesW handles.
|
|
361
361
|
* - Linux has no recursive handle: a directory watch reports only its own
|
|
362
362
|
* direct contents. The same scoped backend runs with one shallow inotify
|
|
363
363
|
* watch per in-scope directory instead — and NONE per file, because the
|
|
@@ -370,7 +370,9 @@ function errorPath(err) {
|
|
|
370
370
|
export function selectTreeWatchBackend(platform, forcePolling) {
|
|
371
371
|
if (forcePolling)
|
|
372
372
|
return "polling";
|
|
373
|
-
if (platform === "darwin"
|
|
373
|
+
if (platform === "darwin")
|
|
374
|
+
return "native-root";
|
|
375
|
+
if (platform === "win32")
|
|
374
376
|
return "native-recursive";
|
|
375
377
|
if (platform === "linux")
|
|
376
378
|
return "native-shallow";
|
|
@@ -390,6 +392,12 @@ function startChokidarTreeWatch(hqRoot, shouldEmit, onEvent, onError, maxWatched
|
|
|
390
392
|
const readyListeners = new Set();
|
|
391
393
|
try {
|
|
392
394
|
cw = watch(hqRoot, {
|
|
395
|
+
// Push walkers record a directory symlink as a link entry and never
|
|
396
|
+
// traverse its target. Keep polling discovery on the same boundary so
|
|
397
|
+
// aliased trees do not consume the path budget or produce events for
|
|
398
|
+
// content that cannot upload. Chokidar still reports changes to the
|
|
399
|
+
// symlink entry itself.
|
|
400
|
+
followSymlinks: false,
|
|
393
401
|
// On Linux chokidar adds one fs.watch/inotify watch per descended dir.
|
|
394
402
|
// The predicate therefore enforces both scope and the hard per-runner cap.
|
|
395
403
|
ignored: toChokidarIgnored(shouldEmit, hqRoot, budget),
|
|
@@ -642,7 +650,7 @@ export function startScopedRecursiveWatch(hqRoot, shouldEmit, onEvent, onError,
|
|
|
642
650
|
const openedOn = directoryIdentity(dir);
|
|
643
651
|
let handle;
|
|
644
652
|
try {
|
|
645
|
-
handle = fs.watch(dir, { recursive, persistent: true }, (eventType, filename) => {
|
|
653
|
+
handle = (opts.watchDirectory ?? fs.watch)(dir, { recursive, persistent: true }, (eventType, filename) => {
|
|
646
654
|
// `filename` is relative to THIS watch's directory (or null/empty if
|
|
647
655
|
// the platform couldn't provide it — nothing actionable then).
|
|
648
656
|
// `String()` coerces both the string and (older @types/node) Buffer
|
|
@@ -680,6 +688,11 @@ export function startScopedRecursiveWatch(hqRoot, shouldEmit, onEvent, onError,
|
|
|
680
688
|
}
|
|
681
689
|
handle.on("error", (err) => {
|
|
682
690
|
onError(err);
|
|
691
|
+
// TreeWatcher handles systemic watch exhaustion by closing this backend
|
|
692
|
+
// and moving to polling. Reopening the failed path before that transition
|
|
693
|
+
// runs only repeats the same EMFILE/ENFILE failure.
|
|
694
|
+
if (isSystemicWatchError(err))
|
|
695
|
+
return;
|
|
683
696
|
// A handle a re-plan already replaced (retiring) must not tear down its
|
|
684
697
|
// replacement at the same path.
|
|
685
698
|
if (closed || watchers.get(dir) !== handle)
|
|
@@ -730,6 +743,7 @@ export function startScopedRecursiveWatch(hqRoot, shouldEmit, onEvent, onError,
|
|
|
730
743
|
}
|
|
731
744
|
: onDirectorySeen,
|
|
732
745
|
recursiveWatches,
|
|
746
|
+
maxDepth: opts.maxDepth,
|
|
733
747
|
listChildDirs: opts.listChildDirs,
|
|
734
748
|
}));
|
|
735
749
|
}
|
|
@@ -982,17 +996,101 @@ export function startScopedRecursiveWatch(hqRoot, shouldEmit, onEvent, onError,
|
|
|
982
996
|
}),
|
|
983
997
|
};
|
|
984
998
|
}
|
|
999
|
+
/** Start one recursive watch on the HQ root and filter every event at ingress. */
|
|
1000
|
+
export function startNativeRootWatch(hqRoot, shouldEmit, onEvent, onError, opts = {}) {
|
|
1001
|
+
const root = path.resolve(hqRoot);
|
|
1002
|
+
const watchDirectory = opts.watchDirectory ?? fs.watch;
|
|
1003
|
+
let handle = null;
|
|
1004
|
+
let closed = false;
|
|
1005
|
+
let attachFailures = 0;
|
|
1006
|
+
const identity = new Map();
|
|
1007
|
+
const closeHandle = () => {
|
|
1008
|
+
const current = handle;
|
|
1009
|
+
handle = null;
|
|
1010
|
+
if (!current)
|
|
1011
|
+
return;
|
|
1012
|
+
try {
|
|
1013
|
+
current.close();
|
|
1014
|
+
}
|
|
1015
|
+
catch {
|
|
1016
|
+
// Already closed by the OS.
|
|
1017
|
+
}
|
|
1018
|
+
};
|
|
1019
|
+
const attach = () => {
|
|
1020
|
+
if (closed)
|
|
1021
|
+
return;
|
|
1022
|
+
try {
|
|
1023
|
+
const openedOn = directoryIdentityAt(root);
|
|
1024
|
+
const opened = watchDirectory(root, { recursive: true, persistent: true }, (eventType, filename) => {
|
|
1025
|
+
if (closed || filename == null)
|
|
1026
|
+
return;
|
|
1027
|
+
const absolutePath = path.resolve(root, String(filename));
|
|
1028
|
+
const relative = path.relative(root, absolutePath);
|
|
1029
|
+
if (relative === ".." ||
|
|
1030
|
+
relative.startsWith(`..${path.sep}`) ||
|
|
1031
|
+
path.isAbsolute(relative)) {
|
|
1032
|
+
return;
|
|
1033
|
+
}
|
|
1034
|
+
let isDir;
|
|
1035
|
+
try {
|
|
1036
|
+
isDir = fs.lstatSync(absolutePath).isDirectory();
|
|
1037
|
+
}
|
|
1038
|
+
catch {
|
|
1039
|
+
// A rename event can describe a path that has already vanished.
|
|
1040
|
+
}
|
|
1041
|
+
const admitted = isDir === undefined
|
|
1042
|
+
? shouldEmit(absolutePath, false) && shouldEmit(absolutePath, true)
|
|
1043
|
+
: shouldEmit(absolutePath, isDir);
|
|
1044
|
+
if (!admitted)
|
|
1045
|
+
return;
|
|
1046
|
+
onEvent(absolutePath, eventType === "change" ? "change" : "rename");
|
|
1047
|
+
});
|
|
1048
|
+
handle = opened;
|
|
1049
|
+
if (openedOn !== undefined)
|
|
1050
|
+
identity.set(root, openedOn);
|
|
1051
|
+
opened.on("error", (err) => {
|
|
1052
|
+
onError(err);
|
|
1053
|
+
// Systemic errors are handled by TreeWatcher, which closes this
|
|
1054
|
+
// backend and switches to polling. Reopening would repeat exhaustion.
|
|
1055
|
+
if (closed || isSystemicWatchError(err) || handle !== opened)
|
|
1056
|
+
return;
|
|
1057
|
+
closeHandle();
|
|
1058
|
+
attach();
|
|
1059
|
+
});
|
|
1060
|
+
}
|
|
1061
|
+
catch (err) {
|
|
1062
|
+
attachFailures += 1;
|
|
1063
|
+
onError(err);
|
|
1064
|
+
}
|
|
1065
|
+
};
|
|
1066
|
+
attach();
|
|
1067
|
+
return {
|
|
1068
|
+
close: () => {
|
|
1069
|
+
if (closed)
|
|
1070
|
+
return;
|
|
1071
|
+
closed = true;
|
|
1072
|
+
closeHandle();
|
|
1073
|
+
},
|
|
1074
|
+
mode: "native-root",
|
|
1075
|
+
needsKnownKinds: true,
|
|
1076
|
+
attachFailures,
|
|
1077
|
+
watchedPathCount: () => (handle ? 1 : 0),
|
|
1078
|
+
probeCoverage: () => probeScopedWatchCoverage({
|
|
1079
|
+
root,
|
|
1080
|
+
identities: identity,
|
|
1081
|
+
directoryIdentity: directoryIdentityAt,
|
|
1082
|
+
}),
|
|
1083
|
+
};
|
|
1084
|
+
}
|
|
985
1085
|
/**
|
|
986
1086
|
* Start watching `hqRoot`, calling `onEvent(absolutePath)` for every change.
|
|
987
1087
|
*
|
|
988
1088
|
* Backend selection (the whole point of this helper):
|
|
989
|
-
* - macOS
|
|
990
|
-
*
|
|
991
|
-
*
|
|
992
|
-
*
|
|
993
|
-
*
|
|
994
|
-
* (instant sync then falls back to the poll).
|
|
995
|
-
* - Linux / other → chokidar (recursive `fs.watch` is unsupported there).
|
|
1089
|
+
* - macOS → one recursive root `fs.watch`, with the scope filter applied
|
|
1090
|
+
* before an event reaches TreeWatcher.
|
|
1091
|
+
* - Windows → scoped recursive `fs.watch` handles over the in-scope tree.
|
|
1092
|
+
* - Linux → one shallow native watch per in-scope directory; other platforms
|
|
1093
|
+
* use chokidar (recursive `fs.watch` is unsupported there).
|
|
996
1094
|
*
|
|
997
1095
|
* The recursive backend does no descent-time filtering WITHIN a watched
|
|
998
1096
|
* subtree, so changes there are funneled to `onEvent`, which re-applies the
|
|
@@ -1002,11 +1100,41 @@ export function startScopedRecursiveWatch(hqRoot, shouldEmit, onEvent, onError,
|
|
|
1002
1100
|
* ancestor-descent pruning that the chokidar backend needs
|
|
1003
1101
|
* {@link toChokidarIgnored} to avoid.
|
|
1004
1102
|
*/
|
|
1005
|
-
|
|
1103
|
+
/** @internal Exported for backend-level regression tests. */
|
|
1104
|
+
export function startTreeWatch(hqRoot, shouldEmit, onEvent, onError, maxWatchedPaths, onWatchBudgetExceeded, onDirectorySeen, forcePolling = false) {
|
|
1105
|
+
// Resolve the root once before the backend starts. Chokidar with
|
|
1106
|
+
// followSymlinks:false treats descendant links as leaf entries, but an
|
|
1107
|
+
// explicitly supplied root may itself be a symlink. Watch its real tree and
|
|
1108
|
+
// map observed paths back to the caller's root spelling so the caller's
|
|
1109
|
+
// filter and path routing retain their existing contract.
|
|
1110
|
+
const displayRoot = path.resolve(hqRoot);
|
|
1111
|
+
const root = fs.realpathSync(displayRoot);
|
|
1112
|
+
const displayPath = (absolutePath) => root === displayRoot
|
|
1113
|
+
? absolutePath
|
|
1114
|
+
: path.join(displayRoot, path.relative(root, absolutePath));
|
|
1115
|
+
const watchFilter = root === displayRoot
|
|
1116
|
+
? shouldEmit
|
|
1117
|
+
: (absolutePath, isDir) => shouldEmit(displayPath(absolutePath), isDir);
|
|
1118
|
+
const watchEvent = root === displayRoot
|
|
1119
|
+
? onEvent
|
|
1120
|
+
: (absolutePath, kind) => onEvent(displayPath(absolutePath), kind);
|
|
1121
|
+
const watchDirectorySeen = onDirectorySeen
|
|
1122
|
+
? root === displayRoot
|
|
1123
|
+
? onDirectorySeen
|
|
1124
|
+
: (absolutePath) => onDirectorySeen(displayPath(absolutePath))
|
|
1125
|
+
: undefined;
|
|
1006
1126
|
const choice = selectTreeWatchBackend(process.platform, forcePolling);
|
|
1007
|
-
if (choice === "native-
|
|
1127
|
+
if (choice === "native-root") {
|
|
1128
|
+
const nativeRoot = startNativeRootWatch(root, watchFilter, watchEvent, onError);
|
|
1129
|
+
if (nativeRoot.attachFailures === 0 && nativeRoot.watchedPathCount() === 1) {
|
|
1130
|
+
return nativeRoot;
|
|
1131
|
+
}
|
|
1132
|
+
nativeRoot.close();
|
|
1133
|
+
}
|
|
1134
|
+
if (choice === "native-recursive" ||
|
|
1135
|
+
choice === "native-shallow") {
|
|
1008
1136
|
try {
|
|
1009
|
-
const scoped = startScopedRecursiveWatch(
|
|
1137
|
+
const scoped = startScopedRecursiveWatch(root, watchFilter, watchEvent, onError, watchDirectorySeen, choice === "native-shallow"
|
|
1010
1138
|
? { recursiveWatches: false, maxWatchedPaths, onWatchBudgetExceeded }
|
|
1011
1139
|
: {});
|
|
1012
1140
|
// Partial coverage is worse than a different backend: a planned root we
|
|
@@ -1024,7 +1152,7 @@ function startTreeWatch(hqRoot, shouldEmit, onEvent, onError, maxWatchedPaths, o
|
|
|
1024
1152
|
onError(err);
|
|
1025
1153
|
}
|
|
1026
1154
|
}
|
|
1027
|
-
return startChokidarTreeWatch(
|
|
1155
|
+
return startChokidarTreeWatch(root, watchFilter, watchEvent, onError, maxWatchedPaths, onWatchBudgetExceeded, forcePolling);
|
|
1028
1156
|
}
|
|
1029
1157
|
/**
|
|
1030
1158
|
* Build the composite emit-decision predicate. Returns true when a change to
|
|
@@ -2333,7 +2461,10 @@ export class TreeWatcher {
|
|
|
2333
2461
|
}
|
|
2334
2462
|
/** Active backend mode, including the no-inotify polling fallback. */
|
|
2335
2463
|
watchMode() {
|
|
2336
|
-
|
|
2464
|
+
const mode = this.backend?.mode;
|
|
2465
|
+
if (mode === "native-root")
|
|
2466
|
+
return "native";
|
|
2467
|
+
return mode ?? (this.backend ? "unknown" : "inactive");
|
|
2337
2468
|
}
|
|
2338
2469
|
/**
|
|
2339
2470
|
* Check that the HQ root and every live watch still resolve to the inode
|