llm-mock-server 1.0.1 → 1.0.3

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 (129) hide show
  1. package/.claude/skills/desloppify/SKILL.md +308 -0
  2. package/.desloppify/external_review_sessions/ext_20260315_000339_a6cdc3e6/canonical_import_20260315_000801.json +242 -0
  3. package/.desloppify/external_review_sessions/ext_20260315_000339_a6cdc3e6/canonical_import_20260315_000905.json +248 -0
  4. package/.desloppify/external_review_sessions/ext_20260315_000339_a6cdc3e6/canonical_import_20260315_000917.json +248 -0
  5. package/.desloppify/external_review_sessions/ext_20260315_000339_a6cdc3e6/canonical_import_20260315_000950.json +311 -0
  6. package/.desloppify/external_review_sessions/ext_20260315_000339_a6cdc3e6/claude_launch_prompt.md +17 -0
  7. package/.desloppify/external_review_sessions/ext_20260315_000339_a6cdc3e6/review_result.json +255 -0
  8. package/.desloppify/external_review_sessions/ext_20260315_000339_a6cdc3e6/review_result.template.json +22 -0
  9. package/.desloppify/external_review_sessions/ext_20260315_000339_a6cdc3e6/reviewer_instructions.md +20 -0
  10. package/.desloppify/external_review_sessions/ext_20260315_000339_a6cdc3e6/session.json +20 -0
  11. package/.desloppify/query.json +284 -0
  12. package/.desloppify/review_packet_blind.json +1303 -0
  13. package/.desloppify/review_packets/holistic_packet_20260315_000339.json +1471 -0
  14. package/.desloppify/state-typescript.json +5114 -0
  15. package/.desloppify/state-typescript.json.bak +5108 -0
  16. package/.editorconfig +12 -0
  17. package/.github/workflows/test.yml +3 -0
  18. package/.oxfmtrc.json +9 -0
  19. package/dist/cli.js +5 -2
  20. package/dist/cli.js.map +1 -1
  21. package/dist/formats/anthropic/index.js +1 -1
  22. package/dist/formats/anthropic/index.js.map +1 -1
  23. package/dist/formats/anthropic/parse.d.ts +1 -1
  24. package/dist/formats/anthropic/parse.d.ts.map +1 -1
  25. package/dist/formats/anthropic/parse.js +1 -1
  26. package/dist/formats/anthropic/parse.js.map +1 -1
  27. package/dist/formats/anthropic/serialize.d.ts +2 -2
  28. package/dist/formats/anthropic/serialize.d.ts.map +1 -1
  29. package/dist/formats/anthropic/serialize.js +6 -3
  30. package/dist/formats/anthropic/serialize.js.map +1 -1
  31. package/dist/formats/openai/index.js +1 -1
  32. package/dist/formats/openai/index.js.map +1 -1
  33. package/dist/formats/openai/parse.d.ts +1 -1
  34. package/dist/formats/openai/parse.d.ts.map +1 -1
  35. package/dist/formats/openai/parse.js +1 -1
  36. package/dist/formats/openai/parse.js.map +1 -1
  37. package/dist/formats/openai/serialize.d.ts +2 -2
  38. package/dist/formats/openai/serialize.d.ts.map +1 -1
  39. package/dist/formats/openai/serialize.js +12 -15
  40. package/dist/formats/openai/serialize.js.map +1 -1
  41. package/dist/formats/request-helpers.d.ts +13 -0
  42. package/dist/formats/request-helpers.d.ts.map +1 -0
  43. package/dist/formats/request-helpers.js +28 -0
  44. package/dist/formats/request-helpers.js.map +1 -0
  45. package/dist/formats/responses/index.js +1 -1
  46. package/dist/formats/responses/index.js.map +1 -1
  47. package/dist/formats/responses/parse.d.ts +1 -1
  48. package/dist/formats/responses/parse.d.ts.map +1 -1
  49. package/dist/formats/responses/parse.js +1 -1
  50. package/dist/formats/responses/parse.js.map +1 -1
  51. package/dist/formats/responses/schema.d.ts +1 -20
  52. package/dist/formats/responses/schema.d.ts.map +1 -1
  53. package/dist/formats/responses/schema.js.map +1 -1
  54. package/dist/formats/responses/serialize.d.ts +2 -2
  55. package/dist/formats/responses/serialize.d.ts.map +1 -1
  56. package/dist/formats/responses/serialize.js +6 -3
  57. package/dist/formats/responses/serialize.js.map +1 -1
  58. package/dist/formats/serialize-helpers.d.ts +14 -0
  59. package/dist/formats/serialize-helpers.d.ts.map +1 -0
  60. package/dist/formats/serialize-helpers.js +25 -0
  61. package/dist/formats/serialize-helpers.js.map +1 -0
  62. package/dist/formats/types.d.ts +3 -3
  63. package/dist/formats/types.d.ts.map +1 -1
  64. package/dist/loader.d.ts +3 -2
  65. package/dist/loader.d.ts.map +1 -1
  66. package/dist/loader.js +6 -9
  67. package/dist/loader.js.map +1 -1
  68. package/dist/logger.d.ts +1 -0
  69. package/dist/logger.d.ts.map +1 -1
  70. package/dist/logger.js +17 -23
  71. package/dist/logger.js.map +1 -1
  72. package/dist/mock-server.d.ts.map +1 -1
  73. package/dist/mock-server.js +8 -15
  74. package/dist/mock-server.js.map +1 -1
  75. package/dist/route-handler.d.ts +2 -1
  76. package/dist/route-handler.d.ts.map +1 -1
  77. package/dist/rule-engine.d.ts +12 -1
  78. package/dist/rule-engine.d.ts.map +1 -1
  79. package/dist/rule-engine.js +14 -0
  80. package/dist/rule-engine.js.map +1 -1
  81. package/dist/types/reply.d.ts +6 -10
  82. package/dist/types/reply.d.ts.map +1 -1
  83. package/dist/types/request.d.ts +7 -11
  84. package/dist/types/request.d.ts.map +1 -1
  85. package/dist/types/rule.d.ts +3 -10
  86. package/dist/types/rule.d.ts.map +1 -1
  87. package/dist/types.d.ts +3 -1
  88. package/dist/types.d.ts.map +1 -1
  89. package/package.json +5 -2
  90. package/scorecard.png +0 -0
  91. package/src/cli-validators.ts +12 -4
  92. package/src/cli.ts +27 -7
  93. package/src/formats/anthropic/index.ts +1 -1
  94. package/src/formats/anthropic/parse.ts +25 -6
  95. package/src/formats/anthropic/schema.ts +16 -8
  96. package/src/formats/anthropic/serialize.ts +116 -28
  97. package/src/formats/openai/index.ts +1 -1
  98. package/src/formats/openai/parse.ts +13 -3
  99. package/src/formats/openai/schema.ts +43 -30
  100. package/src/formats/openai/serialize.ts +84 -30
  101. package/src/formats/{parse-helpers.ts → request-helpers.ts} +4 -32
  102. package/src/formats/responses/index.ts +1 -1
  103. package/src/formats/responses/parse.ts +18 -4
  104. package/src/formats/responses/schema.ts +34 -22
  105. package/src/formats/responses/serialize.ts +237 -38
  106. package/src/formats/serialize-helpers.ts +38 -0
  107. package/src/formats/types.ts +18 -5
  108. package/src/index.ts +3 -1
  109. package/src/loader.ts +43 -20
  110. package/src/logger.ts +31 -19
  111. package/src/mock-server.ts +38 -21
  112. package/src/route-handler.ts +50 -15
  113. package/src/rule-engine.ts +64 -11
  114. package/src/types/reply.ts +12 -12
  115. package/src/types/request.ts +7 -11
  116. package/src/types/rule.ts +3 -10
  117. package/src/types.ts +23 -4
  118. package/test/cli-validators.test.ts +16 -4
  119. package/test/formats/anthropic.test.ts +84 -23
  120. package/test/formats/openai.test.ts +85 -24
  121. package/test/formats/parse-helpers.test.ts +315 -0
  122. package/test/formats/responses.test.ts +99 -34
  123. package/test/helpers/make-req.ts +18 -0
  124. package/test/history.test.ts +361 -0
  125. package/test/loader.test.ts +44 -45
  126. package/test/logger.test.ts +344 -0
  127. package/test/mock-server.test.ts +77 -23
  128. package/test/rule-engine.test.ts +57 -41
  129. package/src/types/index.ts +0 -4
