llm-mock-server 1.0.5 → 1.0.7

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 (251) hide show
  1. package/README.md +1 -1
  2. package/dist/cli/cli.d.ts +3 -0
  3. package/dist/cli/cli.d.ts.map +1 -0
  4. package/dist/cli/cli.js +103 -0
  5. package/dist/cli/cli.js.map +1 -0
  6. package/dist/cli/validators.d.ts +7 -0
  7. package/dist/cli/validators.d.ts.map +1 -0
  8. package/dist/cli/validators.js +53 -0
  9. package/dist/cli/validators.js.map +1 -0
  10. package/dist/formats/anthropic/index.d.ts +1 -1
  11. package/dist/formats/anthropic/index.d.ts.map +1 -1
  12. package/dist/formats/anthropic/index.js +1 -1
  13. package/dist/formats/anthropic/index.js.map +1 -1
  14. package/dist/formats/anthropic/parse.d.ts +2 -2
  15. package/dist/formats/anthropic/parse.d.ts.map +1 -1
  16. package/dist/formats/anthropic/parse.js +4 -2
  17. package/dist/formats/anthropic/parse.js.map +1 -1
  18. package/dist/formats/anthropic/schema.d.ts +1 -1
  19. package/dist/formats/anthropic/schema.d.ts.map +1 -1
  20. package/dist/formats/anthropic/schema.js +9 -4
  21. package/dist/formats/anthropic/schema.js.map +1 -1
  22. package/dist/formats/anthropic/serialize.d.ts +2 -2
  23. package/dist/formats/anthropic/serialize.d.ts.map +1 -1
  24. package/dist/formats/anthropic/serialize.js +76 -19
  25. package/dist/formats/anthropic/serialize.js.map +1 -1
  26. package/dist/formats/openai/chat-completions/index.d.ts +3 -0
  27. package/dist/formats/openai/chat-completions/index.d.ts.map +1 -0
  28. package/dist/formats/openai/chat-completions/index.js +13 -0
  29. package/dist/formats/openai/chat-completions/index.js.map +1 -0
  30. package/dist/formats/openai/chat-completions/parse.d.ts +4 -0
  31. package/dist/formats/openai/chat-completions/parse.d.ts.map +1 -0
  32. package/dist/formats/openai/chat-completions/parse.js +33 -0
  33. package/dist/formats/openai/chat-completions/parse.js.map +1 -0
  34. package/dist/formats/openai/chat-completions/schema.d.ts +93 -0
  35. package/dist/formats/openai/chat-completions/schema.d.ts.map +1 -0
  36. package/dist/formats/openai/chat-completions/schema.js +74 -0
  37. package/dist/formats/openai/chat-completions/schema.js.map +1 -0
  38. package/dist/formats/openai/chat-completions/serialize.d.ts +10 -0
  39. package/dist/formats/openai/chat-completions/serialize.d.ts.map +1 -0
  40. package/dist/formats/openai/chat-completions/serialize.js +99 -0
  41. package/dist/formats/openai/chat-completions/serialize.js.map +1 -0
  42. package/dist/formats/openai/responses/index.d.ts +3 -0
  43. package/dist/formats/openai/responses/index.d.ts.map +1 -0
  44. package/dist/formats/openai/responses/index.js +13 -0
  45. package/dist/formats/openai/responses/index.js.map +1 -0
  46. package/dist/formats/openai/responses/parse.d.ts +4 -0
  47. package/dist/formats/openai/responses/parse.d.ts.map +1 -0
  48. package/dist/formats/openai/responses/parse.js +51 -0
  49. package/dist/formats/openai/responses/parse.js.map +1 -0
  50. package/dist/formats/openai/responses/schema.d.ts +103 -0
  51. package/dist/formats/openai/responses/schema.d.ts.map +1 -0
  52. package/dist/formats/openai/responses/schema.js +71 -0
  53. package/dist/formats/openai/responses/schema.js.map +1 -0
  54. package/dist/formats/openai/responses/serialize.d.ts +10 -0
  55. package/dist/formats/openai/responses/serialize.d.ts.map +1 -0
  56. package/dist/formats/openai/responses/serialize.js +273 -0
  57. package/dist/formats/openai/responses/serialize.js.map +1 -0
  58. package/dist/formats/request-helpers.d.ts +1 -1
  59. package/dist/formats/request-helpers.d.ts.map +1 -1
  60. package/dist/formats/request-helpers.js.map +1 -1
  61. package/dist/formats/serialize-helpers.d.ts +1 -1
  62. package/dist/formats/serialize-helpers.d.ts.map +1 -1
  63. package/dist/formats/serialize-helpers.js +6 -3
  64. package/dist/formats/serialize-helpers.js.map +1 -1
  65. package/dist/formats/types.d.ts +2 -1
  66. package/dist/formats/types.d.ts.map +1 -1
  67. package/dist/history.d.ts +6 -2
  68. package/dist/history.d.ts.map +1 -1
  69. package/dist/history.js +2 -0
  70. package/dist/history.js.map +1 -1
  71. package/dist/index.d.ts.map +1 -1
  72. package/dist/index.js.map +1 -1
  73. package/dist/loader.d.ts +1 -1
  74. package/dist/loader.d.ts.map +1 -1
  75. package/dist/loader.js +26 -9
  76. package/dist/loader.js.map +1 -1
  77. package/dist/logger.d.ts.map +1 -1
  78. package/dist/logger.js +12 -4
  79. package/dist/logger.js.map +1 -1
  80. package/dist/mock-server.d.ts +44 -48
  81. package/dist/mock-server.d.ts.map +1 -1
  82. package/dist/mock-server.js +37 -85
  83. package/dist/mock-server.js.map +1 -1
  84. package/dist/route-handler.d.ts +1 -1
  85. package/dist/route-handler.d.ts.map +1 -1
  86. package/dist/route-handler.js +19 -7
  87. package/dist/route-handler.js.map +1 -1
  88. package/dist/rule-builder.d.ts +21 -0
  89. package/dist/rule-builder.d.ts.map +1 -0
  90. package/dist/rule-builder.js +58 -0
  91. package/dist/rule-builder.js.map +1 -0
  92. package/dist/rule-engine.d.ts +3 -1
  93. package/dist/rule-engine.d.ts.map +1 -1
  94. package/dist/rule-engine.js +7 -2
  95. package/dist/rule-engine.js.map +1 -1
  96. package/dist/sse-writer.d.ts +1 -1
  97. package/dist/sse-writer.d.ts.map +1 -1
  98. package/dist/types/reply.d.ts +51 -8
  99. package/dist/types/reply.d.ts.map +1 -1
  100. package/dist/types/request.d.ts +21 -6
  101. package/dist/types/request.d.ts.map +1 -1
  102. package/dist/types/rule.d.ts +65 -7
  103. package/dist/types/rule.d.ts.map +1 -1
  104. package/dist/types.d.ts +3 -3
  105. package/dist/types.d.ts.map +1 -1
  106. package/package.json +20 -11
  107. package/.claude/skills/desloppify/SKILL.md +0 -308
  108. package/.desloppify/external_review_sessions/ext_20260315_000339_a6cdc3e6/canonical_import_20260315_000801.json +0 -242
  109. package/.desloppify/external_review_sessions/ext_20260315_000339_a6cdc3e6/canonical_import_20260315_000905.json +0 -248
  110. package/.desloppify/external_review_sessions/ext_20260315_000339_a6cdc3e6/canonical_import_20260315_000917.json +0 -248
  111. package/.desloppify/external_review_sessions/ext_20260315_000339_a6cdc3e6/canonical_import_20260315_000950.json +0 -311
  112. package/.desloppify/external_review_sessions/ext_20260315_000339_a6cdc3e6/claude_launch_prompt.md +0 -17
  113. package/.desloppify/external_review_sessions/ext_20260315_000339_a6cdc3e6/review_result.json +0 -255
  114. package/.desloppify/external_review_sessions/ext_20260315_000339_a6cdc3e6/review_result.template.json +0 -22
  115. package/.desloppify/external_review_sessions/ext_20260315_000339_a6cdc3e6/reviewer_instructions.md +0 -20
  116. package/.desloppify/external_review_sessions/ext_20260315_000339_a6cdc3e6/session.json +0 -20
  117. package/.desloppify/external_review_sessions/ext_20260315_045546_0587ea3b/canonical_import_20260315_050000.json +0 -286
  118. package/.desloppify/external_review_sessions/ext_20260315_045546_0587ea3b/canonical_import_20260315_050028.json +0 -303
  119. package/.desloppify/external_review_sessions/ext_20260315_045546_0587ea3b/claude_launch_prompt.md +0 -17
  120. package/.desloppify/external_review_sessions/ext_20260315_045546_0587ea3b/review_result.json +0 -297
  121. package/.desloppify/external_review_sessions/ext_20260315_045546_0587ea3b/review_result.template.json +0 -22
  122. package/.desloppify/external_review_sessions/ext_20260315_045546_0587ea3b/reviewer_instructions.md +0 -20
  123. package/.desloppify/external_review_sessions/ext_20260315_045546_0587ea3b/session.json +0 -20
  124. package/.desloppify/query.json +0 -1312
  125. package/.desloppify/review_packet_blind.json +0 -1249
  126. package/.desloppify/review_packets/holistic_packet_20260315_000339.json +0 -1471
  127. package/.desloppify/review_packets/holistic_packet_20260315_045546.json +0 -1480
  128. package/.desloppify/review_packets/holistic_packet_20260315_185401.json +0 -1407
  129. package/.desloppify/review_packets/holistic_packet_20260315_185613.json +0 -1407
  130. package/.desloppify/state-typescript.json +0 -8438
  131. package/.desloppify/state-typescript.json.bak +0 -8432
  132. package/.desloppify/subagents/runs/20260315_185401/logs/batch-1.log +0 -384
  133. package/.desloppify/subagents/runs/20260315_185401/logs/batch-10.log +0 -484
  134. package/.desloppify/subagents/runs/20260315_185401/logs/batch-2.log +0 -408
  135. package/.desloppify/subagents/runs/20260315_185401/logs/batch-3.log +0 -416
  136. package/.desloppify/subagents/runs/20260315_185401/logs/batch-4.log +0 -360
  137. package/.desloppify/subagents/runs/20260315_185401/logs/batch-5.log +0 -360
  138. package/.desloppify/subagents/runs/20260315_185401/logs/batch-6.log +0 -364
  139. package/.desloppify/subagents/runs/20260315_185401/logs/batch-7.log +0 -428
  140. package/.desloppify/subagents/runs/20260315_185401/logs/batch-8.log +0 -388
  141. package/.desloppify/subagents/runs/20260315_185401/logs/batch-9.log +0 -500
  142. package/.desloppify/subagents/runs/20260315_185401/prompts/batch-1.md +0 -83
  143. package/.desloppify/subagents/runs/20260315_185401/prompts/batch-10.md +0 -108
  144. package/.desloppify/subagents/runs/20260315_185401/prompts/batch-2.md +0 -89
  145. package/.desloppify/subagents/runs/20260315_185401/prompts/batch-3.md +0 -91
  146. package/.desloppify/subagents/runs/20260315_185401/prompts/batch-4.md +0 -77
  147. package/.desloppify/subagents/runs/20260315_185401/prompts/batch-5.md +0 -77
  148. package/.desloppify/subagents/runs/20260315_185401/prompts/batch-6.md +0 -78
  149. package/.desloppify/subagents/runs/20260315_185401/prompts/batch-7.md +0 -94
  150. package/.desloppify/subagents/runs/20260315_185401/prompts/batch-8.md +0 -84
  151. package/.desloppify/subagents/runs/20260315_185401/prompts/batch-9.md +0 -112
  152. package/.desloppify/subagents/runs/20260315_185401/results/batch-1.raw.txt +0 -0
  153. package/.desloppify/subagents/runs/20260315_185401/results/batch-10.raw.txt +0 -0
  154. package/.desloppify/subagents/runs/20260315_185401/results/batch-2.raw.txt +0 -0
  155. package/.desloppify/subagents/runs/20260315_185401/results/batch-3.raw.txt +0 -0
  156. package/.desloppify/subagents/runs/20260315_185401/results/batch-4.raw.txt +0 -0
  157. package/.desloppify/subagents/runs/20260315_185401/results/batch-5.raw.txt +0 -0
  158. package/.desloppify/subagents/runs/20260315_185401/results/batch-6.raw.txt +0 -0
  159. package/.desloppify/subagents/runs/20260315_185401/results/batch-7.raw.txt +0 -0
  160. package/.desloppify/subagents/runs/20260315_185401/results/batch-8.raw.txt +0 -0
  161. package/.desloppify/subagents/runs/20260315_185401/results/batch-9.raw.txt +0 -0
  162. package/.desloppify/subagents/runs/20260315_185401/run.log +0 -36
  163. package/.desloppify/subagents/runs/20260315_185401/run_summary.json +0 -156
  164. package/.desloppify/subagents/runs/20260315_185613/holistic_findings_merged.json +0 -741
  165. package/.desloppify/subagents/runs/20260315_185613/logs/batch-1.log +0 -579
  166. package/.desloppify/subagents/runs/20260315_185613/logs/batch-10.log +0 -1537
  167. package/.desloppify/subagents/runs/20260315_185613/logs/batch-2.log +0 -829
  168. package/.desloppify/subagents/runs/20260315_185613/logs/batch-3.log +0 -927
  169. package/.desloppify/subagents/runs/20260315_185613/logs/batch-4.log +0 -429
  170. package/.desloppify/subagents/runs/20260315_185613/logs/batch-5.log +0 -276
  171. package/.desloppify/subagents/runs/20260315_185613/logs/batch-6.log +0 -450
  172. package/.desloppify/subagents/runs/20260315_185613/logs/batch-7.log +0 -730
  173. package/.desloppify/subagents/runs/20260315_185613/logs/batch-8.log +0 -698
  174. package/.desloppify/subagents/runs/20260315_185613/logs/batch-9.log +0 -938
  175. package/.desloppify/subagents/runs/20260315_185613/prompts/batch-1.md +0 -83
  176. package/.desloppify/subagents/runs/20260315_185613/prompts/batch-10.md +0 -108
  177. package/.desloppify/subagents/runs/20260315_185613/prompts/batch-2.md +0 -89
  178. package/.desloppify/subagents/runs/20260315_185613/prompts/batch-3.md +0 -91
  179. package/.desloppify/subagents/runs/20260315_185613/prompts/batch-4.md +0 -77
  180. package/.desloppify/subagents/runs/20260315_185613/prompts/batch-5.md +0 -77
  181. package/.desloppify/subagents/runs/20260315_185613/prompts/batch-6.md +0 -78
  182. package/.desloppify/subagents/runs/20260315_185613/prompts/batch-7.md +0 -94
  183. package/.desloppify/subagents/runs/20260315_185613/prompts/batch-8.md +0 -84
  184. package/.desloppify/subagents/runs/20260315_185613/prompts/batch-9.md +0 -112
  185. package/.desloppify/subagents/runs/20260315_185613/results/batch-1.raw.txt +0 -78
  186. package/.desloppify/subagents/runs/20260315_185613/results/batch-10.raw.txt +0 -242
  187. package/.desloppify/subagents/runs/20260315_185613/results/batch-2.raw.txt +0 -102
  188. package/.desloppify/subagents/runs/20260315_185613/results/batch-3.raw.txt +0 -94
  189. package/.desloppify/subagents/runs/20260315_185613/results/batch-4.raw.txt +0 -86
  190. package/.desloppify/subagents/runs/20260315_185613/results/batch-5.raw.txt +0 -1
  191. package/.desloppify/subagents/runs/20260315_185613/results/batch-6.raw.txt +0 -87
  192. package/.desloppify/subagents/runs/20260315_185613/results/batch-7.raw.txt +0 -1
  193. package/.desloppify/subagents/runs/20260315_185613/results/batch-8.raw.txt +0 -107
  194. package/.desloppify/subagents/runs/20260315_185613/results/batch-9.raw.txt +0 -67
  195. package/.desloppify/subagents/runs/20260315_185613/run.log +0 -96
  196. package/.desloppify/subagents/runs/20260315_185613/run_summary.json +0 -156
  197. package/.editorconfig +0 -12
  198. package/.github/dependabot.yml +0 -11
  199. package/.github/workflows/docs.yml +0 -46
  200. package/.github/workflows/test.yml +0 -40
  201. package/.markdownlint.jsonc +0 -11
  202. package/.node-version +0 -1
  203. package/.oxfmtrc.json +0 -9
  204. package/.oxlintrc.json +0 -35
  205. package/docs/ARCHITECTURE.md +0 -125
  206. package/scorecard.png +0 -0
  207. package/src/cli/cli.ts +0 -141
  208. package/src/cli/validators.ts +0 -68
  209. package/src/formats/anthropic/index.ts +0 -14
  210. package/src/formats/anthropic/parse.ts +0 -67
  211. package/src/formats/anthropic/schema.ts +0 -74
  212. package/src/formats/anthropic/serialize.ts +0 -179
  213. package/src/formats/openai/chat-completions/index.ts +0 -14
  214. package/src/formats/openai/chat-completions/parse.ts +0 -44
  215. package/src/formats/openai/chat-completions/schema.ts +0 -92
  216. package/src/formats/openai/chat-completions/serialize.ts +0 -146
  217. package/src/formats/openai/responses/index.ts +0 -14
  218. package/src/formats/openai/responses/parse.ts +0 -70
  219. package/src/formats/openai/responses/schema.ts +0 -86
  220. package/src/formats/openai/responses/serialize.ts +0 -332
  221. package/src/formats/request-helpers.ts +0 -56
  222. package/src/formats/serialize-helpers.ts +0 -43
  223. package/src/formats/types.ts +0 -26
  224. package/src/history.ts +0 -70
  225. package/src/index.ts +0 -46
  226. package/src/loader.ts +0 -246
  227. package/src/logger.ts +0 -70
  228. package/src/mock-server.ts +0 -203
  229. package/src/route-handler.ts +0 -144
  230. package/src/rule-builder.ts +0 -73
  231. package/src/rule-engine.ts +0 -165
  232. package/src/sse-writer.ts +0 -35
  233. package/src/types/reply.ts +0 -92
  234. package/src/types/request.ts +0 -56
  235. package/src/types/rule.ts +0 -125
  236. package/src/types.ts +0 -24
  237. package/test/cli-validators.test.ts +0 -151
  238. package/test/formats/anthropic.test.ts +0 -336
  239. package/test/formats/openai.test.ts +0 -316
  240. package/test/formats/parse-helpers.test.ts +0 -315
  241. package/test/formats/responses.test.ts +0 -380
  242. package/test/helpers/make-req.ts +0 -18
  243. package/test/history.test.ts +0 -361
  244. package/test/loader.test.ts +0 -333
  245. package/test/logger.test.ts +0 -344
  246. package/test/mock-server.test.ts +0 -619
  247. package/test/rule-engine.test.ts +0 -229
  248. package/tsconfig.json +0 -26
  249. package/tsconfig.test.json +0 -11
  250. package/typedoc.json +0 -9
  251. package/vitest.config.ts +0 -18
