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.
- package/README.md +1 -1
- package/dist/cli/cli.d.ts +3 -0
- package/dist/cli/cli.d.ts.map +1 -0
- package/dist/cli/cli.js +103 -0
- package/dist/cli/cli.js.map +1 -0
- package/dist/cli/validators.d.ts +7 -0
- package/dist/cli/validators.d.ts.map +1 -0
- package/dist/cli/validators.js +53 -0
- package/dist/cli/validators.js.map +1 -0
- package/dist/formats/anthropic/index.d.ts +1 -1
- package/dist/formats/anthropic/index.d.ts.map +1 -1
- package/dist/formats/anthropic/index.js +1 -1
- package/dist/formats/anthropic/index.js.map +1 -1
- package/dist/formats/anthropic/parse.d.ts +2 -2
- package/dist/formats/anthropic/parse.d.ts.map +1 -1
- package/dist/formats/anthropic/parse.js +4 -2
- package/dist/formats/anthropic/parse.js.map +1 -1
- package/dist/formats/anthropic/schema.d.ts +1 -1
- package/dist/formats/anthropic/schema.d.ts.map +1 -1
- package/dist/formats/anthropic/schema.js +9 -4
- package/dist/formats/anthropic/schema.js.map +1 -1
- package/dist/formats/anthropic/serialize.d.ts +2 -2
- package/dist/formats/anthropic/serialize.d.ts.map +1 -1
- package/dist/formats/anthropic/serialize.js +76 -19
- package/dist/formats/anthropic/serialize.js.map +1 -1
- package/dist/formats/openai/chat-completions/index.d.ts +3 -0
- package/dist/formats/openai/chat-completions/index.d.ts.map +1 -0
- package/dist/formats/openai/chat-completions/index.js +13 -0
- package/dist/formats/openai/chat-completions/index.js.map +1 -0
- package/dist/formats/openai/chat-completions/parse.d.ts +4 -0
- package/dist/formats/openai/chat-completions/parse.d.ts.map +1 -0
- package/dist/formats/openai/chat-completions/parse.js +33 -0
- package/dist/formats/openai/chat-completions/parse.js.map +1 -0
- package/dist/formats/openai/chat-completions/schema.d.ts +93 -0
- package/dist/formats/openai/chat-completions/schema.d.ts.map +1 -0
- package/dist/formats/openai/chat-completions/schema.js +74 -0
- package/dist/formats/openai/chat-completions/schema.js.map +1 -0
- package/dist/formats/openai/chat-completions/serialize.d.ts +10 -0
- package/dist/formats/openai/chat-completions/serialize.d.ts.map +1 -0
- package/dist/formats/openai/chat-completions/serialize.js +99 -0
- package/dist/formats/openai/chat-completions/serialize.js.map +1 -0
- package/dist/formats/openai/responses/index.d.ts +3 -0
- package/dist/formats/openai/responses/index.d.ts.map +1 -0
- package/dist/formats/openai/responses/index.js +13 -0
- package/dist/formats/openai/responses/index.js.map +1 -0
- package/dist/formats/openai/responses/parse.d.ts +4 -0
- package/dist/formats/openai/responses/parse.d.ts.map +1 -0
- package/dist/formats/openai/responses/parse.js +51 -0
- package/dist/formats/openai/responses/parse.js.map +1 -0
- package/dist/formats/openai/responses/schema.d.ts +103 -0
- package/dist/formats/openai/responses/schema.d.ts.map +1 -0
- package/dist/formats/openai/responses/schema.js +71 -0
- package/dist/formats/openai/responses/schema.js.map +1 -0
- package/dist/formats/openai/responses/serialize.d.ts +10 -0
- package/dist/formats/openai/responses/serialize.d.ts.map +1 -0
- package/dist/formats/openai/responses/serialize.js +273 -0
- package/dist/formats/openai/responses/serialize.js.map +1 -0
- package/dist/formats/request-helpers.d.ts +1 -1
- package/dist/formats/request-helpers.d.ts.map +1 -1
- package/dist/formats/request-helpers.js.map +1 -1
- package/dist/formats/serialize-helpers.d.ts +1 -1
- package/dist/formats/serialize-helpers.d.ts.map +1 -1
- package/dist/formats/serialize-helpers.js +6 -3
- package/dist/formats/serialize-helpers.js.map +1 -1
- package/dist/formats/types.d.ts +2 -1
- package/dist/formats/types.d.ts.map +1 -1
- package/dist/history.d.ts +6 -2
- package/dist/history.d.ts.map +1 -1
- package/dist/history.js +2 -0
- package/dist/history.js.map +1 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js.map +1 -1
- package/dist/loader.d.ts +1 -1
- package/dist/loader.d.ts.map +1 -1
- package/dist/loader.js +26 -9
- package/dist/loader.js.map +1 -1
- package/dist/logger.d.ts.map +1 -1
- package/dist/logger.js +12 -4
- package/dist/logger.js.map +1 -1
- package/dist/mock-server.d.ts +44 -48
- package/dist/mock-server.d.ts.map +1 -1
- package/dist/mock-server.js +37 -85
- package/dist/mock-server.js.map +1 -1
- package/dist/route-handler.d.ts +1 -1
- package/dist/route-handler.d.ts.map +1 -1
- package/dist/route-handler.js +19 -7
- package/dist/route-handler.js.map +1 -1
- package/dist/rule-builder.d.ts +21 -0
- package/dist/rule-builder.d.ts.map +1 -0
- package/dist/rule-builder.js +58 -0
- package/dist/rule-builder.js.map +1 -0
- package/dist/rule-engine.d.ts +3 -1
- package/dist/rule-engine.d.ts.map +1 -1
- package/dist/rule-engine.js +7 -2
- package/dist/rule-engine.js.map +1 -1
- package/dist/sse-writer.d.ts +1 -1
- package/dist/sse-writer.d.ts.map +1 -1
- package/dist/types/reply.d.ts +51 -8
- package/dist/types/reply.d.ts.map +1 -1
- package/dist/types/request.d.ts +21 -6
- package/dist/types/request.d.ts.map +1 -1
- package/dist/types/rule.d.ts +65 -7
- package/dist/types/rule.d.ts.map +1 -1
- package/dist/types.d.ts +3 -3
- package/dist/types.d.ts.map +1 -1
- package/package.json +20 -11
- package/.claude/skills/desloppify/SKILL.md +0 -308
- package/.desloppify/external_review_sessions/ext_20260315_000339_a6cdc3e6/canonical_import_20260315_000801.json +0 -242
- package/.desloppify/external_review_sessions/ext_20260315_000339_a6cdc3e6/canonical_import_20260315_000905.json +0 -248
- package/.desloppify/external_review_sessions/ext_20260315_000339_a6cdc3e6/canonical_import_20260315_000917.json +0 -248
- package/.desloppify/external_review_sessions/ext_20260315_000339_a6cdc3e6/canonical_import_20260315_000950.json +0 -311
- package/.desloppify/external_review_sessions/ext_20260315_000339_a6cdc3e6/claude_launch_prompt.md +0 -17
- package/.desloppify/external_review_sessions/ext_20260315_000339_a6cdc3e6/review_result.json +0 -255
- package/.desloppify/external_review_sessions/ext_20260315_000339_a6cdc3e6/review_result.template.json +0 -22
- package/.desloppify/external_review_sessions/ext_20260315_000339_a6cdc3e6/reviewer_instructions.md +0 -20
- package/.desloppify/external_review_sessions/ext_20260315_000339_a6cdc3e6/session.json +0 -20
- package/.desloppify/external_review_sessions/ext_20260315_045546_0587ea3b/canonical_import_20260315_050000.json +0 -286
- package/.desloppify/external_review_sessions/ext_20260315_045546_0587ea3b/canonical_import_20260315_050028.json +0 -303
- package/.desloppify/external_review_sessions/ext_20260315_045546_0587ea3b/claude_launch_prompt.md +0 -17
- package/.desloppify/external_review_sessions/ext_20260315_045546_0587ea3b/review_result.json +0 -297
- package/.desloppify/external_review_sessions/ext_20260315_045546_0587ea3b/review_result.template.json +0 -22
- package/.desloppify/external_review_sessions/ext_20260315_045546_0587ea3b/reviewer_instructions.md +0 -20
- package/.desloppify/external_review_sessions/ext_20260315_045546_0587ea3b/session.json +0 -20
- package/.desloppify/query.json +0 -1312
- package/.desloppify/review_packet_blind.json +0 -1249
- package/.desloppify/review_packets/holistic_packet_20260315_000339.json +0 -1471
- package/.desloppify/review_packets/holistic_packet_20260315_045546.json +0 -1480
- package/.desloppify/review_packets/holistic_packet_20260315_185401.json +0 -1407
- package/.desloppify/review_packets/holistic_packet_20260315_185613.json +0 -1407
- package/.desloppify/state-typescript.json +0 -8438
- package/.desloppify/state-typescript.json.bak +0 -8432
- package/.desloppify/subagents/runs/20260315_185401/logs/batch-1.log +0 -384
- package/.desloppify/subagents/runs/20260315_185401/logs/batch-10.log +0 -484
- package/.desloppify/subagents/runs/20260315_185401/logs/batch-2.log +0 -408
- package/.desloppify/subagents/runs/20260315_185401/logs/batch-3.log +0 -416
- package/.desloppify/subagents/runs/20260315_185401/logs/batch-4.log +0 -360
- package/.desloppify/subagents/runs/20260315_185401/logs/batch-5.log +0 -360
- package/.desloppify/subagents/runs/20260315_185401/logs/batch-6.log +0 -364
- package/.desloppify/subagents/runs/20260315_185401/logs/batch-7.log +0 -428
- package/.desloppify/subagents/runs/20260315_185401/logs/batch-8.log +0 -388
- package/.desloppify/subagents/runs/20260315_185401/logs/batch-9.log +0 -500
- package/.desloppify/subagents/runs/20260315_185401/prompts/batch-1.md +0 -83
- package/.desloppify/subagents/runs/20260315_185401/prompts/batch-10.md +0 -108
- package/.desloppify/subagents/runs/20260315_185401/prompts/batch-2.md +0 -89
- package/.desloppify/subagents/runs/20260315_185401/prompts/batch-3.md +0 -91
- package/.desloppify/subagents/runs/20260315_185401/prompts/batch-4.md +0 -77
- package/.desloppify/subagents/runs/20260315_185401/prompts/batch-5.md +0 -77
- package/.desloppify/subagents/runs/20260315_185401/prompts/batch-6.md +0 -78
- package/.desloppify/subagents/runs/20260315_185401/prompts/batch-7.md +0 -94
- package/.desloppify/subagents/runs/20260315_185401/prompts/batch-8.md +0 -84
- package/.desloppify/subagents/runs/20260315_185401/prompts/batch-9.md +0 -112
- package/.desloppify/subagents/runs/20260315_185401/results/batch-1.raw.txt +0 -0
- package/.desloppify/subagents/runs/20260315_185401/results/batch-10.raw.txt +0 -0
- package/.desloppify/subagents/runs/20260315_185401/results/batch-2.raw.txt +0 -0
- package/.desloppify/subagents/runs/20260315_185401/results/batch-3.raw.txt +0 -0
- package/.desloppify/subagents/runs/20260315_185401/results/batch-4.raw.txt +0 -0
- package/.desloppify/subagents/runs/20260315_185401/results/batch-5.raw.txt +0 -0
- package/.desloppify/subagents/runs/20260315_185401/results/batch-6.raw.txt +0 -0
- package/.desloppify/subagents/runs/20260315_185401/results/batch-7.raw.txt +0 -0
- package/.desloppify/subagents/runs/20260315_185401/results/batch-8.raw.txt +0 -0
- package/.desloppify/subagents/runs/20260315_185401/results/batch-9.raw.txt +0 -0
- package/.desloppify/subagents/runs/20260315_185401/run.log +0 -36
- package/.desloppify/subagents/runs/20260315_185401/run_summary.json +0 -156
- package/.desloppify/subagents/runs/20260315_185613/holistic_findings_merged.json +0 -741
- package/.desloppify/subagents/runs/20260315_185613/logs/batch-1.log +0 -579
- package/.desloppify/subagents/runs/20260315_185613/logs/batch-10.log +0 -1537
- package/.desloppify/subagents/runs/20260315_185613/logs/batch-2.log +0 -829
- package/.desloppify/subagents/runs/20260315_185613/logs/batch-3.log +0 -927
- package/.desloppify/subagents/runs/20260315_185613/logs/batch-4.log +0 -429
- package/.desloppify/subagents/runs/20260315_185613/logs/batch-5.log +0 -276
- package/.desloppify/subagents/runs/20260315_185613/logs/batch-6.log +0 -450
- package/.desloppify/subagents/runs/20260315_185613/logs/batch-7.log +0 -730
- package/.desloppify/subagents/runs/20260315_185613/logs/batch-8.log +0 -698
- package/.desloppify/subagents/runs/20260315_185613/logs/batch-9.log +0 -938
- package/.desloppify/subagents/runs/20260315_185613/prompts/batch-1.md +0 -83
- package/.desloppify/subagents/runs/20260315_185613/prompts/batch-10.md +0 -108
- package/.desloppify/subagents/runs/20260315_185613/prompts/batch-2.md +0 -89
- package/.desloppify/subagents/runs/20260315_185613/prompts/batch-3.md +0 -91
- package/.desloppify/subagents/runs/20260315_185613/prompts/batch-4.md +0 -77
- package/.desloppify/subagents/runs/20260315_185613/prompts/batch-5.md +0 -77
- package/.desloppify/subagents/runs/20260315_185613/prompts/batch-6.md +0 -78
- package/.desloppify/subagents/runs/20260315_185613/prompts/batch-7.md +0 -94
- package/.desloppify/subagents/runs/20260315_185613/prompts/batch-8.md +0 -84
- package/.desloppify/subagents/runs/20260315_185613/prompts/batch-9.md +0 -112
- package/.desloppify/subagents/runs/20260315_185613/results/batch-1.raw.txt +0 -78
- package/.desloppify/subagents/runs/20260315_185613/results/batch-10.raw.txt +0 -242
- package/.desloppify/subagents/runs/20260315_185613/results/batch-2.raw.txt +0 -102
- package/.desloppify/subagents/runs/20260315_185613/results/batch-3.raw.txt +0 -94
- package/.desloppify/subagents/runs/20260315_185613/results/batch-4.raw.txt +0 -86
- package/.desloppify/subagents/runs/20260315_185613/results/batch-5.raw.txt +0 -1
- package/.desloppify/subagents/runs/20260315_185613/results/batch-6.raw.txt +0 -87
- package/.desloppify/subagents/runs/20260315_185613/results/batch-7.raw.txt +0 -1
- package/.desloppify/subagents/runs/20260315_185613/results/batch-8.raw.txt +0 -107
- package/.desloppify/subagents/runs/20260315_185613/results/batch-9.raw.txt +0 -67
- package/.desloppify/subagents/runs/20260315_185613/run.log +0 -96
- package/.desloppify/subagents/runs/20260315_185613/run_summary.json +0 -156
- package/.editorconfig +0 -12
- package/.github/dependabot.yml +0 -11
- package/.github/workflows/docs.yml +0 -46
- package/.github/workflows/test.yml +0 -40
- package/.markdownlint.jsonc +0 -11
- package/.node-version +0 -1
- package/.oxfmtrc.json +0 -9
- package/.oxlintrc.json +0 -35
- package/docs/ARCHITECTURE.md +0 -125
- package/scorecard.png +0 -0
- package/src/cli/cli.ts +0 -141
- package/src/cli/validators.ts +0 -68
- package/src/formats/anthropic/index.ts +0 -14
- package/src/formats/anthropic/parse.ts +0 -67
- package/src/formats/anthropic/schema.ts +0 -74
- package/src/formats/anthropic/serialize.ts +0 -179
- package/src/formats/openai/chat-completions/index.ts +0 -14
- package/src/formats/openai/chat-completions/parse.ts +0 -44
- package/src/formats/openai/chat-completions/schema.ts +0 -92
- package/src/formats/openai/chat-completions/serialize.ts +0 -146
- package/src/formats/openai/responses/index.ts +0 -14
- package/src/formats/openai/responses/parse.ts +0 -70
- package/src/formats/openai/responses/schema.ts +0 -86
- package/src/formats/openai/responses/serialize.ts +0 -332
- package/src/formats/request-helpers.ts +0 -56
- package/src/formats/serialize-helpers.ts +0 -43
- package/src/formats/types.ts +0 -26
- package/src/history.ts +0 -70
- package/src/index.ts +0 -46
- package/src/loader.ts +0 -246
- package/src/logger.ts +0 -70
- package/src/mock-server.ts +0 -203
- package/src/route-handler.ts +0 -144
- package/src/rule-builder.ts +0 -73
- package/src/rule-engine.ts +0 -165
- package/src/sse-writer.ts +0 -35
- package/src/types/reply.ts +0 -92
- package/src/types/request.ts +0 -56
- package/src/types/rule.ts +0 -125
- package/src/types.ts +0 -24
- package/test/cli-validators.test.ts +0 -151
- package/test/formats/anthropic.test.ts +0 -336
- package/test/formats/openai.test.ts +0 -316
- package/test/formats/parse-helpers.test.ts +0 -315
- package/test/formats/responses.test.ts +0 -380
- package/test/helpers/make-req.ts +0 -18
- package/test/history.test.ts +0 -361
- package/test/loader.test.ts +0 -333
- package/test/logger.test.ts +0 -344
- package/test/mock-server.test.ts +0 -619
- package/test/rule-engine.test.ts +0 -229
- package/tsconfig.json +0 -26
- package/tsconfig.test.json +0 -11
- package/typedoc.json +0 -9
- package/vitest.config.ts +0 -18
|
@@ -1,248 +0,0 @@
|
|
|
1
|
-
{
|
|
2
|
-
"assessments": {
|
|
3
|
-
"cross_module_architecture": 92.0,
|
|
4
|
-
"convention_outlier": 88.5,
|
|
5
|
-
"error_consistency": 84.0,
|
|
6
|
-
"abstraction_fitness": 93.0,
|
|
7
|
-
"api_surface_coherence": 86.0,
|
|
8
|
-
"authorization_consistency": 100.0,
|
|
9
|
-
"ai_generated_debt": 82.0,
|
|
10
|
-
"incomplete_migration": 97.0,
|
|
11
|
-
"package_organization": 93.0,
|
|
12
|
-
"high_level_elegance": 93.0,
|
|
13
|
-
"mid_level_elegance": 90.0,
|
|
14
|
-
"low_level_elegance": 87.0,
|
|
15
|
-
"design_coherence": 84.0
|
|
16
|
-
},
|
|
17
|
-
"dimension_notes": {
|
|
18
|
-
"abstraction_fitness": "Lean, earned abstractions with no wrapper bloat. The Format interface is the sole polymorphic boundary and is well-justified. Minor: parse-helpers.ts serves as a utility grab-bag spanning both parse and serialize concerns, which is a slight abstraction seam misalignment (-7).",
|
|
19
|
-
"high_level_elegance": "Clear domain-driven decomposition into formats, types, rule-engine, and server. Each directory owns a vertical slice. Minor: the types barrel re-export chain (types/*.ts -> types.ts -> index.ts) adds a navigational hop that slightly obscures ownership (-7).",
|
|
20
|
-
"incomplete_migration": "Single-tech stack (TypeScript/ESM throughout), no legacy runtime remnants. Minor: cli.ts still hardcodes version string 'v1.0.0' rather than reading from package.json (-3).",
|
|
21
|
-
"package_organization": "Consistent format subdirectories (index/parse/serialize/schema per format), clean separation of concerns. Minor: parse-helpers.ts serves both parse and serialize modules from a shared location, blurring the directional boundary slightly (-7)."
|
|
22
|
-
},
|
|
23
|
-
"findings": [
|
|
24
|
-
{
|
|
25
|
-
"dimension": "cross_module_architecture",
|
|
26
|
-
"identifier": "types_barrel_reexport_indirection",
|
|
27
|
-
"summary": "src/types.ts is a pure re-export barrel with 19 importers, adding an unnecessary indirection hop",
|
|
28
|
-
"related_files": [
|
|
29
|
-
"src/types.ts",
|
|
30
|
-
"src/types/request.ts",
|
|
31
|
-
"src/types/reply.ts",
|
|
32
|
-
"src/types/rule.ts"
|
|
33
|
-
],
|
|
34
|
-
"evidence": [
|
|
35
|
-
"src/types.ts has 19 importers (highest in the codebase) and consists of exactly 3 re-export lines with zero logic or value-add",
|
|
36
|
-
"Both src/ modules and src/formats/ modules import from src/types.ts rather than from the actual definition files in src/types/",
|
|
37
|
-
"This creates a hub module that any type change routes through, inflating apparent coupling"
|
|
38
|
-
],
|
|
39
|
-
"suggestion": "This is borderline since barrel exports are common in TypeScript libraries, but the dual-layer (src/types.ts re-exporting from src/types/*.ts, plus src/index.ts re-exporting again) adds navigational friction. Consider having internal modules import directly from src/types/request.ts, src/types/reply.ts, src/types/rule.ts and reserving src/types.ts only for the public API surface.",
|
|
40
|
-
"confidence": "low"
|
|
41
|
-
},
|
|
42
|
-
{
|
|
43
|
-
"dimension": "convention_outlier",
|
|
44
|
-
"identifier": "makeReq_helper_inconsistent_fields",
|
|
45
|
-
"summary": "Test helper makeReq() has inconsistent field sets across test files -- some include headers/path, others do not",
|
|
46
|
-
"related_files": [
|
|
47
|
-
"test/rule-engine.test.ts",
|
|
48
|
-
"test/history.test.ts",
|
|
49
|
-
"test/loader.test.ts"
|
|
50
|
-
],
|
|
51
|
-
"evidence": [
|
|
52
|
-
"test/rule-engine.test.ts makeReq() omits headers and path fields entirely",
|
|
53
|
-
"test/history.test.ts and test/loader.test.ts makeReq() includes headers: {} and path: '/v1/chat/completions'",
|
|
54
|
-
"This means rule-engine tests create MockRequest objects that are structurally incomplete versus the interface defined in src/types/request.ts which has required headers and path fields"
|
|
55
|
-
],
|
|
56
|
-
"suggestion": "Extract a shared test factory into a test/helpers/ module (e.g., test/helpers/make-req.ts) that always provides all required MockRequest fields. Import it in all test files to ensure consistency and prevent type drift.",
|
|
57
|
-
"confidence": "medium"
|
|
58
|
-
},
|
|
59
|
-
{
|
|
60
|
-
"dimension": "error_consistency",
|
|
61
|
-
"identifier": "resolver_error_swallowed_silently",
|
|
62
|
-
"summary": "When a resolver function throws, the error is logged but the server silently returns the fallback reply with 200 OK",
|
|
63
|
-
"related_files": [
|
|
64
|
-
"src/route-handler.ts"
|
|
65
|
-
],
|
|
66
|
-
"evidence": [
|
|
67
|
-
"In resolveReply() at lines 34-37: catch (err) logs the error then returns the fallback reply with ruleDesc still set, giving no signal to the caller that something went wrong",
|
|
68
|
-
"The matched rule's remaining count was already decremented (line 95 in rule-engine.ts) before the resolver was called, so the rule is consumed even on failure",
|
|
69
|
-
"This means a resolver that intermittently throws will consume its match count, return a fallback, and give the test client no indication that the intended reply was not produced"
|
|
70
|
-
],
|
|
71
|
-
"suggestion": "Consider making resolver errors more visible: either (a) propagate the error as an error reply (e.g., 500 status) so callers can detect it, or (b) at minimum, record the error in the history entry so test assertions can detect resolver failures. The current silent fallback makes debugging flaky test setups difficult.",
|
|
72
|
-
"confidence": "medium"
|
|
73
|
-
},
|
|
74
|
-
{
|
|
75
|
-
"dimension": "error_consistency",
|
|
76
|
-
"identifier": "cli_validators_mixed_sync_async",
|
|
77
|
-
"summary": "parseHost is async while all sibling validators (parsePort, parseLogLevel, parseChunkSize, parseLatency) are synchronous",
|
|
78
|
-
"related_files": [
|
|
79
|
-
"src/cli-validators.ts"
|
|
80
|
-
],
|
|
81
|
-
"evidence": [
|
|
82
|
-
"parseHost performs DNS lookup via await lookup(value) making it the only async function among 5 sibling validators",
|
|
83
|
-
"The caller in cli.ts must use await for parseHost but not for the others, creating an asymmetric call pattern",
|
|
84
|
-
"The api_surface holistic_context explicitly flags src/cli-validators.ts as having a sync_async_mix"
|
|
85
|
-
],
|
|
86
|
-
"suggestion": "This is inherent to the DNS lookup requirement, but document it explicitly with a JSDoc comment explaining why parseHost is async while siblings are sync. Alternatively, consider making all validators async for a uniform API, or doing the DNS check at server.start() time rather than at parse time.",
|
|
87
|
-
"confidence": "low"
|
|
88
|
-
},
|
|
89
|
-
{
|
|
90
|
-
"dimension": "ai_generated_debt",
|
|
91
|
-
"identifier": "type_files_high_comment_ratio",
|
|
92
|
-
"summary": "Type definition files have 35-38% comment ratio vs 5% codebase average -- JSDoc comments restate obvious field names",
|
|
93
|
-
"related_files": [
|
|
94
|
-
"src/types/reply.ts",
|
|
95
|
-
"src/types/request.ts",
|
|
96
|
-
"src/types/rule.ts"
|
|
97
|
-
],
|
|
98
|
-
"evidence": [
|
|
99
|
-
"src/types/reply.ts: '/** Text content to send back. */' on a field named 'text' (line 9)",
|
|
100
|
-
"src/types/reply.ts: '/** Tool calls the model wants to make. */' on a field named 'tools' (line 13)",
|
|
101
|
-
"src/types/request.ts: '/** The last user message text. This is what most matchers check. */' -- first sentence restates the name, second adds genuine value (line 14)",
|
|
102
|
-
"src/types/rule.ts: '/** Human-readable description of what the rule matches. */' on a field named 'description' (line 63)",
|
|
103
|
-
"Codebase average comment ratio is 0.05 (5%), these files are at 0.35-0.38 (35-38%)"
|
|
104
|
-
],
|
|
105
|
-
"suggestion": "Keep comments that add non-obvious context (like 'This is what most matchers check' or 'Falls back to { input: 10, output: 5 } if omitted') but remove comments that merely restate the type+name (e.g., '/** Text content to send back. */' on a field called text?: string). A good rule: if deleting the comment loses zero information beyond what the name+type already convey, delete it.",
|
|
106
|
-
"confidence": "medium"
|
|
107
|
-
},
|
|
108
|
-
{
|
|
109
|
-
"dimension": "ai_generated_debt",
|
|
110
|
-
"identifier": "logger_high_log_density",
|
|
111
|
-
"summary": "Logger class has a log density of 4.0 -- every method is a formatting wrapper around console with identical structure",
|
|
112
|
-
"related_files": [
|
|
113
|
-
"src/logger.ts"
|
|
114
|
-
],
|
|
115
|
-
"evidence": [
|
|
116
|
-
"All four methods (error, warn, info, debug) follow an identical pattern: check threshold, destructure style, format with template literal, call console",
|
|
117
|
-
"The only variation between methods is which LEVEL_STYLE entry and which console method is used",
|
|
118
|
-
"This repetitive structure is a hallmark of AI-generated code where each method is written independently rather than factored"
|
|
119
|
-
],
|
|
120
|
-
"suggestion": "Extract a private _log(level, consoleFn, msg, args) method that contains the shared threshold-check and formatting logic. Each public method becomes a one-liner delegating to _log with the appropriate level key and console method. This reduces the four near-identical 5-line methods to four 1-line methods plus one shared implementation.",
|
|
121
|
-
"confidence": "medium"
|
|
122
|
-
},
|
|
123
|
-
{
|
|
124
|
-
"dimension": "design_coherence",
|
|
125
|
-
"identifier": "openai_serialize_usage_duplication",
|
|
126
|
-
"summary": "Usage object construction is duplicated verbatim between serialize() and serializeComplete() in all three format serializers",
|
|
127
|
-
"related_files": [
|
|
128
|
-
"src/formats/openai/serialize.ts",
|
|
129
|
-
"src/formats/anthropic/serialize.ts",
|
|
130
|
-
"src/formats/responses/serialize.ts"
|
|
131
|
-
],
|
|
132
|
-
"evidence": [
|
|
133
|
-
"OpenAI serialize.ts lines 41-47 and lines 80-86 construct identical prompt_tokens_details and completion_tokens_details objects",
|
|
134
|
-
"Anthropic serialize.ts lines 55 and 85 both construct { input_tokens: usage.input, output_tokens: usage.output }",
|
|
135
|
-
"Responses serialize.ts lines 98 and 123 both construct { input_tokens: usage.input, output_tokens: usage.output, total_tokens: usage.input + usage.output }",
|
|
136
|
-
"Each format repeats its usage construction logic in both the streaming and non-streaming serialization paths"
|
|
137
|
-
],
|
|
138
|
-
"suggestion": "Extract format-specific usage builders into small helpers within each serializer file. For example in openai/serialize.ts, add a buildUsage(usage) function that returns the full usage object with details, then call it from both serialize() and serializeComplete(). This eliminates the duplicated object literals.",
|
|
139
|
-
"confidence": "high"
|
|
140
|
-
},
|
|
141
|
-
{
|
|
142
|
-
"dimension": "design_coherence",
|
|
143
|
-
"identifier": "replySequence_duplicated_in_loader_and_server",
|
|
144
|
-
"summary": "Sequence replay logic (index tracking, last-entry fallback, options mutation) is duplicated between MockServer.when().replySequence() and loader.ts addSequenceRule()",
|
|
145
|
-
"related_files": [
|
|
146
|
-
"src/mock-server.ts",
|
|
147
|
-
"src/loader.ts"
|
|
148
|
-
],
|
|
149
|
-
"evidence": [
|
|
150
|
-
"mock-server.ts lines 107-121: replySequence creates a closure with index++, entries[index] ?? last, and mutates rule.options",
|
|
151
|
-
"loader.ts lines 102-129: addSequenceRule creates a nearly identical closure with index++, resolved[index] ?? lastStep, and mutates rule.options",
|
|
152
|
-
"Both implementations also set rule.remaining = entries.length"
|
|
153
|
-
],
|
|
154
|
-
"suggestion": "Extract a createSequenceResolver(entries) function into a shared location (e.g., rule-engine.ts or a new sequence-helpers.ts) that returns { resolver, entryCount }. Both mock-server.ts replySequence() and loader.ts addSequenceRule() can call this shared function, eliminating the duplicated closure logic.",
|
|
155
|
-
"confidence": "high"
|
|
156
|
-
},
|
|
157
|
-
{
|
|
158
|
-
"dimension": "low_level_elegance",
|
|
159
|
-
"identifier": "cli_start_function_mixed_concerns",
|
|
160
|
-
"summary": "The cli.ts start() function mixes server setup, banner printing, file watching, and signal handling in a single 80-line function",
|
|
161
|
-
"related_files": [
|
|
162
|
-
"src/cli.ts"
|
|
163
|
-
],
|
|
164
|
-
"evidence": [
|
|
165
|
-
"Lines 24-104: start() handles option parsing, server construction, rule/handler loading, fallback setup, banner printing, fs.watch setup with debouncing, and SIGINT/SIGTERM handlers",
|
|
166
|
-
"The file watch setup (lines 70-89) is nested inside start() with its own state (reloading flag, setTimeout)",
|
|
167
|
-
"Signal handlers (lines 91-103) are also wired inline"
|
|
168
|
-
],
|
|
169
|
-
"suggestion": "Extract the file-watch setup into a watchRulesPath(server, rulesPath, fallback, logger) function, and the signal handling into a setupShutdown(server, logger) function. The start() function should read as a linear composition of setup steps rather than containing all the implementation details inline.",
|
|
170
|
-
"confidence": "medium"
|
|
171
|
-
},
|
|
172
|
-
{
|
|
173
|
-
"dimension": "low_level_elegance",
|
|
174
|
-
"identifier": "responses_serialize_deeply_nested_object_literals",
|
|
175
|
-
"summary": "responses/serialize.ts builds deeply nested inline object literals making the streaming event structure hard to read",
|
|
176
|
-
"related_files": [
|
|
177
|
-
"src/formats/responses/serialize.ts"
|
|
178
|
-
],
|
|
179
|
-
"evidence": [
|
|
180
|
-
"Line 42-43: textStreamBlock constructs a 3-level deep inline object: { type: 'response.output_item.added', output_index: i, item: { type: 'message', id: itemId, status: 'in_progress', role: 'assistant', content: [] } }",
|
|
181
|
-
"Line 62: toolStreamBlock spreads an outputItem into a modified copy inline: { ...outputItem, status: 'in_progress', arguments: '' }",
|
|
182
|
-
"These long inline constructions make it difficult to scan the event structure at a glance"
|
|
183
|
-
],
|
|
184
|
-
"suggestion": "Extract the repeated item shapes into named local variables or small builder functions. For example, const inProgressItem = { ...outputItem, status: 'in_progress', arguments: '' } on a separate line before passing it to c(). This improves scanability without adding abstraction overhead.",
|
|
185
|
-
"confidence": "low"
|
|
186
|
-
},
|
|
187
|
-
{
|
|
188
|
-
"dimension": "api_surface_coherence",
|
|
189
|
-
"identifier": "format_serialize_return_type_unknown",
|
|
190
|
-
"summary": "serializeComplete and serializeError return 'unknown' in the Format interface, losing type information at the boundary",
|
|
191
|
-
"related_files": [
|
|
192
|
-
"src/formats/types.ts",
|
|
193
|
-
"src/formats/openai/serialize.ts",
|
|
194
|
-
"src/formats/anthropic/serialize.ts",
|
|
195
|
-
"src/formats/responses/serialize.ts"
|
|
196
|
-
],
|
|
197
|
-
"evidence": [
|
|
198
|
-
"Format interface line 15: serializeComplete returns 'unknown'",
|
|
199
|
-
"Format interface line 16: serializeError returns 'unknown'",
|
|
200
|
-
"Each concrete implementation also returns 'unknown' rather than a typed object",
|
|
201
|
-
"Callers in route-handler.ts pass the result directly to reply.send() without any type checking"
|
|
202
|
-
],
|
|
203
|
-
"suggestion": "Since these are JSON response bodies sent to the client, at minimum type them as Record<string, unknown> or JsonValue. Ideally, use a generic Format<TComplete, TError> or define a union type covering the three format response shapes. This prevents accidental primitive returns and improves IDE support for callers.",
|
|
204
|
-
"confidence": "medium"
|
|
205
|
-
},
|
|
206
|
-
{
|
|
207
|
-
"dimension": "mid_level_elegance",
|
|
208
|
-
"identifier": "route_handler_history_records_before_streaming",
|
|
209
|
-
"summary": "History records the request before the streaming response is written, so history.count() may increment before the client has received any data",
|
|
210
|
-
"related_files": [
|
|
211
|
-
"src/route-handler.ts"
|
|
212
|
-
],
|
|
213
|
-
"evidence": [
|
|
214
|
-
"Line 89: history.record(mockReq, ruleDesc) is called unconditionally before the streaming/non-streaming branch",
|
|
215
|
-
"Line 85: For error replies, history.record is called before returning the error response",
|
|
216
|
-
"If writeSSE (line 111) fails mid-stream, the history already recorded the request as handled"
|
|
217
|
-
],
|
|
218
|
-
"suggestion": "This is a minor timing issue but worth acknowledging. Consider recording the history entry after the response is fully written (after writeSSE completes or after reply.send() returns). Alternatively, add a 'status' field to RecordedRequest indicating whether the response was successfully delivered.",
|
|
219
|
-
"confidence": "low"
|
|
220
|
-
},
|
|
221
|
-
{
|
|
222
|
-
"dimension": "convention_outlier",
|
|
223
|
-
"identifier": "parse_helpers_mixed_responsibility",
|
|
224
|
-
"summary": "parse-helpers.ts mixes request-building utilities (buildMockRequest) with serialization utilities (splitText, genId, toolId, finishReason, shouldEmitText)",
|
|
225
|
-
"related_files": [
|
|
226
|
-
"src/formats/parse-helpers.ts"
|
|
227
|
-
],
|
|
228
|
-
"evidence": [
|
|
229
|
-
"buildMockRequest (line 53) is used only by parse.ts files for constructing MockRequest objects from incoming requests",
|
|
230
|
-
"splitText, genId, toolId, shouldEmitText, finishReason are used only by serialize.ts files for constructing outgoing responses",
|
|
231
|
-
"isStreaming (line 37) is used by both parsing (index.ts) and is a third concern (request detection)",
|
|
232
|
-
"The file has 11 importers across very different use contexts"
|
|
233
|
-
],
|
|
234
|
-
"suggestion": "Split parse-helpers.ts into two files: serialize-helpers.ts (for splitText, genId, toolId, shouldEmitText, finishReason, DEFAULT_USAGE, MS_PER_SECOND) and request-helpers.ts (for buildMockRequest, isStreaming, RequestMeta). This aligns each file with a single direction of data flow.",
|
|
235
|
-
"confidence": "medium"
|
|
236
|
-
}
|
|
237
|
-
],
|
|
238
|
-
"provenance": {
|
|
239
|
-
"kind": "blind_review_batch_import",
|
|
240
|
-
"blind": true,
|
|
241
|
-
"runner": "claude",
|
|
242
|
-
"run_stamp": "ext_20260315_000339_a6cdc3e6",
|
|
243
|
-
"created_at": "2026-03-15T00:09:05+00:00",
|
|
244
|
-
"packet_path": "/Users/suyash.x.srijan/Documents/Personal_Projects/llm-mock-server/.desloppify/review_packet_blind.json",
|
|
245
|
-
"packet_sha256": "61563983650626757ab21c4b1e8ea4db0d723c4ce1fde659f2bbff0a59e56044",
|
|
246
|
-
"external_session_id": "ext_20260315_000339_a6cdc3e6"
|
|
247
|
-
}
|
|
248
|
-
}
|
|
@@ -1,248 +0,0 @@
|
|
|
1
|
-
{
|
|
2
|
-
"assessments": {
|
|
3
|
-
"cross_module_architecture": 92.0,
|
|
4
|
-
"convention_outlier": 88.5,
|
|
5
|
-
"error_consistency": 84.0,
|
|
6
|
-
"abstraction_fitness": 93.0,
|
|
7
|
-
"api_surface_coherence": 86.0,
|
|
8
|
-
"authorization_consistency": 100.0,
|
|
9
|
-
"ai_generated_debt": 82.0,
|
|
10
|
-
"incomplete_migration": 97.0,
|
|
11
|
-
"package_organization": 93.0,
|
|
12
|
-
"high_level_elegance": 93.0,
|
|
13
|
-
"mid_level_elegance": 90.0,
|
|
14
|
-
"low_level_elegance": 87.0,
|
|
15
|
-
"design_coherence": 84.0
|
|
16
|
-
},
|
|
17
|
-
"dimension_notes": {
|
|
18
|
-
"abstraction_fitness": "Lean, earned abstractions with no wrapper bloat. The Format interface is the sole polymorphic boundary and is well-justified. Minor: parse-helpers.ts serves as a utility grab-bag spanning both parse and serialize concerns, which is a slight abstraction seam misalignment (-7).",
|
|
19
|
-
"high_level_elegance": "Clear domain-driven decomposition into formats, types, rule-engine, and server. Each directory owns a vertical slice. Minor: the types barrel re-export chain (types/*.ts -> types.ts -> index.ts) adds a navigational hop that slightly obscures ownership (-7).",
|
|
20
|
-
"incomplete_migration": "Single-tech stack (TypeScript/ESM throughout), no legacy runtime remnants. Minor: cli.ts still hardcodes version string 'v1.0.0' rather than reading from package.json (-3).",
|
|
21
|
-
"package_organization": "Consistent format subdirectories (index/parse/serialize/schema per format), clean separation of concerns. Minor: parse-helpers.ts serves both parse and serialize modules from a shared location, blurring the directional boundary slightly (-7)."
|
|
22
|
-
},
|
|
23
|
-
"findings": [
|
|
24
|
-
{
|
|
25
|
-
"dimension": "cross_module_architecture",
|
|
26
|
-
"identifier": "types_barrel_reexport_indirection",
|
|
27
|
-
"summary": "src/types.ts is a pure re-export barrel with 19 importers, adding an unnecessary indirection hop",
|
|
28
|
-
"related_files": [
|
|
29
|
-
"src/types.ts",
|
|
30
|
-
"src/types/request.ts",
|
|
31
|
-
"src/types/reply.ts",
|
|
32
|
-
"src/types/rule.ts"
|
|
33
|
-
],
|
|
34
|
-
"evidence": [
|
|
35
|
-
"src/types.ts has 19 importers (highest in the codebase) and consists of exactly 3 re-export lines with zero logic or value-add",
|
|
36
|
-
"Both src/ modules and src/formats/ modules import from src/types.ts rather than from the actual definition files in src/types/",
|
|
37
|
-
"This creates a hub module that any type change routes through, inflating apparent coupling"
|
|
38
|
-
],
|
|
39
|
-
"suggestion": "This is borderline since barrel exports are common in TypeScript libraries, but the dual-layer (src/types.ts re-exporting from src/types/*.ts, plus src/index.ts re-exporting again) adds navigational friction. Consider having internal modules import directly from src/types/request.ts, src/types/reply.ts, src/types/rule.ts and reserving src/types.ts only for the public API surface.",
|
|
40
|
-
"confidence": "low"
|
|
41
|
-
},
|
|
42
|
-
{
|
|
43
|
-
"dimension": "convention_outlier",
|
|
44
|
-
"identifier": "makeReq_helper_inconsistent_fields",
|
|
45
|
-
"summary": "Test helper makeReq() has inconsistent field sets across test files -- some include headers/path, others do not",
|
|
46
|
-
"related_files": [
|
|
47
|
-
"test/rule-engine.test.ts",
|
|
48
|
-
"test/history.test.ts",
|
|
49
|
-
"test/loader.test.ts"
|
|
50
|
-
],
|
|
51
|
-
"evidence": [
|
|
52
|
-
"test/rule-engine.test.ts makeReq() omits headers and path fields entirely",
|
|
53
|
-
"test/history.test.ts and test/loader.test.ts makeReq() includes headers: {} and path: '/v1/chat/completions'",
|
|
54
|
-
"This means rule-engine tests create MockRequest objects that are structurally incomplete versus the interface defined in src/types/request.ts which has required headers and path fields"
|
|
55
|
-
],
|
|
56
|
-
"suggestion": "Extract a shared test factory into a test/helpers/ module (e.g., test/helpers/make-req.ts) that always provides all required MockRequest fields. Import it in all test files to ensure consistency and prevent type drift.",
|
|
57
|
-
"confidence": "medium"
|
|
58
|
-
},
|
|
59
|
-
{
|
|
60
|
-
"dimension": "error_consistency",
|
|
61
|
-
"identifier": "resolver_error_swallowed_silently",
|
|
62
|
-
"summary": "When a resolver function throws, the error is logged but the server silently returns the fallback reply with 200 OK",
|
|
63
|
-
"related_files": [
|
|
64
|
-
"src/route-handler.ts"
|
|
65
|
-
],
|
|
66
|
-
"evidence": [
|
|
67
|
-
"In resolveReply() at lines 34-37: catch (err) logs the error then returns the fallback reply with ruleDesc still set, giving no signal to the caller that something went wrong",
|
|
68
|
-
"The matched rule's remaining count was already decremented (line 95 in rule-engine.ts) before the resolver was called, so the rule is consumed even on failure",
|
|
69
|
-
"This means a resolver that intermittently throws will consume its match count, return a fallback, and give the test client no indication that the intended reply was not produced"
|
|
70
|
-
],
|
|
71
|
-
"suggestion": "Consider making resolver errors more visible: either (a) propagate the error as an error reply (e.g., 500 status) so callers can detect it, or (b) at minimum, record the error in the history entry so test assertions can detect resolver failures. The current silent fallback makes debugging flaky test setups difficult.",
|
|
72
|
-
"confidence": "medium"
|
|
73
|
-
},
|
|
74
|
-
{
|
|
75
|
-
"dimension": "error_consistency",
|
|
76
|
-
"identifier": "cli_validators_mixed_sync_async",
|
|
77
|
-
"summary": "parseHost is async while all sibling validators (parsePort, parseLogLevel, parseChunkSize, parseLatency) are synchronous",
|
|
78
|
-
"related_files": [
|
|
79
|
-
"src/cli-validators.ts"
|
|
80
|
-
],
|
|
81
|
-
"evidence": [
|
|
82
|
-
"parseHost performs DNS lookup via await lookup(value) making it the only async function among 5 sibling validators",
|
|
83
|
-
"The caller in cli.ts must use await for parseHost but not for the others, creating an asymmetric call pattern",
|
|
84
|
-
"The api_surface holistic_context explicitly flags src/cli-validators.ts as having a sync_async_mix"
|
|
85
|
-
],
|
|
86
|
-
"suggestion": "This is inherent to the DNS lookup requirement, but document it explicitly with a JSDoc comment explaining why parseHost is async while siblings are sync. Alternatively, consider making all validators async for a uniform API, or doing the DNS check at server.start() time rather than at parse time.",
|
|
87
|
-
"confidence": "low"
|
|
88
|
-
},
|
|
89
|
-
{
|
|
90
|
-
"dimension": "ai_generated_debt",
|
|
91
|
-
"identifier": "type_files_high_comment_ratio",
|
|
92
|
-
"summary": "Type definition files have 35-38% comment ratio vs 5% codebase average -- JSDoc comments restate obvious field names",
|
|
93
|
-
"related_files": [
|
|
94
|
-
"src/types/reply.ts",
|
|
95
|
-
"src/types/request.ts",
|
|
96
|
-
"src/types/rule.ts"
|
|
97
|
-
],
|
|
98
|
-
"evidence": [
|
|
99
|
-
"src/types/reply.ts: '/** Text content to send back. */' on a field named 'text' (line 9)",
|
|
100
|
-
"src/types/reply.ts: '/** Tool calls the model wants to make. */' on a field named 'tools' (line 13)",
|
|
101
|
-
"src/types/request.ts: '/** The last user message text. This is what most matchers check. */' -- first sentence restates the name, second adds genuine value (line 14)",
|
|
102
|
-
"src/types/rule.ts: '/** Human-readable description of what the rule matches. */' on a field named 'description' (line 63)",
|
|
103
|
-
"Codebase average comment ratio is 0.05 (5%), these files are at 0.35-0.38 (35-38%)"
|
|
104
|
-
],
|
|
105
|
-
"suggestion": "Keep comments that add non-obvious context (like 'This is what most matchers check' or 'Falls back to { input: 10, output: 5 } if omitted') but remove comments that merely restate the type+name (e.g., '/** Text content to send back. */' on a field called text?: string). A good rule: if deleting the comment loses zero information beyond what the name+type already convey, delete it.",
|
|
106
|
-
"confidence": "medium"
|
|
107
|
-
},
|
|
108
|
-
{
|
|
109
|
-
"dimension": "ai_generated_debt",
|
|
110
|
-
"identifier": "logger_high_log_density",
|
|
111
|
-
"summary": "Logger class has a log density of 4.0 -- every method is a formatting wrapper around console with identical structure",
|
|
112
|
-
"related_files": [
|
|
113
|
-
"src/logger.ts"
|
|
114
|
-
],
|
|
115
|
-
"evidence": [
|
|
116
|
-
"All four methods (error, warn, info, debug) follow an identical pattern: check threshold, destructure style, format with template literal, call console",
|
|
117
|
-
"The only variation between methods is which LEVEL_STYLE entry and which console method is used",
|
|
118
|
-
"This repetitive structure is a hallmark of AI-generated code where each method is written independently rather than factored"
|
|
119
|
-
],
|
|
120
|
-
"suggestion": "Extract a private _log(level, consoleFn, msg, args) method that contains the shared threshold-check and formatting logic. Each public method becomes a one-liner delegating to _log with the appropriate level key and console method. This reduces the four near-identical 5-line methods to four 1-line methods plus one shared implementation.",
|
|
121
|
-
"confidence": "medium"
|
|
122
|
-
},
|
|
123
|
-
{
|
|
124
|
-
"dimension": "design_coherence",
|
|
125
|
-
"identifier": "openai_serialize_usage_duplication",
|
|
126
|
-
"summary": "Usage object construction is duplicated verbatim between serialize() and serializeComplete() in all three format serializers",
|
|
127
|
-
"related_files": [
|
|
128
|
-
"src/formats/openai/serialize.ts",
|
|
129
|
-
"src/formats/anthropic/serialize.ts",
|
|
130
|
-
"src/formats/responses/serialize.ts"
|
|
131
|
-
],
|
|
132
|
-
"evidence": [
|
|
133
|
-
"OpenAI serialize.ts lines 41-47 and lines 80-86 construct identical prompt_tokens_details and completion_tokens_details objects",
|
|
134
|
-
"Anthropic serialize.ts lines 55 and 85 both construct { input_tokens: usage.input, output_tokens: usage.output }",
|
|
135
|
-
"Responses serialize.ts lines 98 and 123 both construct { input_tokens: usage.input, output_tokens: usage.output, total_tokens: usage.input + usage.output }",
|
|
136
|
-
"Each format repeats its usage construction logic in both the streaming and non-streaming serialization paths"
|
|
137
|
-
],
|
|
138
|
-
"suggestion": "Extract format-specific usage builders into small helpers within each serializer file. For example in openai/serialize.ts, add a buildUsage(usage) function that returns the full usage object with details, then call it from both serialize() and serializeComplete(). This eliminates the duplicated object literals.",
|
|
139
|
-
"confidence": "high"
|
|
140
|
-
},
|
|
141
|
-
{
|
|
142
|
-
"dimension": "design_coherence",
|
|
143
|
-
"identifier": "replySequence_duplicated_in_loader_and_server",
|
|
144
|
-
"summary": "Sequence replay logic (index tracking, last-entry fallback, options mutation) is duplicated between MockServer.when().replySequence() and loader.ts addSequenceRule()",
|
|
145
|
-
"related_files": [
|
|
146
|
-
"src/mock-server.ts",
|
|
147
|
-
"src/loader.ts"
|
|
148
|
-
],
|
|
149
|
-
"evidence": [
|
|
150
|
-
"mock-server.ts lines 107-121: replySequence creates a closure with index++, entries[index] ?? last, and mutates rule.options",
|
|
151
|
-
"loader.ts lines 102-129: addSequenceRule creates a nearly identical closure with index++, resolved[index] ?? lastStep, and mutates rule.options",
|
|
152
|
-
"Both implementations also set rule.remaining = entries.length"
|
|
153
|
-
],
|
|
154
|
-
"suggestion": "Extract a createSequenceResolver(entries) function into a shared location (e.g., rule-engine.ts or a new sequence-helpers.ts) that returns { resolver, entryCount }. Both mock-server.ts replySequence() and loader.ts addSequenceRule() can call this shared function, eliminating the duplicated closure logic.",
|
|
155
|
-
"confidence": "high"
|
|
156
|
-
},
|
|
157
|
-
{
|
|
158
|
-
"dimension": "low_level_elegance",
|
|
159
|
-
"identifier": "cli_start_function_mixed_concerns",
|
|
160
|
-
"summary": "The cli.ts start() function mixes server setup, banner printing, file watching, and signal handling in a single 80-line function",
|
|
161
|
-
"related_files": [
|
|
162
|
-
"src/cli.ts"
|
|
163
|
-
],
|
|
164
|
-
"evidence": [
|
|
165
|
-
"Lines 24-104: start() handles option parsing, server construction, rule/handler loading, fallback setup, banner printing, fs.watch setup with debouncing, and SIGINT/SIGTERM handlers",
|
|
166
|
-
"The file watch setup (lines 70-89) is nested inside start() with its own state (reloading flag, setTimeout)",
|
|
167
|
-
"Signal handlers (lines 91-103) are also wired inline"
|
|
168
|
-
],
|
|
169
|
-
"suggestion": "Extract the file-watch setup into a watchRulesPath(server, rulesPath, fallback, logger) function, and the signal handling into a setupShutdown(server, logger) function. The start() function should read as a linear composition of setup steps rather than containing all the implementation details inline.",
|
|
170
|
-
"confidence": "medium"
|
|
171
|
-
},
|
|
172
|
-
{
|
|
173
|
-
"dimension": "low_level_elegance",
|
|
174
|
-
"identifier": "responses_serialize_deeply_nested_object_literals",
|
|
175
|
-
"summary": "responses/serialize.ts builds deeply nested inline object literals making the streaming event structure hard to read",
|
|
176
|
-
"related_files": [
|
|
177
|
-
"src/formats/responses/serialize.ts"
|
|
178
|
-
],
|
|
179
|
-
"evidence": [
|
|
180
|
-
"Line 42-43: textStreamBlock constructs a 3-level deep inline object: { type: 'response.output_item.added', output_index: i, item: { type: 'message', id: itemId, status: 'in_progress', role: 'assistant', content: [] } }",
|
|
181
|
-
"Line 62: toolStreamBlock spreads an outputItem into a modified copy inline: { ...outputItem, status: 'in_progress', arguments: '' }",
|
|
182
|
-
"These long inline constructions make it difficult to scan the event structure at a glance"
|
|
183
|
-
],
|
|
184
|
-
"suggestion": "Extract the repeated item shapes into named local variables or small builder functions. For example, const inProgressItem = { ...outputItem, status: 'in_progress', arguments: '' } on a separate line before passing it to c(). This improves scanability without adding abstraction overhead.",
|
|
185
|
-
"confidence": "low"
|
|
186
|
-
},
|
|
187
|
-
{
|
|
188
|
-
"dimension": "api_surface_coherence",
|
|
189
|
-
"identifier": "format_serialize_return_type_unknown",
|
|
190
|
-
"summary": "serializeComplete and serializeError return 'unknown' in the Format interface, losing type information at the boundary",
|
|
191
|
-
"related_files": [
|
|
192
|
-
"src/formats/types.ts",
|
|
193
|
-
"src/formats/openai/serialize.ts",
|
|
194
|
-
"src/formats/anthropic/serialize.ts",
|
|
195
|
-
"src/formats/responses/serialize.ts"
|
|
196
|
-
],
|
|
197
|
-
"evidence": [
|
|
198
|
-
"Format interface line 15: serializeComplete returns 'unknown'",
|
|
199
|
-
"Format interface line 16: serializeError returns 'unknown'",
|
|
200
|
-
"Each concrete implementation also returns 'unknown' rather than a typed object",
|
|
201
|
-
"Callers in route-handler.ts pass the result directly to reply.send() without any type checking"
|
|
202
|
-
],
|
|
203
|
-
"suggestion": "Since these are JSON response bodies sent to the client, at minimum type them as Record<string, unknown> or JsonValue. Ideally, use a generic Format<TComplete, TError> or define a union type covering the three format response shapes. This prevents accidental primitive returns and improves IDE support for callers.",
|
|
204
|
-
"confidence": "medium"
|
|
205
|
-
},
|
|
206
|
-
{
|
|
207
|
-
"dimension": "mid_level_elegance",
|
|
208
|
-
"identifier": "route_handler_history_records_before_streaming",
|
|
209
|
-
"summary": "History records the request before the streaming response is written, so history.count() may increment before the client has received any data",
|
|
210
|
-
"related_files": [
|
|
211
|
-
"src/route-handler.ts"
|
|
212
|
-
],
|
|
213
|
-
"evidence": [
|
|
214
|
-
"Line 89: history.record(mockReq, ruleDesc) is called unconditionally before the streaming/non-streaming branch",
|
|
215
|
-
"Line 85: For error replies, history.record is called before returning the error response",
|
|
216
|
-
"If writeSSE (line 111) fails mid-stream, the history already recorded the request as handled"
|
|
217
|
-
],
|
|
218
|
-
"suggestion": "This is a minor timing issue but worth acknowledging. Consider recording the history entry after the response is fully written (after writeSSE completes or after reply.send() returns). Alternatively, add a 'status' field to RecordedRequest indicating whether the response was successfully delivered.",
|
|
219
|
-
"confidence": "low"
|
|
220
|
-
},
|
|
221
|
-
{
|
|
222
|
-
"dimension": "convention_outlier",
|
|
223
|
-
"identifier": "parse_helpers_mixed_responsibility",
|
|
224
|
-
"summary": "parse-helpers.ts mixes request-building utilities (buildMockRequest) with serialization utilities (splitText, genId, toolId, finishReason, shouldEmitText)",
|
|
225
|
-
"related_files": [
|
|
226
|
-
"src/formats/parse-helpers.ts"
|
|
227
|
-
],
|
|
228
|
-
"evidence": [
|
|
229
|
-
"buildMockRequest (line 53) is used only by parse.ts files for constructing MockRequest objects from incoming requests",
|
|
230
|
-
"splitText, genId, toolId, shouldEmitText, finishReason are used only by serialize.ts files for constructing outgoing responses",
|
|
231
|
-
"isStreaming (line 37) is used by both parsing (index.ts) and is a third concern (request detection)",
|
|
232
|
-
"The file has 11 importers across very different use contexts"
|
|
233
|
-
],
|
|
234
|
-
"suggestion": "Split parse-helpers.ts into two files: serialize-helpers.ts (for splitText, genId, toolId, shouldEmitText, finishReason, DEFAULT_USAGE, MS_PER_SECOND) and request-helpers.ts (for buildMockRequest, isStreaming, RequestMeta). This aligns each file with a single direction of data flow.",
|
|
235
|
-
"confidence": "medium"
|
|
236
|
-
}
|
|
237
|
-
],
|
|
238
|
-
"provenance": {
|
|
239
|
-
"kind": "blind_review_batch_import",
|
|
240
|
-
"blind": true,
|
|
241
|
-
"runner": "claude",
|
|
242
|
-
"run_stamp": "ext_20260315_000339_a6cdc3e6",
|
|
243
|
-
"created_at": "2026-03-15T00:09:17+00:00",
|
|
244
|
-
"packet_path": "/Users/suyash.x.srijan/Documents/Personal_Projects/llm-mock-server/.desloppify/review_packet_blind.json",
|
|
245
|
-
"packet_sha256": "61563983650626757ab21c4b1e8ea4db0d723c4ce1fde659f2bbff0a59e56044",
|
|
246
|
-
"external_session_id": "ext_20260315_000339_a6cdc3e6"
|
|
247
|
-
}
|
|
248
|
-
}
|