@interncom/diplomatic 0.0.9 → 0.0.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/client.d.ts CHANGED
@@ -26,11 +26,12 @@ export default class DiplomaticClient {
26
26
  loadHost(): Promise<void>;
27
27
  register(hostURL: string): Promise<void>;
28
28
  get state(): DiplomaticClientState;
29
- private putDelta;
30
- getDeltas(): Promise<IOp[]>;
31
- processDeltas(): Promise<void>;
29
+ processOps(): Promise<void>;
30
+ pullOpPaths(): Promise<never[] | undefined>;
31
+ fetchAndExecQueuedOps(): Promise<never[] | undefined>;
32
32
  private pushQueuedOp;
33
33
  pushQueuedOps(): Promise<void>;
34
+ sync(): Promise<void>;
34
35
  apply(op: IOp): Promise<void>;
35
36
  upsert<T>(type: string, body: T, version?: number): Promise<void>;
36
37
  }
package/dist/index.mjs CHANGED
@@ -80,6 +80,31 @@ function useStateWatcher(mgr, opType, callback) {
80
80
  return val;
81
81
  }
82
82
 
83
+ // src/queue.ts
84
+ var Queue = class {
85
+ map;
86
+ constructor() {
87
+ this.map = /* @__PURE__ */ new Map();
88
+ }
89
+ async enqueue(key, value) {
90
+ this.map.set(key, value);
91
+ }
92
+ async peek(key) {
93
+ return this.map.get(key);
94
+ }
95
+ async entries() {
96
+ return this.map.entries();
97
+ }
98
+ async dequeue(key) {
99
+ const value = this.map.get(key);
100
+ this.map.delete(key);
101
+ return value;
102
+ }
103
+ async size() {
104
+ return this.map.size;
105
+ }
106
+ };
107
+
83
108
  // ../shared/lib.ts
