wirejs-deploy-amplify-basic 0.0.118-realtime → 0.0.119-realtime
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/dist/client/realtime.js
CHANGED
|
@@ -100,37 +100,53 @@ export function subscribe(url, channel, token, authHost, subscriber) {
|
|
|
100
100
|
}
|
|
101
101
|
}
|
|
102
102
|
};
|
|
103
|
-
const
|
|
103
|
+
const cleanup = (reason) => {
|
|
104
|
+
// remove the top-level connection entry *first* so that subsequent
|
|
105
|
+
// subscribers don't try to access a closed WebSocket. they will instead
|
|
106
|
+
// see no connection and a new one will be created.
|
|
107
|
+
connections.delete(urlKey);
|
|
108
|
+
// next. we need to identify all subscriptions to notify -- but wait to notify
|
|
109
|
+
// until after we have removed them from the look-up table so that the callbacks
|
|
110
|
+
// don't interfere with the bookkeeping.
|
|
104
111
|
const subscriptionIds = Array.from(channelSubIdLUT.entries())
|
|
105
112
|
.filter(([urlChannel, _subId]) => urlChannel.startsWith(`${url}#`))
|
|
106
113
|
.map(([_urlChannel, subId]) => subscriptionIdString(subId));
|
|
114
|
+
const subs = [];
|
|
115
|
+
for (const subscriptionId of subscriptionIds) {
|
|
116
|
+
subs.concat(subIdSubscribers.get(subscriptionId));
|
|
117
|
+
}
|
|
118
|
+
// same story with channel names.
|
|
119
|
+
const fullChannelNames = Array.from(channelSubIdLUT.keys())
|
|
120
|
+
.filter(k => k.startsWith(`${url}#`));
|
|
121
|
+
// now that we've *identified* everything that requires bookkeeping and
|
|
122
|
+
// notification, we do the bookkeeping ONLY.
|
|
107
123
|
for (const subscriptionId of subscriptionIds) {
|
|
108
|
-
const subs = subIdSubscribers.get(subscriptionId);
|
|
109
|
-
if (subs) {
|
|
110
|
-
for (const subscriber of subs) {
|
|
111
|
-
if (subscriber.onclose) {
|
|
112
|
-
try {
|
|
113
|
-
subscriber.onclose(reason);
|
|
114
|
-
}
|
|
115
|
-
catch (error) {
|
|
116
|
-
console.error('Error in subscriber onclose:', error);
|
|
117
|
-
}
|
|
118
|
-
}
|
|
119
|
-
}
|
|
120
|
-
}
|
|
121
124
|
subIdSubscribers.delete(subscriptionId);
|
|
122
125
|
subscriptionState.delete(subscriptionId);
|
|
123
126
|
}
|
|
124
|
-
|
|
125
|
-
|
|
127
|
+
for (const fullChannelName of fullChannelNames) {
|
|
128
|
+
channelSubIdLUT.delete(fullChannelName);
|
|
129
|
+
}
|
|
130
|
+
// all bookkeeping is done, now we can notify subscribers.
|
|
131
|
+
// they can no longer interfere with the bookkeeping.
|
|
132
|
+
for (const subscriber of subs || []) {
|
|
133
|
+
try {
|
|
134
|
+
subscriber.onclose?.(reason);
|
|
135
|
+
}
|
|
136
|
+
catch (error) {
|
|
137
|
+
console.error('Error in subscriber onclose:', error);
|
|
138
|
+
}
|
|
139
|
+
}
|
|
126
140
|
console.debug('closed', ws);
|
|
127
141
|
};
|
|
128
|
-
ws.onclose = () =>
|
|
129
|
-
ws.onerror = () =>
|
|
142
|
+
ws.onclose = () => cleanup('closed');
|
|
143
|
+
ws.onerror = () => cleanup('error');
|
|
130
144
|
}
|
|
131
145
|
if (!channelSubIdLUT.has(fullChannelName)) {
|
|
132
146
|
const subscriptionId = subscriptionIdString(crypto.randomUUID());
|
|
133
147
|
subscriptionState.set(subscriptionId, 'connecting');
|
|
148
|
+
channelSubIdLUT.set(fullChannelName, subscriptionId);
|
|
149
|
+
subIdSubscribers.set(subscriptionId, [subscriber]);
|
|
134
150
|
const ws = connections.get(urlKey);
|
|
135
151
|
const subscribe = () => {
|
|
136
152
|
ws.send(JSON.stringify({
|
|
@@ -139,8 +155,6 @@ export function subscribe(url, channel, token, authHost, subscriber) {
|
|
|
139
155
|
channel,
|
|
140
156
|
authorization
|
|
141
157
|
}));
|
|
142
|
-
channelSubIdLUT.set(fullChannelName, subscriptionId);
|
|
143
|
-
subIdSubscribers.set(subscriptionId, [subscriber]);
|
|
144
158
|
};
|
|
145
159
|
if (ws.readyState === WebSocket.OPEN) {
|
|
146
160
|
subscribe();
|
|
@@ -151,9 +165,21 @@ export function subscribe(url, channel, token, authHost, subscriber) {
|
|
|
151
165
|
}
|
|
152
166
|
else {
|
|
153
167
|
const subscriptionId = channelSubIdLUT.get(fullChannelName);
|
|
154
|
-
|
|
168
|
+
if (!subscriptionId) {
|
|
169
|
+
throw new Error(`No subscription ID found for channel: ${fullChannelName}`);
|
|
170
|
+
}
|
|
171
|
+
const subs = subIdSubscribers.get(subscriptionId);
|
|
172
|
+
if (!subs) {
|
|
173
|
+
throw new Error(`No subscriber list found for subscription ID: ${subscriptionId}`);
|
|
174
|
+
}
|
|
175
|
+
subs?.push(subscriber);
|
|
155
176
|
if (subscriptionState.get(subscriptionId) === 'open') {
|
|
156
|
-
|
|
177
|
+
try {
|
|
178
|
+
subscriber.onopen?.();
|
|
179
|
+
}
|
|
180
|
+
catch (error) {
|
|
181
|
+
console.error('Error in subscriber onopen:', error);
|
|
182
|
+
}
|
|
157
183
|
}
|
|
158
184
|
}
|
|
159
185
|
}
|
|
@@ -165,27 +191,48 @@ export function unsubscribe(url, channel, subscriber) {
|
|
|
165
191
|
const subs = subId && subIdSubscribers.get(subId);
|
|
166
192
|
const sub = subs ? subs.find(s => s === subscriber) : undefined;
|
|
167
193
|
if (sub && subs) {
|
|
168
|
-
|
|
194
|
+
try {
|
|
195
|
+
sub.onclose?.('unsubscribed');
|
|
196
|
+
}
|
|
197
|
+
catch (error) {
|
|
198
|
+
console.error('Error in subscriber onclose:', error);
|
|
199
|
+
}
|
|
169
200
|
const i = subs.indexOf(sub);
|
|
170
201
|
if (i > -1)
|
|
171
202
|
subs.splice(i, 1);
|
|
172
203
|
}
|
|
173
204
|
if (subs && subs.length === 0) {
|
|
174
205
|
// No subIdSubscribers left for this channel. We can unsubscribe from channel.
|
|
206
|
+
subscriptionState.delete(subId);
|
|
207
|
+
subIdSubscribers.delete(subId);
|
|
208
|
+
channelSubIdLUT.delete(fullChannelName);
|
|
175
209
|
ws?.send(JSON.stringify({
|
|
176
210
|
id: subId,
|
|
177
211
|
type: 'unsubscribe',
|
|
178
212
|
}));
|
|
179
|
-
subscriptionState.delete(subId);
|
|
180
|
-
subIdSubscribers.delete(subId);
|
|
181
|
-
channelSubIdLUT.delete(fullChannelName);
|
|
182
213
|
}
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
214
|
+
// debounce closing the WebSocket connection for use-cases where a client
|
|
215
|
+
// might just be changing channels. ws should only be closed if there are
|
|
216
|
+
// no more subscriptions running over the socket.
|
|
217
|
+
setTimeout(() => {
|
|
218
|
+
const socketSubs = Array.from(channelSubIdLUT.keys())
|
|
219
|
+
.filter(k => k.startsWith(`${url}#`));
|
|
220
|
+
if (socketSubs.length === 0) {
|
|
221
|
+
// No channels left for this URL. We can close the WebSocket connection.
|
|
222
|
+
connections.delete(urlKey);
|
|
223
|
+
ws?.close();
|
|
224
|
+
console.debug('closed', ws);
|
|
225
|
+
}
|
|
226
|
+
}, 5000);
|
|
227
|
+
// tell the subscriber that they have been unsubscribed as the last step so
|
|
228
|
+
// they can clean up any resources they have allocated without interfering
|
|
229
|
+
// with the preceding bookkeeping.
|
|
230
|
+
if (sub) {
|
|
231
|
+
try {
|
|
232
|
+
sub.onclose?.('unsubscribed');
|
|
233
|
+
}
|
|
234
|
+
catch (error) {
|
|
235
|
+
console.error('Error in subscriber onclose:', error);
|
|
236
|
+
}
|
|
190
237
|
}
|
|
191
238
|
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "wirejs-deploy-amplify-basic",
|
|
3
|
-
"version": "0.0.
|
|
3
|
+
"version": "0.0.119-realtime",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"main": "./dist/index.js",
|
|
6
6
|
"types": "./dist/index.d.ts",
|
|
@@ -41,7 +41,7 @@
|
|
|
41
41
|
"recursive-copy": "^2.0.14",
|
|
42
42
|
"rimraf": "^6.0.1",
|
|
43
43
|
"wirejs-dom": "^1.0.41",
|
|
44
|
-
"wirejs-resources": "^0.1.
|
|
44
|
+
"wirejs-resources": "^0.1.87-realtime"
|
|
45
45
|
},
|
|
46
46
|
"devDependencies": {
|
|
47
47
|
"@aws-amplify/backend": "^1.14.0",
|