@tooluminati/testing 0.1.1 → 0.2.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.
@@ -0,0 +1,8 @@
1
+ import {
2
+ expectWebMcpTool,
3
+ invokeWebMcpTool
4
+ } from "./chunk-M554RXAL.js";
5
+ export {
6
+ expectWebMcpTool,
7
+ invokeWebMcpTool
8
+ };
@@ -1,6 +1,10 @@
1
+ export interface WebMcpInvocationOptions {
2
+ /** Select explicitly for browsers that require JSON string input. Never retries execution. */
3
+ inputFormat?: 'object' | 'json-string';
4
+ }
1
5
  export interface WebMcpTestPage {
2
6
  evaluate<T, A>(callback: (arg: A) => T | Promise<T>, arg: A): Promise<T>;
3
7
  }
4
8
  export declare function expectWebMcpTool(page: WebMcpTestPage, name: string): Promise<void>;
5
- export declare function invokeWebMcpTool<T>(page: WebMcpTestPage, name: string, args: unknown): Promise<T>;
9
+ export declare function invokeWebMcpTool<T>(page: WebMcpTestPage, name: string, args: unknown, options?: WebMcpInvocationOptions): Promise<T>;
6
10
  //# sourceMappingURL=browser-helpers.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"browser-helpers.d.ts","sourceRoot":"","sources":["../src/browser-helpers.ts"],"names":[],"mappings":"AAAA,MAAM,WAAW,cAAc;IAC7B,QAAQ,CAAC,CAAC,EAAE,CAAC,EACX,QAAQ,EAAE,CAAC,GAAG,EAAE,CAAC,KAAK,CAAC,GAAG,OAAO,CAAC,CAAC,CAAC,EACpC,GAAG,EAAE,CAAC,GACL,OAAO,CAAC,CAAC,CAAC,CAAC;CACf;AAED,wBAAsB,gBAAgB,CACpC,IAAI,EAAE,cAAc,EACpB,IAAI,EAAE,MAAM,GACX,OAAO,CAAC,IAAI,CAAC,CAef;AAED,wBAAsB,gBAAgB,CAAC,CAAC,EACtC,IAAI,EAAE,cAAc,EACpB,IAAI,EAAE,MAAM,EACZ,IAAI,EAAE,OAAO,GACZ,OAAO,CAAC,CAAC,CAAC,CAoBZ"}
1
+ {"version":3,"file":"browser-helpers.d.ts","sourceRoot":"","sources":["../src/browser-helpers.ts"],"names":[],"mappings":"AAAA,MAAM,WAAW,uBAAuB;IACtC,8FAA8F;IAC9F,WAAW,CAAC,EAAE,QAAQ,GAAG,aAAa,CAAC;CACxC;AAED,MAAM,WAAW,cAAc;IAC7B,QAAQ,CAAC,CAAC,EAAE,CAAC,EAAE,QAAQ,EAAE,CAAC,GAAG,EAAE,CAAC,KAAK,CAAC,GAAG,OAAO,CAAC,CAAC,CAAC,EAAE,GAAG,EAAE,CAAC,GAAG,OAAO,CAAC,CAAC,CAAC,CAAC;CAC1E;AAED,wBAAsB,gBAAgB,CACpC,IAAI,EAAE,cAAc,EACpB,IAAI,EAAE,MAAM,GACX,OAAO,CAAC,IAAI,CAAC,CAef;AAED,wBAAsB,gBAAgB,CAAC,CAAC,EACtC,IAAI,EAAE,cAAc,EACpB,IAAI,EAAE,MAAM,EACZ,IAAI,EAAE,OAAO,EACb,OAAO,GAAE,uBAA4B,GACpC,OAAO,CAAC,CAAC,CAAC,CA4BZ"}
@@ -9,8 +9,8 @@ export async function expectWebMcpTool(page, name) {
9
9
  throw new Error(`Expected WebMCP tool "${name}" to be registered.`);
10
10
  }
11
11
  }