@@ -1,21 +1,6 @@
1
1
  import { describe, it, expect, beforeEach } from "vitest";
2
2
  import { RuleEngine } from "../src/rule-engine.js";
3
- import type { MockRequest } from "../src/types.js";
4
-
5
- function makeReq(overrides: Partial<MockRequest> = {}): MockRequest {
6
- return {
7
- format: "openai",
8
- model: "gpt-5.4",
9
- streaming: true,
10
- messages: [{ role: "user", content: "hello" }],
11
- lastMessage: "hello",
12
- systemMessage: "",
13
- toolNames: [],
14
- lastToolCallId: undefined,
15
- raw: {},
16
- ...overrides,
17
- };
18
- }
3
+ import { makeReq } from "./helpers/make-req.js";
19
4
 
20
5
  describe("RuleEngine", () => {
21
6
  let engine: RuleEngine;
@@ -27,13 +12,15 @@ describe("RuleEngine", () => {
27
12
  it("matches a string (substring, case-insensitive)", () => {
28
13
  engine.add("hello", "Hi!");
29
14
  const rule = engine.match(makeReq({ lastMessage: "say Hello world" }));
30
- expect(rule).toBeDefined();
31
- expect(rule!.description).toBe('"hello"');
15
+ if (!rule) throw new Error("expected match");
16
+ expect(rule.description).toBe('"hello"');
32
17
  });
33
18
 
34
19
  it("matches a regex", () => {
35
20
  engine.add(/explain (\w+)/i, "Here is an explanation.");
36
- const rule = engine.match(makeReq({ lastMessage: "Can you explain recursion?" }));
21
+ const rule = engine.match(
22
+ makeReq({ lastMessage: "Can you explain recursion?" }),
23
+ );
37
24
  expect(rule).toBeDefined();
38
25
  });
39
26
 
@@ -59,15 +46,25 @@ describe("RuleEngine", () => {
59
46
 
60
47
  it("matches a MatchObject with message + model", () => {
61
48
  engine.add({ model: "gpt-5.4", message: "hello" }, "Hi from GPT-5.4");
62
- expect(engine.match(makeReq({ model: "gpt-5.4", lastMessage: "hello" }))).toBeDefined();
63
- expect(engine.match(makeReq({ model: "gpt-5.4", lastMessage: "bye" }))).toBeUndefined();
64
- expect(engine.match(makeReq({ model: "claude", lastMessage: "hello" }))).toBeUndefined();
49
+ expect(
50
+ engine.match(makeReq({ model: "gpt-5.4", lastMessage: "hello" })),
51
+ ).toBeDefined();
52
+ expect(
53
+ engine.match(makeReq({ model: "gpt-5.4", lastMessage: "bye" })),
54
+ ).toBeUndefined();
55
+ expect(
56
+ engine.match(makeReq({ model: "claude", lastMessage: "hello" })),
57
+ ).toBeUndefined();
65
58
  });
66
59
 
67
60
  it("matches a MatchObject with system", () => {
68
61
  engine.add({ system: /pirate/i }, "Arrr!");
69
- expect(engine.match(makeReq({ systemMessage: "You are a pirate" }))).toBeDefined();
70
- expect(engine.match(makeReq({ systemMessage: "You are helpful" }))).toBeUndefined();
62
+ expect(
63
+ engine.match(makeReq({ systemMessage: "You are a pirate" })),
64
+ ).toBeDefined();
65
+ expect(
66
+ engine.match(makeReq({ systemMessage: "You are helpful" })),
67
+ ).toBeUndefined();
71
68
  });
72
69
 
73
70
  it("matches a MatchObject with format", () => {
@@ -80,7 +77,8 @@ describe("RuleEngine", () => {
80
77
  engine.add("hello", "First");
81
78
  engine.add("hello", "Second");
82
79
  const rule = engine.match(makeReq());
83
- expect(rule!.resolve).toBe("First");
80
+ if (!rule) throw new Error("expected match");
81
+ expect(rule.resolve).toBe("First");
84
82
  });
85
83
 
86
84
  it("returns undefined when no rules match", () => {
@@ -129,13 +127,17 @@ describe("RuleEngine", () => {
129
127
  (req) => req.lastMessage.includes("test"),
130
128
  "Handler reply",
131
129
  );
132
- expect(engine.match(makeReq({ lastMessage: "this is a test" }))).toBeDefined();
130
+ expect(
131
+ engine.match(makeReq({ lastMessage: "this is a test" })),
132
+ ).toBeDefined();
133
133
  });
134
134
 
135
135
  describe("toolName matching", () => {
136
136
  it("matches when toolNames includes the specified tool", () => {
137
137
  engine.add({ toolName: "get_weather" }, "Weather tool present");
138
- expect(engine.match(makeReq({ toolNames: ["get_weather", "search"] }))).toBeDefined();
138
+ expect(
139
+ engine.match(makeReq({ toolNames: ["get_weather", "search"] })),
140
+ ).toBeDefined();
139
141
  expect(engine.match(makeReq({ toolNames: ["search"] }))).toBeUndefined();
140
142
  });
141
143
  });
@@ -143,8 +145,12 @@ describe("RuleEngine", () => {
143
145
  describe("toolCallId matching", () => {
144
146
  it("matches when lastToolCallId equals the specified id", () => {
145
147
  engine.add({ toolCallId: "call_abc" }, "Tool result");
146
- expect(engine.match(makeReq({ lastToolCallId: "call_abc" }))).toBeDefined();
147
- expect(engine.match(makeReq({ lastToolCallId: "call_xyz" }))).toBeUndefined();
148
+ expect(
149
+ engine.match(makeReq({ lastToolCallId: "call_abc" })),
150
+ ).toBeDefined();
151
+ expect(
152
+ engine.match(makeReq({ lastToolCallId: "call_xyz" })),
153
+ ).toBeUndefined();
148
154
  expect(engine.match(makeReq())).toBeUndefined();
149
155
  });
150
156
  });
@@ -155,7 +161,8 @@ describe("RuleEngine", () => {
155
161
  const rule = engine.add("hello", "Second");
156
162
  engine.moveToFront(rule);
157
163
  const matched = engine.match(makeReq());
158
- expect(matched!.resolve).toBe("Second");
164
+ if (!matched) throw new Error("expected match");
165
+ expect(matched.resolve).toBe("Second");
159
166
  });
160
167
  });
161
168
 
@@ -165,23 +172,32 @@ describe("RuleEngine", () => {
165
172
  { model: "gpt-5.4", predicate: (req) => req.messages.length > 2 },
166
173
  "Complex match",
167
174
  );
168
- // Model matches but predicate fails (only 1 message)
169
175
  expect(engine.match(makeReq({ model: "gpt-5.4" }))).toBeUndefined();
170
- // Both model and predicate pass
171
- expect(engine.match(makeReq({
172
- model: "gpt-5.4",
173
- messages: [
174
- { role: "system", content: "sys" },
175
- { role: "user", content: "a" },
176
- { role: "assistant", content: "b" },
177
- ],
178
- }))).toBeDefined();
176
+
177
+ expect(
178
+ engine.match(
179
+ makeReq({
180
+ model: "gpt-5.4",
181
+ messages: [
182
+ { role: "system", content: "sys" },
183
+ { role: "user", content: "a" },
184
+ { role: "assistant", content: "b" },
185
+ ],
186
+ }),
187
+ ),
188
+ ).toBeDefined();
179
189
  });
180
190
 
181
191
  it("predicate runs after other fields (short-circuits)", () => {
182
192
  let called = false;
183
193
  engine.add(
184
- { model: "claude", predicate: () => { called = true; return true; } },
194
+ {
195
+ model: "claude",
196
+ predicate: () => {
197
+ called = true;
198
+ return true;
199
+ },
200
+ },
185
201
  "Never reached",
186
202
  );
187
203
  engine.match(makeReq({ model: "gpt-5.4" }));
@@ -1,4 +0,0 @@
1
- export type { FormatName, MockRequest, Message, ToolDef } from "./request.js";
2
- export type { Reply, ReplyObject, ErrorReply, ToolCall, Resolver, ReplyOptions, SequenceEntry } from "./reply.js";
3
- export type { Match, MatchObject, PendingRule, RuleHandle, RuleSummary, Handler, Rule } from "./rule.js";
4
- export type { RecordedRequest } from "../history.js";