@apollo/client-ai-apps 0.4.0 → 0.5.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/.github/CODEOWNERS +1 -0
- package/.github/workflows/pr.yaml +23 -1
- package/.github/workflows/release.yaml +1 -5
- package/.github/workflows/verify-changeset.yml +112 -21
- package/CHANGELOG.md +353 -0
- package/__mocks__/fs/promises.cjs +3 -0
- package/__mocks__/fs.cjs +3 -0
- package/dist/config/defineConfig.d.ts +26 -0
- package/dist/config/defineConfig.d.ts.map +1 -0
- package/dist/config/defineConfig.js +7 -0
- package/dist/config/defineConfig.js.map +1 -0
- package/dist/config/index.d.ts +3 -0
- package/dist/config/index.d.ts.map +1 -0
- package/dist/config/index.js +2 -0
- package/dist/config/index.js.map +1 -0
- package/dist/config/schema.d.ts +29 -0
- package/dist/config/schema.d.ts.map +1 -0
- package/dist/config/schema.js +51 -0
- package/dist/config/schema.js.map +1 -0
- package/dist/config/types.d.ts +7 -0
- package/dist/config/types.d.ts.map +1 -0
- package/dist/config/types.js +2 -0
- package/dist/config/types.js.map +1 -0
- package/dist/core/ApolloClient.d.ts +4 -3
- package/dist/core/ApolloClient.d.ts.map +1 -1
- package/dist/core/ApolloClient.js +6 -59
- package/dist/core/ApolloClient.js.map +1 -1
- package/dist/core/Platform.d.ts +8 -0
- package/dist/core/Platform.d.ts.map +1 -0
- package/dist/core/Platform.js +20 -0
- package/dist/core/Platform.js.map +1 -0
- package/dist/core/types.d.ts +13 -0
- package/dist/core/types.d.ts.map +1 -0
- package/dist/core/types.js +2 -0
- package/dist/core/types.js.map +1 -0
- package/dist/index.d.ts +1 -14
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +1 -13
- package/dist/index.js.map +1 -1
- package/dist/index.mcp.d.ts +5 -0
- package/dist/index.mcp.d.ts.map +1 -0
- package/dist/index.mcp.js +5 -0
- package/dist/index.mcp.js.map +1 -0
- package/dist/index.openai.d.ts +5 -0
- package/dist/index.openai.d.ts.map +1 -0
- package/dist/index.openai.js +5 -0
- package/dist/index.openai.js.map +1 -0
- package/dist/link/ToolCallLink.d.ts +3 -3
- package/dist/link/ToolCallLink.d.ts.map +1 -1
- package/dist/link/ToolCallLink.js +5 -14
- package/dist/link/ToolCallLink.js.map +1 -1
- package/dist/mcp/core/ApolloClient.d.ts +21 -0
- package/dist/mcp/core/ApolloClient.d.ts.map +1 -0
- package/dist/mcp/core/ApolloClient.js +65 -0
- package/dist/mcp/core/ApolloClient.js.map +1 -0
- package/dist/mcp/core/McpAppManager.d.ts +34 -0
- package/dist/mcp/core/McpAppManager.d.ts.map +1 -0
- package/dist/mcp/core/McpAppManager.js +63 -0
- package/dist/mcp/core/McpAppManager.js.map +1 -0
- package/dist/mcp/core/Platform.d.ts +8 -0
- package/dist/mcp/core/Platform.d.ts.map +1 -0
- package/dist/mcp/core/Platform.js +8 -0
- package/dist/mcp/core/Platform.js.map +1 -0
- package/dist/mcp/index.d.ts +3 -0
- package/dist/mcp/index.d.ts.map +1 -0
- package/dist/mcp/index.js +3 -0
- package/dist/mcp/index.js.map +1 -0
- package/dist/mcp/link/ToolCallLink.d.ts +28 -0
- package/dist/mcp/link/ToolCallLink.d.ts.map +1 -0
- package/dist/mcp/link/ToolCallLink.js +35 -0
- package/dist/mcp/link/ToolCallLink.js.map +1 -0
- package/dist/mcp/react/hooks/useApolloClient.d.ts +3 -0
- package/dist/mcp/react/hooks/useApolloClient.d.ts.map +1 -0
- package/dist/mcp/react/hooks/useApolloClient.js +9 -0
- package/dist/mcp/react/hooks/useApolloClient.js.map +1 -0
- package/dist/mcp/react/hooks/useApp.d.ts +2 -0
- package/dist/mcp/react/hooks/useApp.d.ts.map +1 -0
- package/dist/mcp/react/hooks/useApp.js +5 -0
- package/dist/mcp/react/hooks/useApp.js.map +1 -0
- package/dist/mcp/react/hooks/useToolInput.d.ts +2 -0
- package/dist/mcp/react/hooks/useToolInput.d.ts.map +1 -0
- package/dist/mcp/react/hooks/useToolInput.js +5 -0
- package/dist/mcp/react/hooks/useToolInput.js.map +1 -0
- package/dist/mcp/react/hooks/useToolMetadata.d.ts +8 -0
- package/dist/mcp/react/hooks/useToolMetadata.d.ts.map +1 -0
- package/dist/mcp/react/hooks/useToolMetadata.js +5 -0
- package/dist/mcp/react/hooks/useToolMetadata.js.map +1 -0
- package/dist/mcp/react/hooks/useToolName.d.ts +2 -0
- package/dist/mcp/react/hooks/useToolName.d.ts.map +1 -0
- package/dist/mcp/react/hooks/useToolName.js +5 -0
- package/dist/mcp/react/hooks/useToolName.js.map +1 -0
- package/dist/mcp/react/index.d.ts +5 -0
- package/dist/mcp/react/index.d.ts.map +1 -0
- package/dist/mcp/react/index.js +5 -0
- package/dist/mcp/react/index.js.map +1 -0
- package/dist/openai/core/ApolloClient.d.ts +21 -0
- package/dist/openai/core/ApolloClient.d.ts.map +1 -0
- package/dist/openai/core/ApolloClient.js +66 -0
- package/dist/openai/core/ApolloClient.js.map +1 -0
- package/dist/openai/core/McpAppManager.d.ts +28 -0
- package/dist/openai/core/McpAppManager.d.ts.map +1 -0
- package/dist/openai/core/McpAppManager.js +73 -0
- package/dist/openai/core/McpAppManager.js.map +1 -0
- package/dist/openai/core/Platform.d.ts +8 -0
- package/dist/openai/core/Platform.d.ts.map +1 -0
- package/dist/openai/core/Platform.js +8 -0
- package/dist/openai/core/Platform.js.map +1 -0
- package/dist/openai/globals.d.ts +12 -0
- package/dist/openai/globals.d.ts.map +1 -0
- package/dist/openai/globals.js +2 -0
- package/dist/openai/globals.js.map +1 -0
- package/dist/openai/index.d.ts +3 -0
- package/dist/openai/index.d.ts.map +1 -0
- package/dist/openai/index.js +3 -0
- package/dist/openai/index.js.map +1 -0
- package/dist/openai/link/ToolCallLink.d.ts +28 -0
- package/dist/openai/link/ToolCallLink.d.ts.map +1 -0
- package/dist/openai/link/ToolCallLink.js +35 -0
- package/dist/openai/link/ToolCallLink.js.map +1 -0
- package/dist/openai/react/hooks/useApolloClient.d.ts +3 -0
- package/dist/openai/react/hooks/useApolloClient.d.ts.map +1 -0
- package/dist/openai/react/hooks/useApolloClient.js +9 -0
- package/dist/openai/react/hooks/useApolloClient.js.map +1 -0
- package/dist/openai/react/hooks/useApp.d.ts +2 -0
- package/dist/openai/react/hooks/useApp.d.ts.map +1 -0
- package/dist/openai/react/hooks/useApp.js +5 -0
- package/dist/openai/react/hooks/useApp.js.map +1 -0
- package/dist/openai/react/hooks/useOpenAiGlobal.d.ts +3 -0
- package/dist/openai/react/hooks/useOpenAiGlobal.d.ts.map +1 -0
- package/dist/{react → openai/react}/hooks/useOpenAiGlobal.js +1 -1
- package/dist/openai/react/hooks/useOpenAiGlobal.js.map +1 -0
- package/dist/openai/react/hooks/useToolInput.d.ts +2 -0
- package/dist/openai/react/hooks/useToolInput.d.ts.map +1 -0
- package/dist/openai/react/hooks/useToolInput.js +5 -0
- package/dist/openai/react/hooks/useToolInput.js.map +1 -0
- package/dist/openai/react/hooks/useToolMetadata.d.ts +2 -0
- package/dist/openai/react/hooks/useToolMetadata.d.ts.map +1 -0
- package/dist/openai/react/hooks/useToolMetadata.js +5 -0
- package/dist/openai/react/hooks/useToolMetadata.js.map +1 -0
- package/dist/openai/react/hooks/useToolName.d.ts.map +1 -0
- package/dist/openai/react/hooks/useToolName.js +5 -0
- package/dist/openai/react/hooks/useToolName.js.map +1 -0
- package/dist/{react → openai/react}/hooks/useWidgetState.d.ts +1 -1
- package/dist/openai/react/hooks/useWidgetState.d.ts.map +1 -0
- package/dist/openai/react/hooks/useWidgetState.js.map +1 -0
- package/dist/openai/react/index.d.ts +6 -0
- package/dist/openai/react/index.d.ts.map +1 -0
- package/dist/openai/react/index.js +6 -0
- package/dist/openai/react/index.js.map +1 -0
- package/dist/{types/openai.d.ts → openai/types.d.ts} +7 -11
- package/dist/openai/types.d.ts.map +1 -0
- package/dist/{types/openai.js → openai/types.js} +1 -1
- package/dist/openai/types.js.map +1 -0
- package/dist/react/ApolloProvider.d.ts +6 -2
- package/dist/react/ApolloProvider.d.ts.map +1 -1
- package/dist/react/ApolloProvider.js +12 -27
- package/dist/react/ApolloProvider.js.map +1 -1
- package/dist/react/index.d.ts +6 -0
- package/dist/react/index.d.ts.map +1 -0
- package/dist/react/index.js +9 -0
- package/dist/react/index.js.map +1 -0
- package/dist/react/index.mcp.d.ts +3 -0
- package/dist/react/index.mcp.d.ts.map +1 -0
- package/dist/react/index.mcp.js +3 -0
- package/dist/react/index.mcp.js.map +1 -0
- package/dist/react/index.openai.d.ts +3 -0
- package/dist/react/index.openai.d.ts.map +1 -0
- package/dist/react/index.openai.js +3 -0
- package/dist/react/index.openai.js.map +1 -0
- package/dist/react/missingHook.d.ts +2 -0
- package/dist/react/missingHook.d.ts.map +1 -0
- package/dist/react/missingHook.js +6 -0
- package/dist/react/missingHook.js.map +1 -0
- package/dist/tsconfig/core/tsconfig.json +5 -0
- package/dist/tsconfig/mcp/tsconfig.json +6 -0
- package/dist/tsconfig/openai/tsconfig.json +7 -0
- package/dist/types/application-manifest.d.ts +5 -1
- package/dist/types/application-manifest.d.ts.map +1 -1
- package/dist/types/application-manifest.js.map +1 -1
- package/dist/utilities/cacheAsync.d.ts +4 -0
- package/dist/utilities/cacheAsync.d.ts.map +1 -0
- package/dist/utilities/cacheAsync.js +19 -0
- package/dist/utilities/cacheAsync.js.map +1 -0
- package/dist/utilities/constants.d.ts +6 -0
- package/dist/utilities/constants.d.ts.map +1 -0
- package/dist/utilities/constants.js +6 -0
- package/dist/utilities/constants.js.map +1 -0
- package/dist/utilities/emptyModule.d.ts +2 -0
- package/dist/utilities/emptyModule.d.ts.map +1 -0
- package/dist/utilities/emptyModule.js +2 -0
- package/dist/utilities/emptyModule.js.map +1 -0
- package/dist/utilities/getVariablesForOperationFromToolInput.d.ts +5 -0
- package/dist/utilities/getVariablesForOperationFromToolInput.d.ts.map +1 -0
- package/dist/utilities/getVariablesForOperationFromToolInput.js +18 -0
- package/dist/utilities/getVariablesForOperationFromToolInput.js.map +1 -0
- package/dist/utilities/index.d.ts +6 -0
- package/dist/utilities/index.d.ts.map +1 -0
- package/dist/utilities/index.js +6 -0
- package/dist/utilities/index.js.map +1 -0
- package/dist/utilities/invariant.d.ts +2 -0
- package/dist/utilities/invariant.d.ts.map +1 -0
- package/dist/utilities/invariant.js +6 -0
- package/dist/utilities/invariant.js.map +1 -0
- package/dist/utilities/promiseWithResolvers.d.ts +7 -0
- package/dist/utilities/promiseWithResolvers.d.ts.map +1 -0
- package/dist/utilities/promiseWithResolvers.js +16 -0
- package/dist/utilities/promiseWithResolvers.js.map +1 -0
- package/dist/vite/__tests__/utilities/build.d.ts +41 -0
- package/dist/vite/__tests__/utilities/build.d.ts.map +1 -0
- package/dist/vite/__tests__/utilities/build.js +63 -0
- package/dist/vite/__tests__/utilities/build.js.map +1 -0
- package/dist/vite/apolloClientAiApps.d.ts +14 -0
- package/dist/vite/apolloClientAiApps.d.ts.map +1 -0
- package/dist/vite/apolloClientAiApps.js +350 -0
- package/dist/vite/apolloClientAiApps.js.map +1 -0
- package/dist/vite/index.d.ts +1 -2
- package/dist/vite/index.d.ts.map +1 -1
- package/dist/vite/index.js +1 -2
- package/dist/vite/index.js.map +1 -1
- package/dist/vite/utilities/config.d.ts +2 -0
- package/dist/vite/utilities/config.d.ts.map +1 -0
- package/dist/vite/utilities/config.js +11 -0
- package/dist/vite/utilities/config.js.map +1 -0
- package/dist/vite/utilities/graphql.d.ts +20 -0
- package/dist/vite/utilities/graphql.d.ts.map +1 -0
- package/dist/vite/utilities/graphql.js +42 -0
- package/dist/vite/utilities/graphql.js.map +1 -0
- package/knope.toml +1 -1
- package/package.json +105 -9
- package/src/config/defineConfig.ts +8 -0
- package/src/config/index.ts +2 -0
- package/src/config/schema.ts +73 -0
- package/src/config/types.ts +7 -0
- package/src/core/ApolloClient.ts +7 -95
- package/src/core/Platform.ts +27 -0
- package/src/core/types.ts +14 -0
- package/src/index.mcp.ts +5 -0
- package/src/index.openai.ts +5 -0
- package/src/index.ts +2 -27
- package/src/link/ToolCallLink.ts +6 -22
- package/src/mcp/core/ApolloClient.ts +102 -0
- package/src/mcp/core/McpAppManager.ts +98 -0
- package/src/mcp/core/Platform.ts +11 -0
- package/src/mcp/core/__tests__/ApolloClient.test.ts +464 -0
- package/src/mcp/index.ts +2 -0
- package/src/mcp/link/ToolCallLink.ts +40 -0
- package/src/mcp/link/__tests__/ToolCallLink.test.ts +53 -0
- package/src/mcp/react/hooks/__tests__/useApp.test.tsx +46 -0
- package/src/mcp/react/hooks/__tests__/useToolInput.test.tsx +50 -0
- package/src/mcp/react/hooks/__tests__/useToolMetadata.test.tsx +53 -0
- package/src/mcp/react/hooks/__tests__/useToolName.test.tsx +50 -0
- package/src/mcp/react/hooks/useApolloClient.ts +14 -0
- package/src/mcp/react/hooks/useApp.ts +5 -0
- package/src/mcp/react/hooks/useToolInput.ts +5 -0
- package/src/mcp/react/hooks/useToolMetadata.ts +5 -0
- package/src/mcp/react/hooks/useToolName.ts +5 -0
- package/src/mcp/react/index.ts +4 -0
- package/src/openai/core/ApolloClient.ts +100 -0
- package/src/openai/core/McpAppManager.ts +110 -0
- package/src/openai/core/Platform.ts +11 -0
- package/src/openai/core/__tests__/ApolloClient.test.ts +537 -0
- package/src/openai/globals.ts +14 -0
- package/src/openai/index.ts +2 -0
- package/src/openai/link/ToolCallLink.ts +40 -0
- package/src/{react → openai/react}/hooks/__tests__/useOpenAiGlobal.test.ts +1 -1
- package/src/openai/react/hooks/__tests__/useToolInput.test.tsx +85 -0
- package/src/openai/react/hooks/__tests__/useToolMetadata.test.tsx +86 -0
- package/src/openai/react/hooks/__tests__/useToolName.test.tsx +50 -0
- package/src/{react → openai/react}/hooks/__tests__/useWidgetState.test.tsx +1 -1
- package/src/openai/react/hooks/useApolloClient.ts +14 -0
- package/src/openai/react/hooks/useApp.ts +5 -0
- package/src/{react → openai/react}/hooks/useOpenAiGlobal.ts +2 -2
- package/src/openai/react/hooks/useToolInput.ts +5 -0
- package/src/openai/react/hooks/useToolMetadata.ts +5 -0
- package/src/openai/react/hooks/useToolName.ts +5 -0
- package/src/{react → openai/react}/hooks/useWidgetState.ts +1 -1
- package/src/openai/react/index.ts +5 -0
- package/src/{types/openai.ts → openai/types.ts} +9 -13
- package/src/react/ApolloProvider.tsx +23 -31
- package/src/react/__tests__/ApolloProvider/mcp.test.tsx +74 -0
- package/src/react/__tests__/ApolloProvider/openai.test.tsx +146 -0
- package/src/react/index.mcp.ts +7 -0
- package/src/react/index.openai.ts +7 -0
- package/src/react/index.ts +19 -0
- package/src/react/missingHook.ts +9 -0
- package/src/testing/internal/index.ts +8 -0
- package/src/testing/internal/matchers/index.ts +2 -0
- package/src/testing/internal/matchers/toComplete.ts +46 -0
- package/src/testing/internal/matchers/toEmitValue.ts +71 -0
- package/src/testing/internal/matchers/types.ts +3 -0
- package/src/testing/internal/mcp/graphqlToolResult.ts +31 -0
- package/src/testing/internal/mcp/minimalHostContextWithToolName.ts +9 -0
- package/src/testing/internal/mcp/mockMcpHost.ts +188 -0
- package/src/testing/internal/openai/dispatchStateChange.ts +1 -1
- package/src/testing/internal/openai/stubOpenAiGlobals.ts +22 -9
- package/src/testing/internal/react/renderAsync.ts +7 -0
- package/src/testing/internal/utilities/ObservableStream.ts +172 -0
- package/src/testing/internal/utilities/getSerializableProperties.ts +55 -0
- package/src/testing/internal/utilities/mockApplicationManifest.ts +39 -0
- package/src/testing/internal/utilities/spyOnConsole.ts +29 -0
- package/src/testing/internal/utilities/wait.ts +3 -0
- package/src/tsconfig/core/tsconfig.json +5 -0
- package/src/tsconfig/mcp/tsconfig.json +6 -0
- package/src/tsconfig/openai/tsconfig.json +7 -0
- package/src/types/application-manifest.ts +7 -1
- package/src/utilities/__tests__/cacheAsync.test.ts +64 -0
- package/src/utilities/cacheAsync.ts +25 -0
- package/src/utilities/constants.ts +5 -0
- package/src/utilities/emptyModule.ts +1 -0
- package/src/utilities/getVariablesForOperationFromToolInput.ts +27 -0
- package/src/utilities/index.ts +5 -0
- package/src/utilities/invariant.ts +5 -0
- package/src/utilities/promiseWithResolvers.ts +18 -0
- package/src/vite/__tests__/apolloClientAiApps.test.ts +1521 -0
- package/src/vite/__tests__/utilities/build.ts +72 -0
- package/src/vite/apolloClientAiApps.ts +515 -0
- package/src/vite/index.ts +1 -2
- package/src/vite/utilities/config.ts +13 -0
- package/src/vite/utilities/graphql.ts +123 -0
- package/tsconfig.base.json +2 -2
- package/tsconfig.vite.build.json +4 -1
- package/tsconfig.vite.json +8 -2
- package/vitest-setup.ts +28 -0
- package/dist/react/context/ToolUseContext.d.ts +0 -16
- package/dist/react/context/ToolUseContext.d.ts.map +0 -1
- package/dist/react/context/ToolUseContext.js +0 -11
- package/dist/react/context/ToolUseContext.js.map +0 -1
- package/dist/react/hooks/useCallTool.d.ts +0 -4
- package/dist/react/hooks/useCallTool.d.ts.map +0 -1
- package/dist/react/hooks/useCallTool.js +0 -5
- package/dist/react/hooks/useCallTool.js.map +0 -1
- package/dist/react/hooks/useOpenAiGlobal.d.ts +0 -3
- package/dist/react/hooks/useOpenAiGlobal.d.ts.map +0 -1
- package/dist/react/hooks/useOpenAiGlobal.js.map +0 -1
- package/dist/react/hooks/useOpenExternal.d.ts +0 -4
- package/dist/react/hooks/useOpenExternal.d.ts.map +0 -1
- package/dist/react/hooks/useOpenExternal.js +0 -5
- package/dist/react/hooks/useOpenExternal.js.map +0 -1
- package/dist/react/hooks/useRequestDisplayMode.d.ts +0 -7
- package/dist/react/hooks/useRequestDisplayMode.d.ts.map +0 -1
- package/dist/react/hooks/useRequestDisplayMode.js +0 -6
- package/dist/react/hooks/useRequestDisplayMode.js.map +0 -1
- package/dist/react/hooks/useSendFollowUpMessage.d.ts +0 -2
- package/dist/react/hooks/useSendFollowUpMessage.d.ts.map +0 -1
- package/dist/react/hooks/useSendFollowUpMessage.js +0 -8
- package/dist/react/hooks/useSendFollowUpMessage.js.map +0 -1
- package/dist/react/hooks/useToolEffect.d.ts +0 -3
- package/dist/react/hooks/useToolEffect.d.ts.map +0 -1
- package/dist/react/hooks/useToolEffect.js +0 -28
- package/dist/react/hooks/useToolEffect.js.map +0 -1
- package/dist/react/hooks/useToolInput.d.ts +0 -2
- package/dist/react/hooks/useToolInput.d.ts.map +0 -1
- package/dist/react/hooks/useToolInput.js +0 -6
- package/dist/react/hooks/useToolInput.js.map +0 -1
- package/dist/react/hooks/useToolName.d.ts.map +0 -1
- package/dist/react/hooks/useToolName.js +0 -6
- package/dist/react/hooks/useToolName.js.map +0 -1
- package/dist/react/hooks/useToolOutput.d.ts +0 -2
- package/dist/react/hooks/useToolOutput.d.ts.map +0 -1
- package/dist/react/hooks/useToolOutput.js +0 -5
- package/dist/react/hooks/useToolOutput.js.map +0 -1
- package/dist/react/hooks/useToolResponseMetadata.d.ts +0 -2
- package/dist/react/hooks/useToolResponseMetadata.d.ts.map +0 -1
- package/dist/react/hooks/useToolResponseMetadata.js +0 -5
- package/dist/react/hooks/useToolResponseMetadata.js.map +0 -1
- package/dist/react/hooks/useWidgetState.d.ts.map +0 -1
- package/dist/react/hooks/useWidgetState.js.map +0 -1
- package/dist/types/openai.d.ts.map +0 -1
- package/dist/types/openai.js.map +0 -1
- package/dist/vite/absolute_asset_imports_plugin.d.ts +0 -5
- package/dist/vite/absolute_asset_imports_plugin.d.ts.map +0 -1
- package/dist/vite/absolute_asset_imports_plugin.js +0 -17
- package/dist/vite/absolute_asset_imports_plugin.js.map +0 -1
- package/dist/vite/application_manifest_plugin.d.ts +0 -10
- package/dist/vite/application_manifest_plugin.d.ts.map +0 -1
- package/dist/vite/application_manifest_plugin.js +0 -317
- package/dist/vite/application_manifest_plugin.js.map +0 -1
- package/src/core/__tests__/ApolloClient.test.ts +0 -646
- package/src/react/__tests__/ApolloProvider.test.tsx +0 -41
- package/src/react/context/ToolUseContext.tsx +0 -31
- package/src/react/hooks/__tests__/useCallTool.test.ts +0 -46
- package/src/react/hooks/__tests__/useOpenExternal.test.tsx +0 -24
- package/src/react/hooks/__tests__/useRequestDisplayMode.test.ts +0 -17
- package/src/react/hooks/__tests__/useSendFollowUpMessage.test.ts +0 -15
- package/src/react/hooks/__tests__/useToolEffect.test.tsx +0 -67
- package/src/react/hooks/__tests__/useToolInput.test.ts +0 -13
- package/src/react/hooks/__tests__/useToolName.test.ts +0 -13
- package/src/react/hooks/__tests__/useToolOutput.test.tsx +0 -49
- package/src/react/hooks/__tests__/useToolResponseMetadata.test.tsx +0 -49
- package/src/react/hooks/useCallTool.ts +0 -13
- package/src/react/hooks/useOpenExternal.ts +0 -11
- package/src/react/hooks/useRequestDisplayMode.ts +0 -7
- package/src/react/hooks/useSendFollowUpMessage.ts +0 -7
- package/src/react/hooks/useToolEffect.tsx +0 -37
- package/src/react/hooks/useToolInput.ts +0 -7
- package/src/react/hooks/useToolName.ts +0 -7
- package/src/react/hooks/useToolOutput.ts +0 -5
- package/src/react/hooks/useToolResponseMetadata.ts +0 -5
- package/src/vite/__tests__/absolute_asset_imports_plugin.test.ts +0 -102
- package/src/vite/__tests__/application_manifest_plugin.test.ts +0 -1199
- package/src/vite/absolute_asset_imports_plugin.ts +0 -22
- package/src/vite/application_manifest_plugin.ts +0 -539
- /package/dist/{react → openai/react}/hooks/useToolName.d.ts +0 -0
- /package/dist/{react → openai/react}/hooks/useWidgetState.js +0 -0
|
@@ -0,0 +1,71 @@
|
|
|
1
|
+
import { iterableEquality } from "@jest/expect-utils";
|
|
2
|
+
import { expect } from "vitest";
|
|
3
|
+
|
|
4
|
+
import type { TakeOptions } from "../utilities/ObservableStream.js";
|
|
5
|
+
import {
|
|
6
|
+
ObservableStream,
|
|
7
|
+
EventMismatchError,
|
|
8
|
+
} from "../utilities/ObservableStream.js";
|
|
9
|
+
|
|
10
|
+
import { getSerializableProperties } from "../utilities/getSerializableProperties.js";
|
|
11
|
+
|
|
12
|
+
expect.extend({
|
|
13
|
+
async toEmitValue(actual, expected, options: TakeOptions) {
|
|
14
|
+
const stream = actual as ObservableStream<any>;
|
|
15
|
+
const hint = this.utils.matcherHint("toEmitValue", "stream", "expected", {
|
|
16
|
+
isNot: this.isNot,
|
|
17
|
+
});
|
|
18
|
+
|
|
19
|
+
try {
|
|
20
|
+
const value = await stream.takeNext(options);
|
|
21
|
+
const serializableProperties = getSerializableProperties(value);
|
|
22
|
+
|
|
23
|
+
const pass = this.equals(
|
|
24
|
+
serializableProperties,
|
|
25
|
+
expected,
|
|
26
|
+
// https://github.com/jestjs/jest/blob/22029ba06b69716699254bb9397f2b3bc7b3cf3b/packages/expect/src/matchers.ts#L62-L67
|
|
27
|
+
[...this.customTesters, iterableEquality],
|
|
28
|
+
true
|
|
29
|
+
);
|
|
30
|
+
|
|
31
|
+
return {
|
|
32
|
+
pass,
|
|
33
|
+
message: () => {
|
|
34
|
+
if (pass) {
|
|
35
|
+
return (
|
|
36
|
+
hint +
|
|
37
|
+
"\n\nExpected stream not to emit a fetch result equal to expected but it did."
|
|
38
|
+
);
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
return (
|
|
42
|
+
hint +
|
|
43
|
+
"\n\n" +
|
|
44
|
+
this.utils.printDiffOrStringify(serializableProperties, expected)
|
|
45
|
+
);
|
|
46
|
+
},
|
|
47
|
+
};
|
|
48
|
+
} catch (error) {
|
|
49
|
+
if (
|
|
50
|
+
error instanceof Error &&
|
|
51
|
+
error.message === "Timeout waiting for next event"
|
|
52
|
+
) {
|
|
53
|
+
return {
|
|
54
|
+
pass: false,
|
|
55
|
+
message: () =>
|
|
56
|
+
hint + "\n\nExpected stream to emit a value but it did not.",
|
|
57
|
+
};
|
|
58
|
+
} else if (EventMismatchError.is(error)) {
|
|
59
|
+
return {
|
|
60
|
+
pass: false,
|
|
61
|
+
message: () =>
|
|
62
|
+
this.utils.matcherHint("toEmitNext", "stream", "expected") +
|
|
63
|
+
"\n\n" +
|
|
64
|
+
this.utils.printDiffOrStringify(error.actual, error.expected),
|
|
65
|
+
};
|
|
66
|
+
} else {
|
|
67
|
+
throw error;
|
|
68
|
+
}
|
|
69
|
+
}
|
|
70
|
+
},
|
|
71
|
+
});
|
|
@@ -1,7 +1,10 @@
|
|
|
1
1
|
import type { NextRenderOptions } from "@testing-library/react-render-stream";
|
|
2
|
+
import type { TakeOptions } from "../utilities/ObservableStream.js";
|
|
2
3
|
|
|
3
4
|
interface CustomMatchers<R = unknown> {
|
|
4
5
|
toRerender: (options?: NextRenderOptions) => Promise<R>;
|
|
6
|
+
toComplete: (options?: TakeOptions) => Promise<R>;
|
|
7
|
+
toEmitValue: (expected: unknown, options?: TakeOptions) => Promise<R>;
|
|
5
8
|
}
|
|
6
9
|
|
|
7
10
|
declare module "vitest" {
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
import type { McpUiToolResultNotification } from "@modelcontextprotocol/ext-apps";
|
|
2
|
+
import type { FormattedExecutionResult } from "graphql";
|
|
3
|
+
import type { ApolloMcpServerApps } from "../../../core/types";
|
|
4
|
+
|
|
5
|
+
export function graphqlToolResult(
|
|
6
|
+
options:
|
|
7
|
+
| FormattedExecutionResult
|
|
8
|
+
| {
|
|
9
|
+
prefetch?: Record<string, FormattedExecutionResult>;
|
|
10
|
+
result: FormattedExecutionResult;
|
|
11
|
+
}
|
|
12
|
+
): McpUiToolResultNotification["params"] {
|
|
13
|
+
let structuredContent: ApolloMcpServerApps.StructuredContent;
|
|
14
|
+
|
|
15
|
+
if ("data" in options) {
|
|
16
|
+
structuredContent = { result: options };
|
|
17
|
+
} else if ("result" in options) {
|
|
18
|
+
structuredContent = { result: options.result };
|
|
19
|
+
|
|
20
|
+
if (options.prefetch) {
|
|
21
|
+
structuredContent.prefetch = options.prefetch;
|
|
22
|
+
}
|
|
23
|
+
} else {
|
|
24
|
+
throw new Error("Mock tool result could not be parsed");
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
return {
|
|
28
|
+
content: [{ type: "text", text: JSON.stringify(structuredContent) }],
|
|
29
|
+
structuredContent,
|
|
30
|
+
};
|
|
31
|
+
}
|
|
@@ -0,0 +1,188 @@
|
|
|
1
|
+
import {
|
|
2
|
+
LATEST_PROTOCOL_VERSION,
|
|
3
|
+
type McpUiHostCapabilities,
|
|
4
|
+
type McpUiHostContext,
|
|
5
|
+
type McpUiToolResultNotification,
|
|
6
|
+
type McpUiToolInputNotification,
|
|
7
|
+
} from "@modelcontextprotocol/ext-apps";
|
|
8
|
+
import type {
|
|
9
|
+
CallToolRequest,
|
|
10
|
+
CallToolResult,
|
|
11
|
+
Implementation,
|
|
12
|
+
} from "@modelcontextprotocol/sdk/types.js";
|
|
13
|
+
import { invariant, promiseWithResolvers } from "../../../utilities/index.js";
|
|
14
|
+
|
|
15
|
+
export interface MockMcpHost extends Disposable {
|
|
16
|
+
sendToolResult(params: McpUiToolResultNotification["params"]): Promise<void>;
|
|
17
|
+
sendToolInput(params: McpUiToolInputNotification["params"]): Promise<void>;
|
|
18
|
+
mockToolCall(
|
|
19
|
+
name: string,
|
|
20
|
+
handler: (params: CallToolRequest["params"]) => CallToolResult
|
|
21
|
+
): () => void;
|
|
22
|
+
/**
|
|
23
|
+
* Register an MCP App to be closed during cleanup. This prevents the App's
|
|
24
|
+
* transport from interfering with subsequent tests by responding to messages
|
|
25
|
+
* it shouldn't be handling.
|
|
26
|
+
*/
|
|
27
|
+
onCleanup(cleanup: () => void): void;
|
|
28
|
+
cleanup(): void;
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
/**
|
|
32
|
+
* In vitest's happy-dom environment, `window` is a Proxy around the real
|
|
33
|
+
* `GlobalWindow` object. `event.source` on `MessageEvent` returns the real
|
|
34
|
+
* `GlobalWindow`, which !== the proxied `window`. `PostMessageTransport` uses
|
|
35
|
+
* `event.source !== eventSource` to validate messages, so all messages get
|
|
36
|
+
* rejected when `eventSource` is the proxy.
|
|
37
|
+
*
|
|
38
|
+
* We work around this by discovering the real `GlobalWindow` via a probe
|
|
39
|
+
* `postMessage` and overriding `window.parent` so `PostMessageTransport`
|
|
40
|
+
* (which is constructed with `window.parent` as both target and eventSource)
|
|
41
|
+
* can properly match `event.source`.
|
|
42
|
+
*/
|
|
43
|
+
async function patchWindowParent(): Promise<() => void> {
|
|
44
|
+
const realWindow = await new Promise<Window>((resolve) => {
|
|
45
|
+
const probe = (event: MessageEvent) => {
|
|
46
|
+
window.removeEventListener("message", probe);
|
|
47
|
+
resolve(event.source as Window);
|
|
48
|
+
};
|
|
49
|
+
window.addEventListener("message", probe);
|
|
50
|
+
window.postMessage({ __mockMcpHostProbe: true }, "*");
|
|
51
|
+
});
|
|
52
|
+
|
|
53
|
+
// If event.source already matches window.parent, no patch needed
|
|
54
|
+
if (realWindow === window.parent) {
|
|
55
|
+
return () => {};
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
const originalParent = window.parent;
|
|
59
|
+
Object.defineProperty(window, "parent", {
|
|
60
|
+
value: realWindow,
|
|
61
|
+
writable: true,
|
|
62
|
+
configurable: true,
|
|
63
|
+
});
|
|
64
|
+
|
|
65
|
+
return () => {
|
|
66
|
+
Object.defineProperty(window, "parent", {
|
|
67
|
+
value: originalParent,
|
|
68
|
+
writable: true,
|
|
69
|
+
configurable: true,
|
|
70
|
+
});
|
|
71
|
+
};
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
export declare namespace mockMcpHost {
|
|
75
|
+
export interface Options {
|
|
76
|
+
hostCapabilities?: McpUiHostCapabilities;
|
|
77
|
+
hostContext?: McpUiHostContext;
|
|
78
|
+
hostInfo?: Implementation;
|
|
79
|
+
}
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
export async function mockMcpHost(
|
|
83
|
+
options?: mockMcpHost.Options
|
|
84
|
+
): Promise<MockMcpHost> {
|
|
85
|
+
const {
|
|
86
|
+
hostCapabilities = {},
|
|
87
|
+
hostContext = {},
|
|
88
|
+
hostInfo = { name: "MockMcpHost", version: "1.0.0" },
|
|
89
|
+
} = options ?? {};
|
|
90
|
+
|
|
91
|
+
const restore = await patchWindowParent();
|
|
92
|
+
|
|
93
|
+
const { promise: initialized, resolve: resolveInitialized } =
|
|
94
|
+
promiseWithResolvers<void>();
|
|
95
|
+
|
|
96
|
+
const cleanupFns = new Set<() => void>();
|
|
97
|
+
const toolCallHandlers = new Map<
|
|
98
|
+
string,
|
|
99
|
+
(params: CallToolRequest["params"]) => CallToolResult
|
|
100
|
+
>();
|
|
101
|
+
|
|
102
|
+
const listener = (event: MessageEvent<unknown>) => {
|
|
103
|
+
const data = event.data;
|
|
104
|
+
|
|
105
|
+
if (!data || typeof data !== "object" || !("jsonrpc" in data)) return;
|
|
106
|
+
if (!("method" in data)) return;
|
|
107
|
+
|
|
108
|
+
if (data.method === "ui/initialize" && "id" in data) {
|
|
109
|
+
window.postMessage({
|
|
110
|
+
jsonrpc: "2.0",
|
|
111
|
+
id: data.id,
|
|
112
|
+
result: {
|
|
113
|
+
protocolVersion: LATEST_PROTOCOL_VERSION,
|
|
114
|
+
hostCapabilities,
|
|
115
|
+
hostInfo,
|
|
116
|
+
hostContext,
|
|
117
|
+
},
|
|
118
|
+
});
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
// The App sends this notification after processing the initialize response
|
|
122
|
+
if (data.method === "ui/notifications/initialized") {
|
|
123
|
+
resolveInitialized();
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
if (data.method === "tools/call" && "id" in data) {
|
|
127
|
+
const { params } = data as unknown as CallToolRequest;
|
|
128
|
+
|
|
129
|
+
const handler = toolCallHandlers.get(params.name);
|
|
130
|
+
|
|
131
|
+
invariant(
|
|
132
|
+
handler,
|
|
133
|
+
`mockMcpHost: A mock tool call handler for '${params.name}' is not registered.`
|
|
134
|
+
);
|
|
135
|
+
|
|
136
|
+
window.postMessage({
|
|
137
|
+
jsonrpc: "2.0",
|
|
138
|
+
id: data.id,
|
|
139
|
+
result: handler(params),
|
|
140
|
+
});
|
|
141
|
+
}
|
|
142
|
+
};
|
|
143
|
+
|
|
144
|
+
window.addEventListener("message", listener);
|
|
145
|
+
|
|
146
|
+
function cleanup() {
|
|
147
|
+
window.removeEventListener("message", listener);
|
|
148
|
+
cleanupFns.forEach((fn) => fn());
|
|
149
|
+
restore();
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
return {
|
|
153
|
+
async sendToolResult(params) {
|
|
154
|
+
await initialized;
|
|
155
|
+
window.postMessage({
|
|
156
|
+
jsonrpc: "2.0",
|
|
157
|
+
method: "ui/notifications/tool-result",
|
|
158
|
+
params,
|
|
159
|
+
});
|
|
160
|
+
},
|
|
161
|
+
async sendToolInput(params) {
|
|
162
|
+
await initialized;
|
|
163
|
+
window.postMessage({
|
|
164
|
+
jsonrpc: "2.0",
|
|
165
|
+
method: "ui/notifications/tool-input",
|
|
166
|
+
params,
|
|
167
|
+
});
|
|
168
|
+
},
|
|
169
|
+
mockToolCall(name, handler) {
|
|
170
|
+
if (toolCallHandlers.has(name)) {
|
|
171
|
+
console.warn(
|
|
172
|
+
`mockMcpHost: a handler is already registered for tool "${name}"`
|
|
173
|
+
);
|
|
174
|
+
}
|
|
175
|
+
|
|
176
|
+
toolCallHandlers.set(name, handler);
|
|
177
|
+
|
|
178
|
+
return () => toolCallHandlers.delete(name);
|
|
179
|
+
},
|
|
180
|
+
onCleanup(fn) {
|
|
181
|
+
cleanupFns.add(fn);
|
|
182
|
+
},
|
|
183
|
+
cleanup,
|
|
184
|
+
[Symbol.dispose]() {
|
|
185
|
+
cleanup();
|
|
186
|
+
},
|
|
187
|
+
} satisfies MockMcpHost;
|
|
188
|
+
}
|
|
@@ -3,15 +3,28 @@ import type {
|
|
|
3
3
|
API,
|
|
4
4
|
OpenAiGlobals,
|
|
5
5
|
UnknownObject,
|
|
6
|
-
} from "../../../types
|
|
6
|
+
} from "../../../openai/types.js";
|
|
7
|
+
import "../../../openai/globals.js";
|
|
7
8
|
import { dispatchStateChange } from "./dispatchStateChange.js";
|
|
8
9
|
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
10
|
+
type Globals = API<any> & OpenAiGlobals;
|
|
11
|
+
|
|
12
|
+
const DEFAULTS = Object.freeze({
|
|
13
|
+
setWidgetState: async (state: UnknownObject) => {
|
|
14
|
+
window.openai.widgetState = state;
|
|
15
|
+
dispatchStateChange();
|
|
16
|
+
},
|
|
17
|
+
toolOutput: null,
|
|
18
|
+
toolResponseMetadata: null,
|
|
19
|
+
}) satisfies Partial<Globals>;
|
|
20
|
+
|
|
21
|
+
export function stubOpenAiGlobals(
|
|
22
|
+
globals?: Partial<Globals> | ((defaults: typeof DEFAULTS) => Partial<Globals>)
|
|
23
|
+
) {
|
|
24
|
+
vi.stubGlobal(
|
|
25
|
+
"openai",
|
|
26
|
+
typeof globals === "function" ?
|
|
27
|
+
globals(DEFAULTS)
|
|
28
|
+
: { ...DEFAULTS, ...globals }
|
|
29
|
+
);
|
|
17
30
|
}
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
import { act, render } from "@testing-library/react";
|
|
2
|
+
|
|
3
|
+
// Helper to silence act warnings when testing a component that suspends.
|
|
4
|
+
// See https://github.com/testing-library/react-testing-library/issues/1375
|
|
5
|
+
export async function renderAsync(...args: Parameters<typeof render>) {
|
|
6
|
+
return await act(async () => render(...args));
|
|
7
|
+
}
|
|
@@ -0,0 +1,172 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Adapted from
|
|
3
|
+
* https://github.com/apollographql/apollo-client/blob/1d165ba37eca7e5d667055553aacc4c26be56065/src/testing/internal/ObservableStream.ts
|
|
4
|
+
*/
|
|
5
|
+
import {
|
|
6
|
+
ReadableStream,
|
|
7
|
+
type ReadableStreamDefaultReader,
|
|
8
|
+
} from "node:stream/web";
|
|
9
|
+
import type { Observable, Subscribable, Unsubscribable } from "rxjs";
|
|
10
|
+
import { expect } from "vitest";
|
|
11
|
+
import { equals, iterableEquality, JEST_MATCHERS_OBJECT } from "@vitest/expect";
|
|
12
|
+
import { printDiffOrStringify } from "@vitest/utils/diff";
|
|
13
|
+
|
|
14
|
+
export interface TakeOptions {
|
|
15
|
+
timeout?: number;
|
|
16
|
+
}
|
|
17
|
+
type ObservableEvent<T> =
|
|
18
|
+
| { type: "next"; value: T }
|
|
19
|
+
| { type: "error"; error: any }
|
|
20
|
+
| { type: "complete" };
|
|
21
|
+
|
|
22
|
+
function formatMessage(
|
|
23
|
+
expected: ObservableEvent<any>,
|
|
24
|
+
actual: ObservableEvent<any>
|
|
25
|
+
) {
|
|
26
|
+
return printDiffOrStringify(expected, actual, { expand: true });
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
export class EventMismatchError extends Error {
|
|
30
|
+
readonly actual: ObservableEvent<any>;
|
|
31
|
+
readonly expected: ObservableEvent<any>;
|
|
32
|
+
|
|
33
|
+
static is(error: unknown): error is EventMismatchError {
|
|
34
|
+
return error instanceof Error && error.name === "EventMismatchError";
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
constructor(expected: ObservableEvent<any>, actual: ObservableEvent<any>) {
|
|
38
|
+
super(formatMessage(expected, actual));
|
|
39
|
+
this.name = "EventMismatchError";
|
|
40
|
+
this.actual = actual;
|
|
41
|
+
this.expected = expected;
|
|
42
|
+
|
|
43
|
+
Object.setPrototypeOf(this, EventMismatchError.prototype);
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
export class ObservableStream<T> {
|
|
48
|
+
private reader: ReadableStreamDefaultReader<ObservableEvent<T>>;
|
|
49
|
+
private subscription!: Unsubscribable;
|
|
50
|
+
private readerQueue: Array<Promise<ObservableEvent<T>>> = [];
|
|
51
|
+
|
|
52
|
+
constructor(observable: Observable<T> | Subscribable<T>) {
|
|
53
|
+
this.unsubscribe = this.unsubscribe.bind(this);
|
|
54
|
+
this.reader = new ReadableStream<ObservableEvent<T>>({
|
|
55
|
+
start: (controller) => {
|
|
56
|
+
this.subscription = observable.subscribe({
|
|
57
|
+
next: (value) => controller.enqueue({ type: "next", value }),
|
|
58
|
+
error: (error) => controller.enqueue({ type: "error", error }),
|
|
59
|
+
complete: () => controller.enqueue({ type: "complete" }),
|
|
60
|
+
});
|
|
61
|
+
},
|
|
62
|
+
}).getReader();
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
peek({ timeout = 100 }: TakeOptions = {}) {
|
|
66
|
+
// Calling `peek` multiple times in a row should not advance the reader
|
|
67
|
+
// multiple times until this value has been consumed.
|
|
68
|
+
let readerPromise = this.readerQueue[0];
|
|
69
|
+
|
|
70
|
+
if (!readerPromise) {
|
|
71
|
+
// Since this.reader.read() advances the reader in the stream, we don't
|
|
72
|
+
// want to consume this promise entirely, otherwise we will miss it when
|
|
73
|
+
// calling `take`. Instead, we push it into a queue that can be consumed
|
|
74
|
+
// by `take` the next time its called so that we avoid advancing the
|
|
75
|
+
// reader until we are finished processing all peeked values.
|
|
76
|
+
readerPromise = this.readNextValue();
|
|
77
|
+
this.readerQueue.push(readerPromise);
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
return Promise.race([
|
|
81
|
+
readerPromise,
|
|
82
|
+
new Promise<ObservableEvent<T>>((_, reject) => {
|
|
83
|
+
setTimeout(
|
|
84
|
+
reject,
|
|
85
|
+
timeout,
|
|
86
|
+
new Error("Timeout waiting for next event")
|
|
87
|
+
);
|
|
88
|
+
}),
|
|
89
|
+
]);
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
take({ timeout = 100 }: TakeOptions = {}) {
|
|
93
|
+
return Promise.race([
|
|
94
|
+
this.readerQueue.shift() || this.readNextValue(),
|
|
95
|
+
new Promise<ObservableEvent<T>>((_, reject) => {
|
|
96
|
+
setTimeout(
|
|
97
|
+
reject,
|
|
98
|
+
timeout,
|
|
99
|
+
new Error("Timeout waiting for next event")
|
|
100
|
+
);
|
|
101
|
+
}),
|
|
102
|
+
]).then((value) => {
|
|
103
|
+
if (value.type === "next") {
|
|
104
|
+
this.current = value.value;
|
|
105
|
+
}
|
|
106
|
+
return value;
|
|
107
|
+
});
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
[Symbol.dispose]() {
|
|
111
|
+
this.unsubscribe();
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
unsubscribe() {
|
|
115
|
+
this.subscription.unsubscribe();
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
async takeNext(options?: TakeOptions): Promise<T> {
|
|
119
|
+
const event = await this.take(options);
|
|
120
|
+
if (event.type !== "next") {
|
|
121
|
+
throw new EventMismatchError(
|
|
122
|
+
{ type: "next", value: expect.anything() },
|
|
123
|
+
event
|
|
124
|
+
);
|
|
125
|
+
}
|
|
126
|
+
return (event as ObservableEvent<T> & { type: "next" }).value;
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
async takeError(options?: TakeOptions): Promise<any> {
|
|
130
|
+
const event = await this.take(options);
|
|
131
|
+
validateEquals(event, { type: "error", error: expect.anything() });
|
|
132
|
+
return (event as ObservableEvent<T> & { type: "error" }).error;
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
async takeComplete(options?: TakeOptions): Promise<void> {
|
|
136
|
+
const event = await this.take(options);
|
|
137
|
+
validateEquals(event, { type: "complete" });
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
private async readNextValue() {
|
|
141
|
+
return this.reader.read().then((result) => result.value!);
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
private current?: T;
|
|
145
|
+
getCurrent() {
|
|
146
|
+
return this.current;
|
|
147
|
+
}
|
|
148
|
+
}
|
|
149
|
+
|
|
150
|
+
// Lightweight expect(...).toEqual(...) check that avoids using `expect` so that
|
|
151
|
+
// `expect.assertions(num)` does not double count assertions when using the take*
|
|
152
|
+
// functions inside of expect(stream).toEmit* matchers.
|
|
153
|
+
function validateEquals(
|
|
154
|
+
actualEvent: ObservableEvent<any>,
|
|
155
|
+
expectedEvent: ObservableEvent<any>
|
|
156
|
+
) {
|
|
157
|
+
// Uses the same matchers as expect(...).toEqual(...)
|
|
158
|
+
// https://github.com/vitest-dev/vitest/blob/438c44e7fb8f3a6a36db8ff504f852c01963ba88/packages/expect/src/jest-expect.ts#L107-L110
|
|
159
|
+
const isEqual = equals(actualEvent, expectedEvent, [
|
|
160
|
+
...getCustomEqualityTesters(),
|
|
161
|
+
iterableEquality,
|
|
162
|
+
]);
|
|
163
|
+
|
|
164
|
+
if (!isEqual) {
|
|
165
|
+
throw new EventMismatchError(expectedEvent, actualEvent);
|
|
166
|
+
}
|
|
167
|
+
}
|
|
168
|
+
|
|
169
|
+
// https://github.com/vitest-dev/vitest/blob/438c44e7fb8f3a6a36db8ff504f852c01963ba88/packages/expect/src/jest-matcher-utils.ts#L157-L159
|
|
170
|
+
function getCustomEqualityTesters() {
|
|
171
|
+
return (globalThis as any)[JEST_MATCHERS_OBJECT].customEqualityTesters;
|
|
172
|
+
}
|
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Adapted from
|
|
3
|
+
* https://github.com/apollographql/apollo-client/blob/39adac005bfc947948960ec32635272d7d252bd5/src/testing/matchers/utils/getSerializableProperties.ts
|
|
4
|
+
*/
|
|
5
|
+
import { ApolloClient, ObservableQuery } from "@apollo/client";
|
|
6
|
+
import { isPlainObject } from "@apollo/client/utilities/internal";
|
|
7
|
+
|
|
8
|
+
function isKnownClassInstance(value: unknown) {
|
|
9
|
+
return [ApolloClient, ObservableQuery].some((c) => value instanceof c);
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
export function getSerializableProperties(
|
|
13
|
+
obj: unknown,
|
|
14
|
+
{
|
|
15
|
+
includeKnownClassInstances = false,
|
|
16
|
+
}: {
|
|
17
|
+
includeKnownClassInstances?: boolean;
|
|
18
|
+
} = {}
|
|
19
|
+
): any {
|
|
20
|
+
if (Array.isArray(obj)) {
|
|
21
|
+
return obj.map((item) =>
|
|
22
|
+
getSerializableProperties(item, {
|
|
23
|
+
includeKnownClassInstances,
|
|
24
|
+
})
|
|
25
|
+
);
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
if (isPlainObject(obj)) {
|
|
29
|
+
return [...Object.keys(obj), ...Object.getOwnPropertySymbols(obj)].reduce(
|
|
30
|
+
(memo, key) => {
|
|
31
|
+
const value = obj[key as any];
|
|
32
|
+
if (
|
|
33
|
+
typeof value === "function" ||
|
|
34
|
+
(!includeKnownClassInstances && isKnownClassInstance(value))
|
|
35
|
+
) {
|
|
36
|
+
return memo;
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
// Recurse if we have a nested object/array
|
|
40
|
+
if (isPlainObject(value) || Array.isArray(value)) {
|
|
41
|
+
return {
|
|
42
|
+
...memo,
|
|
43
|
+
[key]: getSerializableProperties(value, {
|
|
44
|
+
includeKnownClassInstances,
|
|
45
|
+
}),
|
|
46
|
+
};
|
|
47
|
+
}
|
|
48
|
+
return { ...memo, [key]: value };
|
|
49
|
+
},
|
|
50
|
+
{} as Record<string, any>
|
|
51
|
+
);
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
return obj;
|
|
55
|
+
}
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
import type { ApplicationManifest } from "../../../types/application-manifest";
|
|
2
|
+
|
|
3
|
+
export function mockApplicationManifest(
|
|
4
|
+
overrides?: Partial<ApplicationManifest>
|
|
5
|
+
): ApplicationManifest {
|
|
6
|
+
return {
|
|
7
|
+
format: "apollo-ai-app-manifest",
|
|
8
|
+
version: "1",
|
|
9
|
+
appVersion: "1.0.0",
|
|
10
|
+
name: "the-store",
|
|
11
|
+
description:
|
|
12
|
+
"An online store selling a variety of high quality products across many different categories.",
|
|
13
|
+
hash: "f6a24922f6ad6ed8c2aa57baf3b8242ae5f38a09a6df3f2693077732434c4256",
|
|
14
|
+
operations: [
|
|
15
|
+
{
|
|
16
|
+
id: "c43af26552874026c3fb346148c5795896aa2f3a872410a0a2621cffee25291c",
|
|
17
|
+
name: "Product",
|
|
18
|
+
type: "query",
|
|
19
|
+
body: "query Product($id: ID!) {\n product(id: $id) {\n id\n title\n rating\n price\n description\n images\n __typename\n }\n}",
|
|
20
|
+
variables: { id: "ID" },
|
|
21
|
+
prefetch: false,
|
|
22
|
+
tools: [
|
|
23
|
+
{
|
|
24
|
+
name: "GetProduct",
|
|
25
|
+
description: "Shows the details page for a specific product.",
|
|
26
|
+
},
|
|
27
|
+
],
|
|
28
|
+
},
|
|
29
|
+
],
|
|
30
|
+
resource: "index.html",
|
|
31
|
+
csp: {
|
|
32
|
+
resourceDomains: [],
|
|
33
|
+
connectDomains: [],
|
|
34
|
+
frameDomains: [],
|
|
35
|
+
redirectDomains: [],
|
|
36
|
+
},
|
|
37
|
+
...overrides,
|
|
38
|
+
};
|
|
39
|
+
}
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
import { vi, type Mock } from "vitest";
|
|
2
|
+
|
|
3
|
+
const noop = () => {};
|
|
4
|
+
|
|
5
|
+
type ConsoleMethod = "log" | "info" | "warn" | "error" | "debug";
|
|
6
|
+
|
|
7
|
+
type Spies<Keys extends ConsoleMethod[]> = Record<
|
|
8
|
+
Keys[number],
|
|
9
|
+
Mock<(message?: any, ...args: any[]) => void>
|
|
10
|
+
>;
|
|
11
|
+
|
|
12
|
+
export function spyOnConsole<Keys extends ConsoleMethod[]>(
|
|
13
|
+
...spyOn: Keys
|
|
14
|
+
): Spies<Keys> & Disposable {
|
|
15
|
+
const spies = {} as Spies<Keys>;
|
|
16
|
+
for (const key of spyOn) {
|
|
17
|
+
// @ts-ignore
|
|
18
|
+
spies[key] = vi.spyOn(console, key).mockImplementation(noop);
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
return {
|
|
22
|
+
...spies,
|
|
23
|
+
[Symbol.dispose]() {
|
|
24
|
+
for (const spy of Object.values(spies) as Mock[]) {
|
|
25
|
+
spy.mockRestore();
|
|
26
|
+
}
|
|
27
|
+
},
|
|
28
|
+
};
|
|
29
|
+
}
|