84
109
  function btoh(bytes) {
85
110
  const hex = Array.from(bytes).map((byte) => byte.toString(16).padStart(2, "0")).join("");
@@ -124,29 +149,8 @@ var LocalStorageStore = class {
124
149
  async setHostID(id) {
125
150
  return localStorage.setItem(hostIDKey, id);
126
151
  }
127
- uploadQueue = /* @__PURE__ */ new Map();
128
- enqueueUpload = async (sha256, cipherOp) => {
129
- const hex = btoh(sha256);
130
- this.uploadQueue.set(hex, cipherOp);
131
- };
132
- dequeueUpload = async (sha256) => {
133
- const hex = btoh(sha256);
134
- this.uploadQueue.delete(hex);
135
- };
136
- peekUpload = async (sha256) => {
137
- const hex = btoh(sha256);
138
- return this.uploadQueue.get(hex);
139
- };
140
- async listUploadQueue() {
141
- return Array.from(this.uploadQueue.keys());
142
- }
143
- downloadQueue = /* @__PURE__ */ new Set();
144
- enqueueDownload = async (path) => {
145
- this.downloadQueue.add(path);
146
- };
147
- dequeueDownload = async (path) => {
148
- this.downloadQueue.delete(path);
149
- };
152
+ pushQueue = new Queue();
153
+ pullQueue = new Queue();
150
154
  };
151
155
  var localStorageStore = new LocalStorageStore();
152
156
 
@@ -8440,7 +8444,6 @@ var DiplomaticClient = class {
8440
8444
  constructor(params) {
8441
8445
  this.store = params.store;
8442
8446
  this.applier = params.stateManager.apply;
8443
- console.log("constructing");
8444
8447
  this.init(params);
8445
8448
  }
8446
8449
  websocket;
@@ -8466,7 +8469,7 @@ var DiplomaticClient = class {
8466
8469
  };
8467
8470
  this.websocket.onmessage = (e) => {
8468
8471
  console.log(`RECEIVED: ${e.data}`);
8469
- this.processDeltas();
8472
+ this.processOps();
8470
8473
  };
8471
8474
  this.websocket.onerror = (e) => {
8472
8475
  console.log(`ERROR: ${e}`);
@@ -8490,7 +8493,7 @@ var DiplomaticClient = class {
8490
8493
  if (this.hostURL) {
8491
8494
  await this.connect(this.hostURL);
8492
8495
  }
8493
- await this.processDeltas();
8496
+ await this.sync();
8494
8497
  this.listener?.(this.state);
8495
8498
  }
8496
8499
  async loadSeed() {
@@ -8540,53 +8543,62 @@ var DiplomaticClient = class {
8540
8543
  }
8541
8544
  return "ready";
8542
8545
  }
8543
- async putDelta(delta) {
8544
- if (!this.hostURL || !this.hostKeyPair || !this.encKey) {
8545
- return;
8546
- }
8547
- const packed = encode(delta);
8548
- const cipherOp = await crypto_default.encryptXSalsa20Poly1305Combined(packed, this.encKey);
8549
- await api_default.putDelta(this.hostURL, cipherOp, this.hostKeyPair);
8546
+ async processOps() {
8547
+ await this.pullOpPaths();
8548
+ await this.fetchAndExecQueuedOps();
8550
8549
  }
8551
- async getDeltas() {
8550
+ async pullOpPaths() {
8552
8551
  if (!this.hostURL || !this.hostKeyPair || !this.encKey) {
8553
8552
  return [];
8554
8553
  }
8555
8554
  const begin = new Date(this.lastFetchedAt ?? 0);
8556
- const { hostURL, hostKeyPair, encKey } = this;
8555
+ const { hostURL, hostKeyPair } = this;
8557
8556
  const pathResp = await api_default.getDeltaPaths(hostURL, begin, hostKeyPair);
8558
8557
  const paths = pathResp.paths;
8558
+ for (const path of paths) {
8559
+ await this.store.pullQueue.enqueue(path, null);
8560
+ }
8559
8561
  this.lastFetchedAt = pathResp.fetchedAt;
8560
- const deltas = await Promise.all(paths.map(async (path) => {
8561
- const cipher = await api_default.getDelta(hostURL, path, hostKeyPair);
8562
- const deltaPack = await crypto_default.decryptXSalsa20Poly1305Combined(cipher, encKey);
8563
- const delta = decode(deltaPack);
8564
- return delta;
8565
- }));
8566
- return deltas;
8567
- }
8568
- async processDeltas() {
8569
- const deltas = await this.getDeltas();
8570
- for (const delta of deltas) {
8571
- await this.applier(delta);
8562
+ }
8563
+ async fetchAndExecQueuedOps() {
8564
+ if (!this.hostURL || !this.hostKeyPair || !this.encKey) {
8565
+ return [];
8566
+ }
8567
+ const { hostURL, hostKeyPair, encKey } = this;
8568
+ for (const [path] of await this.store.pullQueue.entries()) {
8569
+ const cipher = await api_default.getDelta(this.hostURL, path, hostKeyPair);
8570
+ const packed = await crypto_default.decryptXSalsa20Poly1305Combined(cipher, encKey);
8571
+ const op = decode(packed);
8572
+ try {
8573
+ await this.applier(op);
8574
+ this.store.pullQueue.dequeue(path);
8575
+ } catch {
8576
+ const transient = true;
8577
+ if (!transient) {
8578
+ this.store.pullQueue.dequeue(path);
8579
+ }
8580
+ }
8572
8581
  }
8573
8582
  }
8574
8583
  async pushQueuedOp(sha256) {
8575
8584
  if (!this.hostURL || !this.hostKeyPair) {
8576
8585
  return;
8577
8586
  }
8578
- const cipherOp = await this.store.peekUpload(sha256);
8587
+ const cipherOp = await this.store.pushQueue.peek(sha256);
8579
8588
  if (cipherOp) {
8580
8589
  await api_default.putDelta(this.hostURL, cipherOp, this.hostKeyPair);
8581
8590
  }
8582
- await this.store.dequeueUpload(sha256);
8591
+ await this.store.pushQueue.dequeue(sha256);
8583
8592
  }
8584
8593
  async pushQueuedOps() {
8585
- for (const sha256 of await this.store.listUploadQueue()) {
8586
- const bytes = htob(sha256);
8587
- await this.pushQueuedOp(bytes);
8594
+ for (const [sha256] of await this.store.pushQueue.entries()) {
8595
+ await this.pushQueuedOp(sha256);
8588
8596
  }
8589
8597
  }
8598
+ async sync() {
8599
+ await this.processOps();
8600
+ await this.pushQueuedOps();
8601
+ }
8590
8602
  async apply(op) {
8591
8603
  if (!this.encKey) {
8592
8604
  throw "No encryption key";
@@ -8594,13 +8606,14 @@ var DiplomaticClient = class {
8594
8606
  const packed = encode(op);
8595
8607
  const cipherOp = await crypto_default.encryptXSalsa20Poly1305Combined(packed, this.encKey);
8596
8608
  const sha256 = await crypto_default.sha256Hash(cipherOp);
8597
- await this.store.enqueueUpload(sha256, cipherOp);
8609
+ const shaHex = btoh(sha256);
8610
+ await this.store.pushQueue.enqueue(shaHex, cipherOp);
8598
8611
  try {
8599
8612
  await this.applier(op);
8600
8613
  } catch {
8601
- await this.store.dequeueUpload(sha256);
8614
+ await this.store.pushQueue.dequeue(shaHex);
8602
8615
  }
8603
- await this.pushQueuedOp(sha256);
8616
+ await this.pushQueuedOp(shaHex);
8604
8617
  }
8605
8618
  async upsert(type, body, version = 0) {
8606
8619
  const op = genUpsertOp(type, body, version);
@@ -1,3 +1,4 @@
1
+ import { Queue } from "./queue";
1
2
  import type { IClientStateStore } from "./types";
2
3
  declare class LocalStorageStore implements IClientStateStore {
3
4
  getSeed(): Promise<Uint8Array | undefined>;
@@ -6,14 +7,8 @@ declare class LocalStorageStore implements IClientStateStore {
6
7
  setHostURL(url: string): Promise<void>;
7
8
  getHostID(): Promise<string | undefined>;
8
9
  setHostID(id: string): Promise<void>;
9
- uploadQueue: Map<string, Uint8Array>;
10
- enqueueUpload: (sha256: Uint8Array, cipherOp: Uint8Array) => Promise<void>;
11
- dequeueUpload: (sha256: Uint8Array) => Promise<void>;
12
- peekUpload: (sha256: Uint8Array) => Promise<Uint8Array | undefined>;
13
- listUploadQueue(): Promise<string[]>;
14
- downloadQueue: Set<string>;
15
- enqueueDownload: (path: string) => Promise<void>;
16
- dequeueDownload: (path: string) => Promise<void>;
10
+ pushQueue: Queue<string, Uint8Array>;
11
+ pullQueue: Queue<string, null>;
17
12
  }
18
13
  export declare const localStorageStore: LocalStorageStore;
19
14
  export {};
@@ -0,0 +1,10 @@
1
+ import type { IQueue } from "./types";
2
+ export declare class Queue<K, V> implements IQueue<K, V> {
3
+ private map;
4
+ constructor();
5
+ enqueue(key: K, value: V): Promise<void>;
6
+ peek(key: K): Promise<V | undefined>;
7
+ entries(): Promise<Iterable<[K, V]>>;
8
+ dequeue(key: K): Promise<V | undefined>;
9
+ size(): Promise<number>;
10
+ }
package/dist/types.d.ts CHANGED
@@ -1,4 +1,34 @@
1
1
  import type { IOp } from "./shared/types";
2
+ export interface IQueue<K, V> {
3
+ /**
4
+ * Adds an entry to the queue.
5
+ * @param key - The key of the entry.
6
+ * @param value - The value of the entry.
7
+ */
8
+ enqueue(key: K, value: V): Promise<void>;
9
+ /**
10
+ * Returns the value of the entry with the specified key without removing it.
11
+ * @param key - The key of the entry.
12
+ * @returns The value of the entry if found, or undefined if the key does not exist.
13
+ */
14
+ peek(key: K): Promise<V | undefined>;
15
+ /**
16
+ * Returns an iterator for the queue, allowing iteration over all entries.
17
+ * @returns An iterator of entries in the queue.
18
+ */
19
+ entries(): Promise<Iterable<[K, V]>>;
20
+ /**
21
+ * Removes the entry with the specified key from the queue.
22
+ * @param key - The key of the entry.
23
+ * @returns The value of the removed entry if found, or undefined if the key does not exist.
24
+ */
25
+ dequeue(key: K): Promise<V | undefined>;
26
+ /**
27
+ * Returns the number of entries in the queue.
28
+ * @returns The size of the queue.
29
+ */
30
+ size(): Promise<number>;
31
+ }
2
32
  export interface IClientStateStore {
3
33
  init?: () => Promise<void>;
4
34
  getSeed: () => Promise<Uint8Array | undefined>;
@@ -7,12 +37,8 @@ export interface IClientStateStore {
7
37
  setHostURL: (url: string) => Promise<void>;
8
38
  getHostID: () => Promise<string | undefined>;
9
39
  setHostID: (id: string) => Promise<void>;
10
- enqueueUpload: (sha256: Uint8Array, cipherOp: Uint8Array) => Promise<void>;
11
- dequeueUpload: (sha256: Uint8Array) => Promise<void>;
12
- peekUpload: (sha256: Uint8Array) => Promise<Uint8Array | undefined>;
13
- listUploadQueue: () => Promise<string[]>;
14
- enqueueDownload: (path: string) => Promise<void>;
15
- dequeueDownload: (path: string) => Promise<void>;
40
+ pushQueue: IQueue<string, Uint8Array>;
41
+ pullQueue: IQueue<string, null>;
16
42
  }
17
43
  export type DiplomaticClientState = "loading" | "seedless" | "hostless" | "ready";
18
44
  export type Applier = (op: IOp) => Promise<void>;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@interncom/diplomatic",
3
- "version": "0.0.9",
3
+ "version": "0.0.10",
4
4
  "type": "module",
5
5
  "description": "Secure sync layer",
6
6
  "main": "dist/index.mjs",