spck 0.3.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (155) hide show
  1. package/.oxlintrc.json +49 -0
  2. package/LICENSE +21 -0
  3. package/README.md +631 -0
  4. package/bin/cli.js +20 -0
  5. package/bin/validate-cwd.js +41 -0
  6. package/dist/config/__tests__/config.test.d.ts +2 -0
  7. package/dist/config/__tests__/config.test.js +262 -0
  8. package/dist/config/__tests__/credentials.test.d.ts +2 -0
  9. package/dist/config/__tests__/credentials.test.js +360 -0
  10. package/dist/config/config.d.ts +33 -0
  11. package/dist/config/config.js +185 -0
  12. package/dist/config/credentials.d.ts +75 -0
  13. package/dist/config/credentials.js +259 -0
  14. package/dist/config/server-selection.d.ts +40 -0
  15. package/dist/config/server-selection.js +130 -0
  16. package/dist/connection/__tests__/firebase-auth.test.d.ts +2 -0
  17. package/dist/connection/__tests__/firebase-auth.test.js +96 -0
  18. package/dist/connection/__tests__/hmac.test.d.ts +2 -0
  19. package/dist/connection/__tests__/hmac.test.js +372 -0
  20. package/dist/connection/auth.d.ts +13 -0
  21. package/dist/connection/auth.js +91 -0
  22. package/dist/connection/firebase-auth.d.ts +40 -0
  23. package/dist/connection/firebase-auth.js +429 -0
  24. package/dist/connection/hmac.d.ts +24 -0
  25. package/dist/connection/hmac.js +109 -0
  26. package/dist/i18n/index.d.ts +25 -0
  27. package/dist/i18n/index.js +101 -0
  28. package/dist/i18n/locales/en.json +313 -0
  29. package/dist/i18n/locales/es.json +302 -0
  30. package/dist/i18n/locales/fr.json +302 -0
  31. package/dist/i18n/locales/id.json +302 -0
  32. package/dist/i18n/locales/ja.json +302 -0
  33. package/dist/i18n/locales/ko.json +302 -0
  34. package/dist/i18n/locales/locales/en.json +309 -0
  35. package/dist/i18n/locales/locales/es.json +302 -0
  36. package/dist/i18n/locales/locales/fr.json +302 -0
  37. package/dist/i18n/locales/locales/id.json +302 -0
  38. package/dist/i18n/locales/locales/ja.json +302 -0
  39. package/dist/i18n/locales/locales/ko.json +302 -0
  40. package/dist/i18n/locales/locales/pt.json +302 -0
  41. package/dist/i18n/locales/locales/zh-Hans.json +302 -0
  42. package/dist/i18n/locales/pt.json +302 -0
  43. package/dist/i18n/locales/zh-Hans.json +302 -0
  44. package/dist/index.d.ts +25 -0
  45. package/dist/index.js +493 -0
  46. package/dist/proxy/ProxyClient.d.ts +125 -0
  47. package/dist/proxy/ProxyClient.js +781 -0
  48. package/dist/proxy/ProxySocketWrapper.d.ts +43 -0
  49. package/dist/proxy/ProxySocketWrapper.js +98 -0
  50. package/dist/proxy/__tests__/ProxyClient.test.d.ts +2 -0
  51. package/dist/proxy/__tests__/ProxyClient.test.js +445 -0
  52. package/dist/proxy/__tests__/ProxySocketWrapper.test.d.ts +2 -0
  53. package/dist/proxy/__tests__/ProxySocketWrapper.test.js +190 -0
  54. package/dist/proxy/__tests__/handshake-validation.test.d.ts +2 -0
  55. package/dist/proxy/__tests__/handshake-validation.test.js +282 -0
  56. package/dist/proxy/__tests__/token-refresh-race.test.d.ts +14 -0
  57. package/dist/proxy/__tests__/token-refresh-race.test.js +173 -0
  58. package/dist/proxy/chunking.d.ts +53 -0
  59. package/dist/proxy/chunking.js +127 -0
  60. package/dist/proxy/handshake-validation.d.ts +21 -0
  61. package/dist/proxy/handshake-validation.js +49 -0
  62. package/dist/rpc/__tests__/router.test.d.ts +2 -0
  63. package/dist/rpc/__tests__/router.test.js +262 -0
  64. package/dist/rpc/router.d.ts +37 -0
  65. package/dist/rpc/router.js +132 -0
  66. package/dist/services/BrowserProxyService.d.ts +13 -0
  67. package/dist/services/BrowserProxyService.js +139 -0
  68. package/dist/services/FilesystemService.d.ts +99 -0
  69. package/dist/services/FilesystemService.js +742 -0
  70. package/dist/services/GitService.d.ts +243 -0
  71. package/dist/services/GitService.js +1439 -0
  72. package/dist/services/SearchService.d.ts +93 -0
  73. package/dist/services/SearchService.js +670 -0
  74. package/dist/services/TerminalService.d.ts +62 -0
  75. package/dist/services/TerminalService.js +337 -0
  76. package/dist/services/__tests__/BrowserProxyService.test.d.ts +2 -0
  77. package/dist/services/__tests__/BrowserProxyService.test.js +145 -0
  78. package/dist/services/__tests__/FilesystemService.test.d.ts +2 -0
  79. package/dist/services/__tests__/FilesystemService.test.js +609 -0
  80. package/dist/services/__tests__/GitService.test.d.ts +2 -0
  81. package/dist/services/__tests__/GitService.test.js +953 -0
  82. package/dist/services/__tests__/SearchService.test.d.ts +2 -0
  83. package/dist/services/__tests__/SearchService.test.js +384 -0
  84. package/dist/services/__tests__/TerminalService.test.d.ts +2 -0
  85. package/dist/services/__tests__/TerminalService.test.js +513 -0
  86. package/dist/setup/wizard.d.ts +10 -0
  87. package/dist/setup/wizard.js +172 -0
  88. package/dist/types.d.ts +196 -0
  89. package/dist/types.js +44 -0
  90. package/dist/utils/__tests__/gitignore.test.d.ts +2 -0
  91. package/dist/utils/__tests__/gitignore.test.js +127 -0
  92. package/dist/utils/gitignore.d.ts +24 -0
  93. package/dist/utils/gitignore.js +77 -0
  94. package/dist/utils/logger.d.ts +96 -0
  95. package/dist/utils/logger.js +456 -0
  96. package/dist/utils/project-dir.d.ts +51 -0
  97. package/dist/utils/project-dir.js +191 -0
  98. package/dist/utils/ripgrep.d.ts +34 -0
  99. package/dist/utils/ripgrep.js +148 -0
  100. package/dist/utils/tool-detection.d.ts +17 -0
  101. package/dist/utils/tool-detection.js +126 -0
  102. package/dist/watcher/FileWatcher.d.ts +10 -0
  103. package/dist/watcher/FileWatcher.js +42 -0
  104. package/package.json +70 -0
  105. package/src/config/__tests__/config.test.ts +318 -0
  106. package/src/config/__tests__/credentials.test.ts +494 -0
  107. package/src/config/config.ts +206 -0
  108. package/src/config/credentials.ts +302 -0
  109. package/src/config/server-selection.ts +150 -0
  110. package/src/connection/__tests__/firebase-auth.test.ts +121 -0
  111. package/src/connection/__tests__/hmac.test.ts +509 -0
  112. package/src/connection/auth.ts +140 -0
  113. package/src/connection/firebase-auth.ts +504 -0
  114. package/src/connection/hmac.ts +139 -0
  115. package/src/i18n/index.ts +119 -0
  116. package/src/i18n/locales/en.json +313 -0
  117. package/src/i18n/locales/es.json +302 -0
  118. package/src/i18n/locales/fr.json +302 -0
  119. package/src/i18n/locales/id.json +302 -0
  120. package/src/i18n/locales/ja.json +302 -0
  121. package/src/i18n/locales/ko.json +302 -0
  122. package/src/i18n/locales/pt.json +302 -0
  123. package/src/i18n/locales/zh-Hans.json +302 -0
  124. package/src/index.ts +542 -0
  125. package/src/proxy/ProxyClient.ts +968 -0
  126. package/src/proxy/ProxySocketWrapper.ts +113 -0
  127. package/src/proxy/__tests__/ProxyClient.test.ts +575 -0
  128. package/src/proxy/__tests__/ProxySocketWrapper.test.ts +251 -0
  129. package/src/proxy/__tests__/handshake-validation.test.ts +367 -0
  130. package/src/proxy/chunking.ts +162 -0
  131. package/src/proxy/handshake-validation.ts +64 -0
  132. package/src/rpc/__tests__/router.test.ts +400 -0
  133. package/src/rpc/router.ts +183 -0
  134. package/src/services/BrowserProxyService.ts +179 -0
  135. package/src/services/FilesystemService.ts +841 -0
  136. package/src/services/GitService.ts +1639 -0
  137. package/src/services/SearchService.ts +809 -0
  138. package/src/services/TerminalService.ts +413 -0
  139. package/src/services/__tests__/BrowserProxyService.test.ts +155 -0
  140. package/src/services/__tests__/FilesystemService.test.ts +1002 -0
  141. package/src/services/__tests__/GitService.test.ts +1552 -0
  142. package/src/services/__tests__/SearchService.test.ts +484 -0
  143. package/src/services/__tests__/TerminalService.test.ts +702 -0
  144. package/src/setup/wizard.ts +242 -0
  145. package/src/types/fossil-delta.d.ts +4 -0
  146. package/src/types.ts +287 -0
  147. package/src/utils/__tests__/gitignore.test.ts +174 -0
  148. package/src/utils/gitignore.ts +91 -0
  149. package/src/utils/logger.ts +578 -0
  150. package/src/utils/project-dir.ts +218 -0
  151. package/src/utils/ripgrep.ts +180 -0
  152. package/src/utils/tool-detection.ts +141 -0
  153. package/src/watcher/FileWatcher.ts +53 -0
  154. package/tsconfig.json +24 -0
  155. package/vitest.config.ts +19 -0
