@fdcn/sourcefed 0.3.5 → 0.3.7

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/daemon.js CHANGED
@@ -10460,31 +10460,6 @@ async function writeResponse(outgoing, response) {
10460
10460
  });
10461
10461
  }
10462
10462
 
10463
- // packages/daemon/src/utils/request-router.ts
10464
- var RequestRouter = class {
10465
- pending = /* @__PURE__ */ new Map();
10466
- cursor = 1;
10467
- nextID() {
10468
- return this.cursor++;
10469
- }
10470
- register(id) {
10471
- return new Promise((resolve, reject) => {
10472
- this.pending.set(id, { resolve, reject });
10473
- });
10474
- }
10475
- settle(id, frame) {
10476
- const pending = this.pending.get(id);
10477
- if (!pending) return;
10478
- this.pending.delete(id);
10479
- if (frame.error) pending.reject(new Error(frame.error));
10480
- else pending.resolve(frame.result);
10481
- }
10482
- rejectAll(error) {
10483
- for (const { reject } of this.pending.values()) reject(error);
10484
- this.pending.clear();
10485
- }
10486
- };
10487
-
10488
10463
  // packages/daemon/src/utils/spawn.ts
10489
10464
  import { spawn } from "node:child_process";
10490
10465
  import path5 from "node:path";
@@ -10890,165 +10865,228 @@ function parseDaemonFrame(line) {
10890
10865
  }
10891
10866
  }
10892
10867
 