package/src/types.ts DELETED
@@ -1,24 +0,0 @@
1
- export type {
2
- FormatName,
3
- MockRequest,
4
- Message,
5
- ToolDef,
6
- } from "./types/request.js";
7
- export type {
8
- Reply,
9
- ReplyObject,
10
- ErrorReply,
11
- ToolCall,
12
- Resolver,
13
- ReplyOptions,
14
- SequenceEntry,
15
- } from "./types/reply.js";
16
- export type {
17
- Match,
18
- MatchObject,
19
- PendingRule,
20
- RuleHandle,
21
- RuleSummary,
22
- Handler,
23
- Rule,
24
- } from "./types/rule.js";
@@ -1,151 +0,0 @@
1
- import { describe, it, expect } from "vitest";
2
- import {
3
- parsePort,
4
- parseHost,
5
- parseChunkSize,
6
- parseLogLevel,
7
- parseLatency,
8
- } from "../src/cli/validators.js";
9
-
10
- describe("parsePort", () => {
11
- it("parses a valid port", () => {
12
- expect(parsePort("5555")).toBe(5555);
13
- });
14
-
15
- it("accepts port 1", () => {
16
- expect(parsePort("1")).toBe(1);
17
- });
18
-
19
- it("accepts port 65535", () => {
20
- expect(parsePort("65535")).toBe(65535);
21
- });
22
-
23
- it("throws on port 0", () => {
24
- expect(() => parsePort("0")).toThrow('Invalid port "0"');
25
- });
26
-
27
- it("throws on port above 65535", () => {
28
- expect(() => parsePort("65536")).toThrow('Invalid port "65536"');
29
- });
30
-
31
- it("throws on negative port", () => {
32
- expect(() => parsePort("-1")).toThrow('Invalid port "-1"');
33
- });
34
-
35
- it("throws on non-numeric string", () => {
36
- expect(() => parsePort("abc")).toThrow('Invalid port "abc"');
37
- });
38
-
39
- it("throws on empty string", () => {
40
- expect(() => parsePort("")).toThrow('Invalid port ""');
41
- });
42
-
43
- it("throws on floating point", () => {
44
- expect(() => parsePort("80.5")).toThrow('Invalid port "80.5"');
45
- });
46
-
47
- it("throws on numeric string with trailing chars", () => {
48
- expect(() => parsePort("80abc")).toThrow('Invalid port "80abc"');
49
- });
50
- });
51
-
52
- describe("parseLogLevel", () => {
53
- it.each(["none", "error", "warning", "info", "debug", "all"] as const)(
54
- "accepts %s",
55
- (level) => {
56
- expect(parseLogLevel(level)).toBe(level);
57
- },
58
- );
59
-
60
- it("throws on invalid level", () => {
61
- expect(() => parseLogLevel("verbose")).toThrow(
62
- 'Invalid log level "verbose"',
63
- );
64
- });
65
-
66
- it("throws on empty string", () => {
67
- expect(() => parseLogLevel("")).toThrow('Invalid log level ""');
68
- });
69
- });
70
-
71
- describe("parseHost", () => {
72
- it("accepts 127.0.0.1", async () => {
73
- await expect(parseHost("127.0.0.1")).resolves.toBe("127.0.0.1");
74
- });
75
-
76
- it("accepts 0.0.0.0", async () => {
77
- await expect(parseHost("0.0.0.0")).resolves.toBe("0.0.0.0");
78
- });
79
-
80
- it("accepts localhost", async () => {
81
- await expect(parseHost("localhost")).resolves.toBe("localhost");
82
- });
83
-
84
- it("accepts an IPv6 address", async () => {
85
- await expect(parseHost("::1")).resolves.toBe("::1");
86
- });
87
-
88
- it("rejects an empty string", async () => {
89
- await expect(parseHost("")).rejects.toThrow('Invalid host ""');
90
- });
91
-
92
- it("rejects an unresolvable hostname", async () => {
93
- await expect(parseHost("not.a.real.host.invalid")).rejects.toThrow(
94
- "Invalid host",
95
- );
96
- });
97
-
98
- it("rejects a string with spaces", async () => {
99
- await expect(parseHost("local host")).rejects.toThrow(
100
- 'Invalid host "local host"',
101
- );
102
- });
103
- });
104
-
105
- describe("parseLatency", () => {
106
- it("parses a valid latency", () => {
107
- expect(parseLatency("100")).toBe(100);
108
- });
109
-
110
- it("accepts zero", () => {
111
- expect(parseLatency("0")).toBe(0);
112
- });
113
-
114
- it("throws on negative value", () => {
115
- expect(() => parseLatency("-1")).toThrow('Invalid latency "-1"');
116
- });
117
-
118
- it("throws on non-numeric value", () => {
119
- expect(() => parseLatency("abc")).toThrow('Invalid latency "abc"');
120
- });
121
-
122
- it("throws on empty string", () => {
123
- expect(() => parseLatency("")).toThrow('Invalid latency ""');
124
- });
125
-
126
- it("throws on floating point", () => {
127
- expect(() => parseLatency("50.7")).toThrow('Invalid latency "50.7"');
128
- });
129
-
130
- it("throws on numeric string with trailing chars", () => {
131
- expect(() => parseLatency("50abc")).toThrow('Invalid latency "50abc"');
132
- });
133
- });
134
-
135
- describe("parseChunkSize", () => {
136
- it("parses a valid chunk size", () => {
137
- expect(parseChunkSize("20")).toBe(20);
138
- });
139
-
140
- it("accepts zero", () => {
141
- expect(parseChunkSize("0")).toBe(0);
142
- });
143
-
144
- it("throws on negative value", () => {
145
- expect(() => parseChunkSize("-5")).toThrow('Invalid chunk size "-5"');
146
- });
147
-
148
- it("throws on non-numeric value", () => {
149
- expect(() => parseChunkSize("abc")).toThrow('Invalid chunk size "abc"');
150
- });
151
- });
@@ -1,336 +0,0 @@
1
- import { describe, it, expect } from "vitest";
2
- import { anthropicFormat } from "../../src/formats/anthropic/index.js";
3
- import type {
4
- AnthropicMessageStart,
5
- AnthropicBlockEvent,
6
- AnthropicDelta,
7
- AnthropicComplete,
8
- AnthropicError,
9
- } from "../../src/formats/anthropic/schema.js";
10
-
11
- function parse<T>(chunk: { data: string }): T {
12
- return JSON.parse(chunk.data) as T;
13
- }
14
-
15
- describe("Anthropic Format", () => {
16
- describe("parseRequest", () => {
17
- it("parses messages with top-level system", () => {
18
- const req = anthropicFormat.parseRequest({
19
- model: "claude-sonnet-4-6",
20
- system: "You are a pirate",
21
- messages: [{ role: "user", content: "Hello" }],
22
- max_tokens: 1024,
23
- stream: true,
24
- });
25
- expect(req.format).toBe("anthropic");
26
- expect(req.model).toBe("claude-sonnet-4-6");
27
- expect(req.systemMessage).toBe("You are a pirate");
28
- expect(req.lastMessage).toBe("Hello");
29
- expect(req.messages).toHaveLength(2);
30
- });
31
-
32
- it("parses system as array of blocks", () => {
33
- const req = anthropicFormat.parseRequest({
34
- model: "claude-sonnet-4-6",
35
- max_tokens: 1024,
36
- system: [{ type: "text", text: "Be helpful" }],
37
- messages: [{ role: "user", content: "hi" }],
38
- });
39
- expect(req.systemMessage).toBe("Be helpful");
40
- });
41
-
42
- it("parses content block arrays in messages", () => {
43
- const req = anthropicFormat.parseRequest({
44
- model: "claude-sonnet-4-6",
45
- max_tokens: 1024,
46
- messages: [
47
- { role: "user", content: [{ type: "text", text: "Hello there" }] },
48
- ],
49
- });
50
- expect(req.lastMessage).toBe("Hello there");
51
- });
52
-
53
- it("parses tools with input_schema", () => {
54
- const req = anthropicFormat.parseRequest({
55
- model: "claude-sonnet-4-6",
56
- max_tokens: 1024,
57
- messages: [{ role: "user", content: "read file" }],
58
- tools: [
59
- {
60
- name: "read_file",
61
- description: "Read",
62
- input_schema: { type: "object" },
63
- },
64
- ],
65
- });
66
- expect(req.tools).toHaveLength(1);
67
- expect(req.tools![0]!.name).toBe("read_file");
68
- });
69
-
70
- it("extracts toolNames from tools array", () => {
71
- const req = anthropicFormat.parseRequest({
72
- model: "claude-sonnet-4-6",
73
- max_tokens: 1024,
74
- messages: [{ role: "user", content: "hi" }],
75
- tools: [
76
- { name: "get_weather", input_schema: {} },
77
- { name: "search", input_schema: {} },
78
- ],
79
- });
80
- expect(req.toolNames).toEqual(["get_weather", "search"]);
81
- });
82
-
83
- it("extracts lastToolCallId from tool_result blocks", () => {
84
- const req = anthropicFormat.parseRequest({
85
- model: "claude-sonnet-4-6",
86
- max_tokens: 1024,
87
- messages: [
88
- { role: "user", content: "hi" },
89
- {
90
- role: "user",
91
- content: [
92
- {
93
- type: "tool_result",
94
- tool_use_id: "toolu_123",
95
- content: "result",
96
- },
97
- ],
98
- },
99
- ],
100
- });
101
- expect(req.lastToolCallId).toBe("toolu_123");
102
- });
103
- });
104
-
105
- describe("serialize (streaming)", () => {
106
- it("produces correct event sequence for text", () => {
107
- const chunks = anthropicFormat.serialize(
108
- { text: "Hello" },
109
- "claude-sonnet-4-6",
110
- );
111
- const events = chunks.map((c) => c.event);
112
- expect(events).toEqual([
113
- "message_start",
114
- "content_block_start",
115
- "content_block_delta",
116
- "content_block_stop",
117
- "message_delta",
118
- "message_stop",
119
- ]);
120
- });
121
-
122
- it("message_start contains correct structure", () => {
123
- const chunks = anthropicFormat.serialize(
124
- { text: "Hello" },
125
- "claude-sonnet-4-6",
126
- );
127
- const msg = parse<AnthropicMessageStart>(chunks[0]!);
128
- expect(msg.message).toMatchObject({
129
- type: "message",
130
- role: "assistant",
131
- model: "claude-sonnet-4-6",
132
- content: [],
133
- stop_reason: null,
134
- });
135
- expect(msg.message.usage.input_tokens).toBeTypeOf("number");
136
- expect(msg.message.usage.output_tokens).toBe(0);
137
- });
138
-
139
- it("text block uses index 0 when no reasoning", () => {
140
- const chunks = anthropicFormat.serialize(
141
- { text: "Hello" },
142
- "claude-sonnet-4-6",
143
- );
144
- const blockStart = chunks.find((c) => c.event === "content_block_start");
145
- const data = parse<AnthropicBlockEvent>(blockStart!);
146
- expect(data.index).toBe(0);
147
- expect(data.content_block?.type).toBe("text");
148
- });
149
-
150
- it("thinking block at index 0 and text block at index 1 when reasoning present", () => {
151
- const chunks = anthropicFormat.serialize(
152
- { text: "42", reasoning: "Let me think" },
153
- "claude-sonnet-4-6",
154
- );
155
- const blockStarts = chunks
156
- .filter((c) => c.event === "content_block_start")
157
- .map((c) => parse<AnthropicBlockEvent>(c));
158
-
159
- expect(blockStarts[0]!.index).toBe(0);
160
- expect(blockStarts[0]!.content_block?.type).toBe("thinking");
161
- expect(blockStarts[1]!.index).toBe(1);
162
- expect(blockStarts[1]!.content_block?.type).toBe("text");
163
- });
164
-
165
- it("thinking delta has correct type and content", () => {
166
- const chunks = anthropicFormat.serialize(
167
- { text: "42", reasoning: "Let me think" },
168
- "claude-sonnet-4-6",
169
- );
170
- const thinkingDelta = chunks.find((c) => {
171
- if (c.event !== "content_block_delta") return false;
172
- return parse<AnthropicBlockEvent>(c).delta?.type === "thinking_delta";
173
- });
174
- expect(thinkingDelta).toBeDefined();
175
- expect(parse<AnthropicBlockEvent>(thinkingDelta!).delta?.thinking).toBe(
176
- "Let me think",
177
- );
178
- });
179
-
180
- it("closes thinking block before text block starts", () => {
181
- const chunks = anthropicFormat.serialize(
182
- { text: "answer", reasoning: "think" },
183
- "claude-sonnet-4-6",
184
- );
185
- const events = chunks.map((c) => ({
186
- event: c.event,
187
- data: parse<AnthropicBlockEvent>(c),
188
- }));
189
- const thinkingStop = events.findIndex(
190
- (e) => e.event === "content_block_stop" && e.data.index === 0,
191
- );
192
- const textStart = events.findIndex(
193
- (e) =>
194
- e.event === "content_block_start" &&
195
- e.data.content_block?.type === "text",
196
- );
197
- expect(thinkingStop).toBeLessThan(textStart);
198
- });
199
-
200
- it("includes tool_use blocks with correct structure", () => {
201
- const chunks = anthropicFormat.serialize(
202
- { tools: [{ name: "read_file", args: { path: "/tmp" } }] },
203
- "claude-sonnet-4-6",
204
- );
205
- const toolStart = chunks.find((c) => {
206
- if (c.event !== "content_block_start") return false;
207
- return parse<AnthropicBlockEvent>(c).content_block?.type === "tool_use";
208
- });
209
- expect(toolStart).toBeDefined();
210
- const block = parse<AnthropicBlockEvent>(toolStart!).content_block!;
211
- expect(block.name).toBe("read_file");
212
- expect(block.id).toBeTypeOf("string");
213
- expect(block.input).toEqual({});
214
- });
215
-
216
- it("sets stop_reason to tool_use when tools present", () => {
217
- const chunks = anthropicFormat.serialize(
218
- { tools: [{ name: "read_file", args: {} }] },
219
- "claude-sonnet-4-6",
220
- );
221
- const delta = chunks.find((c) => c.event === "message_delta");
222
- expect(parse<AnthropicDelta>(delta!).delta).toMatchObject({
223
- stop_reason: "tool_use",
224
- });
225
- });
226
-
227
- it("includes stop_sequence: null in message_delta", () => {
228
- const chunks = anthropicFormat.serialize(
229
- { text: "Hello" },
230
- "claude-sonnet-4-6",
231
- );
232
- const delta = chunks.find((c) => c.event === "message_delta");
233
- expect(parse<AnthropicDelta>(delta!).delta.stop_sequence).toBeNull();
234
- });
235
-
236
- it("message_delta includes output_tokens in usage", () => {
237
- const chunks = anthropicFormat.serialize(
238
- { text: "Hello", usage: { input: 20, output: 15 } },
239
- "claude-sonnet-4-6",
240
- );
241
- const delta = chunks.find((c) => c.event === "message_delta");
242
- expect(parse<AnthropicDelta>(delta!).usage.output_tokens).toBe(15);
243
- });
244
- });
245
-
246
- describe("serializeComplete (non-streaming)", () => {
247
- it("produces correct top-level structure", () => {
248
- const result = anthropicFormat.serializeComplete(
249
- { text: "Hello" },
250
- "claude-sonnet-4-6",
251
- ) as AnthropicComplete;
252
- expect(result.type).toBe("message");
253
- expect(result.role).toBe("assistant");
254
- expect(result.model).toBe("claude-sonnet-4-6");
255
- expect(result.stop_reason).toBe("end_turn");
256
- expect(result.stop_sequence).toBeNull();
257
- });
258
-
259
- it("includes text content block", () => {
260
- const result = anthropicFormat.serializeComplete(
261
- { text: "Hello, world!" },
262
- "claude-sonnet-4-6",
263
- ) as AnthropicComplete;
264
- expect(result.content[0]).toMatchObject({
265
- type: "text",
266
- text: "Hello, world!",
267
- });
268
- });
269
-
270
- it("includes thinking before text when reasoning provided", () => {
271
- const result = anthropicFormat.serializeComplete(
272
- { text: "42", reasoning: "Thinking..." },
273
- "claude-sonnet-4-6",
274
- ) as AnthropicComplete;
275
- expect(result.content[0]!.type).toBe("thinking");
276
- expect(result.content[0]!.thinking).toBe("Thinking...");
277
- expect(result.content[1]!.type).toBe("text");
278
- });
279
-
280
- it("includes tool_use with correct structure", () => {
281
- const result = anthropicFormat.serializeComplete(
282
- { tools: [{ name: "read_file", args: { path: "/tmp" } }] },
283
- "claude-sonnet-4-6",
284
- ) as AnthropicComplete;
285
- const tool = result.content.find((c) => c.type === "tool_use");
286
- if (!tool) throw new Error("expected tool_use content block");
287
- expect(tool.name).toBe("read_file");
288
- expect(tool.input).toEqual({ path: "/tmp" });
289
- expect(tool.id).toBeTypeOf("string");
290
- });
291
-
292
- it("generates unique IDs for multiple tools", () => {
293
- const result = anthropicFormat.serializeComplete(
294
- {
295
- tools: [
296
- { name: "read_file", args: { path: "/a" } },
297
- { name: "write_file", args: { path: "/b" } },
298
- ],
299
- },
300
- "claude-sonnet-4-6",
301
- ) as AnthropicComplete;
302
- const tools = result.content.filter((c) => c.type === "tool_use");
303
- if (tools.length !== 2) throw new Error("expected 2 tool_use blocks");
304
- expect(tools[0].id).not.toBe(tools[1].id);
305
- });
306
-
307
- it("sets stop_reason to tool_use when tools present", () => {
308
- const result = anthropicFormat.serializeComplete(
309
- { tools: [{ name: "read_file", args: {} }] },
310
- "claude-sonnet-4-6",
311
- ) as AnthropicComplete;
312
- expect(result.stop_reason).toBe("tool_use");
313
- });
314
-
315
- it("includes usage tokens", () => {
316
- const result = anthropicFormat.serializeComplete(
317
- { text: "hi", usage: { input: 20, output: 15 } },
318
- "claude-sonnet-4-6",
319
- ) as AnthropicComplete;
320
- expect(result.usage).toEqual({ input_tokens: 20, output_tokens: 15 });
321
- });
322
- });
323
-
324
- describe("serializeError", () => {
325
- it("produces Anthropic error format", () => {
326
- const result = anthropicFormat.serializeError({
327
- status: 400,
328
- message: "Bad request",
329
- type: "invalid_request_error",
330
- }) as AnthropicError;
331
- expect(result.type).toBe("error");
332
- expect(result.error.type).toBe("invalid_request_error");
333
- expect(result.error.message).toBe("Bad request");
334
- });
335
- });
336
- });