@@ -0,0 +1,132 @@
1
+ /**
2
+ * JSON-RPC 2.0 request router
3
+ */
4
+ import { ErrorCode, createRPCError } from '../types.js';
5
+ import { FilesystemService } from '../services/FilesystemService.js';
6
+ import { GitService } from '../services/GitService.js';
7
+ import { TerminalService } from '../services/TerminalService.js';
8
+ import { SearchService } from '../services/SearchService.js';
9
+ import { BrowserProxyService } from '../services/BrowserProxyService.js';
10
+ export class RPCRouter {
11
+ /**
12
+ * Initialize services
13
+ */
14
+ static initialize(rootPath, config, tools) {
15
+ this.rootPath = rootPath;
16
+ this.tools = tools;
17
+ this.terminalEnabled = config.terminal?.enabled ?? true;
18
+ this.browserProxyEnabled = config.browserProxy?.enabled ?? true;
19
+ this.filesystemService = new FilesystemService(rootPath, config.filesystem);
20
+ this.gitService = new GitService(rootPath);
21
+ this.browserProxyService = new BrowserProxyService();
22
+ // Parse maxFileSize from config
23
+ const maxFileSizeBytes = this.parseFileSize(config.filesystem.maxFileSize);
24
+ this.searchService = new SearchService(rootPath, maxFileSizeBytes, 64 * 1024, tools.ripgrep);
25
+ }
26
+ /**
27
+ * Parse file size string to bytes
28
+ */
29
+ static parseFileSize(sizeStr) {
30
+ const match = sizeStr.match(/^(\d+(?:\.\d+)?)\s*(B|KB|MB|GB)?$/i);
31
+ if (!match) {
32
+ return 10 * 1024 * 1024; // Default 10MB
33
+ }
34
+ const value = parseFloat(match[1]);
35
+ const unit = (match[2] || 'B').toUpperCase();
36
+ const multipliers = {
37
+ B: 1,
38
+ KB: 1024,
39
+ MB: 1024 * 1024,
40
+ GB: 1024 * 1024 * 1024,
41
+ };
42
+ return value * multipliers[unit];
43
+ }
44
+ /**
45
+ * Get or create terminal service for socket
46
+ */
47
+ static getTerminalService(socket) {
48
+ const deviceId = socket.data.deviceId;
49
+ // Update current socket for this deviceId (handles reconnections)
50
+ this.currentSockets.set(deviceId, socket);
51
+ if (!this.terminalServices.has(deviceId)) {
52
+ // Create new service with getter function that returns current socket
53
+ const getSocket = () => {
54
+ const currentSocket = this.currentSockets.get(deviceId);
55
+ if (!currentSocket) {
56
+ throw new Error(`No active socket for device: ${deviceId}`);
57
+ }
58
+ return currentSocket;
59
+ };
60
+ this.terminalServices.set(deviceId, new TerminalService(getSocket, 10, 10000, this.rootPath));
61
+ }
62
+ return this.terminalServices.get(deviceId);
63
+ }
64
+ /**
65
+ * Route JSON-RPC request to appropriate service
66
+ */
67
+ static async route(message, socket) {
68
+ const { method, params } = message;
69
+ // Parse method prefix (split on first dot only so sub-namespaces like browser.proxy.request are preserved)
70
+ const dotIndex = method.indexOf('.');
71
+ const service = dotIndex !== -1 ? method.slice(0, dotIndex) : method;
72
+ const methodName = dotIndex !== -1 ? method.slice(dotIndex + 1) : '';
73
+ if (!service || !methodName) {
74
+ throw createRPCError(ErrorCode.INVALID_REQUEST, `Invalid method format: ${method}`);
75
+ }
76
+ try {
77
+ switch (service) {
78
+ case 'fs':
79
+ return await this.filesystemService.handle(methodName, params, socket);
80
+ case 'git':
81
+ if (!this.tools.git) {
82
+ throw createRPCError(ErrorCode.FEATURE_DISABLED, 'Git is not available. Install Git 2.20.0+ to use version control features.');
83
+ }
84
+ return await this.gitService.handle(methodName, params, socket);
85
+ case 'search':
86
+ // Search is always available, but fast search (ripgrep) may be disabled
87
+ return await this.searchService.handle(methodName, params, socket);
88
+ case 'terminal':
89
+ if (!this.terminalEnabled) {
90
+ throw createRPCError(ErrorCode.FEATURE_DISABLED, 'Terminal is disabled in configuration.');
91
+ }
92
+ const terminalService = this.getTerminalService(socket);
93
+ return await terminalService.handle(methodName, params);
94
+ case 'browser': {
95
+ if (!this.browserProxyEnabled) {
96
+ throw createRPCError(ErrorCode.FEATURE_DISABLED, 'Browser proxy is disabled in configuration.');
97
+ }
98
+ // methodName is 'proxy.request' — strip the 'proxy.' sub-namespace
99
+ const dotIdx = methodName.indexOf('.');
100
+ const browserMethod = dotIdx !== -1 ? methodName.slice(dotIdx + 1) : methodName;
101
+ return await this.browserProxyService.handle(browserMethod, params, socket);
102
+ }
103
+ default:
104
+ throw createRPCError(ErrorCode.METHOD_NOT_FOUND, `Unknown service: ${service}`);
105
+ }
106
+ }
107
+ catch (error) {
108
+ // Re-throw if already an RPC error
109
+ if (error.code && error.message) {
110
+ throw error;
111
+ }
112
+ // Wrap other errors
113
+ console.error(`Error in ${method}:`, error);
114
+ throw createRPCError(ErrorCode.INTERNAL_ERROR, `Internal error: ${error.message || 'Unknown error'}`, { method, originalError: error.toString() });
115
+ }
116
+ }
117
+ /**
118
+ * Cleanup terminal service for socket
119
+ */
120
+ static cleanupTerminalService(socket) {
121
+ const deviceId = socket.data.deviceId;
122
+ const service = this.terminalServices.get(deviceId);
123
+ if (service) {
124
+ service.cleanup();
125
+ this.terminalServices.delete(deviceId);
126
+ this.currentSockets.delete(deviceId);
127
+ }
128
+ }
129
+ }
130
+ RPCRouter.terminalServices = new Map();
131
+ RPCRouter.currentSockets = new Map();
132
+ //# sourceMappingURL=router.js.map
@@ -0,0 +1,13 @@
1
+ /**
2
+ * BrowserProxyService - Handles browser.proxy.request RPC calls
3
+ *
4
+ * Fetches from localhost:PORT/path and returns the HTTP response
5
+ * back to the editor client, which forwards it to the browser.spck.io SW.
6
+ */
7
+ import { AuthenticatedSocket } from '../types.js';
8
+ export declare class BrowserProxyService {
9
+ handle(method: string, params: any, socket: AuthenticatedSocket): Promise<any>;
10
+ private proxyRequest;
11
+ private fetch;
12
+ }
13
+ //# sourceMappingURL=BrowserProxyService.d.ts.map
@@ -0,0 +1,139 @@
1
+ /**
2
+ * BrowserProxyService - Handles browser.proxy.request RPC calls
3
+ *
4
+ * Fetches from localhost:PORT/path and returns the HTTP response
5
+ * back to the editor client, which forwards it to the browser.spck.io SW.
6
+ */
7
+ import http from 'http';
8
+ import https from 'https';
9
+ import { ErrorCode, createRPCError } from '../types.js';
10
+ import { logBrowserProxy } from '../utils/logger.js';
11
+ const REQUEST_TIMEOUT_MS = 30000;
12
+ const ALLOWED_HOSTS = new Set(['localhost', '127.0.0.1']);
13
+ const ALLOWED_METHODS = new Set(['GET', 'POST', 'PUT', 'PATCH', 'DELETE', 'HEAD', 'OPTIONS']);
14
+ const MAX_RESPONSE_BODY_BYTES = 10 * 1024 * 1024; // 10 MB
15
+ export class BrowserProxyService {
16
+ async handle(method, params, socket) {
17
+ switch (method) {
18
+ case 'request':
19
+ return await this.proxyRequest(params, socket.data.deviceId);
20
+ default:
21
+ throw createRPCError(ErrorCode.METHOD_NOT_FOUND, `Method not found: browser.proxy.${method}`);
22
+ }
23
+ }
24
+ async proxyRequest(params, uid) {
25
+ const { requestId, url, method, headers, body } = params;
26
+ // Validate method
27
+ const upperMethod = (method || 'GET').toUpperCase();
28
+ if (!ALLOWED_METHODS.has(upperMethod)) {
29
+ throw createRPCError(ErrorCode.INVALID_PARAMS, `Disallowed HTTP method: ${method}`);
30
+ }
31
+ // Validate URL — must be localhost only
32
+ let parsed;
33
+ try {
34
+ parsed = new URL(url);
35
+ }
36
+ catch {
37
+ throw createRPCError(ErrorCode.INVALID_PARAMS, `Invalid URL: ${url}`);
38
+ }
39
+ if (!ALLOWED_HOSTS.has(parsed.hostname)) {
40
+ throw createRPCError(ErrorCode.PERMISSION_DENIED, `Only localhost requests are allowed`);
41
+ }
42
+ const port = parseInt(parsed.port || '80', 10);
43
+ if (isNaN(port) || port < 1 || port > 65535) {
44
+ throw createRPCError(ErrorCode.INVALID_PARAMS, `Invalid port: ${parsed.port}`);
45
+ }
46
+ // Convert body array back to Buffer
47
+ const bodyBuffer = body && body.length > 0 ? Buffer.from(body) : undefined;
48
+ // Strip hop-by-hop headers that should not be forwarded
49
+ const forwardHeaders = {};
50
+ const hopByHop = new Set([
51
+ 'connection', 'keep-alive', 'transfer-encoding', 'te',
52
+ 'trailer', 'upgrade', 'proxy-authorization', 'proxy-authenticate'
53
+ ]);
54
+ for (const [key, value] of Object.entries(headers || {})) {
55
+ if (!hopByHop.has(key.toLowerCase())) {
56
+ forwardHeaders[key] = value;
57
+ }
58
+ }
59
+ if (bodyBuffer) {
60
+ forwardHeaders['content-length'] = String(bodyBuffer.length);
61
+ }
62
+ try {
63
+ const response = await this.fetch(parsed, upperMethod, forwardHeaders, bodyBuffer);
64
+ logBrowserProxy(params, uid, true, undefined, {
65
+ status: response.status,
66
+ size: Buffer.byteLength(response.body, 'base64') * 3 / 4 | 0,
67
+ });
68
+ return { requestId, ...response };
69
+ }
70
+ catch (error) {
71
+ logBrowserProxy(params, uid, false, error);
72
+ throw error;
73
+ }
74
+ }
75
+ fetch(url, method, headers, body) {
76
+ return new Promise((resolve, reject) => {
77
+ const isHttps = url.protocol === 'https:';
78
+ const lib = isHttps ? https : http;
79
+ const options = {
80
+ hostname: url.hostname,
81
+ port: url.port || (isHttps ? 443 : 80),
82
+ path: url.pathname + url.search,
83
+ method,
84
+ headers,
85
+ timeout: REQUEST_TIMEOUT_MS,
86
+ };
87
+ const req = lib.request(options, (res) => {
88
+ const chunks = [];
89
+ let totalLength = 0;
90
+ res.on('data', (chunk) => {
91
+ totalLength += chunk.length;
92
+ if (totalLength > MAX_RESPONSE_BODY_BYTES) {
93
+ req.destroy();
94
+ reject(createRPCError(ErrorCode.INTERNAL_ERROR, 'Response body too large'));
95
+ return;
96
+ }
97
+ chunks.push(chunk);
98
+ });
99
+ res.on('end', () => {
100
+ const bodyBuffer = Buffer.concat(chunks);
101
+ // Collect response headers (flatten multi-value headers)
102
+ const responseHeaders = {};
103
+ for (const [key, value] of Object.entries(res.headers)) {
104
+ if (value !== undefined) {
105
+ responseHeaders[key] = Array.isArray(value) ? value.join(', ') : value;
106
+ }
107
+ }
108
+ resolve({
109
+ status: res.statusCode || 200,
110
+ statusText: res.statusMessage || '',
111
+ headers: responseHeaders,
112
+ body: bodyBuffer.toString('base64'),
113
+ bodyEncoding: 'base64',
114
+ });
115
+ });
116
+ res.on('error', (err) => {
117
+ reject(createRPCError(ErrorCode.INTERNAL_ERROR, `Response error: ${err.message}`));
118
+ });
119
+ });
120
+ req.on('timeout', () => {
121
+ req.destroy();
122
+ reject(createRPCError(ErrorCode.OPERATION_TIMEOUT, 'Request to localhost timed out'));
123
+ });
124
+ req.on('error', (err) => {
125
+ if (err.code === 'ECONNREFUSED') {
126
+ reject(createRPCError(ErrorCode.INTERNAL_ERROR, `Connection refused on port ${err.port || ''} — is the dev server running?`));
127
+ }
128
+ else {
129
+ reject(createRPCError(ErrorCode.INTERNAL_ERROR, `Request failed: ${err.message}`));
130
+ }
131
+ });
132
+ if (body) {
133
+ req.write(body);
134
+ }
135
+ req.end();
136
+ });
137
+ }
138
+ }
139
+ //# sourceMappingURL=BrowserProxyService.js.map
@@ -0,0 +1,99 @@
1
+ /**
2
+ * Filesystem service - handles file operations with fossil-delta compression
3
+ */
4
+ import { AuthenticatedSocket } from '../types.js';
5
+ export declare class FilesystemService {
6
+ private rootPath;
7
+ private config;
8
+ private resolvedRootPath;
9
+ private realRootPath;
10
+ constructor(rootPath: string, config: any);
11
+ /**
12
+ * Get the real root path (following symlinks), cached after first call
13
+ */
14
+ private getRealRootPath;
15
+ /**
16
+ * Handle filesystem RPC methods
17
+ */
18
+ handle(method: string, params: any, socket: AuthenticatedSocket): Promise<any>;
19
+ /**
20
+ * Validate and sandbox path with symlink resolution
21
+ * Prevents directory traversal and symlink escape attacks
22
+ */
23
+ private validatePath;
24
+ /**
25
+ * Check if file/directory exists
26
+ */
27
+ private exists;
28
+ /**
29
+ * Check existence of multiple paths in parallel
30
+ */
31
+ private bulkExists;
32
+ /**
33
+ * Read file contents
34
+ */
35
+ private readFile;
36
+ /**
37
+ * Write file contents
38
+ */
39
+ private write;
40
+ /**
41
+ * Apply fossil-delta patch to file
42
+ */
43
+ private patchFile;
44
+ /**
45
+ * Get file hash
46
+ */
47
+ private getFileHash;
48
+ /**
49
+ * Get file hash value (internal)
50
+ */
51
+ private getFileHashValue;
52
+ /**
53
+ * Remove file or directory
54
+ * Returns success even if file doesn't exist (idempotent operation)
55
+ */
56
+ private remove;
57
+ /**
58
+ * Create directory
59
+ */
60
+ private mkdir;
61
+ /**
62
+ * Read directory contents
63
+ */
64
+ private readdir;
65
+ /**
66
+ * Read directory recursively using breadth-first (level-order) traversal
67
+ * Performance optimizations:
68
+ * - matchPattern: Regex to filter paths (applied to full relative path)
69
+ * - limit: Maximum number of results (files + folders combined)
70
+ *
71
+ * Breadth-first ensures top-level items are returned first, which is important
72
+ * when using the limit parameter to get a representative sample of the directory.
73
+ */
74
+ private readdirDeep;
75
+ /**
76
+ * Get file metadata
77
+ */
78
+ private lstat;
79
+ /**
80
+ * Move/rename file or directory
81
+ * Returns the type of the moved item ('file' or 'folder')
82
+ */
83
+ private mv;
84
+ /**
85
+ * Copy file or directory
86
+ * Returns the type of the copied item ('file' or 'folder')
87
+ */
88
+ private copy;
89
+ /**
90
+ * Copy directory recursively (helper method)
91
+ */
92
+ private copyDirectory;
93
+ /**
94
+ * Remove directory
95
+ * Returns success even if directory doesn't exist (idempotent operation)
96
+ */
97
+ private rmdir;
98
+ }
99
+ //# sourceMappingURL=FilesystemService.d.ts.map