1// @effect-diagnostics nodeBuiltinImport:off
2import * as NodeFS from "node:fs";
3import * as NodeServices from "@effect/platform-node/NodeServices";
4import { it } from "@effect/vitest";
5import * as FileSystem from "effect/FileSystem";
6import * as Schema from "effect/Schema";
7import * as Effect from "effect/Effect";
8import { vi } from "vite-plus/test";
9import * as HostProcess from "@t3tools/shared/HostProcess";
10
11import { readBootstrapEnvelope } from "./bootstrap.ts";
12import { assertNone, assertSome } from "@effect/vitest/utils";
13
14const openSyncInterceptor = vi.hoisted(() => ({
15 failPath: null as string | null,
16 errorCode: "ENXIO",
17}));
18const fstatSyncInterceptor = vi.hoisted(() => ({ failFd: null as number | null }));
19
20vi.mock("node:fs", async (importOriginal) => {
21 const actual = await importOriginal<typeof import("node:fs")>();
22 return {
23 ...actual,
24 openSync: (...args: Parameters<typeof actual.openSync>) => {
25 const [filePath, flags] = args;
26 if (
27 typeof filePath === "string" &&
28 filePath === openSyncInterceptor.failPath &&
29 flags === "r"
30 ) {
31 const error = new Error(`open failed with ${openSyncInterceptor.errorCode}`);
32 Object.assign(error, { code: openSyncInterceptor.errorCode });
33 throw error;
34 }
35 return (actual.openSync as (...a: typeof args) => number)(...args);
36 },
37 fstatSync: (...args: Parameters<typeof actual.fstatSync>) => {
38 if (args[0] === fstatSyncInterceptor.failFd) {
39 const error = new Error("permission denied");
40 Object.assign(error, { code: "EACCES" });
41 throw error;
42 }
43 return (actual.fstatSync as (...a: typeof args) => NodeFS.Stats)(...args);
44 },
45 };
46});
47
48const windowsHost = HostProcess.Platform.defaultValue() === "win32";
49const nullDevice = windowsHost ? "\\\\.\\NUL" : "/dev/null";
50const closeIfOpen = (fd: number) => {
51 try {
52 NodeFS.closeSync(fd);
53 } catch (error) {
54 if ((error as NodeJS.ErrnoException).code !== "EBADF") throw error;
55 }
56};
57
58// Windows auto-closes the inherited fd; POSIX reopens it, leaving the original for the test to close.
59const openBootstrapInputFd = (filePath: string) =>
60 Effect.acquireRelease(
61 Effect.sync(() => NodeFS.openSync(filePath, "r")),
62 (fd) => (windowsHost ? Effect.void : Effect.sync(() => closeIfOpen(fd))),
63 );
64
65const TestEnvelopeSchema = Schema.Struct({ mode: Schema.String });
66const encodeTestEnvelopeSchema = Schema.encodeEffect(Schema.fromJsonString(TestEnvelopeSchema));
67
68it.layer(NodeServices.layer)("readBootstrapEnvelope", (it) => {
69 it.effect("reads a bootstrap envelope from a provided fd", () =>
70 Effect.gen(function* () {
71 const fs = yield* FileSystem.FileSystem;
72 const filePath = yield* fs.makeTempFileScoped({ prefix: "t3-bootstrap-", suffix: ".ndjson" });
73
74 yield* fs.writeFileString(
75 filePath,
76 `${yield* encodeTestEnvelopeSchema({ mode: "desktop" })}\n`,
77 );
78
79 const fd = yield* openBootstrapInputFd(filePath);
80
81 const payload = yield* readBootstrapEnvelope(TestEnvelopeSchema, fd, { timeoutMs: 100 });
82 assertSome(payload, {
83 mode: "desktop",
84 });
85 }),
86 );
87
88 it.effect("falls back to reading the inherited fd when path duplication fails", () =>
89 Effect.gen(function* () {
90 const fs = yield* FileSystem.FileSystem;
91 const filePath = yield* fs.makeTempFileScoped({ prefix: "t3-bootstrap-", suffix: ".ndjson" });
92
93 yield* fs.writeFileString(
94 filePath,
95 `${yield* encodeTestEnvelopeSchema({ mode: "desktop" })}\n`,
96 );
97
98 // The fallback stream auto-closes asynchronously; a finalizer would race and cause EBADF.
99 const fd = NodeFS.openSync(filePath, "r");
100
101 openSyncInterceptor.failPath = `/proc/self/fd/${fd}`;
102 try {
103 const payload = yield* readBootstrapEnvelope(TestEnvelopeSchema, fd, {
104 timeoutMs: 100,
105 }).pipe(Effect.provideService(HostProcess.Platform, "linux"));
106 assertSome(payload, {
107 mode: "desktop",
108 });
109 } finally {
110 openSyncInterceptor.failPath = null;
111 }
112 }),
113 );
114
115 it.effect("returns none when the fd is unavailable", () =>
116 Effect.gen(function* () {
117 const fd = NodeFS.openSync(nullDevice, "r");
118 NodeFS.closeSync(fd);
119
120 const payload = yield* readBootstrapEnvelope(TestEnvelopeSchema, fd, { timeoutMs: 100 });
121 assertNone(payload);
122 }),
123 );
124});