@fdcn/sourcefed 0.2.4 → 0.2.6
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 +2 -2
- package/dist/{chunk-SKEBKW2L.js → chunk-5NFCKXDQ.js} +51 -71
- package/dist/cli.js +51 -71
- package/dist/daemon.js +51 -71
- package/dist/mcp.js +1 -1
- package/dist/opencode.js +20 -8
- package/dist/pi.js +3 -11
- package/dist.dts/adapters/opencode/bridge.d.ts +1 -0
- package/package.json +1 -1
- package/dist/chunk-7KHZCWGQ.js +0 -5174
- package/dist/chunk-B7T6IZYS.js +0 -5155
- package/dist/chunk-EVSHVYPF.js +0 -5167
- package/dist/chunk-UOTCX4ME.js +0 -3963
- package/dist.dts/packages/cli/src/commands/monitor.d.ts +0 -1
package/README.md
CHANGED
|
@@ -33,12 +33,12 @@ OpenCode loads this package as both a server and a TUI plugin:
|
|
|
33
33
|
|
|
34
34
|
```json
|
|
35
35
|
// opencode.json
|
|
36
|
-
{ "plugin": ["@fdcn/sourcefed@0.2.
|
|
36
|
+
{ "plugin": ["@fdcn/sourcefed@0.2.6"] }
|
|
37
37
|
```
|
|
38
38
|
|
|
39
39
|
```json
|
|
40
40
|
// tui.json
|
|
41
|
-
{ "plugin": ["@fdcn/sourcefed@0.2.
|
|
41
|
+
{ "plugin": ["@fdcn/sourcefed@0.2.6"] }
|
|
42
42
|
```
|
|
43
43
|
|
|
44
44
|
The package exposes `./server`, `./tui`, `./opencode`, `./opencode/tui`, and `./pi` entrypoints for hosts that load them directly.
|
|
@@ -11638,11 +11638,16 @@ var SourcefedDaemon = class {
|
|
|
11638
11638
|
this.listeners.set(key, listeners);
|
|
11639
11639
|
}
|
|
11640
11640
|
listeners.add(onEvents);
|
|
11641
|
+
this.emit(target);
|
|
11641
11642
|
return () => {
|
|
11642
11643
|
listeners.delete(onEvents);
|
|
11644
|
+
const receivedByListener = this.received.get(key);
|
|
11645
|
+
const released = receivedByListener?.delete(onEvents) ?? false;
|
|
11643
11646
|
if (listeners.size === 0) {
|
|
11644
11647
|
this.listeners.delete(key);
|
|
11645
11648
|
this.received.delete(key);
|
|
11649
|
+
} else if (released) {
|
|
11650
|
+
this.emit(target);
|
|
11646
11651
|
}
|
|
11647
11652
|
};
|
|
11648
11653
|
}
|
|
@@ -11678,23 +11683,28 @@ var SourcefedDaemon = class {
|
|
|
11678
11683
|
receivedByListener = /* @__PURE__ */ new Map();
|
|
11679
11684
|
this.received.set(key, receivedByListener);
|
|
11680
11685
|
}
|
|
11681
|
-
const
|
|
11686
|
+
const claimed = /* @__PURE__ */ new Set();
|
|
11687
|
+
for (const received of receivedByListener.values()) {
|
|
11688
|
+
for (const id of received) claimed.add(id);
|
|
11689
|
+
}
|
|
11690
|
+
const fresh = queued.filter((event) => !claimed.has(event.id));
|
|
11691
|
+
if (fresh.length === 0) return;
|
|
11692
|
+
for (const listener of [...listeners]) {
|
|
11682
11693
|
let received = receivedByListener.get(listener);
|
|
11683
11694
|
if (!received) {
|
|
11684
11695
|
received = /* @__PURE__ */ new Set();
|
|
11685
11696
|
receivedByListener.set(listener, received);
|
|
11686
11697
|
}
|
|
11687
|
-
const fresh = queued.filter((event) => !received.has(event.id));
|
|
11688
|
-
if (fresh.length === 0) return;
|
|
11689
11698
|
for (const event of fresh) received.add(event.id);
|
|
11690
11699
|
try {
|
|
11691
11700
|
await listener(fresh);
|
|
11701
|
+
return;
|
|
11692
11702
|
} catch (error) {
|
|
11693
11703
|
console.error(`[sourcefed] event delivery failed: ${error instanceof Error ? error.message : String(error)}`);
|
|
11694
11704
|
for (const event of fresh) received.delete(event.id);
|
|
11705
|
+
if (received.size === 0) receivedByListener.delete(listener);
|
|
11695
11706
|
}
|
|
11696
|
-
}
|
|
11697
|
-
await Promise.all(listenerResults);
|
|
11707
|
+
}
|
|
11698
11708
|
}
|
|
11699
11709
|
};
|
|
11700
11710
|
function monitorView(monitor) {
|
|
@@ -11803,72 +11813,48 @@ var HttpDaemonClient = class {
|
|
|
11803
11813
|
const subscribed = new Promise((resolve) => {
|
|
11804
11814
|
markSubscribed = () => resolve();
|
|
11805
11815
|
});
|
|
11806
|
-
let drainComplete = false;
|
|
11807
|
-
let draining = false;
|
|
11808
|
-
let redrainRequested = false;
|
|
11809
|
-
let retryTimer;
|
|
11810
|
-
let markDrained;
|
|
11811
|
-
const drained = new Promise((resolve) => {
|
|
11812
|
-
markDrained = resolve;
|
|
11813
|
-
});
|
|
11814
|
-
let buffered = [];
|
|
11815
11816
|
let delivering = Promise.resolve();
|
|
11817
|
+
let activeStream;
|
|
11818
|
+
let retryStream = false;
|
|
11816
11819
|
const deliver = (events) => {
|
|
11817
11820
|
if (!callbacks?.has(onEvents) || events.length === 0) return Promise.resolve();
|
|
11818
|
-
delivering = delivering.then(
|
|
11821
|
+
delivering = delivering.then(async () => {
|
|
11822
|
+
try {
|
|
11823
|
+
await onEvents(events);
|
|
11824
|
+
} catch (error) {
|
|
11825
|
+
console.error(`[sourcefed] event delivery failed: ${error instanceof Error ? error.message : String(error)}`);
|
|
11826
|
+
retryStream = true;
|
|
11827
|
+
activeStream?.abort();
|
|
11828
|
+
return;
|
|
11829
|
+
}
|
|
11830
|
+
await acknowledge(events);
|
|
11831
|
+
}).catch((error) => {
|
|
11819
11832
|
console.error(`[sourcefed] event delivery failed: ${error instanceof Error ? error.message : String(error)}`);
|
|
11820
11833
|
});
|
|
11821
11834
|
return delivering;
|
|
11822
11835
|
};
|
|
11823
|
-
const
|
|
11824
|
-
|
|
11825
|
-
|
|
11826
|
-
|
|
11827
|
-
|
|
11828
|
-
console.error(`[sourcefed] event delivery failed after retries; re-draining queue: ${error instanceof Error ? error.message : String(error)}`);
|
|
11829
|
-
redrainRequested = true;
|
|
11830
|
-
void performDrain();
|
|
11836
|
+
const acknowledge = async (events) => {
|
|
11837
|
+
let attempt = 0;
|
|
11838
|
+
while (!controller.signal.aborted && callbacks?.has(onEvents)) {
|
|
11839
|
+
try {
|
|
11840
|
+
await this.request("monitor.ack", { target, eventIDs: events.map((event) => event.id) });
|
|
11831
11841
|
return;
|
|
11842
|
+
} catch (error) {
|
|
11843
|
+
attempt += 1;
|
|
11844
|
+
if (attempt === 3) {
|
|
11845
|
+
console.error(`[sourcefed] event acknowledgement still failing; retrying without repeating delivery: ${error instanceof Error ? error.message : String(error)}`);
|
|
11846
|
+
}
|
|
11847
|
+
await sleep(Math.min(250 * 2 ** (attempt - 1), 4e3));
|
|
11832
11848
|
}
|
|
11833
|
-
await sleep(250 * (attempt + 1));
|
|
11834
|
-
await deliverWithRetry(events, attempt + 1);
|
|
11835
|
-
}
|
|
11836
|
-
};
|
|
11837
|
-
const performDrain = async () => {
|
|
11838
|
-
if (draining) {
|
|
11839
|
-
redrainRequested = true;
|
|
11840
|
-
return;
|
|
11841
|
-
}
|
|
11842
|
-
draining = true;
|
|
11843
|
-
try {
|
|
11844
|
-
let passes = 0;
|
|
11845
|
-
do {
|
|
11846
|
-
redrainRequested = false;
|
|
11847
|
-
passes += 1;
|
|
11848
|
-
try {
|
|
11849
|
-
const drainResult = await this.request("monitor.events", { target });
|
|
11850
|
-
drainComplete = true;
|
|
11851
|
-
const drainedIds = new Set((drainResult.events ?? []).map((event) => event.id));
|
|
11852
|
-
if (drainResult.events && drainResult.events.length > 0) await deliver(drainResult.events);
|
|
11853
|
-
for (const batch of buffered) {
|
|
11854
|
-
const fresh = batch.filter((event) => !drainedIds.has(event.id));
|
|
11855
|
-
if (fresh.length > 0) await deliver(fresh);
|
|
11856
|
-
}
|
|
11857
|
-
buffered = [];
|
|
11858
|
-
markDrained();
|
|
11859
|
-
} catch (error) {
|
|
11860
|
-
console.error(`[sourcefed] event drain failed: ${error instanceof Error ? error.message : String(error)}`);
|
|
11861
|
-
redrainRequested = true;
|
|
11862
|
-
}
|
|
11863
|
-
} while (redrainRequested && passes < 3);
|
|
11864
|
-
if (redrainRequested) markDrained();
|
|
11865
|
-
} finally {
|
|
11866
|
-
draining = false;
|
|
11867
11849
|
}
|
|
11868
11850
|
};
|
|
11869
11851
|
const runStream = async () => {
|
|
11852
|
+
const streamController = new AbortController();
|
|
11853
|
+
const closeStream = () => streamController.abort();
|
|
11854
|
+
activeStream = streamController;
|
|
11855
|
+
controller.signal.addEventListener("abort", closeStream, { once: true });
|
|
11870
11856
|
try {
|
|
11871
|
-
const response = await fetch(url, { signal:
|
|
11857
|
+
const response = await fetch(url, { signal: streamController.signal, headers: this.headers() });
|
|
11872
11858
|
if (!response.ok || !response.body) throw new Error(`event stream failed: ${response.status}`);
|
|
11873
11859
|
const reader = response.body.getReader();
|
|
11874
11860
|
const decoder = new TextDecoder();
|
|
@@ -11884,26 +11870,23 @@ var HttpDaemonClient = class {
|
|
|
11884
11870
|
if (!line.startsWith("data: ")) continue;
|
|
11885
11871
|
const frame = parseDaemonFrame(line.slice(6));
|
|
11886
11872
|
if (!frame || !("type" in frame)) continue;
|
|
11887
|
-
if (frame.type === "subscribed")
|
|
11888
|
-
|
|
11889
|
-
drainComplete = false;
|
|
11890
|
-
void performDrain();
|
|
11891
|
-
}
|
|
11892
|
-
if (frame.type === "event") {
|
|
11893
|
-
if (drainComplete) void deliver(frame.events);
|
|
11894
|
-
else buffered.push(frame.events);
|
|
11895
|
-
}
|
|
11873
|
+
if (frame.type === "subscribed") markSubscribed();
|
|
11874
|
+
if (frame.type === "event") void deliver(frame.events);
|
|
11896
11875
|
}
|
|
11897
11876
|
}
|
|
11898
11877
|
}
|
|
11899
11878
|
consecutiveFailures = 0;
|
|
11900
11879
|
} catch (error) {
|
|
11901
|
-
if (!controller.signal.aborted) {
|
|
11880
|
+
if (!controller.signal.aborted && !retryStream) {
|
|
11902
11881
|
consecutiveFailures += 1;
|
|
11903
11882
|
if (consecutiveFailures === 1) {
|
|
11904
11883
|
console.error(`[sourcefed] event stream ended: ${error instanceof Error ? error.message : String(error)}`);
|
|
11905
11884
|
}
|
|
11906
11885
|
}
|
|
11886
|
+
} finally {
|
|
11887
|
+
controller.signal.removeEventListener("abort", closeStream);
|
|
11888
|
+
if (activeStream === streamController) activeStream = void 0;
|
|
11889
|
+
retryStream = false;
|
|
11907
11890
|
}
|
|
11908
11891
|
};
|
|
11909
11892
|
let consecutiveFailures = 0;
|
|
@@ -11916,9 +11899,7 @@ var HttpDaemonClient = class {
|
|
|
11916
11899
|
})();
|
|
11917
11900
|
try {
|
|
11918
11901
|
await withTimeout(subscribed, 15e3, `sourcefed event stream did not subscribe at ${url}`);
|
|
11919
|
-
await withTimeout(drained, 15e3, "sourcefed initial event drain timed out");
|
|
11920
11902
|
} catch (error) {
|
|
11921
|
-
if (retryTimer) clearTimeout(retryTimer);
|
|
11922
11903
|
const callbacks2 = this.listeners.get(key);
|
|
11923
11904
|
callbacks2?.delete(onEvents);
|
|
11924
11905
|
if (callbacks2 && callbacks2.size === 0) this.listeners.delete(key);
|
|
@@ -11928,7 +11909,6 @@ var HttpDaemonClient = class {
|
|
|
11928
11909
|
}
|
|
11929
11910
|
return {
|
|
11930
11911
|
close: async () => {
|
|
11931
|
-
if (retryTimer) clearTimeout(retryTimer);
|
|
11932
11912
|
const callbacks2 = this.listeners.get(key);
|
|
11933
11913
|
callbacks2?.delete(onEvents);
|
|
11934
11914
|
if (callbacks2 && callbacks2.size === 0) this.listeners.delete(key);
|
package/dist/cli.js
CHANGED
|
@@ -11561,11 +11561,16 @@ var SourcefedDaemon = class {
|
|
|
11561
11561
|
this.listeners.set(key, listeners);
|
|
11562
11562
|
}
|
|
11563
11563
|
listeners.add(onEvents);
|
|
11564
|
+
this.emit(target);
|
|
11564
11565
|
return () => {
|
|
11565
11566
|
listeners.delete(onEvents);
|
|
11567
|
+
const receivedByListener = this.received.get(key);
|
|
11568
|
+
const released = receivedByListener?.delete(onEvents) ?? false;
|
|
11566
11569
|
if (listeners.size === 0) {
|
|
11567
11570
|
this.listeners.delete(key);
|
|
11568
11571
|
this.received.delete(key);
|
|
11572
|
+
} else if (released) {
|
|
11573
|
+
this.emit(target);
|
|
11569
11574
|
}
|
|
11570
11575
|
};
|
|
11571
11576
|
}
|
|
@@ -11601,23 +11606,28 @@ var SourcefedDaemon = class {
|
|
|
11601
11606
|
receivedByListener = /* @__PURE__ */ new Map();
|
|
11602
11607
|
this.received.set(key, receivedByListener);
|
|
11603
11608
|
}
|
|
11604
|
-
const
|
|
11609
|
+
const claimed = /* @__PURE__ */ new Set();
|
|
11610
|
+
for (const received of receivedByListener.values()) {
|
|
11611
|
+
for (const id of received) claimed.add(id);
|
|
11612
|
+
}
|
|
11613
|
+
const fresh = queued.filter((event) => !claimed.has(event.id));
|
|
11614
|
+
if (fresh.length === 0) return;
|
|
11615
|
+
for (const listener of [...listeners]) {
|
|
11605
11616
|
let received = receivedByListener.get(listener);
|
|
11606
11617
|
if (!received) {
|
|
11607
11618
|
received = /* @__PURE__ */ new Set();
|
|
11608
11619
|
receivedByListener.set(listener, received);
|
|
11609
11620
|
}
|
|
11610
|
-
const fresh = queued.filter((event) => !received.has(event.id));
|
|
11611
|
-
if (fresh.length === 0) return;
|
|
11612
11621
|
for (const event of fresh) received.add(event.id);
|
|
11613
11622
|
try {
|
|
11614
11623
|
await listener(fresh);
|
|
11624
|
+
return;
|
|
11615
11625
|
} catch (error2) {
|
|
11616
11626
|
console.error(`[sourcefed] event delivery failed: ${error2 instanceof Error ? error2.message : String(error2)}`);
|
|
11617
11627
|
for (const event of fresh) received.delete(event.id);
|
|
11628
|
+
if (received.size === 0) receivedByListener.delete(listener);
|
|
11618
11629
|
}
|
|
11619
|
-
}
|
|
11620
|
-
await Promise.all(listenerResults);
|
|
11630
|
+
}
|
|
11621
11631
|
}
|
|
11622
11632
|
};
|
|
11623
11633
|
function monitorView(monitor) {
|
|
@@ -11726,72 +11736,48 @@ var HttpDaemonClient = class {
|
|
|
11726
11736
|
const subscribed = new Promise((resolve) => {
|
|
11727
11737
|
markSubscribed = () => resolve();
|
|
11728
11738
|
});
|
|
11729
|
-
let drainComplete = false;
|
|
11730
|
-
let draining = false;
|
|
11731
|
-
let redrainRequested = false;
|
|
11732
|
-
let retryTimer;
|
|
11733
|
-
let markDrained;
|
|
11734
|
-
const drained = new Promise((resolve) => {
|
|
11735
|
-
markDrained = resolve;
|
|
11736
|
-
});
|
|
11737
|
-
let buffered = [];
|
|
11738
11739
|
let delivering = Promise.resolve();
|
|
11740
|
+
let activeStream;
|
|
11741
|
+
let retryStream = false;
|
|
11739
11742
|
const deliver = (events2) => {
|
|
11740
11743
|
if (!callbacks?.has(onEvents) || events2.length === 0) return Promise.resolve();
|
|
11741
|
-
delivering = delivering.then(
|
|
11744
|
+
delivering = delivering.then(async () => {
|
|
11745
|
+
try {
|
|
11746
|
+
await onEvents(events2);
|
|
11747
|
+
} catch (error2) {
|
|
11748
|
+
console.error(`[sourcefed] event delivery failed: ${error2 instanceof Error ? error2.message : String(error2)}`);
|
|
11749
|
+
retryStream = true;
|
|
11750
|
+
activeStream?.abort();
|
|
11751
|
+
return;
|
|
11752
|
+
}
|
|
11753
|
+
await acknowledge(events2);
|
|
11754
|
+
}).catch((error2) => {
|
|
11742
11755
|
console.error(`[sourcefed] event delivery failed: ${error2 instanceof Error ? error2.message : String(error2)}`);
|
|
11743
11756
|
});
|
|
11744
11757
|
return delivering;
|
|
11745
11758
|
};
|
|
11746
|
-
const
|
|
11747
|
-
|
|
11748
|
-
|
|
11749
|
-
|
|
11750
|
-
|
|
11751
|
-
console.error(`[sourcefed] event delivery failed after retries; re-draining queue: ${error2 instanceof Error ? error2.message : String(error2)}`);
|
|
11752
|
-
redrainRequested = true;
|
|
11753
|
-
void performDrain();
|
|
11759
|
+
const acknowledge = async (events2) => {
|
|
11760
|
+
let attempt = 0;
|
|
11761
|
+
while (!controller.signal.aborted && callbacks?.has(onEvents)) {
|
|
11762
|
+
try {
|
|
11763
|
+
await this.request("monitor.ack", { target, eventIDs: events2.map((event) => event.id) });
|
|
11754
11764
|
return;
|
|
11755
|
-
}
|
|
11756
|
-
|
|
11757
|
-
|
|
11758
|
-
|
|
11759
|
-
};
|
|
11760
|
-
const performDrain = async () => {
|
|
11761
|
-
if (draining) {
|
|
11762
|
-
redrainRequested = true;
|
|
11763
|
-
return;
|
|
11764
|
-
}
|
|
11765
|
-
draining = true;
|
|
11766
|
-
try {
|
|
11767
|
-
let passes = 0;
|
|
11768
|
-
do {
|
|
11769
|
-
redrainRequested = false;
|
|
11770
|
-
passes += 1;
|
|
11771
|
-
try {
|
|
11772
|
-
const drainResult = await this.request("monitor.events", { target });
|
|
11773
|
-
drainComplete = true;
|
|
11774
|
-
const drainedIds = new Set((drainResult.events ?? []).map((event) => event.id));
|
|
11775
|
-
if (drainResult.events && drainResult.events.length > 0) await deliver(drainResult.events);
|
|
11776
|
-
for (const batch of buffered) {
|
|
11777
|
-
const fresh = batch.filter((event) => !drainedIds.has(event.id));
|
|
11778
|
-
if (fresh.length > 0) await deliver(fresh);
|
|
11779
|
-
}
|
|
11780
|
-
buffered = [];
|
|
11781
|
-
markDrained();
|
|
11782
|
-
} catch (error2) {
|
|
11783
|
-
console.error(`[sourcefed] event drain failed: ${error2 instanceof Error ? error2.message : String(error2)}`);
|
|
11784
|
-
redrainRequested = true;
|
|
11765
|
+
} catch (error2) {
|
|
11766
|
+
attempt += 1;
|
|
11767
|
+
if (attempt === 3) {
|
|
11768
|
+
console.error(`[sourcefed] event acknowledgement still failing; retrying without repeating delivery: ${error2 instanceof Error ? error2.message : String(error2)}`);
|
|
11785
11769
|
}
|
|
11786
|
-
|
|
11787
|
-
|
|
11788
|
-
} finally {
|
|
11789
|
-
draining = false;
|
|
11770
|
+
await sleep(Math.min(250 * 2 ** (attempt - 1), 4e3));
|
|
11771
|
+
}
|
|
11790
11772
|
}
|
|
11791
11773
|
};
|
|
11792
11774
|
const runStream = async () => {
|
|
11775
|
+
const streamController = new AbortController();
|
|
11776
|
+
const closeStream = () => streamController.abort();
|
|
11777
|
+
activeStream = streamController;
|
|
11778
|
+
controller.signal.addEventListener("abort", closeStream, { once: true });
|
|
11793
11779
|
try {
|
|
11794
|
-
const response = await fetch(url2, { signal:
|
|
11780
|
+
const response = await fetch(url2, { signal: streamController.signal, headers: this.headers() });
|
|
11795
11781
|
if (!response.ok || !response.body) throw new Error(`event stream failed: ${response.status}`);
|
|
11796
11782
|
const reader = response.body.getReader();
|
|
11797
11783
|
const decoder = new TextDecoder();
|
|
@@ -11807,26 +11793,23 @@ var HttpDaemonClient = class {
|
|
|
11807
11793
|
if (!line.startsWith("data: ")) continue;
|
|
11808
11794
|
const frame = parseDaemonFrame(line.slice(6));
|
|
11809
11795
|
if (!frame || !("type" in frame)) continue;
|
|
11810
|
-
if (frame.type === "subscribed")
|
|
11811
|
-
|
|
11812
|
-
drainComplete = false;
|
|
11813
|
-
void performDrain();
|
|
11814
|
-
}
|
|
11815
|
-
if (frame.type === "event") {
|
|
11816
|
-
if (drainComplete) void deliver(frame.events);
|
|
11817
|
-
else buffered.push(frame.events);
|
|
11818
|
-
}
|
|
11796
|
+
if (frame.type === "subscribed") markSubscribed();
|
|
11797
|
+
if (frame.type === "event") void deliver(frame.events);
|
|
11819
11798
|
}
|
|
11820
11799
|
}
|
|
11821
11800
|
}
|
|
11822
11801
|
consecutiveFailures = 0;
|
|
11823
11802
|
} catch (error2) {
|
|
11824
|
-
if (!controller.signal.aborted) {
|
|
11803
|
+
if (!controller.signal.aborted && !retryStream) {
|
|
11825
11804
|
consecutiveFailures += 1;
|
|
11826
11805
|
if (consecutiveFailures === 1) {
|
|
11827
11806
|
console.error(`[sourcefed] event stream ended: ${error2 instanceof Error ? error2.message : String(error2)}`);
|
|
11828
11807
|
}
|
|
11829
11808
|
}
|
|
11809
|
+
} finally {
|
|
11810
|
+
controller.signal.removeEventListener("abort", closeStream);
|
|
11811
|
+
if (activeStream === streamController) activeStream = void 0;
|
|
11812
|
+
retryStream = false;
|
|
11830
11813
|
}
|
|
11831
11814
|
};
|
|
11832
11815
|
let consecutiveFailures = 0;
|
|
@@ -11839,9 +11822,7 @@ var HttpDaemonClient = class {
|
|
|
11839
11822
|
})();
|
|
11840
11823
|
try {
|
|
11841
11824
|
await withTimeout(subscribed, 15e3, `sourcefed event stream did not subscribe at ${url2}`);
|
|
11842
|
-
await withTimeout(drained, 15e3, "sourcefed initial event drain timed out");
|
|
11843
11825
|
} catch (error2) {
|
|
11844
|
-
if (retryTimer) clearTimeout(retryTimer);
|
|
11845
11826
|
const callbacks2 = this.listeners.get(key);
|
|
11846
11827
|
callbacks2?.delete(onEvents);
|
|
11847
11828
|
if (callbacks2 && callbacks2.size === 0) this.listeners.delete(key);
|
|
@@ -11851,7 +11832,6 @@ var HttpDaemonClient = class {
|
|
|
11851
11832
|
}
|
|
11852
11833
|
return {
|
|
11853
11834
|
close: async () => {
|
|
11854
|
-
if (retryTimer) clearTimeout(retryTimer);
|
|
11855
11835
|
const callbacks2 = this.listeners.get(key);
|
|
11856
11836
|
callbacks2?.delete(onEvents);
|
|
11857
11837
|
if (callbacks2 && callbacks2.size === 0) this.listeners.delete(key);
|
package/dist/daemon.js
CHANGED
|
@@ -11663,11 +11663,16 @@ var SourcefedDaemon = class {
|
|
|
11663
11663
|
this.listeners.set(key, listeners);
|
|
11664
11664
|
}
|
|
11665
11665
|
listeners.add(onEvents);
|
|
11666
|
+
this.emit(target);
|
|
11666
11667
|
return () => {
|
|
11667
11668
|
listeners.delete(onEvents);
|
|
11669
|
+
const receivedByListener = this.received.get(key);
|
|
11670
|
+
const released = receivedByListener?.delete(onEvents) ?? false;
|
|
11668
11671
|
if (listeners.size === 0) {
|
|
11669
11672
|
this.listeners.delete(key);
|
|
11670
11673
|
this.received.delete(key);
|
|
11674
|
+
} else if (released) {
|
|
11675
|
+
this.emit(target);
|
|
11671
11676
|
}
|
|
11672
11677
|
};
|
|
11673
11678
|
}
|
|
@@ -11703,23 +11708,28 @@ var SourcefedDaemon = class {
|
|
|
11703
11708
|
receivedByListener = /* @__PURE__ */ new Map();
|
|
11704
11709
|
this.received.set(key, receivedByListener);
|
|
11705
11710
|
}
|
|
11706
|
-
const
|
|
11711
|
+
const claimed = /* @__PURE__ */ new Set();
|
|
11712
|
+
for (const received of receivedByListener.values()) {
|
|
11713
|
+
for (const id of received) claimed.add(id);
|
|
11714
|
+
}
|
|
11715
|
+
const fresh = queued.filter((event) => !claimed.has(event.id));
|
|
11716
|
+
if (fresh.length === 0) return;
|
|
11717
|
+
for (const listener of [...listeners]) {
|
|
11707
11718
|
let received = receivedByListener.get(listener);
|
|
11708
11719
|
if (!received) {
|
|
11709
11720
|
received = /* @__PURE__ */ new Set();
|
|
11710
11721
|
receivedByListener.set(listener, received);
|
|
11711
11722
|
}
|
|
11712
|
-
const fresh = queued.filter((event) => !received.has(event.id));
|
|
11713
|
-
if (fresh.length === 0) return;
|
|
11714
11723
|
for (const event of fresh) received.add(event.id);
|
|
11715
11724
|
try {
|
|
11716
11725
|
await listener(fresh);
|
|
11726
|
+
return;
|
|
11717
11727
|
} catch (error) {
|
|
11718
11728
|
console.error(`[sourcefed] event delivery failed: ${error instanceof Error ? error.message : String(error)}`);
|
|
11719
11729
|
for (const event of fresh) received.delete(event.id);
|
|
11730
|
+
if (received.size === 0) receivedByListener.delete(listener);
|
|
11720
11731
|
}
|
|
11721
|
-
}
|
|
11722
|
-
await Promise.all(listenerResults);
|
|
11732
|
+
}
|
|
11723
11733
|
}
|
|
11724
11734
|
};
|
|
11725
11735
|
function monitorView(monitor) {
|
|
@@ -11832,72 +11842,48 @@ var HttpDaemonClient = class {
|
|
|
11832
11842
|
const subscribed = new Promise((resolve) => {
|
|
11833
11843
|
markSubscribed = () => resolve();
|
|
11834
11844
|
});
|
|
11835
|
-
let drainComplete = false;
|
|
11836
|
-
let draining = false;
|
|
11837
|
-
let redrainRequested = false;
|
|
11838
|
-
let retryTimer;
|
|
11839
|
-
let markDrained;
|
|
11840
|
-
const drained = new Promise((resolve) => {
|
|
11841
|
-
markDrained = resolve;
|
|
11842
|
-
});
|
|
11843
|
-
let buffered = [];
|
|
11844
11845
|
let delivering = Promise.resolve();
|
|
11846
|
+
let activeStream;
|
|
11847
|
+
let retryStream = false;
|
|
11845
11848
|
const deliver = (events) => {
|
|
11846
11849
|
if (!callbacks?.has(onEvents) || events.length === 0) return Promise.resolve();
|
|
11847
|
-
delivering = delivering.then(
|
|
11850
|
+
delivering = delivering.then(async () => {
|
|
11851
|
+
try {
|
|
11852
|
+
await onEvents(events);
|
|
11853
|
+
} catch (error) {
|
|
11854
|
+
console.error(`[sourcefed] event delivery failed: ${error instanceof Error ? error.message : String(error)}`);
|
|
11855
|
+
retryStream = true;
|
|
11856
|
+
activeStream?.abort();
|
|
11857
|
+
return;
|
|
11858
|
+
}
|
|
11859
|
+
await acknowledge(events);
|
|
11860
|
+
}).catch((error) => {
|
|
11848
11861
|
console.error(`[sourcefed] event delivery failed: ${error instanceof Error ? error.message : String(error)}`);
|
|
11849
11862
|
});
|
|
11850
11863
|
return delivering;
|
|
11851
11864
|
};
|
|
11852
|
-
const
|
|
11853
|
-
|
|
11854
|
-
|
|
11855
|
-
|
|
11856
|
-
|
|
11857
|
-
console.error(`[sourcefed] event delivery failed after retries; re-draining queue: ${error instanceof Error ? error.message : String(error)}`);
|
|
11858
|
-
redrainRequested = true;
|
|
11859
|
-
void performDrain();
|
|
11865
|
+
const acknowledge = async (events) => {
|
|
11866
|
+
let attempt = 0;
|
|
11867
|
+
while (!controller.signal.aborted && callbacks?.has(onEvents)) {
|
|
11868
|
+
try {
|
|
11869
|
+
await this.request("monitor.ack", { target, eventIDs: events.map((event) => event.id) });
|
|
11860
11870
|
return;
|
|
11871
|
+
} catch (error) {
|
|
11872
|
+
attempt += 1;
|
|
11873
|
+
if (attempt === 3) {
|
|
11874
|
+
console.error(`[sourcefed] event acknowledgement still failing; retrying without repeating delivery: ${error instanceof Error ? error.message : String(error)}`);
|
|
11875
|
+
}
|
|
11876
|
+
await sleep(Math.min(250 * 2 ** (attempt - 1), 4e3));
|
|
11861
11877
|
}
|
|
11862
|
-
await sleep(250 * (attempt + 1));
|
|
11863
|
-
await deliverWithRetry(events, attempt + 1);
|
|
11864
|
-
}
|
|
11865
|
-
};
|
|
11866
|
-
const performDrain = async () => {
|
|
11867
|
-
if (draining) {
|
|
11868
|
-
redrainRequested = true;
|
|
11869
|
-
return;
|
|
11870
|
-
}
|
|
11871
|
-
draining = true;
|
|
11872
|
-
try {
|
|
11873
|
-
let passes = 0;
|
|
11874
|
-
do {
|
|
11875
|
-
redrainRequested = false;
|
|
11876
|
-
passes += 1;
|
|
11877
|
-
try {
|
|
11878
|
-
const drainResult = await this.request("monitor.events", { target });
|
|
11879
|
-
drainComplete = true;
|
|
11880
|
-
const drainedIds = new Set((drainResult.events ?? []).map((event) => event.id));
|
|
11881
|
-
if (drainResult.events && drainResult.events.length > 0) await deliver(drainResult.events);
|
|
11882
|
-
for (const batch of buffered) {
|
|
11883
|
-
const fresh = batch.filter((event) => !drainedIds.has(event.id));
|
|
11884
|
-
if (fresh.length > 0) await deliver(fresh);
|
|
11885
|
-
}
|
|
11886
|
-
buffered = [];
|
|
11887
|
-
markDrained();
|
|
11888
|
-
} catch (error) {
|
|
11889
|
-
console.error(`[sourcefed] event drain failed: ${error instanceof Error ? error.message : String(error)}`);
|
|
11890
|
-
redrainRequested = true;
|
|
11891
|
-
}
|
|
11892
|
-
} while (redrainRequested && passes < 3);
|
|
11893
|
-
if (redrainRequested) markDrained();
|
|
11894
|
-
} finally {
|
|
11895
|
-
draining = false;
|
|
11896
11878
|
}
|
|
11897
11879
|
};
|
|
11898
11880
|
const runStream = async () => {
|
|
11881
|
+
const streamController = new AbortController();
|
|
11882
|
+
const closeStream = () => streamController.abort();
|
|
11883
|
+
activeStream = streamController;
|
|
11884
|
+
controller.signal.addEventListener("abort", closeStream, { once: true });
|
|
11899
11885
|
try {
|
|
11900
|
-
const response = await fetch(url, { signal:
|
|
11886
|
+
const response = await fetch(url, { signal: streamController.signal, headers: this.headers() });
|
|
11901
11887
|
if (!response.ok || !response.body) throw new Error(`event stream failed: ${response.status}`);
|
|
11902
11888
|
const reader = response.body.getReader();
|
|
11903
11889
|
const decoder = new TextDecoder();
|
|
@@ -11913,26 +11899,23 @@ var HttpDaemonClient = class {
|
|
|
11913
11899
|
if (!line.startsWith("data: ")) continue;
|
|
11914
11900
|
const frame = parseDaemonFrame(line.slice(6));
|
|
11915
11901
|
if (!frame || !("type" in frame)) continue;
|
|
11916
|
-
if (frame.type === "subscribed")
|
|
11917
|
-
|
|
11918
|
-
drainComplete = false;
|
|
11919
|
-
void performDrain();
|
|
11920
|
-
}
|
|
11921
|
-
if (frame.type === "event") {
|
|
11922
|
-
if (drainComplete) void deliver(frame.events);
|
|
11923
|
-
else buffered.push(frame.events);
|
|
11924
|
-
}
|
|
11902
|
+
if (frame.type === "subscribed") markSubscribed();
|
|
11903
|
+
if (frame.type === "event") void deliver(frame.events);
|
|
11925
11904
|
}
|
|
11926
11905
|
}
|
|
11927
11906
|
}
|
|
11928
11907
|
consecutiveFailures = 0;
|
|
11929
11908
|
} catch (error) {
|
|
11930
|
-
if (!controller.signal.aborted) {
|
|
11909
|
+
if (!controller.signal.aborted && !retryStream) {
|
|
11931
11910
|
consecutiveFailures += 1;
|
|
11932
11911
|
if (consecutiveFailures === 1) {
|
|
11933
11912
|
console.error(`[sourcefed] event stream ended: ${error instanceof Error ? error.message : String(error)}`);
|
|
11934
11913
|
}
|
|
11935
11914
|
}
|
|
11915
|
+
} finally {
|
|
11916
|
+
controller.signal.removeEventListener("abort", closeStream);
|
|
11917
|
+
if (activeStream === streamController) activeStream = void 0;
|
|
11918
|
+
retryStream = false;
|
|
11936
11919
|
}
|
|
11937
11920
|
};
|
|
11938
11921
|
let consecutiveFailures = 0;
|
|
@@ -11945,9 +11928,7 @@ var HttpDaemonClient = class {
|
|
|
11945
11928
|
})();
|
|
11946
11929
|
try {
|
|
11947
11930
|
await withTimeout(subscribed, 15e3, `sourcefed event stream did not subscribe at ${url}`);
|
|
11948
|
-
await withTimeout(drained, 15e3, "sourcefed initial event drain timed out");
|
|
11949
11931
|
} catch (error) {
|
|
11950
|
-
if (retryTimer) clearTimeout(retryTimer);
|
|
11951
11932
|
const callbacks2 = this.listeners.get(key);
|
|
11952
11933
|
callbacks2?.delete(onEvents);
|
|
11953
11934
|
if (callbacks2 && callbacks2.size === 0) this.listeners.delete(key);
|
|
@@ -11957,7 +11938,6 @@ var HttpDaemonClient = class {
|
|
|
11957
11938
|
}
|
|
11958
11939
|
return {
|
|
11959
11940
|
close: async () => {
|
|
11960
|
-
if (retryTimer) clearTimeout(retryTimer);
|
|
11961
11941
|
const callbacks2 = this.listeners.get(key);
|
|
11962
11942
|
callbacks2?.delete(onEvents);
|
|
11963
11943
|
if (callbacks2 && callbacks2.size === 0) this.listeners.delete(key);
|