spectra-referee-mcp 0.0.0-stage → 1.0.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/README.md CHANGED
@@ -1,3 +1,71 @@
1
- # Temporary Holding Version
1
+ # @spectra/mcp-server — Model Context Protocol Server
2
+
3
+ > Dual-instrument visual referee for AI coding agents via stdio JSON-RPC 2.0.
4
+
5
+ Spectra MCP Server enables AI coding agents (Muse Code, Claude Code, Cursor, Devin) to visually verify UI changes and catch subtle occlusions using Meta SAM 3.1 and deterministic pixel template matching.
6
+
7
+ ---
8
+
9
+ ## 1-Command Zero-Config Setup
10
+
11
+ ### Muse Code / Claude Code (stdio)
12
+ Add to your project's `.mcp.json` (auto-detected when opening this workspace):
13
+
14
+ ```json
15
+ {
16
+ "mcpServers": {
17
+ "spectra": {
18
+ "command": "node",
19
+ "args": ["packages/mcp-server/dist/index.js"],
20
+ "env": {
21
+ "SPECTRA_OFFLINE_DEFAULT": "true"
22
+ }
23
+ }
24
+ }
25
+ }
26
+ ```
27
+
28
+ Or register globally via Claude CLI:
29
+ ```bash
30
+ claude mcp add spectra -- node /absolute/path/to/packages/mcp-server/dist/index.js
31
+ ```
32
+
33
+ ### Cursor IDE
34
+ Add to `.cursor/mcp.json`:
35
+ ```json
36
+ {
37
+ "mcpServers": {
38
+ "spectra": {
39
+ "command": "node",
40
+ "args": ["packages/mcp-server/dist/index.js"],
41
+ "env": {
42
+ "SPECTRA_OFFLINE_DEFAULT": "true"
43
+ }
44
+ }
45
+ }
46
+ }
47
+ ```
48
+
49
+ ---
50
+
51
+ ## Exposed Bounded Tools (4)
52
+
53
+ | Tool | Purpose | Key Arguments | Auto-Resolution |
54
+ |---|---|---|---|
55
+ | `spectra_verify` | Optical verification on video or frame pair | `targetLabel` + `videoPath` (or `t0FramePath` + `tkFramePath`) | Zero-BBox auto-detection from SAM Frame 0 |
56
+ | `spectra_check` | Batch check against visual invariant suite | `invariantsPath`, `manifestPath`, `cachedSamDir` | Verifies full clip manifest |
57
+ | `spectra_compile_invariant` | Natural language / audio into `.spectra/invariants.json` | `outputPath`, `transcriptText` or `audioPath` | Invariant compiler |
58
+ | `spectra_closed_loop` | Autonomous 4-phase Reject -> Patch -> Re-Verify loop | `scenario` (`s1`, `s2`, `s3`, `all`) | S1 (Canvas), S2 (Mobile), S3 (Catch) |
59
+
60
+ ---
61
+
62
+ ## API Key & Economy Tiers ($0.00 Replay)
63
+
64
+ | Mode | API Keys Needed | Cost | Latency |
65
+ |---|---|---|---|
66
+ | **Cached Mode (Default)** | **None ($0.00)** | **$0.00** | **~480ms** |
67
+ | **Live Uncached SAM 3.1** | `META_API_KEY` | Per-token | ~4.5s |
68
+ | **Live Baseten VLM** | `BASETEN_API_KEY` | Per-token | ~5.7s |
69
+
70
+ All 45 benchmark clips and S1–S3 closed-loop demos replay with **zero API spend ($0.00)**.
2
71
 
