Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ import {
hasUsableWindowsSnapshotIdentity,
NodeCompatibleFileSystemAdapter,
readNodeFileSnapshotWithinLimit,
resolveNoFollowFlag,
} from "./node-filesystem-adapter.ts";
import { setupNodeFsWatcher } from "./shared-watcher.ts";

Expand Down Expand Up @@ -39,7 +40,40 @@ function requireExclusiveCreator(adapter: NodeCompatibleFileSystemAdapter) {
return adapter.createFileBytesExclusive;
}

describe("resolveNoFollowFlag", () => {
it("does not throw when node:fs constants are unavailable (#3661)", () => {
// In a browser bundle `nodeFsConstants` is `undefined`. Reading `.O_NOFOLLOW`
// off it must degrade to "unavailable", not throw at construction. Passing
// `undefined` for `constants` reproduces the non-Node runtime directly.
assertEquals(resolveNoFollowFlag({}, undefined), undefined);
});

it("returns the runtime O_NOFOLLOW when the constants are present", () => {
assertEquals(resolveNoFollowFlag({}, { O_NOFOLLOW: 0x20000 }), 0x20000);
});

it("lets an own noFollow option win over the runtime constants (test seam)", () => {
assertEquals(resolveNoFollowFlag({ noFollow: 7 }, { O_NOFOLLOW: 0x20000 }), 7);
// An own `undefined` means "unavailable" even when constants exist.
assertEquals(resolveNoFollowFlag({ noFollow: undefined }, { O_NOFOLLOW: 0x20000 }), undefined);
});
});

describe("NodeCompatibleFileSystemAdapter", () => {
it("constructs without exact-snapshot support when node:fs constants are absent (#3661)", () => {
// Reproduce the browser path end to end: no runtime O_NOFOLLOW available, so
// the adapter must construct (not throw) and simply cannot bind an exact
// inode snapshot. `noFollow: undefined` is the documented "unavailable" seam.
const adapter = new NodeCompatibleFileSystemAdapter(undefined, {
noFollow: undefined,
platform: "posix",
});
assertEquals(
(adapter as { readFileSnapshotWithinLimit?: unknown }).readFileSnapshotWithinLimit,
undefined,
);
});

it("reads empty and exact-limit snapshots and rejects invalid or oversized inputs", async () => {
const root = await Deno.makeTempDir({ prefix: "veryfront-node-snapshot-" });
try {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -396,6 +396,25 @@ async function createNodeFileBytesExclusive(
);
}

/**
* Resolve the `O_NOFOLLOW` open flag that binds an exact-inode snapshot read.
*
* `constants` is `undefined` in a runtime without `node:fs` — e.g. a browser
* bundle that transitively imports this adapter. Dereferencing `.O_NOFOLLOW` on
* it unconditionally threw `Cannot read properties of undefined (reading
* 'O_NOFOLLOW')` during construction and aborted client hydration (#3661).
* Treat the missing constants as "unavailable" (`undefined`) — the same meaning
* {@link NodeFileSystemCapabilityOptions.noFollow} already documents for an own
* `undefined` — so `canOpenExactSnapshot` degrades to `false` instead of the
* constructor exploding. An own `noFollow` on `options` still wins (the test seam).
*/
export function resolveNoFollowFlag(
options: NodeFileSystemCapabilityOptions,
constants: { readonly O_NOFOLLOW?: number } | undefined,
): number | undefined {
return hasOwn(options, "noFollow") ? options.noFollow : constants?.O_NOFOLLOW;
}

/**
* Filesystem implementation shared by runtimes that provide Node-compatible
* `node:fs` APIs (currently Node.js and Bun).
Expand All @@ -409,7 +428,7 @@ export class NodeCompatibleFileSystemAdapter implements FileSystemAdapter {
...nodeFileSystemOperations,
...options.operations,
} as NodeFileSystemOperations;
const noFollow = hasOwn(options, "noFollow") ? options.noFollow : nodeFsConstants.O_NOFOLLOW;
const noFollow = resolveNoFollowFlag(options, nodeFsConstants);
const platform = options.platform ?? (runtimeUsesWindowsPaths() ? "windows" : "posix");
const canOpenExactSnapshot = platform === "windows"
? hasUsableWindowsSnapshotIdentity(detectNodeCompatibleRuntime())
Expand Down