@dumbql/subscriptions 0.0.1 → 0.0.2-rc.3
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +97 -0
- package/package.json +28 -15
- package/src/angular.ts +6 -0
- package/src/lib/graphql-subscription.service.ts +33 -0
- package/src/lib/graphql-subscription.ts +118 -0
- package/src/lib/provide-subscriptions.ts +6 -0
- package/src/lib/subscribe.ts +14 -0
- package/src/lib/subscriptions-config.ts +13 -0
- package/src/public-api.ts +2 -0
- package/fesm2022/dumbql-subscriptions.mjs +0 -139
- package/fesm2022/dumbql-subscriptions.mjs.map +0 -1
- package/types/dumbql-subscriptions.d.ts +0 -15
package/README.md
ADDED
|
@@ -0,0 +1,97 @@
|
|
|
1
|
+
<p align="center">
|
|
2
|
+
<img src="../../../public/logos/logo.png" alt="DumbQL" width="160"/>
|
|
3
|
+
</p>
|
|
4
|
+
|
|
5
|
+
## Install
|
|
6
|
+
|
|
7
|
+
```bash
|
|
8
|
+
npm install @dumbql/subscriptions
|
|
9
|
+
```
|
|
10
|
+
|
|
11
|
+
## Usage (framework‑agnostic)
|
|
12
|
+
|
|
13
|
+
```typescript
|
|
14
|
+
import { GraphqlSubscription } from '@dumbql/subscriptions';
|
|
15
|
+
import { print, gql } from 'graphql';
|
|
16
|
+
|
|
17
|
+
const subs = new GraphqlSubscription('/graphql');
|
|
18
|
+
|
|
19
|
+
const unsubscribe = subs.subscribe(
|
|
20
|
+
print(gql`subscription { messageAdded { content } }`),
|
|
21
|
+
{},
|
|
22
|
+
{
|
|
23
|
+
next: (data) => console.log('new message', data),
|
|
24
|
+
error: (err) => console.error('sub error', err),
|
|
25
|
+
complete: () => console.log('sub complete'),
|
|
26
|
+
},
|
|
27
|
+
);
|
|
28
|
+
|
|
29
|
+
// later: unsubscribe();
|
|
30
|
+
```
|
|
31
|
+
|
|
32
|
+
## Usage (Angular)
|
|
33
|
+
|
|
34
|
+
```typescript
|
|
35
|
+
import { Component, inject } from '@angular/core';
|
|
36
|
+
import { GraphqlSubscriptionService } from '@dumbql/subscriptions/angular';
|
|
37
|
+
import { gql } from '@dumbql/core';
|
|
38
|
+
|
|
39
|
+
@Component({})
|
|
40
|
+
class MessagesComponent {
|
|
41
|
+
private subs = inject(GraphqlSubscriptionService);
|
|
42
|
+
messages$ = this.subs.subscribe<{ content: string }>(
|
|
43
|
+
gql`subscription { messageAdded { content } }`,
|
|
44
|
+
);
|
|
45
|
+
}
|
|
46
|
+
```
|
|
47
|
+
|
|
48
|
+
Or standalone:
|
|
49
|
+
|
|
50
|
+
```typescript
|
|
51
|
+
import { subscribe } from '@dumbql/subscriptions/angular';
|
|
52
|
+
|
|
53
|
+
@Component({})
|
|
54
|
+
class MessagesComponent {
|
|
55
|
+
messages$ = subscribe<{ content: string }>(
|
|
56
|
+
gql`subscription { messageAdded { content } }`,
|
|
57
|
+
);
|
|
58
|
+
}
|
|
59
|
+
```
|
|
60
|
+
|
|
61
|
+
## Angular Provider
|
|
62
|
+
|
|
63
|
+
```typescript
|
|
64
|
+
import { provideDumbqlSubscriptions } from '@dumbql/subscriptions/angular';
|
|
65
|
+
|
|
66
|
+
export const appConfig = {
|
|
67
|
+
providers: [
|
|
68
|
+
provideDumbqlSubscriptions({
|
|
69
|
+
wsUrl: 'ws://localhost:4000/graphql',
|
|
70
|
+
connectionParams: () => ({ authorization: `Bearer ${getToken()}` }),
|
|
71
|
+
}),
|
|
72
|
+
],
|
|
73
|
+
};
|
|
74
|
+
```
|
|
75
|
+
|
|
76
|
+
## API
|
|
77
|
+
|
|
78
|
+
### `@dumbql/subscriptions` (agnostic)
|
|
79
|
+
|
|
80
|
+
| Export | Description |
|
|
81
|
+
|--------|-------------|
|
|
82
|
+
| `GraphqlSubscription` | Core WebSocket subscription class |
|
|
83
|
+
|
|
84
|
+
### `@dumbql/subscriptions/angular` (Angular)
|
|
85
|
+
|
|
86
|
+
| Export | Description |
|
|
87
|
+
|--------|-------------|
|
|
88
|
+
| `GraphqlSubscriptionService` | Injectable WebSocket subscription manager |
|
|
89
|
+
| `subscribe(document, variables?)` | Standalone function (injection‑free) |
|
|
90
|
+
| `provideDumbqlSubscriptions(config?)` | Provider for subscription config |
|
|
91
|
+
| `SubscriptionsConfig` | Configuration interface |
|
|
92
|
+
| `SUBSCRIPTIONS_CONFIG` | Injection token |
|
|
93
|
+
|
|
94
|
+
## Dependencies
|
|
95
|
+
|
|
96
|
+
- Core (`@dumbql/subscriptions`): `@dumbql/core`
|
|
97
|
+
- Angular (`@dumbql/subscriptions/angular`): additionally `@angular/core`, `rxjs`
|
package/package.json
CHANGED
|
@@ -1,25 +1,38 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@dumbql/subscriptions",
|
|
3
|
-
"version": "0.0.
|
|
3
|
+
"version": "0.0.2-rc.3",
|
|
4
|
+
"description": "GraphQL subscriptions over WebSocket (graphql-transport-ws) — framework-agnostic core + Angular service",
|
|
5
|
+
"keywords": [
|
|
6
|
+
"graphql",
|
|
7
|
+
"subscriptions",
|
|
8
|
+
"websocket",
|
|
9
|
+
"graphql-transport-ws",
|
|
10
|
+
"dumbql"
|
|
11
|
+
],
|
|
12
|
+
"license": "MIT",
|
|
13
|
+
"type": "module",
|
|
14
|
+
"main": "src/public-api.ts",
|
|
15
|
+
"types": "src/public-api.ts",
|
|
16
|
+
"exports": {
|
|
17
|
+
".": "./src/public-api.ts",
|
|
18
|
+
"./angular": "./src/angular.ts",
|
|
19
|
+
"./package.json": "./package.json"
|
|
20
|
+
},
|
|
21
|
+
"sideEffects": false,
|
|
4
22
|
"peerDependencies": {
|
|
5
23
|
"@angular/core": "^22.0.0",
|
|
6
24
|
"@dumbql/core": "^0.0.1",
|
|
7
25
|
"rxjs": "~7.8.0"
|
|
8
26
|
},
|
|
9
|
-
"
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
"
|
|
14
|
-
"
|
|
27
|
+
"peerDependenciesMeta": {
|
|
28
|
+
"@angular/core": {
|
|
29
|
+
"optional": true
|
|
30
|
+
},
|
|
31
|
+
"@dumbql/core": {
|
|
32
|
+
"optional": true
|
|
15
33
|
},
|
|
16
|
-
"
|
|
17
|
-
"
|
|
18
|
-
"default": "./fesm2022/dumbql-subscriptions.mjs"
|
|
34
|
+
"rxjs": {
|
|
35
|
+
"optional": true
|
|
19
36
|
}
|
|
20
|
-
},
|
|
21
|
-
"type": "module",
|
|
22
|
-
"dependencies": {
|
|
23
|
-
"tslib": "^2.3.0"
|
|
24
37
|
}
|
|
25
|
-
}
|
|
38
|
+
}
|
package/src/angular.ts
ADDED
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
// Angular wrappers for @dumbql/subscriptions
|
|
2
|
+
// Import from '@dumbql/subscriptions/angular' in Angular projects
|
|
3
|
+
export { GraphqlSubscriptionService } from './lib/graphql-subscription.service';
|
|
4
|
+
export { subscribe } from './lib/subscribe';
|
|
5
|
+
export { provideDumbqlSubscriptions } from './lib/provide-subscriptions';
|
|
6
|
+
export { SUBSCRIPTIONS_CONFIG, type SubscriptionsConfig } from './lib/subscriptions-config';
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
import { Injectable, inject } from '@angular/core';
|
|
2
|
+
import { Observable, Subscriber } from 'rxjs';
|
|
3
|
+
import { print, type DocumentNode, type DumbqlConfig, DUMBQL_CONFIG } from '@dumbql/core';
|
|
4
|
+
import { GraphqlSubscription } from './graphql-subscription';
|
|
5
|
+
import { SUBSCRIPTIONS_CONFIG } from './subscriptions-config';
|
|
6
|
+
|
|
7
|
+
@Injectable({ providedIn: 'root' })
|
|
8
|
+
export class GraphqlSubscriptionService {
|
|
9
|
+
private readonly config: DumbqlConfig =
|
|
10
|
+
inject(DUMBQL_CONFIG, { optional: true }) ?? { endpoint: '/graphql' } as DumbqlConfig;
|
|
11
|
+
|
|
12
|
+
private readonly subsConfig = inject(SUBSCRIPTIONS_CONFIG, { optional: true });
|
|
13
|
+
|
|
14
|
+
private readonly core = new GraphqlSubscription(
|
|
15
|
+
this.subsConfig?.wsUrl ?? this.config.endpoint ?? '/graphql',
|
|
16
|
+
);
|
|
17
|
+
|
|
18
|
+
subscribe<T>(
|
|
19
|
+
document: DocumentNode,
|
|
20
|
+
variables?: Record<string, unknown>,
|
|
21
|
+
): Observable<T> {
|
|
22
|
+
const query = print(document);
|
|
23
|
+
|
|
24
|
+
return new Observable<T>((subscriber: Subscriber<T>) => {
|
|
25
|
+
const unsubscribe = this.core.subscribe<T>(query, variables, {
|
|
26
|
+
next: (data) => subscriber.next(data),
|
|
27
|
+
error: (err) => subscriber.error(err),
|
|
28
|
+
complete: () => subscriber.complete(),
|
|
29
|
+
});
|
|
30
|
+
return () => unsubscribe();
|
|
31
|
+
});
|
|
32
|
+
}
|
|
33
|
+
}
|
|
@@ -0,0 +1,118 @@
|
|
|
1
|
+
interface WsMessage {
|
|
2
|
+
type: string;
|
|
3
|
+
id?: string;
|
|
4
|
+
payload?: unknown;
|
|
5
|
+
}
|
|
6
|
+
|
|
7
|
+
interface SubscriptionCallbacks<T> {
|
|
8
|
+
next: (data: T) => void;
|
|
9
|
+
error: (err: Error) => void;
|
|
10
|
+
complete: () => void;
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
const WS_PROTOCOL = 'graphql-transport-ws';
|
|
14
|
+
const CONNECTION_TIMEOUT = 10000;
|
|
15
|
+
|
|
16
|
+
function defaultWsUrl(endpoint: string): string {
|
|
17
|
+
return endpoint.replace(/^http/, 'ws');
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
export class GraphqlSubscription {
|
|
21
|
+
constructor(private readonly endpoint: string) {}
|
|
22
|
+
|
|
23
|
+
subscribe<T>(
|
|
24
|
+
query: string,
|
|
25
|
+
variables?: Record<string, unknown>,
|
|
26
|
+
callbacks?: Partial<SubscriptionCallbacks<T>>,
|
|
27
|
+
): () => void {
|
|
28
|
+
const url = defaultWsUrl(this.endpoint);
|
|
29
|
+
let ws: WebSocket;
|
|
30
|
+
let completed = false;
|
|
31
|
+
const subId =
|
|
32
|
+
typeof crypto !== 'undefined' && crypto.randomUUID
|
|
33
|
+
? crypto.randomUUID()
|
|
34
|
+
: 'sub_' + Math.random().toString(36).substring(2, 9);
|
|
35
|
+
|
|
36
|
+
const emit = {
|
|
37
|
+
next: callbacks?.next ?? (() => {}),
|
|
38
|
+
error: callbacks?.error ?? (() => {}),
|
|
39
|
+
complete: callbacks?.complete ?? (() => {}),
|
|
40
|
+
};
|
|
41
|
+
|
|
42
|
+
try {
|
|
43
|
+
ws = new WebSocket(url, WS_PROTOCOL);
|
|
44
|
+
} catch (err) {
|
|
45
|
+
emit.error(err instanceof Error ? err : new Error('WebSocket creation failed'));
|
|
46
|
+
return () => {};
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
const connTimeout = setTimeout(() => {
|
|
50
|
+
if (!completed) {
|
|
51
|
+
ws.close();
|
|
52
|
+
emit.error(new Error('WebSocket connection timeout'));
|
|
53
|
+
}
|
|
54
|
+
}, CONNECTION_TIMEOUT);
|
|
55
|
+
|
|
56
|
+
ws.onopen = () => {
|
|
57
|
+
ws.send(JSON.stringify({ type: 'connection_init' }));
|
|
58
|
+
};
|
|
59
|
+
|
|
60
|
+
ws.onmessage = (event: MessageEvent) => {
|
|
61
|
+
let msg: WsMessage;
|
|
62
|
+
try {
|
|
63
|
+
msg = JSON.parse(event.data) as WsMessage;
|
|
64
|
+
} catch {
|
|
65
|
+
return;
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
switch (msg.type) {
|
|
69
|
+
case 'connection_ack': {
|
|
70
|
+
clearTimeout(connTimeout);
|
|
71
|
+
ws.send(JSON.stringify({
|
|
72
|
+
type: 'subscribe',
|
|
73
|
+
id: subId,
|
|
74
|
+
payload: { query, variables },
|
|
75
|
+
}));
|
|
76
|
+
break;
|
|
77
|
+
}
|
|
78
|
+
case 'next': {
|
|
79
|
+
const payload = msg.payload as { data?: T; errors?: { message: string }[] };
|
|
80
|
+
if (payload.errors && payload.errors.length > 0) {
|
|
81
|
+
emit.error(new Error(payload.errors[0].message));
|
|
82
|
+
} else if (payload.data !== undefined) {
|
|
83
|
+
emit.next(payload.data);
|
|
84
|
+
}
|
|
85
|
+
break;
|
|
86
|
+
}
|
|
87
|
+
case 'error': {
|
|
88
|
+
const payload = msg.payload as { message?: string };
|
|
89
|
+
emit.error(new Error(payload?.message ?? 'Subscription error'));
|
|
90
|
+
break;
|
|
91
|
+
}
|
|
92
|
+
case 'complete': {
|
|
93
|
+
emit.complete();
|
|
94
|
+
break;
|
|
95
|
+
}
|
|
96
|
+
}
|
|
97
|
+
};
|
|
98
|
+
|
|
99
|
+
ws.onerror = () => {
|
|
100
|
+
emit.error(new Error('WebSocket connection failed'));
|
|
101
|
+
};
|
|
102
|
+
|
|
103
|
+
ws.onclose = () => {
|
|
104
|
+
if (!completed) {
|
|
105
|
+
emit.complete();
|
|
106
|
+
}
|
|
107
|
+
};
|
|
108
|
+
|
|
109
|
+
return () => {
|
|
110
|
+
completed = true;
|
|
111
|
+
clearTimeout(connTimeout);
|
|
112
|
+
if (ws.readyState === WebSocket.OPEN || ws.readyState === WebSocket.CONNECTING) {
|
|
113
|
+
ws.send(JSON.stringify({ type: 'complete', id: subId }));
|
|
114
|
+
ws.close(1000);
|
|
115
|
+
}
|
|
116
|
+
};
|
|
117
|
+
}
|
|
118
|
+
}
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
import { type Provider } from '@angular/core';
|
|
2
|
+
import { SUBSCRIPTIONS_CONFIG, type SubscriptionsConfig } from './subscriptions-config';
|
|
3
|
+
|
|
4
|
+
export function provideDumbqlSubscriptions(config?: SubscriptionsConfig): Provider[] {
|
|
5
|
+
return config ? [{ provide: SUBSCRIPTIONS_CONFIG, useValue: config }] : [];
|
|
6
|
+
}
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import { inject } from '@angular/core';
|
|
2
|
+
import { defer, Observable } from 'rxjs';
|
|
3
|
+
import { GraphqlSubscriptionService } from './graphql-subscription.service';
|
|
4
|
+
import type { DocumentNode } from '@dumbql/core';
|
|
5
|
+
|
|
6
|
+
export function subscribe<T>(
|
|
7
|
+
document: DocumentNode,
|
|
8
|
+
variables?: Record<string, unknown>,
|
|
9
|
+
): Observable<T> {
|
|
10
|
+
return defer(() => {
|
|
11
|
+
const svc = inject(GraphqlSubscriptionService);
|
|
12
|
+
return svc.subscribe<T>(document, variables);
|
|
13
|
+
});
|
|
14
|
+
}
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import { InjectionToken } from '@angular/core';
|
|
2
|
+
|
|
3
|
+
export interface SubscriptionsConfig {
|
|
4
|
+
wsUrl?: string;
|
|
5
|
+
connectionParams?: () => Record<string, unknown>;
|
|
6
|
+
reconnect?: boolean;
|
|
7
|
+
reconnectInterval?: number;
|
|
8
|
+
maxReconnectAttempts?: number;
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
export const SUBSCRIPTIONS_CONFIG = new InjectionToken<SubscriptionsConfig | null>(
|
|
12
|
+
'SUBSCRIPTIONS_CONFIG',
|
|
13
|
+
);
|
|
@@ -1,139 +0,0 @@
|
|
|
1
|
-
import * as i0 from '@angular/core';
|
|
2
|
-
import { inject, Injectable } from '@angular/core';
|
|
3
|
-
import { Observable, defer } from 'rxjs';
|
|
4
|
-
import { DUMBQL_CONFIG, print } from '@dumbql/core';
|
|
5
|
-
|
|
6
|
-
const WS_PROTOCOL = 'graphql-transport-ws';
|
|
7
|
-
function sendSubscriptionToExtension(payload) {
|
|
8
|
-
if (typeof window === 'undefined')
|
|
9
|
-
return;
|
|
10
|
-
window.postMessage({
|
|
11
|
-
source: 'dumb-keystore-graphql-debug',
|
|
12
|
-
type: 'graphql-subscription',
|
|
13
|
-
payload,
|
|
14
|
-
}, '*');
|
|
15
|
-
}
|
|
16
|
-
class GraphqlSubscriptionService {
|
|
17
|
-
config = inject(DUMBQL_CONFIG, { optional: true }) ?? { endpoint: '/graphql' };
|
|
18
|
-
wsUrl() {
|
|
19
|
-
const base = this.config.endpoint;
|
|
20
|
-
return base?.replace(/^http/, 'ws') ?? 'ws://localhost/graphql';
|
|
21
|
-
}
|
|
22
|
-
subscribe(document, variables) {
|
|
23
|
-
const query = print(document);
|
|
24
|
-
return new Observable((subscriber) => {
|
|
25
|
-
const url = this.wsUrl();
|
|
26
|
-
let ws;
|
|
27
|
-
let completed = false;
|
|
28
|
-
const subId = typeof crypto !== 'undefined' && crypto.randomUUID
|
|
29
|
-
? crypto.randomUUID()
|
|
30
|
-
: 'sub_' + Math.random().toString(36).substring(2, 9);
|
|
31
|
-
sendSubscriptionToExtension({
|
|
32
|
-
subId,
|
|
33
|
-
type: 'open',
|
|
34
|
-
url,
|
|
35
|
-
timestamp: Date.now(),
|
|
36
|
-
});
|
|
37
|
-
try {
|
|
38
|
-
ws = new WebSocket(url, WS_PROTOCOL);
|
|
39
|
-
}
|
|
40
|
-
catch (err) {
|
|
41
|
-
sendSubscriptionToExtension({ subId, type: 'error', timestamp: Date.now() });
|
|
42
|
-
subscriber.error(err);
|
|
43
|
-
return;
|
|
44
|
-
}
|
|
45
|
-
const connTimeout = setTimeout(() => {
|
|
46
|
-
if (!completed) {
|
|
47
|
-
ws.close();
|
|
48
|
-
sendSubscriptionToExtension({ subId, type: 'error', timestamp: Date.now() });
|
|
49
|
-
subscriber.error(new Error('WebSocket connection timeout'));
|
|
50
|
-
}
|
|
51
|
-
}, 10000);
|
|
52
|
-
ws.onopen = () => {
|
|
53
|
-
ws.send(JSON.stringify({ type: 'connection_init' }));
|
|
54
|
-
};
|
|
55
|
-
ws.onmessage = (event) => {
|
|
56
|
-
let msg;
|
|
57
|
-
try {
|
|
58
|
-
msg = JSON.parse(event.data);
|
|
59
|
-
}
|
|
60
|
-
catch {
|
|
61
|
-
return;
|
|
62
|
-
}
|
|
63
|
-
switch (msg.type) {
|
|
64
|
-
case 'connection_ack': {
|
|
65
|
-
clearTimeout(connTimeout);
|
|
66
|
-
ws.send(JSON.stringify({
|
|
67
|
-
type: 'subscribe',
|
|
68
|
-
id: subId,
|
|
69
|
-
payload: { query, variables },
|
|
70
|
-
}));
|
|
71
|
-
break;
|
|
72
|
-
}
|
|
73
|
-
case 'next': {
|
|
74
|
-
const payload = msg.payload;
|
|
75
|
-
if (payload.errors && payload.errors.length > 0) {
|
|
76
|
-
sendSubscriptionToExtension({ subId, type: 'error', timestamp: Date.now() });
|
|
77
|
-
subscriber.error(new Error(payload.errors[0].message));
|
|
78
|
-
}
|
|
79
|
-
else if (payload.data !== undefined) {
|
|
80
|
-
sendSubscriptionToExtension({ subId, type: 'next', payload: payload.data, timestamp: Date.now() });
|
|
81
|
-
subscriber.next(payload.data);
|
|
82
|
-
}
|
|
83
|
-
break;
|
|
84
|
-
}
|
|
85
|
-
case 'error': {
|
|
86
|
-
const payload = msg.payload;
|
|
87
|
-
sendSubscriptionToExtension({ subId, type: 'error', timestamp: Date.now() });
|
|
88
|
-
subscriber.error(new Error(payload?.message ?? 'Subscription error'));
|
|
89
|
-
break;
|
|
90
|
-
}
|
|
91
|
-
case 'complete': {
|
|
92
|
-
sendSubscriptionToExtension({ subId, type: 'complete', timestamp: Date.now() });
|
|
93
|
-
subscriber.complete();
|
|
94
|
-
break;
|
|
95
|
-
}
|
|
96
|
-
}
|
|
97
|
-
};
|
|
98
|
-
ws.onerror = () => {
|
|
99
|
-
sendSubscriptionToExtension({ subId, type: 'error', timestamp: Date.now() });
|
|
100
|
-
subscriber.error(new Error('WebSocket connection failed'));
|
|
101
|
-
};
|
|
102
|
-
ws.onclose = () => {
|
|
103
|
-
if (!completed) {
|
|
104
|
-
sendSubscriptionToExtension({ subId, type: 'close', timestamp: Date.now() });
|
|
105
|
-
subscriber.complete();
|
|
106
|
-
}
|
|
107
|
-
};
|
|
108
|
-
return () => {
|
|
109
|
-
completed = true;
|
|
110
|
-
clearTimeout(connTimeout);
|
|
111
|
-
if (ws.readyState === WebSocket.OPEN || ws.readyState === WebSocket.CONNECTING) {
|
|
112
|
-
ws.send(JSON.stringify({ type: 'complete', id: subId }));
|
|
113
|
-
ws.close(1000);
|
|
114
|
-
}
|
|
115
|
-
sendSubscriptionToExtension({ subId, type: 'close', timestamp: Date.now() });
|
|
116
|
-
};
|
|
117
|
-
});
|
|
118
|
-
}
|
|
119
|
-
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.0.4", ngImport: i0, type: GraphqlSubscriptionService, deps: [], target: i0.ɵɵFactoryTarget.Injectable });
|
|
120
|
-
static ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "22.0.4", ngImport: i0, type: GraphqlSubscriptionService, providedIn: 'root' });
|
|
121
|
-
}
|
|
122
|
-
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.0.4", ngImport: i0, type: GraphqlSubscriptionService, decorators: [{
|
|
123
|
-
type: Injectable,
|
|
124
|
-
args: [{ providedIn: 'root' }]
|
|
125
|
-
}] });
|
|
126
|
-
|
|
127
|
-
function subscribe(document, variables) {
|
|
128
|
-
return defer(() => {
|
|
129
|
-
const svc = inject(GraphqlSubscriptionService);
|
|
130
|
-
return svc.subscribe(document, variables);
|
|
131
|
-
});
|
|
132
|
-
}
|
|
133
|
-
|
|
134
|
-
/**
|
|
135
|
-
* Generated bundle index. Do not edit.
|
|
136
|
-
*/
|
|
137
|
-
|
|
138
|
-
export { GraphqlSubscriptionService, subscribe };
|
|
139
|
-
//# sourceMappingURL=dumbql-subscriptions.mjs.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"dumbql-subscriptions.mjs","sources":["../../../../projects/dumbql/subscriptions/src/lib/graphql-subscription.service.ts","../../../../projects/dumbql/subscriptions/src/lib/subscribe.ts","../../../../projects/dumbql/subscriptions/src/dumbql-subscriptions.ts"],"sourcesContent":["import { Injectable, inject } from '@angular/core';\nimport { Observable, Subscriber } from 'rxjs';\nimport { print, type DocumentNode, type DumbqlConfig, DUMBQL_CONFIG } from '@dumbql/core';\n\ninterface WsMessage {\n type: string;\n id?: string;\n payload?: unknown;\n}\n\nconst WS_PROTOCOL = 'graphql-transport-ws';\n\nfunction sendSubscriptionToExtension(payload: Record<string, unknown>): void {\n\tif (typeof window === 'undefined') return;\n\twindow.postMessage(\n\t\t{\n\t\t\tsource: 'dumb-keystore-graphql-debug',\n\t\t\ttype: 'graphql-subscription',\n\t\t\tpayload,\n\t\t},\n\t\t'*',\n\t);\n}\n\n@Injectable({ providedIn: 'root' })\nexport class GraphqlSubscriptionService {\n private readonly config: DumbqlConfig =\n inject(DUMBQL_CONFIG, { optional: true }) ?? { endpoint: '/graphql' } as DumbqlConfig;\n\n private wsUrl(): string {\n \tconst base = this.config.endpoint;\n \treturn base?.replace(/^http/, 'ws') ?? 'ws://localhost/graphql';\n }\n\n subscribe<T>(\n \tdocument: DocumentNode,\n \tvariables?: Record<string, unknown>,\n ): Observable<T> {\n \tconst query = print(document);\n\n \treturn new Observable<T>((subscriber: Subscriber<T>) => {\n \t\tconst url = this.wsUrl();\n \t\tlet ws: WebSocket;\n \t\tlet completed = false;\n \t\tconst subId =\n \t\t\ttypeof crypto !== 'undefined' && crypto.randomUUID\n \t\t\t\t? crypto.randomUUID()\n \t\t\t\t: 'sub_' + Math.random().toString(36).substring(2, 9);\n\n \t\tsendSubscriptionToExtension({\n \t\t\tsubId,\n \t\t\ttype: 'open',\n \t\t\turl,\n \t\t\ttimestamp: Date.now(),\n \t\t});\n\n \t\ttry {\n \t\t\tws = new WebSocket(url, WS_PROTOCOL);\n \t\t} catch (err) {\n \t\t\tsendSubscriptionToExtension({ subId, type: 'error', timestamp: Date.now() });\n \t\t\tsubscriber.error(err);\n \t\t\treturn;\n \t\t}\n\n \t\tconst connTimeout = setTimeout(() => {\n \t\t\tif (!completed) {\n \t\t\t\tws.close();\n \t\t\t\tsendSubscriptionToExtension({ subId, type: 'error', timestamp: Date.now() });\n \t\t\t\tsubscriber.error(new Error('WebSocket connection timeout'));\n \t\t\t}\n \t\t}, 10000);\n\n \t\tws.onopen = () => {\n \t\t\tws.send(JSON.stringify({ type: 'connection_init' }));\n \t\t};\n\n \t\tws.onmessage = (event: MessageEvent) => {\n \t\t\tlet msg: WsMessage;\n \t\t\ttry {\n \t\t\t\tmsg = JSON.parse(event.data) as WsMessage;\n \t\t\t} catch {\n \t\t\t\treturn;\n \t\t\t}\n\n \t\t\tswitch (msg.type) {\n\t\t\tcase 'connection_ack': {\n\t\t\t\tclearTimeout(connTimeout);\n\t\t\t\tws.send(JSON.stringify({\n\t\t\t\t\ttype: 'subscribe',\n\t\t\t\t\tid: subId,\n\t\t\t\t\tpayload: { query, variables },\n\t\t\t\t}));\n\t\t\t\tbreak;\n\t\t\t}\n \t\t\tcase 'next': {\n \t\t\t\tconst payload = msg.payload as { data?: T; errors?: { message: string }[] };\n \t\t\t\tif (payload.errors && payload.errors.length > 0) {\n \t\t\t\t\tsendSubscriptionToExtension({ subId, type: 'error', timestamp: Date.now() });\n \t\t\t\t\tsubscriber.error(new Error(payload.errors[0].message));\n \t\t\t\t} else if (payload.data !== undefined) {\n \t\t\t\t\tsendSubscriptionToExtension({ subId, type: 'next', payload: payload.data, timestamp: Date.now() });\n \t\t\t\t\tsubscriber.next(payload.data);\n \t\t\t\t}\n \t\t\t\tbreak;\n \t\t\t}\n \t\t\tcase 'error': {\n \t\t\t\tconst payload = msg.payload as { message?: string };\n \t\t\t\tsendSubscriptionToExtension({ subId, type: 'error', timestamp: Date.now() });\n \t\t\t\tsubscriber.error(new Error(payload?.message ?? 'Subscription error'));\n \t\t\t\tbreak;\n \t\t\t}\n \t\t\tcase 'complete': {\n \t\t\t\tsendSubscriptionToExtension({ subId, type: 'complete', timestamp: Date.now() });\n \t\t\t\tsubscriber.complete();\n \t\t\t\tbreak;\n \t\t\t}\n \t\t\t}\n \t\t};\n\n \t\tws.onerror = () => {\n \t\t\tsendSubscriptionToExtension({ subId, type: 'error', timestamp: Date.now() });\n \t\t\tsubscriber.error(new Error('WebSocket connection failed'));\n \t\t};\n\n \t\tws.onclose = () => {\n \t\t\tif (!completed) {\n \t\t\t\tsendSubscriptionToExtension({ subId, type: 'close', timestamp: Date.now() });\n \t\t\t\tsubscriber.complete();\n \t\t\t}\n \t\t};\n\n \t\treturn () => {\n \t\t\tcompleted = true;\n \t\t\tclearTimeout(connTimeout);\n\t\t\tif (ws.readyState === WebSocket.OPEN || ws.readyState === WebSocket.CONNECTING) {\n\t\t\t\tws.send(JSON.stringify({ type: 'complete', id: subId }));\n\t\t\t\tws.close(1000);\n\t\t\t}\n \t\t\tsendSubscriptionToExtension({ subId, type: 'close', timestamp: Date.now() });\n \t\t};\n \t});\n }\n}\n","import { inject } from '@angular/core';\nimport { defer, Observable } from 'rxjs';\nimport { GraphqlSubscriptionService } from './graphql-subscription.service';\nimport type { DocumentNode } from '@dumbql/core';\n\nexport function subscribe<T>(\n\tdocument: DocumentNode,\n\tvariables?: Record<string, unknown>,\n): Observable<T> {\n\treturn defer(() => {\n\t\tconst svc = inject(GraphqlSubscriptionService);\n\t\treturn svc.subscribe<T>(document, variables);\n\t});\n}\n","/**\n * Generated bundle index. Do not edit.\n */\n\nexport * from './public-api';\n"],"names":[],"mappings":"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"}
|
|
@@ -1,15 +0,0 @@
|
|
|
1
|
-
import { Observable } from 'rxjs';
|
|
2
|
-
import { DocumentNode } from '@dumbql/core';
|
|
3
|
-
import * as i0 from '@angular/core';
|
|
4
|
-
|
|
5
|
-
declare class GraphqlSubscriptionService {
|
|
6
|
-
private readonly config;
|
|
7
|
-
private wsUrl;
|
|
8
|
-
subscribe<T>(document: DocumentNode, variables?: Record<string, unknown>): Observable<T>;
|
|
9
|
-
static ɵfac: i0.ɵɵFactoryDeclaration<GraphqlSubscriptionService, never>;
|
|
10
|
-
static ɵprov: i0.ɵɵInjectableDeclaration<GraphqlSubscriptionService>;
|
|
11
|
-
}
|
|
12
|
-
|
|
13
|
-
declare function subscribe<T>(document: DocumentNode, variables?: Record<string, unknown>): Observable<T>;
|
|
14
|
-
|
|
15
|
-
export { GraphqlSubscriptionService, subscribe };
|