@synnaxlabs/client 0.50.0 → 0.53.0

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 (264) hide show
  1. package/.turbo/turbo-build.log +7 -7
  2. package/dist/client.cjs +58 -34
  3. package/dist/client.js +5368 -4546
  4. package/dist/eslint.config.d.ts +2 -2
  5. package/dist/eslint.config.d.ts.map +1 -1
  6. package/dist/src/access/policy/client.d.ts +90 -90
  7. package/dist/src/access/policy/payload.d.ts +17 -17
  8. package/dist/src/access/role/payload.d.ts +2 -2
  9. package/dist/src/arc/payload.d.ts +2 -2
  10. package/dist/src/channel/client.d.ts +4 -5
  11. package/dist/src/channel/client.d.ts.map +1 -1
  12. package/dist/src/channel/payload.d.ts +9 -11
  13. package/dist/src/channel/payload.d.ts.map +1 -1
  14. package/dist/src/channel/retriever.d.ts.map +1 -1
  15. package/dist/src/client.d.ts +10 -0
  16. package/dist/src/client.d.ts.map +1 -1
  17. package/dist/src/device/client.d.ts +14 -7
  18. package/dist/src/device/client.d.ts.map +1 -1
  19. package/dist/src/device/payload.d.ts +65 -32
  20. package/dist/src/device/payload.d.ts.map +1 -1
  21. package/dist/src/device/payload.spec.d.ts +2 -0
  22. package/dist/src/device/payload.spec.d.ts.map +1 -0
  23. package/dist/src/framer/adapter.d.ts.map +1 -1
  24. package/dist/src/framer/client.d.ts +2 -2
  25. package/dist/src/framer/frame.d.ts +5 -5
  26. package/dist/src/framer/frame.d.ts.map +1 -1
  27. package/dist/src/framer/streamer.d.ts +6 -6
  28. package/dist/src/framer/writer.d.ts +9 -9
  29. package/dist/src/group/access.spec.d.ts.map +1 -0
  30. package/dist/src/{ontology/group → group}/client.d.ts +3 -3
  31. package/dist/src/group/client.d.ts.map +1 -0
  32. package/dist/src/group/external.d.ts.map +1 -0
  33. package/dist/src/group/group.spec.d.ts.map +1 -0
  34. package/dist/src/group/index.d.ts.map +1 -0
  35. package/dist/src/{ontology/group → group}/payload.d.ts +1 -1
  36. package/dist/src/group/payload.d.ts.map +1 -0
  37. package/dist/src/index.d.ts +5 -5
  38. package/dist/src/index.d.ts.map +1 -1
  39. package/dist/src/label/client.d.ts +27 -13
  40. package/dist/src/label/client.d.ts.map +1 -1
  41. package/dist/src/label/payload.d.ts +15 -1
  42. package/dist/src/label/payload.d.ts.map +1 -1
  43. package/dist/src/lineplot/access.spec.d.ts.map +1 -0
  44. package/dist/src/{workspace/lineplot → lineplot}/client.d.ts +6 -6
  45. package/dist/src/lineplot/client.d.ts.map +1 -0
  46. package/dist/src/lineplot/external.d.ts.map +1 -0
  47. package/dist/src/lineplot/index.d.ts.map +1 -0
  48. package/dist/src/lineplot/lineplot.spec.d.ts.map +1 -0
  49. package/dist/src/lineplot/payload.d.ts.map +1 -0
  50. package/dist/src/log/access.spec.d.ts.map +1 -0
  51. package/dist/src/{workspace/log → log}/client.d.ts +6 -6
  52. package/dist/src/log/client.d.ts.map +1 -0
  53. package/dist/src/log/external.d.ts.map +1 -0
  54. package/dist/src/log/index.d.ts.map +1 -0
  55. package/dist/src/log/log.spec.d.ts.map +1 -0
  56. package/dist/src/log/payload.d.ts.map +1 -0
  57. package/dist/src/ontology/client.d.ts +6 -10
  58. package/dist/src/ontology/client.d.ts.map +1 -1
  59. package/dist/src/ontology/payload.d.ts +22 -22
  60. package/dist/src/ontology/writer.d.ts +15 -15
  61. package/dist/src/rack/client.d.ts +3 -3
  62. package/dist/src/rack/client.d.ts.map +1 -1
  63. package/dist/src/rack/payload.d.ts +44 -16
  64. package/dist/src/rack/payload.d.ts.map +1 -1
  65. package/dist/src/ranger/alias/client.d.ts +17 -0
  66. package/dist/src/ranger/alias/client.d.ts.map +1 -0
  67. package/dist/src/ranger/alias/external.d.ts +2 -0
  68. package/dist/src/ranger/alias/external.d.ts.map +1 -0
  69. package/dist/src/ranger/alias/index.d.ts +2 -0
  70. package/dist/src/ranger/alias/index.d.ts.map +1 -0
  71. package/dist/src/ranger/{alias.d.ts → alias/payload.d.ts} +14 -20
  72. package/dist/src/ranger/alias/payload.d.ts.map +1 -0
  73. package/dist/src/ranger/client.d.ts +10 -10
  74. package/dist/src/ranger/client.d.ts.map +1 -1
  75. package/dist/src/ranger/external.d.ts +20 -0
  76. package/dist/src/ranger/external.d.ts.map +1 -1
  77. package/dist/src/ranger/kv/client.d.ts +14 -0
  78. package/dist/src/ranger/kv/client.d.ts.map +1 -0
  79. package/dist/src/ranger/kv/external.d.ts +2 -0
  80. package/dist/src/ranger/kv/external.d.ts.map +1 -0
  81. package/dist/src/ranger/kv/index.d.ts +2 -0
  82. package/dist/src/ranger/kv/index.d.ts.map +1 -0
  83. package/dist/src/ranger/kv/payload.d.ts +26 -0
  84. package/dist/src/ranger/kv/payload.d.ts.map +1 -0
  85. package/dist/src/ranger/payload.d.ts +45 -3
  86. package/dist/src/ranger/payload.d.ts.map +1 -1
  87. package/dist/src/ranger/writer.d.ts +18 -4
  88. package/dist/src/ranger/writer.d.ts.map +1 -1
  89. package/dist/src/schematic/access.spec.d.ts.map +1 -0
  90. package/dist/src/{workspace/schematic → schematic}/client.d.ts +6 -6
  91. package/dist/src/schematic/client.d.ts.map +1 -0
  92. package/dist/src/schematic/external.d.ts.map +1 -0
  93. package/dist/src/schematic/index.d.ts.map +1 -0
  94. package/dist/src/schematic/payload.d.ts.map +1 -0
  95. package/dist/src/schematic/schematic.spec.d.ts.map +1 -0
  96. package/dist/src/schematic/symbol/access.spec.d.ts.map +1 -0
  97. package/dist/src/{workspace/schematic → schematic}/symbol/client.d.ts +2 -2
  98. package/dist/src/schematic/symbol/client.d.ts.map +1 -0
  99. package/dist/src/schematic/symbol/client.spec.d.ts.map +1 -0
  100. package/dist/src/schematic/symbol/external.d.ts.map +1 -0
  101. package/dist/src/schematic/symbol/index.d.ts.map +1 -0
  102. package/dist/src/schematic/symbol/payload.d.ts.map +1 -0
  103. package/dist/src/status/payload.d.ts +43 -7
  104. package/dist/src/status/payload.d.ts.map +1 -1
  105. package/dist/src/table/access.spec.d.ts.map +1 -0
  106. package/dist/src/{workspace/table → table}/client.d.ts +6 -6
  107. package/dist/src/table/client.d.ts.map +1 -0
  108. package/dist/src/table/external.d.ts.map +1 -0
  109. package/dist/src/table/index.d.ts.map +1 -0
  110. package/dist/src/table/payload.d.ts.map +1 -0
  111. package/dist/src/table/table.spec.d.ts.map +1 -0
  112. package/dist/src/task/client.d.ts +19 -21
  113. package/dist/src/task/client.d.ts.map +1 -1
  114. package/dist/src/task/payload.d.ts +32 -27
  115. package/dist/src/task/payload.d.ts.map +1 -1
  116. package/dist/src/user/client.d.ts +2 -2
  117. package/dist/src/view/client.d.ts +2 -2
  118. package/dist/src/workspace/client.d.ts +2 -10
  119. package/dist/src/workspace/client.d.ts.map +1 -1
  120. package/eslint.config.ts +2 -3
  121. package/package.json +10 -9
  122. package/src/arc/lsp.spec.ts +155 -283
  123. package/src/channel/client.ts +3 -5
  124. package/src/channel/payload.spec.ts +0 -57
  125. package/src/channel/payload.ts +7 -14
  126. package/src/channel/retriever.ts +2 -3
  127. package/src/client.ts +16 -1
  128. package/src/device/client.ts +89 -43
  129. package/src/device/payload.spec.ts +118 -0
  130. package/src/device/payload.ts +55 -30
  131. package/src/errors.spec.ts +1 -1
  132. package/src/framer/adapter.ts +5 -7
  133. package/src/framer/frame.ts +8 -8
  134. package/src/{ontology/group → group}/access.spec.ts +7 -7
  135. package/src/{ontology/group → group}/client.ts +1 -1
  136. package/src/group/external.ts +11 -0
  137. package/src/{ontology/group → group}/group.spec.ts +9 -9
  138. package/src/{workspace/log → group}/index.ts +1 -1
  139. package/src/index.ts +5 -5
  140. package/src/label/access.spec.ts +1 -1
  141. package/src/label/client.ts +1 -1
  142. package/src/label/label.spec.ts +4 -4
  143. package/src/{workspace/lineplot → lineplot}/access.spec.ts +14 -14
  144. package/src/{workspace/lineplot → lineplot}/client.ts +13 -13
  145. package/src/lineplot/external.ts +11 -0
  146. package/src/{ontology/group → lineplot}/index.ts +1 -1
  147. package/src/{workspace/lineplot → lineplot}/lineplot.spec.ts +12 -12
  148. package/src/{workspace/log → log}/access.spec.ts +16 -18
  149. package/src/{workspace/log → log}/client.ts +13 -13
  150. package/src/{workspace/schematic/index.ts → log/external.ts} +2 -1
  151. package/src/{workspace/table → log}/index.ts +1 -1
  152. package/src/{workspace/log → log}/log.spec.ts +10 -10
  153. package/src/ontology/client.ts +1 -7
  154. package/src/ontology/ontology.spec.ts +12 -12
  155. package/src/rack/client.ts +10 -21
  156. package/src/ranger/{alias.ts → alias/client.ts} +6 -37
  157. package/src/{workspace/lineplot → ranger/alias}/external.ts +10 -2
  158. package/src/{workspace/lineplot → ranger/alias}/index.ts +1 -1
  159. package/src/ranger/alias/payload.ts +50 -0
  160. package/src/ranger/client.ts +26 -19
  161. package/src/ranger/external.ts +22 -0
  162. package/src/ranger/{kv.ts → kv/client.ts} +5 -19
  163. package/src/{workspace/schematic → ranger/kv}/external.ts +7 -3
  164. package/src/ranger/kv/index.ts +10 -0
  165. package/src/ranger/kv/payload.ts +32 -0
  166. package/src/ranger/payload.ts +2 -2
  167. package/src/ranger/ranger.spec.ts +1 -1
  168. package/src/{workspace/schematic → schematic}/access.spec.ts +14 -14
  169. package/src/{workspace/schematic → schematic}/client.ts +14 -14
  170. package/src/{workspace/log → schematic}/external.ts +3 -2
  171. package/src/schematic/index.ts +10 -0
  172. package/src/{workspace/schematic → schematic}/schematic.spec.ts +17 -17
  173. package/src/{workspace/schematic → schematic}/symbol/access.spec.ts +19 -19
  174. package/src/{workspace/schematic → schematic}/symbol/client.spec.ts +16 -16
  175. package/src/{workspace/schematic → schematic}/symbol/client.ts +8 -8
  176. package/src/{ontology/group → schematic/symbol}/external.ts +2 -2
  177. package/src/schematic/symbol/index.ts +10 -0
  178. package/src/status/payload.ts +1 -1
  179. package/src/status/status.spec.ts +2 -2
  180. package/src/{workspace/table → table}/access.spec.ts +14 -14
  181. package/src/{workspace/table → table}/client.ts +15 -12
  182. package/src/table/external.ts +11 -0
  183. package/src/table/index.ts +10 -0
  184. package/src/{workspace/table → table}/table.spec.ts +12 -12
  185. package/src/task/client.ts +56 -148
  186. package/src/task/payload.ts +34 -58
  187. package/src/workspace/client.ts +0 -12
  188. package/dist/src/ontology/group/access.spec.d.ts.map +0 -1
  189. package/dist/src/ontology/group/client.d.ts.map +0 -1
  190. package/dist/src/ontology/group/external.d.ts.map +0 -1
  191. package/dist/src/ontology/group/group.spec.d.ts.map +0 -1
  192. package/dist/src/ontology/group/index.d.ts.map +0 -1
  193. package/dist/src/ontology/group/payload.d.ts.map +0 -1
  194. package/dist/src/ranger/alias.d.ts.map +0 -1
  195. package/dist/src/ranger/kv.d.ts +0 -48
  196. package/dist/src/ranger/kv.d.ts.map +0 -1
  197. package/dist/src/workspace/lineplot/access.spec.d.ts.map +0 -1
  198. package/dist/src/workspace/lineplot/client.d.ts.map +0 -1
  199. package/dist/src/workspace/lineplot/external.d.ts.map +0 -1
  200. package/dist/src/workspace/lineplot/index.d.ts.map +0 -1
  201. package/dist/src/workspace/lineplot/lineplot.spec.d.ts.map +0 -1
  202. package/dist/src/workspace/lineplot/payload.d.ts.map +0 -1
  203. package/dist/src/workspace/log/access.spec.d.ts.map +0 -1
  204. package/dist/src/workspace/log/client.d.ts.map +0 -1
  205. package/dist/src/workspace/log/external.d.ts.map +0 -1
  206. package/dist/src/workspace/log/index.d.ts.map +0 -1
  207. package/dist/src/workspace/log/log.spec.d.ts.map +0 -1
  208. package/dist/src/workspace/log/payload.d.ts.map +0 -1
  209. package/dist/src/workspace/schematic/access.spec.d.ts.map +0 -1
  210. package/dist/src/workspace/schematic/client.d.ts.map +0 -1
  211. package/dist/src/workspace/schematic/external.d.ts.map +0 -1
  212. package/dist/src/workspace/schematic/index.d.ts.map +0 -1
  213. package/dist/src/workspace/schematic/payload.d.ts.map +0 -1
  214. package/dist/src/workspace/schematic/schematic.spec.d.ts.map +0 -1
  215. package/dist/src/workspace/schematic/symbol/access.spec.d.ts.map +0 -1
  216. package/dist/src/workspace/schematic/symbol/client.d.ts.map +0 -1
  217. package/dist/src/workspace/schematic/symbol/client.spec.d.ts.map +0 -1
  218. package/dist/src/workspace/schematic/symbol/external.d.ts.map +0 -1
  219. package/dist/src/workspace/schematic/symbol/index.d.ts.map +0 -1
  220. package/dist/src/workspace/schematic/symbol/payload.d.ts.map +0 -1
  221. package/dist/src/workspace/table/access.spec.d.ts.map +0 -1
  222. package/dist/src/workspace/table/client.d.ts.map +0 -1
  223. package/dist/src/workspace/table/external.d.ts.map +0 -1
  224. package/dist/src/workspace/table/index.d.ts.map +0 -1
  225. package/dist/src/workspace/table/payload.d.ts.map +0 -1
  226. package/dist/src/workspace/table/table.spec.d.ts.map +0 -1
  227. package/src/workspace/schematic/symbol/external.ts +0 -11
  228. package/src/workspace/schematic/symbol/index.ts +0 -10
  229. package/src/workspace/table/external.ts +0 -11
  230. /package/dist/src/{ontology/group → group}/access.spec.d.ts +0 -0
  231. /package/dist/src/{ontology/group → group}/external.d.ts +0 -0
  232. /package/dist/src/{ontology/group → group}/group.spec.d.ts +0 -0
  233. /package/dist/src/{ontology/group → group}/index.d.ts +0 -0
  234. /package/dist/src/{workspace/lineplot → lineplot}/access.spec.d.ts +0 -0
  235. /package/dist/src/{workspace/lineplot → lineplot}/external.d.ts +0 -0
  236. /package/dist/src/{workspace/lineplot → lineplot}/index.d.ts +0 -0
  237. /package/dist/src/{workspace/lineplot → lineplot}/lineplot.spec.d.ts +0 -0
  238. /package/dist/src/{workspace/lineplot → lineplot}/payload.d.ts +0 -0
  239. /package/dist/src/{workspace/log → log}/access.spec.d.ts +0 -0
  240. /package/dist/src/{workspace/log → log}/external.d.ts +0 -0
  241. /package/dist/src/{workspace/log → log}/index.d.ts +0 -0
  242. /package/dist/src/{workspace/log → log}/log.spec.d.ts +0 -0
  243. /package/dist/src/{workspace/log → log}/payload.d.ts +0 -0
  244. /package/dist/src/{workspace/schematic → schematic}/access.spec.d.ts +0 -0
  245. /package/dist/src/{workspace/schematic → schematic}/external.d.ts +0 -0
  246. /package/dist/src/{workspace/schematic → schematic}/index.d.ts +0 -0
  247. /package/dist/src/{workspace/schematic → schematic}/payload.d.ts +0 -0
  248. /package/dist/src/{workspace/schematic → schematic}/schematic.spec.d.ts +0 -0
  249. /package/dist/src/{workspace/schematic → schematic}/symbol/access.spec.d.ts +0 -0
  250. /package/dist/src/{workspace/schematic → schematic}/symbol/client.spec.d.ts +0 -0
  251. /package/dist/src/{workspace/schematic → schematic}/symbol/external.d.ts +0 -0
  252. /package/dist/src/{workspace/schematic → schematic}/symbol/index.d.ts +0 -0
  253. /package/dist/src/{workspace/schematic → schematic}/symbol/payload.d.ts +0 -0
  254. /package/dist/src/{workspace/table → table}/access.spec.d.ts +0 -0
  255. /package/dist/src/{workspace/table → table}/external.d.ts +0 -0
  256. /package/dist/src/{workspace/table → table}/index.d.ts +0 -0
  257. /package/dist/src/{workspace/table → table}/payload.d.ts +0 -0
  258. /package/dist/src/{workspace/table → table}/table.spec.d.ts +0 -0
  259. /package/src/{ontology/group → group}/payload.ts +0 -0
  260. /package/src/{workspace/lineplot → lineplot}/payload.ts +0 -0
  261. /package/src/{workspace/log → log}/payload.ts +0 -0
  262. /package/src/{workspace/schematic → schematic}/payload.ts +0 -0
  263. /package/src/{workspace/schematic → schematic}/symbol/payload.ts +0 -0
  264. /package/src/{workspace/table → table}/payload.ts +0 -0
