@oh-my-pi/pi-coding-agent 5.7.67 → 5.7.68

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.
Files changed (52) hide show
  1. package/CHANGELOG.md +9 -0
  2. package/package.json +8 -7
  3. package/src/migrations.ts +1 -34
  4. package/src/vendor/photon/index.js +4 -2
  5. package/src/vendor/photon/photon_rs_bg.wasm.b64.js +1 -0
  6. package/src/core/python-executor-display.test.ts +0 -42
  7. package/src/core/python-executor-lifecycle.test.ts +0 -99
  8. package/src/core/python-executor-mapping.test.ts +0 -41
  9. package/src/core/python-executor-per-call.test.ts +0 -49
  10. package/src/core/python-executor-session.test.ts +0 -103
  11. package/src/core/python-executor-streaming.test.ts +0 -77
  12. package/src/core/python-executor-timeout.test.ts +0 -35
  13. package/src/core/python-executor.lifecycle.test.ts +0 -139
  14. package/src/core/python-executor.result.test.ts +0 -49
  15. package/src/core/python-executor.test.ts +0 -180
  16. package/src/core/python-kernel-display.test.ts +0 -54
  17. package/src/core/python-kernel-env.test.ts +0 -138
  18. package/src/core/python-kernel-session.test.ts +0 -87
  19. package/src/core/python-kernel-ws.test.ts +0 -104
  20. package/src/core/python-kernel.lifecycle.test.ts +0 -249
  21. package/src/core/python-kernel.test.ts +0 -461
  22. package/src/core/python-modules.test.ts +0 -102
  23. package/src/core/python-prelude.test.ts +0 -140
  24. package/src/core/settings-manager-python.test.ts +0 -23
  25. package/src/core/streaming-output.test.ts +0 -26
  26. package/src/core/system-prompt.python.test.ts +0 -17
  27. package/src/core/tools/index.test.ts +0 -212
  28. package/src/core/tools/python-execution.test.ts +0 -68
  29. package/src/core/tools/python-fallback.test.ts +0 -72
  30. package/src/core/tools/python-renderer.test.ts +0 -36
  31. package/src/core/tools/python-tool-mode.test.ts +0 -43
  32. package/src/core/tools/python.test.ts +0 -121
  33. package/src/core/tools/schema-validation.test.ts +0 -530
  34. package/src/core/tools/web-scrapers/academic.test.ts +0 -239
  35. package/src/core/tools/web-scrapers/business.test.ts +0 -82
  36. package/src/core/tools/web-scrapers/dev-platforms.test.ts +0 -254
  37. package/src/core/tools/web-scrapers/documentation.test.ts +0 -85
  38. package/src/core/tools/web-scrapers/finance-media.test.ts +0 -144
  39. package/src/core/tools/web-scrapers/git-hosting.test.ts +0 -272
  40. package/src/core/tools/web-scrapers/media.test.ts +0 -138
  41. package/src/core/tools/web-scrapers/package-managers-2.test.ts +0 -199
  42. package/src/core/tools/web-scrapers/package-managers.test.ts +0 -171
  43. package/src/core/tools/web-scrapers/package-registries.test.ts +0 -259
  44. package/src/core/tools/web-scrapers/research.test.ts +0 -107
  45. package/src/core/tools/web-scrapers/security.test.ts +0 -103
  46. package/src/core/tools/web-scrapers/social-extended.test.ts +0 -192
  47. package/src/core/tools/web-scrapers/social.test.ts +0 -259
  48. package/src/core/tools/web-scrapers/stackexchange.test.ts +0 -120
  49. package/src/core/tools/web-scrapers/standards.test.ts +0 -122
  50. package/src/core/tools/web-scrapers/wikipedia.test.ts +0 -73
  51. package/src/core/tools/web-scrapers/youtube.test.ts +0 -198
  52. package/src/discovery/helpers.test.ts +0 -131
