@copilotkit/aimock 1.44.0 → 1.45.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.claude-plugin/marketplace.json +1 -1
- package/.claude-plugin/plugin.json +1 -1
- package/CHANGELOG.md +31 -0
- package/README.md +6 -0
- package/dist/agui-types.d.ts.map +1 -1
- package/dist/aimock-cli.cjs +31 -5
- package/dist/aimock-cli.cjs.map +1 -1
- package/dist/aimock-cli.d.cts.map +1 -1
- package/dist/aimock-cli.d.ts.map +1 -1
- package/dist/aimock-cli.js +31 -5
- package/dist/aimock-cli.js.map +1 -1
- package/dist/bedrock-converse.cjs +312 -39
- package/dist/bedrock-converse.cjs.map +1 -1
- package/dist/bedrock-converse.d.cts.map +1 -1
- package/dist/bedrock-converse.d.ts.map +1 -1
- package/dist/bedrock-converse.js +313 -40
- package/dist/bedrock-converse.js.map +1 -1
- package/dist/bedrock.cjs +195 -42
- package/dist/bedrock.cjs.map +1 -1
- package/dist/bedrock.d.cts.map +1 -1
- package/dist/bedrock.d.ts.map +1 -1
- package/dist/bedrock.js +196 -43
- package/dist/bedrock.js.map +1 -1
- package/dist/chaos.cjs +1 -0
- package/dist/chaos.js +1 -1
- package/dist/cli.cjs +152 -5
- package/dist/cli.cjs.map +1 -1
- package/dist/cli.js +155 -8
- package/dist/cli.js.map +1 -1
- package/dist/cohere.cjs +132 -71
- package/dist/cohere.cjs.map +1 -1
- package/dist/cohere.d.cts.map +1 -1
- package/dist/cohere.d.ts.map +1 -1
- package/dist/cohere.js +133 -72
- package/dist/cohere.js.map +1 -1
- package/dist/config-loader.cjs +62 -4
- package/dist/config-loader.cjs.map +1 -1
- package/dist/config-loader.d.cts +37 -4
- package/dist/config-loader.d.cts.map +1 -1
- package/dist/config-loader.d.ts +37 -4
- package/dist/config-loader.d.ts.map +1 -1
- package/dist/config-loader.js +62 -4
- package/dist/config-loader.js.map +1 -1
- package/dist/fakes-plugin.cjs +131 -0
- package/dist/fakes-plugin.cjs.map +1 -0
- package/dist/fakes-plugin.d.cts +23 -0
- package/dist/fakes-plugin.d.cts.map +1 -0
- package/dist/fakes-plugin.d.ts +23 -0
- package/dist/fakes-plugin.d.ts.map +1 -0
- package/dist/fakes-plugin.js +127 -0
- package/dist/fakes-plugin.js.map +1 -0
- package/dist/fixture-loader-services.cjs +1 -1
- package/dist/fixture-loader-services.cjs.map +1 -1
- package/dist/fixture-loader-services.js +1 -1
- package/dist/fixture-loader-services.js.map +1 -1
- package/dist/fixture-loader.cjs +389 -27
- package/dist/fixture-loader.cjs.map +1 -1
- package/dist/fixture-loader.d.cts +47 -3
- package/dist/fixture-loader.d.cts.map +1 -1
- package/dist/fixture-loader.d.ts +47 -3
- package/dist/fixture-loader.d.ts.map +1 -1
- package/dist/fixture-loader.js +384 -29
- package/dist/fixture-loader.js.map +1 -1
- package/dist/gemini-interactions.cjs +250 -52
- package/dist/gemini-interactions.cjs.map +1 -1
- package/dist/gemini-interactions.d.cts.map +1 -1
- package/dist/gemini-interactions.d.ts.map +1 -1
- package/dist/gemini-interactions.js +251 -53
- package/dist/gemini-interactions.js.map +1 -1
- package/dist/gemini.cjs +226 -37
- package/dist/gemini.cjs.map +1 -1
- package/dist/gemini.d.cts.map +1 -1
- package/dist/gemini.d.ts.map +1 -1
- package/dist/gemini.js +227 -38
- package/dist/gemini.js.map +1 -1
- package/dist/helpers.cjs +881 -7
- package/dist/helpers.cjs.map +1 -1
- package/dist/helpers.d.cts +85 -4
- package/dist/helpers.d.cts.map +1 -1
- package/dist/helpers.d.ts +85 -4
- package/dist/helpers.d.ts.map +1 -1
- package/dist/helpers.js +839 -8
- package/dist/helpers.js.map +1 -1
- package/dist/index.cjs +9 -0
- package/dist/index.d.cts +7 -5
- package/dist/index.d.ts +7 -5
- package/dist/index.js +4 -3
- package/dist/jest.cjs +31 -7
- package/dist/jest.cjs.map +1 -1
- package/dist/jest.d.cts +43 -3
- package/dist/jest.d.cts.map +1 -1
- package/dist/jest.d.ts +43 -3
- package/dist/jest.d.ts.map +1 -1
- package/dist/jest.js +33 -9
- package/dist/jest.js.map +1 -1
- package/dist/journal.cjs +135 -13
- package/dist/journal.cjs.map +1 -1
- package/dist/journal.d.cts +38 -4
- package/dist/journal.d.cts.map +1 -1
- package/dist/journal.d.ts +38 -4
- package/dist/journal.d.ts.map +1 -1
- package/dist/journal.js +135 -13
- package/dist/journal.js.map +1 -1
- package/dist/live-sanitize.cjs +4 -32
- package/dist/live-sanitize.cjs.map +1 -1
- package/dist/live-sanitize.js +4 -32
- package/dist/live-sanitize.js.map +1 -1
- package/dist/llmock.cjs +123 -30
- package/dist/llmock.cjs.map +1 -1
- package/dist/llmock.d.cts +16 -1
- package/dist/llmock.d.cts.map +1 -1
- package/dist/llmock.d.ts +16 -1
- package/dist/llmock.d.ts.map +1 -1
- package/dist/llmock.js +125 -32
- package/dist/llmock.js.map +1 -1
- package/dist/mcp-fakes-mount.cjs +1 -0
- package/dist/mcp-fakes-mount.cjs.map +1 -1
- package/dist/mcp-fakes-mount.js +1 -0
- package/dist/mcp-fakes-mount.js.map +1 -1
- package/dist/mcp-fakes-report.cjs +111 -0
- package/dist/mcp-fakes-report.cjs.map +1 -0
- package/dist/mcp-fakes-report.d.cts +40 -0
- package/dist/mcp-fakes-report.d.cts.map +1 -0
- package/dist/mcp-fakes-report.d.ts +40 -0
- package/dist/mcp-fakes-report.d.ts.map +1 -0
- package/dist/mcp-fakes-report.js +107 -0
- package/dist/mcp-fakes-report.js.map +1 -0
- package/dist/mcp-fakes.cjs +331 -8
- package/dist/mcp-fakes.cjs.map +1 -1
- package/dist/mcp-fakes.d.cts +406 -3
- package/dist/mcp-fakes.d.cts.map +1 -1
- package/dist/mcp-fakes.d.ts +406 -3
- package/dist/mcp-fakes.d.ts.map +1 -1
- package/dist/mcp-fakes.js +331 -8
- package/dist/mcp-fakes.js.map +1 -1
- package/dist/mcp-handler.cjs +209 -16
- package/dist/mcp-handler.cjs.map +1 -1
- package/dist/mcp-handler.d.cts +49 -0
- package/dist/mcp-handler.d.cts.map +1 -0
- package/dist/mcp-handler.d.ts +49 -0
- package/dist/mcp-handler.d.ts.map +1 -0
- package/dist/mcp-handler.js +207 -17
- package/dist/mcp-handler.js.map +1 -1
- package/dist/mcp-mock.cjs +112 -1
- package/dist/mcp-mock.cjs.map +1 -1
- package/dist/mcp-mock.d.cts +33 -2
- package/dist/mcp-mock.d.cts.map +1 -1
- package/dist/mcp-mock.d.ts +33 -2
- package/dist/mcp-mock.d.ts.map +1 -1
- package/dist/mcp-mock.js +112 -1
- package/dist/mcp-mock.js.map +1 -1
- package/dist/mcp-recorder.cjs +1224 -0
- package/dist/mcp-recorder.cjs.map +1 -0
- package/dist/mcp-recorder.d.cts +108 -0
- package/dist/mcp-recorder.d.cts.map +1 -0
- package/dist/mcp-recorder.d.ts +108 -0
- package/dist/mcp-recorder.d.ts.map +1 -0
- package/dist/mcp-recorder.js +1215 -0
- package/dist/mcp-recorder.js.map +1 -0
- package/dist/messages.cjs +362 -56
- package/dist/messages.cjs.map +1 -1
- package/dist/messages.d.cts.map +1 -1
- package/dist/messages.d.ts.map +1 -1
- package/dist/messages.js +363 -58
- package/dist/messages.js.map +1 -1
- package/dist/misbehavior.cjs +1523 -0
- package/dist/misbehavior.cjs.map +1 -0
- package/dist/misbehavior.d.cts +43 -0
- package/dist/misbehavior.d.cts.map +1 -0
- package/dist/misbehavior.d.ts +43 -0
- package/dist/misbehavior.d.ts.map +1 -0
- package/dist/misbehavior.js +1511 -0
- package/dist/misbehavior.js.map +1 -0
- package/dist/ollama.cjs +310 -112
- package/dist/ollama.cjs.map +1 -1
- package/dist/ollama.d.cts.map +1 -1
- package/dist/ollama.d.ts.map +1 -1
- package/dist/ollama.js +311 -113
- package/dist/ollama.js.map +1 -1
- package/dist/record-sanitize.cjs +231 -0
- package/dist/record-sanitize.cjs.map +1 -0
- package/dist/record-sanitize.js +224 -0
- package/dist/record-sanitize.js.map +1 -0
- package/dist/recorder.cjs +106 -5
- package/dist/recorder.cjs.map +1 -1
- package/dist/recorder.d.cts +2 -1
- package/dist/recorder.d.cts.map +1 -1
- package/dist/recorder.d.ts +2 -1
- package/dist/recorder.d.ts.map +1 -1
- package/dist/recorder.js +107 -7
- package/dist/recorder.js.map +1 -1
- package/dist/responses.cjs +667 -171
- package/dist/responses.cjs.map +1 -1
- package/dist/responses.d.cts +5 -13
- package/dist/responses.d.cts.map +1 -1
- package/dist/responses.d.ts +5 -13
- package/dist/responses.d.ts.map +1 -1
- package/dist/responses.js +665 -174
- package/dist/responses.js.map +1 -1
- package/dist/router.cjs +24 -2
- package/dist/router.cjs.map +1 -1
- package/dist/router.d.cts.map +1 -1
- package/dist/router.d.ts.map +1 -1
- package/dist/router.js +25 -4
- package/dist/router.js.map +1 -1
- package/dist/server.cjs +572 -76
- package/dist/server.cjs.map +1 -1
- package/dist/server.d.cts.map +1 -1
- package/dist/server.d.ts.map +1 -1
- package/dist/server.js +574 -78
- package/dist/server.js.map +1 -1
- package/dist/stream-collapse.cjs +236 -34
- package/dist/stream-collapse.cjs.map +1 -1
- package/dist/stream-collapse.d.cts +39 -4
- package/dist/stream-collapse.d.cts.map +1 -1
- package/dist/stream-collapse.d.ts +39 -4
- package/dist/stream-collapse.d.ts.map +1 -1
- package/dist/stream-collapse.js +235 -35
- package/dist/stream-collapse.js.map +1 -1
- package/dist/types.cjs.map +1 -1
- package/dist/types.d.cts +315 -4
- package/dist/types.d.cts.map +1 -1
- package/dist/types.d.ts +315 -4
- package/dist/types.d.ts.map +1 -1
- package/dist/types.js.map +1 -1
- package/dist/validate-cli.cjs +36 -4
- package/dist/validate-cli.cjs.map +1 -1
- package/dist/validate-cli.js +37 -5
- package/dist/validate-cli.js.map +1 -1
- package/dist/vector-types.d.ts.map +1 -1
- package/dist/version.cjs +45 -0
- package/dist/version.cjs.map +1 -0
- package/dist/version.js +44 -0
- package/dist/version.js.map +1 -0
- package/dist/vitest.cjs +66 -10
- package/dist/vitest.cjs.map +1 -1
- package/dist/vitest.d.cts +35 -4
- package/dist/vitest.d.cts.map +1 -1
- package/dist/vitest.d.ts +35 -4
- package/dist/vitest.d.ts.map +1 -1
- package/dist/vitest.js +67 -11
- package/dist/vitest.js.map +1 -1
- package/dist/watcher.cjs +6 -2
- package/dist/watcher.cjs.map +1 -1
- package/dist/watcher.js +6 -2
- package/dist/watcher.js.map +1 -1
- package/dist/ws-gemini-live.cjs +210 -101
- package/dist/ws-gemini-live.cjs.map +1 -1
- package/dist/ws-gemini-live.d.cts +9 -3
- package/dist/ws-gemini-live.d.cts.map +1 -1
- package/dist/ws-gemini-live.d.ts +9 -3
- package/dist/ws-gemini-live.d.ts.map +1 -1
- package/dist/ws-gemini-live.js +211 -102
- package/dist/ws-gemini-live.js.map +1 -1
- package/dist/ws-realtime.cjs +175 -62
- package/dist/ws-realtime.cjs.map +1 -1
- package/dist/ws-realtime.d.cts +8 -3
- package/dist/ws-realtime.d.cts.map +1 -1
- package/dist/ws-realtime.d.ts +8 -3
- package/dist/ws-realtime.d.ts.map +1 -1
- package/dist/ws-realtime.js +176 -63
- package/dist/ws-realtime.js.map +1 -1
- package/dist/ws-responses.cjs +109 -16
- package/dist/ws-responses.cjs.map +1 -1
- package/dist/ws-responses.d.cts +8 -12
- package/dist/ws-responses.d.cts.map +1 -1
- package/dist/ws-responses.d.ts +8 -12
- package/dist/ws-responses.d.ts.map +1 -1
- package/dist/ws-responses.js +111 -18
- package/dist/ws-responses.js.map +1 -1
- package/package.json +10 -2
- package/skills/write-fixtures/SKILL.md +179 -21
|
@@ -0,0 +1,127 @@
|
|
|
1
|
+
import { MCP_FAKES_DEFAULT_TEST_ID } from "./mcp-fakes.js";
|
|
2
|
+
import { AimockFakesReportError, formatFakesReport } from "./mcp-fakes-report.js";
|
|
3
|
+
|
|
4
|
+
//#region src/fakes-plugin.ts
|
|
5
|
+
/** TI1/TI2 separator: space, U+203A, space. */
|
|
6
|
+
const TEST_ID_SEPARATOR = " › ";
|
|
7
|
+
const FAKES_REPORT_MODES = [
|
|
8
|
+
"off",
|
|
9
|
+
"warn",
|
|
10
|
+
"fail"
|
|
11
|
+
];
|
|
12
|
+
/** The `fakesReport` option, checked: an unknown mode fails `beforeAll`. */
|
|
13
|
+
function fakesReportMode(mode) {
|
|
14
|
+
if (mode === void 0) return "off";
|
|
15
|
+
if (!FAKES_REPORT_MODES.includes(mode)) throw new Error(`useAimock(): fakesReport must be one of ${FAKES_REPORT_MODES.map((m) => JSON.stringify(m)).join(", ")}; got ${JSON.stringify(mode)}`);
|
|
16
|
+
return mode;
|
|
17
|
+
}
|
|
18
|
+
/**
|
|
19
|
+
* The URL and headers that reach the fakes scoped to `testId` (and
|
|
20
|
+
* `opts.context`) at `opts.mount`. `URLSearchParams` writes a space as `+`,
|
|
21
|
+
* which the mount reads back as a space in the query.
|
|
22
|
+
*/
|
|
23
|
+
function fakesTarget(url, testId, opts = {}) {
|
|
24
|
+
const mount = opts.mount ?? "/mcp";
|
|
25
|
+
const query = new URLSearchParams({ testId });
|
|
26
|
+
const headers = { "X-Test-Id": encodeURIComponent(testId) };
|
|
27
|
+
if (opts.context) {
|
|
28
|
+
query.set("context", opts.context);
|
|
29
|
+
headers["X-AIMock-Context"] = encodeURIComponent(opts.context);
|
|
30
|
+
}
|
|
31
|
+
return {
|
|
32
|
+
testId,
|
|
33
|
+
mcpUrl: `${url}${mount}?${query.toString()}`,
|
|
34
|
+
headers
|
|
35
|
+
};
|
|
36
|
+
}
|
|
37
|
+
/** RP6: read `GET /__aimock/mcp/fakes/report` for one identity. */
|
|
38
|
+
async function fetchFakesReport(url, testId, context) {
|
|
39
|
+
const query = new URLSearchParams();
|
|
40
|
+
if (testId !== null) query.set("testId", testId);
|
|
41
|
+
if (context !== null) query.set("context", context);
|
|
42
|
+
const res = await fetch(`${url}/__aimock/mcp/fakes/report?${query.toString()}`);
|
|
43
|
+
if (!res.ok) throw new Error(`aimock fakes report: HTTP ${res.status} ${await res.text()}`);
|
|
44
|
+
return await res.json();
|
|
45
|
+
}
|
|
46
|
+
function identityKey(testId, context) {
|
|
47
|
+
return `${testId ?? ""}\u0000${context ?? ""}`;
|
|
48
|
+
}
|
|
49
|
+
/** Per-`useAimock` state shared by the Vitest and Jest adapters. */
|
|
50
|
+
var FakesPluginState = class {
|
|
51
|
+
registered = /* @__PURE__ */ new Map();
|
|
52
|
+
/** RP7 (b): id of the last journal entry when the test started (C2). */
|
|
53
|
+
marker = null;
|
|
54
|
+
constructor(mode) {
|
|
55
|
+
this.mode = mode;
|
|
56
|
+
}
|
|
57
|
+
/** Start of a test, after the match-count reset. */
|
|
58
|
+
beforeEach(llm) {
|
|
59
|
+
this.registered.clear();
|
|
60
|
+
this.marker = llm.getLastRequest()?.id ?? null;
|
|
61
|
+
}
|
|
62
|
+
/** RP7 (a): `fakesFor` registers the identity for the current test. */
|
|
63
|
+
register(testId, context) {
|
|
64
|
+
this.registered.set(identityKey(testId, context), {
|
|
65
|
+
testId,
|
|
66
|
+
context
|
|
67
|
+
});
|
|
68
|
+
}
|
|
69
|
+
/**
|
|
70
|
+
* RP7 (a) and (b): the registered identities, plus every (test id, context)
|
|
71
|
+
* an MCP request carried since the marker. If the marker entry is gone
|
|
72
|
+
* (FIFO cap or a cleared journal), every remaining entry is newer.
|
|
73
|
+
*/
|
|
74
|
+
identities(llm) {
|
|
75
|
+
const all = llm.getRequests();
|
|
76
|
+
const at = this.marker === null ? -1 : all.findIndex((e) => e.id === this.marker);
|
|
77
|
+
const newer = at >= 0 ? all.slice(at + 1) : all;
|
|
78
|
+
const out = new Map(this.registered);
|
|
79
|
+
for (const entry of newer) {
|
|
80
|
+
if (entry.service !== "mcp" || entry.testId === void 0) continue;
|
|
81
|
+
const testId = entry.testId === MCP_FAKES_DEFAULT_TEST_ID ? null : entry.testId;
|
|
82
|
+
const context = entry.context ?? null;
|
|
83
|
+
out.set(identityKey(testId, context), {
|
|
84
|
+
testId,
|
|
85
|
+
context
|
|
86
|
+
});
|
|
87
|
+
}
|
|
88
|
+
return [...out.values()];
|
|
89
|
+
}
|
|
90
|
+
/**
|
|
91
|
+
* End of a test, before the next reset: read each identity's report.
|
|
92
|
+
* Under `"fail"`, a report with `ok: false` throws `AimockFakesReportError`
|
|
93
|
+
* (the first failing report, with every failing report's text); under
|
|
94
|
+
* `"warn"`, the text goes to `console.warn`. An unconsumed entry two
|
|
95
|
+
* reports share (an entry scoped by test id only, checked with and without
|
|
96
|
+
* a context) is listed once.
|
|
97
|
+
*/
|
|
98
|
+
async afterEach(llm, url) {
|
|
99
|
+
if (this.mode === "off") return;
|
|
100
|
+
const failing = [];
|
|
101
|
+
const lines = [];
|
|
102
|
+
const unconsumedSeen = /* @__PURE__ */ new Set();
|
|
103
|
+
for (const id of this.identities(llm)) {
|
|
104
|
+
const report = await fetchFakesReport(url, id.testId, id.context);
|
|
105
|
+
if (report.ok) continue;
|
|
106
|
+
failing.push(report);
|
|
107
|
+
for (const line of formatFakesReport(report).split("\n")) {
|
|
108
|
+
if (line.startsWith(" unconsumed: ")) {
|
|
109
|
+
if (unconsumedSeen.has(line)) continue;
|
|
110
|
+
unconsumedSeen.add(line);
|
|
111
|
+
}
|
|
112
|
+
lines.push(line);
|
|
113
|
+
}
|
|
114
|
+
}
|
|
115
|
+
if (failing.length === 0) return;
|
|
116
|
+
const text = lines.join("\n");
|
|
117
|
+
if (this.mode === "warn") {
|
|
118
|
+
console.warn(text);
|
|
119
|
+
return;
|
|
120
|
+
}
|
|
121
|
+
throw new AimockFakesReportError(failing[0], text);
|
|
122
|
+
}
|
|
123
|
+
};
|
|
124
|
+
|
|
125
|
+
//#endregion
|
|
126
|
+
export { FakesPluginState, TEST_ID_SEPARATOR, fakesReportMode, fakesTarget, fetchFakesReport };
|
|
127
|
+
//# sourceMappingURL=fakes-plugin.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"fakes-plugin.js","names":[],"sources":["../src/fakes-plugin.ts"],"sourcesContent":["/**\n * The MCP fakes part of the Vitest and Jest plugins (record-replay RP7, TI):\n * `fakesFor` targets, the per-test fake report check in `afterEach`, and the\n * state both adapters share. The default test id itself is built by each\n * adapter (TI1 Vitest, TI2 Jest), and it is used only by the fakes helpers\n * (TI4): nothing here tags LLM or control traffic.\n */\nimport type { LLMock } from \"./llmock.js\";\nimport {\n AimockFakesReportError,\n formatFakesReport,\n type McpFakesReport,\n} from \"./mcp-fakes-report.js\";\nimport { MCP_FAKES_DEFAULT_TEST_ID } from \"./mcp-fakes.js\";\n\n/** RP7: what `afterEach` does with a failing report. `\"off\"` is the default. */\nexport type FakesReportMode = \"off\" | \"warn\" | \"fail\";\n\n/** RP7: what `fakesFor` returns, for an MCP client. */\nexport interface FakesTarget {\n /** The test id the URL and headers carry (explicit, or the inferred default). */\n testId: string;\n /** The mount URL with `?testId=` (and `&context=`). */\n mcpUrl: string;\n /** The matching `X-Test-Id` / `X-AIMock-Context` headers, URI-encoded. */\n headers: Record<string, string>;\n}\n\nexport interface FakesForOptions {\n /** The context the fake blocks are scoped to. */\n context?: string;\n /** The MCP mount path (default `\"/mcp\"`). */\n mount?: string;\n}\n\n/** TI1/TI2 separator: space, U+203A, space. */\nexport const TEST_ID_SEPARATOR = \" › \";\n\nconst FAKES_REPORT_MODES: readonly FakesReportMode[] = [\"off\", \"warn\", \"fail\"];\n\n/** The `fakesReport` option, checked: an unknown mode fails `beforeAll`. */\nexport function fakesReportMode(mode: FakesReportMode | undefined): FakesReportMode {\n if (mode === undefined) return \"off\";\n if (!FAKES_REPORT_MODES.includes(mode)) {\n throw new Error(\n `useAimock(): fakesReport must be one of ${FAKES_REPORT_MODES.map((m) => JSON.stringify(m)).join(\", \")}; got ${JSON.stringify(mode)}`,\n );\n }\n return mode;\n}\n\n/**\n * The URL and headers that reach the fakes scoped to `testId` (and\n * `opts.context`) at `opts.mount`. `URLSearchParams` writes a space as `+`,\n * which the mount reads back as a space in the query.\n */\nexport function fakesTarget(url: string, testId: string, opts: FakesForOptions = {}): FakesTarget {\n const mount = opts.mount ?? \"/mcp\";\n const query = new URLSearchParams({ testId });\n const headers: Record<string, string> = { \"X-Test-Id\": encodeURIComponent(testId) };\n if (opts.context) {\n query.set(\"context\", opts.context);\n headers[\"X-AIMock-Context\"] = encodeURIComponent(opts.context);\n }\n return { testId, mcpUrl: `${url}${mount}?${query.toString()}`, headers };\n}\n\n/** RP6: read `GET /__aimock/mcp/fakes/report` for one identity. */\nexport async function fetchFakesReport(\n url: string,\n testId: string | null,\n context: string | null,\n): Promise<McpFakesReport> {\n const query = new URLSearchParams();\n if (testId !== null) query.set(\"testId\", testId);\n if (context !== null) query.set(\"context\", context);\n const res = await fetch(`${url}/__aimock/mcp/fakes/report?${query.toString()}`);\n if (!res.ok) {\n throw new Error(`aimock fakes report: HTTP ${res.status} ${await res.text()}`);\n }\n return (await res.json()) as McpFakesReport;\n}\n\n/** One identity the report checks; `null` means none supplied. */\nexport interface FakesIdentity {\n testId: string | null;\n context: string | null;\n}\n\nfunction identityKey(testId: string | null, context: string | null): string {\n return `${testId ?? \"\"}\\u0000${context ?? \"\"}`;\n}\n\n/** Per-`useAimock` state shared by the Vitest and Jest adapters. */\nexport class FakesPluginState {\n private readonly registered = new Map<string, FakesIdentity>();\n /** RP7 (b): id of the last journal entry when the test started (C2). */\n private marker: string | null = null;\n\n constructor(readonly mode: FakesReportMode) {}\n\n /** Start of a test, after the match-count reset. */\n beforeEach(llm: LLMock): void {\n this.registered.clear();\n this.marker = llm.getLastRequest()?.id ?? null;\n }\n\n /** RP7 (a): `fakesFor` registers the identity for the current test. */\n register(testId: string, context: string | null): void {\n this.registered.set(identityKey(testId, context), { testId, context });\n }\n\n /**\n * RP7 (a) and (b): the registered identities, plus every (test id, context)\n * an MCP request carried since the marker. If the marker entry is gone\n * (FIFO cap or a cleared journal), every remaining entry is newer.\n */\n identities(llm: LLMock): FakesIdentity[] {\n const all = llm.getRequests();\n const at = this.marker === null ? -1 : all.findIndex((e) => e.id === this.marker);\n const newer = at >= 0 ? all.slice(at + 1) : all;\n const out = new Map(this.registered);\n for (const entry of newer) {\n if (entry.service !== \"mcp\" || entry.testId === undefined) continue;\n const testId = entry.testId === MCP_FAKES_DEFAULT_TEST_ID ? null : entry.testId;\n const context = entry.context ?? null;\n out.set(identityKey(testId, context), { testId, context });\n }\n return [...out.values()];\n }\n\n /**\n * End of a test, before the next reset: read each identity's report.\n * Under `\"fail\"`, a report with `ok: false` throws `AimockFakesReportError`\n * (the first failing report, with every failing report's text); under\n * `\"warn\"`, the text goes to `console.warn`. An unconsumed entry two\n * reports share (an entry scoped by test id only, checked with and without\n * a context) is listed once.\n */\n async afterEach(llm: LLMock, url: string): Promise<void> {\n if (this.mode === \"off\") return;\n const failing: McpFakesReport[] = [];\n const lines: string[] = [];\n const unconsumedSeen = new Set<string>();\n for (const id of this.identities(llm)) {\n const report = await fetchFakesReport(url, id.testId, id.context);\n if (report.ok) continue;\n failing.push(report);\n for (const line of formatFakesReport(report).split(\"\\n\")) {\n if (line.startsWith(\" unconsumed: \")) {\n if (unconsumedSeen.has(line)) continue;\n unconsumedSeen.add(line);\n }\n lines.push(line);\n }\n }\n if (failing.length === 0) return;\n const text = lines.join(\"\\n\");\n if (this.mode === \"warn\") {\n console.warn(text);\n return;\n }\n throw new AimockFakesReportError(failing[0], text);\n }\n}\n"],"mappings":"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"}
|
|
@@ -53,7 +53,7 @@ function loadServicesFile(filePath, source, load, logger, liveOptions) {
|
|
|
53
53
|
raw
|
|
54
54
|
});
|
|
55
55
|
}
|
|
56
|
-
out.fixtures.push(...require_fixture_loader.fixturesOf(parsed, filePath, logger, liveOptions, withFakes));
|
|
56
|
+
out.fixtures.push(...require_fixture_loader.fixturesOf(parsed, filePath, logger, liveOptions, withFakes, source));
|
|
57
57
|
}
|
|
58
58
|
/**
|
|
59
59
|
* The result of a finished load, or its errors thrown: the one
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"fixture-loader-services.cjs","names":["readFixtureJson","hasMcpFakesKey","validateMcpFakes","fixturesOf","McpFakesAddError","listFixtureDir"],"sources":["../src/fixture-loader-services.ts"],"sourcesContent":["/**\n * Fixture loaders that carry MCP fakes (spec F1): the `…WithServices`\n * variants of `loadFixtureFile` / `loadFixturesFromDir`. They return the LLM\n * fixtures and the raw `mcpFakes` blocks with their entry-id `source` (I6).\n *\n * They live outside fixture-loader.ts because they validate blocks with\n * `validateMcpFakes`, and mcp-fakes.ts imports `FixtureLoadError` from\n * fixture-loader.ts at module load (`McpFakesAddError extends\n * FixtureLoadError`): a value import of mcp-fakes.ts from fixture-loader.ts\n * is an import cycle that leaves `FixtureLoadError` uninitialized whenever\n * fixture-loader.ts is loaded first.\n */\nimport { join } from \"node:path\";\nimport type { LiveOptions } from \"./live-types.js\";\nimport type { Logger } from \"./logger.js\";\nimport {\n fixturesOf,\n hasMcpFakesKey,\n listFixtureDir,\n readFixtureJson,\n type FixtureLoadError,\n} from \"./fixture-loader.js\";\nimport { McpFakesAddError, validateMcpFakes, type McpFakeIssue } from \"./mcp-fakes.js\";\nimport type { Fixture, McpFakeSource } from \"./types.js\";\n\n/** What {@link loadFixtureFileWithServices} and {@link loadFixturesFromDirWithServices} return. */\nexport interface FixturesWithServices {\n /** LLM fixtures, as the old loaders return them. */\n fixtures: Fixture[];\n /**\n * The raw `mcpFakes` blocks, one per block, in load order, each with its\n * entry-id `source` (spec I6) and `blockIndex` (`null` for the\n * single-object form). Validated per file; checks across files and loads\n * (entry-id collisions) happen when a mount adds them.\n */\n mcpFakes: McpFakeSource[];\n /** Shadowed-entry warnings (L8) of those blocks, for the caller to print. */\n mcpFakeWarnings: McpFakeIssue[];\n /**\n * The entry-id `source` (I6) of each file that could not be read or was\n * not valid JSON. Such a file is warned and skipped, as before, so it adds\n * no fixtures and no blocks; a `--watch` reload uses this list to tell a\n * file it could not parse from a file whose `mcpFakes` were removed.\n */\n unreadable: string[];\n}\n\n/** A load in progress: the result, plus every bad-block error of every file. */\ninterface ServicesLoad {\n out: FixturesWithServices;\n errors: FixtureLoadError[];\n}\n\n/**\n * Load one parsed file into `load.out`: its `mcpFakes` blocks under\n * `source`, then its LLM fixtures. A file with a bad block adds nothing; its\n * errors go to `load.errors`, and the load goes on so every bad block of\n * every file is reported (fail-loud rule, spec 5.4; L3). Its L8 warnings are\n * kept either way.\n */\nfunction loadServicesFile(\n filePath: string,\n source: string,\n load: ServicesLoad,\n logger: Logger | undefined,\n liveOptions: LiveOptions | undefined,\n): void {\n const { out } = load;\n const read = readFixtureJson(filePath, logger);\n if (read === null) {\n out.unreadable.push(source);\n return;\n }\n const parsed = read.value;\n const withFakes = hasMcpFakesKey(parsed);\n if (withFakes) {\n const raw = parsed.mcpFakes;\n const validation = validateMcpFakes(raw, source);\n out.mcpFakeWarnings.push(...validation.warnings);\n if (validation.errors.length > 0) {\n load.errors.push(...validation.errors);\n return;\n }\n if (Array.isArray(raw)) {\n raw.forEach((block: unknown, blockIndex: number) => {\n out.mcpFakes.push({ source, blockIndex, raw: block });\n });\n } else {\n out.mcpFakes.push({ source, blockIndex: null, raw });\n }\n }\n out.fixtures.push(...fixturesOf(parsed, filePath, logger, liveOptions, withFakes));\n}\n\n/**\n * The result of a finished load, or its errors thrown: the one\n * `FixtureLoadError` alone, else a `McpFakesAddError` holding every error and\n * the L8 warnings, so no warning is lost when the load fails.\n */\nfunction finish(load: ServicesLoad): FixturesWithServices {\n const { out, errors } = load;\n const [first] = errors;\n if (first === undefined) return out;\n if (errors.length === 1 && out.mcpFakeWarnings.length === 0) throw first;\n throw new McpFakesAddError(errors, out.mcpFakeWarnings);\n}\n\nfunction newLoad(): ServicesLoad {\n return { out: { fixtures: [], mcpFakes: [], mcpFakeWarnings: [], unreadable: [] }, errors: [] };\n}\n\n/**\n * Load one fixture file: its LLM fixtures and its `mcpFakes` blocks. A\n * fakes-only file is accepted. The blocks' `source` (spec I6) is `filePath`\n * as given, or `sourceLabel` when set (the URL of a remote fixture file).\n * A bad block throws a `FixtureLoadError` (a `McpFakesAddError` when there\n * is more than one error, or L8 warnings to keep with it).\n */\nexport function loadFixtureFileWithServices(\n filePath: string,\n logger?: Logger,\n liveOptions?: LiveOptions,\n sourceLabel?: string,\n): FixturesWithServices {\n const load = newLoad();\n loadServicesFile(filePath, sourceLabel ?? filePath, load, logger, liveOptions);\n return finish(load);\n}\n\nfunction loadServicesDir(\n dirPath: string,\n relDir: string,\n load: ServicesLoad,\n logger: Logger | undefined,\n liveOptions: LiveOptions | undefined,\n): void {\n const listing = listFixtureDir(dirPath, logger);\n if (listing === null) return;\n for (const name of listing.jsonFiles) {\n loadServicesFile(join(dirPath, name), relDir + name, load, logger, liveOptions);\n }\n for (const sub of listing.subdirs) {\n loadServicesDir(join(dirPath, sub), `${relDir}${sub}/`, load, logger, liveOptions);\n }\n}\n\n/**\n * Load every `.json` file under `dirPath` (sorted, then subdirectories, at\n * full depth, as {@link loadFixturesFromDir}): LLM fixtures and `mcpFakes`\n * blocks. Each block's `source` (spec I6) is its file's path relative to\n * `dirPath`, with `/` separators. Every file is checked before anything is\n * thrown: the bad blocks of all files are reported together, as in\n * {@link loadFixtureFileWithServices}.\n */\nexport function loadFixturesFromDirWithServices(\n dirPath: string,\n logger?: Logger,\n liveOptions?: LiveOptions,\n): FixturesWithServices {\n const load = newLoad();\n loadServicesDir(dirPath, \"\", load, logger, liveOptions);\n return finish(load);\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;AA4DA,SAAS,iBACP,UACA,QACA,MACA,QACA,aACM;CACN,MAAM,EAAE,QAAQ;CAChB,MAAM,OAAOA,uCAAgB,UAAU,OAAO;AAC9C,KAAI,SAAS,MAAM;AACjB,MAAI,WAAW,KAAK,OAAO;AAC3B;;CAEF,MAAM,SAAS,KAAK;CACpB,MAAM,YAAYC,sCAAe,OAAO;AACxC,KAAI,WAAW;EACb,MAAM,MAAM,OAAO;EACnB,MAAM,aAAaC,mCAAiB,KAAK,OAAO;AAChD,MAAI,gBAAgB,KAAK,GAAG,WAAW,SAAS;AAChD,MAAI,WAAW,OAAO,SAAS,GAAG;AAChC,QAAK,OAAO,KAAK,GAAG,WAAW,OAAO;AACtC;;AAEF,MAAI,MAAM,QAAQ,IAAI,CACpB,KAAI,SAAS,OAAgB,eAAuB;AAClD,OAAI,SAAS,KAAK;IAAE;IAAQ;IAAY,KAAK;IAAO,CAAC;IACrD;MAEF,KAAI,SAAS,KAAK;GAAE;GAAQ,YAAY;GAAM;GAAK,CAAC;;AAGxD,KAAI,SAAS,KAAK,GAAGC,kCAAW,QAAQ,UAAU,QAAQ,aAAa,
|
|
1
|
+
{"version":3,"file":"fixture-loader-services.cjs","names":["readFixtureJson","hasMcpFakesKey","validateMcpFakes","fixturesOf","McpFakesAddError","listFixtureDir"],"sources":["../src/fixture-loader-services.ts"],"sourcesContent":["/**\n * Fixture loaders that carry MCP fakes (spec F1): the `…WithServices`\n * variants of `loadFixtureFile` / `loadFixturesFromDir`. They return the LLM\n * fixtures and the raw `mcpFakes` blocks with their entry-id `source` (I6).\n *\n * They live outside fixture-loader.ts because they validate blocks with\n * `validateMcpFakes`, and mcp-fakes.ts imports `FixtureLoadError` from\n * fixture-loader.ts at module load (`McpFakesAddError extends\n * FixtureLoadError`): a value import of mcp-fakes.ts from fixture-loader.ts\n * is an import cycle that leaves `FixtureLoadError` uninitialized whenever\n * fixture-loader.ts is loaded first.\n */\nimport { join } from \"node:path\";\nimport type { LiveOptions } from \"./live-types.js\";\nimport type { Logger } from \"./logger.js\";\nimport {\n fixturesOf,\n hasMcpFakesKey,\n listFixtureDir,\n readFixtureJson,\n type FixtureLoadError,\n} from \"./fixture-loader.js\";\nimport { McpFakesAddError, validateMcpFakes, type McpFakeIssue } from \"./mcp-fakes.js\";\nimport type { Fixture, McpFakeSource } from \"./types.js\";\n\n/** What {@link loadFixtureFileWithServices} and {@link loadFixturesFromDirWithServices} return. */\nexport interface FixturesWithServices {\n /** LLM fixtures, as the old loaders return them. */\n fixtures: Fixture[];\n /**\n * The raw `mcpFakes` blocks, one per block, in load order, each with its\n * entry-id `source` (spec I6) and `blockIndex` (`null` for the\n * single-object form). Validated per file; checks across files and loads\n * (entry-id collisions) happen when a mount adds them.\n */\n mcpFakes: McpFakeSource[];\n /** Shadowed-entry warnings (L8) of those blocks, for the caller to print. */\n mcpFakeWarnings: McpFakeIssue[];\n /**\n * The entry-id `source` (I6) of each file that could not be read or was\n * not valid JSON. Such a file is warned and skipped, as before, so it adds\n * no fixtures and no blocks; a `--watch` reload uses this list to tell a\n * file it could not parse from a file whose `mcpFakes` were removed.\n */\n unreadable: string[];\n}\n\n/** A load in progress: the result, plus every bad-block error of every file. */\ninterface ServicesLoad {\n out: FixturesWithServices;\n errors: FixtureLoadError[];\n}\n\n/**\n * Load one parsed file into `load.out`: its `mcpFakes` blocks under\n * `source`, then its LLM fixtures. A file with a bad block adds nothing; its\n * errors go to `load.errors`, and the load goes on so every bad block of\n * every file is reported (fail-loud rule, spec 5.4; L3). Its L8 warnings are\n * kept either way.\n */\nfunction loadServicesFile(\n filePath: string,\n source: string,\n load: ServicesLoad,\n logger: Logger | undefined,\n liveOptions: LiveOptions | undefined,\n): void {\n const { out } = load;\n const read = readFixtureJson(filePath, logger);\n if (read === null) {\n out.unreadable.push(source);\n return;\n }\n const parsed = read.value;\n const withFakes = hasMcpFakesKey(parsed);\n if (withFakes) {\n const raw = parsed.mcpFakes;\n const validation = validateMcpFakes(raw, source);\n out.mcpFakeWarnings.push(...validation.warnings);\n if (validation.errors.length > 0) {\n load.errors.push(...validation.errors);\n return;\n }\n if (Array.isArray(raw)) {\n raw.forEach((block: unknown, blockIndex: number) => {\n out.mcpFakes.push({ source, blockIndex, raw: block });\n });\n } else {\n out.mcpFakes.push({ source, blockIndex: null, raw });\n }\n }\n out.fixtures.push(...fixturesOf(parsed, filePath, logger, liveOptions, withFakes, source));\n}\n\n/**\n * The result of a finished load, or its errors thrown: the one\n * `FixtureLoadError` alone, else a `McpFakesAddError` holding every error and\n * the L8 warnings, so no warning is lost when the load fails.\n */\nfunction finish(load: ServicesLoad): FixturesWithServices {\n const { out, errors } = load;\n const [first] = errors;\n if (first === undefined) return out;\n if (errors.length === 1 && out.mcpFakeWarnings.length === 0) throw first;\n throw new McpFakesAddError(errors, out.mcpFakeWarnings);\n}\n\nfunction newLoad(): ServicesLoad {\n return { out: { fixtures: [], mcpFakes: [], mcpFakeWarnings: [], unreadable: [] }, errors: [] };\n}\n\n/**\n * Load one fixture file: its LLM fixtures and its `mcpFakes` blocks. A\n * fakes-only file is accepted. The blocks' `source` (spec I6) is `filePath`\n * as given, or `sourceLabel` when set (the URL of a remote fixture file).\n * A bad block throws a `FixtureLoadError` (a `McpFakesAddError` when there\n * is more than one error, or L8 warnings to keep with it).\n */\nexport function loadFixtureFileWithServices(\n filePath: string,\n logger?: Logger,\n liveOptions?: LiveOptions,\n sourceLabel?: string,\n): FixturesWithServices {\n const load = newLoad();\n loadServicesFile(filePath, sourceLabel ?? filePath, load, logger, liveOptions);\n return finish(load);\n}\n\nfunction loadServicesDir(\n dirPath: string,\n relDir: string,\n load: ServicesLoad,\n logger: Logger | undefined,\n liveOptions: LiveOptions | undefined,\n): void {\n const listing = listFixtureDir(dirPath, logger);\n if (listing === null) return;\n for (const name of listing.jsonFiles) {\n loadServicesFile(join(dirPath, name), relDir + name, load, logger, liveOptions);\n }\n for (const sub of listing.subdirs) {\n loadServicesDir(join(dirPath, sub), `${relDir}${sub}/`, load, logger, liveOptions);\n }\n}\n\n/**\n * Load every `.json` file under `dirPath` (sorted, then subdirectories, at\n * full depth, as {@link loadFixturesFromDir}): LLM fixtures and `mcpFakes`\n * blocks. Each block's `source` (spec I6) is its file's path relative to\n * `dirPath`, with `/` separators. Every file is checked before anything is\n * thrown: the bad blocks of all files are reported together, as in\n * {@link loadFixtureFileWithServices}.\n */\nexport function loadFixturesFromDirWithServices(\n dirPath: string,\n logger?: Logger,\n liveOptions?: LiveOptions,\n): FixturesWithServices {\n const load = newLoad();\n loadServicesDir(dirPath, \"\", load, logger, liveOptions);\n return finish(load);\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;AA4DA,SAAS,iBACP,UACA,QACA,MACA,QACA,aACM;CACN,MAAM,EAAE,QAAQ;CAChB,MAAM,OAAOA,uCAAgB,UAAU,OAAO;AAC9C,KAAI,SAAS,MAAM;AACjB,MAAI,WAAW,KAAK,OAAO;AAC3B;;CAEF,MAAM,SAAS,KAAK;CACpB,MAAM,YAAYC,sCAAe,OAAO;AACxC,KAAI,WAAW;EACb,MAAM,MAAM,OAAO;EACnB,MAAM,aAAaC,mCAAiB,KAAK,OAAO;AAChD,MAAI,gBAAgB,KAAK,GAAG,WAAW,SAAS;AAChD,MAAI,WAAW,OAAO,SAAS,GAAG;AAChC,QAAK,OAAO,KAAK,GAAG,WAAW,OAAO;AACtC;;AAEF,MAAI,MAAM,QAAQ,IAAI,CACpB,KAAI,SAAS,OAAgB,eAAuB;AAClD,OAAI,SAAS,KAAK;IAAE;IAAQ;IAAY,KAAK;IAAO,CAAC;IACrD;MAEF,KAAI,SAAS,KAAK;GAAE;GAAQ,YAAY;GAAM;GAAK,CAAC;;AAGxD,KAAI,SAAS,KAAK,GAAGC,kCAAW,QAAQ,UAAU,QAAQ,aAAa,WAAW,OAAO,CAAC;;;;;;;AAQ5F,SAAS,OAAO,MAA0C;CACxD,MAAM,EAAE,KAAK,WAAW;CACxB,MAAM,CAAC,SAAS;AAChB,KAAI,UAAU,OAAW,QAAO;AAChC,KAAI,OAAO,WAAW,KAAK,IAAI,gBAAgB,WAAW,EAAG,OAAM;AACnE,OAAM,IAAIC,mCAAiB,QAAQ,IAAI,gBAAgB;;AAGzD,SAAS,UAAwB;AAC/B,QAAO;EAAE,KAAK;GAAE,UAAU,EAAE;GAAE,UAAU,EAAE;GAAE,iBAAiB,EAAE;GAAE,YAAY,EAAE;GAAE;EAAE,QAAQ,EAAE;EAAE;;;;;;;;;AAUjG,SAAgB,4BACd,UACA,QACA,aACA,aACsB;CACtB,MAAM,OAAO,SAAS;AACtB,kBAAiB,UAAU,eAAe,UAAU,MAAM,QAAQ,YAAY;AAC9E,QAAO,OAAO,KAAK;;AAGrB,SAAS,gBACP,SACA,QACA,MACA,QACA,aACM;CACN,MAAM,UAAUC,sCAAe,SAAS,OAAO;AAC/C,KAAI,YAAY,KAAM;AACtB,MAAK,MAAM,QAAQ,QAAQ,UACzB,sCAAsB,SAAS,KAAK,EAAE,SAAS,MAAM,MAAM,QAAQ,YAAY;AAEjF,MAAK,MAAM,OAAO,QAAQ,QACxB,qCAAqB,SAAS,IAAI,EAAE,GAAG,SAAS,IAAI,IAAI,MAAM,QAAQ,YAAY;;;;;;;;;;AAYtF,SAAgB,gCACd,SACA,QACA,aACsB;CACtB,MAAM,OAAO,SAAS;AACtB,iBAAgB,SAAS,IAAI,MAAM,QAAQ,YAAY;AACvD,QAAO,OAAO,KAAK"}
|
|
@@ -52,7 +52,7 @@ function loadServicesFile(filePath, source, load, logger, liveOptions) {
|
|
|
52
52
|
raw
|
|
53
53
|
});
|
|
54
54
|
}
|
|
55
|
-
out.fixtures.push(...fixturesOf(parsed, filePath, logger, liveOptions, withFakes));
|
|
55
|
+
out.fixtures.push(...fixturesOf(parsed, filePath, logger, liveOptions, withFakes, source));
|
|
56
56
|
}
|
|
57
57
|
/**
|
|
58
58
|
* The result of a finished load, or its errors thrown: the one
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"fixture-loader-services.js","names":[],"sources":["../src/fixture-loader-services.ts"],"sourcesContent":["/**\n * Fixture loaders that carry MCP fakes (spec F1): the `…WithServices`\n * variants of `loadFixtureFile` / `loadFixturesFromDir`. They return the LLM\n * fixtures and the raw `mcpFakes` blocks with their entry-id `source` (I6).\n *\n * They live outside fixture-loader.ts because they validate blocks with\n * `validateMcpFakes`, and mcp-fakes.ts imports `FixtureLoadError` from\n * fixture-loader.ts at module load (`McpFakesAddError extends\n * FixtureLoadError`): a value import of mcp-fakes.ts from fixture-loader.ts\n * is an import cycle that leaves `FixtureLoadError` uninitialized whenever\n * fixture-loader.ts is loaded first.\n */\nimport { join } from \"node:path\";\nimport type { LiveOptions } from \"./live-types.js\";\nimport type { Logger } from \"./logger.js\";\nimport {\n fixturesOf,\n hasMcpFakesKey,\n listFixtureDir,\n readFixtureJson,\n type FixtureLoadError,\n} from \"./fixture-loader.js\";\nimport { McpFakesAddError, validateMcpFakes, type McpFakeIssue } from \"./mcp-fakes.js\";\nimport type { Fixture, McpFakeSource } from \"./types.js\";\n\n/** What {@link loadFixtureFileWithServices} and {@link loadFixturesFromDirWithServices} return. */\nexport interface FixturesWithServices {\n /** LLM fixtures, as the old loaders return them. */\n fixtures: Fixture[];\n /**\n * The raw `mcpFakes` blocks, one per block, in load order, each with its\n * entry-id `source` (spec I6) and `blockIndex` (`null` for the\n * single-object form). Validated per file; checks across files and loads\n * (entry-id collisions) happen when a mount adds them.\n */\n mcpFakes: McpFakeSource[];\n /** Shadowed-entry warnings (L8) of those blocks, for the caller to print. */\n mcpFakeWarnings: McpFakeIssue[];\n /**\n * The entry-id `source` (I6) of each file that could not be read or was\n * not valid JSON. Such a file is warned and skipped, as before, so it adds\n * no fixtures and no blocks; a `--watch` reload uses this list to tell a\n * file it could not parse from a file whose `mcpFakes` were removed.\n */\n unreadable: string[];\n}\n\n/** A load in progress: the result, plus every bad-block error of every file. */\ninterface ServicesLoad {\n out: FixturesWithServices;\n errors: FixtureLoadError[];\n}\n\n/**\n * Load one parsed file into `load.out`: its `mcpFakes` blocks under\n * `source`, then its LLM fixtures. A file with a bad block adds nothing; its\n * errors go to `load.errors`, and the load goes on so every bad block of\n * every file is reported (fail-loud rule, spec 5.4; L3). Its L8 warnings are\n * kept either way.\n */\nfunction loadServicesFile(\n filePath: string,\n source: string,\n load: ServicesLoad,\n logger: Logger | undefined,\n liveOptions: LiveOptions | undefined,\n): void {\n const { out } = load;\n const read = readFixtureJson(filePath, logger);\n if (read === null) {\n out.unreadable.push(source);\n return;\n }\n const parsed = read.value;\n const withFakes = hasMcpFakesKey(parsed);\n if (withFakes) {\n const raw = parsed.mcpFakes;\n const validation = validateMcpFakes(raw, source);\n out.mcpFakeWarnings.push(...validation.warnings);\n if (validation.errors.length > 0) {\n load.errors.push(...validation.errors);\n return;\n }\n if (Array.isArray(raw)) {\n raw.forEach((block: unknown, blockIndex: number) => {\n out.mcpFakes.push({ source, blockIndex, raw: block });\n });\n } else {\n out.mcpFakes.push({ source, blockIndex: null, raw });\n }\n }\n out.fixtures.push(...fixturesOf(parsed, filePath, logger, liveOptions, withFakes));\n}\n\n/**\n * The result of a finished load, or its errors thrown: the one\n * `FixtureLoadError` alone, else a `McpFakesAddError` holding every error and\n * the L8 warnings, so no warning is lost when the load fails.\n */\nfunction finish(load: ServicesLoad): FixturesWithServices {\n const { out, errors } = load;\n const [first] = errors;\n if (first === undefined) return out;\n if (errors.length === 1 && out.mcpFakeWarnings.length === 0) throw first;\n throw new McpFakesAddError(errors, out.mcpFakeWarnings);\n}\n\nfunction newLoad(): ServicesLoad {\n return { out: { fixtures: [], mcpFakes: [], mcpFakeWarnings: [], unreadable: [] }, errors: [] };\n}\n\n/**\n * Load one fixture file: its LLM fixtures and its `mcpFakes` blocks. A\n * fakes-only file is accepted. The blocks' `source` (spec I6) is `filePath`\n * as given, or `sourceLabel` when set (the URL of a remote fixture file).\n * A bad block throws a `FixtureLoadError` (a `McpFakesAddError` when there\n * is more than one error, or L8 warnings to keep with it).\n */\nexport function loadFixtureFileWithServices(\n filePath: string,\n logger?: Logger,\n liveOptions?: LiveOptions,\n sourceLabel?: string,\n): FixturesWithServices {\n const load = newLoad();\n loadServicesFile(filePath, sourceLabel ?? filePath, load, logger, liveOptions);\n return finish(load);\n}\n\nfunction loadServicesDir(\n dirPath: string,\n relDir: string,\n load: ServicesLoad,\n logger: Logger | undefined,\n liveOptions: LiveOptions | undefined,\n): void {\n const listing = listFixtureDir(dirPath, logger);\n if (listing === null) return;\n for (const name of listing.jsonFiles) {\n loadServicesFile(join(dirPath, name), relDir + name, load, logger, liveOptions);\n }\n for (const sub of listing.subdirs) {\n loadServicesDir(join(dirPath, sub), `${relDir}${sub}/`, load, logger, liveOptions);\n }\n}\n\n/**\n * Load every `.json` file under `dirPath` (sorted, then subdirectories, at\n * full depth, as {@link loadFixturesFromDir}): LLM fixtures and `mcpFakes`\n * blocks. Each block's `source` (spec I6) is its file's path relative to\n * `dirPath`, with `/` separators. Every file is checked before anything is\n * thrown: the bad blocks of all files are reported together, as in\n * {@link loadFixtureFileWithServices}.\n */\nexport function loadFixturesFromDirWithServices(\n dirPath: string,\n logger?: Logger,\n liveOptions?: LiveOptions,\n): FixturesWithServices {\n const load = newLoad();\n loadServicesDir(dirPath, \"\", load, logger, liveOptions);\n return finish(load);\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;AA4DA,SAAS,iBACP,UACA,QACA,MACA,QACA,aACM;CACN,MAAM,EAAE,QAAQ;CAChB,MAAM,OAAO,gBAAgB,UAAU,OAAO;AAC9C,KAAI,SAAS,MAAM;AACjB,MAAI,WAAW,KAAK,OAAO;AAC3B;;CAEF,MAAM,SAAS,KAAK;CACpB,MAAM,YAAY,eAAe,OAAO;AACxC,KAAI,WAAW;EACb,MAAM,MAAM,OAAO;EACnB,MAAM,aAAa,iBAAiB,KAAK,OAAO;AAChD,MAAI,gBAAgB,KAAK,GAAG,WAAW,SAAS;AAChD,MAAI,WAAW,OAAO,SAAS,GAAG;AAChC,QAAK,OAAO,KAAK,GAAG,WAAW,OAAO;AACtC;;AAEF,MAAI,MAAM,QAAQ,IAAI,CACpB,KAAI,SAAS,OAAgB,eAAuB;AAClD,OAAI,SAAS,KAAK;IAAE;IAAQ;IAAY,KAAK;IAAO,CAAC;IACrD;MAEF,KAAI,SAAS,KAAK;GAAE;GAAQ,YAAY;GAAM;GAAK,CAAC;;AAGxD,KAAI,SAAS,KAAK,GAAG,WAAW,QAAQ,UAAU,QAAQ,aAAa,
|
|
1
|
+
{"version":3,"file":"fixture-loader-services.js","names":[],"sources":["../src/fixture-loader-services.ts"],"sourcesContent":["/**\n * Fixture loaders that carry MCP fakes (spec F1): the `…WithServices`\n * variants of `loadFixtureFile` / `loadFixturesFromDir`. They return the LLM\n * fixtures and the raw `mcpFakes` blocks with their entry-id `source` (I6).\n *\n * They live outside fixture-loader.ts because they validate blocks with\n * `validateMcpFakes`, and mcp-fakes.ts imports `FixtureLoadError` from\n * fixture-loader.ts at module load (`McpFakesAddError extends\n * FixtureLoadError`): a value import of mcp-fakes.ts from fixture-loader.ts\n * is an import cycle that leaves `FixtureLoadError` uninitialized whenever\n * fixture-loader.ts is loaded first.\n */\nimport { join } from \"node:path\";\nimport type { LiveOptions } from \"./live-types.js\";\nimport type { Logger } from \"./logger.js\";\nimport {\n fixturesOf,\n hasMcpFakesKey,\n listFixtureDir,\n readFixtureJson,\n type FixtureLoadError,\n} from \"./fixture-loader.js\";\nimport { McpFakesAddError, validateMcpFakes, type McpFakeIssue } from \"./mcp-fakes.js\";\nimport type { Fixture, McpFakeSource } from \"./types.js\";\n\n/** What {@link loadFixtureFileWithServices} and {@link loadFixturesFromDirWithServices} return. */\nexport interface FixturesWithServices {\n /** LLM fixtures, as the old loaders return them. */\n fixtures: Fixture[];\n /**\n * The raw `mcpFakes` blocks, one per block, in load order, each with its\n * entry-id `source` (spec I6) and `blockIndex` (`null` for the\n * single-object form). Validated per file; checks across files and loads\n * (entry-id collisions) happen when a mount adds them.\n */\n mcpFakes: McpFakeSource[];\n /** Shadowed-entry warnings (L8) of those blocks, for the caller to print. */\n mcpFakeWarnings: McpFakeIssue[];\n /**\n * The entry-id `source` (I6) of each file that could not be read or was\n * not valid JSON. Such a file is warned and skipped, as before, so it adds\n * no fixtures and no blocks; a `--watch` reload uses this list to tell a\n * file it could not parse from a file whose `mcpFakes` were removed.\n */\n unreadable: string[];\n}\n\n/** A load in progress: the result, plus every bad-block error of every file. */\ninterface ServicesLoad {\n out: FixturesWithServices;\n errors: FixtureLoadError[];\n}\n\n/**\n * Load one parsed file into `load.out`: its `mcpFakes` blocks under\n * `source`, then its LLM fixtures. A file with a bad block adds nothing; its\n * errors go to `load.errors`, and the load goes on so every bad block of\n * every file is reported (fail-loud rule, spec 5.4; L3). Its L8 warnings are\n * kept either way.\n */\nfunction loadServicesFile(\n filePath: string,\n source: string,\n load: ServicesLoad,\n logger: Logger | undefined,\n liveOptions: LiveOptions | undefined,\n): void {\n const { out } = load;\n const read = readFixtureJson(filePath, logger);\n if (read === null) {\n out.unreadable.push(source);\n return;\n }\n const parsed = read.value;\n const withFakes = hasMcpFakesKey(parsed);\n if (withFakes) {\n const raw = parsed.mcpFakes;\n const validation = validateMcpFakes(raw, source);\n out.mcpFakeWarnings.push(...validation.warnings);\n if (validation.errors.length > 0) {\n load.errors.push(...validation.errors);\n return;\n }\n if (Array.isArray(raw)) {\n raw.forEach((block: unknown, blockIndex: number) => {\n out.mcpFakes.push({ source, blockIndex, raw: block });\n });\n } else {\n out.mcpFakes.push({ source, blockIndex: null, raw });\n }\n }\n out.fixtures.push(...fixturesOf(parsed, filePath, logger, liveOptions, withFakes, source));\n}\n\n/**\n * The result of a finished load, or its errors thrown: the one\n * `FixtureLoadError` alone, else a `McpFakesAddError` holding every error and\n * the L8 warnings, so no warning is lost when the load fails.\n */\nfunction finish(load: ServicesLoad): FixturesWithServices {\n const { out, errors } = load;\n const [first] = errors;\n if (first === undefined) return out;\n if (errors.length === 1 && out.mcpFakeWarnings.length === 0) throw first;\n throw new McpFakesAddError(errors, out.mcpFakeWarnings);\n}\n\nfunction newLoad(): ServicesLoad {\n return { out: { fixtures: [], mcpFakes: [], mcpFakeWarnings: [], unreadable: [] }, errors: [] };\n}\n\n/**\n * Load one fixture file: its LLM fixtures and its `mcpFakes` blocks. A\n * fakes-only file is accepted. The blocks' `source` (spec I6) is `filePath`\n * as given, or `sourceLabel` when set (the URL of a remote fixture file).\n * A bad block throws a `FixtureLoadError` (a `McpFakesAddError` when there\n * is more than one error, or L8 warnings to keep with it).\n */\nexport function loadFixtureFileWithServices(\n filePath: string,\n logger?: Logger,\n liveOptions?: LiveOptions,\n sourceLabel?: string,\n): FixturesWithServices {\n const load = newLoad();\n loadServicesFile(filePath, sourceLabel ?? filePath, load, logger, liveOptions);\n return finish(load);\n}\n\nfunction loadServicesDir(\n dirPath: string,\n relDir: string,\n load: ServicesLoad,\n logger: Logger | undefined,\n liveOptions: LiveOptions | undefined,\n): void {\n const listing = listFixtureDir(dirPath, logger);\n if (listing === null) return;\n for (const name of listing.jsonFiles) {\n loadServicesFile(join(dirPath, name), relDir + name, load, logger, liveOptions);\n }\n for (const sub of listing.subdirs) {\n loadServicesDir(join(dirPath, sub), `${relDir}${sub}/`, load, logger, liveOptions);\n }\n}\n\n/**\n * Load every `.json` file under `dirPath` (sorted, then subdirectories, at\n * full depth, as {@link loadFixturesFromDir}): LLM fixtures and `mcpFakes`\n * blocks. Each block's `source` (spec I6) is its file's path relative to\n * `dirPath`, with `/` separators. Every file is checked before anything is\n * thrown: the bad blocks of all files are reported together, as in\n * {@link loadFixtureFileWithServices}.\n */\nexport function loadFixturesFromDirWithServices(\n dirPath: string,\n logger?: Logger,\n liveOptions?: LiveOptions,\n): FixturesWithServices {\n const load = newLoad();\n loadServicesDir(dirPath, \"\", load, logger, liveOptions);\n return finish(load);\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;AA4DA,SAAS,iBACP,UACA,QACA,MACA,QACA,aACM;CACN,MAAM,EAAE,QAAQ;CAChB,MAAM,OAAO,gBAAgB,UAAU,OAAO;AAC9C,KAAI,SAAS,MAAM;AACjB,MAAI,WAAW,KAAK,OAAO;AAC3B;;CAEF,MAAM,SAAS,KAAK;CACpB,MAAM,YAAY,eAAe,OAAO;AACxC,KAAI,WAAW;EACb,MAAM,MAAM,OAAO;EACnB,MAAM,aAAa,iBAAiB,KAAK,OAAO;AAChD,MAAI,gBAAgB,KAAK,GAAG,WAAW,SAAS;AAChD,MAAI,WAAW,OAAO,SAAS,GAAG;AAChC,QAAK,OAAO,KAAK,GAAG,WAAW,OAAO;AACtC;;AAEF,MAAI,MAAM,QAAQ,IAAI,CACpB,KAAI,SAAS,OAAgB,eAAuB;AAClD,OAAI,SAAS,KAAK;IAAE;IAAQ;IAAY,KAAK;IAAO,CAAC;IACrD;MAEF,KAAI,SAAS,KAAK;GAAE;GAAQ,YAAY;GAAM;GAAK,CAAC;;AAGxD,KAAI,SAAS,KAAK,GAAG,WAAW,QAAQ,UAAU,QAAQ,aAAa,WAAW,OAAO,CAAC;;;;;;;AAQ5F,SAAS,OAAO,MAA0C;CACxD,MAAM,EAAE,KAAK,WAAW;CACxB,MAAM,CAAC,SAAS;AAChB,KAAI,UAAU,OAAW,QAAO;AAChC,KAAI,OAAO,WAAW,KAAK,IAAI,gBAAgB,WAAW,EAAG,OAAM;AACnE,OAAM,IAAI,iBAAiB,QAAQ,IAAI,gBAAgB;;AAGzD,SAAS,UAAwB;AAC/B,QAAO;EAAE,KAAK;GAAE,UAAU,EAAE;GAAE,UAAU,EAAE;GAAE,iBAAiB,EAAE;GAAE,YAAY,EAAE;GAAE;EAAE,QAAQ,EAAE;EAAE;;;;;;;;;AAUjG,SAAgB,4BACd,UACA,QACA,aACA,aACsB;CACtB,MAAM,OAAO,SAAS;AACtB,kBAAiB,UAAU,eAAe,UAAU,MAAM,QAAQ,YAAY;AAC9E,QAAO,OAAO,KAAK;;AAGrB,SAAS,gBACP,SACA,QACA,MACA,QACA,aACM;CACN,MAAM,UAAU,eAAe,SAAS,OAAO;AAC/C,KAAI,YAAY,KAAM;AACtB,MAAK,MAAM,QAAQ,QAAQ,UACzB,kBAAiB,KAAK,SAAS,KAAK,EAAE,SAAS,MAAM,MAAM,QAAQ,YAAY;AAEjF,MAAK,MAAM,OAAO,QAAQ,QACxB,iBAAgB,KAAK,SAAS,IAAI,EAAE,GAAG,SAAS,IAAI,IAAI,MAAM,QAAQ,YAAY;;;;;;;;;;AAYtF,SAAgB,gCACd,SACA,QACA,aACsB;CACtB,MAAM,OAAO,SAAS;AACtB,iBAAgB,SAAS,IAAI,MAAM,QAAQ,YAAY;AACvD,QAAO,OAAO,KAAK"}
|