12
- export async function invokeWebMcpTool(page, name, args) {
13
- return page.evaluate(async ({ toolName, input }) => {
12
+ export async function invokeWebMcpTool(page, name, args, options = {}) {
13
+ return page.evaluate(async ({ toolName, input, inputFormat }) => {
14
14
  const context = document
15
15
  .modelContext;
16
16
  const tools = (await context?.getTools?.()) ?? [];
@@ -18,6 +18,7 @@ export async function invokeWebMcpTool(page, name, args) {
18
18
  if (!tool || !context?.executeTool) {
19
19
  throw new Error(`WebMCP tool "${toolName}" cannot be invoked.`);
20
20
  }
21
- return context.executeTool(tool, JSON.stringify(input));
22
- }, { toolName: name, input: args });
21
+ const payload = input && typeof input === 'object' ? input : {};
22
+ return context.executeTool(tool, inputFormat === 'json-string' ? JSON.stringify(payload) : payload);
23
+ }, { toolName: name, input: args, inputFormat: options.inputFormat });
23
24
  }
@@ -0,0 +1,146 @@
1
+ import {
2
+ invokeWebMcpTool
3
+ } from "./chunk-M554RXAL.js";
4
+
5
+ // src/dom-only-inspection.ts
6
+ function pushUnique(target, value) {
7
+ const trimmed = value?.trim();
8
+ if (!trimmed || target.includes(trimmed)) {
9
+ return;
10
+ }
11
+ target.push(trimmed);
12
+ }
13
+ function collectDisabledActionReasonsFromDom(root, buttonLabel) {
14
+ const buttons = [...root.querySelectorAll("button")];
15
+ const button = buttons.find(
16
+ (candidate) => candidate.textContent?.includes(buttonLabel)
17
+ );
18
+ if (!button) {
19
+ return {
20
+ label: buttonLabel,
21
+ disabled: false,
22
+ visibleBlockerReasons: []
23
+ };
24
+ }
25
+ const visibleBlockerReasons = [];
26
+ pushUnique(visibleBlockerReasons, button.getAttribute("title"));
27
+ pushUnique(
28
+ visibleBlockerReasons,
29
+ button.getAttribute("aria-label")?.includes("because") ? button.getAttribute("aria-label") : null
30
+ );
31
+ const describedBy = button.getAttribute("aria-describedby");
32
+ if (describedBy) {
33
+ for (const id of describedBy.split(/\s+/)) {
34
+ pushUnique(
35
+ visibleBlockerReasons,
36
+ root.getElementById(id)?.textContent
37
+ );
38
+ }
39
+ }
40
+ const fieldset = button.closest("fieldset");
41
+ if (fieldset) {
42
+ pushUnique(
43
+ visibleBlockerReasons,
44
+ fieldset.querySelector("legend")?.textContent
45
+ );
46
+ }
47
+ const row = button.closest('[data-testid="checkout-row"]');
48
+ if (row) {
49
+ pushUnique(
50
+ visibleBlockerReasons,
51
+ row.querySelector("[data-dom-blocker-hint]")?.textContent
52
+ );
53
+ }
54
+ return {
55
+ label: buttonLabel,
56
+ disabled: button.disabled,
57
+ visibleBlockerReasons
58
+ };
59
+ }
60
+ async function inspectDisabledActionFromDom(page, buttonLabel) {
61
+ return page.evaluate((label) => {
62
+ const buttons = [...document.querySelectorAll("button")];
63
+ const button = buttons.find(
64
+ (candidate) => candidate.textContent?.includes(label)
65
+ );
66
+ if (!button) {
67
+ return {
68
+ label,
69
+ disabled: false,
70
+ visibleBlockerReasons: []
71
+ };
72
+ }
73
+ const visibleBlockerReasons = [];
74
+ const push = (value) => {
75
+ const trimmed = value?.trim();
76
+ if (trimmed && !visibleBlockerReasons.includes(trimmed)) {
77
+ visibleBlockerReasons.push(trimmed);
78
+ }
79
+ };
80
+ push(button.getAttribute("title"));
81
+ const ariaLabel = button.getAttribute("aria-label");
82
+ if (ariaLabel?.includes("because")) {
83
+ push(ariaLabel);
84
+ }
85
+ const describedBy = button.getAttribute("aria-describedby");
86
+ if (describedBy) {
87
+ for (const id of describedBy.split(/\s+/)) {
88
+ push(document.getElementById(id)?.textContent);
89
+ }
90
+ }
91
+ const fieldset = button.closest("fieldset");
92
+ if (fieldset) {
93
+ push(fieldset.querySelector("legend")?.textContent);
94
+ }
95
+ const row = button.closest('[data-testid="checkout-row"]');
96
+ if (row) {
97
+ push(row.querySelector("[data-dom-blocker-hint]")?.textContent);
98
+ }
99
+ return {
100
+ label,
101
+ disabled: button.disabled,
102
+ visibleBlockerReasons
103
+ };
104
+ }, buttonLabel);
105
+ }
106
+
107
+ // src/comparative-proof.ts
108
+ async function diagnoseCheckoutBlockerFromDomOnly(page, buttonLabel) {
109
+ return inspectDisabledActionFromDom(page, buttonLabel);
110
+ }
111
+ async function diagnoseCheckoutBlockerFromWebMcp(page, actionId, toolName = "why_is_action_unavailable") {
112
+ const result = await invokeWebMcpTool(page, toolName, { actionId });
113
+ return {
114
+ actionId: result.actionId,
115
+ available: result.available,
116
+ reasons: result.reasons ?? []
117
+ };
118
+ }
119
+ async function runComparativeProof(page, options) {
120
+ const domOnly = await diagnoseCheckoutBlockerFromDomOnly(
121
+ page,
122
+ options.buttonLabel
123
+ );
124
+ const webMcp = await diagnoseCheckoutBlockerFromWebMcp(
125
+ page,
126
+ options.actionId,
127
+ options.toolName
128
+ );
129
+ const domBlockerCount = domOnly.visibleBlockerReasons.length;
130
+ const webMcpBlockerCount = webMcp.reasons.length;
131
+ return {
132
+ domOnly,
133
+ webMcp,
134
+ domBlockerCount,
135
+ webMcpBlockerCount,
136
+ webMcpIsStrictlyMoreInformative: domOnly.disabled && domBlockerCount === 0 && webMcpBlockerCount > 0 && !webMcp.available
137
+ };
138
+ }
139
+
140
+ export {
141
+ collectDisabledActionReasonsFromDom,
142
+ inspectDisabledActionFromDom,
143
+ diagnoseCheckoutBlockerFromDomOnly,
144
+ diagnoseCheckoutBlockerFromWebMcp,
145
+ runComparativeProof
146
+ };
@@ -0,0 +1,34 @@
1
+ // src/browser-helpers.ts
2
+ async function expectWebMcpTool(page, name) {
3
+ const exists = await page.evaluate(async (toolName) => {
4
+ const context = document.modelContext;
5
+ const tools = await context?.getTools?.() ?? [];
6
+ return tools.some((tool) => tool.name === toolName);
7
+ }, name);
8
+ if (!exists) {
9
+ throw new Error(`Expected WebMCP tool "${name}" to be registered.`);
10
+ }
11
+ }
12
+ async function invokeWebMcpTool(page, name, args, options = {}) {
13
+ return page.evaluate(
14
+ async ({ toolName, input, inputFormat }) => {
15
+ const context = document.modelContext;
16
+ const tools = await context?.getTools?.() ?? [];
17
+ const tool = tools.find((candidate) => candidate.name === toolName);
18
+ if (!tool || !context?.executeTool) {
19
+ throw new Error(`WebMCP tool "${toolName}" cannot be invoked.`);
20
+ }
21
+ const payload = input && typeof input === "object" ? input : {};
22
+ return context.executeTool(
23
+ tool,
24
+ inputFormat === "json-string" ? JSON.stringify(payload) : payload
25
+ );
26
+ },
27
+ { toolName: name, input: args, inputFormat: options.inputFormat }
28
+ );
29
+ }
30
+
31
+ export {
32
+ expectWebMcpTool,
33
+ invokeWebMcpTool
34
+ };
@@ -0,0 +1,11 @@
1
+ import {
2
+ diagnoseCheckoutBlockerFromDomOnly,
3
+ diagnoseCheckoutBlockerFromWebMcp,
4
+ runComparativeProof
5
+ } from "./chunk-K6KLEZGY.js";
6
+ import "./chunk-M554RXAL.js";
7
+ export {
8
+ diagnoseCheckoutBlockerFromDomOnly,
9
+ diagnoseCheckoutBlockerFromWebMcp,
10
+ runComparativeProof
11
+ };
@@ -1,3 +1,4 @@
1
+ import type { WebMcpInvocationOptions } from './browser-helpers';
1
2
  export interface WebMcpToolListing {
2
3
  name: string;
3
4
  description?: string | undefined;
@@ -5,13 +6,13 @@ export interface WebMcpToolListing {
5
6
  type ModelContextHost = Document | Navigator;
6
7
  /**
7
8
  * Lists registered WebMCP tools from the browser model context.
8
- * Returns an empty array when WebMCP is unavailable (e.g. CI without Chrome 149).
9
+ * Returns an empty array when WebMCP is unavailable.
9
10
  */
10
11
  export declare function listWebMcpTools(host?: ModelContextHost): Promise<WebMcpToolListing[]>;
11
12
  /**
12
13
  * Executes a registered WebMCP tool by name.
13
- * Throws when WebMCP is unavailable or the tool cannot be resolved.
14
+ * Uses object input by default; legacy JSON string input must be explicitly selected.
14
15
  */
15
- export declare function executeWebMcpTool(name: string, args?: unknown, host?: ModelContextHost): Promise<unknown>;
16
+ export declare function executeWebMcpTool(name: string, args?: unknown, host?: ModelContextHost, options?: WebMcpInvocationOptions): Promise<unknown>;
16
17
  export {};
17
18
  //# sourceMappingURL=devtools-mcp-helper.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"devtools-mcp-helper.d.ts","sourceRoot":"","sources":["../src/devtools-mcp-helper.ts"],"names":[],"mappings":"AAAA,MAAM,WAAW,iBAAiB;IAChC,IAAI,EAAE,MAAM,CAAC;IACb,WAAW,CAAC,EAAE,MAAM,GAAG,SAAS,CAAC;CAClC;AAED,KAAK,gBAAgB,GAAG,QAAQ,GAAG,SAAS,CAAC;AAsB7C;;;GAGG;AACH,wBAAsB,eAAe,CACnC,IAAI,GAAE,gBAA2B,GAChC,OAAO,CAAC,iBAAiB,EAAE,CAAC,CAW9B;AAED;;;GAGG;AACH,wBAAsB,iBAAiB,CACrC,IAAI,EAAE,MAAM,EACZ,IAAI,GAAE,OAAY,EAClB,IAAI,GAAE,gBAA2B,GAChC,OAAO,CAAC,OAAO,CAAC,CAelB"}
1
+ {"version":3,"file":"devtools-mcp-helper.d.ts","sourceRoot":"","sources":["../src/devtools-mcp-helper.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,uBAAuB,EAAE,MAAM,mBAAmB,CAAC;AAGjE,MAAM,WAAW,iBAAiB;IAChC,IAAI,EAAE,MAAM,CAAC;IACb,WAAW,CAAC,EAAE,MAAM,GAAG,SAAS,CAAC;CAClC;AAED,KAAK,gBAAgB,GAAG,QAAQ,GAAG,SAAS,CAAC;AA6B7C;;;GAGG;AACH,wBAAsB,eAAe,CACnC,IAAI,GAAE,gBAA2B,GAChC,OAAO,CAAC,iBAAiB,EAAE,CAAC,CAW9B;AAED;;;GAGG;AACH,wBAAsB,iBAAiB,CACrC,IAAI,EAAE,MAAM,EACZ,IAAI,GAAE,OAAY,EAClB,IAAI,GAAE,gBAA2B,EACjC,OAAO,GAAE,uBAA4B,GACpC,OAAO,CAAC,OAAO,CAAC,CAmBlB"}
@@ -1,9 +1,10 @@
1
1
  function getModelContext(host = document) {
2
- return host.modelContext;
2
+ return host
3
+ .modelContext;
3
4
  }
4
5
  /**
5
6
  * Lists registered WebMCP tools from the browser model context.
6
- * Returns an empty array when WebMCP is unavailable (e.g. CI without Chrome 149).
7
+ * Returns an empty array when WebMCP is unavailable.
7
8
  */
8
9
  export async function listWebMcpTools(host = document) {
9
10
  const context = getModelContext(host);
@@ -18,9 +19,9 @@ export async function listWebMcpTools(host = document) {
18
19
  }
19
20
  /**
20
21
  * Executes a registered WebMCP tool by name.
21
- * Throws when WebMCP is unavailable or the tool cannot be resolved.
22
+ * Uses object input by default; legacy JSON string input must be explicitly selected.
22
23
  */
23
- export async function executeWebMcpTool(name, args = {}, host = document) {
24
+ export async function executeWebMcpTool(name, args = {}, host = document, options = {}) {
24
25
  const context = getModelContext(host);
25
26
  if (!context?.getTools || !context.executeTool) {
26
27
  throw new Error('WebMCP model context is unavailable. Use installModelContextMock() in tests.');
@@ -30,5 +31,6 @@ export async function executeWebMcpTool(name, args = {}, host = document) {
30
31
  if (!tool) {
31
32
  throw new Error(`WebMCP tool not found: ${name}`);
32
33
  }
33
- return context.executeTool(tool, JSON.stringify(args));
34
+ const payload = args && typeof args === 'object' ? args : {};
35
+ return context.executeTool(tool, options.inputFormat === 'json-string' ? JSON.stringify(payload) : payload);
34
36
  }
package/dist/index.cjs CHANGED
@@ -36,18 +36,22 @@ async function expectWebMcpTool(page, name) {
36
36
  throw new Error(`Expected WebMCP tool "${name}" to be registered.`);
37
37
  }
38
38
  }
39
- async function invokeWebMcpTool(page, name, args) {
39
+ async function invokeWebMcpTool(page, name, args, options = {}) {
40
40
  return page.evaluate(
41
- async ({ toolName, input }) => {
41
+ async ({ toolName, input, inputFormat }) => {
42
42
  const context = document.modelContext;
43
43
  const tools = await context?.getTools?.() ?? [];
44
44
  const tool = tools.find((candidate) => candidate.name === toolName);
45
45
  if (!tool || !context?.executeTool) {
46
46
  throw new Error(`WebMCP tool "${toolName}" cannot be invoked.`);
47
47
  }
48
- return context.executeTool(tool, JSON.stringify(input));
48
+ const payload = input && typeof input === "object" ? input : {};
49
+ return context.executeTool(
50
+ tool,
51
+ inputFormat === "json-string" ? JSON.stringify(payload) : payload
52
+ );
49
53
  },
50
- { toolName: name, input: args }
54
+ { toolName: name, input: args, inputFormat: options.inputFormat }
51
55
  );
52
56
  }
53
57
  var init_browser_helpers = __esm({
@@ -225,6 +229,7 @@ __export(index_exports, {
225
229
  installModelContextMock: () => installModelContextMock,
226
230
  invokeWebMcpTool: () => invokeWebMcpTool,
227
231
  listWebMcpTools: () => listWebMcpTools,
232
+ parseExecuteToolInput: () => parseExecuteToolInput,
228
233
  runComparativeProof: () => runComparativeProof,
229
234
  runTimelineComparativeProof: () => runTimelineComparativeProof,
230
235
  runToolSelectionSmokeTest: () => runToolSelectionSmokeTest,
@@ -249,7 +254,7 @@ async function listWebMcpTools(host = document) {
249
254
  description: tool.description
250
255
  }));
251
256
  }
252
- async function executeWebMcpTool(name, args = {}, host = document) {
257
+ async function executeWebMcpTool(name, args = {}, host = document, options = {}) {
253
258
  const context = getModelContext(host);
254
259
  if (!context?.getTools || !context.executeTool) {
255
260
  throw new Error(
@@ -261,13 +266,23 @@ async function executeWebMcpTool(name, args = {}, host = document) {
261
266
  if (!tool) {
262
267
  throw new Error(`WebMCP tool not found: ${name}`);
263
268
  }
264
- return context.executeTool(tool, JSON.stringify(args));
269
+ const payload = args && typeof args === "object" ? args : {};
270
+ return context.executeTool(
271
+ tool,
272
+ options.inputFormat === "json-string" ? JSON.stringify(payload) : payload
273
+ );
265
274
  }
266
275
 
267
276
  // src/index.ts
268
277
  init_dom_only_inspection();
269
278
 
270
279
  // src/mock-model-context.ts
280
+ function parseExecuteToolInput(input) {
281
+ if (typeof input === "string") {
282
+ return JSON.parse(input || "{}");
283
+ }
284
+ return input ?? {};
285
+ }
271
286
  var MockModelContext = class extends EventTarget {
272
287
  tools = /* @__PURE__ */ new Map();
273
288
  invocations = [];
@@ -292,7 +307,7 @@ var MockModelContext = class extends EventTarget {
292
307
  async getTools() {
293
308
  return [...this.tools.values()];
294
309
  }
295
- async executeTool(toolOrName, argsJson = "{}") {
310
+ async executeTool(toolOrName, input = {}, options = {}) {
296
311
  const name = typeof toolOrName === "string" ? toolOrName : toolOrName.name;
297
312
  if (!name) {
298
313
  throw new Error("Tool name is required.");
@@ -301,10 +316,10 @@ var MockModelContext = class extends EventTarget {
301
316
  if (!tool) {
302
317
  throw new Error(`Tool not found: ${name}`);
303
318
  }
304
- const args = JSON.parse(argsJson);
319
+ const args = parseExecuteToolInput(input);
305
320
  try {
306
321
  const result = await tool.execute(args, {
307
- signal: new AbortController().signal
322
+ signal: options.signal ?? new AbortController().signal
308
323
  });
309
324
  this.invocations.push({ name, args, result });
310
325
  return result;
@@ -331,6 +346,13 @@ var MODEL_CONTEXT_MOCK_INIT_SCRIPT = `
331
346
  (() => {
332
347
  const tools = new Map();
333
348
 
349
+ function parseInput(input) {
350
+ if (typeof input === 'string') {
351
+ return JSON.parse(input || '{}');
352
+ }
353
+ return input ?? {};
354
+ }
355
+
334
356
  Object.defineProperty(document, 'modelContext', {
335
357
  configurable: true,
336
358
  value: {
@@ -351,7 +373,7 @@ var MODEL_CONTEXT_MOCK_INIT_SCRIPT = `
351
373
  async getTools() {
352
374
  return [...tools.values()];
353
375
  },
354
- async executeTool(toolOrName, argsJson = '{}') {
376
+ async executeTool(toolOrName, input = {}, options = {}) {
355
377
  const name =
356
378
  typeof toolOrName === 'string'
357
379
  ? toolOrName
@@ -361,7 +383,9 @@ var MODEL_CONTEXT_MOCK_INIT_SCRIPT = `
361
383
  throw new Error('Tool not found: ' + name);
362
384
  }
363
385
 
364
- return tool.execute(JSON.parse(argsJson));
386
+ return tool.execute(parseInput(input), {
387
+ signal: options.signal ?? new AbortController().signal,
388
+ });
365
389
  },
366
390
  },
367
391
  });
@@ -466,6 +490,7 @@ async function runTimelineComparativeProof(page, options) {
466
490
  installModelContextMock,
467
491
  invokeWebMcpTool,
468
492
  listWebMcpTools,
493
+ parseExecuteToolInput,
469
494
  runComparativeProof,
470
495
  runTimelineComparativeProof,
471
496
  runToolSelectionSmokeTest,
package/dist/index.js CHANGED
@@ -4,11 +4,11 @@ import {
4
4
  diagnoseCheckoutBlockerFromWebMcp,
5
5
  inspectDisabledActionFromDom,
6
6
  runComparativeProof
7
- } from "./chunk-EG3BG65X.js";
7
+ } from "./chunk-K6KLEZGY.js";
8
8
  import {
9
9
  expectWebMcpTool,
10
10
  invokeWebMcpTool
11
- } from "./chunk-6YAIOYYZ.js";
11
+ } from "./chunk-M554RXAL.js";
12
12
 
13
13
  // src/devtools-mcp-helper.ts
14
14
  function getModelContext(host = document) {
@@ -25,7 +25,7 @@ async function listWebMcpTools(host = document) {
25
25
  description: tool.description
26
26
  }));
27
27
  }
28
- async function executeWebMcpTool(name, args = {}, host = document) {
28
+ async function executeWebMcpTool(name, args = {}, host = document, options = {}) {
29
29
  const context = getModelContext(host);
30
30
  if (!context?.getTools || !context.executeTool) {
31
31
  throw new Error(
@@ -37,10 +37,20 @@ async function executeWebMcpTool(name, args = {}, host = document) {
37
37
  if (!tool) {
38
38
  throw new Error(`WebMCP tool not found: ${name}`);
39
39
  }
40
- return context.executeTool(tool, JSON.stringify(args));
40
+ const payload = args && typeof args === "object" ? args : {};
41
+ return context.executeTool(
42
+ tool,
43
+ options.inputFormat === "json-string" ? JSON.stringify(payload) : payload
44
+ );
41
45
  }
42
46
 
43
47
  // src/mock-model-context.ts
48
+ function parseExecuteToolInput(input) {
49
+ if (typeof input === "string") {
50
+ return JSON.parse(input || "{}");
51
+ }
52
+ return input ?? {};
53
+ }
44
54
  var MockModelContext = class extends EventTarget {
45
55
  tools = /* @__PURE__ */ new Map();
46
56
  invocations = [];
@@ -65,7 +75,7 @@ var MockModelContext = class extends EventTarget {
65
75
  async getTools() {
66
76
  return [...this.tools.values()];
67
77
  }
68
- async executeTool(toolOrName, argsJson = "{}") {
78
+ async executeTool(toolOrName, input = {}, options = {}) {
69
79
  const name = typeof toolOrName === "string" ? toolOrName : toolOrName.name;
70
80
  if (!name) {
71
81
  throw new Error("Tool name is required.");
@@ -74,10 +84,10 @@ var MockModelContext = class extends EventTarget {
74
84
  if (!tool) {
75
85
  throw new Error(`Tool not found: ${name}`);
76
86
  }
77
- const args = JSON.parse(argsJson);
87
+ const args = parseExecuteToolInput(input);
78
88
  try {
79
89
  const result = await tool.execute(args, {
80
- signal: new AbortController().signal
90
+ signal: options.signal ?? new AbortController().signal
81
91
  });
82
92
  this.invocations.push({ name, args, result });
83
93
  return result;
@@ -104,6 +114,13 @@ var MODEL_CONTEXT_MOCK_INIT_SCRIPT = `
104
114
  (() => {
105
115
  const tools = new Map();
106
116
 
117
+ function parseInput(input) {
118
+ if (typeof input === 'string') {
119
+ return JSON.parse(input || '{}');
120
+ }
121
+ return input ?? {};
122
+ }
123
+
107
124
  Object.defineProperty(document, 'modelContext', {
108
125
  configurable: true,
109
126
  value: {
@@ -124,7 +141,7 @@ var MODEL_CONTEXT_MOCK_INIT_SCRIPT = `
124
141
  async getTools() {
125
142
  return [...tools.values()];
126
143
  },
127
- async executeTool(toolOrName, argsJson = '{}') {
144
+ async executeTool(toolOrName, input = {}, options = {}) {
128
145
  const name =
129
146
  typeof toolOrName === 'string'
130
147
  ? toolOrName
@@ -134,7 +151,9 @@ var MODEL_CONTEXT_MOCK_INIT_SCRIPT = `
134
151
  throw new Error('Tool not found: ' + name);
135
152
  }
136
153
 
137
- return tool.execute(JSON.parse(argsJson));
154
+ return tool.execute(parseInput(input), {
155
+ signal: options.signal ?? new AbortController().signal,
156
+ });
138
157
  },
139
158
  },
140
159
  });
@@ -208,8 +227,8 @@ function runToolSelectionSmokeTest(tools, prompt, expected) {
208
227
  return haystack.includes(expected.toLowerCase()) || tools.some((t) => t.name === expected);
209
228
  }
210
229
  async function runTimelineComparativeProof(page, options) {
211
- const { runComparativeProof: runComparativeProof2 } = await import("./comparative-proof-QUN7C3KM.js");
212
- const { invokeWebMcpTool: invokeWebMcpTool2 } = await import("./browser-helpers-RSQLRHHZ.js");
230
+ const { runComparativeProof: runComparativeProof2 } = await import("./comparative-proof-IVDBBIIG.js");
231
+ const { invokeWebMcpTool: invokeWebMcpTool2 } = await import("./browser-helpers-V33B4UD5.js");
213
232
  const proof = await runComparativeProof2(page, {
214
233
  buttonLabel: options.buttonLabel,
215
234
  actionId: options.actionId
@@ -238,6 +257,7 @@ export {
238
257
  installModelContextMock,
239
258
  invokeWebMcpTool,
240
259
  listWebMcpTools,
260
+ parseExecuteToolInput,
241
261
  runComparativeProof,
242
262
  runTimelineComparativeProof,
243
263
  runToolSelectionSmokeTest,
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=invocation.test.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"invocation.test.d.ts","sourceRoot":"","sources":["../src/invocation.test.ts"],"names":[],"mappings":""}
@@ -0,0 +1,95 @@
1
+ import { afterEach, describe, expect, it, vi } from 'vitest';
2
+ import { expectWebMcpTool, invokeWebMcpTool, } from './browser-helpers';
3
+ import { executeWebMcpTool, listWebMcpTools } from './devtools-mcp-helper';
4
+ const page = {
5
+ evaluate: async (callback, arg) => callback(arg),
6
+ };
7
+ const original = Object.getOwnPropertyDescriptor(document, 'modelContext');
8
+ afterEach(() => {
9
+ if (original)
10
+ Object.defineProperty(document, 'modelContext', original);
11
+ else
12
+ Reflect.deleteProperty(document, 'modelContext');
13
+ });
14
+ function install(context) {
15
+ Object.defineProperty(document, 'modelContext', {
16
+ configurable: true,
17
+ value: context,
18
+ });
19
+ }
20
+ const tool = { name: 'write_item', description: 'Writes an item.' };
21
+ const callers = [
22
+ [
23
+ 'DevTools',
24
+ (args, options) => executeWebMcpTool(tool.name, args, document, options),
25
+ ],
26
+ [
27
+ 'Playwright',
28
+ (args, options) => invokeWebMcpTool(page, tool.name, args, options),
29
+ ],
30
+ ];
31
+ for (const [label, invoke] of callers) {
32
+ describe(label, () => {
33
+ it('passes the discovered descriptor and object exactly once', async () => {
34
+ const executeTool = vi.fn().mockResolvedValue({ saved: true });
35
+ install({ getTools: async () => [tool], executeTool });
36
+ await expect(invoke({ id: 3 })).resolves.toEqual({ saved: true });
37
+ expect(executeTool).toHaveBeenCalledExactlyOnceWith(tool, { id: 3 });
38
+ });
39
+ it('selects legacy string encoding before executing', async () => {
40
+ const executeTool = vi.fn().mockResolvedValue('saved');
41
+ install({ getTools: async () => [tool], executeTool });
42
+ await expect(invoke({ id: 3 }, { inputFormat: 'json-string' })).resolves.toBe('saved');
43
+ expect(executeTool).toHaveBeenCalledExactlyOnceWith(tool, '{"id":3}');
44
+ });
45
+ it.each([
46
+ new TypeError('handler failed after write'),
47
+ new Error('failed'),
48
+ new DOMException('cancelled', 'AbortError'),
49
+ ])('never retries a failed execution (%s)', async (error) => {
50
+ const executeTool = vi.fn().mockRejectedValue(error);
51
+ install({ getTools: async () => [tool], executeTool });
52
+ await expect(invoke({})).rejects.toBe(error);
53
+ expect(executeTool).toHaveBeenCalledTimes(1);
54
+ });
55
+ it('propagates legacy failures without retrying', async () => {
56
+ const error = new TypeError('legacy failure');
57
+ const executeTool = vi.fn().mockRejectedValue(error);
58
+ install({ getTools: async () => [tool], executeTool });
59
+ await expect(invoke({}, { inputFormat: 'json-string' })).rejects.toBe(error);
60
+ expect(executeTool).toHaveBeenCalledTimes(1);
61
+ });
62
+ it.each([undefined, null, 5, 'text'])('normalizes non-object input %s to empty input', async (input) => {
63
+ const executeTool = vi.fn();
64
+ install({ getTools: async () => [tool], executeTool });
65
+ await invoke(input);
66
+ expect(executeTool).toHaveBeenCalledExactlyOnceWith(tool, {});
67
+ });
68
+ it.each([undefined, {}, { getTools: async () => [] }])('rejects unavailable tools or APIs', async (context) => {
69
+ install(context);
70
+ await expect(invoke({})).rejects.toThrow();
71
+ });
72
+ it('never invokes a missing tool', async () => {
73
+ const executeTool = vi.fn();
74
+ install({ getTools: async () => [], executeTool });
75
+ await expect(invoke({})).rejects.toThrow();
76
+ expect(executeTool).not.toHaveBeenCalled();
77
+ });
78
+ it('propagates discovery errors', async () => {
79
+ const error = new Error('discovery failed');
80
+ const executeTool = vi.fn();
81
+ install({ getTools: vi.fn().mockRejectedValue(error), executeTool });
82
+ await expect(invoke({})).rejects.toBe(error);
83
+ expect(executeTool).not.toHaveBeenCalled();
84
+ });
85
+ });
86
+ }
87
+ it('lists safe metadata and checks tool presence', async () => {
88
+ install({ getTools: async () => [{ ...tool, execute: () => undefined }] });
89
+ await expect(listWebMcpTools()).resolves.toEqual([tool]);
90
+ await expect(expectWebMcpTool(page, tool.name)).resolves.toBeUndefined();
91
+ await expect(expectWebMcpTool(page, 'missing')).rejects.toThrow('missing');
92
+ install(undefined);
93
+ await expect(listWebMcpTools()).resolves.toEqual([]);
94
+ await expect(expectWebMcpTool(page, tool.name)).rejects.toThrow(tool.name);
95
+ });
@@ -1,5 +1,6 @@
1
- import type { BrowserModelContextTestingExtensions, BrowserWebMcpToolDescriptor, WebMcpRegisterToolOptions } from '@tooluminati/core';
2
- export declare class MockModelContext extends EventTarget implements BrowserModelContextTestingExtensions {
1
+ import type { BrowserModelContext, BrowserWebMcpToolDescriptor, WebMcpExecuteToolOptions, WebMcpRegisterToolOptions } from '@tooluminati/core';
2
+ export declare function parseExecuteToolInput(input: unknown): unknown;
3
+ export declare class MockModelContext extends EventTarget implements BrowserModelContext {
3
4
  readonly tools: Map<string, BrowserWebMcpToolDescriptor>;
4
5
  readonly invocations: Array<{
5
6
  name: string;
@@ -9,6 +10,6 @@ export declare class MockModelContext extends EventTarget implements BrowserMode
9
10
  }>;
10
11
  registerTool(tool: BrowserWebMcpToolDescriptor, options?: WebMcpRegisterToolOptions): void;
11
12
  getTools(): Promise<BrowserWebMcpToolDescriptor[]>;
12
- executeTool(toolOrName: unknown, argsJson?: string): Promise<unknown>;
13
+ executeTool(toolOrName: unknown, input?: object | string, options?: WebMcpExecuteToolOptions): Promise<unknown>;
13
14
  }
14
15
  //# sourceMappingURL=mock-model-context.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"mock-model-context.d.ts","sourceRoot":"","sources":["../src/mock-model-context.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EACV,oCAAoC,EACpC,2BAA2B,EAC3B,yBAAyB,EAC1B,MAAM,mBAAmB,CAAC;AAE3B,qBAAa,gBACX,SAAQ,WACR,YAAW,oCAAoC;IAE/C,QAAQ,CAAC,KAAK,2CAAkD;IAChE,QAAQ,CAAC,WAAW,EAAE,KAAK,CAAC;QAC1B,IAAI,EAAE,MAAM,CAAC;QACb,IAAI,EAAE,OAAO,CAAC;QACd,MAAM,CAAC,EAAE,OAAO,CAAC;QACjB,KAAK,CAAC,EAAE,OAAO,CAAC;KACjB,CAAC,CAAM;IAER,YAAY,CACV,IAAI,EAAE,2BAA2B,EACjC,OAAO,GAAE,yBAA8B,GACtC,IAAI;IAsBD,QAAQ,IAAI,OAAO,CAAC,2BAA2B,EAAE,CAAC;IAIlD,WAAW,CAAC,UAAU,EAAE,OAAO,EAAE,QAAQ,SAAO,GAAG,OAAO,CAAC,OAAO,CAAC;CA0B1E"}
1
+ {"version":3,"file":"mock-model-context.d.ts","sourceRoot":"","sources":["../src/mock-model-context.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EACV,mBAAmB,EACnB,2BAA2B,EAC3B,wBAAwB,EACxB,yBAAyB,EAC1B,MAAM,mBAAmB,CAAC;AAE3B,wBAAgB,qBAAqB,CAAC,KAAK,EAAE,OAAO,GAAG,OAAO,CAM7D;AAED,qBAAa,gBACX,SAAQ,WACR,YAAW,mBAAmB;IAE9B,QAAQ,CAAC,KAAK,2CAAkD;IAChE,QAAQ,CAAC,WAAW,EAAE,KAAK,CAAC;QAC1B,IAAI,EAAE,MAAM,CAAC;QACb,IAAI,EAAE,OAAO,CAAC;QACd,MAAM,CAAC,EAAE,OAAO,CAAC;QACjB,KAAK,CAAC,EAAE,OAAO,CAAC;KACjB,CAAC,CAAM;IAER,YAAY,CACV,IAAI,EAAE,2BAA2B,EACjC,OAAO,GAAE,yBAA8B,GACtC,IAAI;IAsBD,QAAQ,IAAI,OAAO,CAAC,2BAA2B,EAAE,CAAC;IAIlD,WAAW,CACf,UAAU,EAAE,OAAO,EACnB,KAAK,GAAE,MAAM,GAAG,MAAW,EAC3B,OAAO,GAAE,wBAA6B,GACrC,OAAO,CAAC,OAAO,CAAC;CA0BpB"}
@@ -1,3 +1,9 @@
1
+ export function parseExecuteToolInput(input) {
2
+ if (typeof input === 'string') {
3
+ return JSON.parse(input || '{}');
4
+ }
5
+ return input ?? {};
6
+ }
1
7
  export class MockModelContext extends EventTarget {
2
8
  tools = new Map();
3
9
  invocations = [];
@@ -18,7 +24,7 @@ export class MockModelContext extends EventTarget {
18
24
  async getTools() {
19
25
  return [...this.tools.values()];
20
26
  }
21
- async executeTool(toolOrName, argsJson = '{}') {
27
+ async executeTool(toolOrName, input = {}, options = {}) {
22
28
  const name = typeof toolOrName === 'string'
23
29
  ? toolOrName
24
30
  : toolOrName.name;
@@ -29,10 +35,10 @@ export class MockModelContext extends EventTarget {
29
35
  if (!tool) {
30
36
  throw new Error(`Tool not found: ${name}`);
31
37
  }
32
- const args = JSON.parse(argsJson);
38
+ const args = parseExecuteToolInput(input);
33
39
  try {
34
40
  const result = await tool.execute(args, {
35
- signal: new AbortController().signal,
41
+ signal: options.signal ?? new AbortController().signal,
36
42
  });
37
43
  this.invocations.push({ name, args, result });
38
44
  return result;
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=mock-model-context.test.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"mock-model-context.test.d.ts","sourceRoot":"","sources":["../src/mock-model-context.test.ts"],"names":[],"mappings":""}
@@ -0,0 +1,106 @@
1
+ import { afterEach, describe, expect, expectTypeOf, it, vi } from 'vitest';
2
+ import { MockModelContext, parseExecuteToolInput } from './mock-model-context';
3
+ import { MODEL_CONTEXT_MOCK_INIT_SCRIPT } from './model-context-mock-script';
4
+ const original = Object.getOwnPropertyDescriptor(document, 'modelContext');
5
+ afterEach(() => {
6
+ if (original)
7
+ Object.defineProperty(document, 'modelContext', original);
8
+ else
9
+ Reflect.deleteProperty(document, 'modelContext');
10
+ });
11
+ it.each([
12
+ [{}, {}],
13
+ ['{"id":2}', { id: 2 }],
14
+ ['', {}],
15
+ [undefined, {}],
16
+ [null, {}],
17
+ ])('parses %s', (input, expected) => {
18
+ expect(parseExecuteToolInput(input)).toEqual(expected);
19
+ });
20
+ it('preserves object identity and rejects invalid JSON', () => {
21
+ const args = { id: 2 };
22
+ expect(parseExecuteToolInput(args)).toBe(args);
23
+ expect(() => parseExecuteToolInput('{broken')).toThrow(SyntaxError);
24
+ });
25
+ it('accepts fromOrigins on the public discovery contract', () => {
26
+ expectTypeOf().toEqualTypeOf();
27
+ });
28
+ for (const variant of ['class', 'init script']) {
29
+ const create = () => {
30
+ if (variant === 'class')
31
+ return new MockModelContext();
32
+ window.eval(MODEL_CONTEXT_MOCK_INIT_SCRIPT);
33
+ return document
34
+ .modelContext;
35
+ };
36
+ describe(variant, () => {
37
+ it.each([{}, '{"id":2}', undefined])('executes descriptor input %s and forwards client cancellation', async (input) => {
38
+ const context = create();
39
+ const client = new AbortController();
40
+ const execute = vi.fn().mockResolvedValue('ok');
41
+ context.registerTool({ name: 'read', description: 'Reads.', execute });
42
+ const [tool] = await context.getTools();
43
+ await expect(context.executeTool(tool, input, { signal: client.signal })).resolves.toBe('ok');
44
+ expect(execute).toHaveBeenCalledExactlyOnceWith(parseExecuteToolInput(input), { signal: client.signal });
45
+ client.abort();
46
+ expect(execute.mock.calls[0][1].signal.aborted).toBe(true);
47
+ expect(await context.getTools()).toHaveLength(1);
48
+ });
49
+ it('unregisters without aborting an executing callback', async () => {
50
+ const context = create();
51
+ const controller = new AbortController();
52
+ let finish;
53
+ let signal;
54
+ context.registerTool({
55
+ name: 'read',
56
+ description: 'Reads.',
57
+ execute: (_args, client) => {
58
+ signal = client.signal;
59
+ return new Promise((resolve) => {
60
+ finish = resolve;
61
+ });
62
+ },
63
+ }, { signal: controller.signal });
64
+ const result = context.executeTool('read', {});
65
+ controller.abort();
66
+ expect(await context.getTools()).toEqual([]);
67
+ expect(signal.aborted).toBe(false);
68
+ finish('finished');
69
+ await expect(result).resolves.toBe('finished');
70
+ });
71
+ it('skips pre-aborted registrations and rejects missing tools and invalid JSON', async () => {
72
+ const context = create();
73
+ const controller = new AbortController();
74
+ controller.abort();
75
+ const execute = vi.fn();
76
+ const tool = { name: 'read', description: 'Reads.', execute };
77
+ context.registerTool(tool, { signal: controller.signal });
78
+ expect(await context.getTools()).toEqual([]);
79
+ await expect(context.executeTool('absent')).rejects.toThrow();
80
+ await expect(context.executeTool({})).rejects.toThrow();
81
+ context.registerTool(tool);
82
+ await expect(context.executeTool('read', '{broken')).rejects.toThrow(SyntaxError);
83
+ expect(execute).not.toHaveBeenCalled();
84
+ });
85
+ });
86
+ }
87
+ it('records successful and failed invocations and emits toolchange on lifecycle changes', async () => {
88
+ const context = new MockModelContext();
89
+ const changed = vi.fn();
90
+ context.addEventListener('toolchange', changed);
91
+ const controller = new AbortController();
92
+ const error = new Error('failed');
93
+ const execute = vi
94
+ .fn()
95
+ .mockResolvedValueOnce('ok')
96
+ .mockRejectedValueOnce(error);
97
+ context.registerTool({ name: 'read', description: 'Reads.', execute }, { signal: controller.signal });
98
+ await context.executeTool('read', {});
99
+ await expect(context.executeTool('read', {})).rejects.toBe(error);
100
+ expect(context.invocations).toEqual([
101
+ { name: 'read', args: {}, result: 'ok' },
102
+ { name: 'read', args: {}, error },
103
+ ]);
104
+ controller.abort();
105
+ expect(changed).toHaveBeenCalledTimes(2);
106
+ });
@@ -1,2 +1,2 @@
1
- export declare const MODEL_CONTEXT_MOCK_INIT_SCRIPT = "\n(() => {\n const tools = new Map();\n\n Object.defineProperty(document, 'modelContext', {\n configurable: true,\n value: {\n registerTool(tool, options = {}) {\n if (options.signal?.aborted) {\n return;\n }\n\n tools.set(tool.name, tool);\n options.signal?.addEventListener(\n 'abort',\n () => {\n tools.delete(tool.name);\n },\n { once: true },\n );\n },\n async getTools() {\n return [...tools.values()];\n },\n async executeTool(toolOrName, argsJson = '{}') {\n const name =\n typeof toolOrName === 'string'\n ? toolOrName\n : toolOrName?.name;\n const tool = name ? tools.get(name) : undefined;\n if (!tool) {\n throw new Error('Tool not found: ' + name);\n }\n\n return tool.execute(JSON.parse(argsJson));\n },\n },\n });\n})();\n";
1
+ export declare const MODEL_CONTEXT_MOCK_INIT_SCRIPT = "\n(() => {\n const tools = new Map();\n\n function parseInput(input) {\n if (typeof input === 'string') {\n return JSON.parse(input || '{}');\n }\n return input ?? {};\n }\n\n Object.defineProperty(document, 'modelContext', {\n configurable: true,\n value: {\n registerTool(tool, options = {}) {\n if (options.signal?.aborted) {\n return;\n }\n\n tools.set(tool.name, tool);\n options.signal?.addEventListener(\n 'abort',\n () => {\n tools.delete(tool.name);\n },\n { once: true },\n );\n },\n async getTools() {\n return [...tools.values()];\n },\n async executeTool(toolOrName, input = {}, options = {}) {\n const name =\n typeof toolOrName === 'string'\n ? toolOrName\n : toolOrName?.name;\n const tool = name ? tools.get(name) : undefined;\n if (!tool) {\n throw new Error('Tool not found: ' + name);\n }\n\n return tool.execute(parseInput(input), {\n signal: options.signal ?? new AbortController().signal,\n });\n },\n },\n });\n})();\n";
2
2
  //# sourceMappingURL=model-context-mock-script.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"model-context-mock-script.d.ts","sourceRoot":"","sources":["../src/model-context-mock-script.ts"],"names":[],"mappings":"AAAA,eAAO,MAAM,8BAA8B,67BAuC1C,CAAC"}
1
+ {"version":3,"file":"model-context-mock-script.d.ts","sourceRoot":"","sources":["../src/model-context-mock-script.ts"],"names":[],"mappings":"AAAA,eAAO,MAAM,8BAA8B,0qCAgD1C,CAAC"}
@@ -2,6 +2,13 @@ export const MODEL_CONTEXT_MOCK_INIT_SCRIPT = `
2
2
  (() => {
3
3
  const tools = new Map();
4
4
 
5
+ function parseInput(input) {
6
+ if (typeof input === 'string') {
7
+ return JSON.parse(input || '{}');
8
+ }
9
+ return input ?? {};
10
+ }
11
+
5
12
  Object.defineProperty(document, 'modelContext', {
6
13
  configurable: true,
7
14
  value: {
@@ -22,7 +29,7 @@ export const MODEL_CONTEXT_MOCK_INIT_SCRIPT = `
22
29
  async getTools() {
23
30
  return [...tools.values()];
24
31
  },
25
- async executeTool(toolOrName, argsJson = '{}') {
32
+ async executeTool(toolOrName, input = {}, options = {}) {
26
33
  const name =
27
34
  typeof toolOrName === 'string'
28
35
  ? toolOrName
@@ -32,7 +39,9 @@ export const MODEL_CONTEXT_MOCK_INIT_SCRIPT = `
32
39
  throw new Error('Tool not found: ' + name);
33
40
  }
34
41
 
35
- return tool.execute(JSON.parse(argsJson));
42
+ return tool.execute(parseInput(input), {
43
+ signal: options.signal ?? new AbortController().signal,
44
+ });
36
45
  },
37
46
  },
38
47
  });
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@tooluminati/testing",
3
- "version": "0.1.1",
3
+ "version": "0.2.0",
4
4
  "description": "Testing utilities for Tooluminati.",
5
5
  "type": "module",
6
6
  "sideEffects": false,
@@ -15,7 +15,7 @@
15
15
  }
16
16
  },
17
17
  "dependencies": {
18
- "@tooluminati/core": "0.1.1"
18
+ "@tooluminati/core": "0.2.0"
19
19
  },
20
20
  "files": [
21
21
  "dist"