@dipras/filesyncer 1.0.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/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 Dipras
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
package/README.md ADDED
@@ -0,0 +1,294 @@
1
+ # FileSyncer 🚀
2
+
3
+ [![npm version](https://badge.fury.io/js/filesyncer.svg)](https://www.npmjs.com/package/filesyncer)
4
+ [![CI](https://github.com/dipras/filesyncer/workflows/CI/badge.svg)](https://github.com/dipras/filesyncer/actions)
5
+ [![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](https://opensource.org/licenses/MIT)
6
+
7
+ **Real-time file synchronization tool for remote development workflows**
8
+
9
+ FileSyncer is a CLI tool that enables developers to automatically sync files from their local environment to remote servers via SSH. Perfect for development workflows with VPS or remote servers.
10
+
11
+ ## ✨ Features
12
+
13
+ - 🔍 **Smart File Watching** - Real-time file change detection with debouncing
14
+ - 🚫 **Intelligent Filtering** - Support for `.gitignore` patterns and custom ignore rules
15
+ - 🔒 **Secure Transfer** - SSH-based transfer using rsync or SCP
16
+ - ⚡ **Efficient Sync** - Delta transfer and compression for optimal performance
17
+ - 🎯 **Git Integration** - Optional: sync only Git-tracked files
18
+ - 📦 **Zero Config Start** - Quick setup with default configuration
19
+
20
+ ## 🔧 Installation
21
+
22
+ ### Global Installation
23
+
24
+ ```bash
25
+ npm install -g filesyncer
26
+ ```
27
+
28
+ ### Local Project
29
+
30
+ ```bash
31
+ npm install --save-dev filesyncer
32
+ ```
33
+
34
+ ### NPX (No Installation Required)
35
+
36
+ ```bash
37
+ npx filesyncer init
38
+ ```
39
+
40
+ ## 🚀 Quick Start
41
+
42
+ ### 1. Initialize Configuration
43
+
44
+ ```bash
45
+ npx filesyncer init
46
+ ```
47
+
48
+ This creates a `sync.json` file:
49
+
50
+ ```json
51
+ {
52
+ "source": ".",
53
+ "destination": "/var/www/app",
54
+ "host": "example.com",
55
+ "username": "user",
56
+ "port": 22,
57
+ "privateKeyPath": "~/.ssh/id_rsa",
58
+ "ignorePatterns": ["node_modules/**", "dist/**", ".git/**"],
59
+ "useGitTracking": false,
60
+ "debounceMs": 1000,
61
+ "syncMethod": "rsync",
62
+ "excludeFromGitIgnore": true
63
+ }
64
+ ```
65
+
66
+ ### 2. Edit Configuration
67
+
68
+ Update `sync.json` with your server details:
69
+
70
+ ```json
71
+ {
72
+ "source": ".",
73
+ "destination": "/home/youruser/myapp",
74
+ "host": "your-server.com",
75
+ "username": "youruser",
76
+ "port": 22,
77
+ "privateKeyPath": "~/.ssh/id_rsa"
78
+ }
79
+ ```
80
+
81
+ ### 3. Start Watching
82
+
83
+ ```bash
84
+ npx filesyncer watch
85
+ ```
86
+
87
+ Now every time you save a file, it will automatically sync to the server! 🎉
88
+
89
+ ## 📖 Usage
90
+
91
+ ### Commands
92
+
93
+ #### `init` - Initialize Configuration
94
+
95
+ ```bash
96
+ filesyncer init
97
+ filesyncer init --config custom-sync.json
98
+ ```
99
+
100
+ #### `watch` - Start File Watcher
101
+
102
+ ```bash
103
+ filesyncer watch
104
+ filesyncer watch --config custom-sync.json
105
+ ```
106
+
107
+ Auto-sync whenever files change.
108
+
109
+ #### `deploy` - One-Time Full Sync
110
+
111
+ ```bash
112
+ filesyncer deploy
113
+ filesyncer deploy --config custom-sync.json
114
+ ```
115
+
116
+ Sync all files once without watching.
117
+
118
+ ## ⚙️ Configuration Options
119
+
120
+ | Option | Type | Default | Description |
121
+ |--------|------|---------|-------------|
122
+ | `source` | string | `"."` | Source directory (local) |
123
+ | `destination` | string | - | Destination path on remote server |
124
+ | `host` | string | - | Remote server hostname/IP |
125
+ | `username` | string | - | SSH username |
126
+ | `port` | number | `22` | SSH port |
127
+ | `privateKeyPath` | string | `~/.ssh/id_rsa` | Path to SSH private key |
128
+ | `ignorePatterns` | string[] | `[]` | Array of glob patterns to ignore |
129
+ | `useGitTracking` | boolean | `false` | Only sync Git-tracked files |
130
+ | `debounceMs` | number | `1000` | Debounce delay (ms) |
131
+ | `syncMethod` | string | `"rsync"` | Transfer method: `"rsync"` or `"scp"` |
132
+ | `excludeFromGitIgnore` | boolean | `true` | Automatically exclude files from `.gitignore` |
133
+
134
+ ## 🎯 Use Cases
135
+
136
+ ### Backend Development on VPS
137
+
138
+ ```bash
139
+ # Local development
140
+ npm run dev
141
+
142
+ # In another terminal
143
+ filesyncer watch
144
+
145
+ # Edit code → Auto sync → Test on server
146
+ ```
147
+
148
+ ### Remote Docker Development
149
+
150
+ ```json
151
+ {
152
+ "source": "./src",
153
+ "destination": "/app/src",
154
+ "host": "docker-host.local",
155
+ "syncMethod": "rsync"
156
+ }
157
+ ```
158
+
159
+ ### Multiple Server Sync
160
+
161
+ Create multiple config files:
162
+
163
+ ```bash
164
+ filesyncer watch --config sync-staging.json
165
+ filesyncer watch --config sync-production.json
166
+ ```
167
+
168
+ ## 🔒 Security Best Practices
169
+
170
+ 1. **Use SSH Keys** - Never hardcode passwords
171
+ 2. **Restrict Key Permissions** - `chmod 600 ~/.ssh/id_rsa`
172
+ 3. **Use Non-Root User** - Don't sync as root user
173
+ 4. **Firewall Rules** - Restrict SSH access by IP
174
+
175
+ ## 🐛 Troubleshooting
176
+
177
+ ### Connection Failed
178
+
179
+ ```bash
180
+ # Test SSH connection manually
181
+ ssh -p 22 user@host
182
+
183
+ # Verify key permissions
184
+ chmod 600 ~/.ssh/id_rsa
185
+ ```
186
+
187
+ ### rsync Not Found
188
+
189
+ Install rsync:
190
+
191
+ ```bash
192
+ # Ubuntu/Debian
193
+ sudo apt install rsync
194
+
195
+ # macOS
196
+ brew install rsync
197
+
198
+ # Or use SCP fallback
199
+ "syncMethod": "scp"
200
+ ```
201
+
202
+ ### Files Not Syncing
203
+
204
+ 1. Check `.gitignore` patterns
205
+ 2. Verify `ignorePatterns` in config
206
+ 3. Check file permissions
207
+ 4. Enable verbose logging
208
+
209
+ ## 🗺️ Roadmap
210
+
211
+ ### Level 1 ✅ (Current)
212
+ - [x] File watcher with chokidar
213
+ - [x] SSH sync (rsync/SCP)
214
+ - [x] Ignore pattern support
215
+ - [x] Basic CLI commands
216
+
217
+ ### Level 2 🚧 (Planned)
218
+ - [ ] Queue transfer system
219
+ - [ ] Parallel upload
220
+ - [ ] Bandwidth throttle
221
+ - [ ] Progress reporting
222
+ - [ ] Conflict resolution
223
+
224
+ ### Level 3 🔮 (Future)
225
+ - [ ] Binary diff transfer
226
+ - [ ] Multi-server sync
227
+ - [ ] Web dashboard
228
+ - [ ] VS Code extension
229
+
230
+ ## 🤝 Contributing
231
+
232
+ Contributions are welcome! Please feel free to submit a Pull Request.
233
+
234
+ ### Development Setup
235
+
236
+ ```bash
237
+ # Clone the repository
238
+ git clone https://github.com/dipras/filesyncer.git
239
+ cd filesyncer
240
+
241
+ # Install dependencies
242
+ npm install
243
+
244
+ # Build
245
+ npm run build
246
+
247
+ # Test locally
248
+ npm link
249
+ filesyncer --help
250
+ ```
251
+
252
+ ### Automated Publishing
253
+
254
+ This project uses GitHub Actions for automated publishing to NPM:
255
+
256
+ - **CI Workflow**: Runs on every push/PR to test the build
257
+ - **Publish Workflow**: Automatically publishes to NPM when you push a version tag
258
+
259
+ To publish a new version:
260
+
261
+ ```bash
262
+ # Update version in package.json
263
+ npm version patch # or minor, or major
264
+
265
+ # Push with tags
266
+ git push && git push --tags
267
+
268
+ # GitHub Actions will automatically publish to NPM
269
+ ```
270
+
271
+ **Setup Required** (one-time):
272
+ 1. Get your NPM access token from [npmjs.com/settings/tokens](https://www.npmjs.com/settings/tokens)
273
+ 2. Add it as `NPM_TOKEN` secret in your GitHub repository settings
274
+ 3. Go to: Settings → Secrets and variables → Actions → New repository secret
275
+
276
+ ## 📄 License
277
+
278
+ ISC © Dipras
279
+
280
+ ## 💡 Inspiration
281
+
282
+ FileSyncer was created to solve development workflow challenges:
283
+ - ✅ Real-time sync without manual upload
284
+ - ✅ Lightweight alternative to Git CI/CD
285
+ - ✅ Near real-time development feedback
286
+ - ✅ Developer productivity boost
287
+
288
+ ## 🌟 Star This Project
289
+
290
+ If you find this useful, please consider giving it a star ⭐
291
+
292
+ ---
293
+
294
+ **Made with ❤️ for developers who love productivity**
package/dist/cli.d.ts ADDED
@@ -0,0 +1,3 @@
1
+ #!/usr/bin/env node
2
+ export {};
3
+ //# sourceMappingURL=cli.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"cli.d.ts","sourceRoot":"","sources":["../src/cli.ts"],"names":[],"mappings":""}
package/dist/cli.js ADDED
@@ -0,0 +1,148 @@
1
+ #!/usr/bin/env node
2
+ import { Command } from 'commander';
3
+ import chalk from 'chalk';
4
+ import ora from 'ora';
5
+ import { resolve } from 'path';
6
+ import { ConfigManager } from './core/ConfigManager.js';
7
+ import { FileWatcher } from './core/FileWatcher.js';
8
+ import { SyncEngine } from './core/SyncEngine.js';
9
+ const program = new Command();
10
+ program
11
+ .name('filesyncer')
12
+ .description('Real-time file synchronization tool for development')
13
+ .version('1.0.0');
14
+ /**
15
+ * Init Command - Create configuration file
16
+ */
17
+ program
18
+ .command('init')
19
+ .description('Initialize FileSyncer configuration')
20
+ .option('-c, --config <path>', 'Config file path', 'sync.json')
21
+ .action(async (options) => {
22
+ try {
23
+ const configManager = new ConfigManager(options.config);
24
+ if (configManager.exists()) {
25
+ console.log(chalk.yellow('⚠️ Configuration file already exists!'));
26
+ console.log(chalk.gray(` ${resolve(options.config)}`));
27
+ return;
28
+ }
29
+ const config = configManager.createDefault();
30
+ console.log(chalk.green('✓ Configuration file created!'));
31
+ console.log(chalk.gray(` ${resolve(options.config)}`));
32
+ console.log();
33
+ console.log(chalk.cyan('📝 Please edit the configuration file with your settings:'));
34
+ console.log(JSON.stringify(config, null, 2));
35
+ }
36
+ catch (error) {
37
+ console.error(chalk.red('✗ Failed to initialize:'), error instanceof Error ? error.message : error);
38
+ process.exit(1);
39
+ }
40
+ });
41
+ /**
42
+ * Watch Command - Start file watcher
43
+ */
44
+ program
45
+ .command('watch')
46
+ .description('Watch files and sync changes automatically')
47
+ .option('-c, --config <path>', 'Config file path', 'sync.json')
48
+ .action(async (options) => {
49
+ try {
50
+ const spinner = ora('Loading configuration...').start();
51
+ const configManager = new ConfigManager(options.config);
52
+ const config = configManager.load();
53
+ spinner.succeed('Configuration loaded');
54
+ // Test connection
55
+ spinner.start('Testing connection to remote server...');
56
+ const syncEngine = new SyncEngine(config);
57
+ const connected = await syncEngine.testConnection();
58
+ if (!connected) {
59
+ spinner.fail('Failed to connect to remote server');
60
+ console.log(chalk.yellow('\n⚠️ Please check your SSH configuration:'));
61
+ console.log(chalk.gray(` Host: ${config.username}@${config.host}:${config.port || 22}`));
62
+ console.log(chalk.gray(` Key: ${config.privateKeyPath || 'default'}`));
63
+ process.exit(1);
64
+ }
65
+ spinner.succeed('Connected to remote server');
66
+ // Start watcher
67
+ const baseDir = resolve(config.source);
68
+ const watcher = new FileWatcher(config, baseDir);
69
+ console.log();
70
+ console.log(chalk.cyan('👀 Watching for changes...'));
71
+ console.log(chalk.gray(` Source: ${baseDir}`));
72
+ console.log(chalk.gray(` Remote: ${config.username}@${config.host}:${config.destination}`));
73
+ console.log(chalk.gray(` Method: ${config.syncMethod || 'rsync'}`));
74
+ console.log();
75
+ await watcher.start(async (events) => {
76
+ console.log(chalk.blue(`\n📦 ${events.length} file(s) changed:`));
77
+ events.forEach(e => {
78
+ const icon = e.type === 'add' ? '➕' : e.type === 'change' ? '📝' : '🗑️';
79
+ console.log(chalk.gray(` ${icon} ${e.path}`));
80
+ });
81
+ const syncSpinner = ora('Syncing...').start();
82
+ const result = await syncEngine.sync(events);
83
+ if (result.success) {
84
+ syncSpinner.succeed(`Synced in ${result.duration}ms`);
85
+ }
86
+ else {
87
+ syncSpinner.fail('Sync failed');
88
+ result.errors?.forEach(err => {
89
+ console.log(chalk.red(` ${err}`));
90
+ });
91
+ }
92
+ });
93
+ // Handle graceful shutdown
94
+ process.on('SIGINT', async () => {
95
+ console.log(chalk.yellow('\n\n⏸️ Stopping watcher...'));
96
+ await watcher.stop();
97
+ process.exit(0);
98
+ });
99
+ }
100
+ catch (error) {
101
+ console.error(chalk.red('✗ Error:'), error instanceof Error ? error.message : error);
102
+ process.exit(1);
103
+ }
104
+ });
105
+ /**
106
+ * Deploy Command - One-time sync
107
+ */
108
+ program
109
+ .command('deploy')
110
+ .description('Deploy all files once (full sync)')
111
+ .option('-c, --config <path>', 'Config file path', 'sync.json')
112
+ .action(async (options) => {
113
+ try {
114
+ const spinner = ora('Loading configuration...').start();
115
+ const configManager = new ConfigManager(options.config);
116
+ const config = configManager.load();
117
+ spinner.succeed('Configuration loaded');
118
+ // Test connection
119
+ spinner.start('Testing connection...');
120
+ const syncEngine = new SyncEngine(config);
121
+ const connected = await syncEngine.testConnection();
122
+ if (!connected) {
123
+ spinner.fail('Failed to connect to remote server');
124
+ process.exit(1);
125
+ }
126
+ spinner.succeed('Connected');
127
+ // Sync
128
+ spinner.start('Deploying files...');
129
+ const result = await syncEngine.sync();
130
+ if (result.success) {
131
+ spinner.succeed(`Deployed successfully in ${result.duration}ms`);
132
+ console.log(chalk.green(`\n✓ All files synced to ${config.username}@${config.host}:${config.destination}`));
133
+ }
134
+ else {
135
+ spinner.fail('Deploy failed');
136
+ result.errors?.forEach(err => {
137
+ console.log(chalk.red(` ${err}`));
138
+ });
139
+ process.exit(1);
140
+ }
141
+ }
142
+ catch (error) {
143
+ console.error(chalk.red('✗ Error:'), error instanceof Error ? error.message : error);
144
+ process.exit(1);
145
+ }
146
+ });
147
+ program.parse();
148
+ //# sourceMappingURL=cli.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"cli.js","sourceRoot":"","sources":["../src/cli.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,31 @@
1
+ import type { SyncConfig } from '../types.js';
2
+ export declare class ConfigManager {
3
+ private configPath;
4
+ private config;
5
+ constructor(configPath?: string);
6
+ /**
7
+ * Load configuration from file
8
+ */
9
+ load(): SyncConfig;
10
+ /**
11
+ * Save configuration to file
12
+ */
13
+ save(config: SyncConfig): void;
14
+ /**
15
+ * Create default configuration
16
+ */
17
+ createDefault(): SyncConfig;
18
+ /**
19
+ * Validate configuration
20
+ */
21
+ private validate;
22
+ /**
23
+ * Get current configuration
24
+ */
25
+ getConfig(): SyncConfig | null;
26
+ /**
27
+ * Check if config file exists
28
+ */
29
+ exists(): boolean;
30
+ }
31
+ //# sourceMappingURL=ConfigManager.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"ConfigManager.d.ts","sourceRoot":"","sources":["../../src/core/ConfigManager.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,aAAa,CAAC;AAE9C,qBAAa,aAAa;IACxB,OAAO,CAAC,UAAU,CAAS;IAC3B,OAAO,CAAC,MAAM,CAA2B;gBAE7B,UAAU,CAAC,EAAE,MAAM;IAI/B;;OAEG;IACH,IAAI,IAAI,UAAU;IAelB;;OAEG;IACH,IAAI,CAAC,MAAM,EAAE,UAAU,GAAG,IAAI;IAU9B;;OAEG;IACH,aAAa,IAAI,UAAU;IAmB3B;;OAEG;IACH,OAAO,CAAC,QAAQ;IAiBhB;;OAEG;IACH,SAAS,IAAI,UAAU,GAAG,IAAI;IAI9B;;OAEG;IACH,MAAM,IAAI,OAAO;CAGlB"}
@@ -0,0 +1,89 @@
1
+ import { existsSync, readFileSync, writeFileSync } from 'fs';
2
+ import { join } from 'path';
3
+ export class ConfigManager {
4
+ configPath;
5
+ config = null;
6
+ constructor(configPath) {
7
+ this.configPath = configPath || join(process.cwd(), 'sync.json');
8
+ }
9
+ /**
10
+ * Load configuration from file
11
+ */
12
+ load() {
13
+ if (!existsSync(this.configPath)) {
14
+ throw new Error(`Config file not found: ${this.configPath}\nRun 'filesyncer init' to create one.`);
15
+ }
16
+ try {
17
+ const content = readFileSync(this.configPath, 'utf-8');
18
+ this.config = JSON.parse(content);
19
+ this.validate(this.config);
20
+ return this.config;
21
+ }
22
+ catch (error) {
23
+ throw new Error(`Failed to load config: ${error instanceof Error ? error.message : error}`);
24
+ }
25
+ }
26
+ /**
27
+ * Save configuration to file
28
+ */
29
+ save(config) {
30
+ this.validate(config);
31
+ try {
32
+ writeFileSync(this.configPath, JSON.stringify(config, null, 2), 'utf-8');
33
+ this.config = config;
34
+ }
35
+ catch (error) {
36
+ throw new Error(`Failed to save config: ${error instanceof Error ? error.message : error}`);
37
+ }
38
+ }
39
+ /**
40
+ * Create default configuration
41
+ */
42
+ createDefault() {
43
+ const defaultConfig = {
44
+ source: '.',
45
+ destination: '/var/www/app',
46
+ host: 'example.com',
47
+ username: 'user',
48
+ port: 22,
49
+ privateKeyPath: '~/.ssh/id_rsa',
50
+ ignorePatterns: ['node_modules/**', 'dist/**', '.git/**'],
51
+ useGitTracking: false,
52
+ debounceMs: 1000,
53
+ syncMethod: 'rsync',
54
+ excludeFromGitIgnore: true
55
+ };
56
+ this.save(defaultConfig);
57
+ return defaultConfig;
58
+ }
59
+ /**
60
+ * Validate configuration
61
+ */
62
+ validate(config) {
63
+ const required = ['source', 'destination', 'host', 'username'];
64
+ for (const field of required) {
65
+ if (!config[field]) {
66
+ throw new Error(`Missing required field: ${field}`);
67
+ }
68
+ }
69
+ if (config.syncMethod && !['rsync', 'scp'].includes(config.syncMethod)) {
70
+ throw new Error('syncMethod must be either "rsync" or "scp"');
71
+ }
72
+ if (config.port && (config.port < 1 || config.port > 65535)) {
73
+ throw new Error('port must be between 1 and 65535');
74
+ }
75
+ }
76
+ /**
77
+ * Get current configuration
78
+ */
79
+ getConfig() {
80
+ return this.config;
81
+ }
82
+ /**
83
+ * Check if config file exists
84
+ */
85
+ exists() {
86
+ return existsSync(this.configPath);
87
+ }
88
+ }
89
+ //# sourceMappingURL=ConfigManager.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"ConfigManager.js","sourceRoot":"","sources":["../../src/core/ConfigManager.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,34 @@
1
+ import type { FileChangeEvent, SyncConfig } from '../types.js';
2
+ export declare class FileWatcher {
3
+ private config;
4
+ private baseDir;
5
+ private watcher;
6
+ private ignoreFilter;
7
+ private gitTracker;
8
+ private changeQueue;
9
+ private debounceTimer;
10
+ private debounceMs;
11
+ private onChangeCallback?;
12
+ constructor(config: SyncConfig, baseDir: string);
13
+ /**
14
+ * Start watching files
15
+ */
16
+ start(onChange: (events: FileChangeEvent[]) => void): Promise<void>;
17
+ /**
18
+ * Handle file change event
19
+ */
20
+ private handleChange;
21
+ /**
22
+ * Process queued changes
23
+ */
24
+ private processQueue;
25
+ /**
26
+ * Stop watching
27
+ */
28
+ stop(): Promise<void>;
29
+ /**
30
+ * Get current watch status
31
+ */
32
+ isWatching(): boolean;
33
+ }
34
+ //# sourceMappingURL=FileWatcher.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"FileWatcher.d.ts","sourceRoot":"","sources":["../../src/core/FileWatcher.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,eAAe,EAAE,UAAU,EAAE,MAAM,aAAa,CAAC;AAI/D,qBAAa,WAAW;IAUpB,OAAO,CAAC,MAAM;IACd,OAAO,CAAC,OAAO;IAVjB,OAAO,CAAC,OAAO,CAA0B;IACzC,OAAO,CAAC,YAAY,CAAe;IACnC,OAAO,CAAC,UAAU,CAAa;IAC/B,OAAO,CAAC,WAAW,CAA2C;IAC9D,OAAO,CAAC,aAAa,CAA+B;IACpD,OAAO,CAAC,UAAU,CAAS;IAC3B,OAAO,CAAC,gBAAgB,CAAC,CAAsC;gBAGrD,MAAM,EAAE,UAAU,EAClB,OAAO,EAAE,MAAM;IAUzB;;OAEG;IACG,KAAK,CAAC,QAAQ,EAAE,CAAC,MAAM,EAAE,eAAe,EAAE,KAAK,IAAI,GAAG,OAAO,CAAC,IAAI,CAAC;IA+BzE;;OAEG;YACW,YAAY;IAsC1B;;OAEG;IACH,OAAO,CAAC,YAAY;IAWpB;;OAEG;IACG,IAAI,IAAI,OAAO,CAAC,IAAI,CAAC;IAa3B;;OAEG;IACH,UAAU,IAAI,OAAO;CAGtB"}