10868
+ // packages/daemon/src/event-stream.ts
10869
+ var DaemonEventStream = class {
10870
+ constructor(url, headers, onEvents, acknowledge, onClose) {
10871
+ this.url = url;
10872
+ this.headers = headers;
10873
+ this.onEvents = onEvents;
10874
+ this.acknowledge = acknowledge;
10875
+ this.onClose = onClose;
10876
+ }
10877
+ controller = new AbortController();
10878
+ subscribed = Promise.withResolvers();
10879
+ stream;
10880
+ delivering = Promise.resolve();
10881
+ closing;
10882
+ ready = false;
10883
+ failedDelivery = false;
10884
+ async start(signal) {
10885
+ const abort = () => {
10886
+ void this.close();
10887
+ };
10888
+ signal?.addEventListener("abort", abort, { once: true });
10889
+ const setup = withTimeout(this.subscribed.promise, 15e3, `sourcefed event stream did not subscribe at ${this.url}`);
10890
+ if (signal?.aborted) abort();
10891
+ const running = this.run();
10892
+ void running.finally(() => signal?.removeEventListener("abort", abort));
10893
+ try {
10894
+ await setup;
10895
+ } catch (error) {
10896
+ await this.close();
10897
+ throw error;
10898
+ }
10899
+ }
10900
+ close() {
10901
+ if (!this.closing) {
10902
+ this.subscribed.reject(new Error("sourcefed event stream closed"));
10903
+ if (!this.ready) this.controller.abort();
10904
+ this.closing = this.drain();
10905
+ }
10906
+ return this.closing;
10907
+ }
10908
+ async drain() {
10909
+ try {
10910
+ await withTimeout(this.delivering, 15e3, "sourcefed event delivery did not drain");
10911
+ } catch (error) {
10912
+ console.error(`[sourcefed] closing event stream: ${String(error)}`);
10913
+ } finally {
10914
+ this.controller.abort();
10915
+ this.stream?.abort();
10916
+ this.onClose();
10917
+ }
10918
+ }
10919
+ async run() {
10920
+ let consecutiveFailures = 0;
10921
+ while (!this.controller.signal.aborted) {
10922
+ this.failedDelivery = false;
10923
+ try {
10924
+ await this.read();
10925
+ consecutiveFailures = 0;
10926
+ } catch (error) {
10927
+ if (!this.controller.signal.aborted && !this.failedDelivery && ++consecutiveFailures === 1) {
10928
+ console.error(`[sourcefed] event stream ended: ${String(error)}`);
10929
+ }
10930
+ }
10931
+ await this.waitForDelivery();
10932
+ if (!this.controller.signal.aborted) await pause(500, this.controller.signal);
10933
+ }
10934
+ }
10935
+ async read() {
10936
+ const controller = new AbortController();
10937
+ const abort = () => controller.abort();
10938
+ this.stream = controller;
10939
+ this.controller.signal.addEventListener("abort", abort, { once: true });
10940
+ let reader;
10941
+ try {
10942
+ this.controller.signal.throwIfAborted();
10943
+ const response = await fetch(this.url, { signal: controller.signal, headers: this.headers });
10944
+ if (!response.ok || !response.body) {
10945
+ await response.body?.cancel();
10946
+ throw new Error(`event stream failed: ${response.status}`);
10947
+ }
10948
+ reader = response.body.getReader();
10949
+ const decoder = new TextDecoder();
10950
+ let buffer = "";
10951
+ while (!controller.signal.aborted) {
10952
+ const { done, value } = await reader.read();
10953
+ if (done) return;
10954
+ buffer += decoder.decode(value, { stream: true });
10955
+ const frames = buffer.split("\n\n");
10956
+ buffer = frames.pop() ?? "";
10957
+ for (const chunk of frames) await this.frame(chunk);
10958
+ }
10959
+ } finally {
10960
+ await reader?.cancel().catch(() => void 0);
10961
+ reader?.releaseLock();
10962
+ this.controller.signal.removeEventListener("abort", abort);
10963
+ if (this.stream === controller) this.stream = void 0;
10964
+ }
10965
+ }
10966
+ async frame(chunk) {
10967
+ if (this.closing || this.controller.signal.aborted) return;
10968
+ for (const line of chunk.split("\n")) {
10969
+ if (!line.startsWith("data: ")) continue;
10970
+ const frame = parseDaemonFrame(line.slice(6));
10971
+ if (!frame || !("type" in frame)) continue;
10972
+ if (frame.type === "subscribed") {
10973
+ this.ready = true;
10974
+ this.subscribed.resolve();
10975
+ }
10976
+ if (frame.type === "event" && frame.events.length > 0) {
10977
+ this.deliver(frame.events);
10978
+ await this.waitForDelivery();
10979
+ }
10980
+ }
10981
+ }
10982
+ deliver(events) {
10983
+ this.delivering = this.delivering.then(async () => {
10984
+ if (this.failedDelivery || this.controller.signal.aborted) return;
10985
+ try {
10986
+ await this.onEvents(events);
10987
+ await this.acknowledge(events, this.controller.signal);
10988
+ } catch (error) {
10989
+ if (!this.controller.signal.aborted) console.error(`[sourcefed] event delivery failed: ${String(error)}`);
10990
+ this.failedDelivery = true;
10991
+ this.stream?.abort();
10992
+ }
10993
+ });
10994
+ }
10995
+ async waitForDelivery() {
10996
+ if (this.controller.signal.aborted) return;
10997
+ const stopped = Promise.withResolvers();
10998
+ const abort = () => stopped.resolve();
10999
+ this.controller.signal.addEventListener("abort", abort, { once: true });
11000
+ try {
11001
+ await Promise.race([this.delivering, stopped.promise]);
11002
+ } finally {
11003
+ this.controller.signal.removeEventListener("abort", abort);
11004
+ }
11005
+ }
11006
+ };
11007
+ function pause(ms, signal) {
11008
+ return new Promise((resolve) => {
11009
+ const finish = () => {
11010
+ clearTimeout(timer);
11011
+ signal.removeEventListener("abort", finish);
11012
+ resolve();
11013
+ };
11014
+ const timer = setTimeout(finish, ms);
11015
+ signal.addEventListener("abort", finish, { once: true });
11016
+ if (signal.aborted) finish();
11017
+ });
11018
+ }
11019
+
10893
11020
  // packages/daemon/src/client.ts
10894
11021
  async function connectDaemonClient(options) {
10895
11022
  const url = options.url ?? process.env.SOURCEFED_DAEMON_URL ?? defaultDaemonUrl();
10896
11023
  return new HttpDaemonClient(url, options.token ?? process.env.SOURCEFED_DAEMON_TOKEN);
10897
11024
  }
