@karmaniverous/jeeves-watcher 0.18.2 → 0.18.3
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.
|
@@ -747,6 +747,104 @@ class ValuesManager extends BinaryFileStore {
|
|
|
747
747
|
}
|
|
748
748
|
}
|
|
749
749
|
|
|
750
|
+
/**
|
|
751
|
+
* @module watcher/globToDir
|
|
752
|
+
* Adapts glob-based watch config to chokidar v4+, which removed glob support
|
|
753
|
+
* (see paulmillr/chokidar#1350). Chokidar v4 treats glob patterns as literal
|
|
754
|
+
* strings, silently producing zero events. This module extracts static directory
|
|
755
|
+
* roots from glob patterns for chokidar to watch, then filters emitted events
|
|
756
|
+
* against the original globs via picomatch.
|
|
757
|
+
*/
|
|
758
|
+
/**
|
|
759
|
+
* Extract the static directory root from a glob pattern.
|
|
760
|
+
* Stops at the first segment containing glob characters (`*`, `{`, `?`, `[`).
|
|
761
|
+
*
|
|
762
|
+
* @param glob - A glob pattern (e.g., `j:/domains/**\/*.json`).
|
|
763
|
+
* @returns The static directory prefix (e.g., `j:/domains`).
|
|
764
|
+
*/
|
|
765
|
+
function globRoot(glob) {
|
|
766
|
+
const normalized = normalizeSlashes(glob);
|
|
767
|
+
const segments = normalized.split('/');
|
|
768
|
+
const staticSegments = [];
|
|
769
|
+
for (const seg of segments) {
|
|
770
|
+
if (/[*?{[\]]/.test(seg))
|
|
771
|
+
break;
|
|
772
|
+
staticSegments.push(seg);
|
|
773
|
+
}
|
|
774
|
+
return staticSegments.join('/') || '.';
|
|
775
|
+
}
|
|
776
|
+
/**
|
|
777
|
+
* Deduplicate directory roots, removing paths that are subdirectories of others.
|
|
778
|
+
*
|
|
779
|
+
* @param roots - Array of directory paths.
|
|
780
|
+
* @returns Deduplicated array with subdirectories removed.
|
|
781
|
+
*/
|
|
782
|
+
function deduplicateRoots(roots) {
|
|
783
|
+
const normalized = roots.map((r) => normalizeSlashes(r).toLowerCase());
|
|
784
|
+
const sorted = [...new Set(normalized)].sort();
|
|
785
|
+
return sorted.filter((root, _i, arr) => {
|
|
786
|
+
const withSlash = root.endsWith('/') ? root : root + '/';
|
|
787
|
+
return !arr.some((other) => other !== root && withSlash.startsWith(other + '/'));
|
|
788
|
+
});
|
|
789
|
+
}
|
|
790
|
+
/**
|
|
791
|
+
* Build a picomatch matcher from an array of glob patterns.
|
|
792
|
+
* Normalizes Windows paths (backslash → forward slash, lowercase drive letter)
|
|
793
|
+
* before matching.
|
|
794
|
+
*
|
|
795
|
+
* @param globs - Glob patterns to match against.
|
|
796
|
+
* @returns A function that tests whether a file path matches any of the globs.
|
|
797
|
+
*/
|
|
798
|
+
function buildGlobMatcher(globs) {
|
|
799
|
+
const normalizedGlobs = globs.map((g) => normalizeSlashes(g));
|
|
800
|
+
const isMatch = picomatch(normalizedGlobs, { dot: true, nocase: true });
|
|
801
|
+
return (filePath) => {
|
|
802
|
+
const normalized = normalizeSlashes(filePath);
|
|
803
|
+
return isMatch(normalized);
|
|
804
|
+
};
|
|
805
|
+
}
|
|
806
|
+
/**
|
|
807
|
+
* Convert an array of glob patterns into chokidar-compatible directory roots
|
|
808
|
+
* and a filter function for post-hoc event filtering.
|
|
809
|
+
*
|
|
810
|
+
* @param globs - Glob patterns from the watch config.
|
|
811
|
+
* @returns Object with `roots` (directories for chokidar) and `matches` (filter function).
|
|
812
|
+
*/
|
|
813
|
+
function resolveWatchPaths(globs) {
|
|
814
|
+
const rawRoots = globs.map(globRoot);
|
|
815
|
+
const roots = deduplicateRoots(rawRoots);
|
|
816
|
+
const matches = buildGlobMatcher(globs);
|
|
817
|
+
return { roots, matches };
|
|
818
|
+
}
|
|
819
|
+
/**
|
|
820
|
+
* Convert ignored glob patterns to picomatch matcher functions.
|
|
821
|
+
*
|
|
822
|
+
* Chokidar v5 replaced the external `anymatch` dependency with an inline
|
|
823
|
+
* implementation that does **exact string equality** for string matchers,
|
|
824
|
+
* breaking glob-based `ignored` patterns. This function converts glob strings
|
|
825
|
+
* to picomatch functions that chokidar's `createPattern` passes through
|
|
826
|
+
* unchanged (`typeof matcher === 'function'`).
|
|
827
|
+
*
|
|
828
|
+
* Non-string entries (functions, RegExps) are passed through as-is.
|
|
829
|
+
*
|
|
830
|
+
* @param ignored - Array of ignored patterns (globs, functions, RegExps).
|
|
831
|
+
* @returns Array with glob strings replaced by picomatch matcher functions.
|
|
832
|
+
*/
|
|
833
|
+
function resolveIgnored(ignored) {
|
|
834
|
+
return ignored.map((entry) => {
|
|
835
|
+
if (typeof entry !== 'string')
|
|
836
|
+
return entry;
|
|
837
|
+
// If the string contains glob characters, convert to a picomatch function.
|
|
838
|
+
// Literal strings (exact paths) are also converted for consistent matching.
|
|
839
|
+
const normalizedPattern = normalizeSlashes(entry);
|
|
840
|
+
const matcher = picomatch(normalizedPattern, { dot: true, nocase: true });
|
|
841
|
+
return (filePath) => {
|
|
842
|
+
const normalized = normalizeSlashes(filePath);
|
|
843
|
+
return matcher(normalized);
|
|
844
|
+
};
|
|
845
|
+
});
|
|
846
|
+
}
|
|
847
|
+
|
|
750
848
|
/**
|
|
751
849
|
* @module vcs/CommitMessageGenerator
|
|
752
850
|
* Generates AI-powered commit messages via the Anthropic Messages API.
|
|
@@ -1480,11 +1578,10 @@ class VcsCoordinator {
|
|
|
1480
1578
|
const rootVcs = entry.vcs?.enabled ?? config.vcs.enabled;
|
|
1481
1579
|
if (!rootVcs)
|
|
1482
1580
|
continue;
|
|
1483
|
-
|
|
1484
|
-
|
|
1581
|
+
const resolvedRoot = normalizeSlashes(resolve(globRoot(entry.path)));
|
|
1582
|
+
// Deduplicate: skip if another glob already resolved to this root.
|
|
1583
|
+
if (this.managers.has(resolvedRoot))
|
|
1485
1584
|
continue;
|
|
1486
|
-
}
|
|
1487
|
-
const resolvedRoot = normalizeSlashes(resolve(entry.path));
|
|
1488
1585
|
const mergedConfig = {
|
|
1489
1586
|
...config.vcs,
|
|
1490
1587
|
...entry.vcs,
|
|
@@ -3088,20 +3185,22 @@ async function initVcs(config, logger) {
|
|
|
3088
3185
|
}
|
|
3089
3186
|
const stateDir = config.stateDir ?? '.jeeves-metadata';
|
|
3090
3187
|
const pathStrings = extractWatchPathStrings(config.watch.paths);
|
|
3091
|
-
const staticPaths = pathStrings.
|
|
3188
|
+
const staticPaths = pathStrings.map((p) => globRoot(p));
|
|
3092
3189
|
validateStateDirOverlap(stateDir, staticPaths);
|
|
3093
3190
|
const normalized = normalizeWatchPaths(config.watch.paths);
|
|
3191
|
+
const initializedRoots = new Set();
|
|
3094
3192
|
for (const entry of normalized) {
|
|
3095
3193
|
const rootVcs = entry.vcs?.enabled ?? config.vcs.enabled;
|
|
3096
3194
|
if (!rootVcs)
|
|
3097
3195
|
continue;
|
|
3098
|
-
|
|
3099
|
-
|
|
3196
|
+
const root = globRoot(entry.path);
|
|
3197
|
+
// Deduplicate: skip if another glob already resolved to this root.
|
|
3198
|
+
if (initializedRoots.has(root))
|
|
3100
3199
|
continue;
|
|
3101
|
-
|
|
3102
|
-
await initRepo(
|
|
3103
|
-
await ensureGitignore(
|
|
3104
|
-
logger.info({ root
|
|
3200
|
+
initializedRoots.add(root);
|
|
3201
|
+
await initRepo(root);
|
|
3202
|
+
await ensureGitignore(root);
|
|
3203
|
+
logger.info({ root }, 'VCS initialized for watch root');
|
|
3105
3204
|
}
|
|
3106
3205
|
return true;
|
|
3107
3206
|
}
|
|
@@ -7054,104 +7153,6 @@ class SystemHealth {
|
|
|
7054
7153
|
}
|
|
7055
7154
|
}
|
|
7056
7155
|
|
|
7057
|
-
/**
|
|
7058
|
-
* @module watcher/globToDir
|
|
7059
|
-
* Adapts glob-based watch config to chokidar v4+, which removed glob support
|
|
7060
|
-
* (see paulmillr/chokidar#1350). Chokidar v4 treats glob patterns as literal
|
|
7061
|
-
* strings, silently producing zero events. This module extracts static directory
|
|
7062
|
-
* roots from glob patterns for chokidar to watch, then filters emitted events
|
|
7063
|
-
* against the original globs via picomatch.
|
|
7064
|
-
*/
|
|
7065
|
-
/**
|
|
7066
|
-
* Extract the static directory root from a glob pattern.
|
|
7067
|
-
* Stops at the first segment containing glob characters (`*`, `{`, `?`, `[`).
|
|
7068
|
-
*
|
|
7069
|
-
* @param glob - A glob pattern (e.g., `j:/domains/**\/*.json`).
|
|
7070
|
-
* @returns The static directory prefix (e.g., `j:/domains`).
|
|
7071
|
-
*/
|
|
7072
|
-
function globRoot(glob) {
|
|
7073
|
-
const normalized = normalizeSlashes(glob);
|
|
7074
|
-
const segments = normalized.split('/');
|
|
7075
|
-
const staticSegments = [];
|
|
7076
|
-
for (const seg of segments) {
|
|
7077
|
-
if (/[*?{[\]]/.test(seg))
|
|
7078
|
-
break;
|
|
7079
|
-
staticSegments.push(seg);
|
|
7080
|
-
}
|
|
7081
|
-
return staticSegments.join('/') || '.';
|
|
7082
|
-
}
|
|
7083
|
-
/**
|
|
7084
|
-
* Deduplicate directory roots, removing paths that are subdirectories of others.
|
|
7085
|
-
*
|
|
7086
|
-
* @param roots - Array of directory paths.
|
|
7087
|
-
* @returns Deduplicated array with subdirectories removed.
|
|
7088
|
-
*/
|
|
7089
|
-
function deduplicateRoots(roots) {
|
|
7090
|
-
const normalized = roots.map((r) => normalizeSlashes(r).toLowerCase());
|
|
7091
|
-
const sorted = [...new Set(normalized)].sort();
|
|
7092
|
-
return sorted.filter((root, _i, arr) => {
|
|
7093
|
-
const withSlash = root.endsWith('/') ? root : root + '/';
|
|
7094
|
-
return !arr.some((other) => other !== root && withSlash.startsWith(other + '/'));
|
|
7095
|
-
});
|
|
7096
|
-
}
|
|
7097
|
-
/**
|
|
7098
|
-
* Build a picomatch matcher from an array of glob patterns.
|
|
7099
|
-
* Normalizes Windows paths (backslash → forward slash, lowercase drive letter)
|
|
7100
|
-
* before matching.
|
|
7101
|
-
*
|
|
7102
|
-
* @param globs - Glob patterns to match against.
|
|
7103
|
-
* @returns A function that tests whether a file path matches any of the globs.
|
|
7104
|
-
*/
|
|
7105
|
-
function buildGlobMatcher(globs) {
|
|
7106
|
-
const normalizedGlobs = globs.map((g) => normalizeSlashes(g));
|
|
7107
|
-
const isMatch = picomatch(normalizedGlobs, { dot: true, nocase: true });
|
|
7108
|
-
return (filePath) => {
|
|
7109
|
-
const normalized = normalizeSlashes(filePath);
|
|
7110
|
-
return isMatch(normalized);
|
|
7111
|
-
};
|
|
7112
|
-
}
|
|
7113
|
-
/**
|
|
7114
|
-
* Convert an array of glob patterns into chokidar-compatible directory roots
|
|
7115
|
-
* and a filter function for post-hoc event filtering.
|
|
7116
|
-
*
|
|
7117
|
-
* @param globs - Glob patterns from the watch config.
|
|
7118
|
-
* @returns Object with `roots` (directories for chokidar) and `matches` (filter function).
|
|
7119
|
-
*/
|
|
7120
|
-
function resolveWatchPaths(globs) {
|
|
7121
|
-
const rawRoots = globs.map(globRoot);
|
|
7122
|
-
const roots = deduplicateRoots(rawRoots);
|
|
7123
|
-
const matches = buildGlobMatcher(globs);
|
|
7124
|
-
return { roots, matches };
|
|
7125
|
-
}
|
|
7126
|
-
/**
|
|
7127
|
-
* Convert ignored glob patterns to picomatch matcher functions.
|
|
7128
|
-
*
|
|
7129
|
-
* Chokidar v5 replaced the external `anymatch` dependency with an inline
|
|
7130
|
-
* implementation that does **exact string equality** for string matchers,
|
|
7131
|
-
* breaking glob-based `ignored` patterns. This function converts glob strings
|
|
7132
|
-
* to picomatch functions that chokidar's `createPattern` passes through
|
|
7133
|
-
* unchanged (`typeof matcher === 'function'`).
|
|
7134
|
-
*
|
|
7135
|
-
* Non-string entries (functions, RegExps) are passed through as-is.
|
|
7136
|
-
*
|
|
7137
|
-
* @param ignored - Array of ignored patterns (globs, functions, RegExps).
|
|
7138
|
-
* @returns Array with glob strings replaced by picomatch matcher functions.
|
|
7139
|
-
*/
|
|
7140
|
-
function resolveIgnored(ignored) {
|
|
7141
|
-
return ignored.map((entry) => {
|
|
7142
|
-
if (typeof entry !== 'string')
|
|
7143
|
-
return entry;
|
|
7144
|
-
// If the string contains glob characters, convert to a picomatch function.
|
|
7145
|
-
// Literal strings (exact paths) are also converted for consistent matching.
|
|
7146
|
-
const normalizedPattern = normalizeSlashes(entry);
|
|
7147
|
-
const matcher = picomatch(normalizedPattern, { dot: true, nocase: true });
|
|
7148
|
-
return (filePath) => {
|
|
7149
|
-
const normalized = normalizeSlashes(filePath);
|
|
7150
|
-
return matcher(normalized);
|
|
7151
|
-
};
|
|
7152
|
-
});
|
|
7153
|
-
}
|
|
7154
|
-
|
|
7155
7156
|
/**
|
|
7156
7157
|
* @module watcher/MoveCorrelator
|
|
7157
7158
|
* Correlates unlink+add events as file moves using content hash matching.
|
|
@@ -224,7 +224,7 @@ async function startFromConfig(configPath, descriptor) {
|
|
|
224
224
|
const config = await loadConfig(resolvedPath);
|
|
225
225
|
// Dynamic import breaks the circular dependency between this module
|
|
226
226
|
// and app/index.ts (which defines JeevesWatcher and re-exports startFromConfig).
|
|
227
|
-
const { JeevesWatcher } = await import('./index-
|
|
227
|
+
const { JeevesWatcher } = await import('./index-Dk_WrV0T.js');
|
|
228
228
|
const app = new JeevesWatcher(config, resolvedPath, descriptor);
|
|
229
229
|
installShutdownHandlers(() => app.stop());
|
|
230
230
|
await app.start();
|
package/dist/index.js
CHANGED
|
@@ -4654,6 +4654,104 @@ async function ensureGitignore(rootPath, alwaysOnEntries = []) {
|
|
|
4654
4654
|
}
|
|
4655
4655
|
}
|
|
4656
4656
|
|
|
4657
|
+
/**
|
|
4658
|
+
* @module watcher/globToDir
|
|
4659
|
+
* Adapts glob-based watch config to chokidar v4+, which removed glob support
|
|
4660
|
+
* (see paulmillr/chokidar#1350). Chokidar v4 treats glob patterns as literal
|
|
4661
|
+
* strings, silently producing zero events. This module extracts static directory
|
|
4662
|
+
* roots from glob patterns for chokidar to watch, then filters emitted events
|
|
4663
|
+
* against the original globs via picomatch.
|
|
4664
|
+
*/
|
|
4665
|
+
/**
|
|
4666
|
+
* Extract the static directory root from a glob pattern.
|
|
4667
|
+
* Stops at the first segment containing glob characters (`*`, `{`, `?`, `[`).
|
|
4668
|
+
*
|
|
4669
|
+
* @param glob - A glob pattern (e.g., `j:/domains/**\/*.json`).
|
|
4670
|
+
* @returns The static directory prefix (e.g., `j:/domains`).
|
|
4671
|
+
*/
|
|
4672
|
+
function globRoot(glob) {
|
|
4673
|
+
const normalized = normalizeSlashes(glob);
|
|
4674
|
+
const segments = normalized.split('/');
|
|
4675
|
+
const staticSegments = [];
|
|
4676
|
+
for (const seg of segments) {
|
|
4677
|
+
if (/[*?{[\]]/.test(seg))
|
|
4678
|
+
break;
|
|
4679
|
+
staticSegments.push(seg);
|
|
4680
|
+
}
|
|
4681
|
+
return staticSegments.join('/') || '.';
|
|
4682
|
+
}
|
|
4683
|
+
/**
|
|
4684
|
+
* Deduplicate directory roots, removing paths that are subdirectories of others.
|
|
4685
|
+
*
|
|
4686
|
+
* @param roots - Array of directory paths.
|
|
4687
|
+
* @returns Deduplicated array with subdirectories removed.
|
|
4688
|
+
*/
|
|
4689
|
+
function deduplicateRoots(roots) {
|
|
4690
|
+
const normalized = roots.map((r) => normalizeSlashes(r).toLowerCase());
|
|
4691
|
+
const sorted = [...new Set(normalized)].sort();
|
|
4692
|
+
return sorted.filter((root, _i, arr) => {
|
|
4693
|
+
const withSlash = root.endsWith('/') ? root : root + '/';
|
|
4694
|
+
return !arr.some((other) => other !== root && withSlash.startsWith(other + '/'));
|
|
4695
|
+
});
|
|
4696
|
+
}
|
|
4697
|
+
/**
|
|
4698
|
+
* Build a picomatch matcher from an array of glob patterns.
|
|
4699
|
+
* Normalizes Windows paths (backslash → forward slash, lowercase drive letter)
|
|
4700
|
+
* before matching.
|
|
4701
|
+
*
|
|
4702
|
+
* @param globs - Glob patterns to match against.
|
|
4703
|
+
* @returns A function that tests whether a file path matches any of the globs.
|
|
4704
|
+
*/
|
|
4705
|
+
function buildGlobMatcher(globs) {
|
|
4706
|
+
const normalizedGlobs = globs.map((g) => normalizeSlashes(g));
|
|
4707
|
+
const isMatch = picomatch(normalizedGlobs, { dot: true, nocase: true });
|
|
4708
|
+
return (filePath) => {
|
|
4709
|
+
const normalized = normalizeSlashes(filePath);
|
|
4710
|
+
return isMatch(normalized);
|
|
4711
|
+
};
|
|
4712
|
+
}
|
|
4713
|
+
/**
|
|
4714
|
+
* Convert an array of glob patterns into chokidar-compatible directory roots
|
|
4715
|
+
* and a filter function for post-hoc event filtering.
|
|
4716
|
+
*
|
|
4717
|
+
* @param globs - Glob patterns from the watch config.
|
|
4718
|
+
* @returns Object with `roots` (directories for chokidar) and `matches` (filter function).
|
|
4719
|
+
*/
|
|
4720
|
+
function resolveWatchPaths(globs) {
|
|
4721
|
+
const rawRoots = globs.map(globRoot);
|
|
4722
|
+
const roots = deduplicateRoots(rawRoots);
|
|
4723
|
+
const matches = buildGlobMatcher(globs);
|
|
4724
|
+
return { roots, matches };
|
|
4725
|
+
}
|
|
4726
|
+
/**
|
|
4727
|
+
* Convert ignored glob patterns to picomatch matcher functions.
|
|
4728
|
+
*
|
|
4729
|
+
* Chokidar v5 replaced the external `anymatch` dependency with an inline
|
|
4730
|
+
* implementation that does **exact string equality** for string matchers,
|
|
4731
|
+
* breaking glob-based `ignored` patterns. This function converts glob strings
|
|
4732
|
+
* to picomatch functions that chokidar's `createPattern` passes through
|
|
4733
|
+
* unchanged (`typeof matcher === 'function'`).
|
|
4734
|
+
*
|
|
4735
|
+
* Non-string entries (functions, RegExps) are passed through as-is.
|
|
4736
|
+
*
|
|
4737
|
+
* @param ignored - Array of ignored patterns (globs, functions, RegExps).
|
|
4738
|
+
* @returns Array with glob strings replaced by picomatch matcher functions.
|
|
4739
|
+
*/
|
|
4740
|
+
function resolveIgnored(ignored) {
|
|
4741
|
+
return ignored.map((entry) => {
|
|
4742
|
+
if (typeof entry !== 'string')
|
|
4743
|
+
return entry;
|
|
4744
|
+
// If the string contains glob characters, convert to a picomatch function.
|
|
4745
|
+
// Literal strings (exact paths) are also converted for consistent matching.
|
|
4746
|
+
const normalizedPattern = normalizeSlashes(entry);
|
|
4747
|
+
const matcher = picomatch(normalizedPattern, { dot: true, nocase: true });
|
|
4748
|
+
return (filePath) => {
|
|
4749
|
+
const normalized = normalizeSlashes(filePath);
|
|
4750
|
+
return matcher(normalized);
|
|
4751
|
+
};
|
|
4752
|
+
});
|
|
4753
|
+
}
|
|
4754
|
+
|
|
4657
4755
|
/**
|
|
4658
4756
|
* @module vcs/VcsManager
|
|
4659
4757
|
* Per-root VCS manager for git-backed content versioning.
|
|
@@ -4957,11 +5055,10 @@ class VcsCoordinator {
|
|
|
4957
5055
|
const rootVcs = entry.vcs?.enabled ?? config.vcs.enabled;
|
|
4958
5056
|
if (!rootVcs)
|
|
4959
5057
|
continue;
|
|
4960
|
-
|
|
4961
|
-
|
|
5058
|
+
const resolvedRoot = normalizeSlashes(resolve(globRoot(entry.path)));
|
|
5059
|
+
// Deduplicate: skip if another glob already resolved to this root.
|
|
5060
|
+
if (this.managers.has(resolvedRoot))
|
|
4962
5061
|
continue;
|
|
4963
|
-
}
|
|
4964
|
-
const resolvedRoot = normalizeSlashes(resolve(entry.path));
|
|
4965
5062
|
const mergedConfig = {
|
|
4966
5063
|
...config.vcs,
|
|
4967
5064
|
...entry.vcs,
|
|
@@ -5224,20 +5321,22 @@ async function initVcs(config, logger) {
|
|
|
5224
5321
|
}
|
|
5225
5322
|
const stateDir = config.stateDir ?? '.jeeves-metadata';
|
|
5226
5323
|
const pathStrings = extractWatchPathStrings(config.watch.paths);
|
|
5227
|
-
const staticPaths = pathStrings.
|
|
5324
|
+
const staticPaths = pathStrings.map((p) => globRoot(p));
|
|
5228
5325
|
validateStateDirOverlap(stateDir, staticPaths);
|
|
5229
5326
|
const normalized = normalizeWatchPaths(config.watch.paths);
|
|
5327
|
+
const initializedRoots = new Set();
|
|
5230
5328
|
for (const entry of normalized) {
|
|
5231
5329
|
const rootVcs = entry.vcs?.enabled ?? config.vcs.enabled;
|
|
5232
5330
|
if (!rootVcs)
|
|
5233
5331
|
continue;
|
|
5234
|
-
|
|
5235
|
-
|
|
5332
|
+
const root = globRoot(entry.path);
|
|
5333
|
+
// Deduplicate: skip if another glob already resolved to this root.
|
|
5334
|
+
if (initializedRoots.has(root))
|
|
5236
5335
|
continue;
|
|
5237
|
-
|
|
5238
|
-
await initRepo(
|
|
5239
|
-
await ensureGitignore(
|
|
5240
|
-
logger.info({ root
|
|
5336
|
+
initializedRoots.add(root);
|
|
5337
|
+
await initRepo(root);
|
|
5338
|
+
await ensureGitignore(root);
|
|
5339
|
+
logger.info({ root }, 'VCS initialized for watch root');
|
|
5241
5340
|
}
|
|
5242
5341
|
return true;
|
|
5243
5342
|
}
|
|
@@ -8093,104 +8192,6 @@ class SystemHealth {
|
|
|
8093
8192
|
}
|
|
8094
8193
|
}
|
|
8095
8194
|
|
|
8096
|
-
/**
|
|
8097
|
-
* @module watcher/globToDir
|
|
8098
|
-
* Adapts glob-based watch config to chokidar v4+, which removed glob support
|
|
8099
|
-
* (see paulmillr/chokidar#1350). Chokidar v4 treats glob patterns as literal
|
|
8100
|
-
* strings, silently producing zero events. This module extracts static directory
|
|
8101
|
-
* roots from glob patterns for chokidar to watch, then filters emitted events
|
|
8102
|
-
* against the original globs via picomatch.
|
|
8103
|
-
*/
|
|
8104
|
-
/**
|
|
8105
|
-
* Extract the static directory root from a glob pattern.
|
|
8106
|
-
* Stops at the first segment containing glob characters (`*`, `{`, `?`, `[`).
|
|
8107
|
-
*
|
|
8108
|
-
* @param glob - A glob pattern (e.g., `j:/domains/**\/*.json`).
|
|
8109
|
-
* @returns The static directory prefix (e.g., `j:/domains`).
|
|
8110
|
-
*/
|
|
8111
|
-
function globRoot(glob) {
|
|
8112
|
-
const normalized = normalizeSlashes(glob);
|
|
8113
|
-
const segments = normalized.split('/');
|
|
8114
|
-
const staticSegments = [];
|
|
8115
|
-
for (const seg of segments) {
|
|
8116
|
-
if (/[*?{[\]]/.test(seg))
|
|
8117
|
-
break;
|
|
8118
|
-
staticSegments.push(seg);
|
|
8119
|
-
}
|
|
8120
|
-
return staticSegments.join('/') || '.';
|
|
8121
|
-
}
|
|
8122
|
-
/**
|
|
8123
|
-
* Deduplicate directory roots, removing paths that are subdirectories of others.
|
|
8124
|
-
*
|
|
8125
|
-
* @param roots - Array of directory paths.
|
|
8126
|
-
* @returns Deduplicated array with subdirectories removed.
|
|
8127
|
-
*/
|
|
8128
|
-
function deduplicateRoots(roots) {
|
|
8129
|
-
const normalized = roots.map((r) => normalizeSlashes(r).toLowerCase());
|
|
8130
|
-
const sorted = [...new Set(normalized)].sort();
|
|
8131
|
-
return sorted.filter((root, _i, arr) => {
|
|
8132
|
-
const withSlash = root.endsWith('/') ? root : root + '/';
|
|
8133
|
-
return !arr.some((other) => other !== root && withSlash.startsWith(other + '/'));
|
|
8134
|
-
});
|
|
8135
|
-
}
|
|
8136
|
-
/**
|
|
8137
|
-
* Build a picomatch matcher from an array of glob patterns.
|
|
8138
|
-
* Normalizes Windows paths (backslash → forward slash, lowercase drive letter)
|
|
8139
|
-
* before matching.
|
|
8140
|
-
*
|
|
8141
|
-
* @param globs - Glob patterns to match against.
|
|
8142
|
-
* @returns A function that tests whether a file path matches any of the globs.
|
|
8143
|
-
*/
|
|
8144
|
-
function buildGlobMatcher(globs) {
|
|
8145
|
-
const normalizedGlobs = globs.map((g) => normalizeSlashes(g));
|
|
8146
|
-
const isMatch = picomatch(normalizedGlobs, { dot: true, nocase: true });
|
|
8147
|
-
return (filePath) => {
|
|
8148
|
-
const normalized = normalizeSlashes(filePath);
|
|
8149
|
-
return isMatch(normalized);
|
|
8150
|
-
};
|
|
8151
|
-
}
|
|
8152
|
-
/**
|
|
8153
|
-
* Convert an array of glob patterns into chokidar-compatible directory roots
|
|
8154
|
-
* and a filter function for post-hoc event filtering.
|
|
8155
|
-
*
|
|
8156
|
-
* @param globs - Glob patterns from the watch config.
|
|
8157
|
-
* @returns Object with `roots` (directories for chokidar) and `matches` (filter function).
|
|
8158
|
-
*/
|
|
8159
|
-
function resolveWatchPaths(globs) {
|
|
8160
|
-
const rawRoots = globs.map(globRoot);
|
|
8161
|
-
const roots = deduplicateRoots(rawRoots);
|
|
8162
|
-
const matches = buildGlobMatcher(globs);
|
|
8163
|
-
return { roots, matches };
|
|
8164
|
-
}
|
|
8165
|
-
/**
|
|
8166
|
-
* Convert ignored glob patterns to picomatch matcher functions.
|
|
8167
|
-
*
|
|
8168
|
-
* Chokidar v5 replaced the external `anymatch` dependency with an inline
|
|
8169
|
-
* implementation that does **exact string equality** for string matchers,
|
|
8170
|
-
* breaking glob-based `ignored` patterns. This function converts glob strings
|
|
8171
|
-
* to picomatch functions that chokidar's `createPattern` passes through
|
|
8172
|
-
* unchanged (`typeof matcher === 'function'`).
|
|
8173
|
-
*
|
|
8174
|
-
* Non-string entries (functions, RegExps) are passed through as-is.
|
|
8175
|
-
*
|
|
8176
|
-
* @param ignored - Array of ignored patterns (globs, functions, RegExps).
|
|
8177
|
-
* @returns Array with glob strings replaced by picomatch matcher functions.
|
|
8178
|
-
*/
|
|
8179
|
-
function resolveIgnored(ignored) {
|
|
8180
|
-
return ignored.map((entry) => {
|
|
8181
|
-
if (typeof entry !== 'string')
|
|
8182
|
-
return entry;
|
|
8183
|
-
// If the string contains glob characters, convert to a picomatch function.
|
|
8184
|
-
// Literal strings (exact paths) are also converted for consistent matching.
|
|
8185
|
-
const normalizedPattern = normalizeSlashes(entry);
|
|
8186
|
-
const matcher = picomatch(normalizedPattern, { dot: true, nocase: true });
|
|
8187
|
-
return (filePath) => {
|
|
8188
|
-
const normalized = normalizeSlashes(filePath);
|
|
8189
|
-
return matcher(normalized);
|
|
8190
|
-
};
|
|
8191
|
-
});
|
|
8192
|
-
}
|
|
8193
|
-
|
|
8194
8195
|
/**
|
|
8195
8196
|
* @module watcher/MoveCorrelator
|
|
8196
8197
|
* Correlates unlink+add events as file moves using content hash matching.
|
package/package.json
CHANGED