@stvor/sdk 2.0.4 → 2.0.8

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.
@@ -1,44 +1,19 @@
1
- /**
2
- * STVOR DX Facade - Main Application Classes
3
- */
4
- import { StvorAppConfig, UserId, MessageContent } from './types';
5
- import { DecryptedMessage, SealedPayload } from './types';
6
- import { Errors, StvorError, ErrorCode } from './errors';
7
- export type { DecryptedMessage, SealedPayload, ErrorCode };
8
- export { StvorError, Errors };
9
- import { RelayClient } from './relay-client';
10
- export declare class StvorApp {
11
- private relay;
12
- private config;
13
- private connectedClients;
14
- constructor(config: Required<StvorAppConfig>);
15
- isReady(): boolean;
16
- connect(userId: UserId): Promise<StvorFacadeClient>;
17
- disconnect(userId?: UserId): Promise<void>;
18
- private initClient;
19
- }
1
+ import type { StvorAppConfig, UserId, MessageContent, DecryptedMessage } from './types.js';
2
+ import { RelayClient } from './relay-client.js';
20
3
  export declare class StvorFacadeClient {
21
- private userId;
22
- private relay;
23
- private initialized;
24
- private sessionKeyPair;
25
- private messageQueue;
26
- private messageHandlers;
27
- private isReceiving;
4
+ readonly userId: UserId;
5
+ private readonly relay;
28
6
  constructor(userId: UserId, relay: RelayClient);
29
7
  internalInitialize(): Promise<void>;
30
- private initialize;
31
8
  send(recipientId: UserId, content: MessageContent): Promise<void>;
32
- receive(): Promise<DecryptedMessage>;
33
- seal(data: MessageContent, recipientId: UserId): Promise<SealedPayload>;
34
- open(sealed: SealedPayload): Promise<Uint8Array>;
35
9
  onMessage(handler: (msg: DecryptedMessage) => void): () => void;
36
- getUserId(): UserId;
37
- disconnect(): Promise<void>;
38
- private deriveSharedKey;
39
- private fetchAndDecryptMessageWithTimeout;
40
- private decryptMessage;
41
- private startMessagePolling;
10
+ }
11
+ export declare class StvorApp {
12
+ private readonly config;
13
+ private clients;
14
+ constructor(config: StvorAppConfig);
15
+ connect(userId: UserId): Promise<StvorFacadeClient>;
16
+ disconnect(userId?: UserId): Promise<void>;
42
17
  }
43
18
  export declare function init(config: StvorAppConfig): Promise<StvorApp>;