10898
11025
  var HttpDaemonClient = class {
10899
- baseURL;
10900
- token;
10901
- router = new RequestRouter();
10902
- listeners = /* @__PURE__ */ new Map();
10903
- streams = /* @__PURE__ */ new Set();
10904
11026
  constructor(baseURL, token) {
10905
- this.baseURL = baseURL.replace(/\/+$/, "");
10906
11027
  this.token = token;
11028
+ this.baseURL = baseURL.replace(/\/+$/, "");
10907
11029
  }
11030
+ baseURL;
11031
+ streams = /* @__PURE__ */ new Set();
11032
+ lifetime = new AbortController();
11033
+ cursor = 0;
11034
+ closing;
10908
11035
  headers() {
10909
11036
  return this.token ? { authorization: `Bearer ${this.token}` } : {};
10910
11037
  }
10911
- async request(method, params) {
10912
- const id = this.router.nextID();
10913
- const promise = this.router.register(id);
11038
+ async request(method, params, signal = this.lifetime.signal) {
11039
+ if (this.closing && signal === this.lifetime.signal) throw new Error("daemon closed");
11040
+ signal.throwIfAborted();
11041
+ const response = await fetch(`${this.baseURL}/rpc`, {
11042
+ method: "POST",
11043
+ headers: { "content-type": "application/json", ...this.headers() },
11044
+ body: JSON.stringify({ id: ++this.cursor, method, params }),
11045
+ signal: AbortSignal.any([signal, this.lifetime.signal, AbortSignal.timeout(15e3)])
11046
+ });
11047
+ const frame = await response.json();
11048
+ if (!response.ok || frame.error) throw new Error(frame.error ?? `daemon request failed: ${response.status}`);
11049
+ return frame.result;
11050
+ }
11051
+ async subscribe(target, onEvents, options) {
11052
+ if (this.closing) throw new Error("daemon closed");
11053
+ this.lifetime.signal.throwIfAborted();
11054
+ const stream = new DaemonEventStream(
11055
+ `${this.baseURL}/events?target=${encodeTarget(target)}`,
11056
+ this.headers(),
11057
+ onEvents,
11058
+ (events, signal) => this.acknowledge(target, events, signal),
11059
+ () => this.streams.delete(stream)
11060
+ );
11061
+ this.streams.add(stream);
10914
11062
  try {
10915
- const response = await fetch(`${this.baseURL}/rpc`, {
10916
- method: "POST",
10917
- headers: { "content-type": "application/json", ...this.headers() },
10918
- body: JSON.stringify({ id, method, params })
10919
- });
10920
- const frame = await response.json();
10921
- if (!response.ok || frame.error) throw new Error(frame.error ?? `daemon request failed: ${response.status}`);
10922
- this.router.settle(id, { result: frame.result });
11063
+ await stream.start(options?.signal);
11064
+ return { close: () => stream.close() };
10923
11065
  } catch (error) {
10924
- this.router.settle(id, { error: error instanceof Error ? error.message : String(error) });
11066
+ await stream.close();
11067
+ throw error;
10925
11068
  }
10926
- return promise;
10927
11069
  }
10928
- async subscribe(target, onEvents) {
10929
- const key = targetKey(target);
10930
- let callbacks = this.listeners.get(key);
10931
- if (!callbacks) {
10932
- callbacks = /* @__PURE__ */ new Set();
10933
- this.listeners.set(key, callbacks);
10934
- }
10935
- callbacks.add(onEvents);
10936
- const controller = new AbortController();
10937
- this.streams.add(controller);
10938
- const url = `${this.baseURL}/events?target=${encodeTarget(target)}`;
10939
- let markSubscribed;
10940
- const subscribed = new Promise((resolve) => {
10941
- markSubscribed = () => resolve();
10942
- });
10943
- let delivering = Promise.resolve();
10944
- let activeStream;
10945
- let retryStream = false;
10946
- const deliver = (events) => {
10947
- if (!callbacks?.has(onEvents) || events.length === 0) return Promise.resolve();
10948
- delivering = delivering.then(async () => {
10949
- try {
10950
- await onEvents(events);
10951
- } catch (error) {
10952
- console.error(`[sourcefed] event delivery failed: ${error instanceof Error ? error.message : String(error)}`);
10953
- retryStream = true;
10954
- activeStream?.abort();
10955
- return;
10956
- }
10957
- await acknowledge(events);
10958
- }).catch((error) => {
10959
- console.error(`[sourcefed] event delivery failed: ${error instanceof Error ? error.message : String(error)}`);
10960
- });
10961
- return delivering;
10962
- };
10963
- const acknowledge = async (events) => {
10964
- let attempt = 0;
10965
- while (!controller.signal.aborted && callbacks?.has(onEvents)) {
10966
- try {
10967
- await this.request("monitor.ack", { target, eventIDs: events.map((event) => event.id) });
10968
- return;
10969
- } catch (error) {
10970
- attempt += 1;
10971
- if (attempt === 3) {
10972
- console.error(`[sourcefed] event acknowledgement still failing; retrying without repeating delivery: ${error instanceof Error ? error.message : String(error)}`);
10973
- }
10974
- await sleep(Math.min(250 * 2 ** (attempt - 1), 4e3));
10975
- }
10976
- }
10977
- };
10978
- const runStream = async () => {
10979
- const streamController = new AbortController();
10980
- const closeStream = () => streamController.abort();
10981
- activeStream = streamController;
10982
- controller.signal.addEventListener("abort", closeStream, { once: true });
11070
+ async acknowledge(target, events, signal) {
11071
+ let attempt = 0;
11072
+ while (!signal.aborted) {
10983
11073
  try {
10984
- const response = await fetch(url, { signal: streamController.signal, headers: this.headers() });
10985
- if (!response.ok || !response.body) throw new Error(`event stream failed: ${response.status}`);
10986
- const reader = response.body.getReader();
10987
- const decoder = new TextDecoder();
10988
- let buffer = "";
10989
- while (true) {
10990
- const { done, value } = await reader.read();
10991
- if (done) break;
10992
- buffer += decoder.decode(value, { stream: true });
10993
- const frames = buffer.split("\n\n");
10994
- buffer = frames.pop() ?? "";
10995
- for (const chunk of frames) {
10996
- for (const line of chunk.split("\n")) {
10997
- if (!line.startsWith("data: ")) continue;
10998
- const frame = parseDaemonFrame(line.slice(6));
10999
- if (!frame || !("type" in frame)) continue;
11000
- if (frame.type === "subscribed") markSubscribed();
11001
- if (frame.type === "event") void deliver(frame.events);
11002
- }
11003
- }
11004
- }
11005
- consecutiveFailures = 0;
11074
+ await this.request("monitor.ack", { target, eventIDs: events.map((event) => event.id) }, signal);
11075
+ return;
11006
11076
  } catch (error) {
11007
- if (!controller.signal.aborted && !retryStream) {
11008
- consecutiveFailures += 1;
11009
- if (consecutiveFailures === 1) {
11010
- console.error(`[sourcefed] event stream ended: ${error instanceof Error ? error.message : String(error)}`);
11011
- }
11077
+ if (++attempt === 3) {
11078
+ console.error(`[sourcefed] event acknowledgement still failing; retrying without repeating delivery: ${String(error)}`);
11012
11079
  }
11013
- } finally {
11014
- controller.signal.removeEventListener("abort", closeStream);
11015
- if (activeStream === streamController) activeStream = void 0;
11016
- retryStream = false;
11017
- }
11018
- };
11019
- let consecutiveFailures = 0;
11020
- void (async () => {
11021
- while (!controller.signal.aborted && callbacks?.has(onEvents)) {
11022
- await runStream();
11023
- if (controller.signal.aborted) break;
11024
- await sleep(500);
11080
+ await pause(Math.min(250 * 2 ** Math.min(attempt - 1, 4), 4e3), signal);
11025
11081
  }
11026
- })();
11027
- try {
11028
- await withTimeout(subscribed, 15e3, `sourcefed event stream did not subscribe at ${url}`);
11029
- } catch (error) {
11030
- const callbacks2 = this.listeners.get(key);
11031
- callbacks2?.delete(onEvents);
11032
- if (callbacks2 && callbacks2.size === 0) this.listeners.delete(key);
11033
- controller.abort();
11034
- this.streams.delete(controller);
11035
- throw error;
11036
11082
  }
11037
- return {
11038
- close: async () => {
11039
- const callbacks2 = this.listeners.get(key);
11040
- callbacks2?.delete(onEvents);
11041
- if (callbacks2 && callbacks2.size === 0) this.listeners.delete(key);
11042
- controller.abort();
11043
- this.streams.delete(controller);
11044
- }
11045
- };
11046
11083
  }
11047
- async close() {
11048
- for (const controller of this.streams) controller.abort();
11049
- this.streams.clear();
11050
- this.router.rejectAll(new Error("daemon closed"));
11051
- this.listeners.clear();
11084
+ close() {
11085
+ this.closing ??= Promise.all([...this.streams].map((stream) => stream.close())).then(() => {
11086
+ this.lifetime.abort();
11087
+ this.streams.clear();
11088
+ });
11089
+ return this.closing;
11052
11090
  }
11053
11091
  };
11054
11092
 
package/dist/mcp.js CHANGED
@@ -1,7 +1,7 @@
1
1
  import {
2
2
  decodeTarget,
3
3
  encodeTarget
4
- } from "./chunk-X3BZSHKZ.js";
4
+ } from "./chunk-E6J3BAXP.js";
5
5
  import {
6
6
  __export
7
7
  } from "./chunk-MLKGABMK.js";