@@ -7,52 +7,91 @@
7
7
  // License, use of this software will be governed by the Apache License, Version 2.0,
8
8
  // included in the file licenses/APL.txt.
9
9
 
10
- import { type jsonRPC } from "@synnaxlabs/x";
11
10
  import { describe, expect, it } from "vitest";
12
11
 
13
12
  import { createTestClient } from "@/testutil/client";
14
13
 
14
+ interface JSONRPCRequest {
15
+ jsonrpc: "2.0";
16
+ id: number;
17
+ method: string;
18
+ params?: unknown;
19
+ }
20
+
21
+ type JSONRPCResponse =
22
+ | { jsonrpc: "2.0"; id: number; result: unknown }
23
+ | {
24
+ jsonrpc: "2.0";
25
+ id: number;
26
+ error: { code: number; message: string; data?: unknown };
27
+ };
28
+
29
+ type LSPReceiver = {
30
+ receive: () => Promise<[{ content: string }, null] | [null, Error]>;
31
+ };
32
+
33
+ const MAX_DRAIN = 50;
34
+
35
+ /** Drains messages from the stream until a JSON-RPC response with the expected id arrives. */
36
+ const receiveResponse = async (
37
+ stream: LSPReceiver,
38
+ expectedId: number,
39
+ ): Promise<JSONRPCResponse> => {
40
+ for (let i = 0; i < MAX_DRAIN; i++) {
41
+ const [res, err] = await stream.receive();
42
+ if (err != null) throw err;
43
+ if (res == null) throw new Error("Expected response");
44
+ const msg = JSON.parse(res.content);
45
+ if (!("method" in msg) && "id" in msg && msg.id === expectedId)
46
+ return msg as JSONRPCResponse;
47
+ }
48
+ throw new Error(
49
+ `receiveResponse: drained ${MAX_DRAIN} messages without seeing id=${expectedId}`,
50
+ );
51
+ };
52
+
53
+ /** Drains messages from the stream until a JSON-RPC notification with the expected method arrives. */
54
+ const receiveNotification = async (
55
+ stream: LSPReceiver,
56
+ expectedMethod: string,
57
+ ): Promise<JSONRPCRequest> => {
58
+ for (let i = 0; i < MAX_DRAIN; i++) {
59
+ const [res, err] = await stream.receive();
60
+ if (err != null) throw err;
61
+ if (res == null) throw new Error("Expected message");
62
+ const msg = JSON.parse(res.content);
63
+ if ("method" in msg && msg.method === expectedMethod) return msg as JSONRPCRequest;
64
+ }
65
+ throw new Error(
66
+ `receiveNotification: drained ${MAX_DRAIN} messages without seeing method=${expectedMethod}`,
67
+ );
68
+ };
69
+
15
70
  describe("Arc LSP", () => {
16
71
  it("should open an LSP stream and handle initialize request", async () => {
17
72
  const client = createTestClient();
18
73
  const stream = await client.arcs.openLSP();
19
74
 
20
- // Send LSP initialize request (raw JSON, no Content-Length headers)
21
- const initializeRequest: jsonRPC.Request = {
75
+ const initializeRequest: JSONRPCRequest = {
22
76
  jsonrpc: "2.0",
23
77
  id: 1,
24
78
  method: "initialize",
25
79
  params: {
26
80
  processId: null,
27
- clientInfo: {
28
- name: "test-client",
29
- version: "1.0.0",
30
- },
81
+ clientInfo: { name: "test-client", version: "1.0.0" },
31
82
  rootUri: null,
32
83
  capabilities: {},
33
84
  },
34
85
  };
35
86
 
36
- stream.send({
37
- content: JSON.stringify(initializeRequest),
38
- });
39
-
40
- // Receive response (raw JSON, no Content-Length headers)
41
- const [res, err] = await stream.receive();
42
- expect(err).toBeNull();
43
- if (!res) throw new Error("Expected response");
87
+ stream.send({ content: JSON.stringify(initializeRequest) });
44
88
 
45
- // Parse raw JSON response directly
46
- const msg = JSON.parse(res.content) as jsonRPC.Response;
89
+ const msg = await receiveResponse(stream, 1);
47
90
  expect(msg.jsonrpc).toBe("2.0");
48
91
  expect(msg.id).toBe(1);
49
-
50
- // Check for error response
51
92
  if ("error" in msg) throw new Error(`LSP error: ${msg.error.message}`);
52
-
53
93
  expect("result" in msg).toBe(true);
54
94
 
55
- // Verify capabilities are present
56
95
  if ("result" in msg) {
57
96
  const result = msg.result as Record<string, unknown>;
58
97
  expect(result).toHaveProperty("capabilities");
@@ -65,8 +104,7 @@ describe("Arc LSP", () => {
65
104
  const client = createTestClient();
66
105
  const stream = await client.arcs.openLSP();
67
106
 
68
- // First initialize
69
- const initializeRequest: jsonRPC.Request = {
107
+ const initializeRequest: JSONRPCRequest = {
70
108
  jsonrpc: "2.0",
71
109
  id: 1,
72
110
  method: "initialize",
@@ -79,62 +117,37 @@ describe("Arc LSP", () => {
79
117
  };
80
118
 
81
119
  stream.send({ content: JSON.stringify(initializeRequest) });
82
- const [initResponse, initErr] = await stream.receive();
83
- expect(initErr).toBeNull();
84
- expect(initResponse).not.toBeNull();
85
- if (!initResponse) throw new Error("Expected response");
86
-
87
- // Parse raw JSON response
88
- const initMsg = JSON.parse(initResponse.content) as jsonRPC.Response;
89
- expect(initMsg.id).toBe(1);
90
-
91
- // Check for error response
120
+ const initMsg = await receiveResponse(stream, 1);
92
121
  if ("error" in initMsg) throw new Error(`LSP error: ${initMsg.error.message}`);
93
122
 
94
- // Send initialized notification
95
- const initializedNotification: jsonRPC.Request = {
96
- jsonrpc: "2.0",
97
- method: "initialized",
98
- params: {},
99
- };
100
-
101
- stream.send({ content: JSON.stringify(initializedNotification) });
123
+ stream.send({
124
+ content: JSON.stringify({
125
+ jsonrpc: "2.0",
126
+ method: "initialized",
127
+ params: {},
128
+ }),
129
+ });
102
130
 
103
- // Send didOpen notification
104
- const didOpenNotification: jsonRPC.Request = {
105
- jsonrpc: "2.0",
106
- method: "textDocument/didOpen",
107
- params: {
108
- textDocument: {
109
- uri: "file:///test.arc",
110
- languageId: "arc",
111
- version: 1,
112
- text: "let x = 1 + 2;",
131
+ stream.send({
132
+ content: JSON.stringify({
133
+ jsonrpc: "2.0",
134
+ method: "textDocument/didOpen",
135
+ params: {
136
+ textDocument: {
137
+ uri: "file:///test.arc",
138
+ languageId: "arc",
139
+ version: 1,
140
+ text: "let x = 1 + 2;",
141
+ },
113
142
  },
114
- },
115
- };
116
-
117
- stream.send({ content: JSON.stringify(didOpenNotification) });
143
+ }),
144
+ });
118
145
 
119
- // Notifications don't get responses, but we might get diagnostics back
120
- // Wait a bit to see if we get any messages
121
- const diagnosticsPromise = stream.receive();
122
- const timeoutPromise = new Promise((resolve) =>
123
- setTimeout(() => resolve(null), 100),
146
+ const diagMsg = await receiveNotification(
147
+ stream,
148
+ "textDocument/publishDiagnostics",
124
149
  );
125
-
126
- const result = await Promise.race([diagnosticsPromise, timeoutPromise]);
127
-
128
- if (result && Array.isArray(result)) {
129
- const [diagResponse, diagErr] = result;
130
- if (diagResponse && !diagErr) {
131
- // Parse raw JSON notification
132
- const diagMsg = JSON.parse(diagResponse.content) as jsonRPC.Message;
133
- expect(diagMsg.jsonrpc).toBe("2.0");
134
- if ("method" in diagMsg)
135
- expect(diagMsg.method).toBe("textDocument/publishDiagnostics");
136
- }
137
- }
150
+ expect(diagMsg.jsonrpc).toBe("2.0");
138
151
 
139
152
  stream.closeSend();
140
153
  client.close();
@@ -144,8 +157,7 @@ describe("Arc LSP", () => {
144
157
  const client = createTestClient();
145
158
  const stream = await client.arcs.openLSP();
146
159
 
147
- // Initialize
148
- const initializeRequest: jsonRPC.Request = {
160
+ const initializeRequest: JSONRPCRequest = {
149
161
  jsonrpc: "2.0",
150
162
  id: 1,
151
163
  method: "initialize",
@@ -158,14 +170,9 @@ describe("Arc LSP", () => {
158
170
  };
159
171
 
160
172
  stream.send({ content: JSON.stringify(initializeRequest) });
161
- const [initRes] = await stream.receive();
162
- expect(initRes).not.toBeNull();
163
- if (!initRes) throw new Error("Expected response");
164
-
165
- const initMsg = JSON.parse(initRes.content) as jsonRPC.Response;
173
+ const initMsg = await receiveResponse(stream, 1);
166
174
  if ("error" in initMsg) throw new Error(`LSP error: ${initMsg.error.message}`);
167
175
 
168
- // Send initialized notification
169
176
  stream.send({
170
177
  content: JSON.stringify({
171
178
  jsonrpc: "2.0",
@@ -174,7 +181,6 @@ describe("Arc LSP", () => {
174
181
  }),
175
182
  });
176
183
 
177
- // Open a document
178
184
  stream.send({
179
185
  content: JSON.stringify({
180
186
  jsonrpc: "2.0",
@@ -190,12 +196,9 @@ describe("Arc LSP", () => {
190
196
  }),
191
197
  });
192
198
 
193
- // Wait for and consume the publishDiagnostics notification
194
- const [diagResponse] = await stream.receive();
195
- expect(diagResponse).not.toBeNull();
199
+ await receiveNotification(stream, "textDocument/publishDiagnostics");
196
200
 
197
- // Request hover information
198
- const hoverRequest: jsonRPC.Request = {
201
+ const hoverRequest: JSONRPCRequest = {
199
202
  jsonrpc: "2.0",
200
203
  id: 2,
201
204
  method: "textDocument/hover",
@@ -207,17 +210,9 @@ describe("Arc LSP", () => {
207
210
 
208
211
  stream.send({ content: JSON.stringify(hoverRequest) });
209
212
 
210
- const [hoverResponse, hoverErr] = await stream.receive();
211
- expect(hoverErr).toBeNull();
212
- expect(hoverResponse).not.toBeNull();
213
- if (!hoverResponse) throw new Error("Expected response");
214
-
215
- // Parse raw JSON response
216
- const responseMsg = JSON.parse(hoverResponse.content) as jsonRPC.Response;
213
+ const responseMsg = await receiveResponse(stream, 2);
217
214
  expect(responseMsg.jsonrpc).toBe("2.0");
218
215
  expect(responseMsg.id).toBe(2);
219
-
220
- // Check for error response
221
216
  if ("error" in responseMsg)
222
217
  throw new Error(`LSP error: ${responseMsg.error.message}`);
223
218
 
@@ -229,10 +224,9 @@ describe("Arc LSP", () => {
229
224
  const client = createTestClient();
230
225
  const stream = await client.arcs.openLSP();
231
226
 
232
- const receivedMessages: jsonRPC.Response[] = [];
227
+ const receivedMessages: JSONRPCResponse[] = [];
233
228
 
234
- // Initialize
235
- const initializeRequest: jsonRPC.Request = {
229
+ const initializeRequest: JSONRPCRequest = {
236
230
  jsonrpc: "2.0",
237
231
  id: 1,
238
232
  method: "initialize",
@@ -245,14 +239,11 @@ describe("Arc LSP", () => {
245
239
  };
246
240
 
247
241
  stream.send({ content: JSON.stringify(initializeRequest) });
248
- const [initResponse] = await stream.receive();
249
- if (!initResponse) throw new Error("Expected response");
250
- const initMsg = JSON.parse(initResponse.content) as jsonRPC.Response;
242
+ const initMsg = await receiveResponse(stream, 1);
251
243
  if ("error" in initMsg) throw new Error(`LSP error: ${initMsg.error.message}`);
252
244
 
253
245
  receivedMessages.push(initMsg);
254
246
 
255
- // Send multiple requests with different IDs
256
247
  const requests = [
257
248
  {
258
249
  jsonrpc: "2.0",
@@ -270,20 +261,14 @@ describe("Arc LSP", () => {
270
261
 
271
262
  for (const req of requests) stream.send({ content: JSON.stringify(req) });
272
263
 
273
- // Receive responses (order may vary)
274
- for (let i = 0; i < requests.length; i++) {
275
- const [response] = await stream.receive();
276
- if (!response) throw new Error("Expected response");
277
- const msg = JSON.parse(response.content) as jsonRPC.Response;
264
+ for (const req of requests) {
265
+ const msg = await receiveResponse(stream, req.id);
278
266
  if ("error" in msg) throw new Error(`LSP error: ${msg.error.message}`);
279
-
280
267
  receivedMessages.push(msg);
281
268
  }
282
269
 
283
- // Should have init response + 2 request responses
284
270
  expect(receivedMessages.length).toBeGreaterThanOrEqual(3);
285
271
 
286
- // Verify all responses have correct structure
287
272
  for (const msg of receivedMessages) {
288
273
  expect(msg.jsonrpc).toBe("2.0");
289
274
  expect(msg.id).toBeDefined();
@@ -297,31 +282,19 @@ describe("Arc LSP", () => {
297
282
  const client = createTestClient();
298
283
  const stream = await client.arcs.openLSP();
299
284
 
300
- // Test that our message format is just JSON without Content-Length headers
301
- const testMessage: jsonRPC.Request = {
285
+ const testMessage: JSONRPCRequest = {
302
286
  jsonrpc: "2.0",
303
287
  id: 999,
304
288
  method: "test/method",
305
289
  params: { data: "test" },
306
290
  };
307
291
 
308
- const messageContent = JSON.stringify(testMessage);
292
+ stream.send({ content: JSON.stringify(testMessage) });
309
293
 
310
- // Send raw JSON (no headers)
311
- stream.send({ content: messageContent });
312
-
313
- // The server should respond (even if it's an error for unknown method)
314
- const [response, err] = await stream.receive();
315
- expect(err).toBeNull();
316
- expect(response).not.toBeNull();
317
- if (!response) throw new Error("Expected response");
318
-
319
- // Response should be parseable JSON
320
- const parsed = JSON.parse(response.content) as jsonRPC.Response;
294
+ const parsed = await receiveResponse(stream, 999);
321
295
  expect(parsed.jsonrpc).toBe("2.0");
322
296
  expect(parsed.id).toBe(999);
323
297
 
324
- // This test expects an error response for unknown method
325
298
  if ("error" in parsed) expect(parsed.error).toBeDefined();
326
299
 
327
300
  stream.closeSend();
@@ -332,8 +305,7 @@ describe("Arc LSP", () => {
332
305
  const client = createTestClient();
333
306
  const stream = await client.arcs.openLSP();
334
307
 
335
- // Initialize with semantic tokens capability
336
- const initializeRequest: jsonRPC.Request = {
308
+ const initializeRequest: JSONRPCRequest = {
337
309
  jsonrpc: "2.0",
338
310
  id: 1,
339
311
  method: "initialize",
@@ -353,21 +325,15 @@ describe("Arc LSP", () => {
353
325
  };
354
326
 
355
327
  stream.send({ content: JSON.stringify(initializeRequest) });
356
- const [initRes] = await stream.receive();
357
- expect(initRes).not.toBeNull();
358
- if (!initRes) throw new Error("Expected response");
359
-
360
- const initMsg = JSON.parse(initRes.content) as jsonRPC.Response;
328
+ const initMsg = await receiveResponse(stream, 1);
361
329
  if ("error" in initMsg) throw new Error(`LSP error: ${initMsg.error.message}`);
362
330
 
363
- // Verify server advertises semantic tokens support
364
331
  if ("result" in initMsg) {
365
332
  const result = initMsg.result as Record<string, unknown>;
366
333
  const capabilities = result.capabilities as Record<string, unknown>;
367
334
  expect(capabilities).toHaveProperty("semanticTokensProvider");
368
335
  }
369
336
 
370
- // Send initialized notification
371
337
  stream.send({
372
338
  content: JSON.stringify({
373
339
  jsonrpc: "2.0",
@@ -376,7 +342,6 @@ describe("Arc LSP", () => {
376
342
  }),
377
343
  });
378
344
 
379
- // Open a document with Arc code
380
345
  const arcCode = "func add(x i32, y i32) i32 {\n return x + y\n}";
381
346
  stream.send({
382
347
  content: JSON.stringify({
@@ -393,11 +358,9 @@ describe("Arc LSP", () => {
393
358
  }),
394
359
  });
395
360
 
396
- // Wait for diagnostics
397
- await stream.receive();
361
+ await receiveNotification(stream, "textDocument/publishDiagnostics");
398
362
 
399
- // Request semantic tokens
400
- const semanticTokensRequest: jsonRPC.Request = {
363
+ const semanticTokensRequest: JSONRPCRequest = {
401
364
  jsonrpc: "2.0",
402
365
  id: 2,
403
366
  method: "textDocument/semanticTokens/full",
@@ -408,26 +371,18 @@ describe("Arc LSP", () => {
408
371
 
409
372
  stream.send({ content: JSON.stringify(semanticTokensRequest) });
410
373
 
411
- const [tokenResponse, tokenErr] = await stream.receive();
412
- expect(tokenErr).toBeNull();
413
- expect(tokenResponse).not.toBeNull();
414
- if (!tokenResponse) throw new Error("Expected response");
415
-
416
- const tokenMsg = JSON.parse(tokenResponse.content) as jsonRPC.Response;
374
+ const tokenMsg = await receiveResponse(stream, 2);
417
375
  expect(tokenMsg.jsonrpc).toBe("2.0");
418
376
  expect(tokenMsg.id).toBe(2);
419
-
420
377
  if ("error" in tokenMsg) throw new Error(`LSP error: ${tokenMsg.error.message}`);
421
378
 
422
- // Verify semantic tokens are returned
423
379
  if ("result" in tokenMsg) {
424
380
  const result = tokenMsg.result as Record<string, unknown>;
425
381
  expect(result).toHaveProperty("data");
426
382
  const data = result.data as number[];
427
383
  expect(Array.isArray(data)).toBe(true);
428
- // Should have tokens (encoded as [deltaLine, deltaStart, length, type, modifiers] * N)
429
384
  expect(data.length).toBeGreaterThan(0);
430
- // Tokens should be in groups of 5
385
+ // Tokens are encoded as [deltaLine, deltaStart, length, type, modifiers] * N
431
386
  expect(data.length % 5).toBe(0);
432
387
  }
433
388
 
@@ -440,8 +395,7 @@ describe("Arc LSP", () => {
440
395
  const client = createTestClient();
441
396
  const stream = await client.arcs.openLSP();
442
397
 
443
- // Initialize
444
- const initializeRequest: jsonRPC.Request = {
398
+ const initializeRequest: JSONRPCRequest = {
445
399
  jsonrpc: "2.0",
446
400
  id: 1,
447
401
  method: "initialize",
@@ -454,14 +408,9 @@ describe("Arc LSP", () => {
454
408
  };
455
409
 
456
410
  stream.send({ content: JSON.stringify(initializeRequest) });
457
- const [initRes] = await stream.receive();
458
- expect(initRes).not.toBeNull();
459
- if (!initRes) throw new Error("Expected response");
460
-
461
- const initMsg = JSON.parse(initRes.content) as jsonRPC.Response;
411
+ const initMsg = await receiveResponse(stream, 1);
462
412
  if ("error" in initMsg) throw new Error(`LSP error: ${initMsg.error.message}`);
463
413
 
464
- // Send initialized notification
465
414
  stream.send({
466
415
  content: JSON.stringify({
467
416
  jsonrpc: "2.0",
@@ -470,13 +419,10 @@ describe("Arc LSP", () => {
470
419
  }),
471
420
  });
472
421
 
473
- // Create metadata for block expression
474
422
  const metadata = { is_block: true };
475
423
  const encoded = btoa(JSON.stringify(metadata));
476
424
  const blockURI = `arc://block/test123#${encoded}`;
477
425
 
478
- // Open a document with block URI containing a simple expression
479
- const blockExpression = "return x * 2";
480
426
  stream.send({
481
427
  content: JSON.stringify({
482
428
  jsonrpc: "2.0",
@@ -486,30 +432,22 @@ describe("Arc LSP", () => {
486
432
  uri: blockURI,
487
433
  languageId: "arc",
488
434
  version: 1,
489
- text: blockExpression,
435
+ text: "return x * 2",
490
436
  },
491
437
  },
492
438
  }),
493
439
  });
494
440
 
495
- // Should receive diagnostics
496
- const [diagResponse, diagErr] = await stream.receive();
497
- expect(diagErr).toBeNull();
498
- expect(diagResponse).not.toBeNull();
499
- if (!diagResponse) throw new Error("Expected diagnostics");
500
-
501
- const diagMsg = JSON.parse(diagResponse.content) as jsonRPC.Message;
441
+ const diagMsg = await receiveNotification(
442
+ stream,
443
+ "textDocument/publishDiagnostics",
444
+ );
502
445
  expect(diagMsg.jsonrpc).toBe("2.0");
503
- if ("method" in diagMsg) {
504
- expect(diagMsg.method).toBe("textDocument/publishDiagnostics");
505
- if ("params" in diagMsg) {
506
- const params = diagMsg.params as Record<string, unknown>;
507
- expect(params.uri).toBe(blockURI);
508
- // Expression should be wrapped and parsed successfully
509
- const diagnostics = params.diagnostics as unknown[];
510
- // May have diagnostics about undefined 'x', but should parse successfully
511
- expect(Array.isArray(diagnostics)).toBe(true);
512
- }
446
+ if ("params" in diagMsg) {
447
+ const params = diagMsg.params as Record<string, unknown>;
448
+ expect(params.uri).toBe(blockURI);
449
+ const diagnostics = params.diagnostics as unknown[];
450
+ expect(Array.isArray(diagnostics)).toBe(true);
513
451
  }
514
452
 
515
453
  stream.closeSend();
@@ -520,8 +458,7 @@ describe("Arc LSP", () => {
520
458
  const client = createTestClient();
521
459
  const stream = await client.arcs.openLSP();
522
460
 
523
- // Initialize
524
- const initializeRequest: jsonRPC.Request = {
461
+ const initializeRequest: JSONRPCRequest = {
525
462
  jsonrpc: "2.0",
526
463
  id: 1,
527
464
  method: "initialize",
@@ -534,14 +471,9 @@ describe("Arc LSP", () => {
534
471
  };
535
472
 
536
473
  stream.send({ content: JSON.stringify(initializeRequest) });
537
- const [initRes] = await stream.receive();
538
- expect(initRes).not.toBeNull();
539
- if (!initRes) throw new Error("Expected response");
540
-
541
- const initMsg = JSON.parse(initRes.content) as jsonRPC.Response;
474
+ const initMsg = await receiveResponse(stream, 1);
542
475
  if ("error" in initMsg) throw new Error(`LSP error: ${initMsg.error.message}`);
543
476
 
544
- // Send initialized notification
545
477
  stream.send({
546
478
  content: JSON.stringify({
547
479
  jsonrpc: "2.0",
@@ -550,13 +482,10 @@ describe("Arc LSP", () => {
550
482
  }),
551
483
  });
552
484
 
553
- // Create block URI
554
485
  const metadata = { is_block: true };
555
486
  const encoded = btoa(JSON.stringify(metadata));
556
487
  const blockURI = `arc://block/syntax-error#${encoded}`;
557
488
 
558
- // Open a document with invalid syntax
559
- const invalidExpression = "return x +";
560
489
  stream.send({
561
490
  content: JSON.stringify({
562
491
  jsonrpc: "2.0",
@@ -566,21 +495,17 @@ describe("Arc LSP", () => {
566
495
  uri: blockURI,
567
496
  languageId: "arc",
568
497
  version: 1,
569
- text: invalidExpression,
498
+ text: "return x +",
570
499
  },
571
500
  },
572
501
  }),
573
502
  });
574
503
 
575
- // Should receive diagnostics with syntax error
576
- const [diagResponse, diagErr] = await stream.receive();
577
- expect(diagErr).toBeNull();
578
- expect(diagResponse).not.toBeNull();
579
- if (!diagResponse) throw new Error("Expected diagnostics");
580
-
581
- const diagMsg = JSON.parse(diagResponse.content) as jsonRPC.Message;
582
- if ("method" in diagMsg && "params" in diagMsg) {
583
- expect(diagMsg.method).toBe("textDocument/publishDiagnostics");
504
+ const diagMsg = await receiveNotification(
505
+ stream,
506
+ "textDocument/publishDiagnostics",
507
+ );
508
+ if ("params" in diagMsg) {
584
509
  const params = diagMsg.params as Record<string, unknown>;
585
510
  const diagnostics = params.diagnostics as Array<{
586
511
  range: { start: { line: number; character: number } };
@@ -588,12 +513,9 @@ describe("Arc LSP", () => {
588
513
  severity: number;
589
514
  }>;
590
515
 
591
- // Should have at least one diagnostic
592
516
  expect(diagnostics.length).toBeGreaterThan(0);
593
-
594
- // Position should be mapped correctly (line 0 for original expression)
595
- const firstDiag = diagnostics[0];
596
- expect(firstDiag.range.start.line).toBe(0);
517
+ // Position should be mapped to line 0 for original expression
518
+ expect(diagnostics[0].range.start.line).toBe(0);
597
519
  }
598
520
 
599
521
  stream.closeSend();
@@ -604,8 +526,7 @@ describe("Arc LSP", () => {
604
526
  const client = createTestClient();
605
527
  const stream = await client.arcs.openLSP();
606
528
 
607
- // Initialize
608
- const initializeRequest: jsonRPC.Request = {
529
+ const initializeRequest: JSONRPCRequest = {
609
530
  jsonrpc: "2.0",
610
531
  id: 1,
611
532
  method: "initialize",
@@ -618,14 +539,9 @@ describe("Arc LSP", () => {
618
539
  };
619
540
 
620
541
  stream.send({ content: JSON.stringify(initializeRequest) });
621
- const [initRes] = await stream.receive();
622
- expect(initRes).not.toBeNull();
623
- if (!initRes) throw new Error("Expected response");
624
-
625
- const initMsg = JSON.parse(initRes.content) as jsonRPC.Response;
542
+ const initMsg = await receiveResponse(stream, 1);
626
543
  if ("error" in initMsg) throw new Error(`LSP error: ${initMsg.error.message}`);
627
544
 
628
- // Send initialized notification
629
545
  stream.send({
630
546
  content: JSON.stringify({
631
547
  jsonrpc: "2.0",
@@ -634,14 +550,10 @@ describe("Arc LSP", () => {
634
550
  }),
635
551
  });
636
552
 
637
- // Create block URI
638
553
  const metadata = { is_block: true };
639
554
  const encoded = btoa(JSON.stringify(metadata));
640
555
  const blockURI = `arc://block/multiline#${encoded}`;
641
556
 
642
- // Multi-line block expression
643
- const multiLineExpression =
644
- "let temp = x * 2\nlet result = temp + 1\nreturn result";
645
557
  stream.send({
646
558
  content: JSON.stringify({
647
559
  jsonrpc: "2.0",
@@ -651,25 +563,20 @@ describe("Arc LSP", () => {
651
563
  uri: blockURI,
652
564
  languageId: "arc",
653
565
  version: 1,
654
- text: multiLineExpression,
566
+ text: "let temp = x * 2\nlet result = temp + 1\nreturn result",
655
567
  },
656
568
  },
657
569
  }),
658
570
  });
659
571
 
660
- // Should receive diagnostics
661
- const [diagResponse, diagErr] = await stream.receive();
662
- expect(diagErr).toBeNull();
663
- expect(diagResponse).not.toBeNull();
664
- if (!diagResponse) throw new Error("Expected diagnostics");
665
-
666
- const diagMsg = JSON.parse(diagResponse.content) as jsonRPC.Message;
667
- if ("method" in diagMsg && "params" in diagMsg) {
668
- expect(diagMsg.method).toBe("textDocument/publishDiagnostics");
572
+ const diagMsg = await receiveNotification(
573
+ stream,
574
+ "textDocument/publishDiagnostics",
575
+ );
576
+ if ("params" in diagMsg) {
669
577
  const params = diagMsg.params as Record<string, unknown>;
670
578
  expect(params.uri).toBe(blockURI);
671
579
  const diagnostics = params.diagnostics as unknown[];
672
- // Should successfully parse multi-line expression
673
580
  expect(Array.isArray(diagnostics)).toBe(true);
674
581
  }
675
582
 
@@ -681,8 +588,7 @@ describe("Arc LSP", () => {
681
588
  const client = createTestClient();
682
589
  const stream = await client.arcs.openLSP();
683
590
 
684
- // Initialize
685
- const initializeRequest: jsonRPC.Request = {
591
+ const initializeRequest: JSONRPCRequest = {
686
592
  jsonrpc: "2.0",
687
593
  id: 1,
688
594
  method: "initialize",
@@ -695,14 +601,9 @@ describe("Arc LSP", () => {
695
601
  };
696
602
 
697
603
  stream.send({ content: JSON.stringify(initializeRequest) });
698
- const [initRes] = await stream.receive();
699
- expect(initRes).not.toBeNull();
700
- if (!initRes) throw new Error("Expected response");
701
-
702
- const initMsg = JSON.parse(initRes.content) as jsonRPC.Response;
604
+ const initMsg = await receiveResponse(stream, 1);
703
605
  if ("error" in initMsg) throw new Error(`LSP error: ${initMsg.error.message}`);
704
606
 
705
- // Send initialized notification
706
607
  stream.send({
707
608
  content: JSON.stringify({
708
609
  jsonrpc: "2.0",
@@ -711,12 +612,10 @@ describe("Arc LSP", () => {
711
612
  }),
712
613
  });
713
614
 
714
- // Create block URI
715
615
  const metadata = { is_block: true };
716
616
  const encoded = btoa(JSON.stringify(metadata));
717
617
  const blockURI = `arc://block/change-test#${encoded}`;
718
618
 
719
- // Open initial document
720
619
  stream.send({
721
620
  content: JSON.stringify({
722
621
  jsonrpc: "2.0",
@@ -732,37 +631,24 @@ describe("Arc LSP", () => {
732
631
  }),
733
632
  });
734
633
 
735
- // Receive initial diagnostics
736
- await stream.receive();
634
+ await receiveNotification(stream, "textDocument/publishDiagnostics");
737
635
 
738
- // Send didChange to update the expression
739
636
  stream.send({
740
637
  content: JSON.stringify({
741
638
  jsonrpc: "2.0",
742
639
  method: "textDocument/didChange",
743
640
  params: {
744
- textDocument: {
745
- uri: blockURI,
746
- version: 2,
747
- },
748
- contentChanges: [
749
- {
750
- text: "return x + y",
751
- },
752
- ],
641
+ textDocument: { uri: blockURI, version: 2 },
642
+ contentChanges: [{ text: "return x + y" }],
753
643
  },
754
644
  }),
755
645
  });
756
646
 
757
- // Should receive updated diagnostics
758
- const [changeDiagResponse, changeDiagErr] = await stream.receive();
759
- expect(changeDiagErr).toBeNull();
760
- expect(changeDiagResponse).not.toBeNull();
761
- if (!changeDiagResponse) throw new Error("Expected diagnostics");
762
-
763
- const changeDiagMsg = JSON.parse(changeDiagResponse.content) as jsonRPC.Message;
764
- if ("method" in changeDiagMsg && "params" in changeDiagMsg) {
765
- expect(changeDiagMsg.method).toBe("textDocument/publishDiagnostics");
647
+ const changeDiagMsg = await receiveNotification(
648
+ stream,
649
+ "textDocument/publishDiagnostics",
650
+ );
651
+ if ("params" in changeDiagMsg) {
766
652
  const params = changeDiagMsg.params as Record<string, unknown>;
767
653
  expect(params.uri).toBe(blockURI);
768
654
  }
@@ -775,8 +661,7 @@ describe("Arc LSP", () => {
775
661
  const client = createTestClient();
776
662
  const stream = await client.arcs.openLSP();
777
663
 
778
- // Initialize
779
- const initializeRequest: jsonRPC.Request = {
664
+ const initializeRequest: JSONRPCRequest = {
780
665
  jsonrpc: "2.0",
781
666
  id: 1,
782
667
  method: "initialize",
@@ -789,14 +674,9 @@ describe("Arc LSP", () => {
789
674
  };
790
675
 
791
676
  stream.send({ content: JSON.stringify(initializeRequest) });
792
- const [initRes] = await stream.receive();
793
- expect(initRes).not.toBeNull();
794
- if (!initRes) throw new Error("Expected response");
795
-
796
- const initMsg = JSON.parse(initRes.content) as jsonRPC.Response;
677
+ const initMsg = await receiveResponse(stream, 1);
797
678
  if ("error" in initMsg) throw new Error(`LSP error: ${initMsg.error.message}`);
798
679
 
799
- // Send initialized notification
800
680
  stream.send({
801
681
  content: JSON.stringify({
802
682
  jsonrpc: "2.0",
@@ -805,11 +685,9 @@ describe("Arc LSP", () => {
805
685
  }),
806
686
  });
807
687
 
808
- // Try to open a block URI without metadata fragment
688
+ // Block URI without metadata fragment - expression won't be wrapped
809
689
  const invalidBlockURI = "arc://block/no-metadata";
810
690
 
811
- // Open a document with invalid block expression (missing wrapping)
812
- const blockExpression = "return x * 2";
813
691
  stream.send({
814
692
  content: JSON.stringify({
815
693
  jsonrpc: "2.0",
@@ -819,26 +697,20 @@ describe("Arc LSP", () => {
819
697
  uri: invalidBlockURI,
820
698
  languageId: "arc",
821
699
  version: 1,
822
- text: blockExpression,
700
+ text: "return x * 2",
823
701
  },
824
702
  },
825
703
  }),
826
704
  });
827
705
 
828
- // Should receive diagnostics with parse errors (not wrapped)
829
- const [diagResponse, diagErr] = await stream.receive();
830
- expect(diagErr).toBeNull();
831
- expect(diagResponse).not.toBeNull();
832
- if (!diagResponse) throw new Error("Expected diagnostics");
833
-
834
- const diagMsg = JSON.parse(diagResponse.content) as jsonRPC.Message;
835
- if ("method" in diagMsg && "params" in diagMsg) {
836
- expect(diagMsg.method).toBe("textDocument/publishDiagnostics");
706
+ const diagMsg = await receiveNotification(
707
+ stream,
708
+ "textDocument/publishDiagnostics",
709
+ );
710
+ if ("params" in diagMsg) {
837
711
  const params = diagMsg.params as Record<string, unknown>;
838
712
  const diagnostics = params.diagnostics as Array<{ severity: number }>;
839
-
840
- // Should have error diagnostics because expression isn't wrapped
841
- const errors = diagnostics.filter((d) => d.severity === 1); // Error severity
713
+ const errors = diagnostics.filter((d) => d.severity === 1);
842
714
  expect(errors.length).toBeGreaterThan(0);
843
715
  }
844
716