# graphql-stream-viewer-helper

> Helper library for GraphQL Stream Viewer extension - enables live SSE event monitoring without CDP permissions.

[![NPM Version](https://img.shields.io/npm/v/graphql-stream-viewer-helper)](https://www.npmjs.com/package/graphql-stream-viewer-helper)
[![License](https://img.shields.io/npm/l/graphql-stream-viewer-helper)](./LICENSE)

## What is this?

This is an opt-in helper library that developers can integrate into their applications to enable **live Server-Sent Events (SSE) monitoring** in the GraphQL Stream Viewer browser extension.

### Why do I need this?

The GraphQL Stream Viewer extension can monitor GraphQL requests in three ways:

1. **Network API** (default) - Captures completed requests. SSE events appear after the connection closes.
2. **Chrome DevTools Protocol (CDP)** - Captures live events but requires `debugger` permission (prompts user).
3. **Helper Library** (this package) - Captures live events with **no special permissions** required!

By including this library in your application, you get:
- ✅ **Live SSE event monitoring** - Events appear instantly as they arrive
- ✅ **No permission prompts** - Works without CDP/debugger access
- ✅ **Developer-friendly** - Opt-in only when you need debugging
- ✅ **Zero dependencies** - Tiny footprint (~5KB gzipped)
- ✅ **Works everywhere** - Any browser, any environment

## Installation

### From Package Managers

```bash
# npm
npm install --save-dev graphql-stream-viewer-helper

# yarn
yarn add --dev graphql-stream-viewer-helper

# bun
bun add --dev graphql-stream-viewer-helper

# pnpm
pnpm add --save-dev graphql-stream-viewer-helper
```

### Direct URL Import (ESM)

Import directly from ESM-compatible registries without installation:

```typescript
// From esm.sh (recommended)
import 'https://esm.sh/graphql-stream-viewer-helper/auto';

// From unpkg
import 'https://unpkg.com/graphql-stream-viewer-helper@latest/dist/auto.js';

// From jsdelivr
import 'https://cdn.jsdelivr.net/npm/graphql-stream-viewer-helper@latest/dist/auto.js';

// From skypack
import 'https://cdn.skypack.dev/graphql-stream-viewer-helper/auto';
```

**Example usage in HTML:**

```html
<!DOCTYPE html>
<html>
<head>
  <title>My App</title>
</head>
<body>
  <div id="app"></div>

  <script type="module">
    // Import directly from CDN (no build step needed!)
    import 'https://esm.sh/graphql-stream-viewer-helper/auto';

    // Your app code
    console.log('GraphQL Stream Viewer helper loaded!');
  </script>
</body>
</html>
```

**Deno example:**

```typescript
// deno run --allow-net your-script.ts
import { connectDevTools } from 'https://esm.sh/graphql-stream-viewer-helper';

connectDevTools({ debug: true });
```

> **Note**: Install as a dev dependency since this is only needed during development.

## Usage

### Option 1: Automatic (Easiest)

Import the auto-initialization module at your application's entry point:

```typescript
// In your main entry file (e.g., index.ts, main.ts, App.tsx)
import 'graphql-stream-viewer-helper/auto';
```

This automatically connects to the extension when your app loads.

### Option 2: Manual (More Control)

For more control over when the library connects:

```typescript
import { connectDevTools } from 'graphql-stream-viewer-helper';

// Connect with default options
const connection = connectDevTools();

// Later, disconnect if needed
connection.disconnect();
```

### Option 3: Custom Configuration

```typescript
import { connectDevTools } from 'graphql-stream-viewer-helper';

const connection = connectDevTools({
  // Enable debug logging
  debug: true,

  // Capture HTTP GraphQL requests too (experimental)
  captureHTTP: false,

  // Custom URL filter
  urlFilter: (url) => {
    // Only capture URLs with '/graphql' or '/api/graphql'
    return url.includes('/graphql') || url.includes('/api/graphql');
  }
});

// Check if connected
console.log(connection.isConnected()); // true

// Get current config
console.log(connection.getConfig());

// Disconnect when done
connection.disconnect();
```

## Integration Examples

### React

```tsx
// src/index.tsx or src/main.tsx
import React from 'react';
import ReactDOM from 'react-dom/client';
import App from './App';

// Only import in development
if (process.env.NODE_ENV === 'development') {
  import('graphql-stream-viewer-helper/auto');
}

ReactDOM.createRoot(document.getElementById('root')!).render(
  <React.StrictMode>
    <App />
  </React.StrictMode>
);
```

### Vue 3

```typescript
// src/main.ts
import { createApp } from 'vue';
import App from './App.vue';

// Only import in development
if (import.meta.env.DEV) {
  import('graphql-stream-viewer-helper/auto');
}

createApp(App).mount('#app');
```

### Next.js

```typescript
// pages/_app.tsx
import type { AppProps } from 'next/app';
import { useEffect } from 'react';

export default function App({ Component, pageProps }: AppProps) {
  useEffect(() => {
    // Only in development
    if (process.env.NODE_ENV === 'development') {
      import('graphql-stream-viewer-helper').then(({ connectDevTools }) => {
        connectDevTools({ debug: true });
      });
    }
  }, []);

  return <Component {...pageProps} />;
}
```

### Vanilla JavaScript

```html
<!-- index.html -->
<!DOCTYPE html>
<html>
<head>
  <title>My App</title>
</head>
<body>
  <div id="app"></div>

  <script type="module">
    // Import the helper library
    import 'graphql-stream-viewer-helper/auto';

    // Your application code
    import './app.js';
  </script>
</body>
</html>
```

### Apollo Client

```typescript
import { ApolloClient, InMemoryCache, HttpLink, split } from '@apollo/client';
import { GraphQLWsLink } from '@apollo/client/link/subscriptions';
import { createClient } from 'graphql-ws';
import { getMainDefinition } from '@apollo/client/utilities';

// Initialize helper library
if (process.env.NODE_ENV === 'development') {
  import('graphql-stream-viewer-helper/auto');
}

// Create HTTP link
const httpLink = new HttpLink({
  uri: 'https://api.example.com/graphql'
});

// Create WebSocket link for subscriptions
const wsLink = new GraphQLWsLink(createClient({
  url: 'wss://api.example.com/graphql'
}));

// Split links based on operation type
const splitLink = split(
  ({ query }) => {
    const definition = getMainDefinition(query);
    return (
      definition.kind === 'OperationDefinition' &&
      definition.operation === 'subscription'
    );
  },
  wsLink,
  httpLink,
);

const client = new ApolloClient({
  link: splitLink,
  cache: new InMemoryCache()
});
```

### urql

```typescript
import { createClient, fetchExchange } from 'urql';
import { subscriptionExchange } from '@urql/exchange-graphql-ws';
import { createClient as createWSClient } from 'graphql-ws';

// Initialize helper library
if (process.env.NODE_ENV === 'development') {
  import('graphql-stream-viewer-helper/auto');
}

const wsClient = createWSClient({
  url: 'wss://api.example.com/graphql',
});

const client = createClient({
  url: 'https://api.example.com/graphql',
  exchanges: [
    fetchExchange,
    subscriptionExchange({
      forwardSubscription: (operation) => ({
        subscribe: (sink) => ({
          unsubscribe: wsClient.subscribe(operation, sink),
        }),
      }),
    }),
  ],
});
```

## API Reference

### `connectDevTools(config?)`

Connect to the GraphQL Stream Viewer extension.

**Parameters:**
- `config` (optional): Configuration options

**Returns:** `HelperConnection` object with:
- `disconnect()`: Disconnect and clean up
- `isConnected()`: Check if still connected
- `getConfig()`: Get current configuration

**Example:**
```typescript
const connection = connectDevTools({
  debug: true,
  captureSSE: true,
  captureHTTP: false,
  urlFilter: (url) => url.includes('/graphql')
});
```

### `disconnect()`

Disconnect from the extension (if connected).

**Example:**
```typescript
import { disconnect } from 'graphql-stream-viewer-helper';

disconnect();
```

### `isConnected()`

Check if currently connected to the extension.

**Returns:** `boolean`

**Example:**
```typescript
import { isConnected } from 'graphql-stream-viewer-helper';

if (isConnected()) {
  console.log('Connected to extension');
}
```

### `checkExtensionAvailable()`

Check if the GraphQL Stream Viewer extension is available (installed and active).

**Returns:** `boolean`

**Example:**
```typescript
import { checkExtensionAvailable } from 'graphql-stream-viewer-helper';

if (checkExtensionAvailable()) {
  console.log('Extension detected!');
}
```

### `waitForExtension(timeoutMs?)`

Wait for the extension to become available (with timeout).

**Parameters:**
- `timeoutMs` (optional): Timeout in milliseconds (default: 5000)

**Returns:** `Promise<boolean>` - `true` if extension becomes available

**Example:**
```typescript
import { waitForExtension, connectDevTools } from 'graphql-stream-viewer-helper';

async function setupDevTools() {
  const available = await waitForExtension(10000); // Wait up to 10 seconds
  if (available) {
    connectDevTools({ debug: true });
    console.log('Connected to extension!');
  } else {
    console.warn('Extension not detected');
  }
}

setupDevTools();
```

## Configuration Options

| Option | Type | Default | Description |
|--------|------|---------|-------------|
| `captureSSE` | `boolean` | `true` | Enable SSE (Server-Sent Events) capture |
| `captureHTTP` | `boolean` | `false` | Enable HTTP GraphQL request capture (experimental) |
| `debug` | `boolean` | `false` | Enable debug logging to console |
| `urlFilter` | `(url: string) => boolean` | See below | Custom filter function for URLs |

**Default URL Filter:**
```typescript
(url: string) => {
  const lowerUrl = url.toLowerCase();
  return (
    lowerUrl.includes('/graphql') ||
    lowerUrl.includes('/gql') ||
    lowerUrl.includes('query=')
  );
}
```

## How It Works

1. **You include the library** in your application code
2. **Library wraps EventSource** at the application level (where JavaScript CAN access events)
3. **Events are captured** as they arrive from your SSE connections
4. **Library sends events** to extension via `window.postMessage()`
5. **Extension displays events** live in the DevTools panel

Unlike browser-level interception (which is impossible with EventSource), this approach works because the library runs in your application's JavaScript context where events are accessible.

## Browser Compatibility

Works in all modern browsers that support:
- EventSource API
- window.postMessage
- Chrome/Edge/Firefox extensions

## Security & Privacy

- **Opt-in only** - Only works when you explicitly include the library
- **Development only** - Should be installed as a dev dependency
- **No data leaves your machine** - All communication is local (extension ↔ page)
- **No network requests** - Library doesn't make any external requests
- **No tracking** - Zero telemetry or analytics

## Troubleshooting

### Events not appearing in extension

1. **Check extension is installed and active**
   ```typescript
   import { checkExtensionAvailable } from 'graphql-stream-viewer-helper';
   console.log('Extension available:', checkExtensionAvailable());
   ```

2. **Enable debug mode**
   ```typescript
   connectDevTools({ debug: true });
   ```

3. **Check browser console** for helper library messages

4. **Verify URL is captured** - Check your `urlFilter` configuration

### Library not connecting

1. **Extension might load after page** - Use `waitForExtension()`:
   ```typescript
   import { waitForExtension, connectDevTools } from 'graphql-stream-viewer-helper';

   waitForExtension().then((available) => {
     if (available) connectDevTools();
   });
   ```

2. **Check for errors in console** with debug mode enabled

### SSE events not captured

1. **Verify EventSource is used** - Library only works with EventSource API
2. **Check URL matches filter** - Default filter requires `/graphql`, `/gql`, or `query=`
3. **Customize URL filter** if needed:
   ```typescript
   connectDevTools({
     urlFilter: (url) => url.includes('/your-endpoint')
   });
   ```

## FAQ

**Q: Do I need to install the browser extension?**
A: Yes, this library works together with the GraphQL Stream Viewer extension.

**Q: Will this affect my production builds?**
A: No, install it as a dev dependency and only import in development:
```typescript
if (process.env.NODE_ENV === 'development') {
  import('graphql-stream-viewer-helper/auto');
}
```

**Q: Does this work with WebSockets?**
A: Not yet. Currently supports Server-Sent Events (EventSource) only. WebSocket support is planned.

**Q: What about performance?**
A: Negligible overhead. The library only adds event listeners and sends messages. Zero performance impact on production builds (when not included).

**Q: Can I use this with any GraphQL client?**
A: Yes! Works with Apollo Client, urql, Relay, or any client that uses EventSource for subscriptions.

**Q: Does it capture GraphQL queries/mutations?**
A: Currently only SSE subscriptions. HTTP request capture is experimental (set `captureHTTP: true`).

## Examples

See the [examples/](../../examples/) directory for complete working examples:

- [React + Apollo Client](../../examples/react-apollo/)
- [Vue 3 + urql](../../examples/vue-urql/)
- [Vanilla JS](../../examples/vanilla-js/)
- [Next.js](../../examples/nextjs/)

## License

MIT © Austin Golding

## Publishing

### Registries with Direct URL Import Support

This package is automatically available on multiple ESM-compatible registries that support direct URL imports:

#### 1. **npm Registry** (Primary)

```bash
npm publish
```

Once published to npm, the package is automatically available on:

#### 2. **esm.sh** (Recommended CDN)

- **URL**: `https://esm.sh/graphql-stream-viewer-helper`
- **Features**:
  - TypeScript types included
  - Automatic transpilation
  - Version pinning: `https://esm.sh/graphql-stream-viewer-helper@1.0.0`
  - Subpath imports: `https://esm.sh/graphql-stream-viewer-helper/auto`
- **Auto-sync**: Updates within minutes of npm publish

#### 3. **unpkg**

- **URL**: `https://unpkg.com/graphql-stream-viewer-helper`
- **Features**:
  - Serves exact files from npm package
  - Browse package contents: `https://unpkg.com/browse/graphql-stream-viewer-helper/`
  - Version pinning supported
- **Auto-sync**: Instant after npm publish

#### 4. **jsDelivr**

- **URL**: `https://cdn.jsdelivr.net/npm/graphql-stream-viewer-helper`
- **Features**:
  - Global CDN with high performance
  - Version ranges: `@latest`, `@1`, `@1.2`, `@1.2.3`
  - Minified files: `.min.js` suffix supported
- **Auto-sync**: Within minutes of npm publish

#### 5. **Skypack**

- **URL**: `https://cdn.skypack.dev/graphql-stream-viewer-helper`
- **Features**:
  - Optimized for modern browsers
  - TypeScript types included
  - HTTP/2 and HTTP/3 support
- **Auto-sync**: Updates periodically from npm

#### 6. **Deno Land** (Optional)

For native Deno support, you can also publish to `deno.land/x`:

```bash
# 1. Create a webhook on GitHub to deno.land/x
# 2. Tag and push to trigger webhook
git tag v1.0.0
git push origin v1.0.0
```

Then available at: `https://deno.land/x/graphql_stream_viewer_helper`

### Publishing Checklist

1. **Build the package**:

   ```bash
   cd packages/helper
   bun run build
   ```

2. **Update version in package.json**:

   ```json
   {
     "version": "1.0.0"
   }
   ```

3. **Ensure exports are configured** (already done):

   ```json
   {
     "exports": {
       ".": {
         "import": "./dist/index.js",
         "types": "./dist/index.d.ts"
       },
       "./auto": {
         "import": "./dist/auto.js",
         "types": "./dist/auto.d.ts"
       }
     }
   }
   ```

4. **Publish to npm**:

   ```bash
   npm publish --access public
   ```

5. **Verify on CDNs** (wait 1-5 minutes):
   - esm.sh: `curl -I https://esm.sh/graphql-stream-viewer-helper`
   - unpkg: `curl -I https://unpkg.com/graphql-stream-viewer-helper`
   - jsDelivr: `curl -I https://cdn.jsdelivr.net/npm/graphql-stream-viewer-helper`

### Package Requirements for URL Imports

The package is already configured with:

✅ **ES Modules** (`"type": "module"` in package.json)
✅ **TypeScript declarations** (`.d.ts` files)
✅ **Proper exports** (subpath exports for `/auto`)
✅ **No dependencies** (zero runtime deps = works everywhere)
✅ **Browser-compatible** (no Node.js-specific APIs)

### Usage After Publishing

Once published, users can import directly without any build tools:

```html
<script type="module">
  // Latest version
  import 'https://esm.sh/graphql-stream-viewer-helper/auto';

  // Pinned version (recommended for production)
  import 'https://esm.sh/graphql-stream-viewer-helper@1.0.0/auto';
</script>
```

## Links

- [GraphQL Stream Viewer Extension](https://github.com/yourusername/stream-viewer)
- [Report Issues](https://github.com/yourusername/stream-viewer/issues)
- [NPM Package](https://www.npmjs.com/package/graphql-stream-viewer-helper)
- [esm.sh Package](https://esm.sh/graphql-stream-viewer-helper)
- [unpkg Package](https://unpkg.com/browse/graphql-stream-viewer-helper/)
- [jsDelivr Package](https://www.jsdelivr.com/package/npm/graphql-stream-viewer-helper)

## Contributing

Contributions welcome! See [CONTRIBUTING.md](../../CONTRIBUTING.md)