@@ -1,180 +0,0 @@
1
- import { afterEach, describe, expect, it, vi } from "bun:test";
2
- import { rmSync } from "node:fs";
3
- import {
4
- disposeAllKernelSessions,
5
- executePythonWithKernel,
6
- getPreludeDocs,
7
- type PythonKernelExecutor,
8
- resetPreludeDocsCache,
9
- warmPythonEnvironment,
10
- } from "./python-executor";
11
- import { type KernelExecuteOptions, type KernelExecuteResult, type PreludeHelper, PythonKernel } from "./python-kernel";
12
- import { DEFAULT_MAX_BYTES } from "./tools/truncate";
13
-
14
- class FakeKernel implements PythonKernelExecutor {
15
- private result: KernelExecuteResult;
16
- private onExecute: (options?: KernelExecuteOptions) => void;
17
-
18
- constructor(result: KernelExecuteResult, onExecute: (options?: KernelExecuteOptions) => void) {
19
- this.result = result;
20
- this.onExecute = onExecute;
21
- }
22
-
23
- async execute(_code: string, options?: KernelExecuteOptions): Promise<KernelExecuteResult> {
24
- this.onExecute(options);
25
- return this.result;
26
- }
27
- }
28
-
29
- describe("executePythonWithKernel", () => {
30
- it("captures text and display outputs", async () => {
31
- const kernel = new FakeKernel(
32
- { status: "ok", cancelled: false, timedOut: false, stdinRequested: false },
33
- (options) => {
34
- options?.onChunk?.("hello\n");
35
- options?.onDisplay?.({ type: "json", data: { foo: "bar" } });
36
- },
37
- );
38
-
39
- const result = await executePythonWithKernel(kernel, "print('hello')");
40
-
41
- expect(result.exitCode).toBe(0);
42
- expect(result.output).toContain("hello");
43
- expect(result.displayOutputs).toHaveLength(1);
44
- });
45
-
46
- it("marks stdin request as error", async () => {
47
- const kernel = new FakeKernel(
48
- { status: "ok", cancelled: false, timedOut: false, stdinRequested: true },
49
- () => {},
50
- );
51
-
52
- const result = await executePythonWithKernel(kernel, "input('prompt')");
53
-
54
- expect(result.exitCode).toBe(1);
55
- expect(result.stdinRequested).toBe(true);
56
- expect(result.output).toContain("Kernel requested stdin; interactive input is not supported.");
57
- });
58
-
59
- it("maps error status to exit code 1", async () => {
60
- const kernel = new FakeKernel(
61
- { status: "error", cancelled: false, timedOut: false, stdinRequested: false },
62
- (options) => {
63
- options?.onChunk?.("Traceback\n");
64
- },
65
- );
66
-
67
- const result = await executePythonWithKernel(kernel, "raise ValueError('nope')");
68
-
69
- expect(result.exitCode).toBe(1);
70
- expect(result.cancelled).toBe(false);
71
- expect(result.output).toContain("Traceback");
72
- });
73
-
74
- it("sanitizes streamed chunks", async () => {
75
- const kernel = new FakeKernel(
76
- { status: "ok", cancelled: false, timedOut: false, stdinRequested: false },
77
- (options) => {
78
- options?.onChunk?.("\u001b[31mred\r\n");
79
- },
80
- );
81
-
82
- const result = await executePythonWithKernel(kernel, "print('red')");
83
-
84
- expect(result.output).toBe("red\n");
85
- });
86
-
87
- it("returns cancelled result with timeout annotation", async () => {
88
- const kernel = new FakeKernel(
89
- { status: "ok", cancelled: true, timedOut: true, stdinRequested: false },
90
- (options) => {
91
- options?.onChunk?.("partial output\n");
92
- },
93
- );
94
-
95
- const result = await executePythonWithKernel(kernel, "while True: pass", { timeout: 4100 });
96
-
97
- expect(result.exitCode).toBeUndefined();
98
- expect(result.cancelled).toBe(true);
99
- expect(result.output).toContain("Command timed out after 4 seconds");
100
- });
101
-
102
- it("returns cancelled result without timeout annotation", async () => {
103
- const kernel = new FakeKernel(
104
- { status: "ok", cancelled: true, timedOut: false, stdinRequested: false },
105
- (options) => {
106
- options?.onChunk?.("cancelled output\n");
107
- },
108
- );
109
-
110
- const result = await executePythonWithKernel(kernel, "while True: pass");
111
-
112
- expect(result.exitCode).toBeUndefined();
113
- expect(result.cancelled).toBe(true);
114
- expect(result.output).toContain("cancelled output");
115
- expect(result.output).not.toContain("Command timed out");
116
- });
117
-
118
- it("truncates large output and stores full output file", async () => {
119
- const largeOutput = `${"x".repeat(DEFAULT_MAX_BYTES + 1024)}TAIL`;
120
- const kernel = new FakeKernel(
121
- { status: "ok", cancelled: false, timedOut: false, stdinRequested: false },
122
- (options) => {
123
- options?.onChunk?.(largeOutput);
124
- },
125
- );
126
-
127
- const result = await executePythonWithKernel(kernel, "print('big')");
128
-
129
- expect(result.truncated).toBe(true);
130
- expect(result.fullOutputPath).toBeDefined();
131
- expect(result.output).toContain("TAIL");
132
-
133
- const fullText = await Bun.file(result.fullOutputPath as string).text();
134
- expect(fullText).toBe(largeOutput);
135
-
136
- rmSync(result.fullOutputPath as string, { force: true });
137
- });
138
- });
139
-
140
- afterEach(async () => {
141
- await disposeAllKernelSessions();
142
- resetPreludeDocsCache();
143
- vi.restoreAllMocks();
144
- });
145
-
146
- describe("warmPythonEnvironment", () => {
147
- it("caches prelude docs on warmup", async () => {
148
- const previousSkip = process.env.OMP_PYTHON_SKIP_CHECK;
149
- process.env.OMP_PYTHON_SKIP_CHECK = "1";
150
- const docs: PreludeHelper[] = [
151
- {
152
- name: "read",
153
- signature: "(path)",
154
- docstring: "Read file contents.",
155
- category: "File I/O",
156
- },
157
- ];
158
- const kernel = {
159
- introspectPrelude: vi.fn().mockResolvedValue(docs),
160
- ping: vi.fn().mockResolvedValue(true),
161
- isAlive: () => true,
162
- shutdown: vi.fn().mockResolvedValue(undefined),
163
- };
164
- const startSpy = vi.spyOn(PythonKernel, "start").mockResolvedValue(kernel as unknown as PythonKernel);
165
-
166
- const result = await warmPythonEnvironment("/tmp/test", "session-1");
167
-
168
- expect(result.ok).toBe(true);
169
- expect(result.docs).toEqual(docs);
170
- expect(getPreludeDocs()).toEqual(docs);
171
- expect(kernel.introspectPrelude).toHaveBeenCalledTimes(1);
172
-
173
- startSpy.mockRestore();
174
- if (previousSkip === undefined) {
175
- delete process.env.OMP_PYTHON_SKIP_CHECK;
176
- } else {
177
- process.env.OMP_PYTHON_SKIP_CHECK = previousSkip;
178
- }
179
- });
180
- });
@@ -1,54 +0,0 @@
1
- import { describe, expect, it } from "bun:test";
2
- import { type KernelDisplayOutput, PythonKernel } from "./python-kernel";
3
-
4
- const renderDisplay = (
5
- PythonKernel as unknown as {
6
- prototype: {
7
- renderDisplay: (content: Record<string, unknown>) => {
8
- text: string;
9
- outputs: KernelDisplayOutput[];
10
- };
11
- };
12
- }
13
- ).prototype.renderDisplay;
14
-
15
- describe("PythonKernel display rendering", () => {
16
- it("normalizes text/plain output and returns no display outputs", () => {
17
- const { text, outputs } = renderDisplay.call({} as PythonKernel, {
18
- data: { "text/plain": "hello" },
19
- });
20
-
21
- expect(text).toBe("hello\n");
22
- expect(outputs).toHaveLength(0);
23
- });
24
-
25
- it("collects image and json display outputs without text", () => {
26
- const { text, outputs } = renderDisplay.call({} as PythonKernel, {
27
- data: { "image/png": "abc", "application/json": { foo: "bar" } },
28
- });
29
-
30
- expect(text).toBe("");
31
- expect(outputs).toEqual([
32
- { type: "image", data: "abc", mimeType: "image/png" },
33
- { type: "json", data: { foo: "bar" } },
34
- ]);
35
- });
36
-
37
- it("converts text/html to markdown", () => {
38
- const { text, outputs } = renderDisplay.call({} as PythonKernel, {
39
- data: { "text/html": "<p><strong>Hello</strong></p>" },
40
- });
41
-
42
- expect(outputs).toHaveLength(0);
43
- expect(text).toBe("**Hello**\n");
44
- });
45
-
46
- it("combines text/plain with json output", () => {
47
- const { text, outputs } = renderDisplay.call({} as PythonKernel, {
48
- data: { "text/plain": "value", "application/json": { ok: true } },
49
- });
50
-
51
- expect(text).toBe("value\n");
52
- expect(outputs).toEqual([{ type: "json", data: { ok: true } }]);
53
- });
54
- });
@@ -1,138 +0,0 @@
1
- import { afterEach, beforeEach, describe, expect, it, vi } from "bun:test";
2
- import * as shell from "../utils/shell";
3
- import * as shellSnapshot from "../utils/shell-snapshot";
4
- import { PythonKernel } from "./python-kernel";
5
- import { PYTHON_PRELUDE } from "./python-prelude";
6
-
7
- class FakeWebSocket {
8
- static OPEN = 1;
9
- static CLOSED = 3;
10
- readyState = FakeWebSocket.OPEN;
11
- binaryType = "arraybuffer";
12
- url: string;
13
- onopen?: () => void;
14
- onerror?: (event: unknown) => void;
15
- onclose?: () => void;
16
- onmessage?: (event: { data: ArrayBuffer }) => void;
17
-
18
- constructor(url: string) {
19
- this.url = url;
20
- queueMicrotask(() => {
21
- this.onopen?.();
22
- });
23
- }
24
-
25
- send(_data: ArrayBuffer) {}
26
-
27
- close() {
28
- this.readyState = FakeWebSocket.CLOSED;
29
- this.onclose?.();
30
- }
31
- }
32
-
33
- describe("PythonKernel.start (local gateway)", () => {
34
- const originalEnv = { ...process.env };
35
- const originalFetch = globalThis.fetch;
36
- const originalWebSocket = globalThis.WebSocket;
37
-
38
- beforeEach(() => {
39
- process.env.BUN_ENV = "test";
40
- delete process.env.OMP_PYTHON_GATEWAY_URL;
41
- delete process.env.OMP_PYTHON_GATEWAY_TOKEN;
42
- globalThis.WebSocket = FakeWebSocket as unknown as typeof WebSocket;
43
- });
44
-
45
- afterEach(() => {
46
- for (const key of Object.keys(process.env)) {
47
- if (!(key in originalEnv)) {
48
- delete process.env[key];
49
- }
50
- }
51
- for (const [key, value] of Object.entries(originalEnv)) {
52
- process.env[key] = value;
53
- }
54
- globalThis.fetch = originalFetch;
55
- globalThis.WebSocket = originalWebSocket;
56
- vi.restoreAllMocks();
57
- });
58
-
59
- it("filters environment variables before spawning gateway", async () => {
60
- const fetchSpy = vi.fn(async (input: string | URL, init?: RequestInit) => {
61
- const url = typeof input === "string" ? input : input.toString();
62
- if (url.endsWith("/api/kernelspecs")) {
63
- return new Response(JSON.stringify({}), { status: 200 });
64
- }
65
- if (url.endsWith("/api/kernels") && init?.method === "POST") {
66
- return new Response(JSON.stringify({ id: "kernel-1" }), { status: 201 });
67
- }
68
- return new Response("", { status: 200 });
69
- });
70
- globalThis.fetch = fetchSpy as unknown as typeof fetch;
71
-
72
- const shellSpy = vi.spyOn(shell, "getShellConfig").mockResolvedValue({
73
- shell: "/bin/bash",
74
- args: ["-lc"],
75
- env: {
76
- PATH: "/bin",
77
- HOME: "/home/test",
78
- OPENAI_API_KEY: "secret",
79
- UNSAFE_TOKEN: "nope",
80
- OMP_CUSTOM: "1",
81
- LC_ALL: "en_US.UTF-8",
82
- },
83
- prefix: undefined,
84
- });
85
- const snapshotSpy = vi.spyOn(shellSnapshot, "getOrCreateSnapshot").mockResolvedValue(null);
86
- const whichSpy = vi.spyOn(Bun, "which").mockReturnValue("/usr/bin/python");
87
-
88
- let spawnEnv: Record<string, string | undefined> | undefined;
89
- let spawnArgs: string[] | undefined;
90
- const spawnSpy = vi.spyOn(Bun, "spawn").mockImplementation(((...args: unknown[]) => {
91
- const [cmd, options] = args as [string[] | { cmd: string[] }, { env?: Record<string, string | undefined> }?];
92
- spawnArgs = Array.isArray(cmd) ? cmd : cmd.cmd;
93
- spawnEnv = options?.env;
94
- return { pid: 1234, exited: Promise.resolve(0) } as unknown as Bun.Subprocess;
95
- }) as unknown as typeof Bun.spawn);
96
-
97
- const executeSpy = vi
98
- .spyOn(PythonKernel.prototype, "execute")
99
- .mockResolvedValue({ status: "ok", cancelled: false, timedOut: false, stdinRequested: false });
100
-
101
- const kernel = await PythonKernel.start({ cwd: "/tmp/project", env: { CUSTOM_VAR: "ok" } });
102
-
103
- const createCall = fetchSpy.mock.calls.find(([input, init]) => {
104
- const url = typeof input === "string" ? input : input.toString();
105
- return url.endsWith("/api/kernels") && init?.method === "POST";
106
- });
107
- expect(createCall).toBeDefined();
108
- if (createCall) {
109
- expect(JSON.parse(String(createCall[1]?.body ?? "{}"))).toEqual({ name: "python3" });
110
- }
111
-
112
- expect(spawnArgs).toContain("kernel_gateway");
113
- expect(spawnEnv?.PATH).toBe("/bin");
114
- expect(spawnEnv?.HOME).toBe("/home/test");
115
- expect(spawnEnv?.OMP_CUSTOM).toBe("1");
116
- expect(spawnEnv?.LC_ALL).toBe("en_US.UTF-8");
117
- expect(spawnEnv?.CUSTOM_VAR).toBe("ok");
118
- expect(spawnEnv?.OPENAI_API_KEY).toBeUndefined();
119
- expect(spawnEnv?.UNSAFE_TOKEN).toBeUndefined();
120
- expect(spawnEnv?.PYTHONPATH).toBe("/tmp/project");
121
-
122
- expect(executeSpy).toHaveBeenCalledWith(
123
- PYTHON_PRELUDE,
124
- expect.objectContaining({
125
- silent: true,
126
- storeHistory: false,
127
- }),
128
- );
129
-
130
- await kernel.shutdown();
131
-
132
- shellSpy.mockRestore();
133
- snapshotSpy.mockRestore();
134
- whichSpy.mockRestore();
135
- spawnSpy.mockRestore();
136
- executeSpy.mockRestore();
137
- });
138
- });
@@ -1,87 +0,0 @@
1
- import { afterEach, beforeEach, describe, expect, it } from "bun:test";
2
- import { disposeAllKernelSessions, executePython } from "./python-executor";
3
- import type { KernelExecuteOptions, KernelExecuteResult } from "./python-kernel";
4
- import { PythonKernel } from "./python-kernel";
5
-
6
- class FakeKernel {
7
- executeCalls = 0;
8
- shutdownCalls = 0;
9
- alive = true;
10
- readonly id: string;
11
-
12
- constructor(id: string) {
13
- this.id = id;
14
- }
15
-
16
- async execute(_code: string, options?: KernelExecuteOptions): Promise<KernelExecuteResult> {
17
- this.executeCalls += 1;
18
- options?.onChunk?.("ok\n");
19
- return { status: "ok", cancelled: false, timedOut: false, stdinRequested: false };
20
- }
21
-
22
- async shutdown(): Promise<void> {
23
- this.shutdownCalls += 1;
24
- this.alive = false;
25
- }
26
-
27
- isAlive(): boolean {
28
- return this.alive;
29
- }
30
-
31
- async ping(): Promise<boolean> {
32
- return this.alive;
33
- }
34
- }
35
-
36
- describe("executePython kernel reuse", () => {
37
- const originalStart = PythonKernel.start;
38
- let startCalls = 0;
39
- let kernels: FakeKernel[] = [];
40
-
41
- beforeEach(() => {
42
- process.env.OMP_PYTHON_SKIP_CHECK = "1";
43
- startCalls = 0;
44
- kernels = [];
45
- PythonKernel.start = (async () => {
46
- startCalls += 1;
47
- const kernel = new FakeKernel(`kernel-${startCalls}`);
48
- kernels.push(kernel);
49
- return kernel as unknown as PythonKernel;
50
- }) as typeof PythonKernel.start;
51
- });
52
-
53
- afterEach(async () => {
54
- PythonKernel.start = originalStart;
55
- await disposeAllKernelSessions();
56
- });
57
-
58
- it("reuses kernels for session mode", async () => {
59
- await executePython("print('one')", { cwd: "/tmp", sessionId: "session-a", kernelMode: "session" });
60
- await executePython("print('two')", { cwd: "/tmp", sessionId: "session-a", kernelMode: "session" });
61
-
62
- expect(startCalls).toBe(1);
63
- expect(kernels[0]?.executeCalls).toBe(2);
64
- });
65
-
66
- it("creates and disposes per-call kernels", async () => {
67
- await executePython("print('one')", { cwd: "/tmp", kernelMode: "per-call" });
68
- await executePython("print('two')", { cwd: "/tmp", kernelMode: "per-call" });
69
-
70
- expect(startCalls).toBe(2);
71
- expect(kernels[0]?.shutdownCalls).toBe(1);
72
- expect(kernels[1]?.shutdownCalls).toBe(1);
73
- });
74
-
75
- it("resets the session kernel when requested", async () => {
76
- await executePython("print('one')", { cwd: "/tmp", sessionId: "session-b", kernelMode: "session" });
77
- await executePython("print('two')", {
78
- cwd: "/tmp",
79
- sessionId: "session-b",
80
- kernelMode: "session",
81
- reset: true,
82
- });
83
-
84
- expect(startCalls).toBe(2);
85
- expect(kernels[0]?.shutdownCalls).toBe(1);
86
- });
87
- });
@@ -1,104 +0,0 @@
1
- import { describe, expect, it } from "bun:test";
2
- import { deserializeWebSocketMessage, type JupyterMessage, serializeWebSocketMessage } from "./python-kernel";
3
-
4
- const encoder = new TextEncoder();
5
-
6
- function buildFrame(message: Omit<JupyterMessage, "buffers">, buffers: Uint8Array[] = []): ArrayBuffer {
7
- const msgBytes = encoder.encode(JSON.stringify(message));
8
- const offsetCount = 1 + buffers.length;
9
- const headerSize = 4 + offsetCount * 4;
10
-
11
- let totalSize = headerSize + msgBytes.length;
12
- for (const buffer of buffers) {
13
- totalSize += buffer.length;
14
- }
15
-
16
- const frame = new ArrayBuffer(totalSize);
17
- const view = new DataView(frame);
18
- const bytes = new Uint8Array(frame);
19
-
20
- view.setUint32(0, offsetCount, true);
21
- view.setUint32(4, headerSize, true);
22
- bytes.set(msgBytes, headerSize);
23
-
24
- let offset = headerSize + msgBytes.length;
25
- for (let i = 0; i < buffers.length; i++) {
26
- view.setUint32(4 + (i + 1) * 4, offset, true);
27
- bytes.set(buffers[i], offset);
28
- offset += buffers[i].length;
29
- }
30
-
31
- return frame;
32
- }
33
-
34
- describe("deserializeWebSocketMessage", () => {
35
- it("parses offset tables and buffers", () => {
36
- const message = {
37
- channel: "iopub",
38
- header: {
39
- msg_id: "msg-1",
40
- session: "session-1",
41
- username: "omp",
42
- date: "2024-01-01T00:00:00Z",
43
- msg_type: "stream",
44
- version: "5.5",
45
- },
46
- parent_header: {},
47
- metadata: {},
48
- content: { text: "hello" },
49
- };
50
- const buffer = new Uint8Array([1, 2, 3]);
51
- const frame = buildFrame(message, [buffer]);
52
-
53
- const parsed = deserializeWebSocketMessage(frame);
54
-
55
- expect(parsed).not.toBeNull();
56
- expect(parsed?.header.msg_id).toBe("msg-1");
57
- expect(parsed?.content).toEqual({ text: "hello" });
58
- expect(parsed?.buffers?.[0]).toEqual(buffer);
59
- });
60
-
61
- it("returns null for invalid frames", () => {
62
- const headerSize = 8;
63
- const bytes = encoder.encode("not-json");
64
- const frame = new ArrayBuffer(headerSize + bytes.length);
65
- const view = new DataView(frame);
66
- const data = new Uint8Array(frame);
67
- view.setUint32(0, 1, true);
68
- view.setUint32(4, headerSize, true);
69
- data.set(bytes, headerSize);
70
-
71
- expect(deserializeWebSocketMessage(frame)).toBeNull();
72
- const emptyFrame = new ArrayBuffer(4);
73
- new DataView(emptyFrame).setUint32(0, 0, true);
74
- expect(deserializeWebSocketMessage(emptyFrame)).toBeNull();
75
- });
76
- });
77
-
78
- describe("serializeWebSocketMessage", () => {
79
- it("round trips message payloads", () => {
80
- const message: JupyterMessage = {
81
- channel: "shell",
82
- header: {
83
- msg_id: "msg-2",
84
- session: "session-2",
85
- username: "omp",
86
- date: "2024-02-01T00:00:00Z",
87
- msg_type: "execute_request",
88
- version: "5.5",
89
- },
90
- parent_header: { parent: "root" },
91
- metadata: { tag: "meta" },
92
- content: { code: "print('hi')" },
93
- buffers: [new Uint8Array([9, 8, 7])],
94
- };
95
-
96
- const frame = serializeWebSocketMessage(message);
97
- const parsed = deserializeWebSocketMessage(frame);
98
-
99
- expect(parsed).not.toBeNull();
100
- expect(parsed?.header.msg_type).toBe("execute_request");
101
- expect(parsed?.content).toEqual({ code: "print('hi')" });
102
- expect(parsed?.buffers?.[0]).toEqual(new Uint8Array([9, 8, 7]));
103
- });
104
- });