1import * as Alchemy from "@/index.ts";
2import * as Cloudflare from "@/Cloudflare";
3import * as Test from "@/Test/Alchemy";
4import { expect } from "alchemy-test";
5import * as Effect from "effect/Effect";
6import * as HttpClient from "effect/http/HttpClient";
7import ZlibProbeWorker from "./fixtures/zlib-probe-worker.ts";
8
9const state = Alchemy.inMemoryState();
10
11const { test, beforeAll, afterAll, deploy, destroy } = Test.make({
12 providers: Cloudflare.providers(),
13 state,
14 dev: true,
15});
16
17const Stack = Alchemy.Stack(
18 "ZlibProbeStack",
19 { providers: Cloudflare.providers(), state },
20 Effect.gen(function* () {
21 const worker = yield* ZlibProbeWorker;
22 return { url: worker.url.as<string>() };
23 }),
24);
25
26const stack = beforeAll(deploy(Stack));
27afterAll(destroy(Stack));
28
29test(
30 "workerd node:zlib keeps the synchronous exact-span inflate the pack parser relies on",
31 Effect.gen(function* () {
32 const { url } = yield* stack;
33 const client = yield* HttpClient.HttpClient;
34 const res = yield* client.get(`${url}/?path=probe`);
35 for (const path of [
36 "processChunk",
37 "processChunkReuse",
38 "info",
39 "plain",
40 "stream",
41 "sha1",
42 "copy",
43 "jsloop",
44 "effect",
45 "deflate6",
46 "deflate1",
47 ]) {
48 const n = path.startsWith("deflate") ? 200 : 2000;
49 yield* client
50 .get(`${url}/?path=${path}&n=50`)
51 .pipe(Effect.flatMap((r) => r.json));
52 const t0 = performance.now();
53 const r = (yield* client
54 .get(`${url}/?path=${path}&n=${n}`)
55 .pipe(Effect.flatMap((r) => r.json))) as { ok: number; error?: string };
56 const ms = performance.now() - t0;
57 console.log(
58 `[zlib-local] ${path.padEnd(12)} ok ${r.ok}/${n} in ${ms.toFixed(0)}ms → ${(ms / n).toFixed(3)} ms/entry${r.error ? ` error: ${r.error}` : ""}`,
59 );
60 }
61 const body = (yield* res.json) as {
62 hasProcessChunk: boolean;
63 info?: { outLen: number; bytesWritten: number; expectedConsumed: number };
64 contentLen: number;
65 };
66 console.log("[zlib-probe]", JSON.stringify(body));
67 expect(res.status).toBe(200);
68 // PackParser's per-entry inflate takes the synchronous `_processChunk`
69 // fast path (Zlib.ts); a workerd without it falls back to a streaming
70 // inflater measured ~50x slower per object. Pin it.
71 expect(body.hasProcessChunk).toBe(true);
72 expect(body.info?.outLen).toBe(body.contentLen);
73 expect(body.info?.bytesWritten).toBe(body.info?.expectedConsumed);
74 }),
75 {
76 tags: ["provider:cloudflare", "provider:cloudflare:worker", "local"],
77 timeout: 60_000,
78 },
79);