@fabriktor/fx 0.0.101 → 0.0.102
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.
|
@@ -26,6 +26,8 @@ export class ChatSendQueue {
|
|
|
26
26
|
listeners = new Set();
|
|
27
27
|
queue;
|
|
28
28
|
uploads = new Set();
|
|
29
|
+
sends = new Map();
|
|
30
|
+
cancellations = new Set();
|
|
29
31
|
writes = Promise.resolve();
|
|
30
32
|
pendingSaves = new Map();
|
|
31
33
|
error;
|
|
@@ -213,38 +215,55 @@ export class ChatSendQueue {
|
|
|
213
215
|
await this.refresh();
|
|
214
216
|
}
|
|
215
217
|
async remove(id) {
|
|
216
|
-
const
|
|
218
|
+
const owner = this.owner, signal = this.controller.signal;
|
|
219
|
+
let entry = this.entries.find((value) => value.id === id);
|
|
217
220
|
if (!entry ||
|
|
218
|
-
!this.
|
|
219
|
-
entry
|
|
220
|
-
|
|
221
|
-
entry.status === ChatSendStatus.Uploading) {
|
|
221
|
+
!this.current(owner, signal) ||
|
|
222
|
+
!chatSendCanRemove(entry) ||
|
|
223
|
+
this.cancellations.has(id)) {
|
|
222
224
|
return;
|
|
223
225
|
}
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
this.
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
if (!this.current(owner, signal))
|
|
226
|
+
this.cancellations.add(id);
|
|
227
|
+
try {
|
|
228
|
+
// Finish a pending local save before removing its durable record. The
|
|
229
|
+
// cancellation flag prevents this tab from starting that message meanwhile.
|
|
230
|
+
if (entry.status === ChatSendStatus.Saving) {
|
|
231
|
+
await this.writes;
|
|
232
|
+
entry = this.entries.find((value) => value.id === id);
|
|
233
|
+
}
|
|
234
|
+
if (!entry || !this.current(owner, signal) || !chatSendCanRemove(entry)) {
|
|
234
235
|
return;
|
|
235
|
-
|
|
236
|
-
|
|
236
|
+
}
|
|
237
|
+
// Cancel only this request. A server response that already arrived wins;
|
|
238
|
+
// the shared room lock still protects removal from another tab's sender.
|
|
239
|
+
this.sends.get(id)?.abort();
|
|
240
|
+
if (entry.sequence === undefined) {
|
|
241
|
+
this.pendingSaves.delete(id);
|
|
242
|
+
this.entries = this.entries.filter((value) => value.id !== id);
|
|
243
|
+
this.publish();
|
|
244
|
+
this.persist();
|
|
237
245
|
return;
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
246
|
+
}
|
|
247
|
+
const removeStored = async () => {
|
|
248
|
+
if (!this.current(owner, signal))
|
|
249
|
+
return;
|
|
250
|
+
const stored = (await this.opts.store.list(owner)).find((value) => value.id === id);
|
|
251
|
+
if (stored && !chatSendCanRemove(stored))
|
|
252
|
+
return;
|
|
253
|
+
await this.opts.store.remove(id);
|
|
254
|
+
this.entries = this.entries.filter((value) => value.id !== id);
|
|
255
|
+
this.publish();
|
|
256
|
+
this.opts.changed?.();
|
|
257
|
+
};
|
|
258
|
+
// Upload retries use their own lock so a slow multipart never holds the
|
|
259
|
+
// message lane. Discard must coordinate with that same lock.
|
|
260
|
+
await this.opts.lock(entry.chat ? this.uploadLock(entry) : this.roomLock(entry), signal, removeStored);
|
|
261
|
+
}
|
|
262
|
+
finally {
|
|
263
|
+
this.cancellations.delete(id);
|
|
264
|
+
this.persist();
|
|
265
|
+
await this.refresh();
|
|
266
|
+
}
|
|
248
267
|
}
|
|
249
268
|
work(room, owner, signal) {
|
|
250
269
|
this.queue.process({
|
|
@@ -264,7 +283,7 @@ export class ChatSendQueue {
|
|
|
264
283
|
}
|
|
265
284
|
},
|
|
266
285
|
run: async (entry) => {
|
|
267
|
-
if (!this.current(owner, signal)) {
|
|
286
|
+
if (!this.current(owner, signal) || this.cancellations.has(entry.id)) {
|
|
268
287
|
return false;
|
|
269
288
|
}
|
|
270
289
|
if (entry.status === ChatSendStatus.Sending ||
|
|
@@ -278,6 +297,9 @@ export class ChatSendQueue {
|
|
|
278
297
|
return false;
|
|
279
298
|
}
|
|
280
299
|
let current = entry;
|
|
300
|
+
const request = new AbortController();
|
|
301
|
+
const requestSignal = AbortSignal.any([signal, request.signal]);
|
|
302
|
+
this.sends.set(entry.id, request);
|
|
281
303
|
try {
|
|
282
304
|
current = {
|
|
283
305
|
...entry,
|
|
@@ -289,9 +311,10 @@ export class ChatSendQueue {
|
|
|
289
311
|
return false;
|
|
290
312
|
}
|
|
291
313
|
if (!current.server_id) {
|
|
314
|
+
requestSignal.throwIfAborted();
|
|
292
315
|
const out = await this.opts.chat.insertOneChat({
|
|
293
316
|
in: current.input,
|
|
294
|
-
signal,
|
|
317
|
+
signal: requestSignal,
|
|
295
318
|
idempotency_key: current.id,
|
|
296
319
|
});
|
|
297
320
|
current = {
|
|
@@ -339,7 +362,7 @@ export class ChatSendQueue {
|
|
|
339
362
|
try {
|
|
340
363
|
await this.save({
|
|
341
364
|
...current,
|
|
342
|
-
status:
|
|
365
|
+
status: requestSignal.aborted
|
|
343
366
|
? ChatSendStatus.Interrupted
|
|
344
367
|
: ChatSendStatus.Failed,
|
|
345
368
|
});
|
|
@@ -347,12 +370,15 @@ export class ChatSendQueue {
|
|
|
347
370
|
catch (storageError) {
|
|
348
371
|
this.error = storageError;
|
|
349
372
|
}
|
|
350
|
-
if (this.current(owner, signal)) {
|
|
373
|
+
if (this.current(owner, signal) && !request.signal.aborted) {
|
|
351
374
|
this.error = err;
|
|
352
375
|
this.publish();
|
|
353
376
|
}
|
|
354
377
|
return false;
|
|
355
378
|
}
|
|
379
|
+
finally {
|
|
380
|
+
this.sends.delete(entry.id);
|
|
381
|
+
}
|
|
356
382
|
return true;
|
|
357
383
|
},
|
|
358
384
|
});
|
|
@@ -518,5 +544,11 @@ export function chatSendNeedsRetry(entry) {
|
|
|
518
544
|
entry.status === ChatSendStatus.UploadInterrupted);
|
|
519
545
|
}
|
|
520
546
|
export function chatSendCanRemove(entry) {
|
|
521
|
-
|
|
547
|
+
if (!entry.chat && entry.server_id) {
|
|
548
|
+
return false;
|
|
549
|
+
}
|
|
550
|
+
return (entry.status === ChatSendStatus.Saving ||
|
|
551
|
+
entry.status === ChatSendStatus.Queued ||
|
|
552
|
+
entry.status === ChatSendStatus.Sending ||
|
|
553
|
+
chatSendNeedsRetry(entry));
|
|
522
554
|
}
|