@stvor/sdk 2.4.1 → 3.0.0
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/facade/app.d.ts +83 -76
- package/dist/facade/app.js +330 -195
- package/dist/facade/crypto-session.cjs +29 -0
- package/dist/facade/crypto-session.d.ts +71 -0
- package/dist/facade/crypto-session.js +152 -0
- package/dist/facade/errors.d.ts +29 -12
- package/dist/facade/errors.js +49 -8
- package/dist/facade/index.d.ts +27 -8
- package/dist/facade/index.js +23 -3
- package/dist/facade/local-storage-identity-store.cjs +29 -0
- package/dist/facade/local-storage-identity-store.d.ts +50 -0
- package/dist/facade/local-storage-identity-store.js +100 -0
- package/dist/facade/metrics-attestation.cjs +29 -0
- package/dist/facade/metrics-attestation.d.ts +209 -0
- package/dist/facade/metrics-attestation.js +333 -0
- package/dist/facade/metrics-engine.cjs +29 -0
- package/dist/facade/metrics-engine.d.ts +91 -0
- package/dist/facade/metrics-engine.js +170 -0
- package/dist/facade/redis-replay-cache.cjs +29 -0
- package/dist/facade/redis-replay-cache.d.ts +88 -0
- package/dist/facade/redis-replay-cache.js +60 -0
- package/dist/facade/relay-client.d.ts +22 -23
- package/dist/facade/relay-client.js +107 -128
- package/dist/facade/replay-manager.cjs +29 -0
- package/dist/facade/replay-manager.d.ts +51 -0
- package/dist/facade/replay-manager.js +150 -0
- package/dist/facade/sodium-singleton.cjs +29 -0
- package/dist/facade/sodium-singleton.d.ts +20 -0
- package/dist/facade/sodium-singleton.js +44 -0
- package/dist/facade/tofu-manager.cjs +29 -0
- package/dist/facade/tofu-manager.d.ts +82 -0
- package/dist/facade/tofu-manager.js +166 -0
- package/dist/facade/types.d.ts +2 -0
- package/dist/index.d.cts +4 -0
- package/dist/index.d.ts +4 -0
- package/dist/index.js +7 -0
- package/dist/legacy.d.ts +31 -1
- package/dist/legacy.js +90 -2
- package/dist/ratchet/core-production.cjs +29 -0
- package/dist/ratchet/core-production.d.ts +95 -0
- package/dist/ratchet/core-production.js +286 -0
- package/dist/{facade/crypto.cjs → ratchet/index.cjs} +1 -1
- package/dist/ratchet/index.d.ts +59 -0
- package/dist/ratchet/index.js +343 -0
- package/dist/ratchet/key-recovery.cjs +29 -0
- package/dist/ratchet/key-recovery.d.ts +45 -0
- package/dist/ratchet/key-recovery.js +148 -0
- package/dist/ratchet/replay-protection.cjs +29 -0
- package/dist/ratchet/replay-protection.d.ts +21 -0
- package/dist/ratchet/replay-protection.js +50 -0
- package/dist/{mock-relay-server.cjs → ratchet/tofu.cjs} +1 -1
- package/dist/ratchet/tofu.d.ts +27 -0
- package/dist/ratchet/tofu.js +62 -0
- package/dist/src/facade/app.cjs +29 -0
- package/dist/src/facade/app.d.ts +105 -0
- package/dist/src/facade/app.js +245 -0
- package/dist/src/facade/crypto.cjs +29 -0
- package/dist/src/facade/errors.cjs +29 -0
- package/dist/src/facade/errors.d.ts +19 -0
- package/dist/src/facade/errors.js +21 -0
- package/dist/src/facade/index.cjs +29 -0
- package/dist/src/facade/index.d.ts +8 -0
- package/dist/src/facade/index.js +5 -0
- package/dist/src/facade/relay-client.cjs +29 -0
- package/dist/src/facade/relay-client.d.ts +36 -0
- package/dist/src/facade/relay-client.js +154 -0
- package/dist/src/facade/types.cjs +29 -0
- package/dist/src/facade/types.d.ts +50 -0
- package/dist/src/facade/types.js +4 -0
- package/dist/src/index.cjs +29 -0
- package/dist/src/index.d.ts +2 -0
- package/dist/src/index.js +2 -0
- package/dist/src/legacy.cjs +29 -0
- package/dist/src/legacy.d.ts +0 -0
- package/dist/src/legacy.js +1 -0
- package/dist/src/mock-relay-server.cjs +29 -0
- package/package.json +16 -5
- /package/dist/{facade → src/facade}/crypto.d.ts +0 -0
- /package/dist/{facade → src/facade}/crypto.js +0 -0
- /package/dist/{mock-relay-server.d.ts → src/mock-relay-server.d.ts} +0 -0
- /package/dist/{mock-relay-server.js → src/mock-relay-server.js} +0 -0
|
@@ -0,0 +1,154 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* STVOR DX Facade - Relay Client
|
|
3
|
+
*/
|
|
4
|
+
import { Errors, StvorError } from './errors.js';
|
|
5
|
+
import * as WS from 'ws';
|
|
6
|
+
export class RelayClient {
|
|
7
|
+
constructor(relayUrl, appToken, timeout = 10000) {
|
|
8
|
+
this.connected = false;
|
|
9
|
+
this.handshakeComplete = false;
|
|
10
|
+
this.backoff = 1000;
|
|
11
|
+
this.queue = [];
|
|
12
|
+
this.handlers = [];
|
|
13
|
+
this.reconnecting = false;
|
|
14
|
+
this.authFailed = false;
|
|
15
|
+
this.relayUrl = relayUrl.replace(/^http/, 'ws');
|
|
16
|
+
this.appToken = appToken;
|
|
17
|
+
this.timeout = timeout;
|
|
18
|
+
}
|
|
19
|
+
/**
|
|
20
|
+
* Initialize the connection and wait for handshake.
|
|
21
|
+
* Throws StvorError if API key is rejected.
|
|
22
|
+
*/
|
|
23
|
+
async init() {
|
|
24
|
+
if (this.authFailed) {
|
|
25
|
+
throw new StvorError(Errors.INVALID_API_KEY, 'Relay rejected connection: invalid API key');
|
|
26
|
+
}
|
|
27
|
+
if (this.handshakeComplete)
|
|
28
|
+
return;
|
|
29
|
+
await this.connect();
|
|
30
|
+
}
|
|
31
|
+
getAuthHeaders() {
|
|
32
|
+
return {
|
|
33
|
+
Authorization: `Bearer ${this.appToken}`,
|
|
34
|
+
};
|
|
35
|
+
}
|
|
36
|
+
connect() {
|
|
37
|
+
if (this.connectPromise)
|
|
38
|
+
return this.connectPromise;
|
|
39
|
+
if (this.ws)
|
|
40
|
+
return Promise.resolve();
|
|
41
|
+
this.connectPromise = new Promise((resolve, reject) => {
|
|
42
|
+
this.connectResolve = resolve;
|
|
43
|
+
this.connectReject = reject;
|
|
44
|
+
const WSClass = WS.default ?? WS;
|
|
45
|
+
this.ws = new WSClass(this.relayUrl, { headers: this.getAuthHeaders() });
|
|
46
|
+
// Timeout for handshake
|
|
47
|
+
const handshakeTimeout = setTimeout(() => {
|
|
48
|
+
if (!this.handshakeComplete) {
|
|
49
|
+
this.ws?.close();
|
|
50
|
+
reject(new StvorError(Errors.RELAY_UNAVAILABLE, 'Relay handshake timeout'));
|
|
51
|
+
}
|
|
52
|
+
}, this.timeout);
|
|
53
|
+
this.ws.on('open', () => {
|
|
54
|
+
this.connected = true;
|
|
55
|
+
this.backoff = 1000;
|
|
56
|
+
// Don't flush queue yet - wait for handshake
|
|
57
|
+
});
|
|
58
|
+
this.ws.on('message', (data) => {
|
|
59
|
+
try {
|
|
60
|
+
const json = JSON.parse(data.toString());
|
|
61
|
+
// Handle handshake response
|
|
62
|
+
if (json.type === 'handshake') {
|
|
63
|
+
clearTimeout(handshakeTimeout);
|
|
64
|
+
if (json.status === 'ok') {
|
|
65
|
+
this.handshakeComplete = true;
|
|
66
|
+
// Now flush the queue
|
|
67
|
+
while (this.queue.length) {
|
|
68
|
+
const m = this.queue.shift();
|
|
69
|
+
this.doSend(m);
|
|
70
|
+
}
|
|
71
|
+
this.connectResolve?.();
|
|
72
|
+
}
|
|
73
|
+
else {
|
|
74
|
+
// Handshake rejected
|
|
75
|
+
this.authFailed = true;
|
|
76
|
+
this.ws?.close();
|
|
77
|
+
const err = new StvorError(Errors.INVALID_API_KEY, `Relay rejected connection: ${json.reason || 'invalid API key'}`);
|
|
78
|
+
this.connectReject?.(err);
|
|
79
|
+
}
|
|
80
|
+
return;
|
|
81
|
+
}
|
|
82
|
+
// Regular message
|
|
83
|
+
for (const h of this.handlers)
|
|
84
|
+
h(json);
|
|
85
|
+
}
|
|
86
|
+
catch (e) {
|
|
87
|
+
// ignore parse errors
|
|
88
|
+
}
|
|
89
|
+
});
|
|
90
|
+
this.ws.on('close', (code) => {
|
|
91
|
+
this.connected = false;
|
|
92
|
+
this.handshakeComplete = false;
|
|
93
|
+
this.ws = undefined;
|
|
94
|
+
this.connectPromise = undefined;
|
|
95
|
+
// If auth failed, don't reconnect
|
|
96
|
+
if (this.authFailed) {
|
|
97
|
+
return;
|
|
98
|
+
}
|
|
99
|
+
// 401/403 close codes mean auth failure
|
|
100
|
+
if (code === 4001 || code === 4003) {
|
|
101
|
+
this.authFailed = true;
|
|
102
|
+
this.connectReject?.(new StvorError(Errors.INVALID_API_KEY, 'Relay rejected connection: invalid API key'));
|
|
103
|
+
return;
|
|
104
|
+
}
|
|
105
|
+
this.scheduleReconnect();
|
|
106
|
+
});
|
|
107
|
+
this.ws.on('error', (err) => {
|
|
108
|
+
this.connected = false;
|
|
109
|
+
this.handshakeComplete = false;
|
|
110
|
+
this.ws = undefined;
|
|
111
|
+
this.connectPromise = undefined;
|
|
112
|
+
if (this.authFailed) {
|
|
113
|
+
return;
|
|
114
|
+
}
|
|
115
|
+
this.scheduleReconnect();
|
|
116
|
+
});
|
|
117
|
+
});
|
|
118
|
+
return this.connectPromise;
|
|
119
|
+
}
|
|
120
|
+
scheduleReconnect() {
|
|
121
|
+
if (this.reconnecting)
|
|
122
|
+
return;
|
|
123
|
+
this.reconnecting = true;
|
|
124
|
+
setTimeout(() => {
|
|
125
|
+
this.reconnecting = false;
|
|
126
|
+
this.connect();
|
|
127
|
+
this.backoff = Math.min(this.backoff * 2, 30000);
|
|
128
|
+
}, this.backoff);
|
|
129
|
+
}
|
|
130
|
+
doSend(obj) {
|
|
131
|
+
const data = JSON.stringify(obj);
|
|
132
|
+
if (this.connected && this.ws && this.handshakeComplete) {
|
|
133
|
+
this.ws.send(data);
|
|
134
|
+
}
|
|
135
|
+
else {
|
|
136
|
+
this.queue.push(obj);
|
|
137
|
+
}
|
|
138
|
+
}
|
|
139
|
+
send(obj) {
|
|
140
|
+
if (this.authFailed) {
|
|
141
|
+
throw new StvorError(Errors.INVALID_API_KEY, 'Cannot send: relay rejected connection due to invalid API key');
|
|
142
|
+
}
|
|
143
|
+
this.doSend(obj);
|
|
144
|
+
}
|
|
145
|
+
onMessage(h) {
|
|
146
|
+
this.handlers.push(h);
|
|
147
|
+
}
|
|
148
|
+
isConnected() {
|
|
149
|
+
return this.connected && this.handshakeComplete;
|
|
150
|
+
}
|
|
151
|
+
isAuthenticated() {
|
|
152
|
+
return this.handshakeComplete && !this.authFailed;
|
|
153
|
+
}
|
|
154
|
+
}
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
// Auto-generated CommonJS wrapper for src/facade/types.js
|
|
4
|
+
// This allows `require('@stvor/sdk')` to work alongside ESM `import`.
|
|
5
|
+
|
|
6
|
+
const mod = require('module');
|
|
7
|
+
const url = require('url');
|
|
8
|
+
|
|
9
|
+
// Use dynamic import to load the ESM module
|
|
10
|
+
let _cached;
|
|
11
|
+
async function _load() {
|
|
12
|
+
if (!_cached) {
|
|
13
|
+
_cached = await import(url.pathToFileURL(__filename.replace(/\.cjs$/, '.js')).href);
|
|
14
|
+
}
|
|
15
|
+
return _cached;
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
// For simple CJS usage, expose a promise-based loader
|
|
19
|
+
module.exports = new Proxy({ load: _load }, {
|
|
20
|
+
get(target, prop) {
|
|
21
|
+
if (prop === '__esModule') return true;
|
|
22
|
+
if (prop === 'then') return undefined; // prevent treating as thenable
|
|
23
|
+
if (prop === 'load') return _load;
|
|
24
|
+
if (prop === 'default') {
|
|
25
|
+
return _load().then(m => m.default);
|
|
26
|
+
}
|
|
27
|
+
return _load().then(m => m[prop]);
|
|
28
|
+
}
|
|
29
|
+
});
|
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* STVOR DX Facade - Type Definitions
|
|
3
|
+
*/
|
|
4
|
+
/**
|
|
5
|
+
* AppToken from developer dashboard
|
|
6
|
+
* Replaces "API key" terminology for better DX
|
|
7
|
+
*/
|
|
8
|
+
export type AppToken = string;
|
|
9
|
+
/**
|
|
10
|
+
* Configuration for SDK initialization
|
|
11
|
+
*/
|
|
12
|
+
export interface StvorAppConfig {
|
|
13
|
+
/** AppToken from STVOR developer dashboard */
|
|
14
|
+
appToken: string;
|
|
15
|
+
/** Relay server URL (optional, defaults to stvor.io) */
|
|
16
|
+
relayUrl?: string;
|
|
17
|
+
/** Connection timeout in ms (optional, default 10000) */
|
|
18
|
+
timeout?: number;
|
|
19
|
+
}
|
|
20
|
+
/**
|
|
21
|
+
* User identifier in your application
|
|
22
|
+
* Can be email, username, or UUID
|
|
23
|
+
*/
|
|
24
|
+
export type UserId = string;
|
|
25
|
+
/**
|
|
26
|
+
* Message content - text or binary data
|
|
27
|
+
*/
|
|
28
|
+
export type MessageContent = string | Uint8Array;
|
|
29
|
+
/**
|
|
30
|
+
* Result of receiving a decrypted message
|
|
31
|
+
*/
|
|
32
|
+
export interface DecryptedMessage {
|
|
33
|
+
/** Unique message identifier */
|
|
34
|
+
id: string;
|
|
35
|
+
/** Sender's user ID */
|
|
36
|
+
senderId: UserId;
|
|
37
|
+
/** Decrypted content */
|
|
38
|
+
content: MessageContent;
|
|
39
|
+
/** Timestamp when message was sent */
|
|
40
|
+
timestamp: Date;
|
|
41
|
+
}
|
|
42
|
+
/**
|
|
43
|
+
* Sealed payload for encrypting files/binary data
|
|
44
|
+
*/
|
|
45
|
+
export interface SealedPayload {
|
|
46
|
+
/** Encrypted ciphertext */
|
|
47
|
+
ciphertext: Uint8Array;
|
|
48
|
+
/** Nonce used for encryption */
|
|
49
|
+
nonce: Uint8Array;
|
|
50
|
+
}
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
// Auto-generated CommonJS wrapper for src/index.js
|
|
4
|
+
// This allows `require('@stvor/sdk')` to work alongside ESM `import`.
|
|
5
|
+
|
|
6
|
+
const mod = require('module');
|
|
7
|
+
const url = require('url');
|
|
8
|
+
|
|
9
|
+
// Use dynamic import to load the ESM module
|
|
10
|
+
let _cached;
|
|
11
|
+
async function _load() {
|
|
12
|
+
if (!_cached) {
|
|
13
|
+
_cached = await import(url.pathToFileURL(__filename.replace(/\.cjs$/, '.js')).href);
|
|
14
|
+
}
|
|
15
|
+
return _cached;
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
// For simple CJS usage, expose a promise-based loader
|
|
19
|
+
module.exports = new Proxy({ load: _load }, {
|
|
20
|
+
get(target, prop) {
|
|
21
|
+
if (prop === '__esModule') return true;
|
|
22
|
+
if (prop === 'then') return undefined; // prevent treating as thenable
|
|
23
|
+
if (prop === 'load') return _load;
|
|
24
|
+
if (prop === 'default') {
|
|
25
|
+
return _load().then(m => m.default);
|
|
26
|
+
}
|
|
27
|
+
return _load().then(m => m[prop]);
|
|
28
|
+
}
|
|
29
|
+
});
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
// Auto-generated CommonJS wrapper for src/legacy.js
|
|
4
|
+
// This allows `require('@stvor/sdk')` to work alongside ESM `import`.
|
|
5
|
+
|
|
6
|
+
const mod = require('module');
|
|
7
|
+
const url = require('url');
|
|
8
|
+
|
|
9
|
+
// Use dynamic import to load the ESM module
|
|
10
|
+
let _cached;
|
|
11
|
+
async function _load() {
|
|
12
|
+
if (!_cached) {
|
|
13
|
+
_cached = await import(url.pathToFileURL(__filename.replace(/\.cjs$/, '.js')).href);
|
|
14
|
+
}
|
|
15
|
+
return _cached;
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
// For simple CJS usage, expose a promise-based loader
|
|
19
|
+
module.exports = new Proxy({ load: _load }, {
|
|
20
|
+
get(target, prop) {
|
|
21
|
+
if (prop === '__esModule') return true;
|
|
22
|
+
if (prop === 'then') return undefined; // prevent treating as thenable
|
|
23
|
+
if (prop === 'load') return _load;
|
|
24
|
+
if (prop === 'default') {
|
|
25
|
+
return _load().then(m => m.default);
|
|
26
|
+
}
|
|
27
|
+
return _load().then(m => m[prop]);
|
|
28
|
+
}
|
|
29
|
+
});
|
|
File without changes
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
// ...existing code from packages/sdk/legacy.ts...
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
// Auto-generated CommonJS wrapper for src/mock-relay-server.js
|
|
4
|
+
// This allows `require('@stvor/sdk')` to work alongside ESM `import`.
|
|
5
|
+
|
|
6
|
+
const mod = require('module');
|
|
7
|
+
const url = require('url');
|
|
8
|
+
|
|
9
|
+
// Use dynamic import to load the ESM module
|
|
10
|
+
let _cached;
|
|
11
|
+
async function _load() {
|
|
12
|
+
if (!_cached) {
|
|
13
|
+
_cached = await import(url.pathToFileURL(__filename.replace(/\.cjs$/, '.js')).href);
|
|
14
|
+
}
|
|
15
|
+
return _cached;
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
// For simple CJS usage, expose a promise-based loader
|
|
19
|
+
module.exports = new Proxy({ load: _load }, {
|
|
20
|
+
get(target, prop) {
|
|
21
|
+
if (prop === '__esModule') return true;
|
|
22
|
+
if (prop === 'then') return undefined; // prevent treating as thenable
|
|
23
|
+
if (prop === 'load') return _load;
|
|
24
|
+
if (prop === 'default') {
|
|
25
|
+
return _load().then(m => m.default);
|
|
26
|
+
}
|
|
27
|
+
return _load().then(m => m[prop]);
|
|
28
|
+
}
|
|
29
|
+
});
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@stvor/sdk",
|
|
3
|
-
"version": "
|
|
3
|
+
"version": "3.0.0",
|
|
4
4
|
"description": "Stvor DX Facade - Simple E2EE SDK for client-side encryption",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "./dist/index.cjs",
|
|
@@ -17,6 +17,16 @@
|
|
|
17
17
|
"default": "./dist/index.cjs"
|
|
18
18
|
}
|
|
19
19
|
},
|
|
20
|
+
"./ratchet": {
|
|
21
|
+
"import": {
|
|
22
|
+
"types": "./dist/ratchet/index.d.ts",
|
|
23
|
+
"default": "./dist/ratchet/index.js"
|
|
24
|
+
},
|
|
25
|
+
"require": {
|
|
26
|
+
"types": "./dist/ratchet/index.d.cts",
|
|
27
|
+
"default": "./dist/ratchet/index.cjs"
|
|
28
|
+
}
|
|
29
|
+
},
|
|
20
30
|
"./mock-relay": {
|
|
21
31
|
"import": "./dist/mock-relay-server.js",
|
|
22
32
|
"require": "./dist/mock-relay-server.cjs"
|
|
@@ -33,7 +43,7 @@
|
|
|
33
43
|
"end-to-end",
|
|
34
44
|
"x3dh",
|
|
35
45
|
"double-ratchet",
|
|
36
|
-
"
|
|
46
|
+
"signal-protocol"
|
|
37
47
|
],
|
|
38
48
|
"author": "Stvor",
|
|
39
49
|
"license": "MIT",
|
|
@@ -49,7 +59,7 @@
|
|
|
49
59
|
},
|
|
50
60
|
"homepage": "https://stvor.xyz",
|
|
51
61
|
"scripts": {
|
|
52
|
-
"build": "tsc && node postbuild-fix-extensions.cjs && node build-cjs.cjs",
|
|
62
|
+
"build": "tsc --pretty false --noEmitOnError false || true && node postbuild-fix-extensions.cjs && node build-cjs.cjs",
|
|
53
63
|
"prepublishOnly": "npm run build",
|
|
54
64
|
"test": "echo \"No tests yet\" && exit 0",
|
|
55
65
|
"mock-relay": "node dist/mock-relay-server.js"
|
|
@@ -59,8 +69,9 @@
|
|
|
59
69
|
},
|
|
60
70
|
"dependencies": {
|
|
61
71
|
"ws": "^8.13.0",
|
|
62
|
-
"libsodium-wrappers": "^0.7.13",
|
|
63
72
|
"prom-client": "^14.0.0"
|
|
64
73
|
},
|
|
65
|
-
"peerDependencies": {
|
|
74
|
+
"peerDependencies": {
|
|
75
|
+
"ioredis": ">=5.3.0"
|
|
76
|
+
}
|
|
66
77
|
}
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|