@relayfile/local-mount 0.7.23 → 0.7.25

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/README.md CHANGED
@@ -21,6 +21,8 @@ const handle = await createMount(projectDir, mountDir, options);
21
21
 
22
22
  interface MountHandle {
23
23
  mountDir: string;
24
+ initialFileCount?: number;
25
+ initialMountDurationMs?: number;
24
26
  syncBack(opts?: { signal?: AbortSignal; paths?: Iterable<string> }): Promise<number>;
25
27
  startAutoSync(opts?: AutoSyncOptions): AutoSyncHandle;
26
28
  cleanup(): void;
@@ -28,6 +30,7 @@ interface MountHandle {
28
30
  ```
29
31
 
30
32
  `createMount` returns `Promise<MountHandle>`. The walker yields the event loop between directory entries so consumer-side timers (e.g. an `ora` spinner driven by `setInterval`) keep firing while the mount is being built.
33
+ The returned handle includes initial mount timing and copied-file count metadata so callers can report setup performance.
31
34
 
32
35
  Behavior:
33
36
  - Copies regular files into the mount, requesting a filesystem reflink clone when the source and mount are on a compatible same-volume filesystem and falling back to a byte copy otherwise
package/dist/auto-sync.js CHANGED
@@ -1,4 +1,4 @@
1
- import { chmodSync, copyFileSync, existsSync, lstatSync, mkdirSync, readdirSync, readFileSync, realpathSync, rmSync, statSync, } from 'node:fs';
1
+ import { chmodSync, constants as fsConstants, copyFileSync, existsSync, lstatSync, mkdirSync, readdirSync, readFileSync, realpathSync, rmSync, statSync, } from 'node:fs';
2
2
  import path from 'node:path';
3
3
  import watcher from '@parcel/watcher';
4
4
  const STOP_EVENT_SETTLE_MS = 250;
@@ -504,7 +504,7 @@ function doMountToProject(relPosix, state, ctx, mountAbs, projectAbs) {
504
504
  updateState(state, relPosix, mountAbs, target);
505
505
  return false;
506
506
  }
507
- copyFileSync(mountAbs, target);
507
+ copyFileSync(mountAbs, target, fsConstants.COPYFILE_FICLONE);
508
508
  updateState(state, relPosix, mountAbs, target);
509
509
  return true;
510
510
  }
@@ -526,7 +526,7 @@ function doProjectToMount(relPosix, state, ctx, projectAbs, mountAbs, readonly)
526
526
  }
527
527
  catch { /* best effort */ }
528
528
  }
529
- copyFileSync(projectAbs, target);
529
+ copyFileSync(projectAbs, target, fsConstants.COPYFILE_FICLONE);
530
530
  if (readonly) {
531
531
  try {
532
532
  chmodSync(target, 0o444);
package/dist/mount.d.ts CHANGED
@@ -30,6 +30,8 @@ export interface MountOptions {
30
30
  }
31
31
  export interface MountHandle {
32
32
  mountDir: string;
33
+ initialFileCount?: number;
34
+ initialMountDurationMs?: number;
33
35
  syncBack(opts?: {
34
36
  signal?: AbortSignal;
35
37
  paths?: Iterable<string>;
package/dist/mount.js CHANGED
@@ -60,7 +60,9 @@ export async function createMount(projectDir, mountDir, options) {
60
60
  mkdirSync(resolvedMountDir, { recursive: true });
61
61
  const realMountDir = realpathSync(resolvedMountDir);
62
62
  writeFileSync(path.join(realMountDir, MOUNT_MARKER_FILENAME), MOUNT_MARKER_CONTENT, 'utf8');
63
- await walkProjectTree(resolvedProjectDir, resolvedProjectDir, realMountDir, realMountDir, excludeRules, readonlyMatcher, ignoredMatcher);
63
+ const initialMountStartedAt = Date.now();
64
+ const initialFileCount = await walkProjectTree(resolvedProjectDir, resolvedProjectDir, realMountDir, realMountDir, excludeRules, readonlyMatcher, ignoredMatcher);
65
+ const initialMountDurationMs = Date.now() - initialMountStartedAt;
64
66
  const readmePath = resolveSafeCopyTarget(realMountDir, path.join(realMountDir, MOUNT_README_FILENAME));
65
67
  if (!readmePath) {
66
68
  throw new Error('Failed to create mount readme inside mountDir');
@@ -79,6 +81,8 @@ export async function createMount(projectDir, mountDir, options) {
79
81
  };
80
82
  return {
81
83
  mountDir: resolvedMountDir,
84
+ initialFileCount,
85
+ initialMountDurationMs,
82
86
  async syncBack(opts) {
83
87
  let synced = 0;
84
88
  const realProjectDir = realpathSync(resolvedProjectDir);
@@ -160,6 +164,7 @@ async function walkProjectTree(projectDir, currentDir, mountDir, currentMountDir
160
164
  await yieldToEventLoop();
161
165
  const entries = readdirSync(currentDir, { withFileTypes: true });
162
166
  let processed = 0;
167
+ let copiedFiles = 0;
163
168
  for (const entry of entries) {
164
169
  if (processed > 0 && processed % WALK_YIELD_EVERY === 0) {
165
170
  await yieldToEventLoop();
@@ -185,18 +190,23 @@ async function walkProjectTree(projectDir, currentDir, mountDir, currentMountDir
185
190
  if (!safeMountDir) {
186
191
  continue;
187
192
  }
188
- await walkProjectTree(projectDir, absolutePath, mountDir, safeMountDir, excludeRules, readonlyMatcher, ignoredMatcher);
193
+ copiedFiles += await walkProjectTree(projectDir, absolutePath, mountDir, safeMountDir, excludeRules, readonlyMatcher, ignoredMatcher);
189
194
  continue;
190
195
  }
191
196
  if (entry.isSymbolicLink()) {
192
- copySymlinkedFile(projectDir, mountDir, absolutePath, mountPath, relativePath, readonlyMatcher);
197
+ if (copySymlinkedFile(projectDir, mountDir, absolutePath, mountPath, relativePath, readonlyMatcher)) {
198
+ copiedFiles += 1;
199
+ }
193
200
  continue;
194
201
  }
195
202
  if (!entry.isFile()) {
196
203
  continue;
197
204
  }
198
- copyMountedFile(projectDir, mountDir, absolutePath, mountPath, relativePath, readonlyMatcher);
205
+ if (copyMountedFile(projectDir, mountDir, absolutePath, mountPath, relativePath, readonlyMatcher)) {
206
+ copiedFiles += 1;
207
+ }
199
208
  }
209
+ return copiedFiles;
200
210
  }
201
211
  function yieldToEventLoop() {
202
212
  return new Promise((resolve) => setImmediate(resolve));
@@ -209,29 +219,30 @@ function copySymlinkedFile(projectDir, mountDir, sourcePath, mountPath, relative
209
219
  resolvedStat = statSync(sourcePath);
210
220
  }
211
221
  catch {
212
- return;
222
+ return false;
213
223
  }
214
224
  if (!isPathWithinRoot(realSource, projectDir) || !resolvedStat.isFile()) {
215
- return;
225
+ return false;
216
226
  }
217
- copyMountedFile(projectDir, mountDir, realSource, mountPath, relativePath, readonlyMatcher, resolvedStat.mode);
227
+ return copyMountedFile(projectDir, mountDir, realSource, mountPath, relativePath, readonlyMatcher, resolvedStat.mode);
218
228
  }
219
229
  function copyMountedFile(sourceRoot, mountDir, sourcePath, mountPath, relativePath, readonlyMatcher, sourceMode) {
220
230
  const safeMountPath = resolveSafeCopyTarget(mountDir, mountPath);
221
231
  if (!safeMountPath) {
222
- return;
232
+ return false;
223
233
  }
224
234
  const safeSourcePath = resolveVerifiedFilePath(sourceRoot, sourcePath);
225
235
  if (!safeSourcePath) {
226
- return;
236
+ return false;
227
237
  }
228
238
  copyFileSync(safeSourcePath, safeMountPath, fsConstants.COPYFILE_FICLONE);
229
239
  if (isPathMatched(relativePath, readonlyMatcher)) {
230
240
  chmodSync(safeMountPath, 0o444);
231
- return;
241
+ return true;
232
242
  }
233
243
  const mode = sourceMode ?? statSync(safeSourcePath).mode;
234
244
  chmodSync(safeMountPath, mode & 0o777);
245
+ return true;
235
246
  }
236
247
  function ensureDirectory(pathValue) {
237
248
  mkdirSync(pathValue, { recursive: true });
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@relayfile/local-mount",
3
- "version": "0.7.23",
3
+ "version": "0.7.25",
4
4
  "description": "Create a symlink/copy mount of a project directory with .agentignore/.agentreadonly semantics, then launch a CLI inside it and sync writable changes back on exit",
5
5
  "main": "dist/index.js",
6
6
  "types": "dist/index.d.ts",