@interncom/diplomatic 0.0.8 → 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.d.ts CHANGED
@@ -4,6 +4,6 @@ import { localStorageStore } from "./localStorageStore";
4
4
  import DiplomaticClient from "./client";
5
5
  import libsodiumCrypto from "./crypto";
6
6
  import { btoh, htob } from "./shared/lib";
7
- import type { IOp, Verb } from "./shared/types";
8
- export { useClientState, StateManager, useStateWatcher, localStorageStore, DiplomaticClient, libsodiumCrypto, btoh, htob, };
9
- export type { IOp, Verb, };
7
+ import { type IOp, Verb } from "./shared/types";
8
+ export { useClientState, StateManager, useStateWatcher, localStorageStore, DiplomaticClient, libsodiumCrypto, btoh, htob, Verb, };
9
+ export type { IOp, };
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
 
@@ -8409,6 +8413,13 @@ var msgpack = {
8409
8413
  var webClientAPI = new DiplomaticClientAPI(msgpack, crypto_default);
8410
8414
  var api_default = webClientAPI;
8411
8415
 
8416
+ // ../shared/types.ts
8417
+ var Verb = /* @__PURE__ */ ((Verb2) => {
8418
+ Verb2[Verb2["DELETE"] = 0] = "DELETE";
8419
+ Verb2[Verb2["UPSERT"] = 1] = "UPSERT";
8420
+ return Verb2;
8421
+ })(Verb || {});
8422
+
8412
8423
  // ../shared/ops.ts
8413
8424
  function genUpsertOp(type, body, version = 0) {
8414
8425
  return {
@@ -8433,7 +8444,6 @@ var DiplomaticClient = class {
8433
8444
  constructor(params) {
8434
8445
  this.store = params.store;
8435
8446
  this.applier = params.stateManager.apply;
8436
- console.log("constructing");
8437
8447
  this.init(params);
8438
8448
  }
8439
8449
  websocket;
@@ -8459,7 +8469,7 @@ var DiplomaticClient = class {
8459
8469
  };
8460
8470
  this.websocket.onmessage = (e) => {
8461
8471
  console.log(`RECEIVED: ${e.data}`);
8462
- this.processDeltas();
8472
+ this.processOps();
8463
8473
  };
8464
8474
  this.websocket.onerror = (e) => {
8465
8475
  console.log(`ERROR: ${e}`);
@@ -8483,7 +8493,7 @@ var DiplomaticClient = class {
8483
8493
  if (this.hostURL) {
8484
8494
  await this.connect(this.hostURL);
8485
8495
  }
8486
- await this.processDeltas();
8496
+ await this.sync();
8487
8497
  this.listener?.(this.state);
8488
8498
  }
8489
8499
  async loadSeed() {
@@ -8533,53 +8543,62 @@ var DiplomaticClient = class {
8533
8543
  }
8534
8544
  return "ready";
8535
8545
  }
8536
- async putDelta(delta) {
8537
- if (!this.hostURL || !this.hostKeyPair || !this.encKey) {
8538
- return;
8539
- }
8540
- const packed = encode(delta);
8541
- const cipherOp = await crypto_default.encryptXSalsa20Poly1305Combined(packed, this.encKey);
8542
- await api_default.putDelta(this.hostURL, cipherOp, this.hostKeyPair);
8546
+ async processOps() {
8547
+ await this.pullOpPaths();
8548
+ await this.fetchAndExecQueuedOps();
8543
8549
  }
8544
- async getDeltas() {
8550
+ async pullOpPaths() {
8545
8551
  if (!this.hostURL || !this.hostKeyPair || !this.encKey) {
8546
8552
  return [];
8547
8553
  }
8548
8554
  const begin = new Date(this.lastFetchedAt ?? 0);
8549
- const { hostURL, hostKeyPair, encKey } = this;
8555
+ const { hostURL, hostKeyPair } = this;
8550
8556
  const pathResp = await api_default.getDeltaPaths(hostURL, begin, hostKeyPair);
8551
8557
  const paths = pathResp.paths;
8558
+ for (const path of paths) {
8559
+ await this.store.pullQueue.enqueue(path, null);
8560
+ }
8552
8561
  this.lastFetchedAt = pathResp.fetchedAt;
8553
- const deltas = await Promise.all(paths.map(async (path) => {
8554
- const cipher = await api_default.getDelta(hostURL, path, hostKeyPair);
8555
- const deltaPack = await crypto_default.decryptXSalsa20Poly1305Combined(cipher, encKey);
8556
- const delta = decode(deltaPack);
8557
- return delta;
8558
- }));
8559
- return deltas;
8560
- }
8561
- async processDeltas() {
8562
- const deltas = await this.getDeltas();
8563
- for (const delta of deltas) {
8564
- 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
+ }
8565
8581
  }
8566
8582
  }
8567
8583
  async pushQueuedOp(sha256) {
8568
8584
  if (!this.hostURL || !this.hostKeyPair) {
8569
8585
  return;
8570
8586
  }
8571
- const cipherOp = await this.store.peekUpload(sha256);
8587
+ const cipherOp = await this.store.pushQueue.peek(sha256);
8572
8588
  if (cipherOp) {
8573
8589
  await api_default.putDelta(this.hostURL, cipherOp, this.hostKeyPair);
8574
8590
  }
8575
- await this.store.dequeueUpload(sha256);
8591
+ await this.store.pushQueue.dequeue(sha256);
8576
8592
  }
8577
8593
  async pushQueuedOps() {
8578
- for (const sha256 of await this.store.listUploadQueue()) {
8579
- const bytes = htob(sha256);
8580
- await this.pushQueuedOp(bytes);
8594
+ for (const [sha256] of await this.store.pushQueue.entries()) {
8595
+ await this.pushQueuedOp(sha256);
8581
8596
  }
8582
8597
  }
8598
+ async sync() {
8599
+ await this.processOps();
8600
+ await this.pushQueuedOps();
8601
+ }
8583
8602
  async apply(op) {
8584
8603
  if (!this.encKey) {
8585
8604
  throw "No encryption key";
@@ -8587,13 +8606,14 @@ var DiplomaticClient = class {
8587
8606
  const packed = encode(op);
8588
8607
  const cipherOp = await crypto_default.encryptXSalsa20Poly1305Combined(packed, this.encKey);
8589
8608
  const sha256 = await crypto_default.sha256Hash(cipherOp);
8590
- await this.store.enqueueUpload(sha256, cipherOp);
8609
+ const shaHex = btoh(sha256);
8610
+ await this.store.pushQueue.enqueue(shaHex, cipherOp);
8591
8611
  try {
8592
8612
  await this.applier(op);
8593
8613
  } catch {
8594
- await this.store.dequeueUpload(sha256);
8614
+ await this.store.pushQueue.dequeue(shaHex);
8595
8615
  }
8596
- await this.pushQueuedOp(sha256);
8616
+ await this.pushQueuedOp(shaHex);
8597
8617
  }
8598
8618
  async upsert(type, body, version = 0) {
8599
8619
  const op = genUpsertOp(type, body, version);
@@ -8603,6 +8623,7 @@ var DiplomaticClient = class {
8603
8623
  export {
8604
8624
  DiplomaticClient,
8605
8625
  StateManager,
8626
+ Verb,
8606
8627
  btoh,
8607
8628
  htob,
8608
8629
  crypto_default as libsodiumCrypto,
@@ -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.8",
3
+ "version": "0.0.10",
4
4
  "type": "module",
5
5
  "description": "Secure sync layer",
6
6
  "main": "dist/index.mjs",