ping-openmls-sdk 0.0.1
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/README.md +63 -0
- package/dist/index.cjs +463 -0
- package/dist/index.cjs.map +1 -0
- package/dist/index.d.cts +73 -0
- package/dist/index.d.ts +73 -0
- package/dist/index.js +433 -0
- package/dist/index.js.map +1 -0
- package/dist/storage/indexeddb.cjs +97 -0
- package/dist/storage/indexeddb.cjs.map +1 -0
- package/dist/storage/indexeddb.d.cts +12 -0
- package/dist/storage/indexeddb.d.ts +12 -0
- package/dist/storage/indexeddb.js +72 -0
- package/dist/storage/indexeddb.js.map +1 -0
- package/dist/transport/websocket.cjs +112 -0
- package/dist/transport/websocket.cjs.map +1 -0
- package/dist/transport/websocket.d.cts +23 -0
- package/dist/transport/websocket.d.ts +23 -0
- package/dist/transport/websocket.js +87 -0
- package/dist/transport/websocket.js.map +1 -0
- package/dist/types-COHY5NU8.d.cts +74 -0
- package/dist/types-COHY5NU8.d.ts +74 -0
- package/dist/worker.cjs +155 -0
- package/dist/worker.cjs.map +1 -0
- package/dist/worker.d.cts +2 -0
- package/dist/worker.d.ts +2 -0
- package/dist/worker.js +131 -0
- package/dist/worker.js.map +1 -0
- package/package.json +50 -0
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../../src/storage/indexeddb.ts"],"sourcesContent":["// Default IndexedDB-backed Storage implementation. Hosts can supply their own.\n\nimport type { Storage } from \"../types\";\n\nconst DB_NAME = \"ping-sdk\";\nconst DB_VERSION = 1;\nconst STORE = \"kv\";\n\ninterface Row { ns: string; key: string; value: Uint8Array }\n\nfunction open(): Promise<IDBDatabase> {\n return new Promise((resolve, reject) => {\n const req = indexedDB.open(DB_NAME, DB_VERSION);\n req.onupgradeneeded = () => {\n const db = req.result;\n if (!db.objectStoreNames.contains(STORE)) {\n const os = db.createObjectStore(STORE, { keyPath: [\"ns\", \"key\"] });\n os.createIndex(\"ns\", \"ns\", { unique: false });\n }\n };\n req.onsuccess = () => resolve(req.result);\n req.onerror = () => reject(req.error);\n });\n}\n\nexport class IndexedDbStorage implements Storage {\n private dbPromise: Promise<IDBDatabase>;\n\n constructor() { this.dbPromise = open(); }\n\n async get(namespace: string, key: string): Promise<Uint8Array | null> {\n const db = await this.dbPromise;\n return new Promise((resolve, reject) => {\n const tx = db.transaction(STORE, \"readonly\");\n const r = tx.objectStore(STORE).get([namespace, key]);\n r.onsuccess = () => resolve(((r.result as Row | undefined)?.value) ?? null);\n r.onerror = () => reject(r.error);\n });\n }\n\n async put(namespace: string, key: string, value: Uint8Array): Promise<void> {\n const db = await this.dbPromise;\n return new Promise((resolve, reject) => {\n const tx = db.transaction(STORE, \"readwrite\");\n tx.objectStore(STORE).put({ ns: namespace, key, value } as Row);\n tx.oncomplete = () => resolve();\n tx.onerror = () => reject(tx.error);\n });\n }\n\n async del(namespace: string, key: string): Promise<void> {\n const db = await this.dbPromise;\n return new Promise((resolve, reject) => {\n const tx = db.transaction(STORE, \"readwrite\");\n tx.objectStore(STORE).delete([namespace, key]);\n tx.oncomplete = () => resolve();\n tx.onerror = () => reject(tx.error);\n });\n }\n\n async listKeys(namespace: string, prefix: string): Promise<string[]> {\n const db = await this.dbPromise;\n return new Promise((resolve, reject) => {\n const tx = db.transaction(STORE, \"readonly\");\n const idx = tx.objectStore(STORE).index(\"ns\");\n const req = idx.openCursor(IDBKeyRange.only(namespace));\n const out: string[] = [];\n req.onsuccess = () => {\n const cur = req.result;\n if (!cur) return resolve(out);\n const row = cur.value as Row;\n if (!prefix || row.key.startsWith(prefix)) out.push(row.key);\n cur.continue();\n };\n req.onerror = () => reject(req.error);\n });\n }\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;;;AAAA;AAAA;AAAA;AAAA;AAAA;AAIA,IAAM,UAAU;AAChB,IAAM,aAAa;AACnB,IAAM,QAAQ;AAId,SAAS,OAA6B;AACpC,SAAO,IAAI,QAAQ,CAAC,SAAS,WAAW;AACtC,UAAM,MAAM,UAAU,KAAK,SAAS,UAAU;AAC9C,QAAI,kBAAkB,MAAM;AAC1B,YAAM,KAAK,IAAI;AACf,UAAI,CAAC,GAAG,iBAAiB,SAAS,KAAK,GAAG;AACxC,cAAM,KAAK,GAAG,kBAAkB,OAAO,EAAE,SAAS,CAAC,MAAM,KAAK,EAAE,CAAC;AACjE,WAAG,YAAY,MAAM,MAAM,EAAE,QAAQ,MAAM,CAAC;AAAA,MAC9C;AAAA,IACF;AACA,QAAI,YAAY,MAAM,QAAQ,IAAI,MAAM;AACxC,QAAI,UAAU,MAAM,OAAO,IAAI,KAAK;AAAA,EACtC,CAAC;AACH;AAEO,IAAM,mBAAN,MAA0C;AAAA,EACvC;AAAA,EAER,cAAc;AAAE,SAAK,YAAY,KAAK;AAAA,EAAG;AAAA,EAEzC,MAAM,IAAI,WAAmB,KAAyC;AACpE,UAAM,KAAK,MAAM,KAAK;AACtB,WAAO,IAAI,QAAQ,CAAC,SAAS,WAAW;AACtC,YAAM,KAAK,GAAG,YAAY,OAAO,UAAU;AAC3C,YAAM,IAAI,GAAG,YAAY,KAAK,EAAE,IAAI,CAAC,WAAW,GAAG,CAAC;AACpD,QAAE,YAAY,MAAM,QAAU,EAAE,QAA4B,SAAU,IAAI;AAC1E,QAAE,UAAU,MAAM,OAAO,EAAE,KAAK;AAAA,IAClC,CAAC;AAAA,EACH;AAAA,EAEA,MAAM,IAAI,WAAmB,KAAa,OAAkC;AAC1E,UAAM,KAAK,MAAM,KAAK;AACtB,WAAO,IAAI,QAAQ,CAAC,SAAS,WAAW;AACtC,YAAM,KAAK,GAAG,YAAY,OAAO,WAAW;AAC5C,SAAG,YAAY,KAAK,EAAE,IAAI,EAAE,IAAI,WAAW,KAAK,MAAM,CAAQ;AAC9D,SAAG,aAAa,MAAM,QAAQ;AAC9B,SAAG,UAAU,MAAM,OAAO,GAAG,KAAK;AAAA,IACpC,CAAC;AAAA,EACH;AAAA,EAEA,MAAM,IAAI,WAAmB,KAA4B;AACvD,UAAM,KAAK,MAAM,KAAK;AACtB,WAAO,IAAI,QAAQ,CAAC,SAAS,WAAW;AACtC,YAAM,KAAK,GAAG,YAAY,OAAO,WAAW;AAC5C,SAAG,YAAY,KAAK,EAAE,OAAO,CAAC,WAAW,GAAG,CAAC;AAC7C,SAAG,aAAa,MAAM,QAAQ;AAC9B,SAAG,UAAU,MAAM,OAAO,GAAG,KAAK;AAAA,IACpC,CAAC;AAAA,EACH;AAAA,EAEA,MAAM,SAAS,WAAmB,QAAmC;AACnE,UAAM,KAAK,MAAM,KAAK;AACtB,WAAO,IAAI,QAAQ,CAAC,SAAS,WAAW;AACtC,YAAM,KAAK,GAAG,YAAY,OAAO,UAAU;AAC3C,YAAM,MAAM,GAAG,YAAY,KAAK,EAAE,MAAM,IAAI;AAC5C,YAAM,MAAM,IAAI,WAAW,YAAY,KAAK,SAAS,CAAC;AACtD,YAAM,MAAgB,CAAC;AACvB,UAAI,YAAY,MAAM;AACpB,cAAM,MAAM,IAAI;AAChB,YAAI,CAAC,IAAK,QAAO,QAAQ,GAAG;AAC5B,cAAM,MAAM,IAAI;AAChB,YAAI,CAAC,UAAU,IAAI,IAAI,WAAW,MAAM,EAAG,KAAI,KAAK,IAAI,GAAG;AAC3D,YAAI,SAAS;AAAA,MACf;AACA,UAAI,UAAU,MAAM,OAAO,IAAI,KAAK;AAAA,IACtC,CAAC;AAAA,EACH;AACF;","names":[]}
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import { S as Storage } from '../types-COHY5NU8.cjs';
|
|
2
|
+
|
|
3
|
+
declare class IndexedDbStorage implements Storage {
|
|
4
|
+
private dbPromise;
|
|
5
|
+
constructor();
|
|
6
|
+
get(namespace: string, key: string): Promise<Uint8Array | null>;
|
|
7
|
+
put(namespace: string, key: string, value: Uint8Array): Promise<void>;
|
|
8
|
+
del(namespace: string, key: string): Promise<void>;
|
|
9
|
+
listKeys(namespace: string, prefix: string): Promise<string[]>;
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
export { IndexedDbStorage };
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import { S as Storage } from '../types-COHY5NU8.js';
|
|
2
|
+
|
|
3
|
+
declare class IndexedDbStorage implements Storage {
|
|
4
|
+
private dbPromise;
|
|
5
|
+
constructor();
|
|
6
|
+
get(namespace: string, key: string): Promise<Uint8Array | null>;
|
|
7
|
+
put(namespace: string, key: string, value: Uint8Array): Promise<void>;
|
|
8
|
+
del(namespace: string, key: string): Promise<void>;
|
|
9
|
+
listKeys(namespace: string, prefix: string): Promise<string[]>;
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
export { IndexedDbStorage };
|
|
@@ -0,0 +1,72 @@
|
|
|
1
|
+
// src/storage/indexeddb.ts
|
|
2
|
+
var DB_NAME = "ping-sdk";
|
|
3
|
+
var DB_VERSION = 1;
|
|
4
|
+
var STORE = "kv";
|
|
5
|
+
function open() {
|
|
6
|
+
return new Promise((resolve, reject) => {
|
|
7
|
+
const req = indexedDB.open(DB_NAME, DB_VERSION);
|
|
8
|
+
req.onupgradeneeded = () => {
|
|
9
|
+
const db = req.result;
|
|
10
|
+
if (!db.objectStoreNames.contains(STORE)) {
|
|
11
|
+
const os = db.createObjectStore(STORE, { keyPath: ["ns", "key"] });
|
|
12
|
+
os.createIndex("ns", "ns", { unique: false });
|
|
13
|
+
}
|
|
14
|
+
};
|
|
15
|
+
req.onsuccess = () => resolve(req.result);
|
|
16
|
+
req.onerror = () => reject(req.error);
|
|
17
|
+
});
|
|
18
|
+
}
|
|
19
|
+
var IndexedDbStorage = class {
|
|
20
|
+
dbPromise;
|
|
21
|
+
constructor() {
|
|
22
|
+
this.dbPromise = open();
|
|
23
|
+
}
|
|
24
|
+
async get(namespace, key) {
|
|
25
|
+
const db = await this.dbPromise;
|
|
26
|
+
return new Promise((resolve, reject) => {
|
|
27
|
+
const tx = db.transaction(STORE, "readonly");
|
|
28
|
+
const r = tx.objectStore(STORE).get([namespace, key]);
|
|
29
|
+
r.onsuccess = () => resolve(r.result?.value ?? null);
|
|
30
|
+
r.onerror = () => reject(r.error);
|
|
31
|
+
});
|
|
32
|
+
}
|
|
33
|
+
async put(namespace, key, value) {
|
|
34
|
+
const db = await this.dbPromise;
|
|
35
|
+
return new Promise((resolve, reject) => {
|
|
36
|
+
const tx = db.transaction(STORE, "readwrite");
|
|
37
|
+
tx.objectStore(STORE).put({ ns: namespace, key, value });
|
|
38
|
+
tx.oncomplete = () => resolve();
|
|
39
|
+
tx.onerror = () => reject(tx.error);
|
|
40
|
+
});
|
|
41
|
+
}
|
|
42
|
+
async del(namespace, key) {
|
|
43
|
+
const db = await this.dbPromise;
|
|
44
|
+
return new Promise((resolve, reject) => {
|
|
45
|
+
const tx = db.transaction(STORE, "readwrite");
|
|
46
|
+
tx.objectStore(STORE).delete([namespace, key]);
|
|
47
|
+
tx.oncomplete = () => resolve();
|
|
48
|
+
tx.onerror = () => reject(tx.error);
|
|
49
|
+
});
|
|
50
|
+
}
|
|
51
|
+
async listKeys(namespace, prefix) {
|
|
52
|
+
const db = await this.dbPromise;
|
|
53
|
+
return new Promise((resolve, reject) => {
|
|
54
|
+
const tx = db.transaction(STORE, "readonly");
|
|
55
|
+
const idx = tx.objectStore(STORE).index("ns");
|
|
56
|
+
const req = idx.openCursor(IDBKeyRange.only(namespace));
|
|
57
|
+
const out = [];
|
|
58
|
+
req.onsuccess = () => {
|
|
59
|
+
const cur = req.result;
|
|
60
|
+
if (!cur) return resolve(out);
|
|
61
|
+
const row = cur.value;
|
|
62
|
+
if (!prefix || row.key.startsWith(prefix)) out.push(row.key);
|
|
63
|
+
cur.continue();
|
|
64
|
+
};
|
|
65
|
+
req.onerror = () => reject(req.error);
|
|
66
|
+
});
|
|
67
|
+
}
|
|
68
|
+
};
|
|
69
|
+
export {
|
|
70
|
+
IndexedDbStorage
|
|
71
|
+
};
|
|
72
|
+
//# sourceMappingURL=indexeddb.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../../src/storage/indexeddb.ts"],"sourcesContent":["// Default IndexedDB-backed Storage implementation. Hosts can supply their own.\n\nimport type { Storage } from \"../types\";\n\nconst DB_NAME = \"ping-sdk\";\nconst DB_VERSION = 1;\nconst STORE = \"kv\";\n\ninterface Row { ns: string; key: string; value: Uint8Array }\n\nfunction open(): Promise<IDBDatabase> {\n return new Promise((resolve, reject) => {\n const req = indexedDB.open(DB_NAME, DB_VERSION);\n req.onupgradeneeded = () => {\n const db = req.result;\n if (!db.objectStoreNames.contains(STORE)) {\n const os = db.createObjectStore(STORE, { keyPath: [\"ns\", \"key\"] });\n os.createIndex(\"ns\", \"ns\", { unique: false });\n }\n };\n req.onsuccess = () => resolve(req.result);\n req.onerror = () => reject(req.error);\n });\n}\n\nexport class IndexedDbStorage implements Storage {\n private dbPromise: Promise<IDBDatabase>;\n\n constructor() { this.dbPromise = open(); }\n\n async get(namespace: string, key: string): Promise<Uint8Array | null> {\n const db = await this.dbPromise;\n return new Promise((resolve, reject) => {\n const tx = db.transaction(STORE, \"readonly\");\n const r = tx.objectStore(STORE).get([namespace, key]);\n r.onsuccess = () => resolve(((r.result as Row | undefined)?.value) ?? null);\n r.onerror = () => reject(r.error);\n });\n }\n\n async put(namespace: string, key: string, value: Uint8Array): Promise<void> {\n const db = await this.dbPromise;\n return new Promise((resolve, reject) => {\n const tx = db.transaction(STORE, \"readwrite\");\n tx.objectStore(STORE).put({ ns: namespace, key, value } as Row);\n tx.oncomplete = () => resolve();\n tx.onerror = () => reject(tx.error);\n });\n }\n\n async del(namespace: string, key: string): Promise<void> {\n const db = await this.dbPromise;\n return new Promise((resolve, reject) => {\n const tx = db.transaction(STORE, \"readwrite\");\n tx.objectStore(STORE).delete([namespace, key]);\n tx.oncomplete = () => resolve();\n tx.onerror = () => reject(tx.error);\n });\n }\n\n async listKeys(namespace: string, prefix: string): Promise<string[]> {\n const db = await this.dbPromise;\n return new Promise((resolve, reject) => {\n const tx = db.transaction(STORE, \"readonly\");\n const idx = tx.objectStore(STORE).index(\"ns\");\n const req = idx.openCursor(IDBKeyRange.only(namespace));\n const out: string[] = [];\n req.onsuccess = () => {\n const cur = req.result;\n if (!cur) return resolve(out);\n const row = cur.value as Row;\n if (!prefix || row.key.startsWith(prefix)) out.push(row.key);\n cur.continue();\n };\n req.onerror = () => reject(req.error);\n });\n }\n}\n"],"mappings":";AAIA,IAAM,UAAU;AAChB,IAAM,aAAa;AACnB,IAAM,QAAQ;AAId,SAAS,OAA6B;AACpC,SAAO,IAAI,QAAQ,CAAC,SAAS,WAAW;AACtC,UAAM,MAAM,UAAU,KAAK,SAAS,UAAU;AAC9C,QAAI,kBAAkB,MAAM;AAC1B,YAAM,KAAK,IAAI;AACf,UAAI,CAAC,GAAG,iBAAiB,SAAS,KAAK,GAAG;AACxC,cAAM,KAAK,GAAG,kBAAkB,OAAO,EAAE,SAAS,CAAC,MAAM,KAAK,EAAE,CAAC;AACjE,WAAG,YAAY,MAAM,MAAM,EAAE,QAAQ,MAAM,CAAC;AAAA,MAC9C;AAAA,IACF;AACA,QAAI,YAAY,MAAM,QAAQ,IAAI,MAAM;AACxC,QAAI,UAAU,MAAM,OAAO,IAAI,KAAK;AAAA,EACtC,CAAC;AACH;AAEO,IAAM,mBAAN,MAA0C;AAAA,EACvC;AAAA,EAER,cAAc;AAAE,SAAK,YAAY,KAAK;AAAA,EAAG;AAAA,EAEzC,MAAM,IAAI,WAAmB,KAAyC;AACpE,UAAM,KAAK,MAAM,KAAK;AACtB,WAAO,IAAI,QAAQ,CAAC,SAAS,WAAW;AACtC,YAAM,KAAK,GAAG,YAAY,OAAO,UAAU;AAC3C,YAAM,IAAI,GAAG,YAAY,KAAK,EAAE,IAAI,CAAC,WAAW,GAAG,CAAC;AACpD,QAAE,YAAY,MAAM,QAAU,EAAE,QAA4B,SAAU,IAAI;AAC1E,QAAE,UAAU,MAAM,OAAO,EAAE,KAAK;AAAA,IAClC,CAAC;AAAA,EACH;AAAA,EAEA,MAAM,IAAI,WAAmB,KAAa,OAAkC;AAC1E,UAAM,KAAK,MAAM,KAAK;AACtB,WAAO,IAAI,QAAQ,CAAC,SAAS,WAAW;AACtC,YAAM,KAAK,GAAG,YAAY,OAAO,WAAW;AAC5C,SAAG,YAAY,KAAK,EAAE,IAAI,EAAE,IAAI,WAAW,KAAK,MAAM,CAAQ;AAC9D,SAAG,aAAa,MAAM,QAAQ;AAC9B,SAAG,UAAU,MAAM,OAAO,GAAG,KAAK;AAAA,IACpC,CAAC;AAAA,EACH;AAAA,EAEA,MAAM,IAAI,WAAmB,KAA4B;AACvD,UAAM,KAAK,MAAM,KAAK;AACtB,WAAO,IAAI,QAAQ,CAAC,SAAS,WAAW;AACtC,YAAM,KAAK,GAAG,YAAY,OAAO,WAAW;AAC5C,SAAG,YAAY,KAAK,EAAE,OAAO,CAAC,WAAW,GAAG,CAAC;AAC7C,SAAG,aAAa,MAAM,QAAQ;AAC9B,SAAG,UAAU,MAAM,OAAO,GAAG,KAAK;AAAA,IACpC,CAAC;AAAA,EACH;AAAA,EAEA,MAAM,SAAS,WAAmB,QAAmC;AACnE,UAAM,KAAK,MAAM,KAAK;AACtB,WAAO,IAAI,QAAQ,CAAC,SAAS,WAAW;AACtC,YAAM,KAAK,GAAG,YAAY,OAAO,UAAU;AAC3C,YAAM,MAAM,GAAG,YAAY,KAAK,EAAE,MAAM,IAAI;AAC5C,YAAM,MAAM,IAAI,WAAW,YAAY,KAAK,SAAS,CAAC;AACtD,YAAM,MAAgB,CAAC;AACvB,UAAI,YAAY,MAAM;AACpB,cAAM,MAAM,IAAI;AAChB,YAAI,CAAC,IAAK,QAAO,QAAQ,GAAG;AAC5B,cAAM,MAAM,IAAI;AAChB,YAAI,CAAC,UAAU,IAAI,IAAI,WAAW,MAAM,EAAG,KAAI,KAAK,IAAI,GAAG;AAC3D,YAAI,SAAS;AAAA,MACf;AACA,UAAI,UAAU,MAAM,OAAO,IAAI,KAAK;AAAA,IACtC,CAAC;AAAA,EACH;AACF;","names":[]}
|
|
@@ -0,0 +1,112 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __defProp = Object.defineProperty;
|
|
3
|
+
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
|
|
4
|
+
var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
5
|
+
var __hasOwnProp = Object.prototype.hasOwnProperty;
|
|
6
|
+
var __export = (target, all) => {
|
|
7
|
+
for (var name in all)
|
|
8
|
+
__defProp(target, name, { get: all[name], enumerable: true });
|
|
9
|
+
};
|
|
10
|
+
var __copyProps = (to, from, except, desc) => {
|
|
11
|
+
if (from && typeof from === "object" || typeof from === "function") {
|
|
12
|
+
for (let key of __getOwnPropNames(from))
|
|
13
|
+
if (!__hasOwnProp.call(to, key) && key !== except)
|
|
14
|
+
__defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
|
|
15
|
+
}
|
|
16
|
+
return to;
|
|
17
|
+
};
|
|
18
|
+
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
|
|
19
|
+
|
|
20
|
+
// src/transport/websocket.ts
|
|
21
|
+
var websocket_exports = {};
|
|
22
|
+
__export(websocket_exports, {
|
|
23
|
+
WebSocketTransport: () => WebSocketTransport
|
|
24
|
+
});
|
|
25
|
+
module.exports = __toCommonJS(websocket_exports);
|
|
26
|
+
var WebSocketTransport = class {
|
|
27
|
+
constructor(cfg) {
|
|
28
|
+
this.cfg = cfg;
|
|
29
|
+
this.fetchImpl = cfg.fetchImpl ?? globalThis.fetch.bind(globalThis);
|
|
30
|
+
this.openSocket();
|
|
31
|
+
}
|
|
32
|
+
cfg;
|
|
33
|
+
ws = null;
|
|
34
|
+
liveHandlers = /* @__PURE__ */ new Set();
|
|
35
|
+
fetchImpl;
|
|
36
|
+
openSocket() {
|
|
37
|
+
if (typeof WebSocket === "undefined") return;
|
|
38
|
+
const url = `${this.cfg.baseUrl.replace(/^http/, "ws")}/stream`;
|
|
39
|
+
this.ws = new WebSocket(url);
|
|
40
|
+
this.ws.onmessage = (ev) => {
|
|
41
|
+
const text = typeof ev.data === "string" ? ev.data : new TextDecoder().decode(ev.data);
|
|
42
|
+
try {
|
|
43
|
+
const env = JSON.parse(text, reviver);
|
|
44
|
+
for (const h of this.liveHandlers) h(env);
|
|
45
|
+
} catch (e) {
|
|
46
|
+
if (typeof console !== "undefined") console.debug("[ping-sdk] WS frame parse error:", e);
|
|
47
|
+
}
|
|
48
|
+
};
|
|
49
|
+
this.ws.onclose = () => setTimeout(() => this.openSocket(), 1e3);
|
|
50
|
+
}
|
|
51
|
+
async send(envelope) {
|
|
52
|
+
const res = await this.fetchImpl(`${this.cfg.baseUrl}/send`, {
|
|
53
|
+
method: "POST",
|
|
54
|
+
headers: {
|
|
55
|
+
"content-type": "application/json",
|
|
56
|
+
...this.cfg.authHeader ? { authorization: this.cfg.authHeader } : {}
|
|
57
|
+
},
|
|
58
|
+
body: JSON.stringify(envelope, replacer)
|
|
59
|
+
});
|
|
60
|
+
if (res.status === 409) throw new Error("EpochOccupied");
|
|
61
|
+
if (!res.ok) throw new Error(`transport send failed: ${res.status}`);
|
|
62
|
+
}
|
|
63
|
+
async fetchSince(conversationId, cursor, limit) {
|
|
64
|
+
const url = new URL(`${this.cfg.baseUrl}/sync`);
|
|
65
|
+
url.searchParams.set("conv", toHex(conversationId));
|
|
66
|
+
url.searchParams.set("cursor", btoaUrl(cursor));
|
|
67
|
+
url.searchParams.set("limit", String(limit));
|
|
68
|
+
const res = await this.fetchImpl(url.toString(), {
|
|
69
|
+
headers: { ...this.cfg.authHeader ? { authorization: this.cfg.authHeader } : {} }
|
|
70
|
+
});
|
|
71
|
+
if (!res.ok) throw new Error(`transport fetch failed: ${res.status}`);
|
|
72
|
+
const text = await res.text();
|
|
73
|
+
return JSON.parse(text, reviver);
|
|
74
|
+
}
|
|
75
|
+
subscribe(onEvent) {
|
|
76
|
+
this.liveHandlers.add(onEvent);
|
|
77
|
+
return { unsubscribe: () => this.liveHandlers.delete(onEvent) };
|
|
78
|
+
}
|
|
79
|
+
async discoverDevices(userId) {
|
|
80
|
+
const res = await this.fetchImpl(`${this.cfg.baseUrl}/devices/${toHex(userId)}`, {
|
|
81
|
+
headers: { ...this.cfg.authHeader ? { authorization: this.cfg.authHeader } : {} }
|
|
82
|
+
});
|
|
83
|
+
if (!res.ok) throw new Error(`device discovery failed: ${res.status}`);
|
|
84
|
+
const text = await res.text();
|
|
85
|
+
return JSON.parse(text, reviver);
|
|
86
|
+
}
|
|
87
|
+
};
|
|
88
|
+
function replacer(_k, v) {
|
|
89
|
+
if (v instanceof Uint8Array) return { _bytes: Array.from(v) };
|
|
90
|
+
return v;
|
|
91
|
+
}
|
|
92
|
+
function reviver(_k, v) {
|
|
93
|
+
if (v && typeof v === "object" && Array.isArray(v._bytes)) {
|
|
94
|
+
return new Uint8Array(v._bytes);
|
|
95
|
+
}
|
|
96
|
+
return v;
|
|
97
|
+
}
|
|
98
|
+
function toHex(b) {
|
|
99
|
+
let s = "";
|
|
100
|
+
for (const v of b) s += v.toString(16).padStart(2, "0");
|
|
101
|
+
return s;
|
|
102
|
+
}
|
|
103
|
+
function btoaUrl(b) {
|
|
104
|
+
let s = "";
|
|
105
|
+
for (const v of b) s += String.fromCharCode(v);
|
|
106
|
+
return btoa(s).replace(/\+/g, "-").replace(/\//g, "_").replace(/=+$/, "");
|
|
107
|
+
}
|
|
108
|
+
// Annotate the CommonJS export names for ESM import in node:
|
|
109
|
+
0 && (module.exports = {
|
|
110
|
+
WebSocketTransport
|
|
111
|
+
});
|
|
112
|
+
//# sourceMappingURL=websocket.cjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../../src/transport/websocket.ts"],"sourcesContent":["// Reference WebSocket transport. Treats the server as an opaque relay that:\n// - accepts a CBOR-encoded MessageEnvelope on `send`\n// - returns events newer than a cursor on `fetchSince`\n// - streams new events on the WS itself for `subscribe`\n// - exposes a directory endpoint for `discoverDevices`\n//\n// This is a sample. Real deployments will tune framing / auth.\n\nimport type { ConversationId, DiscoveredDevice, MessageEnvelope, Transport, UserId } from \"../types\";\n\nexport interface WsTransportConfig {\n baseUrl: string; // ws[s]://...\n authHeader?: string; // optional bearer token\n fetchImpl?: typeof fetch; // override for SSR / tests\n}\n\nexport class WebSocketTransport implements Transport {\n private ws: WebSocket | null = null;\n private liveHandlers = new Set<(env: MessageEnvelope) => void>();\n private fetchImpl: typeof fetch;\n\n constructor(private cfg: WsTransportConfig) {\n this.fetchImpl = cfg.fetchImpl ?? globalThis.fetch.bind(globalThis);\n this.openSocket();\n }\n\n private openSocket() {\n if (typeof WebSocket === \"undefined\") return;\n const url = `${this.cfg.baseUrl.replace(/^http/, \"ws\")}/stream`;\n this.ws = new WebSocket(url);\n this.ws.onmessage = (ev) => {\n // Server sends one JSON envelope per text frame.\n const text = typeof ev.data === \"string\" ? ev.data : new TextDecoder().decode(ev.data as ArrayBuffer);\n try {\n const env = JSON.parse(text, reviver) as MessageEnvelope;\n for (const h of this.liveHandlers) h(env);\n } catch (e) {\n if (typeof console !== \"undefined\") console.debug(\"[ping-sdk] WS frame parse error:\", e);\n }\n };\n this.ws.onclose = () => setTimeout(() => this.openSocket(), 1000);\n }\n\n async send(envelope: MessageEnvelope): Promise<void> {\n const res = await this.fetchImpl(`${this.cfg.baseUrl}/send`, {\n method: \"POST\",\n headers: {\n \"content-type\": \"application/json\",\n ...(this.cfg.authHeader ? { authorization: this.cfg.authHeader } : {}),\n },\n body: JSON.stringify(envelope, replacer),\n });\n if (res.status === 409) throw new Error(\"EpochOccupied\");\n if (!res.ok) throw new Error(`transport send failed: ${res.status}`);\n }\n\n async fetchSince(conversationId: ConversationId, cursor: Uint8Array, limit: number): Promise<MessageEnvelope[]> {\n const url = new URL(`${this.cfg.baseUrl}/sync`);\n url.searchParams.set(\"conv\", toHex(conversationId));\n url.searchParams.set(\"cursor\", btoaUrl(cursor));\n url.searchParams.set(\"limit\", String(limit));\n const res = await this.fetchImpl(url.toString(), {\n headers: { ...(this.cfg.authHeader ? { authorization: this.cfg.authHeader } : {}) },\n });\n if (!res.ok) throw new Error(`transport fetch failed: ${res.status}`);\n const text = await res.text();\n return JSON.parse(text, reviver) as MessageEnvelope[];\n }\n\n subscribe(onEvent: (env: MessageEnvelope) => void): { unsubscribe(): void } {\n this.liveHandlers.add(onEvent);\n return { unsubscribe: () => this.liveHandlers.delete(onEvent) };\n }\n\n async discoverDevices(userId: UserId): Promise<DiscoveredDevice[]> {\n const res = await this.fetchImpl(`${this.cfg.baseUrl}/devices/${toHex(userId)}`, {\n headers: { ...(this.cfg.authHeader ? { authorization: this.cfg.authHeader } : {}) },\n });\n if (!res.ok) throw new Error(`device discovery failed: ${res.status}`);\n const text = await res.text();\n return JSON.parse(text, reviver) as DiscoveredDevice[];\n }\n}\n\n// ---- Wire codec ----\n//\n// Demo wire format: JSON with byte fields wrapped as `{ _bytes: number[] }`. The relay's\n// `envFromJson`/`envToJson` use the same shape. The spec calls for CBOR; v0.2 swaps in a\n// real CBOR codec, but the JSON form is much friendlier to debug.\n//\n// (decodeEnvelope helpers used to live here; collapsed into inline JSON.parse calls above.)\n\nfunction replacer(_k: string, v: unknown): unknown {\n if (v instanceof Uint8Array) return { _bytes: Array.from(v) };\n return v;\n}\nfunction reviver(_k: string, v: unknown): unknown {\n if (v && typeof v === \"object\" && Array.isArray((v as { _bytes?: number[] })._bytes)) {\n return new Uint8Array((v as { _bytes: number[] })._bytes);\n }\n return v;\n}\n\nfunction toHex(b: Uint8Array): string {\n let s = \"\"; for (const v of b) s += v.toString(16).padStart(2, \"0\"); return s;\n}\nfunction btoaUrl(b: Uint8Array): string {\n let s = \"\"; for (const v of b) s += String.fromCharCode(v);\n return btoa(s).replace(/\\+/g, \"-\").replace(/\\//g, \"_\").replace(/=+$/, \"\");\n}\n"],"mappings":"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","names":[]}
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
import { T as Transport, M as MessageEnvelope, C as ConversationId, U as UserId, c as DiscoveredDevice } from '../types-COHY5NU8.cjs';
|
|
2
|
+
|
|
3
|
+
interface WsTransportConfig {
|
|
4
|
+
baseUrl: string;
|
|
5
|
+
authHeader?: string;
|
|
6
|
+
fetchImpl?: typeof fetch;
|
|
7
|
+
}
|
|
8
|
+
declare class WebSocketTransport implements Transport {
|
|
9
|
+
private cfg;
|
|
10
|
+
private ws;
|
|
11
|
+
private liveHandlers;
|
|
12
|
+
private fetchImpl;
|
|
13
|
+
constructor(cfg: WsTransportConfig);
|
|
14
|
+
private openSocket;
|
|
15
|
+
send(envelope: MessageEnvelope): Promise<void>;
|
|
16
|
+
fetchSince(conversationId: ConversationId, cursor: Uint8Array, limit: number): Promise<MessageEnvelope[]>;
|
|
17
|
+
subscribe(onEvent: (env: MessageEnvelope) => void): {
|
|
18
|
+
unsubscribe(): void;
|
|
19
|
+
};
|
|
20
|
+
discoverDevices(userId: UserId): Promise<DiscoveredDevice[]>;
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
export { WebSocketTransport, type WsTransportConfig };
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
import { T as Transport, M as MessageEnvelope, C as ConversationId, U as UserId, c as DiscoveredDevice } from '../types-COHY5NU8.js';
|
|
2
|
+
|
|
3
|
+
interface WsTransportConfig {
|
|
4
|
+
baseUrl: string;
|
|
5
|
+
authHeader?: string;
|
|
6
|
+
fetchImpl?: typeof fetch;
|
|
7
|
+
}
|
|
8
|
+
declare class WebSocketTransport implements Transport {
|
|
9
|
+
private cfg;
|
|
10
|
+
private ws;
|
|
11
|
+
private liveHandlers;
|
|
12
|
+
private fetchImpl;
|
|
13
|
+
constructor(cfg: WsTransportConfig);
|
|
14
|
+
private openSocket;
|
|
15
|
+
send(envelope: MessageEnvelope): Promise<void>;
|
|
16
|
+
fetchSince(conversationId: ConversationId, cursor: Uint8Array, limit: number): Promise<MessageEnvelope[]>;
|
|
17
|
+
subscribe(onEvent: (env: MessageEnvelope) => void): {
|
|
18
|
+
unsubscribe(): void;
|
|
19
|
+
};
|
|
20
|
+
discoverDevices(userId: UserId): Promise<DiscoveredDevice[]>;
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
export { WebSocketTransport, type WsTransportConfig };
|
|
@@ -0,0 +1,87 @@
|
|
|
1
|
+
// src/transport/websocket.ts
|
|
2
|
+
var WebSocketTransport = class {
|
|
3
|
+
constructor(cfg) {
|
|
4
|
+
this.cfg = cfg;
|
|
5
|
+
this.fetchImpl = cfg.fetchImpl ?? globalThis.fetch.bind(globalThis);
|
|
6
|
+
this.openSocket();
|
|
7
|
+
}
|
|
8
|
+
cfg;
|
|
9
|
+
ws = null;
|
|
10
|
+
liveHandlers = /* @__PURE__ */ new Set();
|
|
11
|
+
fetchImpl;
|
|
12
|
+
openSocket() {
|
|
13
|
+
if (typeof WebSocket === "undefined") return;
|
|
14
|
+
const url = `${this.cfg.baseUrl.replace(/^http/, "ws")}/stream`;
|
|
15
|
+
this.ws = new WebSocket(url);
|
|
16
|
+
this.ws.onmessage = (ev) => {
|
|
17
|
+
const text = typeof ev.data === "string" ? ev.data : new TextDecoder().decode(ev.data);
|
|
18
|
+
try {
|
|
19
|
+
const env = JSON.parse(text, reviver);
|
|
20
|
+
for (const h of this.liveHandlers) h(env);
|
|
21
|
+
} catch (e) {
|
|
22
|
+
if (typeof console !== "undefined") console.debug("[ping-sdk] WS frame parse error:", e);
|
|
23
|
+
}
|
|
24
|
+
};
|
|
25
|
+
this.ws.onclose = () => setTimeout(() => this.openSocket(), 1e3);
|
|
26
|
+
}
|
|
27
|
+
async send(envelope) {
|
|
28
|
+
const res = await this.fetchImpl(`${this.cfg.baseUrl}/send`, {
|
|
29
|
+
method: "POST",
|
|
30
|
+
headers: {
|
|
31
|
+
"content-type": "application/json",
|
|
32
|
+
...this.cfg.authHeader ? { authorization: this.cfg.authHeader } : {}
|
|
33
|
+
},
|
|
34
|
+
body: JSON.stringify(envelope, replacer)
|
|
35
|
+
});
|
|
36
|
+
if (res.status === 409) throw new Error("EpochOccupied");
|
|
37
|
+
if (!res.ok) throw new Error(`transport send failed: ${res.status}`);
|
|
38
|
+
}
|
|
39
|
+
async fetchSince(conversationId, cursor, limit) {
|
|
40
|
+
const url = new URL(`${this.cfg.baseUrl}/sync`);
|
|
41
|
+
url.searchParams.set("conv", toHex(conversationId));
|
|
42
|
+
url.searchParams.set("cursor", btoaUrl(cursor));
|
|
43
|
+
url.searchParams.set("limit", String(limit));
|
|
44
|
+
const res = await this.fetchImpl(url.toString(), {
|
|
45
|
+
headers: { ...this.cfg.authHeader ? { authorization: this.cfg.authHeader } : {} }
|
|
46
|
+
});
|
|
47
|
+
if (!res.ok) throw new Error(`transport fetch failed: ${res.status}`);
|
|
48
|
+
const text = await res.text();
|
|
49
|
+
return JSON.parse(text, reviver);
|
|
50
|
+
}
|
|
51
|
+
subscribe(onEvent) {
|
|
52
|
+
this.liveHandlers.add(onEvent);
|
|
53
|
+
return { unsubscribe: () => this.liveHandlers.delete(onEvent) };
|
|
54
|
+
}
|
|
55
|
+
async discoverDevices(userId) {
|
|
56
|
+
const res = await this.fetchImpl(`${this.cfg.baseUrl}/devices/${toHex(userId)}`, {
|
|
57
|
+
headers: { ...this.cfg.authHeader ? { authorization: this.cfg.authHeader } : {} }
|
|
58
|
+
});
|
|
59
|
+
if (!res.ok) throw new Error(`device discovery failed: ${res.status}`);
|
|
60
|
+
const text = await res.text();
|
|
61
|
+
return JSON.parse(text, reviver);
|
|
62
|
+
}
|
|
63
|
+
};
|
|
64
|
+
function replacer(_k, v) {
|
|
65
|
+
if (v instanceof Uint8Array) return { _bytes: Array.from(v) };
|
|
66
|
+
return v;
|
|
67
|
+
}
|
|
68
|
+
function reviver(_k, v) {
|
|
69
|
+
if (v && typeof v === "object" && Array.isArray(v._bytes)) {
|
|
70
|
+
return new Uint8Array(v._bytes);
|
|
71
|
+
}
|
|
72
|
+
return v;
|
|
73
|
+
}
|
|
74
|
+
function toHex(b) {
|
|
75
|
+
let s = "";
|
|
76
|
+
for (const v of b) s += v.toString(16).padStart(2, "0");
|
|
77
|
+
return s;
|
|
78
|
+
}
|
|
79
|
+
function btoaUrl(b) {
|
|
80
|
+
let s = "";
|
|
81
|
+
for (const v of b) s += String.fromCharCode(v);
|
|
82
|
+
return btoa(s).replace(/\+/g, "-").replace(/\//g, "_").replace(/=+$/, "");
|
|
83
|
+
}
|
|
84
|
+
export {
|
|
85
|
+
WebSocketTransport
|
|
86
|
+
};
|
|
87
|
+
//# sourceMappingURL=websocket.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../../src/transport/websocket.ts"],"sourcesContent":["// Reference WebSocket transport. Treats the server as an opaque relay that:\n// - accepts a CBOR-encoded MessageEnvelope on `send`\n// - returns events newer than a cursor on `fetchSince`\n// - streams new events on the WS itself for `subscribe`\n// - exposes a directory endpoint for `discoverDevices`\n//\n// This is a sample. Real deployments will tune framing / auth.\n\nimport type { ConversationId, DiscoveredDevice, MessageEnvelope, Transport, UserId } from \"../types\";\n\nexport interface WsTransportConfig {\n baseUrl: string; // ws[s]://...\n authHeader?: string; // optional bearer token\n fetchImpl?: typeof fetch; // override for SSR / tests\n}\n\nexport class WebSocketTransport implements Transport {\n private ws: WebSocket | null = null;\n private liveHandlers = new Set<(env: MessageEnvelope) => void>();\n private fetchImpl: typeof fetch;\n\n constructor(private cfg: WsTransportConfig) {\n this.fetchImpl = cfg.fetchImpl ?? globalThis.fetch.bind(globalThis);\n this.openSocket();\n }\n\n private openSocket() {\n if (typeof WebSocket === \"undefined\") return;\n const url = `${this.cfg.baseUrl.replace(/^http/, \"ws\")}/stream`;\n this.ws = new WebSocket(url);\n this.ws.onmessage = (ev) => {\n // Server sends one JSON envelope per text frame.\n const text = typeof ev.data === \"string\" ? ev.data : new TextDecoder().decode(ev.data as ArrayBuffer);\n try {\n const env = JSON.parse(text, reviver) as MessageEnvelope;\n for (const h of this.liveHandlers) h(env);\n } catch (e) {\n if (typeof console !== \"undefined\") console.debug(\"[ping-sdk] WS frame parse error:\", e);\n }\n };\n this.ws.onclose = () => setTimeout(() => this.openSocket(), 1000);\n }\n\n async send(envelope: MessageEnvelope): Promise<void> {\n const res = await this.fetchImpl(`${this.cfg.baseUrl}/send`, {\n method: \"POST\",\n headers: {\n \"content-type\": \"application/json\",\n ...(this.cfg.authHeader ? { authorization: this.cfg.authHeader } : {}),\n },\n body: JSON.stringify(envelope, replacer),\n });\n if (res.status === 409) throw new Error(\"EpochOccupied\");\n if (!res.ok) throw new Error(`transport send failed: ${res.status}`);\n }\n\n async fetchSince(conversationId: ConversationId, cursor: Uint8Array, limit: number): Promise<MessageEnvelope[]> {\n const url = new URL(`${this.cfg.baseUrl}/sync`);\n url.searchParams.set(\"conv\", toHex(conversationId));\n url.searchParams.set(\"cursor\", btoaUrl(cursor));\n url.searchParams.set(\"limit\", String(limit));\n const res = await this.fetchImpl(url.toString(), {\n headers: { ...(this.cfg.authHeader ? { authorization: this.cfg.authHeader } : {}) },\n });\n if (!res.ok) throw new Error(`transport fetch failed: ${res.status}`);\n const text = await res.text();\n return JSON.parse(text, reviver) as MessageEnvelope[];\n }\n\n subscribe(onEvent: (env: MessageEnvelope) => void): { unsubscribe(): void } {\n this.liveHandlers.add(onEvent);\n return { unsubscribe: () => this.liveHandlers.delete(onEvent) };\n }\n\n async discoverDevices(userId: UserId): Promise<DiscoveredDevice[]> {\n const res = await this.fetchImpl(`${this.cfg.baseUrl}/devices/${toHex(userId)}`, {\n headers: { ...(this.cfg.authHeader ? { authorization: this.cfg.authHeader } : {}) },\n });\n if (!res.ok) throw new Error(`device discovery failed: ${res.status}`);\n const text = await res.text();\n return JSON.parse(text, reviver) as DiscoveredDevice[];\n }\n}\n\n// ---- Wire codec ----\n//\n// Demo wire format: JSON with byte fields wrapped as `{ _bytes: number[] }`. The relay's\n// `envFromJson`/`envToJson` use the same shape. The spec calls for CBOR; v0.2 swaps in a\n// real CBOR codec, but the JSON form is much friendlier to debug.\n//\n// (decodeEnvelope helpers used to live here; collapsed into inline JSON.parse calls above.)\n\nfunction replacer(_k: string, v: unknown): unknown {\n if (v instanceof Uint8Array) return { _bytes: Array.from(v) };\n return v;\n}\nfunction reviver(_k: string, v: unknown): unknown {\n if (v && typeof v === \"object\" && Array.isArray((v as { _bytes?: number[] })._bytes)) {\n return new Uint8Array((v as { _bytes: number[] })._bytes);\n }\n return v;\n}\n\nfunction toHex(b: Uint8Array): string {\n let s = \"\"; for (const v of b) s += v.toString(16).padStart(2, \"0\"); return s;\n}\nfunction btoaUrl(b: Uint8Array): string {\n let s = \"\"; for (const v of b) s += String.fromCharCode(v);\n return btoa(s).replace(/\\+/g, \"-\").replace(/\\//g, \"_\").replace(/=+$/, \"\");\n}\n"],"mappings":"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","names":[]}
|
|
@@ -0,0 +1,74 @@
|
|
|
1
|
+
type Bytes = Uint8Array;
|
|
2
|
+
type UserId = Bytes;
|
|
3
|
+
type DeviceId = Bytes;
|
|
4
|
+
type ConversationId = Bytes;
|
|
5
|
+
interface Hlc {
|
|
6
|
+
wall_ms: number;
|
|
7
|
+
logical: number;
|
|
8
|
+
}
|
|
9
|
+
type MessageKind = "Application" | "Commit" | "Welcome" | "Proposal" | "KeyPackage";
|
|
10
|
+
interface MessageEnvelope {
|
|
11
|
+
v: number;
|
|
12
|
+
conversation_id: ConversationId;
|
|
13
|
+
epoch: number;
|
|
14
|
+
kind: MessageKind;
|
|
15
|
+
sender_device: DeviceId;
|
|
16
|
+
seq: number;
|
|
17
|
+
hlc: Hlc;
|
|
18
|
+
payload: Bytes;
|
|
19
|
+
content_hash: Bytes;
|
|
20
|
+
}
|
|
21
|
+
interface IncomingMessage {
|
|
22
|
+
conversation_id: ConversationId;
|
|
23
|
+
sender_device: DeviceId;
|
|
24
|
+
epoch: number;
|
|
25
|
+
hlc: Hlc;
|
|
26
|
+
plaintext: Bytes;
|
|
27
|
+
content_hash: Bytes;
|
|
28
|
+
}
|
|
29
|
+
interface ConversationMeta {
|
|
30
|
+
id: ConversationId;
|
|
31
|
+
name: string | null;
|
|
32
|
+
epoch: number;
|
|
33
|
+
member_count: number;
|
|
34
|
+
is_device_group: boolean;
|
|
35
|
+
created_at_ms: number;
|
|
36
|
+
}
|
|
37
|
+
interface DeviceInfo {
|
|
38
|
+
device_id: DeviceId;
|
|
39
|
+
user_id: UserId;
|
|
40
|
+
label: string;
|
|
41
|
+
created_at_ms: number;
|
|
42
|
+
last_seen_ms: number;
|
|
43
|
+
revoked: boolean;
|
|
44
|
+
}
|
|
45
|
+
interface LinkingTicket {
|
|
46
|
+
v: number;
|
|
47
|
+
user_id: UserId;
|
|
48
|
+
user_pubkey: Bytes;
|
|
49
|
+
new_device_id: DeviceId;
|
|
50
|
+
device_binding_sig: Bytes;
|
|
51
|
+
device_group_welcome: Bytes;
|
|
52
|
+
catchup_snapshot: Bytes;
|
|
53
|
+
}
|
|
54
|
+
interface DiscoveredDevice {
|
|
55
|
+
device_id: DeviceId;
|
|
56
|
+
key_package: Bytes;
|
|
57
|
+
}
|
|
58
|
+
interface Storage {
|
|
59
|
+
get(namespace: string, key: string): Promise<Uint8Array | null>;
|
|
60
|
+
put(namespace: string, key: string, value: Uint8Array): Promise<void>;
|
|
61
|
+
del(namespace: string, key: string): Promise<void>;
|
|
62
|
+
listKeys(namespace: string, prefix: string): Promise<string[]>;
|
|
63
|
+
}
|
|
64
|
+
interface Transport {
|
|
65
|
+
send(envelope: MessageEnvelope): Promise<void>;
|
|
66
|
+
fetchSince(conversationId: ConversationId, cursor: Uint8Array, limit: number): Promise<MessageEnvelope[]>;
|
|
67
|
+
/** Live-stream subscribe. The TS SDK runs its own loop and calls `processEnvelope` per event. */
|
|
68
|
+
subscribe(onEvent: (env: MessageEnvelope) => void): {
|
|
69
|
+
unsubscribe(): void;
|
|
70
|
+
};
|
|
71
|
+
discoverDevices(userId: UserId): Promise<DiscoveredDevice[]>;
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
export type { Bytes as B, ConversationId as C, DeviceId as D, Hlc as H, IncomingMessage as I, LinkingTicket as L, MessageEnvelope as M, Storage as S, Transport as T, UserId as U, ConversationMeta as a, DeviceInfo as b, DiscoveredDevice as c, MessageKind as d };
|
|
@@ -0,0 +1,74 @@
|
|
|
1
|
+
type Bytes = Uint8Array;
|
|
2
|
+
type UserId = Bytes;
|
|
3
|
+
type DeviceId = Bytes;
|
|
4
|
+
type ConversationId = Bytes;
|
|
5
|
+
interface Hlc {
|
|
6
|
+
wall_ms: number;
|
|
7
|
+
logical: number;
|
|
8
|
+
}
|
|
9
|
+
type MessageKind = "Application" | "Commit" | "Welcome" | "Proposal" | "KeyPackage";
|
|
10
|
+
interface MessageEnvelope {
|
|
11
|
+
v: number;
|
|
12
|
+
conversation_id: ConversationId;
|
|
13
|
+
epoch: number;
|
|
14
|
+
kind: MessageKind;
|
|
15
|
+
sender_device: DeviceId;
|
|
16
|
+
seq: number;
|
|
17
|
+
hlc: Hlc;
|
|
18
|
+
payload: Bytes;
|
|
19
|
+
content_hash: Bytes;
|
|
20
|
+
}
|
|
21
|
+
interface IncomingMessage {
|
|
22
|
+
conversation_id: ConversationId;
|
|
23
|
+
sender_device: DeviceId;
|
|
24
|
+
epoch: number;
|
|
25
|
+
hlc: Hlc;
|
|
26
|
+
plaintext: Bytes;
|
|
27
|
+
content_hash: Bytes;
|
|
28
|
+
}
|
|
29
|
+
interface ConversationMeta {
|
|
30
|
+
id: ConversationId;
|
|
31
|
+
name: string | null;
|
|
32
|
+
epoch: number;
|
|
33
|
+
member_count: number;
|
|
34
|
+
is_device_group: boolean;
|
|
35
|
+
created_at_ms: number;
|
|
36
|
+
}
|
|
37
|
+
interface DeviceInfo {
|
|
38
|
+
device_id: DeviceId;
|
|
39
|
+
user_id: UserId;
|
|
40
|
+
label: string;
|
|
41
|
+
created_at_ms: number;
|
|
42
|
+
last_seen_ms: number;
|
|
43
|
+
revoked: boolean;
|
|
44
|
+
}
|
|
45
|
+
interface LinkingTicket {
|
|
46
|
+
v: number;
|
|
47
|
+
user_id: UserId;
|
|
48
|
+
user_pubkey: Bytes;
|
|
49
|
+
new_device_id: DeviceId;
|
|
50
|
+
device_binding_sig: Bytes;
|
|
51
|
+
device_group_welcome: Bytes;
|
|
52
|
+
catchup_snapshot: Bytes;
|
|
53
|
+
}
|
|
54
|
+
interface DiscoveredDevice {
|
|
55
|
+
device_id: DeviceId;
|
|
56
|
+
key_package: Bytes;
|
|
57
|
+
}
|
|
58
|
+
interface Storage {
|
|
59
|
+
get(namespace: string, key: string): Promise<Uint8Array | null>;
|
|
60
|
+
put(namespace: string, key: string, value: Uint8Array): Promise<void>;
|
|
61
|
+
del(namespace: string, key: string): Promise<void>;
|
|
62
|
+
listKeys(namespace: string, prefix: string): Promise<string[]>;
|
|
63
|
+
}
|
|
64
|
+
interface Transport {
|
|
65
|
+
send(envelope: MessageEnvelope): Promise<void>;
|
|
66
|
+
fetchSince(conversationId: ConversationId, cursor: Uint8Array, limit: number): Promise<MessageEnvelope[]>;
|
|
67
|
+
/** Live-stream subscribe. The TS SDK runs its own loop and calls `processEnvelope` per event. */
|
|
68
|
+
subscribe(onEvent: (env: MessageEnvelope) => void): {
|
|
69
|
+
unsubscribe(): void;
|
|
70
|
+
};
|
|
71
|
+
discoverDevices(userId: UserId): Promise<DiscoveredDevice[]>;
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
export type { Bytes as B, ConversationId as C, DeviceId as D, Hlc as H, IncomingMessage as I, LinkingTicket as L, MessageEnvelope as M, Storage as S, Transport as T, UserId as U, ConversationMeta as a, DeviceInfo as b, DiscoveredDevice as c, MessageKind as d };
|