@efffrida/vitest-pool 0.0.13 → 0.0.14
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/dist/frida/agent.js +14 -69
- package/dist/frida/agent.js.map +1 -1
- package/dist/src/index.d.ts +3 -6
- package/dist/src/index.d.ts.map +1 -1
- package/dist/src/index.js +94 -254
- package/dist/src/index.js.map +1 -1
- package/package.json +18 -14
- package/src/index.ts +158 -308
package/src/index.ts
CHANGED
|
@@ -1,18 +1,18 @@
|
|
|
1
|
-
import type * as
|
|
1
|
+
import type * as Context from "effect/Context";
|
|
2
2
|
|
|
3
|
+
import * as Array from "effect/Array";
|
|
3
4
|
import * as Cause from "effect/Cause";
|
|
4
5
|
import * as Deferred from "effect/Deferred";
|
|
5
6
|
import * as Duration from "effect/Duration";
|
|
6
7
|
import * as Effect from "effect/Effect";
|
|
7
8
|
import * as Exit from "effect/Exit";
|
|
8
9
|
import * as FileSystem from "effect/FileSystem";
|
|
10
|
+
import * as Function from "effect/Function";
|
|
9
11
|
import * as Layer from "effect/Layer";
|
|
10
|
-
import * as ManagedRuntime from "effect/ManagedRuntime";
|
|
11
12
|
import * as Match from "effect/Match";
|
|
12
13
|
import * as Path from "effect/Path";
|
|
13
14
|
import * as Schema from "effect/Schema";
|
|
14
15
|
import * as Scope from "effect/Scope";
|
|
15
|
-
import * as Sink from "effect/Sink";
|
|
16
16
|
import * as Stream from "effect/Stream";
|
|
17
17
|
import * as ChildProcess from "effect/unstable/process/ChildProcess";
|
|
18
18
|
|
|
@@ -22,7 +22,6 @@ import * as NodeServices from "@effect/platform-node/NodeServices";
|
|
|
22
22
|
import * as FridaDevice from "@efffrida/frida-tools/FridaDevice";
|
|
23
23
|
import * as FridaScript from "@efffrida/frida-tools/FridaScript";
|
|
24
24
|
import * as FridaSession from "@efffrida/frida-tools/FridaSession";
|
|
25
|
-
import * as Esbuild from "esbuild";
|
|
26
25
|
import * as Flatted from "flatted";
|
|
27
26
|
import * as Frida from "frida";
|
|
28
27
|
|
|
@@ -80,35 +79,17 @@ const ConfigSchema = Schema.Struct({
|
|
|
80
79
|
* @category Tests
|
|
81
80
|
*/
|
|
82
81
|
export class FridaPoolWorker implements VitestNode.PoolWorker {
|
|
83
|
-
readonly
|
|
84
|
-
|
|
82
|
+
public readonly name = "frida-pool";
|
|
83
|
+
private static initQueue: Promise<void> = Promise.resolve();
|
|
85
84
|
|
|
86
|
-
private readonly
|
|
85
|
+
private readonly scope: Scope.Closeable;
|
|
86
|
+
private readonly scriptContext: Promise<Context.Context<FridaScript.FridaScript>>;
|
|
87
|
+
private readonly cancelables: Map<(arg: any) => void, (interrupter?: number) => void> = new Map();
|
|
87
88
|
|
|
88
|
-
private
|
|
89
|
-
|
|
90
|
-
private modifiedAgentScope: Scope.Closeable;
|
|
91
|
-
private managedRuntime: ManagedRuntime.ManagedRuntime<FridaScript.FridaScript, unknown> | undefined;
|
|
92
|
-
private sends: Array<Promise<Exit.Exit<unknown, unknown>>> = [];
|
|
93
|
-
private compiledAgentUrlPromise: Promise<URL>;
|
|
94
|
-
private messageCallback: ((arg: any) => void) | undefined;
|
|
89
|
+
private sends: Array<Promise<unknown>> = [];
|
|
95
90
|
|
|
96
91
|
constructor(poolOptions: VitestNode.PoolOptions, customOptions: Schema.Schema.Type<typeof ConfigSchema>) {
|
|
97
|
-
|
|
98
|
-
this.cancelables = new Map();
|
|
99
|
-
this.managedRuntime = undefined;
|
|
100
|
-
this.modifiedAgentScope = Effect.runSync(Scope.make());
|
|
101
|
-
this.compiledAgentUrlPromise = compileTestFiles(this.agentTemplatePath, poolOptions).pipe(
|
|
102
|
-
Scope.provide(this.modifiedAgentScope),
|
|
103
|
-
Effect.provide(NodeServices.layer),
|
|
104
|
-
Effect.runPromise
|
|
105
|
-
);
|
|
106
|
-
}
|
|
107
|
-
|
|
108
|
-
async start(): Promise<void> {
|
|
109
|
-
const tempAgentUrl = await this.compiledAgentUrlPromise;
|
|
110
|
-
|
|
111
|
-
const FridaRuntime = Match.value(this.customOptions.runtime).pipe(
|
|
92
|
+
const FridaRuntime = Match.value(customOptions.runtime).pipe(
|
|
112
93
|
Match.when(undefined, () => undefined),
|
|
113
94
|
Match.when("v8", () => Frida.ScriptRuntime.V8),
|
|
114
95
|
Match.when("qjs", () => Frida.ScriptRuntime.QJS),
|
|
@@ -116,7 +97,7 @@ export class FridaPoolWorker implements VitestNode.PoolWorker {
|
|
|
116
97
|
Match.exhaustive
|
|
117
98
|
);
|
|
118
99
|
|
|
119
|
-
const FridaPlatform = Match.value(
|
|
100
|
+
const FridaPlatform = Match.value(customOptions.platform).pipe(
|
|
120
101
|
Match.when(undefined, () => undefined),
|
|
121
102
|
Match.when("gum", () => Frida.JsPlatform.Gum),
|
|
122
103
|
Match.when("browser", () => Frida.JsPlatform.Browser),
|
|
@@ -124,19 +105,19 @@ export class FridaPoolWorker implements VitestNode.PoolWorker {
|
|
|
124
105
|
Match.exhaustive
|
|
125
106
|
);
|
|
126
107
|
|
|
127
|
-
const DeviceLive = Match.value(
|
|
108
|
+
const DeviceLive = Match.value(customOptions.device).pipe(
|
|
128
109
|
Match.when({ connection: "local" }, () => FridaDevice.layerLocalDevice),
|
|
129
110
|
Match.when({ connection: "usb" }, ({ timeout }) =>
|
|
130
|
-
FridaDevice.layerUsbDevice({
|
|
131
|
-
timeout: timeout ? Duration.toMillis(timeout) : undefined,
|
|
132
|
-
} as Frida.GetDeviceOptions)
|
|
111
|
+
FridaDevice.layerUsbDevice(timeout !== undefined ? { timeout: Duration.toMillis(timeout) } : {})
|
|
133
112
|
),
|
|
134
113
|
Match.when({ connection: "remote" }, ({ address, keepaliveInterval, origin, token }) =>
|
|
135
114
|
FridaDevice.layerRemoteDevice(address, {
|
|
136
|
-
token,
|
|
137
|
-
origin,
|
|
138
|
-
|
|
139
|
-
|
|
115
|
+
...(token !== undefined ? { token } : {}),
|
|
116
|
+
...(origin !== undefined ? { origin } : {}),
|
|
117
|
+
...(keepaliveInterval !== undefined
|
|
118
|
+
? { keepaliveInterval: Duration.toMillis(keepaliveInterval) }
|
|
119
|
+
: {}),
|
|
120
|
+
})
|
|
140
121
|
),
|
|
141
122
|
Match.when(
|
|
142
123
|
{ connection: "android-emulator" },
|
|
@@ -151,11 +132,22 @@ export class FridaPoolWorker implements VitestNode.PoolWorker {
|
|
|
151
132
|
Match.exhaustive
|
|
152
133
|
);
|
|
153
134
|
|
|
154
|
-
const SessionLive = Match.value(
|
|
135
|
+
const SessionLive = Match.value(customOptions.attach).pipe(
|
|
155
136
|
Match.when({ pid: Match.number }, ({ pid }) => FridaSession.layer(pid)),
|
|
156
|
-
Match.when({ attachFrontmost: true }, ({ frontmostScope }) =>
|
|
157
|
-
FridaSession.layerFrontmost(
|
|
158
|
-
|
|
137
|
+
Match.when({ attachFrontmost: true }, ({ frontmostScope }) => {
|
|
138
|
+
return FridaSession.layerFrontmost(
|
|
139
|
+
frontmostScope !== undefined
|
|
140
|
+
? {
|
|
141
|
+
scope: Match.value(frontmostScope).pipe(
|
|
142
|
+
Match.when("minimal", () => Frida.Scope.Minimal),
|
|
143
|
+
Match.when("metadata", () => Frida.Scope.Metadata),
|
|
144
|
+
Match.when("full", () => Frida.Scope.Full),
|
|
145
|
+
Match.exhaustive
|
|
146
|
+
),
|
|
147
|
+
}
|
|
148
|
+
: {}
|
|
149
|
+
);
|
|
150
|
+
}),
|
|
159
151
|
Match.when({ preSpawn: true }, ({ spawn }) =>
|
|
160
152
|
Layer.unwrap(
|
|
161
153
|
Effect.gen(function* () {
|
|
@@ -168,98 +160,156 @@ export class FridaPoolWorker implements VitestNode.PoolWorker {
|
|
|
168
160
|
Match.orElse(({ spawn }) => FridaSession.layer(spawn))
|
|
169
161
|
);
|
|
170
162
|
|
|
171
|
-
const FridaLive = Layer.provide(SessionLive, DeviceLive)
|
|
172
|
-
const ScriptLive =
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
163
|
+
const FridaLive = Layer.provide(SessionLive, DeviceLive);
|
|
164
|
+
const ScriptLive = Effect.gen(function* () {
|
|
165
|
+
const path = yield* Path.Path;
|
|
166
|
+
const fs = yield* FileSystem.FileSystem;
|
|
167
|
+
|
|
168
|
+
const tempDir = path.join(poolOptions.project.config.root, "temp");
|
|
169
|
+
yield* fs.makeDirectory(tempDir, { recursive: true });
|
|
170
|
+
|
|
171
|
+
const agentUrl = yield* path.fromFileUrl(new URL("../frida/agent.ts", import.meta.url));
|
|
172
|
+
const baseAgent = yield* fs.readFileString(agentUrl);
|
|
173
|
+
const tempFile = yield* fs.makeTempFileScoped({
|
|
174
|
+
directory: tempDir,
|
|
175
|
+
prefix: ".vitest-frida-pool-agent-",
|
|
176
|
+
suffix: ".ts",
|
|
177
|
+
});
|
|
178
|
+
|
|
179
|
+
const setupFiles = poolOptions.project.config.setupFiles;
|
|
180
|
+
const testFiles = yield* Effect.map(
|
|
181
|
+
Effect.promise(() => poolOptions.project.globTestFiles()),
|
|
182
|
+
({ testFiles }) => testFiles
|
|
183
|
+
);
|
|
184
|
+
const globalSetupFiles = Array.isArray(poolOptions.project.config.globalSetup)
|
|
185
|
+
? poolOptions.project.config.globalSetup
|
|
186
|
+
: Array.make(poolOptions.project.config.globalSetup);
|
|
187
|
+
|
|
188
|
+
const marker = "// @efffrida/vitest-pool/agent/file-map";
|
|
189
|
+
const allFiles = Array.flatten([setupFiles, testFiles, globalSetupFiles]);
|
|
190
|
+
const newContent = Function.pipe(
|
|
191
|
+
allFiles,
|
|
192
|
+
Array.map(
|
|
193
|
+
(file) => `
|
|
194
|
+
if (_file === "${file}") {
|
|
195
|
+
// @ts-ignore
|
|
196
|
+
return await import("${file}")
|
|
197
|
+
}`
|
|
198
|
+
),
|
|
199
|
+
Array.join("\n")
|
|
200
|
+
);
|
|
201
|
+
|
|
202
|
+
yield* fs.writeFileString(tempFile, baseAgent.replace(marker, newContent));
|
|
203
|
+
return yield* path.toFileUrl(tempFile);
|
|
204
|
+
}).pipe(
|
|
205
|
+
Effect.map(
|
|
206
|
+
FridaScript.layer({
|
|
207
|
+
...(FridaRuntime !== undefined ? { runtime: FridaRuntime } : {}),
|
|
208
|
+
...(FridaPlatform !== undefined ? { platform: FridaPlatform } : {}),
|
|
209
|
+
})
|
|
210
|
+
),
|
|
211
|
+
Layer.unwrap,
|
|
212
|
+
Layer.provide(FridaLive),
|
|
213
|
+
Layer.provide(NodeServices.layer),
|
|
214
|
+
Layer.satisfiesSuccessType<FridaScript.FridaScript>()
|
|
215
|
+
);
|
|
177
216
|
|
|
178
|
-
this.
|
|
217
|
+
this.scope = Scope.makeUnsafe();
|
|
218
|
+
const prev = FridaPoolWorker.initQueue;
|
|
219
|
+
const runInit = () => ScriptLive.pipe(Layer.buildWithScope(this.scope), Effect.runPromise);
|
|
220
|
+
this.scriptContext = prev.then(runInit, runInit);
|
|
221
|
+
FridaPoolWorker.initQueue = this.scriptContext.then(
|
|
222
|
+
() => undefined,
|
|
223
|
+
() => undefined
|
|
224
|
+
);
|
|
225
|
+
}
|
|
179
226
|
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
const prettyError = Cause.prettyErrors(exit.cause);
|
|
183
|
-
throw prettyError[0];
|
|
227
|
+
async start(): Promise<void> {
|
|
228
|
+
await this.scriptContext;
|
|
184
229
|
}
|
|
185
230
|
|
|
186
231
|
async stop(): Promise<void> {
|
|
232
|
+
await Promise.allSettled(this.sends);
|
|
187
233
|
for (const cancelable of this.cancelables.values()) cancelable();
|
|
234
|
+
await Scope.close(this.scope, Exit.void).pipe(Effect.runPromise);
|
|
188
235
|
this.cancelables.clear();
|
|
189
|
-
await Promise.allSettled(this.sends);
|
|
190
|
-
await this.managedRuntime!.dispose();
|
|
191
|
-
await Effect.runPromise(Scope.close(this.modifiedAgentScope, Exit.void));
|
|
192
236
|
}
|
|
193
237
|
|
|
194
238
|
async send(message: VitestNode.WorkerRequest): Promise<void> {
|
|
195
|
-
const
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
return;
|
|
239
|
+
const context = await this.scriptContext;
|
|
240
|
+
let sendPromise: Promise<unknown> = undefined!;
|
|
241
|
+
|
|
242
|
+
try {
|
|
243
|
+
sendPromise = Effect.flatMap(FridaScript.FridaScript, (fridaScript) =>
|
|
244
|
+
fridaScript.callExport("onMessage")(message)
|
|
245
|
+
).pipe(Effect.runPromiseWith(context));
|
|
246
|
+
this.sends.push(sendPromise);
|
|
247
|
+
await sendPromise;
|
|
248
|
+
} finally {
|
|
249
|
+
this.sends = this.sends.filter((p) => p !== sendPromise);
|
|
207
250
|
}
|
|
208
|
-
const prettyError = Cause.prettyErrors(exit.cause);
|
|
209
|
-
throw prettyError[0];
|
|
210
251
|
}
|
|
211
252
|
|
|
212
253
|
on(event: string, callback: (arg: any) => void): void {
|
|
213
|
-
let cancelable!: (interrupter?: number) => void;
|
|
214
|
-
|
|
215
254
|
switch (event) {
|
|
216
255
|
case "message": {
|
|
217
|
-
this.
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
);
|
|
231
|
-
cancelable = this.managedRuntime!.runCallback(
|
|
232
|
-
Effect.flatMap(FridaScript.FridaScript, (fridaScript) => Stream.run(fridaScript.stream, sink))
|
|
233
|
-
);
|
|
256
|
+
this.scriptContext.then((ctx) => {
|
|
257
|
+
this.cancelables.set(
|
|
258
|
+
callback,
|
|
259
|
+
Effect.runCallbackWith(ctx)(
|
|
260
|
+
Effect.flatMap(FridaScript.FridaScript, (fridaScript) =>
|
|
261
|
+
Stream.runForEach(fridaScript.stream, (input) =>
|
|
262
|
+
Effect.sync(() => callback(input.message))
|
|
263
|
+
)
|
|
264
|
+
)
|
|
265
|
+
)
|
|
266
|
+
);
|
|
267
|
+
});
|
|
268
|
+
|
|
234
269
|
break;
|
|
235
270
|
}
|
|
236
271
|
|
|
237
272
|
case "error":
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
273
|
+
this.scriptContext.then((ctx) => {
|
|
274
|
+
this.cancelables.set(
|
|
275
|
+
callback,
|
|
276
|
+
Effect.runCallbackWith(ctx)(
|
|
277
|
+
Effect.flatMap(FridaScript.FridaScript, (fridaScript) =>
|
|
278
|
+
Deferred.await(fridaScript.scriptError)
|
|
279
|
+
),
|
|
280
|
+
{
|
|
281
|
+
onExit: (exit) => {
|
|
282
|
+
if (Exit.isSuccess(exit)) {
|
|
283
|
+
callback(exit.value);
|
|
284
|
+
} else if (!Cause.hasInterruptsOnly(exit.cause)) {
|
|
285
|
+
callback(exit.cause);
|
|
286
|
+
}
|
|
287
|
+
},
|
|
288
|
+
}
|
|
289
|
+
)
|
|
290
|
+
);
|
|
291
|
+
});
|
|
292
|
+
|
|
249
293
|
break;
|
|
250
294
|
|
|
251
295
|
case "exit":
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
296
|
+
this.scriptContext.then((ctx) => {
|
|
297
|
+
this.cancelables.set(
|
|
298
|
+
callback,
|
|
299
|
+
Effect.runCallbackWith(ctx)(
|
|
300
|
+
Effect.flatMap(FridaScript.FridaScript, (fridaScript) =>
|
|
301
|
+
Deferred.await(fridaScript.destroyed)
|
|
302
|
+
),
|
|
303
|
+
{ onExit: () => callback(void 0) }
|
|
304
|
+
)
|
|
305
|
+
);
|
|
306
|
+
});
|
|
307
|
+
|
|
256
308
|
break;
|
|
257
309
|
|
|
258
310
|
default:
|
|
259
311
|
throw new Error(`Event ${event} not supported in FridaPoolWorker`);
|
|
260
312
|
}
|
|
261
|
-
|
|
262
|
-
this.cancelables.set(callback, cancelable);
|
|
263
313
|
}
|
|
264
314
|
|
|
265
315
|
off(_event: string, callback: (arg: any) => void): void {
|
|
@@ -268,9 +318,6 @@ export class FridaPoolWorker implements VitestNode.PoolWorker {
|
|
|
268
318
|
this.cancelables.delete(callback);
|
|
269
319
|
cancelable();
|
|
270
320
|
}
|
|
271
|
-
if (this.messageCallback === callback) {
|
|
272
|
-
this.messageCallback = undefined;
|
|
273
|
-
}
|
|
274
321
|
}
|
|
275
322
|
|
|
276
323
|
deserialize(data: unknown) {
|
|
@@ -290,206 +337,9 @@ export class FridaPoolWorker implements VitestNode.PoolWorker {
|
|
|
290
337
|
export const createFridaPool = (
|
|
291
338
|
customOptions: Schema.Codec.Encoded<typeof ConfigSchema>
|
|
292
339
|
): VitestNode.PoolRunnerInitializer => {
|
|
293
|
-
const decoded = Schema.decodeUnknownSync(ConfigSchema)(customOptions);
|
|
294
340
|
return {
|
|
295
341
|
name: "frida-pool",
|
|
296
|
-
createPoolWorker: (options: VitestNode.PoolOptions) =>
|
|
342
|
+
createPoolWorker: (options: VitestNode.PoolOptions) =>
|
|
343
|
+
new FridaPoolWorker(options, Schema.decodeUnknownSync(ConfigSchema)(customOptions)),
|
|
297
344
|
};
|
|
298
345
|
};
|
|
299
|
-
|
|
300
|
-
/** @internal */
|
|
301
|
-
const vitestGlobalsPlugin: Esbuild.Plugin = {
|
|
302
|
-
name: "vitest-globals",
|
|
303
|
-
setup(build) {
|
|
304
|
-
// Handle vitest and @vitest/* imports
|
|
305
|
-
build.onResolve({ filter: /^(vitest|@vitest\/.*)$/ }, (args) => ({
|
|
306
|
-
path: args.path,
|
|
307
|
-
namespace: "vitest-globals",
|
|
308
|
-
}));
|
|
309
|
-
|
|
310
|
-
build.onLoad({ filter: /.*/, namespace: "vitest-globals" }, (args) => {
|
|
311
|
-
if (args.path === "vitest") {
|
|
312
|
-
return {
|
|
313
|
-
loader: "js",
|
|
314
|
-
contents: "export * from 'VITEST_GLOBAL';\nexport { default } from 'VITEST_GLOBAL';",
|
|
315
|
-
};
|
|
316
|
-
}
|
|
317
|
-
if (args.path === "@vitest/runner") {
|
|
318
|
-
return {
|
|
319
|
-
loader: "js",
|
|
320
|
-
contents: "export * from 'VITEST_RUNNER_GLOBAL';\nexport { default } from 'VITEST_RUNNER_GLOBAL';",
|
|
321
|
-
};
|
|
322
|
-
}
|
|
323
|
-
return {
|
|
324
|
-
loader: "js",
|
|
325
|
-
contents: "export * from 'VITEST_GLOBAL';\nexport { default } from 'VITEST_GLOBAL';",
|
|
326
|
-
};
|
|
327
|
-
});
|
|
328
|
-
|
|
329
|
-
build.onResolve({ filter: /^VITEST_(GLOBAL|RUNNER_GLOBAL)$/ }, (args) => ({
|
|
330
|
-
path: args.path,
|
|
331
|
-
namespace: "vitest-synthetic",
|
|
332
|
-
}));
|
|
333
|
-
|
|
334
|
-
build.onLoad({ filter: /.*/, namespace: "vitest-synthetic" }, (args) => {
|
|
335
|
-
const globalName = args.path === "VITEST_GLOBAL" ? "__vitest" : "__vitest_runner";
|
|
336
|
-
return {
|
|
337
|
-
loader: "js",
|
|
338
|
-
contents: `
|
|
339
|
-
const g = globalThis.${globalName};
|
|
340
|
-
export default g;
|
|
341
|
-
export const {
|
|
342
|
-
describe, it, test, expect, vi, beforeAll, afterAll,
|
|
343
|
-
beforeEach, afterEach, suite, bench, assert
|
|
344
|
-
} = g;
|
|
345
|
-
`,
|
|
346
|
-
};
|
|
347
|
-
});
|
|
348
|
-
|
|
349
|
-
// Handle Node.js built-in modules - redirect to globals set up by the agent
|
|
350
|
-
// The agent imports these from frida-compile's shims and exposes them as globals
|
|
351
|
-
const nodeModuleMap: Record<string, string> = {
|
|
352
|
-
"node:assert": "__node_assert",
|
|
353
|
-
"node:buffer": "__node_buffer",
|
|
354
|
-
"node:crypto": "__node_crypto",
|
|
355
|
-
"node:diagnostics_channel": "__node_diagnosticsChannel",
|
|
356
|
-
"node:events": "__node_events",
|
|
357
|
-
"node:fs": "__node_fs",
|
|
358
|
-
"node:net": "__node_net",
|
|
359
|
-
"node:os": "__node_os",
|
|
360
|
-
"node:path": "__node_path",
|
|
361
|
-
"node:process": "__node_process",
|
|
362
|
-
"node:stream": "__node_stream",
|
|
363
|
-
"node:timers": "__node_timers",
|
|
364
|
-
"node:tty": "__node_tty",
|
|
365
|
-
"node:url": "__node_url",
|
|
366
|
-
"node:util": "__node_util",
|
|
367
|
-
"node:vm": "__node_vm",
|
|
368
|
-
assert: "__node_assert",
|
|
369
|
-
buffer: "__node_buffer",
|
|
370
|
-
crypto: "__node_crypto",
|
|
371
|
-
diagnostics_channel: "__node_diagnosticsChannel",
|
|
372
|
-
events: "__node_events",
|
|
373
|
-
fs: "__node_fs",
|
|
374
|
-
net: "__node_net",
|
|
375
|
-
os: "__node_os",
|
|
376
|
-
path: "__node_path",
|
|
377
|
-
process: "__node_process",
|
|
378
|
-
stream: "__node_stream",
|
|
379
|
-
timers: "__node_timers",
|
|
380
|
-
tty: "__node_tty",
|
|
381
|
-
url: "__node_url",
|
|
382
|
-
util: "__node_util",
|
|
383
|
-
vm: "__node_vm",
|
|
384
|
-
};
|
|
385
|
-
|
|
386
|
-
build.onResolve(
|
|
387
|
-
{
|
|
388
|
-
filter: /^(node:)?(assert|buffer|crypto|diagnostics_channel|events|fs|net|os|path|process|stream|timers|tty|url|util|vm)$/,
|
|
389
|
-
},
|
|
390
|
-
(args) => ({
|
|
391
|
-
path: args.path,
|
|
392
|
-
namespace: "node-globals",
|
|
393
|
-
})
|
|
394
|
-
);
|
|
395
|
-
|
|
396
|
-
build.onLoad({ filter: /.*/, namespace: "node-globals" }, (args) => {
|
|
397
|
-
const globalName = nodeModuleMap[args.path];
|
|
398
|
-
if (!globalName) {
|
|
399
|
-
return { loader: "js", contents: "export default {};" };
|
|
400
|
-
}
|
|
401
|
-
return {
|
|
402
|
-
loader: "js",
|
|
403
|
-
contents: `
|
|
404
|
-
const m = globalThis.${globalName};
|
|
405
|
-
export default m;
|
|
406
|
-
export const { Buffer } = m.Buffer ? m : { Buffer: m.default?.Buffer };
|
|
407
|
-
export * from 'NODE_MODULE_REEXPORT_${globalName}';
|
|
408
|
-
`,
|
|
409
|
-
};
|
|
410
|
-
});
|
|
411
|
-
|
|
412
|
-
// Handle re-exports from node modules
|
|
413
|
-
build.onResolve({ filter: /^NODE_MODULE_REEXPORT_/ }, (args) => ({
|
|
414
|
-
path: args.path,
|
|
415
|
-
namespace: "node-reexport",
|
|
416
|
-
}));
|
|
417
|
-
|
|
418
|
-
build.onLoad({ filter: /.*/, namespace: "node-reexport" }, (args) => {
|
|
419
|
-
const globalName = args.path.replace("NODE_MODULE_REEXPORT_", "");
|
|
420
|
-
return {
|
|
421
|
-
loader: "js",
|
|
422
|
-
contents: `
|
|
423
|
-
const m = globalThis.${globalName};
|
|
424
|
-
const mod = m.default || m;
|
|
425
|
-
for (const key in mod) {
|
|
426
|
-
if (key !== 'default') {
|
|
427
|
-
Object.defineProperty(exports, key, {
|
|
428
|
-
enumerable: true,
|
|
429
|
-
get: () => mod[key]
|
|
430
|
-
});
|
|
431
|
-
}
|
|
432
|
-
}
|
|
433
|
-
`,
|
|
434
|
-
};
|
|
435
|
-
});
|
|
436
|
-
},
|
|
437
|
-
};
|
|
438
|
-
|
|
439
|
-
/** @internal */
|
|
440
|
-
const compileTestFiles = Effect.fnUntraced(function* (
|
|
441
|
-
agentTemplatePath: URL,
|
|
442
|
-
poolOptions: VitestNode.PoolOptions,
|
|
443
|
-
customOptions?: Schema.Schema.Type<typeof ConfigSchema> | undefined
|
|
444
|
-
) {
|
|
445
|
-
const path = yield* Path.Path;
|
|
446
|
-
const fs = yield* FileSystem.FileSystem;
|
|
447
|
-
const url = yield* path.fromFileUrl(agentTemplatePath);
|
|
448
|
-
|
|
449
|
-
const testFilesList: Array<string> = (poolOptions.project as any).testFilesList ?? [];
|
|
450
|
-
const setupFiles: Array<string> = poolOptions.project.config.setupFiles ?? [];
|
|
451
|
-
const allFiles = [...new Set([...setupFiles, ...testFilesList])];
|
|
452
|
-
const testFilesMap: Record<string, string> = {};
|
|
453
|
-
|
|
454
|
-
const esbuildPlatform = Match.value(customOptions?.platform).pipe(
|
|
455
|
-
Match.when("browser", () => "browser" as const),
|
|
456
|
-
Match.when("neutral", () => "neutral" as const),
|
|
457
|
-
Match.whenOr("gum", undefined, () => "node" as const),
|
|
458
|
-
Match.orElseAbsurd
|
|
459
|
-
);
|
|
460
|
-
|
|
461
|
-
for (const testFile of allFiles) {
|
|
462
|
-
const result = yield* Effect.promise(() =>
|
|
463
|
-
Esbuild.build({
|
|
464
|
-
bundle: true,
|
|
465
|
-
write: false,
|
|
466
|
-
format: "esm",
|
|
467
|
-
platform: esbuildPlatform,
|
|
468
|
-
target: "es2020",
|
|
469
|
-
entryPoints: [testFile],
|
|
470
|
-
plugins: [vitestGlobalsPlugin],
|
|
471
|
-
})
|
|
472
|
-
);
|
|
473
|
-
|
|
474
|
-
if (!result.outputFiles || result.outputFiles.length === 0) {
|
|
475
|
-
return yield* Effect.die(new Error(`esbuild produced no output for ${testFile}`));
|
|
476
|
-
} else {
|
|
477
|
-
testFilesMap[testFile] = Buffer.from(result.outputFiles[0].text).toString("base64");
|
|
478
|
-
}
|
|
479
|
-
}
|
|
480
|
-
|
|
481
|
-
const agentTemplateContents = yield* fs.readFileString(url);
|
|
482
|
-
const modifiedAgentContents = agentTemplateContents.replace(
|
|
483
|
-
/^const testFiles: Record<string, string> = \{\};$/m,
|
|
484
|
-
`const testFiles: Record<string, string> = ${JSON.stringify(testFilesMap, null, 4)};`
|
|
485
|
-
);
|
|
486
|
-
|
|
487
|
-
const tempAgentPath = yield* fs.makeTempFileScoped({
|
|
488
|
-
suffix: ".ts",
|
|
489
|
-
prefix: ".agent-",
|
|
490
|
-
directory: path.dirname(url),
|
|
491
|
-
});
|
|
492
|
-
|
|
493
|
-
yield* fs.writeFileString(tempAgentPath, modifiedAgentContents);
|
|
494
|
-
return yield* path.toFileUrl(tempAgentPath);
|
|
495
|
-
});
|