pulse-js-framework 1.10.0 → 1.10.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/compiler/parser/_extract.js +393 -0
- package/compiler/parser/blocks.js +361 -0
- package/compiler/parser/core.js +306 -0
- package/compiler/parser/expressions.js +386 -0
- package/compiler/parser/imports.js +108 -0
- package/compiler/parser/index.js +47 -0
- package/compiler/parser/state.js +155 -0
- package/compiler/parser/style.js +445 -0
- package/compiler/parser/view.js +632 -0
- package/compiler/parser.js +15 -2372
- package/compiler/parser.js.original +2376 -0
- package/package.json +2 -1
- package/runtime/a11y/announcements.js +213 -0
- package/runtime/a11y/contrast.js +125 -0
- package/runtime/a11y/focus.js +412 -0
- package/runtime/a11y/index.js +35 -0
- package/runtime/a11y/preferences.js +121 -0
- package/runtime/a11y/utils.js +164 -0
- package/runtime/a11y/validation.js +258 -0
- package/runtime/a11y/widgets.js +545 -0
- package/runtime/a11y.js +15 -1840
- package/runtime/a11y.js.original +1844 -0
- package/runtime/graphql/cache.js +69 -0
- package/runtime/graphql/client.js +563 -0
- package/runtime/graphql/hooks.js +492 -0
- package/runtime/graphql/index.js +62 -0
- package/runtime/graphql/subscriptions.js +241 -0
- package/runtime/graphql.js +12 -1322
- package/runtime/graphql.js.original +1326 -0
- package/runtime/router/core.js +956 -0
- package/runtime/router/guards.js +90 -0
- package/runtime/router/history.js +204 -0
- package/runtime/router/index.js +36 -0
- package/runtime/router/lazy.js +180 -0
- package/runtime/router/utils.js +226 -0
- package/runtime/router.js +12 -1600
- package/runtime/router.js.original +1605 -0
|
@@ -0,0 +1,241 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Pulse GraphQL - WebSocket Subscriptions
|
|
3
|
+
*
|
|
4
|
+
* Handles GraphQL subscriptions using the graphql-ws protocol
|
|
5
|
+
*
|
|
6
|
+
* @module pulse-js-framework/runtime/graphql/subscriptions
|
|
7
|
+
*/
|
|
8
|
+
|
|
9
|
+
import { pulse, effect, onCleanup } from '../pulse.js';
|
|
10
|
+
import { createWebSocket, WebSocketError } from '../websocket.js';
|
|
11
|
+
import { ClientError } from '../errors.js';
|
|
12
|
+
|
|
13
|
+
// ============================================================================
|
|
14
|
+
// Constants
|
|
15
|
+
// ============================================================================
|
|
16
|
+
|
|
17
|
+
/**
|
|
18
|
+
* graphql-ws protocol message types
|
|
19
|
+
* @see https://github.com/enisdenjo/graphql-ws/blob/master/PROTOCOL.md
|
|
20
|
+
*/
|
|
21
|
+
export const MessageType = {
|
|
22
|
+
// Client -> Server
|
|
23
|
+
ConnectionInit: 'connection_init',
|
|
24
|
+
Subscribe: 'subscribe',
|
|
25
|
+
Complete: 'complete',
|
|
26
|
+
Ping: 'ping',
|
|
27
|
+
Pong: 'pong',
|
|
28
|
+
|
|
29
|
+
// Server -> Client
|
|
30
|
+
ConnectionAck: 'connection_ack',
|
|
31
|
+
Next: 'next',
|
|
32
|
+
Error: 'error'
|
|
33
|
+
// Complete is bidirectional
|
|
34
|
+
};
|
|
35
|
+
|
|
36
|
+
// ============================================================================
|
|
37
|
+
// Subscription Manager
|
|
38
|
+
// ============================================================================
|
|
39
|
+
|
|
40
|
+
/**
|
|
41
|
+
* Manages GraphQL subscriptions over WebSocket
|
|
42
|
+
*/
|
|
43
|
+
export class SubscriptionManager {
|
|
44
|
+
#ws = null;
|
|
45
|
+
#subscriptions = new Map();
|
|
46
|
+
#nextId = 1;
|
|
47
|
+
#connectionParams;
|
|
48
|
+
#connected = pulse(false);
|
|
49
|
+
#pending = [];
|
|
50
|
+
|
|
51
|
+
/**
|
|
52
|
+
* @param {Object} ws - WebSocket instance from createWebSocket
|
|
53
|
+
* @param {Object|Function} [connectionParams] - Connection parameters
|
|
54
|
+
*/
|
|
55
|
+
constructor(ws, connectionParams) {
|
|
56
|
+
this.#ws = ws;
|
|
57
|
+
this.#connectionParams = connectionParams;
|
|
58
|
+
|
|
59
|
+
// Handle incoming messages
|
|
60
|
+
ws.on('message', (msg) => this.#handleMessage(msg));
|
|
61
|
+
ws.on('open', () => this.#handleOpen());
|
|
62
|
+
ws.on('close', () => this.#handleClose());
|
|
63
|
+
ws.on('error', (err) => this.#handleError(err));
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
/**
|
|
67
|
+
* Get connection state
|
|
68
|
+
*/
|
|
69
|
+
get connected() {
|
|
70
|
+
return this.#connected;
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
/**
|
|
74
|
+
* Handle WebSocket open
|
|
75
|
+
*/
|
|
76
|
+
async #handleOpen() {
|
|
77
|
+
// Send connection_init
|
|
78
|
+
const params = typeof this.#connectionParams === 'function'
|
|
79
|
+
? await this.#connectionParams()
|
|
80
|
+
: this.#connectionParams;
|
|
81
|
+
|
|
82
|
+
this.#ws.send({
|
|
83
|
+
type: MessageType.ConnectionInit,
|
|
84
|
+
payload: params || {}
|
|
85
|
+
});
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
/**
|
|
89
|
+
* Handle WebSocket close
|
|
90
|
+
*/
|
|
91
|
+
#handleClose() {
|
|
92
|
+
this.#connected.set(false);
|
|
93
|
+
// Notify all active subscriptions
|
|
94
|
+
for (const [id, sub] of this.#subscriptions) {
|
|
95
|
+
sub.handlers.onError?.(new GraphQLError('Connection closed', {
|
|
96
|
+
code: 'SUBSCRIPTION_ERROR'
|
|
97
|
+
}));
|
|
98
|
+
}
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
/**
|
|
102
|
+
* Handle WebSocket error
|
|
103
|
+
*/
|
|
104
|
+
#handleError(error) {
|
|
105
|
+
for (const [id, sub] of this.#subscriptions) {
|
|
106
|
+
sub.handlers.onError?.(new GraphQLError(error.message || 'WebSocket error', {
|
|
107
|
+
code: 'SUBSCRIPTION_ERROR'
|
|
108
|
+
}));
|
|
109
|
+
}
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
/**
|
|
113
|
+
* Handle incoming WebSocket message
|
|
114
|
+
* @param {Object} message - Parsed message
|
|
115
|
+
*/
|
|
116
|
+
#handleMessage(message) {
|
|
117
|
+
const { id, type, payload } = message;
|
|
118
|
+
|
|
119
|
+
switch (type) {
|
|
120
|
+
case MessageType.ConnectionAck:
|
|
121
|
+
this.#connected.set(true);
|
|
122
|
+
// Send any pending subscriptions
|
|
123
|
+
for (const pending of this.#pending) {
|
|
124
|
+
this.#ws.send(pending);
|
|
125
|
+
}
|
|
126
|
+
this.#pending = [];
|
|
127
|
+
break;
|
|
128
|
+
|
|
129
|
+
case MessageType.Next: {
|
|
130
|
+
const sub = this.#subscriptions.get(id);
|
|
131
|
+
if (sub) {
|
|
132
|
+
sub.handlers.onData?.(payload.data);
|
|
133
|
+
}
|
|
134
|
+
break;
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
case MessageType.Error: {
|
|
138
|
+
const sub = this.#subscriptions.get(id);
|
|
139
|
+
if (sub) {
|
|
140
|
+
sub.handlers.onError?.(new GraphQLError('Subscription error', {
|
|
141
|
+
code: 'SUBSCRIPTION_ERROR',
|
|
142
|
+
errors: Array.isArray(payload) ? payload : [payload]
|
|
143
|
+
}));
|
|
144
|
+
}
|
|
145
|
+
break;
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
case MessageType.Complete: {
|
|
149
|
+
const sub = this.#subscriptions.get(id);
|
|
150
|
+
if (sub) {
|
|
151
|
+
sub.handlers.onComplete?.();
|
|
152
|
+
this.#subscriptions.delete(id);
|
|
153
|
+
}
|
|
154
|
+
break;
|
|
155
|
+
}
|
|
156
|
+
|
|
157
|
+
case MessageType.Ping:
|
|
158
|
+
this.#ws.send({ type: MessageType.Pong });
|
|
159
|
+
break;
|
|
160
|
+
}
|
|
161
|
+
}
|
|
162
|
+
|
|
163
|
+
/**
|
|
164
|
+
* Subscribe to a GraphQL subscription
|
|
165
|
+
* @param {string} query - GraphQL subscription query
|
|
166
|
+
* @param {Object} [variables] - Subscription variables
|
|
167
|
+
* @param {Object} handlers - Event handlers
|
|
168
|
+
* @returns {Function} Unsubscribe function
|
|
169
|
+
*/
|
|
170
|
+
subscribe(query, variables, handlers) {
|
|
171
|
+
const id = String(this.#nextId++);
|
|
172
|
+
|
|
173
|
+
const message = {
|
|
174
|
+
id,
|
|
175
|
+
type: MessageType.Subscribe,
|
|
176
|
+
payload: {
|
|
177
|
+
query,
|
|
178
|
+
variables,
|
|
179
|
+
operationName: extractOperationName(query)
|
|
180
|
+
}
|
|
181
|
+
};
|
|
182
|
+
|
|
183
|
+
// Store subscription
|
|
184
|
+
this.#subscriptions.set(id, {
|
|
185
|
+
query,
|
|
186
|
+
variables,
|
|
187
|
+
handlers
|
|
188
|
+
});
|
|
189
|
+
|
|
190
|
+
// Send or queue the subscription message
|
|
191
|
+
if (this.#connected.get()) {
|
|
192
|
+
this.#ws.send(message);
|
|
193
|
+
} else {
|
|
194
|
+
this.#pending.push(message);
|
|
195
|
+
// Ensure WebSocket is connecting
|
|
196
|
+
if (this.#ws.state.get() === 'closed') {
|
|
197
|
+
this.#ws.connect();
|
|
198
|
+
}
|
|
199
|
+
}
|
|
200
|
+
|
|
201
|
+
// Return unsubscribe function
|
|
202
|
+
return () => {
|
|
203
|
+
if (this.#subscriptions.has(id)) {
|
|
204
|
+
this.#subscriptions.delete(id);
|
|
205
|
+
if (this.#connected.get()) {
|
|
206
|
+
this.#ws.send({ id, type: MessageType.Complete });
|
|
207
|
+
}
|
|
208
|
+
}
|
|
209
|
+
};
|
|
210
|
+
}
|
|
211
|
+
|
|
212
|
+
/**
|
|
213
|
+
* Get active subscription count
|
|
214
|
+
* @returns {number}
|
|
215
|
+
*/
|
|
216
|
+
get activeCount() {
|
|
217
|
+
return this.#subscriptions.size;
|
|
218
|
+
}
|
|
219
|
+
|
|
220
|
+
/**
|
|
221
|
+
* Close all subscriptions
|
|
222
|
+
*/
|
|
223
|
+
closeAll() {
|
|
224
|
+
for (const id of this.#subscriptions.keys()) {
|
|
225
|
+
if (this.#connected.get()) {
|
|
226
|
+
this.#ws.send({ id, type: MessageType.Complete });
|
|
227
|
+
}
|
|
228
|
+
}
|
|
229
|
+
this.#subscriptions.clear();
|
|
230
|
+
}
|
|
231
|
+
|
|
232
|
+
/**
|
|
233
|
+
* Dispose the subscription manager
|
|
234
|
+
*/
|
|
235
|
+
dispose() {
|
|
236
|
+
this.closeAll();
|
|
237
|
+
this.#ws.dispose();
|
|
238
|
+
}
|
|
239
|
+
}
|
|
240
|
+
|
|
241
|
+
|