@3plate/graph-core 0.1.9 → 0.1.10

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/index.cjs CHANGED
@@ -3717,11 +3717,11 @@ var WebSocketSource = class {
3717
3717
  connectStartTime = null;
3718
3718
  totalTimeoutMs = 1e4;
3719
3719
  totalTimeoutTimer = null;
3720
- constructor(url, onMessage, onStatus, reconnectMs = 1500) {
3721
- this.url = url;
3722
- this.onMessage = onMessage;
3723
- this.onStatus = onStatus;
3724
- this.reconnectMs = reconnectMs;
3720
+ constructor(args) {
3721
+ this.url = args.url;
3722
+ this.onMessage = args.onMessage;
3723
+ this.onStatus = args.onStatus;
3724
+ this.reconnectMs = args.reconnectMs ?? 1500;
3725
3725
  }
3726
3726
  connect() {
3727
3727
  this.closedByUser = false;
@@ -3822,11 +3822,11 @@ var FileSource = class {
3822
3822
  lastContent = "";
3823
3823
  intervalMs = 1e3;
3824
3824
  closed = false;
3825
- constructor(url, onMessage, onStatus, intervalMs = 1e3) {
3826
- this.url = url;
3827
- this.onMessage = onMessage;
3828
- this.onStatus = onStatus;
3829
- this.intervalMs = intervalMs;
3825
+ constructor(args) {
3826
+ this.url = args.url;
3827
+ this.onMessage = args.onMessage;
3828
+ this.onStatus = args.onStatus;
3829
+ this.intervalMs = args.intervalMs ?? 1e3;
3830
3830
  }
3831
3831
  async connect() {
3832
3832
  this.closed = false;
@@ -3888,6 +3888,68 @@ var FileSource = class {
3888
3888
  }
3889
3889
  };
3890
3890
 
3891
+ // src/api/sources/FileSystemSource.ts
3892
+ var FileSystemSource = class {
3893
+ handle = null;
3894
+ onMessage;
3895
+ onStatus;
3896
+ timer = null;
3897
+ lastSize = 0;
3898
+ filename;
3899
+ intervalMs;
3900
+ constructor(args) {
3901
+ this.filename = args.filename;
3902
+ this.onMessage = args.onMessage;
3903
+ this.onStatus = args.onStatus;
3904
+ this.intervalMs = args.intervalMs ?? 1e3;
3905
+ }
3906
+ async openDirectory() {
3907
+ try {
3908
+ const dir = await window.showDirectoryPicker?.();
3909
+ if (!dir) throw new Error("File System Access not supported or cancelled");
3910
+ const handle = await dir.getFileHandle(this.filename, { create: false });
3911
+ this.handle = handle;
3912
+ this.onStatus?.("opened", { file: this.filename });
3913
+ this.lastSize = 0;
3914
+ this.startPolling();
3915
+ } catch (e) {
3916
+ this.onStatus?.("error", e);
3917
+ }
3918
+ }
3919
+ close() {
3920
+ if (this.timer) {
3921
+ window.clearInterval(this.timer);
3922
+ this.timer = null;
3923
+ }
3924
+ this.handle = null;
3925
+ this.onStatus?.("closed");
3926
+ }
3927
+ startPolling() {
3928
+ if (this.timer) window.clearInterval(this.timer);
3929
+ this.timer = window.setInterval(() => this.readNewLines(), this.intervalMs);
3930
+ }
3931
+ async readNewLines() {
3932
+ try {
3933
+ if (!this.handle) return;
3934
+ this.onStatus?.("reading");
3935
+ const file = await this.handle.getFile();
3936
+ if (file.size === this.lastSize) return;
3937
+ const slice = await file.slice(this.lastSize).text();
3938
+ this.lastSize = file.size;
3939
+ const lines = slice.split("\n").map((l) => l.trim()).filter(Boolean);
3940
+ for (const line of lines) {
3941
+ try {
3942
+ const obj = JSON.parse(line);
3943
+ this.onMessage(obj);
3944
+ } catch {
3945
+ }
3946
+ }
3947
+ } catch (e) {
3948
+ this.onStatus?.("error", e);
3949
+ }
3950
+ }
3951
+ };
3952
+
3891
3953
  // src/api/api.ts
3892
3954
  var log11 = logger("api");
3893
3955
  var API = class {
@@ -3970,29 +4032,19 @@ var API = class {
3970
4032
  /** Connect to the configured ingestion source */
3971
4033
  connectIngestion() {
3972
4034
  if (!this.ingestionConfig || !this.ingest) return;
3973
- const handleMessage = (msg) => {
3974
- this.ingest.apply(msg);
4035
+ const args = {
4036
+ ...this.ingestionConfig,
4037
+ onMessage: (msg) => {
4038
+ this.ingest.apply(msg);
4039
+ }
3975
4040
  };
3976
- switch (this.ingestionConfig.type) {
3977
- case "websocket":
3978
- this.ingestionSource = new WebSocketSource(
3979
- this.ingestionConfig.url,
3980
- handleMessage,
3981
- void 0,
3982
- this.ingestionConfig.reconnectMs
3983
- );
3984
- this.ingestionSource.connect();
3985
- break;
3986
- case "file":
3987
- this.ingestionSource = new FileSource(
3988
- this.ingestionConfig.url,
3989
- handleMessage,
3990
- void 0,
3991
- this.ingestionConfig.intervalMs
3992
- );
3993
- this.ingestionSource.connect();
3994
- break;
3995
- }
4041
+ const source = {
4042
+ "websocket": WebSocketSource,
4043
+ "file": FileSource,
4044
+ "filesystem": FileSystemSource
4045
+ }[this.ingestionConfig.type];
4046
+ this.ingestionSource = new source[this.ingestionConfig.type](args);
4047
+ this.ingestionSource?.connect();
3996
4048
  }
3997
4049
  /** Disconnect from the ingestion source */
3998
4050
  disconnectIngestion() {
@@ -4578,68 +4630,6 @@ function shallowEqualUpdate(a, b) {
4578
4630
  return true;
4579
4631
  }
4580
4632
 
4581
- // src/api/sources/FileSystemSource.ts
4582
- var FileSystemSource = class {
4583
- handle = null;
4584
- onMessage;
4585
- onStatus;
4586
- timer = null;
4587
- lastSize = 0;
4588
- filename;
4589
- intervalMs;
4590
- constructor(onMessage, onStatus, filename = "graph.ndjson", intervalMs = 1e3) {
4591
- this.onMessage = onMessage;
4592
- this.onStatus = onStatus;
4593
- this.filename = filename;
4594
- this.intervalMs = intervalMs;
4595
- }
4596
- async openDirectory() {
4597
- try {
4598
- const dir = await window.showDirectoryPicker?.();
4599
- if (!dir) throw new Error("File System Access not supported or cancelled");
4600
- const handle = await dir.getFileHandle(this.filename, { create: false });
4601
- this.handle = handle;
4602
- this.onStatus?.("opened", { file: this.filename });
4603
- this.lastSize = 0;
4604
- this.startPolling();
4605
- } catch (e) {
4606
- this.onStatus?.("error", e);
4607
- }
4608
- }
4609
- close() {
4610
- if (this.timer) {
4611
- window.clearInterval(this.timer);
4612
- this.timer = null;
4613
- }
4614
- this.handle = null;
4615
- this.onStatus?.("closed");
4616
- }
4617
- startPolling() {
4618
- if (this.timer) window.clearInterval(this.timer);
4619
- this.timer = window.setInterval(() => this.readNewLines(), this.intervalMs);
4620
- }
4621
- async readNewLines() {
4622
- try {
4623
- if (!this.handle) return;
4624
- this.onStatus?.("reading");
4625
- const file = await this.handle.getFile();
4626
- if (file.size === this.lastSize) return;
4627
- const slice = await file.slice(this.lastSize).text();
4628
- this.lastSize = file.size;
4629
- const lines = slice.split("\n").map((l) => l.trim()).filter(Boolean);
4630
- for (const line of lines) {
4631
- try {
4632
- const obj = JSON.parse(line);
4633
- this.onMessage(obj);
4634
- } catch {
4635
- }
4636
- }
4637
- } catch (e) {
4638
- this.onStatus?.("error", e);
4639
- }
4640
- }
4641
- };
4642
-
4643
4633
  // src/playground/playground.ts
4644
4634
  var import_styles2 = __toESM(require("./styles.css?raw"), 1);
4645
4635
  var Playground = class {
@@ -4918,10 +4908,18 @@ var Playground = class {
4918
4908
  this.disconnectAllSources();
4919
4909
  if (example.source.type === "websocket") {
4920
4910
  this.wsUrl = example.source.url;
4921
- this.wsSource = new WebSocketSource(example.source.url, this.handleIngestMessage.bind(this), this.updateWsStatus);
4911
+ this.wsSource = new WebSocketSource({
4912
+ url: example.source.url,
4913
+ onMessage: this.handleIngestMessage.bind(this),
4914
+ onStatus: this.updateWsStatus
4915
+ });
4922
4916
  this.wsSource.connect();
4923
4917
  } else if (example.source.type === "file") {
4924
- this.fileSource = new FileSource(example.source.path, this.handleIngestMessage.bind(this), this.updateFileStatus);
4918
+ this.fileSource = new FileSource({
4919
+ url: example.source.path,
4920
+ onMessage: this.handleIngestMessage.bind(this),
4921
+ onStatus: this.updateFileStatus
4922
+ });
4925
4923
  this.fileSource.connect();
4926
4924
  }
4927
4925
  }
@@ -5321,7 +5319,11 @@ var Playground = class {
5321
5319
  if (this.wsSource) {
5322
5320
  this.wsSource.disconnect();
5323
5321
  }
5324
- this.wsSource = new WebSocketSource(url, this.handleIngestMessage.bind(this), this.updateWsStatus);
5322
+ this.wsSource = new WebSocketSource({
5323
+ url,
5324
+ onMessage: this.handleIngestMessage.bind(this),
5325
+ onStatus: this.updateWsStatus
5326
+ });
5325
5327
  this.wsSource.connect();
5326
5328
  this.updateSourceModal();
5327
5329
  }
@@ -5342,7 +5344,11 @@ var Playground = class {
5342
5344
  }
5343
5345
  async handleOpenFolder() {
5344
5346
  if (!this.fsSource) {
5345
- this.fsSource = new FileSystemSource(this.handleIngestMessage.bind(this), this.updateFsStatus);
5347
+ this.fsSource = new FileSystemSource({
5348
+ filename: "graph.ndjson",
5349
+ onMessage: this.handleIngestMessage.bind(this),
5350
+ onStatus: this.updateFsStatus
5351
+ });
5346
5352
  }
5347
5353
  this.updateSourceModal();
5348
5354
  await this.fsSource.openDirectory();
package/dist/index.d.cts CHANGED
@@ -3,6 +3,7 @@ type MergeOrder = Side[];
3
3
  type NodeAlign = 'natural' | 'top' | 'bottom' | 'left' | 'right';
4
4
  type Orientation = 'TB' | 'BT' | 'LR' | 'RL';
5
5
  type Nav = 'first' | 'last' | 'prev' | 'next';
6
+ type PortStyle = 'inside' | 'outside' | 'custom';
6
7
  type LayoutStep = 'alignChildren' | 'alignParents' | 'compact';
7
8
  type Dims = {
8
9
  w: number;
@@ -11,19 +12,33 @@ type Dims = {
11
12
 
12
13
  type MarkerType = 'arrow' | 'circle' | 'diamond' | 'bar' | 'none';
13
14
 
14
- /**
15
- * Ingestion source configuration.
16
- * Used to connect to an external data source for graph updates.
17
- */
18
- type IngestionConfig = {
15
+ /** WebSocket ingestion configuration */
16
+ type WebSocketIngestionConfig = {
19
17
  type: 'websocket';
18
+ /** WebSocket URL */
20
19
  url: string;
20
+ /** Reconnect interval in milliseconds */
21
21
  reconnectMs?: number;
22
- } | {
22
+ /** Callback when the WebSocket connection is established */
23
+ onConnect?: () => void;
24
+ /** Callback when the WebSocket connection is closed */
25
+ onDisconnect?: () => void;
26
+ /** Callback when the WebSocket connection encounters an error */
27
+ onError?: (error: Error) => void;
28
+ };
29
+ /** File ingestion configuration */
30
+ type FileIngestionConfig = {
23
31
  type: 'file';
32
+ /** File URL */
24
33
  url: string;
34
+ /** Polling interval in milliseconds */
25
35
  intervalMs?: number;
26
36
  };
37
+ /**
38
+ * Ingestion source configuration.
39
+ * Used to connect to an external data source for graph updates.
40
+ */
41
+ type IngestionConfig = WebSocketIngestionConfig | FileIngestionConfig;
27
42
  /**
28
43
  * Arguments to the API constructor.
29
44
  *
@@ -518,7 +533,14 @@ declare class Ingest<N, E> {
518
533
  apply(msg: IngestMessage<N, E>): Promise<void>;
519
534
  }
520
535
 
521
- type StatusListener$2 = (status: 'connecting' | 'connected' | 'reconnecting' | 'closed' | 'error', detail?: any) => void;
536
+ type WebSocketStatus = 'connecting' | 'connected' | 'reconnecting' | 'closed' | 'error';
537
+ type WebSocketStatusListener = (status: WebSocketStatus, detail?: any) => void;
538
+ type WebSocketSourceArgs<N, E> = {
539
+ url: string;
540
+ onMessage: (msg: IngestMessage<N, E>) => void;
541
+ onStatus?: WebSocketStatusListener;
542
+ reconnectMs?: number;
543
+ };
522
544
  declare class WebSocketSource<N, E> {
523
545
  private url;
524
546
  private ws;
@@ -529,7 +551,7 @@ declare class WebSocketSource<N, E> {
529
551
  private connectStartTime;
530
552
  private totalTimeoutMs;
531
553
  private totalTimeoutTimer;
532
- constructor(url: string, onMessage: (msg: IngestMessage<N, E>) => void, onStatus?: StatusListener$2, reconnectMs?: number);
554
+ constructor(args: WebSocketSourceArgs<N, E>);
533
555
  connect(): void;
534
556
  disconnect(): void;
535
557
  private startTotalTimeout;
@@ -538,6 +560,12 @@ declare class WebSocketSource<N, E> {
538
560
  }
539
561
 
540
562
  type StatusListener$1 = (status: 'idle' | 'opened' | 'reading' | 'error' | 'closed', detail?: any) => void;
563
+ type FileSystemSourceArgs<N, E> = {
564
+ filename: string;
565
+ onMessage: (msg: IngestMessage<N, E>) => void;
566
+ onStatus?: StatusListener$1;
567
+ intervalMs?: number;
568
+ };
541
569
  declare class FileSystemSource<N, E> {
542
570
  private handle;
543
571
  private onMessage;
@@ -546,7 +574,7 @@ declare class FileSystemSource<N, E> {
546
574
  private lastSize;
547
575
  private filename;
548
576
  private intervalMs;
549
- constructor(onMessage: (msg: IngestMessage<N, E>) => void, onStatus?: StatusListener$1, filename?: string, intervalMs?: number);
577
+ constructor(args: FileSystemSourceArgs<N, E>);
550
578
  openDirectory(): Promise<void>;
551
579
  close(): void;
552
580
  private startPolling;
@@ -554,6 +582,12 @@ declare class FileSystemSource<N, E> {
554
582
  }
555
583
 
556
584
  type StatusListener = (status: 'idle' | 'opened' | 'reading' | 'error' | 'closed', detail?: any) => void;
585
+ type FileSourceArgs<N, E> = {
586
+ url: string;
587
+ onMessage: (msg: IngestMessage<N, E>) => void;
588
+ onStatus?: StatusListener;
589
+ intervalMs?: number;
590
+ };
557
591
  declare class FileSource<N, E> {
558
592
  private url;
559
593
  private onMessage;
@@ -563,7 +597,7 @@ declare class FileSource<N, E> {
563
597
  private lastContent;
564
598
  private intervalMs;
565
599
  private closed;
566
- constructor(url: string, onMessage: (msg: IngestMessage<N, E>) => void, onStatus?: StatusListener, intervalMs?: number);
600
+ constructor(args: FileSourceArgs<N, E>);
567
601
  connect(): Promise<void>;
568
602
  close(): void;
569
603
  private startPolling;
@@ -685,4 +719,4 @@ declare class Playground {
685
719
 
686
720
  declare function graph<N, E>(args?: APIArguments<N, E>): Promise<API<N, E>>;
687
721
 
688
- export { API, type APIArguments, type APIOptions, type EventsOptions, type Example, type ExampleEdge, type ExampleNode, type ExampleOptions, FileSource, FileSystemSource, type HistoryMessage, Ingest, type IngestMessage, type IngestionConfig, Playground, type PlaygroundOptions, type SnapshotMessage, type Update, type UpdateMessage, Updater, WebSocketSource, graph as default, graph };
722
+ export { API, type APIArguments, type APIOptions, type CanvasTheme, type ColorMode, type EdgeProps, type EdgeTheme, type EventsOptions, type Example, type ExampleEdge, type ExampleNode, type ExampleOptions, FileSource, type FileSourceArgs, FileSystemSource, type FileSystemSourceArgs, type HistoryMessage, Ingest, type IngestMessage, type IngestionConfig, type NewEdge, type NewNode, type NodeAlign, type NodeProps, type NodeTheme, type Orientation, Playground, type PlaygroundOptions, type PortProps, type PortStyle, type PortTheme, type RenderNode, type SnapshotMessage, type ThemeVars, type Update, type UpdateMessage, Updater, WebSocketSource, type WebSocketSourceArgs, type WebSocketStatus, type WebSocketStatusListener, graph as default, graph };
package/dist/index.d.ts CHANGED
@@ -3,6 +3,7 @@ type MergeOrder = Side[];
3
3
  type NodeAlign = 'natural' | 'top' | 'bottom' | 'left' | 'right';
4
4
  type Orientation = 'TB' | 'BT' | 'LR' | 'RL';
5
5
  type Nav = 'first' | 'last' | 'prev' | 'next';
6
+ type PortStyle = 'inside' | 'outside' | 'custom';
6
7
  type LayoutStep = 'alignChildren' | 'alignParents' | 'compact';
7
8
  type Dims = {
8
9
  w: number;
@@ -11,19 +12,33 @@ type Dims = {
11
12
 
12
13
  type MarkerType = 'arrow' | 'circle' | 'diamond' | 'bar' | 'none';
13
14
 
14
- /**
15
- * Ingestion source configuration.
16
- * Used to connect to an external data source for graph updates.
17
- */
18
- type IngestionConfig = {
15
+ /** WebSocket ingestion configuration */
16
+ type WebSocketIngestionConfig = {
19
17
  type: 'websocket';
18
+ /** WebSocket URL */
20
19
  url: string;
20
+ /** Reconnect interval in milliseconds */
21
21
  reconnectMs?: number;
22
- } | {
22
+ /** Callback when the WebSocket connection is established */
23
+ onConnect?: () => void;
24
+ /** Callback when the WebSocket connection is closed */
25
+ onDisconnect?: () => void;
26
+ /** Callback when the WebSocket connection encounters an error */
27
+ onError?: (error: Error) => void;
28
+ };
29
+ /** File ingestion configuration */
30
+ type FileIngestionConfig = {
23
31
  type: 'file';
32
+ /** File URL */
24
33
  url: string;
34
+ /** Polling interval in milliseconds */
25
35
  intervalMs?: number;
26
36
  };
37
+ /**
38
+ * Ingestion source configuration.
39
+ * Used to connect to an external data source for graph updates.
40
+ */
41
+ type IngestionConfig = WebSocketIngestionConfig | FileIngestionConfig;
27
42
  /**
28
43
  * Arguments to the API constructor.
29
44
  *
@@ -518,7 +533,14 @@ declare class Ingest<N, E> {
518
533
  apply(msg: IngestMessage<N, E>): Promise<void>;
519
534
  }
520
535
 
521
- type StatusListener$2 = (status: 'connecting' | 'connected' | 'reconnecting' | 'closed' | 'error', detail?: any) => void;
536
+ type WebSocketStatus = 'connecting' | 'connected' | 'reconnecting' | 'closed' | 'error';
537
+ type WebSocketStatusListener = (status: WebSocketStatus, detail?: any) => void;
538
+ type WebSocketSourceArgs<N, E> = {
539
+ url: string;
540
+ onMessage: (msg: IngestMessage<N, E>) => void;
541
+ onStatus?: WebSocketStatusListener;
542
+ reconnectMs?: number;
543
+ };
522
544
  declare class WebSocketSource<N, E> {
523
545
  private url;
524
546
  private ws;
@@ -529,7 +551,7 @@ declare class WebSocketSource<N, E> {
529
551
  private connectStartTime;
530
552
  private totalTimeoutMs;
531
553
  private totalTimeoutTimer;
532
- constructor(url: string, onMessage: (msg: IngestMessage<N, E>) => void, onStatus?: StatusListener$2, reconnectMs?: number);
554
+ constructor(args: WebSocketSourceArgs<N, E>);
533
555
  connect(): void;
534
556
  disconnect(): void;
535
557
  private startTotalTimeout;
@@ -538,6 +560,12 @@ declare class WebSocketSource<N, E> {
538
560
  }
539
561
 
540
562
  type StatusListener$1 = (status: 'idle' | 'opened' | 'reading' | 'error' | 'closed', detail?: any) => void;
563
+ type FileSystemSourceArgs<N, E> = {
564
+ filename: string;
565
+ onMessage: (msg: IngestMessage<N, E>) => void;
566
+ onStatus?: StatusListener$1;
567
+ intervalMs?: number;
568
+ };
541
569
  declare class FileSystemSource<N, E> {
542
570
  private handle;
543
571
  private onMessage;
@@ -546,7 +574,7 @@ declare class FileSystemSource<N, E> {
546
574
  private lastSize;
547
575
  private filename;
548
576
  private intervalMs;
549
- constructor(onMessage: (msg: IngestMessage<N, E>) => void, onStatus?: StatusListener$1, filename?: string, intervalMs?: number);
577
+ constructor(args: FileSystemSourceArgs<N, E>);
550
578
  openDirectory(): Promise<void>;
551
579
  close(): void;
552
580
  private startPolling;
@@ -554,6 +582,12 @@ declare class FileSystemSource<N, E> {
554
582
  }
555
583
 
556
584
  type StatusListener = (status: 'idle' | 'opened' | 'reading' | 'error' | 'closed', detail?: any) => void;
585
+ type FileSourceArgs<N, E> = {
586
+ url: string;
587
+ onMessage: (msg: IngestMessage<N, E>) => void;
588
+ onStatus?: StatusListener;
589
+ intervalMs?: number;
590
+ };
557
591
  declare class FileSource<N, E> {
558
592
  private url;
559
593
  private onMessage;
@@ -563,7 +597,7 @@ declare class FileSource<N, E> {
563
597
  private lastContent;
564
598
  private intervalMs;
565
599
  private closed;
566
- constructor(url: string, onMessage: (msg: IngestMessage<N, E>) => void, onStatus?: StatusListener, intervalMs?: number);
600
+ constructor(args: FileSourceArgs<N, E>);
567
601
  connect(): Promise<void>;
568
602
  close(): void;
569
603
  private startPolling;
@@ -685,4 +719,4 @@ declare class Playground {
685
719
 
686
720
  declare function graph<N, E>(args?: APIArguments<N, E>): Promise<API<N, E>>;
687
721
 
688
- export { API, type APIArguments, type APIOptions, type EventsOptions, type Example, type ExampleEdge, type ExampleNode, type ExampleOptions, FileSource, FileSystemSource, type HistoryMessage, Ingest, type IngestMessage, type IngestionConfig, Playground, type PlaygroundOptions, type SnapshotMessage, type Update, type UpdateMessage, Updater, WebSocketSource, graph as default, graph };
722
+ export { API, type APIArguments, type APIOptions, type CanvasTheme, type ColorMode, type EdgeProps, type EdgeTheme, type EventsOptions, type Example, type ExampleEdge, type ExampleNode, type ExampleOptions, FileSource, type FileSourceArgs, FileSystemSource, type FileSystemSourceArgs, type HistoryMessage, Ingest, type IngestMessage, type IngestionConfig, type NewEdge, type NewNode, type NodeAlign, type NodeProps, type NodeTheme, type Orientation, Playground, type PlaygroundOptions, type PortProps, type PortStyle, type PortTheme, type RenderNode, type SnapshotMessage, type ThemeVars, type Update, type UpdateMessage, Updater, WebSocketSource, type WebSocketSourceArgs, type WebSocketStatus, type WebSocketStatusListener, graph as default, graph };
package/dist/index.js CHANGED
@@ -3674,11 +3674,11 @@ var WebSocketSource = class {
3674
3674
  connectStartTime = null;
3675
3675
  totalTimeoutMs = 1e4;
3676
3676
  totalTimeoutTimer = null;
3677
- constructor(url, onMessage, onStatus, reconnectMs = 1500) {
3678
- this.url = url;
3679
- this.onMessage = onMessage;
3680
- this.onStatus = onStatus;
3681
- this.reconnectMs = reconnectMs;
3677
+ constructor(args) {
3678
+ this.url = args.url;
3679
+ this.onMessage = args.onMessage;
3680
+ this.onStatus = args.onStatus;
3681
+ this.reconnectMs = args.reconnectMs ?? 1500;
3682
3682
  }
3683
3683
  connect() {
3684
3684
  this.closedByUser = false;
@@ -3779,11 +3779,11 @@ var FileSource = class {
3779
3779
  lastContent = "";
3780
3780
  intervalMs = 1e3;
3781
3781
  closed = false;
3782
- constructor(url, onMessage, onStatus, intervalMs = 1e3) {
3783
- this.url = url;
3784
- this.onMessage = onMessage;
3785
- this.onStatus = onStatus;
3786
- this.intervalMs = intervalMs;
3782
+ constructor(args) {
3783
+ this.url = args.url;
3784
+ this.onMessage = args.onMessage;
3785
+ this.onStatus = args.onStatus;
3786
+ this.intervalMs = args.intervalMs ?? 1e3;
3787
3787
  }
3788
3788
  async connect() {
3789
3789
  this.closed = false;
@@ -3845,6 +3845,68 @@ var FileSource = class {
3845
3845
  }
3846
3846
  };
3847
3847
 
3848
+ // src/api/sources/FileSystemSource.ts
3849
+ var FileSystemSource = class {
3850
+ handle = null;
3851
+ onMessage;
3852
+ onStatus;
3853
+ timer = null;
3854
+ lastSize = 0;
3855
+ filename;
3856
+ intervalMs;
3857
+ constructor(args) {
3858
+ this.filename = args.filename;
3859
+ this.onMessage = args.onMessage;
3860
+ this.onStatus = args.onStatus;
3861
+ this.intervalMs = args.intervalMs ?? 1e3;
3862
+ }
3863
+ async openDirectory() {
3864
+ try {
3865
+ const dir = await window.showDirectoryPicker?.();
3866
+ if (!dir) throw new Error("File System Access not supported or cancelled");
3867
+ const handle = await dir.getFileHandle(this.filename, { create: false });
3868
+ this.handle = handle;
3869
+ this.onStatus?.("opened", { file: this.filename });
3870
+ this.lastSize = 0;
3871
+ this.startPolling();
3872
+ } catch (e) {
3873
+ this.onStatus?.("error", e);
3874
+ }
3875
+ }
3876
+ close() {
3877
+ if (this.timer) {
3878
+ window.clearInterval(this.timer);
3879
+ this.timer = null;
3880
+ }
3881
+ this.handle = null;
3882
+ this.onStatus?.("closed");
3883
+ }
3884
+ startPolling() {
3885
+ if (this.timer) window.clearInterval(this.timer);
3886
+ this.timer = window.setInterval(() => this.readNewLines(), this.intervalMs);
3887
+ }
3888
+ async readNewLines() {
3889
+ try {
3890
+ if (!this.handle) return;
3891
+ this.onStatus?.("reading");
3892
+ const file = await this.handle.getFile();
3893
+ if (file.size === this.lastSize) return;
3894
+ const slice = await file.slice(this.lastSize).text();
3895
+ this.lastSize = file.size;
3896
+ const lines = slice.split("\n").map((l) => l.trim()).filter(Boolean);
3897
+ for (const line of lines) {
3898
+ try {
3899
+ const obj = JSON.parse(line);
3900
+ this.onMessage(obj);
3901
+ } catch {
3902
+ }
3903
+ }
3904
+ } catch (e) {
3905
+ this.onStatus?.("error", e);
3906
+ }
3907
+ }
3908
+ };
3909
+
3848
3910
  // src/api/api.ts
3849
3911
  var log11 = logger("api");
3850
3912
  var API = class {
@@ -3927,29 +3989,19 @@ var API = class {
3927
3989
  /** Connect to the configured ingestion source */
3928
3990
  connectIngestion() {
3929
3991
  if (!this.ingestionConfig || !this.ingest) return;
3930
- const handleMessage = (msg) => {
3931
- this.ingest.apply(msg);
3992
+ const args = {
3993
+ ...this.ingestionConfig,
3994
+ onMessage: (msg) => {
3995
+ this.ingest.apply(msg);
3996
+ }
3932
3997
  };
3933
- switch (this.ingestionConfig.type) {
3934
- case "websocket":
3935
- this.ingestionSource = new WebSocketSource(
3936
- this.ingestionConfig.url,
3937
- handleMessage,
3938
- void 0,
3939
- this.ingestionConfig.reconnectMs
3940
- );
3941
- this.ingestionSource.connect();
3942
- break;
3943
- case "file":
3944
- this.ingestionSource = new FileSource(
3945
- this.ingestionConfig.url,
3946
- handleMessage,
3947
- void 0,
3948
- this.ingestionConfig.intervalMs
3949
- );
3950
- this.ingestionSource.connect();
3951
- break;
3952
- }
3998
+ const source = {
3999
+ "websocket": WebSocketSource,
4000
+ "file": FileSource,
4001
+ "filesystem": FileSystemSource
4002
+ }[this.ingestionConfig.type];
4003
+ this.ingestionSource = new source[this.ingestionConfig.type](args);
4004
+ this.ingestionSource?.connect();
3953
4005
  }
3954
4006
  /** Disconnect from the ingestion source */
3955
4007
  disconnectIngestion() {
@@ -4535,68 +4587,6 @@ function shallowEqualUpdate(a, b) {
4535
4587
  return true;
4536
4588
  }
4537
4589
 
4538
- // src/api/sources/FileSystemSource.ts
4539
- var FileSystemSource = class {
4540
- handle = null;
4541
- onMessage;
4542
- onStatus;
4543
- timer = null;
4544
- lastSize = 0;
4545
- filename;
4546
- intervalMs;
4547
- constructor(onMessage, onStatus, filename = "graph.ndjson", intervalMs = 1e3) {
4548
- this.onMessage = onMessage;
4549
- this.onStatus = onStatus;
4550
- this.filename = filename;
4551
- this.intervalMs = intervalMs;
4552
- }
4553
- async openDirectory() {
4554
- try {
4555
- const dir = await window.showDirectoryPicker?.();
4556
- if (!dir) throw new Error("File System Access not supported or cancelled");
4557
- const handle = await dir.getFileHandle(this.filename, { create: false });
4558
- this.handle = handle;
4559
- this.onStatus?.("opened", { file: this.filename });
4560
- this.lastSize = 0;
4561
- this.startPolling();
4562
- } catch (e) {
4563
- this.onStatus?.("error", e);
4564
- }
4565
- }
4566
- close() {
4567
- if (this.timer) {
4568
- window.clearInterval(this.timer);
4569
- this.timer = null;
4570
- }
4571
- this.handle = null;
4572
- this.onStatus?.("closed");
4573
- }
4574
- startPolling() {
4575
- if (this.timer) window.clearInterval(this.timer);
4576
- this.timer = window.setInterval(() => this.readNewLines(), this.intervalMs);
4577
- }
4578
- async readNewLines() {
4579
- try {
4580
- if (!this.handle) return;
4581
- this.onStatus?.("reading");
4582
- const file = await this.handle.getFile();
4583
- if (file.size === this.lastSize) return;
4584
- const slice = await file.slice(this.lastSize).text();
4585
- this.lastSize = file.size;
4586
- const lines = slice.split("\n").map((l) => l.trim()).filter(Boolean);
4587
- for (const line of lines) {
4588
- try {
4589
- const obj = JSON.parse(line);
4590
- this.onMessage(obj);
4591
- } catch {
4592
- }
4593
- }
4594
- } catch (e) {
4595
- this.onStatus?.("error", e);
4596
- }
4597
- }
4598
- };
4599
-
4600
4590
  // src/playground/playground.ts
4601
4591
  import styles2 from "./styles.css?raw";
4602
4592
  var Playground = class {
@@ -4875,10 +4865,18 @@ var Playground = class {
4875
4865
  this.disconnectAllSources();
4876
4866
  if (example.source.type === "websocket") {
4877
4867
  this.wsUrl = example.source.url;
4878
- this.wsSource = new WebSocketSource(example.source.url, this.handleIngestMessage.bind(this), this.updateWsStatus);
4868
+ this.wsSource = new WebSocketSource({
4869
+ url: example.source.url,
4870
+ onMessage: this.handleIngestMessage.bind(this),
4871
+ onStatus: this.updateWsStatus
4872
+ });
4879
4873
  this.wsSource.connect();
4880
4874
  } else if (example.source.type === "file") {
4881
- this.fileSource = new FileSource(example.source.path, this.handleIngestMessage.bind(this), this.updateFileStatus);
4875
+ this.fileSource = new FileSource({
4876
+ url: example.source.path,
4877
+ onMessage: this.handleIngestMessage.bind(this),
4878
+ onStatus: this.updateFileStatus
4879
+ });
4882
4880
  this.fileSource.connect();
4883
4881
  }
4884
4882
  }
@@ -5278,7 +5276,11 @@ var Playground = class {
5278
5276
  if (this.wsSource) {
5279
5277
  this.wsSource.disconnect();
5280
5278
  }
5281
- this.wsSource = new WebSocketSource(url, this.handleIngestMessage.bind(this), this.updateWsStatus);
5279
+ this.wsSource = new WebSocketSource({
5280
+ url,
5281
+ onMessage: this.handleIngestMessage.bind(this),
5282
+ onStatus: this.updateWsStatus
5283
+ });
5282
5284
  this.wsSource.connect();
5283
5285
  this.updateSourceModal();
5284
5286
  }
@@ -5299,7 +5301,11 @@ var Playground = class {
5299
5301
  }
5300
5302
  async handleOpenFolder() {
5301
5303
  if (!this.fsSource) {
5302
- this.fsSource = new FileSystemSource(this.handleIngestMessage.bind(this), this.updateFsStatus);
5304
+ this.fsSource = new FileSystemSource({
5305
+ filename: "graph.ndjson",
5306
+ onMessage: this.handleIngestMessage.bind(this),
5307
+ onStatus: this.updateFsStatus
5308
+ });
5303
5309
  }
5304
5310
  this.updateSourceModal();
5305
5311
  await this.fsSource.openDirectory();
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@3plate/graph-core",
3
- "version": "0.1.9",
3
+ "version": "0.1.10",
4
4
  "type": "module",
5
5
  "license": "GPL-3.0",
6
6
  "repository": {