@massalabs/gossip-sdk 0.0.2-dev.20260220053835 → 0.0.2-dev.20260220083849
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/api/authProtocol.d.ts +19 -0
- package/dist/api/authProtocol.js +37 -0
- package/dist/api/messageProtocol/rest.d.ts +2 -8
- package/dist/api/messageProtocol/rest.js +2 -80
- package/dist/api/messageProtocol/types.d.ts +0 -12
- package/dist/api/restClient.d.ts +16 -0
- package/dist/api/restClient.js +57 -0
- package/dist/gossip.js +3 -2
- package/dist/services/auth.d.ts +4 -5
- package/dist/services/auth.js +12 -20
- package/dist/services/message.js +1 -1
- package/package.json +1 -1
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Auth Protocol — public key publishing & retrieval
|
|
3
|
+
*/
|
|
4
|
+
import { RestClient } from './restClient';
|
|
5
|
+
export interface IAuthProtocol {
|
|
6
|
+
/** Fetches the base64-encoded public key for the given user ID. */
|
|
7
|
+
fetchPublicKeyByUserId(userId: Uint8Array): Promise<string>;
|
|
8
|
+
/** Publishes the given base64-encoded public key to the server. */
|
|
9
|
+
postPublicKey(base64PublicKeys: string): Promise<string>;
|
|
10
|
+
}
|
|
11
|
+
export declare function createAuthProtocol(config?: Partial<{
|
|
12
|
+
baseUrl: string;
|
|
13
|
+
timeout: number;
|
|
14
|
+
retryAttempts: number;
|
|
15
|
+
}>): IAuthProtocol;
|
|
16
|
+
export declare class RestAuthProtocol extends RestClient implements IAuthProtocol {
|
|
17
|
+
fetchPublicKeyByUserId(userId: Uint8Array): Promise<string>;
|
|
18
|
+
postPublicKey(base64PublicKeys: string): Promise<string>;
|
|
19
|
+
}
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Auth Protocol — public key publishing & retrieval
|
|
3
|
+
*/
|
|
4
|
+
import { RestClient } from './restClient';
|
|
5
|
+
import { encodeToBase64 } from '../utils/base64';
|
|
6
|
+
import { protocolConfig } from '../config/protocol';
|
|
7
|
+
export function createAuthProtocol(config) {
|
|
8
|
+
return new RestAuthProtocol(config?.baseUrl ?? protocolConfig.baseUrl, config?.timeout ?? protocolConfig.timeout, config?.retryAttempts ?? protocolConfig.retryAttempts);
|
|
9
|
+
}
|
|
10
|
+
export class RestAuthProtocol extends RestClient {
|
|
11
|
+
async fetchPublicKeyByUserId(userId) {
|
|
12
|
+
const response = await this.makeRequest(`${this.baseUrl}/auth/retrieve`, {
|
|
13
|
+
method: 'POST',
|
|
14
|
+
headers: { 'Content-Type': 'application/json' },
|
|
15
|
+
body: JSON.stringify({ key: encodeToBase64(userId) }),
|
|
16
|
+
});
|
|
17
|
+
if (!response.success || !response.data) {
|
|
18
|
+
throw new Error(response.error || 'Failed to fetch public key');
|
|
19
|
+
}
|
|
20
|
+
if (!response.data.value) {
|
|
21
|
+
throw new Error('Public key not found');
|
|
22
|
+
}
|
|
23
|
+
return response.data.value;
|
|
24
|
+
}
|
|
25
|
+
async postPublicKey(base64PublicKeys) {
|
|
26
|
+
const url = `${this.baseUrl}/auth`;
|
|
27
|
+
const response = await this.makeRequest(url, {
|
|
28
|
+
method: 'POST',
|
|
29
|
+
headers: { 'Content-Type': 'application/json' },
|
|
30
|
+
body: JSON.stringify({ value: base64PublicKeys }),
|
|
31
|
+
});
|
|
32
|
+
if (!response.success || !response.data) {
|
|
33
|
+
throw new Error(response.error || 'Failed to store public key');
|
|
34
|
+
}
|
|
35
|
+
return response.data.value;
|
|
36
|
+
}
|
|
37
|
+
}
|
|
@@ -2,22 +2,16 @@
|
|
|
2
2
|
* REST API implementation of the message protocol
|
|
3
3
|
*/
|
|
4
4
|
import { BulletinItem, EncryptedMessage, IMessageProtocol, MessageProtocolResponse } from './types';
|
|
5
|
+
import { RestClient } from '../restClient';
|
|
5
6
|
export type BulletinsPage = {
|
|
6
7
|
counter: string;
|
|
7
8
|
data: string;
|
|
8
9
|
}[];
|
|
9
|
-
export declare class RestMessageProtocol implements IMessageProtocol {
|
|
10
|
-
private baseUrl;
|
|
11
|
-
private timeout;
|
|
12
|
-
private retryAttempts;
|
|
13
|
-
constructor(baseUrl: string, timeout?: number, retryAttempts?: number);
|
|
10
|
+
export declare class RestMessageProtocol extends RestClient implements IMessageProtocol {
|
|
14
11
|
fetchMessages(seekers: Uint8Array[]): Promise<EncryptedMessage[]>;
|
|
15
12
|
sendMessage(message: EncryptedMessage): Promise<void>;
|
|
16
13
|
sendAnnouncement(announcement: Uint8Array): Promise<string>;
|
|
17
14
|
fetchAnnouncements(limit?: number, cursor?: string): Promise<BulletinItem[]>;
|
|
18
15
|
fetchBulletinCounter(): Promise<string>;
|
|
19
|
-
fetchPublicKeyByUserId(userId: Uint8Array): Promise<string>;
|
|
20
|
-
postPublicKey(base64PublicKeys: string): Promise<string>;
|
|
21
|
-
private makeRequest;
|
|
22
16
|
changeNode(nodeUrl?: string): Promise<MessageProtocolResponse>;
|
|
23
17
|
}
|
|
@@ -1,30 +1,11 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* REST API implementation of the message protocol
|
|
3
3
|
*/
|
|
4
|
+
import { RestClient } from '../restClient';
|
|
4
5
|
import { encodeToBase64, decodeFromBase64 } from '../../utils/base64';
|
|
5
6
|
const BULLETIN_ENDPOINT = '/bulletin';
|
|
6
7
|
const MESSAGES_ENDPOINT = '/messages';
|
|
7
|
-
export class RestMessageProtocol {
|
|
8
|
-
constructor(baseUrl, timeout = 10000, retryAttempts = 3) {
|
|
9
|
-
Object.defineProperty(this, "baseUrl", {
|
|
10
|
-
enumerable: true,
|
|
11
|
-
configurable: true,
|
|
12
|
-
writable: true,
|
|
13
|
-
value: baseUrl
|
|
14
|
-
});
|
|
15
|
-
Object.defineProperty(this, "timeout", {
|
|
16
|
-
enumerable: true,
|
|
17
|
-
configurable: true,
|
|
18
|
-
writable: true,
|
|
19
|
-
value: timeout
|
|
20
|
-
});
|
|
21
|
-
Object.defineProperty(this, "retryAttempts", {
|
|
22
|
-
enumerable: true,
|
|
23
|
-
configurable: true,
|
|
24
|
-
writable: true,
|
|
25
|
-
value: retryAttempts
|
|
26
|
-
});
|
|
27
|
-
}
|
|
8
|
+
export class RestMessageProtocol extends RestClient {
|
|
28
9
|
// TODO: Implement a fetch with pagination to avoid fetching all messages at once
|
|
29
10
|
async fetchMessages(seekers) {
|
|
30
11
|
const url = `${this.baseUrl}${MESSAGES_ENDPOINT}/fetch`;
|
|
@@ -101,65 +82,6 @@ export class RestMessageProtocol {
|
|
|
101
82
|
}
|
|
102
83
|
return response.data.counter;
|
|
103
84
|
}
|
|
104
|
-
async fetchPublicKeyByUserId(userId) {
|
|
105
|
-
const response = await this.makeRequest(`${this.baseUrl}/auth/retrieve`, {
|
|
106
|
-
method: 'POST',
|
|
107
|
-
headers: { 'Content-Type': 'application/json' },
|
|
108
|
-
body: JSON.stringify({ key: encodeToBase64(userId) }),
|
|
109
|
-
});
|
|
110
|
-
if (!response.success || !response.data) {
|
|
111
|
-
throw new Error(response.error || 'Failed to fetch public key');
|
|
112
|
-
}
|
|
113
|
-
if (!response.data.value) {
|
|
114
|
-
throw new Error('Public key not found');
|
|
115
|
-
}
|
|
116
|
-
return response.data.value;
|
|
117
|
-
}
|
|
118
|
-
async postPublicKey(base64PublicKeys) {
|
|
119
|
-
const url = `${this.baseUrl}/auth`;
|
|
120
|
-
const response = await this.makeRequest(url, {
|
|
121
|
-
method: 'POST',
|
|
122
|
-
headers: { 'Content-Type': 'application/json' },
|
|
123
|
-
body: JSON.stringify({ value: base64PublicKeys }),
|
|
124
|
-
});
|
|
125
|
-
if (!response.success || !response.data) {
|
|
126
|
-
const errorMessage = response.error || 'Failed to store public key';
|
|
127
|
-
throw new Error(errorMessage);
|
|
128
|
-
}
|
|
129
|
-
return response.data.value;
|
|
130
|
-
}
|
|
131
|
-
async makeRequest(url, options) {
|
|
132
|
-
let lastError = null;
|
|
133
|
-
for (let attempt = 1; attempt <= this.retryAttempts; attempt++) {
|
|
134
|
-
try {
|
|
135
|
-
const controller = new AbortController();
|
|
136
|
-
const timeoutId = setTimeout(() => controller.abort(), this.timeout);
|
|
137
|
-
const response = await fetch(url, {
|
|
138
|
-
...options,
|
|
139
|
-
signal: controller.signal,
|
|
140
|
-
});
|
|
141
|
-
clearTimeout(timeoutId);
|
|
142
|
-
if (!response.ok) {
|
|
143
|
-
throw new Error(`HTTP ${response.status}: ${response.statusText}`);
|
|
144
|
-
}
|
|
145
|
-
const data = await response.json();
|
|
146
|
-
return { success: true, data };
|
|
147
|
-
}
|
|
148
|
-
catch (error) {
|
|
149
|
-
lastError = error;
|
|
150
|
-
console.warn(`Request attempt ${attempt} failed:`, error);
|
|
151
|
-
if (attempt < this.retryAttempts) {
|
|
152
|
-
// Exponential backoff
|
|
153
|
-
const delay = Math.pow(2, attempt) * 1000;
|
|
154
|
-
await new Promise(resolve => setTimeout(resolve, delay));
|
|
155
|
-
}
|
|
156
|
-
}
|
|
157
|
-
}
|
|
158
|
-
return {
|
|
159
|
-
success: false,
|
|
160
|
-
error: lastError?.message || 'Request failed after all retry attempts',
|
|
161
|
-
};
|
|
162
|
-
}
|
|
163
85
|
async changeNode(nodeUrl) {
|
|
164
86
|
return {
|
|
165
87
|
success: true,
|
|
@@ -44,18 +44,6 @@ export interface IMessageProtocol {
|
|
|
44
44
|
* Fetch the latest bulletin counter from the API without downloading announcement data.
|
|
45
45
|
*/
|
|
46
46
|
fetchBulletinCounter(): Promise<string>;
|
|
47
|
-
/**
|
|
48
|
-
* Fetch public key by userId hash (base64 string)
|
|
49
|
-
* @param userId - Decoded userId bytes
|
|
50
|
-
* @returns Base64-encoded public keys
|
|
51
|
-
*/
|
|
52
|
-
fetchPublicKeyByUserId(userId: Uint8Array): Promise<string>;
|
|
53
|
-
/**
|
|
54
|
-
* Store public key in the auth API
|
|
55
|
-
* @param base64PublicKeys - Base64-encoded public keys
|
|
56
|
-
* @returns The hash key (hex string) returned by the API
|
|
57
|
-
*/
|
|
58
|
-
postPublicKey(base64PublicKeys: string): Promise<string>;
|
|
59
47
|
/**
|
|
60
48
|
* Change the current node provider
|
|
61
49
|
* @param nodeUrl - The URL of the new node
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Base REST client with retry and timeout support.
|
|
3
|
+
* Shared by RestMessageProtocol and RestAuthProtocol.
|
|
4
|
+
*/
|
|
5
|
+
export interface RestResponse<T = unknown> {
|
|
6
|
+
success: boolean;
|
|
7
|
+
data?: T;
|
|
8
|
+
error?: string;
|
|
9
|
+
}
|
|
10
|
+
export declare class RestClient {
|
|
11
|
+
protected baseUrl: string;
|
|
12
|
+
protected timeout: number;
|
|
13
|
+
protected retryAttempts: number;
|
|
14
|
+
constructor(baseUrl: string, timeout?: number, retryAttempts?: number);
|
|
15
|
+
protected makeRequest<T>(url: string, options: RequestInit): Promise<RestResponse<T>>;
|
|
16
|
+
}
|
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Base REST client with retry and timeout support.
|
|
3
|
+
* Shared by RestMessageProtocol and RestAuthProtocol.
|
|
4
|
+
*/
|
|
5
|
+
export class RestClient {
|
|
6
|
+
constructor(baseUrl, timeout = 10000, retryAttempts = 3) {
|
|
7
|
+
Object.defineProperty(this, "baseUrl", {
|
|
8
|
+
enumerable: true,
|
|
9
|
+
configurable: true,
|
|
10
|
+
writable: true,
|
|
11
|
+
value: baseUrl
|
|
12
|
+
});
|
|
13
|
+
Object.defineProperty(this, "timeout", {
|
|
14
|
+
enumerable: true,
|
|
15
|
+
configurable: true,
|
|
16
|
+
writable: true,
|
|
17
|
+
value: timeout
|
|
18
|
+
});
|
|
19
|
+
Object.defineProperty(this, "retryAttempts", {
|
|
20
|
+
enumerable: true,
|
|
21
|
+
configurable: true,
|
|
22
|
+
writable: true,
|
|
23
|
+
value: retryAttempts
|
|
24
|
+
});
|
|
25
|
+
}
|
|
26
|
+
async makeRequest(url, options) {
|
|
27
|
+
let lastError = null;
|
|
28
|
+
for (let attempt = 1; attempt <= this.retryAttempts; attempt++) {
|
|
29
|
+
try {
|
|
30
|
+
const controller = new AbortController();
|
|
31
|
+
const timeoutId = setTimeout(() => controller.abort(), this.timeout);
|
|
32
|
+
const response = await fetch(url, {
|
|
33
|
+
...options,
|
|
34
|
+
signal: controller.signal,
|
|
35
|
+
});
|
|
36
|
+
clearTimeout(timeoutId);
|
|
37
|
+
if (!response.ok) {
|
|
38
|
+
throw new Error(`HTTP ${response.status}: ${response.statusText}`);
|
|
39
|
+
}
|
|
40
|
+
const data = await response.json();
|
|
41
|
+
return { success: true, data };
|
|
42
|
+
}
|
|
43
|
+
catch (error) {
|
|
44
|
+
lastError = error;
|
|
45
|
+
console.warn(`Request attempt ${attempt} failed:`, error);
|
|
46
|
+
if (attempt < this.retryAttempts) {
|
|
47
|
+
const delay = Math.pow(2, attempt) * 1000;
|
|
48
|
+
await new Promise(resolve => setTimeout(resolve, delay));
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
return {
|
|
53
|
+
success: false,
|
|
54
|
+
error: lastError?.message || 'Request failed after all retry attempts',
|
|
55
|
+
};
|
|
56
|
+
}
|
|
57
|
+
}
|
package/dist/gossip.js
CHANGED
|
@@ -41,6 +41,7 @@
|
|
|
41
41
|
import { MessageStatus, } from './db';
|
|
42
42
|
import { toDiscussion, toSortedDiscussions } from './utils/discussions';
|
|
43
43
|
import { createMessageProtocol } from './api/messageProtocol';
|
|
44
|
+
import { createAuthProtocol } from './api/authProtocol';
|
|
44
45
|
import { setProtocolBaseUrl } from './config/protocol';
|
|
45
46
|
import { defaultSdkConfig, mergeConfig, } from './config/sdk';
|
|
46
47
|
import { startWasmInitialization, ensureWasmInitialized } from './wasm/loader';
|
|
@@ -180,8 +181,8 @@ class GossipSdk {
|
|
|
180
181
|
console.log('[GossipSdk] SQLite initialized');
|
|
181
182
|
// Create message protocol
|
|
182
183
|
const messageProtocol = createMessageProtocol();
|
|
183
|
-
// Create auth service (doesn't need session)
|
|
184
|
-
this._auth = new AuthService(
|
|
184
|
+
// Create auth protocol + service (doesn't need session)
|
|
185
|
+
this._auth = new AuthService(createAuthProtocol());
|
|
185
186
|
this.state = {
|
|
186
187
|
status: SdkStatus.INITIALIZED,
|
|
187
188
|
messageProtocol,
|
package/dist/services/auth.d.ts
CHANGED
|
@@ -4,18 +4,17 @@
|
|
|
4
4
|
* Handles storing and retrieving public keys by userId hash via the auth API.
|
|
5
5
|
*/
|
|
6
6
|
import { UserPublicKeys } from '../wasm/bindings';
|
|
7
|
-
import {
|
|
7
|
+
import { IAuthProtocol } from '../api/authProtocol';
|
|
8
8
|
export declare class AuthService {
|
|
9
|
-
|
|
10
|
-
constructor(
|
|
9
|
+
authProtocol: IAuthProtocol;
|
|
10
|
+
constructor(authProtocol: IAuthProtocol);
|
|
11
11
|
/**
|
|
12
12
|
* Fetch public key by userId
|
|
13
13
|
* @param userId - Bech32-encoded userId (e.g., "gossip1...")
|
|
14
14
|
*/
|
|
15
15
|
fetchPublicKeyByUserId(userId: string): Promise<UserPublicKeys>;
|
|
16
16
|
/**
|
|
17
|
-
* Ensure public key is published
|
|
18
|
-
* If no user profile exists, the key is still published so the gossip ID is discoverable.
|
|
17
|
+
* Ensure public key is published. Checks the server first, only publishes if not found.
|
|
19
18
|
* @param publicKeys - UserPublicKeys instance
|
|
20
19
|
* @param userId - Bech32-encoded userId (e.g., "gossip1...")
|
|
21
20
|
*/
|
package/dist/services/auth.js
CHANGED
|
@@ -6,14 +6,13 @@
|
|
|
6
6
|
import { UserPublicKeys } from '../wasm/bindings';
|
|
7
7
|
import { decodeUserId } from '../utils/userId';
|
|
8
8
|
import { encodeToBase64, decodeFromBase64 } from '../utils/base64';
|
|
9
|
-
import { getUserProfileField, updateUserProfileById } from '../queries';
|
|
10
9
|
export class AuthService {
|
|
11
|
-
constructor(
|
|
12
|
-
Object.defineProperty(this, "
|
|
10
|
+
constructor(authProtocol) {
|
|
11
|
+
Object.defineProperty(this, "authProtocol", {
|
|
13
12
|
enumerable: true,
|
|
14
13
|
configurable: true,
|
|
15
14
|
writable: true,
|
|
16
|
-
value:
|
|
15
|
+
value: authProtocol
|
|
17
16
|
});
|
|
18
17
|
}
|
|
19
18
|
/**
|
|
@@ -22,7 +21,7 @@ export class AuthService {
|
|
|
22
21
|
*/
|
|
23
22
|
async fetchPublicKeyByUserId(userId) {
|
|
24
23
|
try {
|
|
25
|
-
const base64PublicKey = await this.
|
|
24
|
+
const base64PublicKey = await this.authProtocol.fetchPublicKeyByUserId(decodeUserId(userId));
|
|
26
25
|
return UserPublicKeys.from_bytes(decodeFromBase64(base64PublicKey));
|
|
27
26
|
}
|
|
28
27
|
catch (err) {
|
|
@@ -30,29 +29,22 @@ export class AuthService {
|
|
|
30
29
|
}
|
|
31
30
|
}
|
|
32
31
|
/**
|
|
33
|
-
* Ensure public key is published
|
|
34
|
-
* If no user profile exists, the key is still published so the gossip ID is discoverable.
|
|
32
|
+
* Ensure public key is published. Checks the server first, only publishes if not found.
|
|
35
33
|
* @param publicKeys - UserPublicKeys instance
|
|
36
34
|
* @param userId - Bech32-encoded userId (e.g., "gossip1...")
|
|
37
35
|
*/
|
|
38
36
|
async ensurePublicKeyPublished(publicKeys, userId) {
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
return;
|
|
44
|
-
}
|
|
37
|
+
// Check if our key is already on the server
|
|
38
|
+
try {
|
|
39
|
+
await this.authProtocol.fetchPublicKeyByUserId(decodeUserId(userId));
|
|
40
|
+
return; // Key exists on server, nothing to do
|
|
45
41
|
}
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
await updateUserProfileById(userId, { lastPublicKeyPush: new Date() });
|
|
42
|
+
catch {
|
|
43
|
+
// Key not found on server — publish it
|
|
49
44
|
}
|
|
45
|
+
await this.authProtocol.postPublicKey(encodeToBase64(publicKeys.to_bytes()));
|
|
50
46
|
}
|
|
51
47
|
}
|
|
52
|
-
const ONE_WEEK_IN_MILLIS = 7 * 24 * 60 * 60 * 1000;
|
|
53
|
-
function moreThanOneWeekAgo(date) {
|
|
54
|
-
return Date.now() - date.getTime() >= ONE_WEEK_IN_MILLIS;
|
|
55
|
-
}
|
|
56
48
|
export const PUBLIC_KEY_NOT_FOUND_ERROR = 'Public key not found';
|
|
57
49
|
export const PUBLIC_KEY_NOT_FOUND_MESSAGE = 'Contact public key not found. It may not be published yet.';
|
|
58
50
|
export const FAILED_TO_FETCH_ERROR = 'Failed to fetch';
|
package/dist/services/message.js
CHANGED
|
@@ -483,7 +483,7 @@ export class MessageService {
|
|
|
483
483
|
};
|
|
484
484
|
}
|
|
485
485
|
if (message.replyTo) {
|
|
486
|
-
const originalMessage = await this.findMessageByMsgId(message.replyTo.originalMsgId, message.ownerUserId);
|
|
486
|
+
const originalMessage = await this.findMessageByMsgId(message.replyTo.originalMsgId, message.ownerUserId, message.contactUserId);
|
|
487
487
|
if (!originalMessage) {
|
|
488
488
|
return {
|
|
489
489
|
success: false,
|
package/package.json
CHANGED