@interncom/diplomatic 0.0.13 → 0.0.15

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.d.ts CHANGED
@@ -1,5 +1,5 @@
1
1
  import type { IOp, KeyPair } from "./shared/types";
2
- import type { IClientStateStore, DiplomaticClientState, Applier } from "./types";
2
+ import type { IClientStateStore, Applier, IDiplomaticClientState } from "./types";
3
3
  import type { StateManager } from "./state";
4
4
  export interface IDiplomaticClientParams {
5
5
  store: IClientStateStore;
@@ -11,7 +11,7 @@ export interface IDiplomaticClientParams {
11
11
  export default class DiplomaticClient {
12
12
  store: IClientStateStore;
13
13
  applier: Applier;
14
- listener?: (state: DiplomaticClientState) => void;
14
+ listener?: (state: IDiplomaticClientState) => void;
15
15
  seed?: Uint8Array;
16
16
  encKey?: Uint8Array;
17
17
  hostURL?: URL;
@@ -25,7 +25,8 @@ export default class DiplomaticClient {
25
25
  setSeed(seed: Uint8Array): Promise<void>;
26
26
  loadHost(): Promise<void>;
27
27
  register(hostURL: string): Promise<void>;
28
- get state(): DiplomaticClientState;
28
+ emitUpdate(): Promise<void>;
29
+ getState(): Promise<IDiplomaticClientState>;
29
30
  processOps(): Promise<void>;
30
31
  pullOpPaths(): Promise<never[] | undefined>;
31
32
  fetchAndExecQueuedOps(): Promise<never[] | undefined>;
@@ -15,10 +15,12 @@ declare class IDBStore implements IClientStateStore {
15
15
  dequeueUpload: (sha256: string) => Promise<void>;
16
16
  peekUpload: (sha256: string) => Promise<Uint8Array | undefined>;
17
17
  listUploads: () => Promise<string[]>;
18
+ numUploads: () => Promise<number>;
18
19
  downloadQueue: Set<string>;
19
20
  enqueueDownload: (path: string) => Promise<void>;
20
21
  dequeueDownload: (path: string) => Promise<void>;
21
22
  listDownloads: () => Promise<string[]>;
23
+ numDownloads: () => Promise<number>;
22
24
  }
23
25
  export declare const idbStore: IDBStore;
24
26
  export {};
package/dist/index.d.ts CHANGED
@@ -1,10 +1,11 @@
1
- import { useClientState } from "./useClient";
1
+ import { useClientState, useSyncOnResume } from "./useClient";
2
2
  import { StateManager, useStateWatcher } from './state';
3
3
  import { localStorageStore } from "./localStorageStore";
4
4
  import { idbStore } from "./idbStore";
5
+ import type { IDiplomaticClientState } from "./types";
5
6
  import DiplomaticClient from "./client";
6
7
  import libsodiumCrypto from "./crypto";
7
8
  import { btoh, htob } from "./shared/lib";
8
9
  import { type IOp, Verb } from "./shared/types";
9
- export { useClientState, StateManager, useStateWatcher, localStorageStore, idbStore, DiplomaticClient, libsodiumCrypto, btoh, htob, Verb, };
10
- export type { IOp, };
10
+ export { useClientState, useSyncOnResume, StateManager, useStateWatcher, localStorageStore, idbStore, DiplomaticClient, libsodiumCrypto, btoh, htob, Verb, };
11
+ export type { IOp, IDiplomaticClientState, };
package/dist/index.mjs CHANGED
@@ -12,15 +12,32 @@ var __export = (target, all) => {
12
12
 
13
13
  // src/useClient.ts
14
14
  import { useState, useEffect } from "react";
15
- var initialState = "loading";
16
15
  function useClientState(client) {
17
- const [state, setState] = useState(initialState);
16
+ const [state, setState] = useState();
18
17
  useEffect(() => {
19
- setState(client.state);
20
- client.listener = setState;
18
+ async function updateState() {
19
+ const state2 = await client.getState();
20
+ setState(state2);
21
+ }
22
+ client.listener = updateState;
23
+ updateState();
24
+ return () => {
25
+ client.listener = void 0;
26
+ };
21
27
  }, [client]);
22
28
  return state;
23
29
  }
30
+ function useSyncOnResume(client) {
31
+ useEffect(() => {
32
+ function handleOnline() {
33
+ client.sync();
34
+ }
35
+ window.addEventListener("online", handleOnline);
36
+ return () => {
37
+ window.removeEventListener("online", handleOnline);
38
+ };
39
+ }, [client]);
40
+ }
24
41
 
25
42
  // src/state.ts
26
43
  import { useEffect as useEffect2, useState as useState2 } from "react";
@@ -140,6 +157,9 @@ var LocalStorageStore = class {
140
157
  listUploads = async () => {
141
158
  return Array.from(this.uploadQueue.keys());
142
159
  };
160
+ numUploads = async () => {
161
+ return this.uploadQueue.size;
162
+ };
143
163
  downloadQueue = /* @__PURE__ */ new Set();
144
164
  enqueueDownload = async (path) => {
145
165
  this.downloadQueue.add(path);
@@ -150,6 +170,9 @@ var LocalStorageStore = class {
150
170
  listDownloads = async () => {
151
171
  return Array.from(this.downloadQueue.keys());
152
172
  };
173
+ numDownloads = async () => {
174
+ return this.downloadQueue.size;
175
+ };
153
176
  };
154
177
  var localStorageStore = new LocalStorageStore();
155
178
 
@@ -470,6 +493,10 @@ var IDBStore = class {
470
493
  const sha256s = await db.getAllKeys("uploadQueue");
471
494
  return sha256s;
472
495
  };
496
+ numUploads = async () => {
497
+ const db = await dbPromise;
498
+ return db.count("uploadQueue");
499
+ };
473
500
  downloadQueue = /* @__PURE__ */ new Set();
474
501
  enqueueDownload = async (path) => {
475
502
  const db = await dbPromise;
@@ -484,6 +511,10 @@ var IDBStore = class {
484
511
  const paths = await db.getAllKeys("downloadQueue");
485
512
  return paths;
486
513
  };
514
+ numDownloads = async () => {
515
+ const db = await dbPromise;
516
+ return db.count("downloadQueue");
517
+ };
487
518
  };
488
519
  var idbStore = new IDBStore();
489
520
 
@@ -8795,10 +8826,12 @@ var DiplomaticClient = class {
8795
8826
  this.websocket = new WebSocket(url);
8796
8827
  this.websocket.onopen = (e) => {
8797
8828
  console.log("CONNECTED");
8829
+ this.emitUpdate();
8798
8830
  };
8799
8831
  this.websocket.onclose = (e) => {
8800
8832
  console.log("DISCONNECTED");
8801
8833
  this.connect(hostURL);
8834
+ this.emitUpdate();
8802
8835
  };
8803
8836
  this.websocket.onmessage = (e) => {
8804
8837
  console.log(`RECEIVED: ${e.data}`);
@@ -8826,7 +8859,7 @@ var DiplomaticClient = class {
8826
8859
  await this.connect(this.hostURL);
8827
8860
  }
8828
8861
  await this.sync();
8829
- this.listener?.(this.state);
8862
+ this.emitUpdate();
8830
8863
  }
8831
8864
  async loadSeed() {
8832
8865
  const seed = await this.store.getSeed();
@@ -8839,7 +8872,7 @@ var DiplomaticClient = class {
8839
8872
  this.seed = seed;
8840
8873
  this.encKey = await crypto_default.deriveXSalsa20Poly1305Key(seed);
8841
8874
  await this.store.setSeed(seed);
8842
- this.listener?.(this.state);
8875
+ this.emitUpdate();
8843
8876
  }
8844
8877
  async loadHost() {
8845
8878
  if (!this.seed) {
@@ -8864,16 +8897,20 @@ var DiplomaticClient = class {
8864
8897
  await api_default.register(this.hostURL, this.hostKeyPair.publicKey, "tok123");
8865
8898
  await this.store.setHostURL(hostURL);
8866
8899
  await this.store.setHostID(hostID);
8867
- this.listener?.(this.state);
8868
- }
8869
- get state() {
8870
- if (!this.seed || !this.encKey) {
8871
- return "seedless";
8872
- }
8873
- if (!this.hostURL || !this.hostKeyPair) {
8874
- return "hostless";
8875
- }
8876
- return "ready";
8900
+ this.emitUpdate();
8901
+ }
8902
+ async emitUpdate() {
8903
+ const state = await this.getState();
8904
+ this.listener?.(state);
8905
+ }
8906
+ async getState() {
8907
+ return {
8908
+ hasSeed: this.seed !== void 0 && this.encKey !== void 0,
8909
+ hasHost: this.hostURL !== void 0 && this.hostKeyPair !== void 0,
8910
+ connected: this.websocket?.readyState === WebSocket.OPEN,
8911
+ numUploads: await this.store.numUploads(),
8912
+ numDownloads: await this.store.numDownloads()
8913
+ };
8877
8914
  }
8878
8915
  async processOps() {
8879
8916
  await this.pullOpPaths();
@@ -8889,6 +8926,7 @@ var DiplomaticClient = class {
8889
8926
  const paths = pathResp.paths;
8890
8927
  for (const path of paths) {
8891
8928
  await this.store.enqueueDownload(path);
8929
+ this.emitUpdate();
8892
8930
  }
8893
8931
  this.lastFetchedAt = pathResp.fetchedAt;
8894
8932
  }
@@ -8906,10 +8944,12 @@ var DiplomaticClient = class {
8906
8944
  try {
8907
8945
  await this.applier(op);
8908
8946
  await this.store.dequeueDownload(path);
8947
+ this.emitUpdate();
8909
8948
  } catch {
8910
8949
  const transient = true;
8911
8950
  if (!transient) {
8912
8951
  await this.store.dequeueDownload(path);
8952
+ this.emitUpdate();
8913
8953
  }
8914
8954
  }
8915
8955
  }
@@ -8923,6 +8963,7 @@ var DiplomaticClient = class {
8923
8963
  await api_default.putDelta(this.hostURL, cipherOp, this.hostKeyPair);
8924
8964
  }
8925
8965
  await this.store.dequeueUpload(sha256);
8966
+ this.emitUpdate();
8926
8967
  }
8927
8968
  async pushQueuedOps() {
8928
8969
  for (const sha256 of await this.store.listUploads()) {
@@ -8942,12 +8983,18 @@ var DiplomaticClient = class {
8942
8983
  const sha256 = await crypto_default.sha256Hash(cipherOp);
8943
8984
  const shaHex = btoh(sha256);
8944
8985
  await this.store.enqueueUpload(shaHex, cipherOp);
8986
+ this.emitUpdate();
8945
8987
  try {
8946
8988
  await this.applier(op);
8947
8989
  } catch {
8948
8990
  await this.store.dequeueUpload(shaHex);
8991
+ this.emitUpdate();
8992
+ }
8993
+ try {
8994
+ await this.pushQueuedOp(shaHex);
8995
+ } catch (err) {
8996
+ console.info("failed to push");
8949
8997
  }
8950
- await this.pushQueuedOp(shaHex);
8951
8998
  }
8952
8999
  async upsert(type, body, version = 0) {
8953
9000
  const op = genUpsertOp(type, body, version);
@@ -8964,5 +9011,6 @@ export {
8964
9011
  crypto_default as libsodiumCrypto,
8965
9012
  localStorageStore,
8966
9013
  useClientState,
8967
- useStateWatcher
9014
+ useStateWatcher,
9015
+ useSyncOnResume
8968
9016
  };
@@ -12,10 +12,12 @@ declare class LocalStorageStore implements IClientStateStore {
12
12
  dequeueUpload: (sha256: string) => Promise<void>;
13
13
  peekUpload: (sha256: string) => Promise<Uint8Array | undefined>;
14
14
  listUploads: () => Promise<string[]>;
15
+ numUploads: () => Promise<number>;
15
16
  downloadQueue: Set<string>;
16
17
  enqueueDownload: (path: string) => Promise<void>;
17
18
  dequeueDownload: (path: string) => Promise<void>;
18
19
  listDownloads: () => Promise<string[]>;
20
+ numDownloads: () => Promise<number>;
19
21
  }
20
22
  export declare const localStorageStore: LocalStorageStore;
21
23
  export {};
@@ -15,10 +15,12 @@ declare class MemoryStore implements IClientStateStore {
15
15
  dequeueUpload: (sha256: string) => Promise<void>;
16
16
  peekUpload: (sha256: string) => Promise<Uint8Array | undefined>;
17
17
  listUploads: () => Promise<string[]>;
18
+ numUploads: () => Promise<number>;
18
19
  downloadQueue: Set<string>;
19
20
  enqueueDownload: (path: string) => Promise<void>;
20
21
  dequeueDownload: (path: string) => Promise<void>;
21
22
  listDownloads: () => Promise<string[]>;
23
+ numDownloads: () => Promise<number>;
22
24
  }
23
25
  export declare const memoryStore: MemoryStore;
24
26
  export {};
package/dist/types.d.ts CHANGED
@@ -11,9 +11,17 @@ export interface IClientStateStore {
11
11
  dequeueUpload: (sha256: string) => Promise<void>;
12
12
  peekUpload: (sha256: string) => Promise<Uint8Array | undefined>;
13
13
  listUploads: () => Promise<string[]>;
14
+ numUploads: () => Promise<number>;
14
15
  enqueueDownload: (path: string) => Promise<void>;
15
16
  dequeueDownload: (path: string) => Promise<void>;
16
17
  listDownloads: () => Promise<string[]>;
18
+ numDownloads: () => Promise<number>;
19
+ }
20
+ export interface IDiplomaticClientState {
21
+ hasSeed: boolean;
22
+ hasHost: boolean;
23
+ connected: boolean;
24
+ numUploads: number;
25
+ numDownloads: number;
17
26
  }
18
- export type DiplomaticClientState = "loading" | "seedless" | "hostless" | "ready";
19
27
  export type Applier = (op: IOp) => Promise<void>;
@@ -1,3 +1,4 @@
1
- import type { DiplomaticClientState } from "./types";
1
+ import type { IDiplomaticClientState } from "./types";
2
2
  import type DiplomaticClient from "./client";
3
- export declare function useClientState(client: DiplomaticClient): DiplomaticClientState;
3
+ export declare function useClientState(client: DiplomaticClient): IDiplomaticClientState | undefined;
4
+ export declare function useSyncOnResume(client: DiplomaticClient): void;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@interncom/diplomatic",
3
- "version": "0.0.13",
3
+ "version": "0.0.15",
4
4
  "type": "module",
5
5
  "description": "Secure sync layer",
6
6
  "main": "dist/index.mjs",