44
19
  export declare const Stvor: {
@@ -1,247 +1,48 @@
1
- /**
2
- * STVOR DX Facade - Main Application Classes
3
- */
4
- import { Errors, StvorError } from './errors';
5
- export { StvorError, Errors };
6
- import { RelayClient } from './relay-client';
7
- export class StvorApp {
8
- constructor(config) {
9
- this.connectedClients = new Map();
10
- this.config = config;
11
- this.relay = new RelayClient(config.relayUrl, config.appToken, config.timeout);
12
- }
13
- isReady() {
14
- return this.relay.isConnected();
15
- }
16
- async connect(userId) {
17
- const existingClient = this.connectedClients.get(userId);
18
- if (existingClient) {
19
- console.warn(`[STVOR] Warning: User "${userId}" is already connected. Returning cached client.`);
20
- return existingClient;
21
- }
22
- const client = new StvorFacadeClient(userId, this.relay);
23
- await this.initClient(client);
24
- this.connectedClients.set(userId, client);
25
- return client;
26
- }
27
- async disconnect(userId) {
28
- if (userId) {
29
- const client = this.connectedClients.get(userId);
30
- if (client) {
31
- await client.disconnect();
32
- this.connectedClients.delete(userId);
33
- }
34
- }
35
- else {
36
- for (const client of this.connectedClients.values()) {
37
- await client.disconnect();
38
- }
39
- this.connectedClients.clear();
40
- this.relay.disconnect();
41
- }
42
- }
43
- async initClient(client) {
44
- await client.internalInitialize();
45
- }
46
- }
1
+ import { Errors, StvorError } from './errors.js';
2
+ import { RelayClient } from './relay-client.js';
47
3
  export class StvorFacadeClient {
48
4
  constructor(userId, relay) {
49
- this.initialized = false;
50
- this.sessionKeyPair = null;
51
- this.messageQueue = [];
52
- this.messageHandlers = new Map();
53
- this.isReceiving = false;
54
5
  this.userId = userId;
55
6
  this.relay = relay;
56
7
  }
57
8
  async internalInitialize() {
58
- await this.initialize();
59
- }
60
- async initialize() {
61
- if (this.initialized)
62
- return;
63
- this.sessionKeyPair = await crypto.subtle.generateKey({ name: 'ECDH', namedCurve: 'X25519' }, true, ['deriveKey', 'deriveBits']);
64
- const publicKeyJwk = await crypto.subtle.exportKey('jwk', this.sessionKeyPair.publicKey);
65
- await this.relay.register(this.userId, publicKeyJwk);
66
- this.initialized = true;
67
- this.startMessagePolling();
9
+ // TODO: init logic
68
10
  }
69
11
  async send(recipientId, content) {
70
- if (!this.initialized) {
71
- throw Errors.clientNotReady();
72
- }
73
- const contentBytes = typeof content === 'string'
74
- ? new TextEncoder().encode(content)
75
- : content;
76
- const recipientKey = await this.relay.getPublicKey(recipientId);
77
- if (!recipientKey) {
78
- throw Errors.recipientNotFound(recipientId);
79
- }
80
- const sharedKey = await this.deriveSharedKey(recipientKey);
81
- const iv = crypto.getRandomValues(new Uint8Array(12));
82
- const encrypted = await crypto.subtle.encrypt({ name: 'AES-GCM', iv }, sharedKey, contentBytes.buffer);
83
- await this.relay.send({
84
- to: recipientId,
85
- from: this.userId,
86
- ciphertext: new Uint8Array(encrypted),
87
- nonce: iv,
88
- });
89
- }
90
- async receive() {
91
- if (!this.initialized) {
92
- throw Errors.clientNotReady();
93
- }
94
- if (this.isReceiving) {
95
- throw Errors.receiveInProgress();
96
- }
97
- if (this.messageQueue.length > 0) {
98
- return this.messageQueue.shift();
99
- }
100
- this.isReceiving = true;
101
- try {
102
- return await this.fetchAndDecryptMessageWithTimeout(30000);
103
- }
104
- finally {
105
- this.isReceiving = false;
106
- }
107
- }
108
- async seal(data, recipientId) {
109
- if (!this.initialized) {
110
- throw Errors.clientNotReady();
111
- }
112
- const contentBytes = typeof data === 'string'
113
- ? new TextEncoder().encode(data)
114
- : data;
115
- const recipientKey = await this.relay.getPublicKey(recipientId);
116
- if (!recipientKey) {
117
- throw Errors.recipientNotFound(recipientId);
118
- }
119
- const sharedKey = await this.deriveSharedKey(recipientKey);
120
- const iv = crypto.getRandomValues(new Uint8Array(12));
121
- const encrypted = await crypto.subtle.encrypt({ name: 'AES-GCM', iv }, sharedKey, contentBytes.buffer);
122
- return {
123
- ciphertext: new Uint8Array(encrypted),
124
- nonce: iv,
125
- recipientId,
126
- };
127
- }
128
- async open(sealed) {
129
- if (!this.initialized) {
130
- throw Errors.clientNotReady();
131
- }
132
- const senderKey = await this.relay.getPublicKey(sealed.recipientId);
133
- if (!senderKey) {
134
- throw Errors.recipientNotFound(sealed.recipientId);
135
- }
136
- const sharedKey = await this.deriveSharedKey(senderKey);
137
- const decrypted = await crypto.subtle.decrypt({ name: 'AES-GCM', iv: sealed.nonce }, sharedKey, sealed.ciphertext.buffer);
138
- return new Uint8Array(decrypted);
12
+ // TODO: send logic
139
13
  }
140
14
  onMessage(handler) {
141
- const id = crypto.randomUUID();
142
- this.messageHandlers.set(id, handler);
143
- return () => {
144
- this.messageHandlers.delete(id);
145
- };
15
+ // TODO: subscription logic
16
+ return () => { };
146
17
  }
147
- getUserId() {
148
- return this.userId;
149
- }
150
- async disconnect() {
151
- this.messageHandlers.clear();
152
- this.initialized = false;
153
- this.sessionKeyPair = null;
154
- this.messageQueue = [];
155
- this.isReceiving = false;
156
- }
157
- async deriveSharedKey(peerPublicKey) {
158
- return await crypto.subtle.deriveKey({ name: 'ECDH', public: peerPublicKey }, this.sessionKeyPair.privateKey, { name: 'AES-GCM', length: 256 }, false, ['encrypt', 'decrypt']);
18
+ }
19
+ export class StvorApp {
20
+ constructor(config) {
21
+ this.config = config;
22
+ this.clients = new Map();
159
23
  }
160
- async fetchAndDecryptMessageWithTimeout(timeoutMs) {
161
- const pollInterval = 1000;
162
- let waited = 0;
163
- while (waited < timeoutMs) {
164
- try {
165
- const messages = await this.relay.fetchMessages(this.userId);
166
- if (messages.length > 0) {
167
- return await this.decryptMessage(messages[0]);
168
- }
169
- }
170
- catch {
171
- // Silent error on poll
172
- }
173
- waited += pollInterval;
174
- await new Promise(resolve => setTimeout(resolve, pollInterval));
175
- }
176
- throw Errors.receiveTimeout();
24
+ async connect(userId) {
25
+ const existing = this.clients.get(userId);
26
+ if (existing)
27
+ return existing;
28
+ const relay = new RelayClient(this.config.relayUrl ?? 'wss://stvor.xyz/relay', this.config.appToken, this.config.timeout ?? 10000);
29
+ const client = new StvorFacadeClient(userId, relay);
30
+ await client.internalInitialize();
31
+ this.clients.set(userId, client);
32
+ return client;
177
33
  }
178
- async decryptMessage(msg) {
179
- const senderKey = await this.relay.getPublicKey(msg.from);
180
- if (!senderKey) {
181
- throw Errors.recipientNotFound(msg.from);
182
- }
183
- const sharedKey = await this.deriveSharedKey(senderKey);
184
- try {
185
- const decrypted = await crypto.subtle.decrypt({ name: 'AES-GCM', iv: new Uint8Array(msg.nonce) }, sharedKey, new Uint8Array(msg.ciphertext).buffer);
186
- return {
187
- id: msg.id || crypto.randomUUID(),
188
- senderId: msg.from,
189
- content: new TextDecoder().decode(decrypted),
190
- timestamp: new Date(msg.timestamp),
191
- };
192
- }
193
- catch {
194
- throw Errors.messageIntegrityFailed();
34
+ async disconnect(userId) {
35
+ if (userId) {
36
+ this.clients.delete(userId);
37
+ return;
195
38
  }
196
- }
197
- startMessagePolling() {
198
- const poll = async () => {
199
- try {
200
- const messages = await this.relay.fetchMessages(this.userId);
201
- if (messages.length > 0) {
202
- const msg = await this.decryptMessage(messages[0]);
203
- this.messageQueue.push(msg);
204
- for (const handler of this.messageHandlers.values()) {
205
- try {
206
- handler(msg);
207
- }
208
- catch {
209
- // Handler error does not break other handlers
210
- }
211
- }
212
- }
213
- }
214
- catch {
215
- // Silent error on poll
216
- }
217
- if (this.initialized) {
218
- setTimeout(poll, 1000);
219
- }
220
- };
221
- poll();
39
+ this.clients.clear();
222
40
  }
223
41
  }
224
42
  export async function init(config) {
225
- const relayUrl = config.relayUrl || 'https://relay.stvor.io';
226
- const timeout = config.timeout || 10000;
227
- if (!config.appToken || !config.appToken.startsWith('stvor_')) {
228
- throw Errors.invalidAppToken();
229
- }
230
- const appConfig = {
231
- appToken: config.appToken,
232
- relayUrl,
233
- timeout,
234
- };
235
- const app = new StvorApp(appConfig);
236
- try {
237
- const relay = new RelayClient(relayUrl, config.appToken, timeout);
238
- await relay.healthCheck();
239
- }
240
- catch {
241
- throw Errors.relayUnavailable();
43
+ if (!config.appToken.startsWith('stvor_')) {
44
+ throw new StvorError(Errors.INVALID_APP_TOKEN, 'Invalid app token');
242
45
  }
243
- return app;
46
+ return new StvorApp(config);
244
47
  }
245
- export const Stvor = {
246
- init,
247
- };
48
+ export const Stvor = { init };
@@ -1,38 +1,12 @@
1
- /**
2
- * STVOR DX Facade - Error Handling
3
- */
4
- export declare const ErrorCode: {
5
- readonly AUTH_FAILED: "AUTH_FAILED";
1
+ export declare const Errors: {
6
2
  readonly INVALID_APP_TOKEN: "INVALID_APP_TOKEN";
7
3
  readonly RELAY_UNAVAILABLE: "RELAY_UNAVAILABLE";
8
- readonly RECIPIENT_NOT_FOUND: "RECIPIENT_NOT_FOUND";
9
- readonly MESSAGE_INTEGRITY_FAILED: "MESSAGE_INTEGRITY_FAILED";
10
- readonly KEYSTORE_CORRUPTED: "KEYSTORE_CORRUPTED";
11
- readonly DEVICE_COMPROMISED: "DEVICE_COMPROMISED";
12
- readonly PROTOCOL_VERSION_MISMATCH: "PROTOCOL_VERSION_MISMATCH";
13
- readonly CLIENT_NOT_READY: "CLIENT_NOT_READY";
14
4
  readonly DELIVERY_FAILED: "DELIVERY_FAILED";
15
- readonly RECEIVE_TIMEOUT: "RECEIVE_TIMEOUT";
16
- readonly RECEIVE_IN_PROGRESS: "RECEIVE_IN_PROGRESS";
17
5
  };
18
- export type ErrorCode = typeof ErrorCode[keyof typeof ErrorCode];
6
+ export type ErrorCode = (typeof Errors)[keyof typeof Errors];
19
7
  export declare class StvorError extends Error {
20
- code: string;
21
- action?: string;
22
- retryable?: boolean;
23
- constructor(code: string, message: string, action?: string, retryable?: boolean);
8
+ code: ErrorCode;
9
+ action?: string | undefined;
10
+ retryable?: boolean | undefined;
11
+ constructor(code: ErrorCode, message: string, action?: string | undefined, retryable?: boolean | undefined);
24
12
  }
25
- export declare const Errors: {
26
- authFailed(): StvorError;
27
- invalidAppToken(): StvorError;
28
- relayUnavailable(): StvorError;
29
- recipientNotFound(userId: string): StvorError;
30
- messageIntegrityFailed(): StvorError;
31
- keystoreCorrupted(): StvorError;
32
- deviceCompromised(): StvorError;
33
- protocolMismatch(): StvorError;
34
- clientNotReady(): StvorError;
35
- deliveryFailed(recipientId: string): StvorError;
36
- receiveTimeout(): StvorError;
37
- receiveInProgress(): StvorError;
38
- };
@@ -1,64 +1,13 @@
1
- /**
2
- * STVOR DX Facade - Error Handling
3
- */
4
- export const ErrorCode = {
5
- AUTH_FAILED: 'AUTH_FAILED',
1
+ export const Errors = {
6
2
  INVALID_APP_TOKEN: 'INVALID_APP_TOKEN',
7
3
  RELAY_UNAVAILABLE: 'RELAY_UNAVAILABLE',
8
- RECIPIENT_NOT_FOUND: 'RECIPIENT_NOT_FOUND',
9
- MESSAGE_INTEGRITY_FAILED: 'MESSAGE_INTEGRITY_FAILED',
10
- KEYSTORE_CORRUPTED: 'KEYSTORE_CORRUPTED',
11
- DEVICE_COMPROMISED: 'DEVICE_COMPROMISED',
12
- PROTOCOL_VERSION_MISMATCH: 'PROTOCOL_VERSION_MISMATCH',
13
- CLIENT_NOT_READY: 'CLIENT_NOT_READY',
14
4
  DELIVERY_FAILED: 'DELIVERY_FAILED',
15
- RECEIVE_TIMEOUT: 'RECEIVE_TIMEOUT',
16
- RECEIVE_IN_PROGRESS: 'RECEIVE_IN_PROGRESS',
17
5
  };
18
6
  export class StvorError extends Error {
19
7
  constructor(code, message, action, retryable) {
20
8
  super(message);
21
- this.name = 'StvorError';
22
9
  this.code = code;
23
10
  this.action = action;
24
11
  this.retryable = retryable;
25
12
  }
26
13
  }
27
- export const Errors = {
28
- authFailed() {
29
- return new StvorError(ErrorCode.AUTH_FAILED, 'The AppToken is invalid or has been revoked.', 'Check your dashboard and regenerate a new AppToken.', false);
30
- },
31
- invalidAppToken() {
32
- return new StvorError(ErrorCode.INVALID_APP_TOKEN, 'Invalid AppToken format. AppToken must start with "stvor_".', 'Get your AppToken from the developer dashboard.', false);
33
- },
34
- relayUnavailable() {
35
- return new StvorError(ErrorCode.RELAY_UNAVAILABLE, 'Cannot connect to STVOR relay server.', 'Check your internet connection.', true);
36
- },
37
- recipientNotFound(userId) {
38
- return new StvorError(ErrorCode.RECIPIENT_NOT_FOUND, `User "${userId}" not found. They may not have registered with STVOR.`, 'Ask the recipient to initialize STVOR first, or verify the userId is correct.', false);
39
- },
40
- messageIntegrityFailed() {
41
- return new StvorError(ErrorCode.MESSAGE_INTEGRITY_FAILED, 'Message integrity check failed. The message may be corrupted or tampered with.', 'Request the message again from the sender.', false);
42
- },
43
- keystoreCorrupted() {
44
- return new StvorError(ErrorCode.KEYSTORE_CORRUPTED, 'Local keystore is corrupted. All local keys have been wiped.', 'The user will need to re-register this device.', false);
45
- },
46
- deviceCompromised() {
47
- return new StvorError(ErrorCode.DEVICE_COMPROMISED, 'Security violation detected. This device has been flagged.', 'Immediately log out the user and investigate.', false);
48
- },
49
- protocolMismatch() {
50
- return new StvorError(ErrorCode.PROTOCOL_VERSION_MISMATCH, 'This app version uses an older protocol version.', 'Update the SDK to the latest version.', false);
51
- },
52
- clientNotReady() {
53
- return new StvorError(ErrorCode.CLIENT_NOT_READY, 'Client is not ready. Call connect() first and await it.', 'Make sure to await app.connect() before sending messages.', false);
54
- },
55
- deliveryFailed(recipientId) {
56
- return new StvorError(ErrorCode.DELIVERY_FAILED, `Failed to deliver message to ${recipientId}.`, 'Check that the recipient exists and try again.', true);
57
- },
58
- receiveTimeout() {
59
- return new StvorError(ErrorCode.RECEIVE_TIMEOUT, 'No messages received within 30 second timeout.', 'Use onMessage() subscription for real-time updates.', true);
60
- },
61
- receiveInProgress() {
62
- return new StvorError(ErrorCode.RECEIVE_IN_PROGRESS, 'A receive() call is already in progress.', 'Wait for the current receive() to complete before calling again.', true);
63
- },
64
- };
@@ -1,17 +1,8 @@
1
- /**
2
- * STVOR DX Facade SDK
3
- * High-level developer experience layer for STVOR E2E encryption
4
- *
5
- * Design goals:
6
- * - Minimal API surface
7
- * - Zero crypto knowledge required
8
- * - Secure by default
9
- * - Opinionated (no configuration)
10
- */
11
- export type { DecryptedMessage, SealedPayload } from './app';
12
- export type { StvorAppConfig, AppToken, UserId, MessageContent } from './types';
13
- export type { ErrorCode } from './errors';
14
- export { StvorError } from './errors';
15
- export { StvorApp, StvorFacadeClient } from './app';
16
- export { Stvor, init } from './app';
17
- export { ErrorCode as STVOR_ERRORS } from './errors';
1
+ export * from './app.js';
2
+ export * from './errors.js';
3
+ export * from './types.js';
4
+ export type { DecryptedMessage, SealedPayload } from './types.js';
5
+ export type { StvorAppConfig, AppToken, UserId, MessageContent } from './types.js';
6
+ export { StvorError } from './errors.js';
7
+ export { StvorApp, StvorFacadeClient, Stvor, init } from './app.js';
8
+ export { ErrorCode as STVOR_ERRORS } from './errors.js';
@@ -1,15 +1,5 @@
1
- /**
2
- * STVOR DX Facade SDK
3
- * High-level developer experience layer for STVOR E2E encryption
4
- *
5
- * Design goals:
6
- * - Minimal API surface
7
- * - Zero crypto knowledge required
8
- * - Secure by default
9
- * - Opinionated (no configuration)
10
- */
11
- export { StvorError } from './errors';
12
- // Re-export classes and functions
13
- export { StvorApp, StvorFacadeClient } from './app';
14
- export { Stvor, init } from './app';
15
- export { ErrorCode as STVOR_ERRORS } from './errors';
1
+ export * from './app.js';
2
+ export * from './errors.js';
3
+ export * from './types.js';
4
+ export { StvorError } from './errors.js';
5
+ export { StvorApp, StvorFacadeClient, Stvor, init } from './app.js';
@@ -1,19 +1,6 @@
1
1
  /**
2
2
  * STVOR DX Facade - Relay Client
3
3
  */
4
- interface OutgoingMessage {
5
- to: string;
6
- from: string;
7
- ciphertext: Uint8Array;
8
- nonce: Uint8Array;
9
- }
10
- interface IncomingMessage {
11
- id?: string;
12
- from: string;
13
- ciphertext: number[];
14
- nonce: number[];
15
- timestamp: string;
16
- }
17
4
  export declare class RelayClient {
18
5
  private relayUrl;
19
6
  private timeout;
@@ -23,10 +10,4 @@ export declare class RelayClient {
23
10
  private getAuthHeaders;
24
11
  healthCheck(): Promise<void>;
25
12
  isConnected(): boolean;
26
- register(userId: string, publicKey: JsonWebKey): Promise<void>;
27
- getPublicKey(userId: string): Promise<CryptoKey | null>;
28
- send(message: OutgoingMessage): Promise<void>;
29
- fetchMessages(userId: string): Promise<IncomingMessage[]>;
30
- disconnect(): void;
31
13
  }
32
- export {};
@@ -1,7 +1,7 @@
1
1
  /**
2
2
  * STVOR DX Facade - Relay Client
3
3
  */
4
- import { Errors } from './errors';
4
+ import { Errors, StvorError } from './errors.js';
5
5
  export class RelayClient {
6
6
  constructor(relayUrl, appToken, timeout = 10000) {
7
7
  this.connected = false;
@@ -24,7 +24,7 @@ export class RelayClient {
24
24
  signal: controller.signal,
25
25
  });
26
26
  if (!res.ok) {
27
- throw Errors.relayUnavailable();
27
+ throw new StvorError(Errors.RELAY_UNAVAILABLE, 'Relay unavailable', 'Check network', true);
28
28
  }
29
29
  }
30
30
  finally {
@@ -34,95 +34,4 @@ export class RelayClient {
34
34
  isConnected() {
35
35
  return this.connected;
36
36
  }
37
- async register(userId, publicKey) {
38
- const controller = new AbortController();
39
- const timeoutId = setTimeout(() => controller.abort(), this.timeout);
40
- try {
41
- const res = await fetch(`${this.relayUrl}/register`, {
42
- method: 'POST',
43
- headers: this.getAuthHeaders(),
44
- body: JSON.stringify({ user_id: userId, publicKey }),
45
- signal: controller.signal,
46
- });
47
- if (!res.ok) {
48
- const error = await res.json().catch(() => ({}));
49
- if (error.code === 'AUTH_FAILED') {
50
- throw Errors.authFailed();
51
- }
52
- throw Errors.relayUnavailable();
53
- }
54
- this.connected = true;
55
- }
56
- finally {
57
- clearTimeout(timeoutId);
58
- }
59
- }
60
- async getPublicKey(userId) {
61
- const controller = new AbortController();
62
- const timeoutId = setTimeout(() => controller.abort(), this.timeout);
63
- try {
64
- const res = await fetch(`${this.relayUrl}/public-key/${userId}`, {
65
- method: 'GET',
66
- headers: this.getAuthHeaders(),
67
- signal: controller.signal,
68
- });
69
- if (res.status === 404) {
70
- return null;
71
- }
72
- if (!res.ok) {
73
- throw Errors.relayUnavailable();
74
- }
75
- const data = await res.json();
76
- const jwk = data.publicKey;
77
- return await crypto.subtle.importKey('jwk', jwk, { name: 'ECDH', namedCurve: 'X25519' }, false, []);
78
- }
79
- finally {
80
- clearTimeout(timeoutId);
81
- }
82
- }
83
- async send(message) {
84
- const controller = new AbortController();
85
- const timeoutId = setTimeout(() => controller.abort(), this.timeout);
86
- try {
87
- const res = await fetch(`${this.relayUrl}/message`, {
88
- method: 'POST',
89
- headers: this.getAuthHeaders(),
90
- body: JSON.stringify({
91
- to: message.to,
92
- from: message.from,
93
- ciphertext: Array.from(message.ciphertext),
94
- nonce: Array.from(message.nonce),
95
- }),
96
- signal: controller.signal,
97
- });
98
- if (!res.ok) {
99
- throw Errors.deliveryFailed(message.to);
100
- }
101
- }
102
- finally {
103
- clearTimeout(timeoutId);
104
- }
105
- }
106
- async fetchMessages(userId) {
107
- const controller = new AbortController();
108
- const timeoutId = setTimeout(() => controller.abort(), this.timeout);
109
- try {
110
- const res = await fetch(`${this.relayUrl}/messages/${userId}`, {
111
- method: 'GET',
112
- headers: this.getAuthHeaders(),
113
- signal: controller.signal,
114
- });
115
- if (!res.ok) {
116
- throw Errors.relayUnavailable();
117
- }
118
- const data = await res.json();
119
- return data.messages || [];
120
- }
121
- finally {
122
- clearTimeout(timeoutId);
123
- }
124
- }
125
- disconnect() {
126
- this.connected = false;
127
- }
128
37
  }
@@ -47,6 +47,4 @@ export interface SealedPayload {
47
47
  ciphertext: Uint8Array;
48
48
  /** Nonce used for encryption */
49
49
  nonce: Uint8Array;
50
- /** Recipient user ID this was sealed for */
51
- recipientId: UserId;
52
50
  }
package/dist/index.d.ts CHANGED
@@ -1,5 +1,2 @@
1
- /**
2
- * STVOR SDK - Main exports
3
- */
4
- export * from './legacy';
5
- export * from './facade';
1
+ export * from './legacy.js';
2
+ export * from './facade/index.js';
package/dist/index.js CHANGED
@@ -1,5 +1,2 @@
1
- /**
2
- * STVOR SDK - Main exports
3
- */
4
1
  export * from './legacy.js';
5
2
  export * from './facade/index.js';
package/dist/legacy.d.ts CHANGED
@@ -1,31 +1 @@
1
- /**
2
- * STVOR SDK - Legacy Core API
3
- * Kept for backwards compatibility
4
- */
5
- export interface StvorConfig {
6
- apiKey: string;
7
- serverUrl?: string;
8
- }
9
- export interface Peer {
10
- id: string;
11
- publicKey: any;
12
- }
13
- export interface EncryptedMessage {
14
- ciphertext: string;
15
- nonce: string;
16
- from: string;
17
- }
18
- export declare class StvorClient {
19
- private config;
20
- private myKeyPair;
21
- private myId;
22
- private peers;
23
- constructor(config: StvorConfig);
24
- ready(): Promise<void>;
25
- createPeer(name: string): Promise<Peer>;
26
- send({ to, message }: {
27
- to: string;
28
- message: string;
29
- }): Promise<EncryptedMessage>;
30
- receive(encrypted: EncryptedMessage): Promise<string>;
31
- }
1
+ export {};
package/dist/legacy.js CHANGED
@@ -1,90 +1,2 @@
1
- /**
2
- * STVOR SDK - Legacy Core API
3
- * Kept for backwards compatibility
4
- */
5
- export class StvorClient {
6
- constructor(config) {
7
- this.myKeyPair = null;
8
- this.myId = '';
9
- this.peers = new Map();
10
- this.config = {
11
- serverUrl: 'http://localhost:3001',
12
- ...config,
13
- };
14
- }
15
- async ready() {
16
- if (!this.config.apiKey) {
17
- throw new Error('API key is required');
18
- }
19
- this.myKeyPair = await crypto.subtle.generateKey({ name: 'ECDH', namedCurve: 'P-256' }, true, ['deriveKey', 'deriveBits']);
20
- this.myId = this.config.apiKey.substring(0, 8);
21
- }
22
- async createPeer(name) {
23
- if (!this.myKeyPair)
24
- throw new Error('Call ready() first');
25
- const publicKey = await crypto.subtle.exportKey('jwk', this.myKeyPair.publicKey);
26
- const res = await fetch(`${this.config.serverUrl}/register`, {
27
- method: 'POST',
28
- headers: { 'Content-Type': 'application/json' },
29
- body: JSON.stringify({ user_id: name, publicKey }),
30
- });
31
- if (!res.ok) {
32
- const err = await res.json();
33
- throw new Error(`Registration failed: ${JSON.stringify(err)}`);
34
- }
35
- return { id: name, publicKey };
36
- }
37
- async send({ to, message }) {
38
- if (!this.myKeyPair)
39
- throw new Error('Call ready() first');
40
- let recipientKey = this.peers.get(to);
41
- if (!recipientKey) {
42
- const res = await fetch(`${this.config.serverUrl}/public-key/${to}`);
43
- if (!res.ok)
44
- throw new Error(`Peer ${to} not found`);
45
- const { publicKey } = await res.json();
46
- recipientKey = await crypto.subtle.importKey('jwk', publicKey, { name: 'ECDH', namedCurve: 'P-256' }, false, []);
47
- this.peers.set(to, recipientKey);
48
- }
49
- const sharedKey = await crypto.subtle.deriveKey({ name: 'ECDH', public: recipientKey }, this.myKeyPair.privateKey, { name: 'AES-GCM', length: 256 }, false, ['encrypt', 'decrypt']);
50
- const iv = crypto.getRandomValues(new Uint8Array(12));
51
- const encoder = new TextEncoder();
52
- const encrypted = await crypto.subtle.encrypt({ name: 'AES-GCM', iv }, sharedKey, encoder.encode(message));
53
- const sendRes = await fetch(`${this.config.serverUrl}/message`, {
54
- method: 'POST',
55
- headers: { 'Content-Type': 'application/json' },
56
- body: JSON.stringify({
57
- from: this.myId,
58
- to,
59
- ciphertext: Buffer.from(encrypted).toString('base64'),
60
- nonce: Buffer.from(iv).toString('base64'),
61
- }),
62
- });
63
- if (!sendRes.ok) {
64
- throw new Error('Failed to send message');
65
- }
66
- return {
67
- ciphertext: Buffer.from(encrypted).toString('base64'),
68
- nonce: Buffer.from(iv).toString('base64'),
69
- from: this.myId
70
- };
71
- }
72
- async receive(encrypted) {
73
- if (!this.myKeyPair)
74
- throw new Error('Call ready() first');
75
- let senderKey = this.peers.get(encrypted.from);
76
- if (!senderKey) {
77
- const res = await fetch(`${this.config.serverUrl}/public-key/${encrypted.from}`);
78
- if (!res.ok)
79
- throw new Error(`Sender ${encrypted.from} not found`);
80
- const { publicKey } = await res.json();
81
- senderKey = await crypto.subtle.importKey('jwk', publicKey, { name: 'ECDH', namedCurve: 'P-256' }, false, []);
82
- this.peers.set(encrypted.from, senderKey);
83
- }
84
- const sharedKey = await crypto.subtle.deriveKey({ name: 'ECDH', public: senderKey }, this.myKeyPair.privateKey, { name: 'AES-GCM', length: 256 }, false, ['encrypt', 'decrypt']);
85
- const iv = Buffer.from(encrypted.nonce, 'base64');
86
- const ciphertext = Buffer.from(encrypted.ciphertext, 'base64');
87
- const decrypted = await crypto.subtle.decrypt({ name: 'AES-GCM', iv }, sharedKey, ciphertext);
88
- return new TextDecoder().decode(decrypted);
89
- }
90
- }
1
+ export {};
2
+ // ...existing code from packages/sdk/legacy.ts...
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@stvor/sdk",
3
- "version": "2.0.4",
3
+ "version": "2.0.8",
4
4
  "description": "STVOR DX Facade - Simple E2EE SDK for client-side encryption",
5
5
  "type": "module",
6
6
  "main": "./dist/index.js",
package/dist/example.d.ts DELETED
@@ -1,9 +0,0 @@
1
- /**
2
- * STVOR DX Facade - Quick Start Example
3
- *
4
- * Copy-paste ready example for getting started.
5
- *
6
- * IMPORTANT: This is a DX facade. The actual security guarantees
7
- * depend on the STVOR core implementation.
8
- */
9
- export {};
package/dist/example.js DELETED
@@ -1,57 +0,0 @@
1
- /**
2
- * STVOR DX Facade - Quick Start Example
3
- *
4
- * Copy-paste ready example for getting started.
5
- *
6
- * IMPORTANT: This is a DX facade. The actual security guarantees
7
- * depend on the STVOR core implementation.
8
- */
9
- import { Stvor, StvorError } from './index.js';
10
- /**
11
- * Basic messaging example with proper error handling
12
- */
13
- async function main() {
14
- try {
15
- // 1. Initialize SDK with AppToken from environment
16
- const app = await Stvor.init({
17
- appToken: process.env.STVOR_APP_TOKEN || 'stvor_demo_abc123...'
18
- });
19
- console.log('SDK initialized');
20
- // 2. Connect as a user
21
- const alice = await app.connect('alice@example.com');
22
- console.log(`Connected as ${alice.getUserId()}`);
23
- // 3. Subscribe to incoming messages (recommended for production)
24
- const unsubscribe = alice.onMessage((msg) => {
25
- console.log(`[Push] ${msg.senderId}: ${msg.content}`);
26
- });
27
- // 4. Send encrypted message
28
- await alice.send('bob@example.com', 'Hello Bob!');
29
- // 5. Cleanup
30
- await app.disconnect();
31
- unsubscribe();
32
- }
33
- catch (error) {
34
- if (error instanceof StvorError) {
35
- console.error(`[${error.code}] ${error.message}`);
36
- console.error(`Action: ${error.action}`);
37
- console.error(`Retryable: ${error.retryable}`);
38
- }
39
- else {
40
- console.error('Unknown error:', error);
41
- }
42
- }
43
- }
44
- /**
45
- * Note on security guarantees:
46
- *
47
- * The facade provides a convenient API, but actual security
48
- * (E2EE, PFS, post-quantum resistance) depends on the STVOR core.
49
- *
50
- * For production use, verify:
51
- * 1. Core implements Double Ratchet for PFS
52
- * 2. Core implements ML-KEM for post-quantum resistance
53
- * 3. Keys are stored securely (not in plain memory)
54
- * 4. Relay is trusted or verified
55
- */
56
- // Run example
57
- main();