@apollo/client-ai-apps 0.5.4 → 0.6.1
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/CHANGELOG.md +180 -0
- package/dist/config/defineConfig.d.ts +1 -0
- package/dist/config/defineConfig.d.ts.map +1 -1
- package/dist/config/schema.d.ts +1 -0
- package/dist/config/schema.d.ts.map +1 -1
- package/dist/config/schema.js +1 -0
- package/dist/config/schema.js.map +1 -1
- package/dist/core/typeRegistration.d.ts +33 -0
- package/dist/core/typeRegistration.d.ts.map +1 -0
- package/dist/core/typeRegistration.js +2 -0
- package/dist/core/typeRegistration.js.map +1 -0
- package/dist/index.d.ts +1 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js.map +1 -1
- package/dist/mcp/core/McpAppManager.d.ts +2 -1
- package/dist/mcp/core/McpAppManager.d.ts.map +1 -1
- package/dist/mcp/core/McpAppManager.js +11 -1
- package/dist/mcp/core/McpAppManager.js.map +1 -1
- package/dist/mcp/react/hooks/useHostContext.d.ts +2 -0
- package/dist/mcp/react/hooks/useHostContext.d.ts.map +1 -0
- package/dist/mcp/react/hooks/useHostContext.js +7 -0
- package/dist/mcp/react/hooks/useHostContext.js.map +1 -0
- package/dist/mcp/react/hooks/useToolInfo.d.ts +3 -0
- package/dist/mcp/react/hooks/useToolInfo.d.ts.map +1 -0
- package/dist/mcp/react/hooks/useToolInfo.js +10 -0
- package/dist/mcp/react/hooks/useToolInfo.js.map +1 -0
- package/dist/mcp/react/hooks/useToolInput.d.ts +6 -1
- package/dist/mcp/react/hooks/useToolInput.d.ts.map +1 -1
- package/dist/mcp/react/hooks/useToolInput.js +4 -0
- package/dist/mcp/react/hooks/useToolInput.js.map +1 -1
- package/dist/mcp/react/hooks/useToolName.d.ts +6 -1
- package/dist/mcp/react/hooks/useToolName.d.ts.map +1 -1
- package/dist/mcp/react/hooks/useToolName.js +4 -0
- package/dist/mcp/react/hooks/useToolName.js.map +1 -1
- package/dist/mcp/react/index.d.ts +2 -0
- package/dist/mcp/react/index.d.ts.map +1 -1
- package/dist/mcp/react/index.js +2 -0
- package/dist/mcp/react/index.js.map +1 -1
- package/dist/openai/core/McpAppManager.d.ts +2 -1
- package/dist/openai/core/McpAppManager.d.ts.map +1 -1
- package/dist/openai/core/McpAppManager.js +11 -1
- package/dist/openai/core/McpAppManager.js.map +1 -1
- package/dist/openai/react/hooks/useHostContext.d.ts +2 -0
- package/dist/openai/react/hooks/useHostContext.d.ts.map +1 -0
- package/dist/openai/react/hooks/useHostContext.js +7 -0
- package/dist/openai/react/hooks/useHostContext.js.map +1 -0
- package/dist/openai/react/hooks/useToolInfo.d.ts +3 -0
- package/dist/openai/react/hooks/useToolInfo.d.ts.map +1 -0
- package/dist/openai/react/hooks/useToolInfo.js +10 -0
- package/dist/openai/react/hooks/useToolInfo.js.map +1 -0
- package/dist/openai/react/hooks/useToolInput.d.ts +6 -1
- package/dist/openai/react/hooks/useToolInput.d.ts.map +1 -1
- package/dist/openai/react/hooks/useToolInput.js +4 -0
- package/dist/openai/react/hooks/useToolInput.js.map +1 -1
- package/dist/openai/react/hooks/useToolName.d.ts +6 -1
- package/dist/openai/react/hooks/useToolName.d.ts.map +1 -1
- package/dist/openai/react/hooks/useToolName.js +4 -0
- package/dist/openai/react/hooks/useToolName.js.map +1 -1
- package/dist/openai/react/index.d.ts +2 -0
- package/dist/openai/react/index.d.ts.map +1 -1
- package/dist/openai/react/index.js +2 -0
- package/dist/openai/react/index.js.map +1 -1
- package/dist/react/index.d.ts +10 -0
- package/dist/react/index.d.ts.map +1 -1
- package/dist/react/index.js +10 -0
- package/dist/react/index.js.map +1 -1
- package/dist/react/index.mcp.d.ts +1 -1
- package/dist/react/index.mcp.d.ts.map +1 -1
- package/dist/react/index.mcp.js +1 -1
- package/dist/react/index.mcp.js.map +1 -1
- package/dist/react/index.openai.d.ts +1 -1
- package/dist/react/index.openai.d.ts.map +1 -1
- package/dist/react/index.openai.js +1 -1
- package/dist/react/index.openai.js.map +1 -1
- package/dist/tsconfig/core/tsconfig.json +2 -0
- package/dist/tsconfig/mcp/tsconfig.json +2 -0
- package/dist/tsconfig/openai/tsconfig.json +2 -0
- package/dist/types/application-manifest.d.ts +1 -0
- package/dist/types/application-manifest.d.ts.map +1 -1
- package/dist/types/application-manifest.js.map +1 -1
- package/dist/vite/__tests__/utilities/build.d.ts.map +1 -1
- package/dist/vite/__tests__/utilities/build.js +0 -1
- package/dist/vite/__tests__/utilities/build.js.map +1 -1
- package/dist/vite/apolloClientAiApps.d.ts +2 -0
- package/dist/vite/apolloClientAiApps.d.ts.map +1 -1
- package/dist/vite/apolloClientAiApps.js +362 -53
- package/dist/vite/apolloClientAiApps.js.map +1 -1
- package/dist/vite/utilities/recast.d.ts +54 -0
- package/dist/vite/utilities/recast.d.ts.map +1 -0
- package/dist/vite/utilities/recast.js +71 -0
- package/dist/vite/utilities/recast.js.map +1 -0
- package/package.json +7 -6
- package/src/config/schema.ts +1 -0
- package/src/core/typeRegistration.ts +32 -0
- package/src/index.ts +7 -0
- package/src/mcp/core/McpAppManager.ts +23 -1
- package/src/mcp/react/hooks/__tests__/useHostContext.test.tsx +95 -0
- package/src/mcp/react/hooks/__tests__/useToolInfo.test.tsx +53 -0
- package/src/mcp/react/hooks/useHostContext.ts +14 -0
- package/src/mcp/react/hooks/useToolInfo.ts +13 -0
- package/src/mcp/react/hooks/useToolInput.ts +6 -1
- package/src/mcp/react/hooks/useToolName.ts +6 -1
- package/src/mcp/react/index.ts +2 -0
- package/src/openai/core/McpAppManager.ts +22 -1
- package/src/openai/react/hooks/__tests__/useToolInfo.test.tsx +92 -0
- package/src/openai/react/hooks/useHostContext.ts +14 -0
- package/src/openai/react/hooks/useToolInfo.ts +13 -0
- package/src/openai/react/hooks/useToolInput.ts +6 -1
- package/src/openai/react/hooks/useToolName.ts +6 -1
- package/src/openai/react/index.ts +2 -0
- package/src/react/index.mcp.ts +2 -0
- package/src/react/index.openai.ts +2 -0
- package/src/react/index.ts +14 -0
- package/src/testing/internal/mcp/mockMcpHost.ts +12 -0
- package/src/testing/internal/utilities/mockApplicationManifest.ts +1 -0
- package/src/tsconfig/core/tsconfig.json +2 -0
- package/src/tsconfig/mcp/tsconfig.json +2 -0
- package/src/tsconfig/openai/tsconfig.json +2 -0
- package/src/types/application-manifest.ts +1 -0
- package/src/vite/__tests__/apolloClientAiApps.test.ts +1022 -66
- package/src/vite/__tests__/utilities/build.ts +0 -1
- package/src/vite/apolloClientAiApps.ts +604 -81
- package/src/vite/utilities/recast.ts +100 -0
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { beforeEach, describe, expect, test, vi } from "vitest";
|
|
2
|
+
import { spyOnConsole, wait } from "../../testing/internal/index.js";
|
|
2
3
|
import fs from "node:fs";
|
|
3
4
|
import { gql, type DocumentNode } from "@apollo/client";
|
|
4
5
|
import { getMainDefinition, print } from "@apollo/client/utilities";
|
|
@@ -6,13 +7,11 @@ import { getOperationName } from "@apollo/client/utilities/internal";
|
|
|
6
7
|
import { vol } from "memfs";
|
|
7
8
|
import { apolloClientAiApps } from "../apolloClientAiApps.js";
|
|
8
9
|
import { buildApp, setupServer } from "./utilities/build.js";
|
|
9
|
-
import type {
|
|
10
|
-
ApplicationManifest,
|
|
11
|
-
ManifestWidgetSettings,
|
|
12
|
-
} from "../../types/application-manifest.js";
|
|
10
|
+
import type { ApplicationManifest } from "../../types/application-manifest.js";
|
|
13
11
|
import { explorer } from "../utilities/config.js";
|
|
14
12
|
import { invariant } from "@apollo/client/utilities/invariant";
|
|
15
13
|
import { Kind } from "graphql";
|
|
14
|
+
import type { ApolloClientAiAppsConfig } from "../../config/types.js";
|
|
16
15
|
|
|
17
16
|
beforeEach(() => {
|
|
18
17
|
explorer.clearCaches();
|
|
@@ -29,12 +28,19 @@ describe("operations", () => {
|
|
|
29
28
|
invoked: "Tested global!",
|
|
30
29
|
},
|
|
31
30
|
},
|
|
31
|
+
csp: {
|
|
32
|
+
baseUriDomains: ["https://base.example.com"],
|
|
33
|
+
connectDomains: ["https://connect.example.com"],
|
|
34
|
+
frameDomains: ["https://frame.example.com"],
|
|
35
|
+
redirectDomains: ["https://redirect.example.com"],
|
|
36
|
+
resourceDomains: ["https://resource.example.com"],
|
|
37
|
+
},
|
|
32
38
|
widgetSettings: {
|
|
33
39
|
description: "Test",
|
|
34
40
|
domain: "https://example.com",
|
|
35
41
|
prefersBorder: true,
|
|
36
|
-
}
|
|
37
|
-
},
|
|
42
|
+
},
|
|
43
|
+
} satisfies ApolloClientAiAppsConfig.Config,
|
|
38
44
|
}),
|
|
39
45
|
"src/my-component.tsx": declareOperation(gql`
|
|
40
46
|
query HelloWorldQuery($name: string!)
|
|
@@ -61,7 +67,9 @@ describe("operations", () => {
|
|
|
61
67
|
});
|
|
62
68
|
|
|
63
69
|
await using server = await setupServer({
|
|
64
|
-
plugins: [
|
|
70
|
+
plugins: [
|
|
71
|
+
apolloClientAiApps({ targets: ["mcp"], appsOutDir: "dist/apps" }),
|
|
72
|
+
],
|
|
65
73
|
});
|
|
66
74
|
await server.listen();
|
|
67
75
|
|
|
@@ -70,10 +78,21 @@ describe("operations", () => {
|
|
|
70
78
|
{
|
|
71
79
|
"appVersion": "1.0.0",
|
|
72
80
|
"csp": {
|
|
73
|
-
"
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
"
|
|
81
|
+
"baseUriDomains": [
|
|
82
|
+
"https://base.example.com",
|
|
83
|
+
],
|
|
84
|
+
"connectDomains": [
|
|
85
|
+
"https://connect.example.com",
|
|
86
|
+
],
|
|
87
|
+
"frameDomains": [
|
|
88
|
+
"https://frame.example.com",
|
|
89
|
+
],
|
|
90
|
+
"redirectDomains": [
|
|
91
|
+
"https://redirect.example.com",
|
|
92
|
+
],
|
|
93
|
+
"resourceDomains": [
|
|
94
|
+
"https://resource.example.com",
|
|
95
|
+
],
|
|
77
96
|
},
|
|
78
97
|
"format": "apollo-ai-app-manifest",
|
|
79
98
|
"hash": "abc",
|
|
@@ -81,6 +100,7 @@ describe("operations", () => {
|
|
|
81
100
|
"toolInvocation/invoked": "Tested global!",
|
|
82
101
|
"toolInvocation/invoking": "Testing global...",
|
|
83
102
|
},
|
|
103
|
+
"name": "my-app",
|
|
84
104
|
"operations": [
|
|
85
105
|
{
|
|
86
106
|
"body": "query HelloWorldQuery($name: string!) {
|
|
@@ -145,7 +165,9 @@ describe("operations", () => {
|
|
|
145
165
|
});
|
|
146
166
|
|
|
147
167
|
await using server = await setupServer({
|
|
148
|
-
plugins: [
|
|
168
|
+
plugins: [
|
|
169
|
+
apolloClientAiApps({ targets: ["mcp"], appsOutDir: "dist/apps" }),
|
|
170
|
+
],
|
|
149
171
|
});
|
|
150
172
|
await server.listen();
|
|
151
173
|
|
|
@@ -154,6 +176,7 @@ describe("operations", () => {
|
|
|
154
176
|
{
|
|
155
177
|
"appVersion": "1.0.0",
|
|
156
178
|
"csp": {
|
|
179
|
+
"baseUriDomains": [],
|
|
157
180
|
"connectDomains": [],
|
|
158
181
|
"frameDomains": [],
|
|
159
182
|
"redirectDomains": [],
|
|
@@ -161,6 +184,7 @@ describe("operations", () => {
|
|
|
161
184
|
},
|
|
162
185
|
"format": "apollo-ai-app-manifest",
|
|
163
186
|
"hash": "abc",
|
|
187
|
+
"name": "my-app",
|
|
164
188
|
"operations": [
|
|
165
189
|
{
|
|
166
190
|
"body": "query HelloWorldQuery {
|
|
@@ -229,7 +253,9 @@ describe("operations", () => {
|
|
|
229
253
|
});
|
|
230
254
|
|
|
231
255
|
await using server = await setupServer({
|
|
232
|
-
plugins: [
|
|
256
|
+
plugins: [
|
|
257
|
+
apolloClientAiApps({ targets: ["mcp"], appsOutDir: "dist/apps" }),
|
|
258
|
+
],
|
|
233
259
|
});
|
|
234
260
|
await server.listen();
|
|
235
261
|
|
|
@@ -238,6 +264,7 @@ describe("operations", () => {
|
|
|
238
264
|
{
|
|
239
265
|
"appVersion": "1.0.0",
|
|
240
266
|
"csp": {
|
|
267
|
+
"baseUriDomains": [],
|
|
241
268
|
"connectDomains": [],
|
|
242
269
|
"frameDomains": [],
|
|
243
270
|
"redirectDomains": [],
|
|
@@ -245,6 +272,7 @@ describe("operations", () => {
|
|
|
245
272
|
},
|
|
246
273
|
"format": "apollo-ai-app-manifest",
|
|
247
274
|
"hash": "abc",
|
|
275
|
+
"name": "my-app",
|
|
248
276
|
"operations": [
|
|
249
277
|
{
|
|
250
278
|
"body": "query HelloWorldQuery {
|
|
@@ -303,7 +331,9 @@ describe("operations", () => {
|
|
|
303
331
|
|
|
304
332
|
await buildApp({
|
|
305
333
|
mode: "production",
|
|
306
|
-
plugins: [
|
|
334
|
+
plugins: [
|
|
335
|
+
apolloClientAiApps({ targets: ["mcp"], appsOutDir: "dist/apps" }),
|
|
336
|
+
],
|
|
307
337
|
});
|
|
308
338
|
|
|
309
339
|
const manifest = readManifestFile();
|
|
@@ -320,7 +350,9 @@ describe("operations", () => {
|
|
|
320
350
|
});
|
|
321
351
|
|
|
322
352
|
await using server = await setupServer({
|
|
323
|
-
plugins: [
|
|
353
|
+
plugins: [
|
|
354
|
+
apolloClientAiApps({ targets: ["mcp"], appsOutDir: "dist/apps" }),
|
|
355
|
+
],
|
|
324
356
|
});
|
|
325
357
|
await server.listen();
|
|
326
358
|
|
|
@@ -329,6 +361,7 @@ describe("operations", () => {
|
|
|
329
361
|
{
|
|
330
362
|
"appVersion": "1.0.0",
|
|
331
363
|
"csp": {
|
|
364
|
+
"baseUriDomains": [],
|
|
332
365
|
"connectDomains": [],
|
|
333
366
|
"frameDomains": [],
|
|
334
367
|
"redirectDomains": [],
|
|
@@ -336,6 +369,7 @@ describe("operations", () => {
|
|
|
336
369
|
},
|
|
337
370
|
"format": "apollo-ai-app-manifest",
|
|
338
371
|
"hash": "abc",
|
|
372
|
+
"name": "my-app",
|
|
339
373
|
"operations": [],
|
|
340
374
|
"resource": "http://localhost:3333",
|
|
341
375
|
"version": "1",
|
|
@@ -354,7 +388,9 @@ describe("operations", () => {
|
|
|
354
388
|
});
|
|
355
389
|
|
|
356
390
|
await using server = await setupServer({
|
|
357
|
-
plugins: [
|
|
391
|
+
plugins: [
|
|
392
|
+
apolloClientAiApps({ targets: ["mcp"], appsOutDir: "dist/apps" }),
|
|
393
|
+
],
|
|
358
394
|
});
|
|
359
395
|
await server.listen();
|
|
360
396
|
|
|
@@ -363,6 +399,7 @@ describe("operations", () => {
|
|
|
363
399
|
{
|
|
364
400
|
"appVersion": "1.0.0",
|
|
365
401
|
"csp": {
|
|
402
|
+
"baseUriDomains": [],
|
|
366
403
|
"connectDomains": [],
|
|
367
404
|
"frameDomains": [],
|
|
368
405
|
"redirectDomains": [],
|
|
@@ -370,6 +407,7 @@ describe("operations", () => {
|
|
|
370
407
|
},
|
|
371
408
|
"format": "apollo-ai-app-manifest",
|
|
372
409
|
"hash": "abc",
|
|
410
|
+
"name": "my-app",
|
|
373
411
|
"operations": [
|
|
374
412
|
{
|
|
375
413
|
"body": "query HelloWorldQuery {
|
|
@@ -401,7 +439,9 @@ describe("operations", () => {
|
|
|
401
439
|
});
|
|
402
440
|
|
|
403
441
|
await using server = await setupServer({
|
|
404
|
-
plugins: [
|
|
442
|
+
plugins: [
|
|
443
|
+
apolloClientAiApps({ targets: ["mcp"], appsOutDir: "dist/apps" }),
|
|
444
|
+
],
|
|
405
445
|
});
|
|
406
446
|
await server.listen();
|
|
407
447
|
|
|
@@ -410,6 +450,7 @@ describe("operations", () => {
|
|
|
410
450
|
{
|
|
411
451
|
"appVersion": "1.0.0",
|
|
412
452
|
"csp": {
|
|
453
|
+
"baseUriDomains": [],
|
|
413
454
|
"connectDomains": [],
|
|
414
455
|
"frameDomains": [],
|
|
415
456
|
"redirectDomains": [],
|
|
@@ -417,6 +458,7 @@ describe("operations", () => {
|
|
|
417
458
|
},
|
|
418
459
|
"format": "apollo-ai-app-manifest",
|
|
419
460
|
"hash": "abc",
|
|
461
|
+
"name": "my-app",
|
|
420
462
|
"operations": [
|
|
421
463
|
{
|
|
422
464
|
"body": "mutation HelloWorldQuery {
|
|
@@ -454,7 +496,9 @@ describe("operations", () => {
|
|
|
454
496
|
|
|
455
497
|
await expect(async () => {
|
|
456
498
|
await using server = await setupServer({
|
|
457
|
-
plugins: [
|
|
499
|
+
plugins: [
|
|
500
|
+
apolloClientAiApps({ targets: ["mcp"], appsOutDir: "dist/apps" }),
|
|
501
|
+
],
|
|
458
502
|
});
|
|
459
503
|
await server.listen();
|
|
460
504
|
}).rejects.toThrowErrorMatchingInlineSnapshot(
|
|
@@ -482,7 +526,9 @@ describe("operations", () => {
|
|
|
482
526
|
});
|
|
483
527
|
|
|
484
528
|
await using server = await setupServer({
|
|
485
|
-
plugins: [
|
|
529
|
+
plugins: [
|
|
530
|
+
apolloClientAiApps({ targets: ["mcp"], appsOutDir: "dist/apps" }),
|
|
531
|
+
],
|
|
486
532
|
});
|
|
487
533
|
await server.listen();
|
|
488
534
|
|
|
@@ -491,6 +537,7 @@ describe("operations", () => {
|
|
|
491
537
|
{
|
|
492
538
|
"appVersion": "1.0.0",
|
|
493
539
|
"csp": {
|
|
540
|
+
"baseUriDomains": [],
|
|
494
541
|
"connectDomains": [],
|
|
495
542
|
"frameDomains": [],
|
|
496
543
|
"redirectDomains": [],
|
|
@@ -498,6 +545,7 @@ describe("operations", () => {
|
|
|
498
545
|
},
|
|
499
546
|
"format": "apollo-ai-app-manifest",
|
|
500
547
|
"hash": "abc",
|
|
548
|
+
"name": "my-app",
|
|
501
549
|
"operations": [
|
|
502
550
|
{
|
|
503
551
|
"body": "query HelloWorldQuery {
|
|
@@ -557,7 +605,9 @@ describe("@prefetch", () => {
|
|
|
557
605
|
});
|
|
558
606
|
|
|
559
607
|
await using server = await setupServer({
|
|
560
|
-
plugins: [
|
|
608
|
+
plugins: [
|
|
609
|
+
apolloClientAiApps({ targets: ["mcp"], appsOutDir: "dist/apps" }),
|
|
610
|
+
],
|
|
561
611
|
});
|
|
562
612
|
await server.listen();
|
|
563
613
|
|
|
@@ -566,6 +616,7 @@ describe("@prefetch", () => {
|
|
|
566
616
|
{
|
|
567
617
|
"appVersion": "1.0.0",
|
|
568
618
|
"csp": {
|
|
619
|
+
"baseUriDomains": [],
|
|
569
620
|
"connectDomains": [],
|
|
570
621
|
"frameDomains": [],
|
|
571
622
|
"redirectDomains": [],
|
|
@@ -573,6 +624,7 @@ describe("@prefetch", () => {
|
|
|
573
624
|
},
|
|
574
625
|
"format": "apollo-ai-app-manifest",
|
|
575
626
|
"hash": "abc",
|
|
627
|
+
"name": "my-app",
|
|
576
628
|
"operations": [
|
|
577
629
|
{
|
|
578
630
|
"body": "query HelloWorldQuery {
|
|
@@ -612,7 +664,9 @@ describe("@prefetch", () => {
|
|
|
612
664
|
|
|
613
665
|
await expect(async () => {
|
|
614
666
|
await using server = await setupServer({
|
|
615
|
-
plugins: [
|
|
667
|
+
plugins: [
|
|
668
|
+
apolloClientAiApps({ targets: ["mcp"], appsOutDir: "dist/apps" }),
|
|
669
|
+
],
|
|
616
670
|
});
|
|
617
671
|
await server.listen();
|
|
618
672
|
}).rejects.toThrowErrorMatchingInlineSnapshot(
|
|
@@ -622,11 +676,11 @@ describe("@prefetch", () => {
|
|
|
622
676
|
});
|
|
623
677
|
|
|
624
678
|
describe("@tool validation", () => {
|
|
625
|
-
test("errors when tool name is not provided", async () => {
|
|
679
|
+
test("errors when tool name is not provided on anonymous operation", async () => {
|
|
626
680
|
vol.fromJSON({
|
|
627
681
|
"package.json": mockPackageJson(),
|
|
628
682
|
"src/my-component.tsx": declareOperation(gql`
|
|
629
|
-
query
|
|
683
|
+
query @tool {
|
|
630
684
|
helloWorld
|
|
631
685
|
}
|
|
632
686
|
`),
|
|
@@ -634,15 +688,17 @@ describe("@tool validation", () => {
|
|
|
634
688
|
|
|
635
689
|
await expect(async () => {
|
|
636
690
|
await using server = await setupServer({
|
|
637
|
-
plugins: [
|
|
691
|
+
plugins: [
|
|
692
|
+
apolloClientAiApps({ targets: ["mcp"], appsOutDir: "dist/apps" }),
|
|
693
|
+
],
|
|
638
694
|
});
|
|
639
695
|
await server.listen();
|
|
640
696
|
}).rejects.toThrowErrorMatchingInlineSnapshot(
|
|
641
|
-
`[Error:
|
|
697
|
+
`[Error: Anonymous operations cannot use @tool without providing a 'name' argument]`
|
|
642
698
|
);
|
|
643
699
|
});
|
|
644
700
|
|
|
645
|
-
test("errors when tool description is not provided", async () => {
|
|
701
|
+
test("errors when tool description is not provided and operation has no description", async () => {
|
|
646
702
|
vol.fromJSON({
|
|
647
703
|
"package.json": mockPackageJson(),
|
|
648
704
|
"src/my-component.tsx": declareOperation(gql`
|
|
@@ -654,14 +710,106 @@ describe("@tool validation", () => {
|
|
|
654
710
|
|
|
655
711
|
await expect(async () => {
|
|
656
712
|
await using server = await setupServer({
|
|
657
|
-
plugins: [
|
|
713
|
+
plugins: [
|
|
714
|
+
apolloClientAiApps({ targets: ["mcp"], appsOutDir: "dist/apps" }),
|
|
715
|
+
],
|
|
658
716
|
});
|
|
659
717
|
await server.listen();
|
|
660
718
|
}).rejects.toThrowErrorMatchingInlineSnapshot(
|
|
661
|
-
`[Error: 'description' argument must
|
|
719
|
+
`[Error: Operations using @tool without a 'description' argument must have a description on the operation definition]`
|
|
662
720
|
);
|
|
663
721
|
});
|
|
664
722
|
|
|
723
|
+
test("uses operation name as tool name when name is omitted from @tool", async () => {
|
|
724
|
+
vol.fromJSON({
|
|
725
|
+
"package.json": mockPackageJson(),
|
|
726
|
+
"src/my-component.tsx": declareOperation(gql`
|
|
727
|
+
query HelloWorldQuery @tool(description: "A greeting tool") {
|
|
728
|
+
helloWorld
|
|
729
|
+
}
|
|
730
|
+
`),
|
|
731
|
+
});
|
|
732
|
+
|
|
733
|
+
await using server = await setupServer({
|
|
734
|
+
plugins: [
|
|
735
|
+
apolloClientAiApps({ targets: ["mcp"], appsOutDir: "dist/apps" }),
|
|
736
|
+
],
|
|
737
|
+
});
|
|
738
|
+
await server.listen();
|
|
739
|
+
|
|
740
|
+
const manifest = readManifestFile();
|
|
741
|
+
expect(manifest.operations[0].tools).toMatchInlineSnapshot(`
|
|
742
|
+
[
|
|
743
|
+
{
|
|
744
|
+
"description": "A greeting tool",
|
|
745
|
+
"name": "HelloWorldQuery",
|
|
746
|
+
},
|
|
747
|
+
]
|
|
748
|
+
`);
|
|
749
|
+
});
|
|
750
|
+
|
|
751
|
+
test("uses operation description as tool description when description is omitted from @tool", async () => {
|
|
752
|
+
vol.fromJSON({
|
|
753
|
+
"package.json": mockPackageJson(),
|
|
754
|
+
"src/my-component.tsx": declareOperation(gql`
|
|
755
|
+
"""
|
|
756
|
+
A greeting tool
|
|
757
|
+
"""
|
|
758
|
+
query HelloWorldQuery @tool(name: "hello-world") {
|
|
759
|
+
helloWorld
|
|
760
|
+
}
|
|
761
|
+
`),
|
|
762
|
+
});
|
|
763
|
+
|
|
764
|
+
await using server = await setupServer({
|
|
765
|
+
plugins: [
|
|
766
|
+
apolloClientAiApps({ targets: ["mcp"], appsOutDir: "dist/apps" }),
|
|
767
|
+
],
|
|
768
|
+
});
|
|
769
|
+
await server.listen();
|
|
770
|
+
|
|
771
|
+
const manifest = readManifestFile();
|
|
772
|
+
expect(manifest.operations[0].tools).toMatchInlineSnapshot(`
|
|
773
|
+
[
|
|
774
|
+
{
|
|
775
|
+
"description": "A greeting tool",
|
|
776
|
+
"name": "hello-world",
|
|
777
|
+
},
|
|
778
|
+
]
|
|
779
|
+
`);
|
|
780
|
+
});
|
|
781
|
+
|
|
782
|
+
test("uses operation name and description when both are omitted from @tool", async () => {
|
|
783
|
+
vol.fromJSON({
|
|
784
|
+
"package.json": mockPackageJson(),
|
|
785
|
+
"src/my-component.tsx": declareOperation(gql`
|
|
786
|
+
"""
|
|
787
|
+
A greeting tool
|
|
788
|
+
"""
|
|
789
|
+
query HelloWorldQuery @tool {
|
|
790
|
+
helloWorld
|
|
791
|
+
}
|
|
792
|
+
`),
|
|
793
|
+
});
|
|
794
|
+
|
|
795
|
+
await using server = await setupServer({
|
|
796
|
+
plugins: [
|
|
797
|
+
apolloClientAiApps({ targets: ["mcp"], appsOutDir: "dist/apps" }),
|
|
798
|
+
],
|
|
799
|
+
});
|
|
800
|
+
await server.listen();
|
|
801
|
+
|
|
802
|
+
const manifest = readManifestFile();
|
|
803
|
+
expect(manifest.operations[0].tools).toMatchInlineSnapshot(`
|
|
804
|
+
[
|
|
805
|
+
{
|
|
806
|
+
"description": "A greeting tool",
|
|
807
|
+
"name": "HelloWorldQuery",
|
|
808
|
+
},
|
|
809
|
+
]
|
|
810
|
+
`);
|
|
811
|
+
});
|
|
812
|
+
|
|
665
813
|
test("errors when tool name contains spaces", async () => {
|
|
666
814
|
vol.fromJSON({
|
|
667
815
|
"package.json": mockPackageJson(),
|
|
@@ -675,7 +823,9 @@ describe("@tool validation", () => {
|
|
|
675
823
|
|
|
676
824
|
await expect(async () => {
|
|
677
825
|
await using server = await setupServer({
|
|
678
|
-
plugins: [
|
|
826
|
+
plugins: [
|
|
827
|
+
apolloClientAiApps({ targets: ["mcp"], appsOutDir: "dist/apps" }),
|
|
828
|
+
],
|
|
679
829
|
});
|
|
680
830
|
await server.listen();
|
|
681
831
|
}).rejects.toThrowErrorMatchingInlineSnapshot(
|
|
@@ -698,7 +848,9 @@ describe("@tool validation", () => {
|
|
|
698
848
|
|
|
699
849
|
await expect(async () => {
|
|
700
850
|
await using server = await setupServer({
|
|
701
|
-
plugins: [
|
|
851
|
+
plugins: [
|
|
852
|
+
apolloClientAiApps({ targets: ["mcp"], appsOutDir: "dist/apps" }),
|
|
853
|
+
],
|
|
702
854
|
});
|
|
703
855
|
await server.listen();
|
|
704
856
|
}).rejects.toThrowErrorMatchingInlineSnapshot(
|
|
@@ -718,7 +870,9 @@ describe("@tool validation", () => {
|
|
|
718
870
|
|
|
719
871
|
await expect(async () => {
|
|
720
872
|
await using server = await setupServer({
|
|
721
|
-
plugins: [
|
|
873
|
+
plugins: [
|
|
874
|
+
apolloClientAiApps({ targets: ["mcp"], appsOutDir: "dist/apps" }),
|
|
875
|
+
],
|
|
722
876
|
});
|
|
723
877
|
await server.listen();
|
|
724
878
|
}).rejects.toThrowErrorMatchingInlineSnapshot(
|
|
@@ -739,7 +893,9 @@ describe("@tool validation", () => {
|
|
|
739
893
|
|
|
740
894
|
await expect(async () => {
|
|
741
895
|
await using server = await setupServer({
|
|
742
|
-
plugins: [
|
|
896
|
+
plugins: [
|
|
897
|
+
apolloClientAiApps({ targets: ["mcp"], appsOutDir: "dist/apps" }),
|
|
898
|
+
],
|
|
743
899
|
});
|
|
744
900
|
await server.listen();
|
|
745
901
|
}).rejects.toThrowErrorMatchingInlineSnapshot(
|
|
@@ -764,13 +920,63 @@ describe("@tool validation", () => {
|
|
|
764
920
|
|
|
765
921
|
await expect(async () => {
|
|
766
922
|
await using server = await setupServer({
|
|
767
|
-
plugins: [
|
|
923
|
+
plugins: [
|
|
924
|
+
apolloClientAiApps({ targets: ["mcp"], appsOutDir: "dist/apps" }),
|
|
925
|
+
],
|
|
768
926
|
});
|
|
769
927
|
await server.listen();
|
|
770
928
|
}).rejects.toThrowErrorMatchingInlineSnapshot(
|
|
771
929
|
`[Error: Error when parsing directive values: unexpected type 'FloatValue']`
|
|
772
930
|
);
|
|
773
931
|
});
|
|
932
|
+
|
|
933
|
+
test("errors when multiple @tool directives are used and one is missing a name", async () => {
|
|
934
|
+
vol.fromJSON({
|
|
935
|
+
"package.json": mockPackageJson(),
|
|
936
|
+
"src/my-component.tsx": declareOperation(gql`
|
|
937
|
+
query HelloWorldQuery
|
|
938
|
+
@tool(name: "tool-a", description: "Tool A")
|
|
939
|
+
@tool(description: "Tool B") {
|
|
940
|
+
helloWorld
|
|
941
|
+
}
|
|
942
|
+
`),
|
|
943
|
+
});
|
|
944
|
+
|
|
945
|
+
await expect(async () => {
|
|
946
|
+
await using server = await setupServer({
|
|
947
|
+
plugins: [
|
|
948
|
+
apolloClientAiApps({ targets: ["mcp"], appsOutDir: "dist/apps" }),
|
|
949
|
+
],
|
|
950
|
+
});
|
|
951
|
+
await server.listen();
|
|
952
|
+
}).rejects.toThrowErrorMatchingInlineSnapshot(
|
|
953
|
+
`[Error: Operations with multiple @tool directives must provide a 'name' argument on each @tool]`
|
|
954
|
+
);
|
|
955
|
+
});
|
|
956
|
+
|
|
957
|
+
test("errors when multiple @tool directives are used and one is missing a description", async () => {
|
|
958
|
+
vol.fromJSON({
|
|
959
|
+
"package.json": mockPackageJson(),
|
|
960
|
+
"src/my-component.tsx": declareOperation(gql`
|
|
961
|
+
query HelloWorldQuery
|
|
962
|
+
@tool(name: "tool-a", description: "Tool A")
|
|
963
|
+
@tool(name: "tool-b") {
|
|
964
|
+
helloWorld
|
|
965
|
+
}
|
|
966
|
+
`),
|
|
967
|
+
});
|
|
968
|
+
|
|
969
|
+
await expect(async () => {
|
|
970
|
+
await using server = await setupServer({
|
|
971
|
+
plugins: [
|
|
972
|
+
apolloClientAiApps({ targets: ["mcp"], appsOutDir: "dist/apps" }),
|
|
973
|
+
],
|
|
974
|
+
});
|
|
975
|
+
await server.listen();
|
|
976
|
+
}).rejects.toThrowErrorMatchingInlineSnapshot(
|
|
977
|
+
`[Error: Operations with multiple @tool directives must provide a 'description' argument on each @tool]`
|
|
978
|
+
);
|
|
979
|
+
});
|
|
774
980
|
});
|
|
775
981
|
|
|
776
982
|
describe("config validation", () => {
|
|
@@ -792,7 +998,9 @@ describe("config validation", () => {
|
|
|
792
998
|
|
|
793
999
|
await expect(async () => {
|
|
794
1000
|
await using server = await setupServer({
|
|
795
|
-
plugins: [
|
|
1001
|
+
plugins: [
|
|
1002
|
+
apolloClientAiApps({ targets: ["mcp"], appsOutDir: "dist/apps" }),
|
|
1003
|
+
],
|
|
796
1004
|
});
|
|
797
1005
|
await server.listen();
|
|
798
1006
|
}).rejects.toThrowErrorMatchingInlineSnapshot(
|
|
@@ -821,7 +1029,9 @@ describe("config validation", () => {
|
|
|
821
1029
|
|
|
822
1030
|
await expect(async () => {
|
|
823
1031
|
await using server = await setupServer({
|
|
824
|
-
plugins: [
|
|
1032
|
+
plugins: [
|
|
1033
|
+
apolloClientAiApps({ targets: ["mcp"], appsOutDir: "dist/apps" }),
|
|
1034
|
+
],
|
|
825
1035
|
});
|
|
826
1036
|
await server.listen();
|
|
827
1037
|
}).rejects.toThrowErrorMatchingInlineSnapshot(
|
|
@@ -850,7 +1060,9 @@ describe("config validation", () => {
|
|
|
850
1060
|
|
|
851
1061
|
await expect(async () => {
|
|
852
1062
|
await using server = await setupServer({
|
|
853
|
-
plugins: [
|
|
1063
|
+
plugins: [
|
|
1064
|
+
apolloClientAiApps({ targets: ["mcp"], appsOutDir: "dist/apps" }),
|
|
1065
|
+
],
|
|
854
1066
|
});
|
|
855
1067
|
await server.listen();
|
|
856
1068
|
}).rejects.toThrowErrorMatchingInlineSnapshot(
|
|
@@ -874,7 +1086,9 @@ describe("config validation", () => {
|
|
|
874
1086
|
});
|
|
875
1087
|
|
|
876
1088
|
await using server = await setupServer({
|
|
877
|
-
plugins: [
|
|
1089
|
+
plugins: [
|
|
1090
|
+
apolloClientAiApps({ targets: ["mcp"], appsOutDir: "dist/apps" }),
|
|
1091
|
+
],
|
|
878
1092
|
});
|
|
879
1093
|
await server.listen();
|
|
880
1094
|
|
|
@@ -902,7 +1116,9 @@ describe("config validation", () => {
|
|
|
902
1116
|
|
|
903
1117
|
await expect(async () => {
|
|
904
1118
|
await using server = await setupServer({
|
|
905
|
-
plugins: [
|
|
1119
|
+
plugins: [
|
|
1120
|
+
apolloClientAiApps({ targets: ["mcp"], appsOutDir: "dist/apps" }),
|
|
1121
|
+
],
|
|
906
1122
|
});
|
|
907
1123
|
await server.listen();
|
|
908
1124
|
}).rejects.toThrowErrorMatchingInlineSnapshot(
|
|
@@ -930,7 +1146,9 @@ describe("config validation", () => {
|
|
|
930
1146
|
|
|
931
1147
|
await expect(async () => {
|
|
932
1148
|
await using server = await setupServer({
|
|
933
|
-
plugins: [
|
|
1149
|
+
plugins: [
|
|
1150
|
+
apolloClientAiApps({ targets: ["mcp"], appsOutDir: "dist/apps" }),
|
|
1151
|
+
],
|
|
934
1152
|
});
|
|
935
1153
|
await server.listen();
|
|
936
1154
|
}).rejects.toThrowErrorMatchingInlineSnapshot(
|
|
@@ -961,7 +1179,9 @@ describe("config validation", () => {
|
|
|
961
1179
|
|
|
962
1180
|
await expect(async () => {
|
|
963
1181
|
await using server = await setupServer({
|
|
964
|
-
plugins: [
|
|
1182
|
+
plugins: [
|
|
1183
|
+
apolloClientAiApps({ targets: ["mcp"], appsOutDir: "dist/apps" }),
|
|
1184
|
+
],
|
|
965
1185
|
});
|
|
966
1186
|
await server.listen();
|
|
967
1187
|
}).rejects.toThrowErrorMatchingInlineSnapshot(
|
|
@@ -989,7 +1209,9 @@ describe("config validation", () => {
|
|
|
989
1209
|
|
|
990
1210
|
await expect(async () => {
|
|
991
1211
|
await using server = await setupServer({
|
|
992
|
-
plugins: [
|
|
1212
|
+
plugins: [
|
|
1213
|
+
apolloClientAiApps({ targets: ["mcp"], appsOutDir: "dist/apps" }),
|
|
1214
|
+
],
|
|
993
1215
|
});
|
|
994
1216
|
await server.listen();
|
|
995
1217
|
}).rejects.toThrowErrorMatchingInlineSnapshot(
|
|
@@ -1012,7 +1234,9 @@ describe("config validation", () => {
|
|
|
1012
1234
|
});
|
|
1013
1235
|
|
|
1014
1236
|
await using server = await setupServer({
|
|
1015
|
-
plugins: [
|
|
1237
|
+
plugins: [
|
|
1238
|
+
apolloClientAiApps({ targets: ["mcp"], appsOutDir: "dist/apps" }),
|
|
1239
|
+
],
|
|
1016
1240
|
});
|
|
1017
1241
|
await server.listen();
|
|
1018
1242
|
|
|
@@ -1041,7 +1265,9 @@ describe("entry points", () => {
|
|
|
1041
1265
|
|
|
1042
1266
|
await using server = await setupServer({
|
|
1043
1267
|
mode: "staging",
|
|
1044
|
-
plugins: [
|
|
1268
|
+
plugins: [
|
|
1269
|
+
apolloClientAiApps({ targets: ["mcp"], appsOutDir: "dist/apps" }),
|
|
1270
|
+
],
|
|
1045
1271
|
});
|
|
1046
1272
|
await server.listen();
|
|
1047
1273
|
|
|
@@ -1070,7 +1296,13 @@ describe("entry points", () => {
|
|
|
1070
1296
|
|
|
1071
1297
|
await using server = await setupServer({
|
|
1072
1298
|
mode: "staging",
|
|
1073
|
-
plugins: [
|
|
1299
|
+
plugins: [
|
|
1300
|
+
apolloClientAiApps({
|
|
1301
|
+
targets: ["mcp"],
|
|
1302
|
+
devTarget: "mcp",
|
|
1303
|
+
appsOutDir: "dist/apps",
|
|
1304
|
+
}),
|
|
1305
|
+
],
|
|
1074
1306
|
});
|
|
1075
1307
|
await server.listen();
|
|
1076
1308
|
|
|
@@ -1091,7 +1323,9 @@ describe("entry points", () => {
|
|
|
1091
1323
|
|
|
1092
1324
|
await using server = await setupServer({
|
|
1093
1325
|
server: { https: {}, port: 5678 },
|
|
1094
|
-
plugins: [
|
|
1326
|
+
plugins: [
|
|
1327
|
+
apolloClientAiApps({ targets: ["mcp"], appsOutDir: "dist/apps" }),
|
|
1328
|
+
],
|
|
1095
1329
|
});
|
|
1096
1330
|
await server.listen();
|
|
1097
1331
|
|
|
@@ -1112,7 +1346,9 @@ describe("entry points", () => {
|
|
|
1112
1346
|
|
|
1113
1347
|
await using server = await setupServer({
|
|
1114
1348
|
server: { port: 5678, host: "0.0.0.0" },
|
|
1115
|
-
plugins: [
|
|
1349
|
+
plugins: [
|
|
1350
|
+
apolloClientAiApps({ targets: ["mcp"], appsOutDir: "dist/apps" }),
|
|
1351
|
+
],
|
|
1116
1352
|
});
|
|
1117
1353
|
await server.listen();
|
|
1118
1354
|
|
|
@@ -1139,7 +1375,9 @@ describe("entry points", () => {
|
|
|
1139
1375
|
|
|
1140
1376
|
await buildApp({
|
|
1141
1377
|
mode: "staging",
|
|
1142
|
-
plugins: [
|
|
1378
|
+
plugins: [
|
|
1379
|
+
apolloClientAiApps({ targets: ["mcp"], appsOutDir: "dist/apps" }),
|
|
1380
|
+
],
|
|
1143
1381
|
});
|
|
1144
1382
|
|
|
1145
1383
|
const manifest = readManifestFile();
|
|
@@ -1168,7 +1406,12 @@ describe("entry points", () => {
|
|
|
1168
1406
|
|
|
1169
1407
|
await buildApp({
|
|
1170
1408
|
mode: "staging",
|
|
1171
|
-
plugins: [
|
|
1409
|
+
plugins: [
|
|
1410
|
+
apolloClientAiApps({
|
|
1411
|
+
targets: ["mcp", "openai"],
|
|
1412
|
+
appsOutDir: "dist/apps",
|
|
1413
|
+
}),
|
|
1414
|
+
],
|
|
1172
1415
|
});
|
|
1173
1416
|
|
|
1174
1417
|
const manifest = readManifestFile();
|
|
@@ -1197,7 +1440,12 @@ describe("entry points", () => {
|
|
|
1197
1440
|
|
|
1198
1441
|
await buildApp({
|
|
1199
1442
|
mode: "staging",
|
|
1200
|
-
plugins: [
|
|
1443
|
+
plugins: [
|
|
1444
|
+
apolloClientAiApps({
|
|
1445
|
+
targets: ["mcp", "openai"],
|
|
1446
|
+
appsOutDir: "dist/apps",
|
|
1447
|
+
}),
|
|
1448
|
+
],
|
|
1201
1449
|
});
|
|
1202
1450
|
|
|
1203
1451
|
const manifest = readManifestFile();
|
|
@@ -1228,7 +1476,9 @@ describe("entry points", () => {
|
|
|
1228
1476
|
|
|
1229
1477
|
await buildApp({
|
|
1230
1478
|
mode: "staging",
|
|
1231
|
-
plugins: [
|
|
1479
|
+
plugins: [
|
|
1480
|
+
apolloClientAiApps({ targets: ["mcp"], appsOutDir: "dist/apps" }),
|
|
1481
|
+
],
|
|
1232
1482
|
});
|
|
1233
1483
|
|
|
1234
1484
|
const manifest = readManifestFile();
|
|
@@ -1250,7 +1500,9 @@ describe("entry points", () => {
|
|
|
1250
1500
|
|
|
1251
1501
|
await buildApp({
|
|
1252
1502
|
mode: "production",
|
|
1253
|
-
plugins: [
|
|
1503
|
+
plugins: [
|
|
1504
|
+
apolloClientAiApps({ targets: ["mcp"], appsOutDir: "dist/apps" }),
|
|
1505
|
+
],
|
|
1254
1506
|
});
|
|
1255
1507
|
|
|
1256
1508
|
const manifest = readManifestFile();
|
|
@@ -1270,7 +1522,12 @@ describe("entry points", () => {
|
|
|
1270
1522
|
|
|
1271
1523
|
await buildApp({
|
|
1272
1524
|
mode: "production",
|
|
1273
|
-
plugins: [
|
|
1525
|
+
plugins: [
|
|
1526
|
+
apolloClientAiApps({
|
|
1527
|
+
targets: ["mcp", "openai"],
|
|
1528
|
+
appsOutDir: "dist/apps",
|
|
1529
|
+
}),
|
|
1530
|
+
],
|
|
1274
1531
|
});
|
|
1275
1532
|
|
|
1276
1533
|
const manifest = readManifestFile();
|
|
@@ -1295,7 +1552,9 @@ describe("entry points", () => {
|
|
|
1295
1552
|
async () =>
|
|
1296
1553
|
await buildApp({
|
|
1297
1554
|
mode: "staging",
|
|
1298
|
-
plugins: [
|
|
1555
|
+
plugins: [
|
|
1556
|
+
apolloClientAiApps({ targets: ["mcp"], appsOutDir: "dist/apps" }),
|
|
1557
|
+
],
|
|
1299
1558
|
})
|
|
1300
1559
|
).rejects.toThrowError(
|
|
1301
1560
|
`[@apollo/client-ai-apps/vite] No entry point found for mode "staging". Entry points other than "development" and "production" must be defined in package.json file.`
|
|
@@ -1315,11 +1574,15 @@ describe("entry points", () => {
|
|
|
1315
1574
|
|
|
1316
1575
|
await buildApp({
|
|
1317
1576
|
mode: "production",
|
|
1318
|
-
plugins: [
|
|
1577
|
+
plugins: [
|
|
1578
|
+
apolloClientAiApps({ targets: ["mcp"], appsOutDir: "dist/apps" }),
|
|
1579
|
+
],
|
|
1319
1580
|
});
|
|
1320
1581
|
|
|
1321
1582
|
expect(vol.existsSync(".application-manifest.json")).toBe(true);
|
|
1322
|
-
expect(vol.existsSync("dist/.application-manifest.json")).toBe(
|
|
1583
|
+
expect(vol.existsSync("dist/apps/my-app/.application-manifest.json")).toBe(
|
|
1584
|
+
true
|
|
1585
|
+
);
|
|
1323
1586
|
});
|
|
1324
1587
|
|
|
1325
1588
|
test("writes to both locations when running in build mode with multiple targets", async () => {
|
|
@@ -1335,11 +1598,155 @@ describe("entry points", () => {
|
|
|
1335
1598
|
|
|
1336
1599
|
await buildApp({
|
|
1337
1600
|
mode: "production",
|
|
1338
|
-
plugins: [
|
|
1601
|
+
plugins: [
|
|
1602
|
+
apolloClientAiApps({
|
|
1603
|
+
targets: ["mcp", "openai"],
|
|
1604
|
+
appsOutDir: "dist/apps",
|
|
1605
|
+
}),
|
|
1606
|
+
],
|
|
1339
1607
|
});
|
|
1340
1608
|
|
|
1341
1609
|
expect(vol.existsSync(".application-manifest.json")).toBe(true);
|
|
1342
|
-
expect(vol.existsSync("dist/.application-manifest.json")).toBe(
|
|
1610
|
+
expect(vol.existsSync("dist/apps/my-app/.application-manifest.json")).toBe(
|
|
1611
|
+
true
|
|
1612
|
+
);
|
|
1613
|
+
});
|
|
1614
|
+
|
|
1615
|
+
test("generates .application-manifest.d.json.ts at root in build mode", async () => {
|
|
1616
|
+
vol.fromJSON({
|
|
1617
|
+
"package.json": mockPackageJson(),
|
|
1618
|
+
"src/my-component.tsx": declareOperation(gql`
|
|
1619
|
+
query HelloWorldQuery
|
|
1620
|
+
@tool(name: "hello-world", description: "This is an awesome tool!") {
|
|
1621
|
+
helloWorld
|
|
1622
|
+
}
|
|
1623
|
+
`),
|
|
1624
|
+
});
|
|
1625
|
+
|
|
1626
|
+
await buildApp({
|
|
1627
|
+
mode: "production",
|
|
1628
|
+
plugins: [
|
|
1629
|
+
apolloClientAiApps({ targets: ["mcp"], appsOutDir: "dist/apps" }),
|
|
1630
|
+
],
|
|
1631
|
+
});
|
|
1632
|
+
|
|
1633
|
+
expect(vol.existsSync(".application-manifest.d.json.ts")).toBe(true);
|
|
1634
|
+
});
|
|
1635
|
+
|
|
1636
|
+
test("generates .application-manifest.d.json.ts at root in serve mode", async () => {
|
|
1637
|
+
vol.fromJSON({
|
|
1638
|
+
"package.json": mockPackageJson(),
|
|
1639
|
+
"src/my-component.tsx": declareOperation(gql`
|
|
1640
|
+
query HelloWorldQuery
|
|
1641
|
+
@tool(name: "hello-world", description: "This is an awesome tool!") {
|
|
1642
|
+
helloWorld
|
|
1643
|
+
}
|
|
1644
|
+
`),
|
|
1645
|
+
});
|
|
1646
|
+
|
|
1647
|
+
await using server = await setupServer({
|
|
1648
|
+
plugins: [
|
|
1649
|
+
apolloClientAiApps({ targets: ["mcp"], appsOutDir: "dist/apps" }),
|
|
1650
|
+
],
|
|
1651
|
+
});
|
|
1652
|
+
await server.listen();
|
|
1653
|
+
|
|
1654
|
+
expect(vol.existsSync(".application-manifest.d.json.ts")).toBe(true);
|
|
1655
|
+
});
|
|
1656
|
+
|
|
1657
|
+
test("does not write .application-manifest.json.d.ts to appsOutDir", async () => {
|
|
1658
|
+
vol.fromJSON({
|
|
1659
|
+
"package.json": mockPackageJson(),
|
|
1660
|
+
"src/my-component.tsx": declareOperation(gql`
|
|
1661
|
+
query HelloWorldQuery
|
|
1662
|
+
@tool(name: "hello-world", description: "This is an awesome tool!") {
|
|
1663
|
+
helloWorld
|
|
1664
|
+
}
|
|
1665
|
+
`),
|
|
1666
|
+
});
|
|
1667
|
+
|
|
1668
|
+
await buildApp({
|
|
1669
|
+
mode: "production",
|
|
1670
|
+
plugins: [
|
|
1671
|
+
apolloClientAiApps({ targets: ["mcp"], appsOutDir: "dist/apps" }),
|
|
1672
|
+
],
|
|
1673
|
+
});
|
|
1674
|
+
|
|
1675
|
+
expect(
|
|
1676
|
+
vol.existsSync("dist/apps/my-app/.application-manifest.json.d.ts")
|
|
1677
|
+
).toBe(false);
|
|
1678
|
+
});
|
|
1679
|
+
});
|
|
1680
|
+
|
|
1681
|
+
describe("appsOutDir", () => {
|
|
1682
|
+
test("errors when last segment is not `apps`", () => {
|
|
1683
|
+
expect(() =>
|
|
1684
|
+
apolloClientAiApps({ targets: ["mcp"], appsOutDir: "dist/output" })
|
|
1685
|
+
).toThrowError(
|
|
1686
|
+
"`appsOutDir` must end with `apps` as the final path segment (e.g. `path/to/apps`)."
|
|
1687
|
+
);
|
|
1688
|
+
});
|
|
1689
|
+
|
|
1690
|
+
test("accepts trailing slash", async () => {
|
|
1691
|
+
vol.fromJSON({
|
|
1692
|
+
"package.json": mockPackageJson(),
|
|
1693
|
+
});
|
|
1694
|
+
|
|
1695
|
+
await expect(
|
|
1696
|
+
buildApp({
|
|
1697
|
+
mode: "production",
|
|
1698
|
+
build: { write: false },
|
|
1699
|
+
plugins: [
|
|
1700
|
+
apolloClientAiApps({ targets: ["mcp"], appsOutDir: "dist/apps/" }),
|
|
1701
|
+
],
|
|
1702
|
+
})
|
|
1703
|
+
).resolves.not.toThrowError();
|
|
1704
|
+
});
|
|
1705
|
+
|
|
1706
|
+
test("warns when `build.outDir` is set", async () => {
|
|
1707
|
+
vol.fromJSON({
|
|
1708
|
+
"package.json": mockPackageJson(),
|
|
1709
|
+
});
|
|
1710
|
+
|
|
1711
|
+
using _ = spyOnConsole("warn");
|
|
1712
|
+
|
|
1713
|
+
await buildApp({
|
|
1714
|
+
mode: "production",
|
|
1715
|
+
build: { outDir: "custom-out", write: false },
|
|
1716
|
+
plugins: [
|
|
1717
|
+
apolloClientAiApps({ targets: ["mcp"], appsOutDir: "dist/apps" }),
|
|
1718
|
+
],
|
|
1719
|
+
});
|
|
1720
|
+
|
|
1721
|
+
expect(console.warn).toHaveBeenCalledWith(
|
|
1722
|
+
expect.stringContaining(
|
|
1723
|
+
"`build.outDir` is set in your Vite config but will be ignored"
|
|
1724
|
+
)
|
|
1725
|
+
);
|
|
1726
|
+
});
|
|
1727
|
+
|
|
1728
|
+
test("places output under `appsOutDir`", async () => {
|
|
1729
|
+
vol.fromJSON({
|
|
1730
|
+
"package.json": mockPackageJson(),
|
|
1731
|
+
"src/my-component.tsx": declareOperation(gql`
|
|
1732
|
+
query HelloWorldQuery
|
|
1733
|
+
@tool(name: "hello-world", description: "This is an awesome tool!") {
|
|
1734
|
+
helloWorld
|
|
1735
|
+
}
|
|
1736
|
+
`),
|
|
1737
|
+
});
|
|
1738
|
+
|
|
1739
|
+
await buildApp({
|
|
1740
|
+
mode: "production",
|
|
1741
|
+
plugins: [
|
|
1742
|
+
apolloClientAiApps({ targets: ["mcp"], appsOutDir: "dist/apps" }),
|
|
1743
|
+
],
|
|
1744
|
+
});
|
|
1745
|
+
|
|
1746
|
+
expect(vol.existsSync("dist/apps/my-app/.application-manifest.json")).toBe(
|
|
1747
|
+
true
|
|
1748
|
+
);
|
|
1749
|
+
expect(vol.existsSync(".application-manifest.json")).toBe(true);
|
|
1343
1750
|
});
|
|
1344
1751
|
});
|
|
1345
1752
|
|
|
@@ -1387,7 +1794,9 @@ export default config;
|
|
|
1387
1794
|
|
|
1388
1795
|
await buildApp({
|
|
1389
1796
|
mode: "production",
|
|
1390
|
-
plugins: [
|
|
1797
|
+
plugins: [
|
|
1798
|
+
apolloClientAiApps({ targets: ["mcp"], appsOutDir: "dist/apps" }),
|
|
1799
|
+
],
|
|
1391
1800
|
});
|
|
1392
1801
|
|
|
1393
1802
|
const manifest = readManifestFile();
|
|
@@ -1423,7 +1832,9 @@ export default config;
|
|
|
1423
1832
|
|
|
1424
1833
|
await buildApp({
|
|
1425
1834
|
mode: "production",
|
|
1426
|
-
plugins: [
|
|
1835
|
+
plugins: [
|
|
1836
|
+
apolloClientAiApps({ targets: ["mcp"], appsOutDir: "dist/apps" }),
|
|
1837
|
+
],
|
|
1427
1838
|
});
|
|
1428
1839
|
|
|
1429
1840
|
const manifest = readManifestFile();
|
|
@@ -1457,7 +1868,9 @@ describe("file watching", () => {
|
|
|
1457
1868
|
});
|
|
1458
1869
|
|
|
1459
1870
|
await using server = await setupServer({
|
|
1460
|
-
plugins: [
|
|
1871
|
+
plugins: [
|
|
1872
|
+
apolloClientAiApps({ targets: ["mcp"], appsOutDir: "dist/apps" }),
|
|
1873
|
+
],
|
|
1461
1874
|
});
|
|
1462
1875
|
await server.listen();
|
|
1463
1876
|
|
|
@@ -1485,6 +1898,541 @@ describe("file watching", () => {
|
|
|
1485
1898
|
});
|
|
1486
1899
|
});
|
|
1487
1900
|
|
|
1901
|
+
describe("tool input types", () => {
|
|
1902
|
+
const schema = `
|
|
1903
|
+
type Query {
|
|
1904
|
+
todo(id: ID!): Todo
|
|
1905
|
+
}
|
|
1906
|
+
|
|
1907
|
+
type Mutation {
|
|
1908
|
+
createTodo(title: String!, description: String): Todo
|
|
1909
|
+
deleteTodo(id: ID!): Boolean
|
|
1910
|
+
}
|
|
1911
|
+
|
|
1912
|
+
type Todo {
|
|
1913
|
+
id: ID!
|
|
1914
|
+
title: String!
|
|
1915
|
+
description: String
|
|
1916
|
+
}
|
|
1917
|
+
`;
|
|
1918
|
+
|
|
1919
|
+
test("generates operation-types.d.ts with variable types when schema is provided", async () => {
|
|
1920
|
+
vol.fromJSON({
|
|
1921
|
+
"package.json": mockPackageJson(),
|
|
1922
|
+
"src/my-component.tsx": declareOperation(gql`
|
|
1923
|
+
mutation CreateTodo($title: String!, $description: String)
|
|
1924
|
+
@tool(name: "CreateTodo", description: "Creates a todo") {
|
|
1925
|
+
createTodo(title: $title, description: $description) {
|
|
1926
|
+
id
|
|
1927
|
+
}
|
|
1928
|
+
}
|
|
1929
|
+
`),
|
|
1930
|
+
});
|
|
1931
|
+
|
|
1932
|
+
await using server = await setupServer({
|
|
1933
|
+
plugins: [
|
|
1934
|
+
apolloClientAiApps({
|
|
1935
|
+
targets: ["mcp"],
|
|
1936
|
+
appsOutDir: "dist/apps",
|
|
1937
|
+
schema,
|
|
1938
|
+
}),
|
|
1939
|
+
],
|
|
1940
|
+
});
|
|
1941
|
+
await server.listen();
|
|
1942
|
+
|
|
1943
|
+
const content = fs.readFileSync(
|
|
1944
|
+
".apollo-client-ai-apps/types/operation-types.d.ts",
|
|
1945
|
+
"utf-8"
|
|
1946
|
+
);
|
|
1947
|
+
expect(content).toMatchInlineSnapshot(`
|
|
1948
|
+
"// Auto-generated by @apollo/client-ai-apps. Do not edit manually.
|
|
1949
|
+
export type Maybe<T> = T | null;
|
|
1950
|
+
|
|
1951
|
+
export type InputMaybe<T> = Maybe<T>;
|
|
1952
|
+
|
|
1953
|
+
export type Exact<T extends {
|
|
1954
|
+
[key: string]: unknown;
|
|
1955
|
+
}> = {
|
|
1956
|
+
[K in keyof T]: T[K];
|
|
1957
|
+
};
|
|
1958
|
+
|
|
1959
|
+
/** All built-in and custom scalars, mapped to their actual values */
|
|
1960
|
+
export type Scalars = {
|
|
1961
|
+
ID: {
|
|
1962
|
+
input: string;
|
|
1963
|
+
output: string;
|
|
1964
|
+
};
|
|
1965
|
+
String: {
|
|
1966
|
+
input: string;
|
|
1967
|
+
output: string;
|
|
1968
|
+
};
|
|
1969
|
+
Boolean: {
|
|
1970
|
+
input: boolean;
|
|
1971
|
+
output: boolean;
|
|
1972
|
+
};
|
|
1973
|
+
Int: {
|
|
1974
|
+
input: number;
|
|
1975
|
+
output: number;
|
|
1976
|
+
};
|
|
1977
|
+
Float: {
|
|
1978
|
+
input: number;
|
|
1979
|
+
output: number;
|
|
1980
|
+
};
|
|
1981
|
+
};
|
|
1982
|
+
|
|
1983
|
+
export type CreateTodoMutationVariables = Exact<{
|
|
1984
|
+
title: Scalars["String"]["input"];
|
|
1985
|
+
description?: InputMaybe<Scalars["String"]["input"]>;
|
|
1986
|
+
}>;"
|
|
1987
|
+
`);
|
|
1988
|
+
});
|
|
1989
|
+
|
|
1990
|
+
test("generates register.d.ts with toolInputs when schema is provided", async () => {
|
|
1991
|
+
vol.fromJSON({
|
|
1992
|
+
"package.json": mockPackageJson(),
|
|
1993
|
+
"src/my-component.tsx": declareOperation(gql`
|
|
1994
|
+
mutation CreateTodo($title: String!, $description: String)
|
|
1995
|
+
@tool(name: "CreateTodo", description: "Creates a todo") {
|
|
1996
|
+
createTodo(title: $title, description: $description) {
|
|
1997
|
+
id
|
|
1998
|
+
}
|
|
1999
|
+
}
|
|
2000
|
+
`),
|
|
2001
|
+
});
|
|
2002
|
+
|
|
2003
|
+
await using server = await setupServer({
|
|
2004
|
+
plugins: [
|
|
2005
|
+
apolloClientAiApps({
|
|
2006
|
+
targets: ["mcp"],
|
|
2007
|
+
appsOutDir: "dist/apps",
|
|
2008
|
+
schema,
|
|
2009
|
+
}),
|
|
2010
|
+
],
|
|
2011
|
+
});
|
|
2012
|
+
await server.listen();
|
|
2013
|
+
|
|
2014
|
+
const content = fs.readFileSync(
|
|
2015
|
+
".apollo-client-ai-apps/types/register.d.ts",
|
|
2016
|
+
"utf-8"
|
|
2017
|
+
);
|
|
2018
|
+
expect(content).toMatchInlineSnapshot(`
|
|
2019
|
+
"// This file is auto-generated by @apollo/client-ai-apps. Do not edit manually.
|
|
2020
|
+
import "@apollo/client-ai-apps";
|
|
2021
|
+
|
|
2022
|
+
import type { CreateTodoMutationVariables } from "./operation-types.js";
|
|
2023
|
+
|
|
2024
|
+
declare module "@apollo/client-ai-apps" {
|
|
2025
|
+
interface Register {
|
|
2026
|
+
toolName: "CreateTodo";
|
|
2027
|
+
toolInputs: {
|
|
2028
|
+
CreateTodo: CreateTodoMutationVariables;
|
|
2029
|
+
};
|
|
2030
|
+
}
|
|
2031
|
+
}"
|
|
2032
|
+
`);
|
|
2033
|
+
});
|
|
2034
|
+
|
|
2035
|
+
test("generates register.d.ts with operations that contain fragments", async () => {
|
|
2036
|
+
vol.fromJSON({
|
|
2037
|
+
"package.json": mockPackageJson(),
|
|
2038
|
+
"src/my-component.tsx": declareOperation(gql`
|
|
2039
|
+
mutation CreateTodo($title: String!, $description: String)
|
|
2040
|
+
@tool(name: "CreateTodo", description: "Creates a todo") {
|
|
2041
|
+
createTodo(title: $title, description: $description) {
|
|
2042
|
+
id
|
|
2043
|
+
...TodoFragment
|
|
2044
|
+
}
|
|
2045
|
+
}
|
|
2046
|
+
`),
|
|
2047
|
+
"src/todo-fragment.tsx": declareFragment(gql`
|
|
2048
|
+
fragment TodoFragment on Todo {
|
|
2049
|
+
title
|
|
2050
|
+
}
|
|
2051
|
+
`),
|
|
2052
|
+
});
|
|
2053
|
+
|
|
2054
|
+
await using server = await setupServer({
|
|
2055
|
+
plugins: [
|
|
2056
|
+
apolloClientAiApps({
|
|
2057
|
+
targets: ["mcp"],
|
|
2058
|
+
appsOutDir: "dist/apps",
|
|
2059
|
+
schema,
|
|
2060
|
+
}),
|
|
2061
|
+
],
|
|
2062
|
+
});
|
|
2063
|
+
await server.listen();
|
|
2064
|
+
|
|
2065
|
+
expect(
|
|
2066
|
+
fs.readFileSync(".apollo-client-ai-apps/types/register.d.ts", "utf-8")
|
|
2067
|
+
).toMatchInlineSnapshot(`
|
|
2068
|
+
"// This file is auto-generated by @apollo/client-ai-apps. Do not edit manually.
|
|
2069
|
+
import "@apollo/client-ai-apps";
|
|
2070
|
+
|
|
2071
|
+
import type { CreateTodoMutationVariables } from "./operation-types.js";
|
|
2072
|
+
|
|
2073
|
+
declare module "@apollo/client-ai-apps" {
|
|
2074
|
+
interface Register {
|
|
2075
|
+
toolName: "CreateTodo";
|
|
2076
|
+
toolInputs: {
|
|
2077
|
+
CreateTodo: CreateTodoMutationVariables;
|
|
2078
|
+
};
|
|
2079
|
+
}
|
|
2080
|
+
}"
|
|
2081
|
+
`);
|
|
2082
|
+
expect(
|
|
2083
|
+
fs.readFileSync(
|
|
2084
|
+
".apollo-client-ai-apps/types/operation-types.d.ts",
|
|
2085
|
+
"utf-8"
|
|
2086
|
+
)
|
|
2087
|
+
).toMatchInlineSnapshot(`
|
|
2088
|
+
"// Auto-generated by @apollo/client-ai-apps. Do not edit manually.
|
|
2089
|
+
export type Maybe<T> = T | null;
|
|
2090
|
+
|
|
2091
|
+
export type InputMaybe<T> = Maybe<T>;
|
|
2092
|
+
|
|
2093
|
+
export type Exact<T extends {
|
|
2094
|
+
[key: string]: unknown;
|
|
2095
|
+
}> = {
|
|
2096
|
+
[K in keyof T]: T[K];
|
|
2097
|
+
};
|
|
2098
|
+
|
|
2099
|
+
/** All built-in and custom scalars, mapped to their actual values */
|
|
2100
|
+
export type Scalars = {
|
|
2101
|
+
ID: {
|
|
2102
|
+
input: string;
|
|
2103
|
+
output: string;
|
|
2104
|
+
};
|
|
2105
|
+
String: {
|
|
2106
|
+
input: string;
|
|
2107
|
+
output: string;
|
|
2108
|
+
};
|
|
2109
|
+
Boolean: {
|
|
2110
|
+
input: boolean;
|
|
2111
|
+
output: boolean;
|
|
2112
|
+
};
|
|
2113
|
+
Int: {
|
|
2114
|
+
input: number;
|
|
2115
|
+
output: number;
|
|
2116
|
+
};
|
|
2117
|
+
Float: {
|
|
2118
|
+
input: number;
|
|
2119
|
+
output: number;
|
|
2120
|
+
};
|
|
2121
|
+
};
|
|
2122
|
+
|
|
2123
|
+
export type CreateTodoMutationVariables = Exact<{
|
|
2124
|
+
title: Scalars["String"]["input"];
|
|
2125
|
+
description?: InputMaybe<Scalars["String"]["input"]>;
|
|
2126
|
+
}>;"
|
|
2127
|
+
`);
|
|
2128
|
+
});
|
|
2129
|
+
|
|
2130
|
+
test("tool with no extraInputs uses only the Variables type", async () => {
|
|
2131
|
+
vol.fromJSON({
|
|
2132
|
+
"package.json": mockPackageJson(),
|
|
2133
|
+
"src/my-component.tsx": declareOperation(gql`
|
|
2134
|
+
mutation DeleteTodo($id: ID!)
|
|
2135
|
+
@tool(name: "DeleteTodo", description: "Deletes a todo") {
|
|
2136
|
+
deleteTodo(id: $id)
|
|
2137
|
+
}
|
|
2138
|
+
`),
|
|
2139
|
+
});
|
|
2140
|
+
|
|
2141
|
+
await using server = await setupServer({
|
|
2142
|
+
plugins: [
|
|
2143
|
+
apolloClientAiApps({
|
|
2144
|
+
targets: ["mcp"],
|
|
2145
|
+
appsOutDir: "dist/apps",
|
|
2146
|
+
schema,
|
|
2147
|
+
}),
|
|
2148
|
+
],
|
|
2149
|
+
});
|
|
2150
|
+
await server.listen();
|
|
2151
|
+
|
|
2152
|
+
const content = fs.readFileSync(
|
|
2153
|
+
".apollo-client-ai-apps/types/register.d.ts",
|
|
2154
|
+
"utf-8"
|
|
2155
|
+
);
|
|
2156
|
+
expect(content).toMatchInlineSnapshot(`
|
|
2157
|
+
"// This file is auto-generated by @apollo/client-ai-apps. Do not edit manually.
|
|
2158
|
+
import "@apollo/client-ai-apps";
|
|
2159
|
+
|
|
2160
|
+
import type { DeleteTodoMutationVariables } from "./operation-types.js";
|
|
2161
|
+
|
|
2162
|
+
declare module "@apollo/client-ai-apps" {
|
|
2163
|
+
interface Register {
|
|
2164
|
+
toolName: "DeleteTodo";
|
|
2165
|
+
toolInputs: {
|
|
2166
|
+
DeleteTodo: DeleteTodoMutationVariables;
|
|
2167
|
+
};
|
|
2168
|
+
}
|
|
2169
|
+
}"
|
|
2170
|
+
`);
|
|
2171
|
+
});
|
|
2172
|
+
|
|
2173
|
+
test("tool with extraInputs adds properties to tool input type", async () => {
|
|
2174
|
+
vol.fromJSON({
|
|
2175
|
+
"package.json": mockPackageJson(),
|
|
2176
|
+
"src/my-component.tsx": declareOperation(gql`
|
|
2177
|
+
mutation CreateTodo($title: String!)
|
|
2178
|
+
@tool(
|
|
2179
|
+
name: "CreateTodo"
|
|
2180
|
+
description: "Creates a todo"
|
|
2181
|
+
extraInputs: [
|
|
2182
|
+
{ name: "priority", type: "string", description: "Priority" }
|
|
2183
|
+
{ name: "urgent", type: "boolean", description: "Is urgent?" }
|
|
2184
|
+
{
|
|
2185
|
+
name: "odd-name"
|
|
2186
|
+
type: "boolean"
|
|
2187
|
+
description: "Test of odd name"
|
|
2188
|
+
}
|
|
2189
|
+
]
|
|
2190
|
+
) {
|
|
2191
|
+
createTodo(title: $title) {
|
|
2192
|
+
id
|
|
2193
|
+
}
|
|
2194
|
+
}
|
|
2195
|
+
`),
|
|
2196
|
+
});
|
|
2197
|
+
|
|
2198
|
+
await using server = await setupServer({
|
|
2199
|
+
plugins: [
|
|
2200
|
+
apolloClientAiApps({
|
|
2201
|
+
targets: ["mcp"],
|
|
2202
|
+
appsOutDir: "dist/apps",
|
|
2203
|
+
schema,
|
|
2204
|
+
}),
|
|
2205
|
+
],
|
|
2206
|
+
});
|
|
2207
|
+
await server.listen();
|
|
2208
|
+
|
|
2209
|
+
const content = fs.readFileSync(
|
|
2210
|
+
".apollo-client-ai-apps/types/register.d.ts",
|
|
2211
|
+
"utf-8"
|
|
2212
|
+
);
|
|
2213
|
+
expect(content).toMatchInlineSnapshot(`
|
|
2214
|
+
"// This file is auto-generated by @apollo/client-ai-apps. Do not edit manually.
|
|
2215
|
+
import "@apollo/client-ai-apps";
|
|
2216
|
+
|
|
2217
|
+
import type { CreateTodoMutationVariables } from "./operation-types.js";
|
|
2218
|
+
|
|
2219
|
+
declare module "@apollo/client-ai-apps" {
|
|
2220
|
+
interface Register {
|
|
2221
|
+
toolName: "CreateTodo";
|
|
2222
|
+
toolInputs: {
|
|
2223
|
+
CreateTodo: CreateTodoMutationVariables & {
|
|
2224
|
+
priority?: string;
|
|
2225
|
+
urgent?: boolean;
|
|
2226
|
+
"odd-name"?: boolean;
|
|
2227
|
+
};
|
|
2228
|
+
};
|
|
2229
|
+
}
|
|
2230
|
+
}"
|
|
2231
|
+
`);
|
|
2232
|
+
});
|
|
2233
|
+
|
|
2234
|
+
test("multiple @tool directives on an operation each get their own toolInputs entry", async () => {
|
|
2235
|
+
vol.fromJSON({
|
|
2236
|
+
"package.json": mockPackageJson(),
|
|
2237
|
+
"src/my-component.tsx": declareOperation(gql`
|
|
2238
|
+
mutation CreateTodo($title: String!)
|
|
2239
|
+
@tool(name: "CreateTodo", description: "Creates a todo")
|
|
2240
|
+
@tool(name: "AddTask", description: "Adds a task") {
|
|
2241
|
+
createTodo(title: $title) {
|
|
2242
|
+
id
|
|
2243
|
+
}
|
|
2244
|
+
}
|
|
2245
|
+
`),
|
|
2246
|
+
});
|
|
2247
|
+
|
|
2248
|
+
await using server = await setupServer({
|
|
2249
|
+
plugins: [
|
|
2250
|
+
apolloClientAiApps({
|
|
2251
|
+
targets: ["mcp"],
|
|
2252
|
+
appsOutDir: "dist/apps",
|
|
2253
|
+
schema,
|
|
2254
|
+
}),
|
|
2255
|
+
],
|
|
2256
|
+
});
|
|
2257
|
+
await server.listen();
|
|
2258
|
+
|
|
2259
|
+
const content = fs.readFileSync(
|
|
2260
|
+
".apollo-client-ai-apps/types/register.d.ts",
|
|
2261
|
+
"utf-8"
|
|
2262
|
+
);
|
|
2263
|
+
expect(content).toMatchInlineSnapshot(`
|
|
2264
|
+
"// This file is auto-generated by @apollo/client-ai-apps. Do not edit manually.
|
|
2265
|
+
import "@apollo/client-ai-apps";
|
|
2266
|
+
|
|
2267
|
+
import type { CreateTodoMutationVariables } from "./operation-types.js";
|
|
2268
|
+
|
|
2269
|
+
declare module "@apollo/client-ai-apps" {
|
|
2270
|
+
interface Register {
|
|
2271
|
+
toolName: "CreateTodo" | "AddTask";
|
|
2272
|
+
toolInputs: {
|
|
2273
|
+
CreateTodo: CreateTodoMutationVariables;
|
|
2274
|
+
AddTask: CreateTodoMutationVariables;
|
|
2275
|
+
};
|
|
2276
|
+
}
|
|
2277
|
+
}"
|
|
2278
|
+
`);
|
|
2279
|
+
});
|
|
2280
|
+
|
|
2281
|
+
test("operation-types.d.ts is not rewritten when operation content has not changed", async () => {
|
|
2282
|
+
vol.fromJSON({
|
|
2283
|
+
"package.json": mockPackageJson(),
|
|
2284
|
+
"src/my-component.tsx": declareOperation(gql`
|
|
2285
|
+
mutation CreateTodo($title: String!)
|
|
2286
|
+
@tool(name: "CreateTodo", description: "Creates a todo") {
|
|
2287
|
+
createTodo(title: $title) {
|
|
2288
|
+
id
|
|
2289
|
+
}
|
|
2290
|
+
}
|
|
2291
|
+
`),
|
|
2292
|
+
});
|
|
2293
|
+
|
|
2294
|
+
await using server = await setupServer({
|
|
2295
|
+
plugins: [
|
|
2296
|
+
apolloClientAiApps({
|
|
2297
|
+
targets: ["mcp"],
|
|
2298
|
+
appsOutDir: "dist/apps",
|
|
2299
|
+
schema,
|
|
2300
|
+
}),
|
|
2301
|
+
],
|
|
2302
|
+
});
|
|
2303
|
+
await server.listen();
|
|
2304
|
+
|
|
2305
|
+
const content = fs.readFileSync(
|
|
2306
|
+
".apollo-client-ai-apps/types/operation-types.d.ts",
|
|
2307
|
+
"utf-8"
|
|
2308
|
+
);
|
|
2309
|
+
const mtime = fs.statSync(
|
|
2310
|
+
".apollo-client-ai-apps/types/operation-types.d.ts"
|
|
2311
|
+
).mtimeMs;
|
|
2312
|
+
|
|
2313
|
+
// Trigger another manifest generation (simulating a file change to an unrelated file)
|
|
2314
|
+
server.watcher.emit("change", "package.json");
|
|
2315
|
+
await wait(100);
|
|
2316
|
+
|
|
2317
|
+
expect(
|
|
2318
|
+
fs.readFileSync(
|
|
2319
|
+
".apollo-client-ai-apps/types/operation-types.d.ts",
|
|
2320
|
+
"utf-8"
|
|
2321
|
+
)
|
|
2322
|
+
).toBe(content);
|
|
2323
|
+
|
|
2324
|
+
expect(
|
|
2325
|
+
fs.statSync(".apollo-client-ai-apps/types/operation-types.d.ts").mtimeMs
|
|
2326
|
+
).toBe(mtime);
|
|
2327
|
+
});
|
|
2328
|
+
|
|
2329
|
+
test("does not generate operation-types.d.ts when schema is not provided", async () => {
|
|
2330
|
+
vol.fromJSON({
|
|
2331
|
+
"package.json": mockPackageJson(),
|
|
2332
|
+
"src/my-component.tsx": declareOperation(gql`
|
|
2333
|
+
mutation CreateTodo($title: String!)
|
|
2334
|
+
@tool(name: "CreateTodo", description: "Creates a todo") {
|
|
2335
|
+
createTodo(title: $title) {
|
|
2336
|
+
id
|
|
2337
|
+
}
|
|
2338
|
+
}
|
|
2339
|
+
`),
|
|
2340
|
+
});
|
|
2341
|
+
|
|
2342
|
+
await using server = await setupServer({
|
|
2343
|
+
plugins: [
|
|
2344
|
+
apolloClientAiApps({ targets: ["mcp"], appsOutDir: "dist/apps" }),
|
|
2345
|
+
],
|
|
2346
|
+
});
|
|
2347
|
+
await server.listen();
|
|
2348
|
+
|
|
2349
|
+
expect(
|
|
2350
|
+
fs.existsSync(".apollo-client-ai-apps/types/operation-types.d.ts")
|
|
2351
|
+
).toBe(false);
|
|
2352
|
+
});
|
|
2353
|
+
|
|
2354
|
+
test("generates register.d.ts with error message when schema build fails in dev mode", async () => {
|
|
2355
|
+
using _ = spyOnConsole("error");
|
|
2356
|
+
|
|
2357
|
+
vol.fromJSON({
|
|
2358
|
+
"package.json": mockPackageJson(),
|
|
2359
|
+
"src/my-component.tsx": declareOperation(gql`
|
|
2360
|
+
query GetNonExistent
|
|
2361
|
+
@tool(name: "GetNonExistent", description: "A tool") {
|
|
2362
|
+
nonExistentField
|
|
2363
|
+
}
|
|
2364
|
+
`),
|
|
2365
|
+
});
|
|
2366
|
+
|
|
2367
|
+
await using server = await setupServer({
|
|
2368
|
+
plugins: [
|
|
2369
|
+
apolloClientAiApps({
|
|
2370
|
+
targets: ["mcp"],
|
|
2371
|
+
appsOutDir: "dist/apps",
|
|
2372
|
+
schema,
|
|
2373
|
+
}),
|
|
2374
|
+
],
|
|
2375
|
+
});
|
|
2376
|
+
await server.listen();
|
|
2377
|
+
|
|
2378
|
+
expect(console.error).toHaveBeenCalledOnce();
|
|
2379
|
+
|
|
2380
|
+
const content = fs.readFileSync(
|
|
2381
|
+
".apollo-client-ai-apps/types/register.d.ts",
|
|
2382
|
+
"utf-8"
|
|
2383
|
+
);
|
|
2384
|
+
expect(content).toMatchInlineSnapshot(`
|
|
2385
|
+
"// This file is auto-generated by @apollo/client-ai-apps. Do not edit manually.
|
|
2386
|
+
import "@apollo/client-ai-apps";
|
|
2387
|
+
|
|
2388
|
+
declare module "@apollo/client-ai-apps" {
|
|
2389
|
+
interface Register {
|
|
2390
|
+
toolName: "[@apollo/client-ai-apps/vite]: There was an error building generated types. See the vite build output for more details.\\n\\nGraphQL Document Validation failed with 1 errors;\\n Error 0: Cannot query field \\"nonExistentField\\" on type \\"Query\\".\\n at 116185304.graphql:2:3";
|
|
2391
|
+
toolInputs: {
|
|
2392
|
+
"[@apollo/client-ai-apps/vite]: There was an error building generated types. See the vite build output for more details.\\n\\nGraphQL Document Validation failed with 1 errors;\\n Error 0: Cannot query field \\"nonExistentField\\" on type \\"Query\\".\\n at 116185304.graphql:2:3": "[@apollo/client-ai-apps/vite]: There was an error building generated types. See the vite build output for more details.\\n\\nGraphQL Document Validation failed with 1 errors;\\n Error 0: Cannot query field \\"nonExistentField\\" on type \\"Query\\".\\n at 116185304.graphql:2:3";
|
|
2393
|
+
};
|
|
2394
|
+
}
|
|
2395
|
+
}"
|
|
2396
|
+
`);
|
|
2397
|
+
});
|
|
2398
|
+
|
|
2399
|
+
test("generates register.d.ts without toolInputs when schema is not provided", async () => {
|
|
2400
|
+
vol.fromJSON({
|
|
2401
|
+
"package.json": mockPackageJson(),
|
|
2402
|
+
"src/my-component.tsx": declareOperation(gql`
|
|
2403
|
+
mutation CreateTodo($title: String!)
|
|
2404
|
+
@tool(name: "CreateTodo", description: "Creates a todo") {
|
|
2405
|
+
createTodo(title: $title) {
|
|
2406
|
+
id
|
|
2407
|
+
}
|
|
2408
|
+
}
|
|
2409
|
+
`),
|
|
2410
|
+
});
|
|
2411
|
+
|
|
2412
|
+
await using server = await setupServer({
|
|
2413
|
+
plugins: [
|
|
2414
|
+
apolloClientAiApps({ targets: ["mcp"], appsOutDir: "dist/apps" }),
|
|
2415
|
+
],
|
|
2416
|
+
});
|
|
2417
|
+
await server.listen();
|
|
2418
|
+
|
|
2419
|
+
const content = fs.readFileSync(
|
|
2420
|
+
".apollo-client-ai-apps/types/register.d.ts",
|
|
2421
|
+
"utf-8"
|
|
2422
|
+
);
|
|
2423
|
+
expect(content).toMatchInlineSnapshot(`
|
|
2424
|
+
"// This file is auto-generated by @apollo/client-ai-apps. Do not edit manually.
|
|
2425
|
+
import "@apollo/client-ai-apps";
|
|
2426
|
+
|
|
2427
|
+
declare module "@apollo/client-ai-apps" {
|
|
2428
|
+
interface Register {
|
|
2429
|
+
toolName: "CreateTodo";
|
|
2430
|
+
}
|
|
2431
|
+
}"
|
|
2432
|
+
`);
|
|
2433
|
+
});
|
|
2434
|
+
});
|
|
2435
|
+
|
|
1488
2436
|
describe("html transforms", () => {
|
|
1489
2437
|
test("replaces root relative scripts with full url when origin is provided", async () => {
|
|
1490
2438
|
vol.fromJSON({
|
|
@@ -1495,7 +2443,9 @@ describe("html transforms", () => {
|
|
|
1495
2443
|
server: {
|
|
1496
2444
|
origin: "http://localhost:3000",
|
|
1497
2445
|
},
|
|
1498
|
-
plugins: [
|
|
2446
|
+
plugins: [
|
|
2447
|
+
apolloClientAiApps({ targets: ["mcp"], appsOutDir: "dist/apps" }),
|
|
2448
|
+
],
|
|
1499
2449
|
});
|
|
1500
2450
|
|
|
1501
2451
|
const html = `<html><head><script type="module" src="/@vite/client"></script></head><body><script module src="/assets/main.ts?t=12345"></script></body></html>`;
|
|
@@ -1519,7 +2469,9 @@ describe("html transforms", () => {
|
|
|
1519
2469
|
server: {
|
|
1520
2470
|
port: 3000,
|
|
1521
2471
|
},
|
|
1522
|
-
plugins: [
|
|
2472
|
+
plugins: [
|
|
2473
|
+
apolloClientAiApps({ targets: ["mcp"], appsOutDir: "dist/apps" }),
|
|
2474
|
+
],
|
|
1523
2475
|
});
|
|
1524
2476
|
|
|
1525
2477
|
await server.listen();
|
|
@@ -1547,7 +2499,9 @@ window.$RefreshSig$ = () => (type) => type;</script></head></html>`;
|
|
|
1547
2499
|
server: {
|
|
1548
2500
|
origin: "http://localhost:3000",
|
|
1549
2501
|
},
|
|
1550
|
-
plugins: [
|
|
2502
|
+
plugins: [
|
|
2503
|
+
apolloClientAiApps({ targets: ["mcp"], appsOutDir: "dist/apps" }),
|
|
2504
|
+
],
|
|
1551
2505
|
});
|
|
1552
2506
|
|
|
1553
2507
|
const html = `<html><head> <script type="module">import { injectIntoGlobalHook } from "/@react-refresh";
|
|
@@ -1573,7 +2527,9 @@ window.$RefreshSig$ = () => (type) => type;</script></head></html>`;
|
|
|
1573
2527
|
server: {
|
|
1574
2528
|
port: 3000,
|
|
1575
2529
|
},
|
|
1576
|
-
plugins: [
|
|
2530
|
+
plugins: [
|
|
2531
|
+
apolloClientAiApps({ targets: ["mcp"], appsOutDir: "dist/apps" }),
|
|
2532
|
+
],
|
|
1577
2533
|
});
|
|
1578
2534
|
|
|
1579
2535
|
await server.listen();
|
|
@@ -1604,7 +2560,7 @@ window.$RefreshSig$ = () => (type) => type;</script></head></html>`;
|
|
|
1604
2560
|
origin: "http://localhost:3000",
|
|
1605
2561
|
},
|
|
1606
2562
|
plugins: [
|
|
1607
|
-
apolloClientAiApps({ targets: ["mcp"] }),
|
|
2563
|
+
apolloClientAiApps({ targets: ["mcp"], appsOutDir: "dist/apps" }),
|
|
1608
2564
|
{
|
|
1609
2565
|
name: "capture-html",
|
|
1610
2566
|
transformIndexHtml: {
|
|
@@ -1643,7 +2599,7 @@ function declareFragment(fragment: DocumentNode) {
|
|
|
1643
2599
|
}
|
|
1644
2600
|
|
|
1645
2601
|
function mockPackageJson(config?: Record<string, unknown>) {
|
|
1646
|
-
return JSON.stringify({ version: "1.0.0", ...config });
|
|
2602
|
+
return JSON.stringify({ version: "1.0.0", name: "my-app", ...config });
|
|
1647
2603
|
}
|
|
1648
2604
|
|
|
1649
2605
|
function readManifestFile(
|