3
- This version is a temporary placeholder for this package. An operational version to replace this has been submitted for review and is awaiting a staged release.
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=mcp-server.test.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"mcp-server.test.d.ts","sourceRoot":"","sources":["../../src/__tests__/mcp-server.test.ts"],"names":[],"mappings":""}
@@ -0,0 +1,312 @@
1
+ import path from 'node:path';
2
+ import readline from 'node:readline';
3
+ import { PassThrough } from 'node:stream';
4
+ import { describe, it, expect, beforeEach, afterEach } from 'vitest';
5
+ import { SpectraMcpServer, extractBBoxFromSamFrame0, findRepoRoot } from '../index.js';
6
+ function findProjectRoot() {
7
+ return findRepoRoot();
8
+ }
9
+ class McpTestClient {
10
+ inStream;
11
+ outStream;
12
+ server;
13
+ pending = new Map();
14
+ nextId = 1;
15
+ constructor() {
16
+ this.inStream = new PassThrough();
17
+ this.outStream = new PassThrough();
18
+ this.server = new SpectraMcpServer();
19
+ this.server.listen(this.inStream, this.outStream);
20
+ const rl = readline.createInterface({ input: this.outStream, terminal: false });
21
+ rl.on('line', (line) => {
22
+ const trimmed = line.trim();
23
+ if (!trimmed)
24
+ return;
25
+ try {
26
+ const parsed = JSON.parse(trimmed);
27
+ if (typeof parsed.id === 'number' && this.pending.has(parsed.id)) {
28
+ const resolver = this.pending.get(parsed.id);
29
+ this.pending.delete(parsed.id);
30
+ resolver(parsed);
31
+ }
32
+ }
33
+ catch {
34
+ // ignore unparseable lines in stream listener
35
+ }
36
+ });
37
+ }
38
+ request(method, params) {
39
+ const id = this.nextId++;
40
+ return new Promise((resolve) => {
41
+ this.pending.set(id, resolve);
42
+ this.inStream.write(JSON.stringify({ jsonrpc: '2.0', id, method, params }) + '\n');
43
+ });
44
+ }
45
+ notify(method, params) {
46
+ this.inStream.write(JSON.stringify({ jsonrpc: '2.0', method, params }) + '\n');
47
+ }
48
+ sendRaw(raw) {
49
+ return new Promise((resolve) => {
50
+ this.outStream.once('data', (d) => {
51
+ resolve(d.toString());
52
+ });
53
+ this.inStream.write(raw + '\n');
54
+ });
55
+ }
56
+ close() {
57
+ this.server.close();
58
+ this.inStream.end();
59
+ this.outStream.end();
60
+ }
61
+ }
62
+ describe('@spectra/mcp-server - End-to-End Stdio Protocol Integration Tests ($0.00 Spend)', () => {
63
+ const root = findProjectRoot();
64
+ let client;
65
+ beforeEach(() => {
66
+ client = new McpTestClient();
67
+ });
68
+ afterEach(() => {
69
+ client.close();
70
+ });
71
+ describe('Protocol Lifecycle & Discovery', () => {
72
+ it('handles initialize handshake with server capabilities and protocol version', async () => {
73
+ const res = await client.request('initialize', {
74
+ protocolVersion: '2024-11-05',
75
+ capabilities: {},
76
+ clientInfo: { name: 'test-agent', version: '1.0' },
77
+ });
78
+ expect(res.jsonrpc).toBe('2.0');
79
+ expect(res.error).toBeUndefined();
80
+ expect(res.result).toBeDefined();
81
+ expect(res.result?.protocolVersion).toBe('2024-11-05');
82
+ expect(res.result?.serverInfo.name).toBe('spectra-mcp');
83
+ expect(res.result?.capabilities.tools).toBeDefined();
84
+ });
85
+ it('acknowledges notifications/initialized without errors', () => {
86
+ expect(() => {
87
+ client.notify('notifications/initialized');
88
+ }).not.toThrow();
89
+ });
90
+ it('lists all 4 bounded Spectra tools with schemas in tools/list', async () => {
91
+ const res = await client.request('tools/list');
92
+ expect(res.error).toBeUndefined();
93
+ expect(res.result?.tools).toBeDefined();
94
+ expect(res.result?.tools).toHaveLength(4);
95
+ const toolNames = res.result?.tools.map((t) => t.name);
96
+ expect(toolNames).toContain('spectra_verify');
97
+ expect(toolNames).toContain('spectra_check');
98
+ expect(toolNames).toContain('spectra_compile_invariant');
99
+ expect(toolNames).toContain('spectra_closed_loop');
100
+ const verifyTool = res.result?.tools.find((t) => t.name === 'spectra_verify');
101
+ expect(verifyTool?.description).toContain('Dual-Instrument Referee');
102
+ expect(verifyTool?.inputSchema.properties.t0FramePath).toBeDefined();
103
+ expect(verifyTool?.inputSchema.properties.tkFramePath).toBeDefined();
104
+ expect(verifyTool?.inputSchema.properties.targetBBox).toBeDefined();
105
+ expect(verifyTool?.inputSchema.properties.videoPath).toBeDefined();
106
+ expect(verifyTool?.inputSchema.required).toEqual(['targetLabel']);
107
+ });
108
+ });
109
+ describe('tools/call: spectra_verify', () => {
110
+ it('verifies canvas_000_v1 (0% clean control) returning verdict === "pass" and pixelLossCount === 0', async () => {
111
+ const t0Path = path.join(root, 'benchmark/clips/canvas_000_v1_t0.png');
112
+ const tkPath = path.join(root, 'benchmark/clips/canvas_000_v1_tk.png');
113
+ const samPath = path.join(root, 'benchmark/responses/phase2-smoke/canvas_000_v1_sam.json');
114
+ const res = await client.request('tools/call', {
115
+ name: 'spectra_verify',
116
+ arguments: {
117
+ clipId: 'canvas_000_v1',
118
+ t0FramePath: t0Path,
119
+ tkFramePath: tkPath,
120
+ targetLabel: 'Revenue Series Legend',
121
+ targetBBox: { x: 820, y: 240, width: 220, height: 48 },
122
+ cachedSamResponsePath: samPath,
123
+ },
124
+ });
125
+ expect(res.error).toBeUndefined();
126
+ expect(res.result?.isError).toBe(false);
127
+ expect(res.result?.content[0]?.type).toBe('text');
128
+ const data = JSON.parse(res.result?.content[0]?.text || '{}');
129
+ expect(data.verdict).toBe('pass');
130
+ expect(data.rSam).toBeGreaterThan(0.98);
131
+ expect(data.visRatio).toBe(1.0);
132
+ expect(data.pixelLossCount).toBe(0);
133
+ expect(data.threshold).toBeCloseTo(0.0135, 4);
134
+ expect(data.calibrated).toBe(true);
135
+ expect(data.reason).toContain('VERIFIED:');
136
+ expect(data.resolvedBBox).toBeDefined();
137
+ expect(data.decisionExplanation).toBeDefined();
138
+ expect(data.decisionExplanation.ruleFired).toBe('RULE_4_THREE_SIGMA_CLEAN_PASS');
139
+ expect(data.decisionExplanation.marginToThreshold).toBeLessThanOrEqual(0);
140
+ });
141
+ it('catches dom_catch_010_v1 (10% occlusion) returning verdict === "fail" and pixelLossCount > 0', async () => {
142
+ const t0Path = path.join(root, 'benchmark/clips/dom_catch_010_v1_t0.png');
143
+ const tkPath = path.join(root, 'benchmark/clips/dom_catch_010_v1_tk.png');
144
+ const samPath = path.join(root, 'benchmark/responses/phase2-smoke/dom_catch_010_v1_sam.json');
145
+ const res = await client.request('tools/call', {
146
+ name: 'spectra_verify',
147
+ arguments: {
148
+ clipId: 'dom_catch_010_v1',
149
+ t0FramePath: t0Path,
150
+ tkFramePath: tkPath,
151
+ targetLabel: 'Complete Checkout',
152
+ targetBBox: { x: 910, y: 600, width: 340, height: 50 },
153
+ cachedSamResponsePath: samPath,
154
+ },
155
+ });
156
+ expect(res.error).toBeUndefined();
157
+ expect(res.result?.isError).toBe(false);
158
+ const data = JSON.parse(res.result?.content[0]?.text || '{}');
159
+ expect(data.verdict).toBe('fail');
160
+ expect(data.pixelLossCount).toBe(1700);
161
+ expect(data.pixelLossCount).toBeGreaterThan(0);
162
+ expect(data.visRatio).toBe(0.9);
163
+ expect(data.rSam).toBeCloseTo(0.908, 2);
164
+ expect(data.reason).toContain('REJECTED: Visibility loss 9.60% exceeds threshold');
165
+ expect(data.repairActionRequired).toBe(true);
166
+ expect(data.decisionExplanation).toBeDefined();
167
+ expect(data.decisionExplanation.ruleFired).toBe('RULE_3_THREE_SIGMA_OCCLUSION_REJECT');
168
+ expect(data.decisionExplanation.marginToThreshold).toBeGreaterThan(0);
169
+ });
170
+ // Test 1: Zero-BBox and Zero-PNG resolution with videoPath only (canvas_000_v1)
171
+ it('verifies canvas_000_v1 with ONLY videoPath and targetLabel (zero-bbox auto-resolution)', async () => {
172
+ const res = await client.request('tools/call', {
173
+ name: 'spectra_verify',
174
+ arguments: {
175
+ videoPath: 'benchmark/clips/canvas_000_v1.mp4',
176
+ targetLabel: 'Revenue Series Legend',
177
+ },
178
+ });
179
+ expect(res.error).toBeUndefined();
180
+ expect(res.result?.isError).toBe(false);
181
+ const data = JSON.parse(res.result?.content[0]?.text || '{}');
182
+ expect(data.verdict).toBe('pass');
183
+ expect(data.rSam).toBeCloseTo(0.9912, 3);
184
+ expect(data.resolvedBBox).toBeDefined();
185
+ expect(data.resolvedBBox.x).toBe(820);
186
+ expect(data.resolvedBBox.y).toBe(240);
187
+ expect(data.resolvedBBox.width).toBe(220);
188
+ expect(data.resolvedBBox.height).toBe(48);
189
+ expect(data.bboxSource).toBe('manifest');
190
+ });
191
+ // Test 2: Zero-BBox and Zero-PNG resolution with videoPath only (dom_catch_010_v1)
192
+ it('catches dom_catch_010_v1 with ONLY videoPath and targetLabel returning fail and pixelLossCount === 1700', async () => {
193
+ const res = await client.request('tools/call', {
194
+ name: 'spectra_verify',
195
+ arguments: {
196
+ videoPath: 'benchmark/clips/dom_catch_010_v1.mp4',
197
+ targetLabel: 'Complete Checkout',
198
+ },
199
+ });
200
+ expect(res.error).toBeUndefined();
201
+ expect(res.result?.isError).toBe(false);
202
+ const data = JSON.parse(res.result?.content[0]?.text || '{}');
203
+ expect(data.verdict).toBe('fail');
204
+ expect(data.pixelLossCount).toBe(1700);
205
+ expect(data.repairActionRequired).toBe(true);
206
+ expect(data.resolvedBBox).toBeDefined();
207
+ expect(data.resolvedBBox.x).toBe(910);
208
+ expect(data.resolvedBBox.y).toBe(600);
209
+ expect(data.resolvedBBox.width).toBe(340);
210
+ expect(data.resolvedBBox.height).toBe(50);
211
+ expect(data.bboxSource).toBe('manifest');
212
+ });
213
+ });
214
+ describe('SAM Frame 0 Bounding Box Extraction', () => {
215
+ // Test 3: Direct unit test for extractBBoxFromSamFrame0
216
+ it('extracts valid non-empty bounding box from SAM 3.1 Frame 0 raster mask directly', () => {
217
+ const samPath = path.join(root, 'benchmark/responses/phase2-smoke/dom_catch_010_v1_sam.json');
218
+ const bbox = extractBBoxFromSamFrame0(samPath);
219
+ expect(bbox).not.toBeNull();
220
+ expect(bbox?.x).toBeGreaterThan(0);
221
+ expect(bbox?.y).toBeGreaterThan(0);
222
+ expect(bbox?.width).toBeGreaterThan(0);
223
+ expect(bbox?.height).toBeGreaterThan(0);
224
+ expect(bbox?.x).toBe(909);
225
+ expect(bbox?.y).toBe(600);
226
+ expect(bbox?.width).toBe(342);
227
+ expect(bbox?.height).toBe(51);
228
+ });
229
+ });
230
+ describe('tools/call: spectra_closed_loop', () => {
231
+ // Test 4: Closed-loop referee invocation over MCP stdio
232
+ it('executes closed loop referee over stdio for scenario s3 returning closedLoopSuccess === true', async () => {
233
+ const res = await client.request('tools/call', {
234
+ name: 'spectra_closed_loop',
235
+ arguments: {
236
+ scenario: 's3',
237
+ },
238
+ });
239
+ expect(res.error).toBeUndefined();
240
+ expect(res.result?.isError).toBe(false);
241
+ const data = JSON.parse(res.result?.content[0]?.text || '{}');
242
+ expect(data.closedLoopSuccess).toBe(true);
243
+ expect(data.scenario).toBe('s3');
244
+ expect(data.pass1).toBeDefined();
245
+ expect(data.pass1.combinedVerdict).toBe('fail');
246
+ expect(data.repair).toBeDefined();
247
+ expect(data.pass2).toBeDefined();
248
+ expect(data.pass2.combinedVerdict).toBe('pass');
249
+ });
250
+ });
251
+ describe('tools/call: spectra_compile_invariant and spectra_check', () => {
252
+ it('compiles voice expectation from cached voice smoke directory', async () => {
253
+ const cachedDir = path.join(root, 'benchmark/responses/voice-smoke');
254
+ const res = await client.request('tools/call', {
255
+ name: 'spectra_compile_invariant',
256
+ arguments: {
257
+ cachedDir,
258
+ },
259
+ });
260
+ expect(res.error).toBeUndefined();
261
+ expect(res.result?.isError).toBe(false);
262
+ const data = JSON.parse(res.result?.content[0]?.text || '{}');
263
+ expect(data.success).toBe(true);
264
+ expect(data.invariantsCount).toBe(2);
265
+ expect(data.document.invariants[0].id).toBe('complete-checkout-button-visible');
266
+ });
267
+ it('checks invariants across manifest returning BenchmarkReport', async () => {
268
+ const invariantsPath = path.join(root, '.spectra/invariants.json');
269
+ const manifestPath = path.join(root, 'benchmark/manifest.json');
270
+ const cachedSamDir = path.join(root, 'benchmark/responses/phase2-smoke');
271
+ const res = await client.request('tools/call', {
272
+ name: 'spectra_check',
273
+ arguments: {
274
+ invariantsPath,
275
+ manifestPath,
276
+ cachedSamDir,
277
+ },
278
+ });
279
+ expect(res.error).toBeUndefined();
280
+ expect(res.result?.isError).toBe(false);
281
+ const data = JSON.parse(res.result?.content[0]?.text || '{}');
282
+ expect(data.overallVerdict).toBe('fail');
283
+ expect(data.results.length).toBe(45);
284
+ expect(data.passed).toBeGreaterThan(0);
285
+ expect(data.failed).toBeGreaterThan(0);
286
+ });
287
+ });
288
+ describe('Error Handling', () => {
289
+ it('returns JSON-RPC error -32601 for unknown methods', async () => {
290
+ const res = await client.request('unknown/method');
291
+ expect(res.error).toBeDefined();
292
+ expect(res.error?.code).toBe(-32601);
293
+ expect(res.error?.message).toContain('Method not found');
294
+ });
295
+ it('returns isError: true when a tool call fails (e.g. unknown tool or missing file)', async () => {
296
+ const res = await client.request('tools/call', {
297
+ name: 'non_existent_tool',
298
+ arguments: {},
299
+ });
300
+ expect(res.result?.isError).toBe(true);
301
+ const data = JSON.parse(res.result?.content[0]?.text || '{}');
302
+ expect(data.error).toContain('Unknown tool');
303
+ });
304
+ it('handles raw invalid JSON with parse error -32700', async () => {
305
+ const rawRes = await client.sendRaw('INVALID_JSON_LINE');
306
+ const parsed = JSON.parse(rawRes);
307
+ expect(parsed.error.code).toBe(-32700);
308
+ expect(parsed.error.message).toContain('Parse error');
309
+ });
310
+ });
311
+ });
312
+ //# sourceMappingURL=mcp-server.test.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"mcp-server.test.js","sourceRoot":"","sources":["../../src/__tests__/mcp-server.test.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=resolver-validation.test.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"resolver-validation.test.d.ts","sourceRoot":"","sources":["../../src/__tests__/resolver-validation.test.ts"],"names":[],"mappings":""}
@@ -0,0 +1,38 @@
1
+ import { describe, it, expect } from 'vitest';
2
+ import { normalizeBBox, resolveVerifyInputs } from '../auto-resolver.js';
3
+ describe('@spectra/mcp-server - resolver input validation ($0.00 Spend)', () => {
4
+ it('accepts valid bboxes and rounds fractional pixels', () => {
5
+ expect(normalizeBBox([10.4, 20.5, 30.6, 40.1])).toEqual({
6
+ x: 10,
7
+ y: 21,
8
+ width: 31,
9
+ height: 40,
10
+ });
11
+ expect(normalizeBBox({ x: 1, y: 2, width: 3, height: 4 })).toEqual({
12
+ x: 1,
13
+ y: 2,
14
+ width: 3,
15
+ height: 4,
16
+ });
17
+ });
18
+ it('rejects non-finite, negative, and malformed bboxes', () => {
19
+ expect(() => normalizeBBox([0, 0, Number.POSITIVE_INFINITY, 10])).toThrow('Invalid targetBBox');
20
+ expect(() => normalizeBBox([0, 0, Number.NaN, 10])).toThrow('Invalid targetBBox');
21
+ expect(() => normalizeBBox([-1, 0, 10, 10])).toThrow('Invalid targetBBox');
22
+ expect(() => normalizeBBox([0, 0, 10])).toThrow('Invalid targetBBox');
23
+ });
24
+ it('rejects non-finite and negative tolerance', () => {
25
+ const base = {
26
+ t0FramePath: 'a_t0.png',
27
+ tkFramePath: 'a_tk.png',
28
+ targetLabel: 'btn',
29
+ targetBBox: [0, 0, 10, 10],
30
+ };
31
+ expect(() => resolveVerifyInputs({ ...base, tolerance: Number.NaN })).toThrow('Invalid tolerance');
32
+ expect(() => resolveVerifyInputs({ ...base, tolerance: -1 })).toThrow('Invalid tolerance');
33
+ expect(() => resolveVerifyInputs({ ...base, tolerance: Number.POSITIVE_INFINITY })).toThrow('Invalid tolerance');
34
+ const ok = resolveVerifyInputs({ ...base, tolerance: 5 });
35
+ expect(ok.tolerance).toBe(5);
36
+ });
37
+ });
38
+ //# sourceMappingURL=resolver-validation.test.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"resolver-validation.test.js","sourceRoot":"","sources":["../../src/__tests__/resolver-validation.test.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,29 @@
1
+ import type { BoundingBox } from '@spectra/core';
2
+ export interface RawVerifyToolArgs {
3
+ videoPath?: string;
4
+ t0FramePath?: string;
5
+ tkFramePath?: string;
6
+ targetLabel: string;
7
+ targetBBox?: BoundingBox | [number, number, number, number];
8
+ cachedSamResponsePath?: string;
9
+ tolerance?: number;
10
+ }
11
+ export interface ResolvedVerifyInputs {
12
+ videoPath?: string;
13
+ t0FramePath: string;
14
+ tkFramePath: string;
15
+ targetLabel: string;
16
+ targetBBox: BoundingBox;
17
+ bboxSource: 'caller' | 'sam-frame-0' | 'manifest';
18
+ cachedSamResponsePath?: string;
19
+ tolerance?: number;
20
+ cleanupTempFrames: () => void;
21
+ }
22
+ export declare function findRepoRoot(startDir?: string): string;
23
+ export declare function normalizeBBox(input: BoundingBox | [number, number, number, number]): BoundingBox;
24
+ /**
25
+ * Extracts tight pixel bounding box { x, y, width, height } from Frame 0 of a SAM 3.1 response JSON.
26
+ */
27
+ export declare function extractBBoxFromSamFrame0(samJsonPath: string): BoundingBox | null;
28
+ export declare function resolveVerifyInputs(args: RawVerifyToolArgs): ResolvedVerifyInputs;
29
+ //# sourceMappingURL=auto-resolver.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"auto-resolver.d.ts","sourceRoot":"","sources":["../src/auto-resolver.ts"],"names":[],"mappings":"AAKA,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,eAAe,CAAC;AAEjD,MAAM,WAAW,iBAAiB;IAChC,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,WAAW,EAAE,MAAM,CAAC;IACpB,UAAU,CAAC,EAAE,WAAW,GAAG,CAAC,MAAM,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,CAAC,CAAC;IAC5D,qBAAqB,CAAC,EAAE,MAAM,CAAC;IAC/B,SAAS,CAAC,EAAE,MAAM,CAAC;CACpB;AAED,MAAM,WAAW,oBAAoB;IACnC,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,WAAW,EAAE,MAAM,CAAC;IACpB,WAAW,EAAE,MAAM,CAAC;IACpB,WAAW,EAAE,MAAM,CAAC;IACpB,UAAU,EAAE,WAAW,CAAC;IACxB,UAAU,EAAE,QAAQ,GAAG,aAAa,GAAG,UAAU,CAAC;IAClD,qBAAqB,CAAC,EAAE,MAAM,CAAC;IAC/B,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,iBAAiB,EAAE,MAAM,IAAI,CAAC;CAC/B;AAED,wBAAgB,YAAY,CAAC,QAAQ,GAAE,MAAsB,GAAG,MAAM,CAcrE;AAED,wBAAgB,aAAa,CAAC,KAAK,EAAE,WAAW,GAAG,CAAC,MAAM,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,CAAC,GAAG,WAAW,CAmBhG;AAED;;GAEG;AACH,wBAAgB,wBAAwB,CAAC,WAAW,EAAE,MAAM,GAAG,WAAW,GAAG,IAAI,CA+GhF;AAED,wBAAgB,mBAAmB,CAAC,IAAI,EAAE,iBAAiB,GAAG,oBAAoB,CA2JjF"}