From da8cd870b0afac4e84504fc30c4d94643019943a Mon Sep 17 00:00:00 2001 From: Matt Carey Date: Wed, 8 Jul 2026 13:08:38 +0100 Subject: [PATCH] fix(sync): pull every path by default --- docs/02_sync_protocol.md | 8 +++----- packages/dofs/src/sync/ignore.ts | 2 +- packages/rpc/src/sync-driver.test.ts | 23 ++++++++++++++++++++--- packages/rpc/src/sync-driver.ts | 4 +++- 4 files changed, 27 insertions(+), 10 deletions(-) diff --git a/docs/02_sync_protocol.md b/docs/02_sync_protocol.md index d5699e09..46c9f936 100644 --- a/docs/02_sync_protocol.md +++ b/docs/02_sync_protocol.md @@ -407,7 +407,6 @@ first-class conflict primitives. ## Ignore lists - The `ignore` option hides path segments from the pull. Excluded paths are still written and read inside the container — the bytes just never cross the wire back to the DO. This is essential for any large @@ -416,10 +415,9 @@ directory of derived files: `node_modules`, `.next`, `target`, push tens of thousands of small files through the sync wire on the next pull. -The default is `["node_modules"]`, applied server-side when `ignore` is -omitted. A caller-supplied list **replaces** the default — it does not -extend it. Pass `[]` to disable ignoring entirely, or pass your full -list (including `"node_modules"` if you still want it) to customise. +No paths are ignored by default. A sync server can configure an ignore list, +and an individual `fetchChanges` call can replace it. Pass `[]` to override a +server-level list and request every path. ### Ignored entries diff --git a/packages/dofs/src/sync/ignore.ts b/packages/dofs/src/sync/ignore.ts index 91f7b841..47dd489c 100644 --- a/packages/dofs/src/sync/ignore.ts +++ b/packages/dofs/src/sync/ignore.ts @@ -8,7 +8,7 @@ // node_modules_old or my_node_modules. Patterns are plain strings, // not globs; we can extend to globs later if a real case demands it. -export const DEFAULT_IGNORE = ["node_modules"]; +export const DEFAULT_IGNORE: string[] = []; export function isIgnored(path: string, patterns: string[]): boolean { if (patterns.length === 0) return false; diff --git a/packages/rpc/src/sync-driver.test.ts b/packages/rpc/src/sync-driver.test.ts index ad78667b..278e6832 100644 --- a/packages/rpc/src/sync-driver.test.ts +++ b/packages/rpc/src/sync-driver.test.ts @@ -88,15 +88,31 @@ function trackFetchDisposal( } describe("sync driver — pullOnce", () => { - it("pulls a single entry from upstream", async () => { + it("pulls every path by default, including node_modules", async () => { const a = makePeer(); const b = makePeer(); try { const provider = new SQLiteWorkspaceProvider(a.db, { now: () => 1 }); provider.writeFileSync("/hello.txt", "hello"); + provider.mkdirSync("/node_modules/pkg", { recursive: true }); + provider.writeFileSync("/node_modules/pkg/index.js", "dependency"); - const applied = await pullOnce(b.db, a.rpc); - expect(applied.applied).toBe(1); + let requestedIgnore: string[] | undefined; + const remote = new Proxy(a.rpc as object, { + get(target, prop, receiver) { + if (prop === "fetchChanges") { + return (input: Parameters[0]) => { + requestedIgnore = input.ignore; + return Reflect.get(target, prop, receiver).call(target, input); + }; + } + return Reflect.get(target, prop, receiver); + }, + }) as SyncRPC; + + const applied = await pullOnce(b.db, remote); + expect(requestedIgnore).toEqual([]); + expect(applied.applied).toBeGreaterThan(1); expect(applied.skipped).toEqual([]); expect(fileEntries(b.db)).toContain("hello.txt"); // Asserting the bytes arrived, not just the dirent. The @@ -105,6 +121,7 @@ describe("sync driver — pullOnce", () => { // empty on the receiver — RPC reads landed HTTP 200 / 0 bytes. const providerB = new SQLiteWorkspaceProvider(b.db, { now: () => 1 }); expect(providerB.readFileSync("/hello.txt", "utf8")).toBe("hello"); + expect(providerB.readFileSync("/node_modules/pkg/index.js", "utf8")).toBe("dependency"); } finally { a.close(); b.close(); diff --git a/packages/rpc/src/sync-driver.ts b/packages/rpc/src/sync-driver.ts index 75028ba3..1cf7176c 100644 --- a/packages/rpc/src/sync-driver.ts +++ b/packages/rpc/src/sync-driver.ts @@ -103,7 +103,9 @@ async function pullOnceImpl( // invariant trip, and any throw inside the batch loop. Disposing the // envelope tears down the contained stream stub, releasing the // remote iterator. - const fetchResult = await remote.fetchChanges({ after }); + // Pull the complete remote tree. Sending [] explicitly also overrides the + // historical wsd default that filtered node_modules when ignore was omitted. + const fetchResult = await remote.fetchChanges({ after, ignore: [] }); try { const { currentCursor, appliedPushCursor } = fetchResult; // Cross-side watermark divergence. Two shapes are recoverable: