@massalabs/gossip-sdk 0.0.2-dev.20260128094509 → 0.0.2-dev.20260128111120
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/messageProtocol/index.d.ts +19 -0
- package/dist/api/messageProtocol/index.js +26 -0
- package/dist/api/messageProtocol/mock.d.ts +12 -0
- package/{src/api/messageProtocol/mock.ts → dist/api/messageProtocol/mock.js} +2 -3
- package/dist/api/messageProtocol/rest.d.ts +22 -0
- package/dist/api/messageProtocol/rest.js +161 -0
- package/dist/api/messageProtocol/types.d.ts +61 -0
- package/dist/api/messageProtocol/types.js +6 -0
- package/dist/assets/generated/wasm/README.md +281 -0
- package/dist/assets/generated/wasm/gossip_wasm.d.ts +498 -0
- package/dist/assets/generated/wasm/gossip_wasm.js +1399 -0
- package/dist/assets/generated/wasm/gossip_wasm_bg.wasm +0 -0
- package/dist/assets/generated/wasm/gossip_wasm_bg.wasm.d.ts +68 -0
- package/dist/assets/generated/wasm/package.json +15 -0
- package/dist/config/protocol.d.ts +36 -0
- package/dist/config/protocol.js +77 -0
- package/dist/config/sdk.d.ts +82 -0
- package/dist/config/sdk.js +55 -0
- package/{src/contacts.ts → dist/contacts.d.ts} +10 -94
- package/dist/contacts.js +166 -0
- package/dist/core/SdkEventEmitter.d.ts +36 -0
- package/dist/core/SdkEventEmitter.js +59 -0
- package/dist/core/SdkPolling.d.ts +35 -0
- package/dist/core/SdkPolling.js +100 -0
- package/{src/core/index.ts → dist/core/index.d.ts} +0 -2
- package/dist/core/index.js +5 -0
- package/dist/crypto/bip39.d.ts +34 -0
- package/dist/crypto/bip39.js +62 -0
- package/dist/crypto/encryption.d.ts +37 -0
- package/dist/crypto/encryption.js +46 -0
- package/dist/db.d.ts +190 -0
- package/dist/db.js +311 -0
- package/dist/gossipSdk.d.ts +274 -0
- package/dist/gossipSdk.js +690 -0
- package/dist/index.d.ts +73 -0
- package/dist/index.js +77 -0
- package/dist/services/announcement.d.ts +43 -0
- package/dist/services/announcement.js +491 -0
- package/dist/services/auth.d.ts +37 -0
- package/dist/services/auth.js +76 -0
- package/dist/services/discussion.d.ts +63 -0
- package/dist/services/discussion.js +297 -0
- package/dist/services/message.d.ts +74 -0
- package/dist/services/message.js +826 -0
- package/dist/services/refresh.d.ts +41 -0
- package/dist/services/refresh.js +205 -0
- package/{src/sw.ts → dist/sw.d.ts} +1 -8
- package/dist/sw.js +10 -0
- package/dist/types/events.d.ts +80 -0
- package/dist/types/events.js +7 -0
- package/dist/types.d.ts +32 -0
- package/dist/types.js +7 -0
- package/dist/utils/base64.d.ts +10 -0
- package/dist/utils/base64.js +30 -0
- package/dist/utils/contacts.d.ts +42 -0
- package/dist/utils/contacts.js +113 -0
- package/dist/utils/discussions.d.ts +24 -0
- package/dist/utils/discussions.js +38 -0
- package/dist/utils/logs.d.ts +19 -0
- package/dist/utils/logs.js +89 -0
- package/dist/utils/messageSerialization.d.ts +64 -0
- package/dist/utils/messageSerialization.js +184 -0
- package/dist/utils/queue.d.ts +50 -0
- package/dist/utils/queue.js +110 -0
- package/dist/utils/type.d.ts +10 -0
- package/dist/utils/type.js +4 -0
- package/dist/utils/userId.d.ts +40 -0
- package/dist/utils/userId.js +90 -0
- package/dist/utils/validation.d.ts +50 -0
- package/dist/utils/validation.js +112 -0
- package/dist/utils.d.ts +30 -0
- package/{src/utils.ts → dist/utils.js} +9 -19
- package/dist/wasm/encryption.d.ts +56 -0
- package/{src/wasm/encryption.ts → dist/wasm/encryption.js} +22 -51
- package/dist/wasm/index.d.ts +10 -0
- package/{src/wasm/index.ts → dist/wasm/index.js} +1 -8
- package/dist/wasm/loader.d.ts +21 -0
- package/dist/wasm/loader.js +103 -0
- package/dist/wasm/session.d.ts +85 -0
- package/dist/wasm/session.js +226 -0
- package/dist/wasm/userKeys.d.ts +17 -0
- package/{src/wasm/userKeys.ts → dist/wasm/userKeys.js} +6 -13
- package/package.json +5 -1
- package/src/api/messageProtocol/index.ts +0 -53
- package/src/api/messageProtocol/rest.ts +0 -209
- package/src/api/messageProtocol/types.ts +0 -70
- package/src/config/protocol.ts +0 -97
- package/src/config/sdk.ts +0 -131
- package/src/core/SdkEventEmitter.ts +0 -91
- package/src/core/SdkPolling.ts +0 -134
- package/src/crypto/bip39.ts +0 -84
- package/src/crypto/encryption.ts +0 -77
- package/src/db.ts +0 -465
- package/src/gossipSdk.ts +0 -994
- package/src/index.ts +0 -211
- package/src/services/announcement.ts +0 -653
- package/src/services/auth.ts +0 -95
- package/src/services/discussion.ts +0 -380
- package/src/services/message.ts +0 -1055
- package/src/services/refresh.ts +0 -234
- package/src/types/events.ts +0 -108
- package/src/types.ts +0 -70
- package/src/utils/base64.ts +0 -39
- package/src/utils/contacts.ts +0 -161
- package/src/utils/discussions.ts +0 -55
- package/src/utils/logs.ts +0 -86
- package/src/utils/messageSerialization.ts +0 -257
- package/src/utils/queue.ts +0 -106
- package/src/utils/type.ts +0 -7
- package/src/utils/userId.ts +0 -114
- package/src/utils/validation.ts +0 -144
- package/src/wasm/loader.ts +0 -123
- package/src/wasm/session.ts +0 -276
- package/test/config/protocol.spec.ts +0 -31
- package/test/config/sdk.spec.ts +0 -163
- package/test/db/helpers.spec.ts +0 -142
- package/test/db/operations.spec.ts +0 -128
- package/test/db/states.spec.ts +0 -535
- package/test/integration/discussion-flow.spec.ts +0 -422
- package/test/integration/messaging-flow.spec.ts +0 -708
- package/test/integration/sdk-lifecycle.spec.ts +0 -325
- package/test/mocks/index.ts +0 -9
- package/test/mocks/mockMessageProtocol.ts +0 -100
- package/test/services/auth.spec.ts +0 -311
- package/test/services/discussion.spec.ts +0 -279
- package/test/services/message-deduplication.spec.ts +0 -299
- package/test/services/message-startup.spec.ts +0 -331
- package/test/services/message.spec.ts +0 -817
- package/test/services/refresh.spec.ts +0 -199
- package/test/services/session-status.spec.ts +0 -349
- package/test/session/wasm.spec.ts +0 -227
- package/test/setup.ts +0 -52
- package/test/utils/contacts.spec.ts +0 -156
- package/test/utils/discussions.spec.ts +0 -66
- package/test/utils/queue.spec.ts +0 -52
- package/test/utils/serialization.spec.ts +0 -120
- package/test/utils/userId.spec.ts +0 -120
- package/test/utils/validation.spec.ts +0 -223
- package/test/utils.ts +0 -212
- package/tsconfig.json +0 -26
- package/tsconfig.tsbuildinfo +0 -1
- package/vitest.config.ts +0 -28
package/src/utils/logs.ts
DELETED
|
@@ -1,86 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Logger utility for SDK
|
|
3
|
-
*
|
|
4
|
-
* Provides structured console logging with module and context support.
|
|
5
|
-
* All output goes to the terminal via console methods.
|
|
6
|
-
*/
|
|
7
|
-
|
|
8
|
-
export class Logger {
|
|
9
|
-
private module: string;
|
|
10
|
-
private context: string;
|
|
11
|
-
|
|
12
|
-
constructor(module: string, context: string = '') {
|
|
13
|
-
this.module = module;
|
|
14
|
-
this.context = context;
|
|
15
|
-
}
|
|
16
|
-
|
|
17
|
-
private getSource(): string {
|
|
18
|
-
return this.context ? `${this.module}:${this.context}` : this.module;
|
|
19
|
-
}
|
|
20
|
-
|
|
21
|
-
private formatMainMessage(message: string): string {
|
|
22
|
-
const source = this.getSource();
|
|
23
|
-
return `[${source}] ${message}`;
|
|
24
|
-
}
|
|
25
|
-
|
|
26
|
-
info(message: string, extra?: unknown): void {
|
|
27
|
-
const main = this.formatMainMessage(message);
|
|
28
|
-
if (extra !== undefined) {
|
|
29
|
-
console.log(main, extra);
|
|
30
|
-
} else {
|
|
31
|
-
console.log(main);
|
|
32
|
-
}
|
|
33
|
-
}
|
|
34
|
-
|
|
35
|
-
error(messageOrError: string | Error | unknown, extra?: unknown): void {
|
|
36
|
-
const source = this.getSource();
|
|
37
|
-
|
|
38
|
-
if (messageOrError instanceof Error) {
|
|
39
|
-
const main = `[${source}] ${messageOrError.message}`;
|
|
40
|
-
console.error(main, messageOrError, extra);
|
|
41
|
-
} else {
|
|
42
|
-
const message =
|
|
43
|
-
typeof messageOrError === 'string'
|
|
44
|
-
? messageOrError
|
|
45
|
-
: messageOrError instanceof Error
|
|
46
|
-
? messageOrError.message
|
|
47
|
-
: JSON.stringify(messageOrError);
|
|
48
|
-
const main = `[${source}] ${message}`;
|
|
49
|
-
if (extra !== undefined) {
|
|
50
|
-
console.error(main, extra);
|
|
51
|
-
} else {
|
|
52
|
-
console.error(main);
|
|
53
|
-
}
|
|
54
|
-
}
|
|
55
|
-
}
|
|
56
|
-
|
|
57
|
-
debug(message: string, extra?: unknown): void {
|
|
58
|
-
const main = this.formatMainMessage(message);
|
|
59
|
-
if (extra !== undefined) {
|
|
60
|
-
console.debug(main, extra);
|
|
61
|
-
} else {
|
|
62
|
-
console.debug(main);
|
|
63
|
-
}
|
|
64
|
-
}
|
|
65
|
-
|
|
66
|
-
warn(message: string, extra?: unknown): void {
|
|
67
|
-
const main = this.formatMainMessage(message);
|
|
68
|
-
if (extra !== undefined) {
|
|
69
|
-
console.warn(main, extra);
|
|
70
|
-
} else {
|
|
71
|
-
console.warn(main);
|
|
72
|
-
}
|
|
73
|
-
}
|
|
74
|
-
|
|
75
|
-
// Chainable context builder
|
|
76
|
-
withContext(newContext: string): Logger {
|
|
77
|
-
const fullContext = this.context
|
|
78
|
-
? `${this.context}:${newContext}`
|
|
79
|
-
: newContext;
|
|
80
|
-
return new Logger(this.module, fullContext);
|
|
81
|
-
}
|
|
82
|
-
|
|
83
|
-
forMethod(methodName: string): Logger {
|
|
84
|
-
return this.withContext(methodName);
|
|
85
|
-
}
|
|
86
|
-
}
|
|
@@ -1,257 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Message Serialization Utilities
|
|
3
|
-
*
|
|
4
|
-
* Functions for serializing and deserializing messages for the protocol.
|
|
5
|
-
* Supports regular text messages, replies, forwards, and keep-alive messages.
|
|
6
|
-
*/
|
|
7
|
-
|
|
8
|
-
import { strToBytes, bytesToStr, U32 } from '@massalabs/massa-web3';
|
|
9
|
-
import { MessageType } from '../db';
|
|
10
|
-
|
|
11
|
-
// Message type constants (protocol-level)
|
|
12
|
-
const MESSAGE_TYPE_REGULAR = 0x00;
|
|
13
|
-
const MESSAGE_TYPE_REPLY = 0x01;
|
|
14
|
-
const MESSAGE_TYPE_FORWARD = 0x02;
|
|
15
|
-
export const MESSAGE_TYPE_KEEP_ALIVE = 0x03;
|
|
16
|
-
|
|
17
|
-
// Seeker size: 1 byte length prefix + 32 bytes hash + 1 byte key index
|
|
18
|
-
const SEEKER_SIZE = 34;
|
|
19
|
-
|
|
20
|
-
export interface DeserializedMessage {
|
|
21
|
-
content: string;
|
|
22
|
-
replyTo?: {
|
|
23
|
-
originalContent: string;
|
|
24
|
-
originalSeeker: Uint8Array;
|
|
25
|
-
};
|
|
26
|
-
forwardOf?: {
|
|
27
|
-
originalContent: string;
|
|
28
|
-
originalSeeker: Uint8Array;
|
|
29
|
-
};
|
|
30
|
-
type: MessageType;
|
|
31
|
-
}
|
|
32
|
-
|
|
33
|
-
/**
|
|
34
|
-
* Serialize a keep-alive message
|
|
35
|
-
* Keep-alive messages are used to maintain session activity
|
|
36
|
-
*/
|
|
37
|
-
export function serializeKeepAliveMessage(): Uint8Array {
|
|
38
|
-
return new Uint8Array([MESSAGE_TYPE_KEEP_ALIVE]);
|
|
39
|
-
}
|
|
40
|
-
|
|
41
|
-
/**
|
|
42
|
-
* Serialize a regular text message
|
|
43
|
-
*
|
|
44
|
-
* Format: [type: 1 byte][content: variable]
|
|
45
|
-
*
|
|
46
|
-
* @param content - The message content string
|
|
47
|
-
* @returns Serialized message bytes
|
|
48
|
-
*/
|
|
49
|
-
export function serializeRegularMessage(content: string): Uint8Array {
|
|
50
|
-
const contentBytes = strToBytes(content);
|
|
51
|
-
const result = new Uint8Array(1 + contentBytes.length);
|
|
52
|
-
result[0] = MESSAGE_TYPE_REGULAR;
|
|
53
|
-
result.set(contentBytes, 1);
|
|
54
|
-
return result;
|
|
55
|
-
}
|
|
56
|
-
|
|
57
|
-
/**
|
|
58
|
-
* Serialize a reply message
|
|
59
|
-
*
|
|
60
|
-
* Format: [type: 1 byte][originalContentLen: 4 bytes][originalContent][seeker: 34 bytes][newContent]
|
|
61
|
-
*
|
|
62
|
-
* @param newContent - The reply content
|
|
63
|
-
* @param originalContent - The content being replied to
|
|
64
|
-
* @param originalSeeker - The seeker of the original message
|
|
65
|
-
* @returns Serialized reply message bytes
|
|
66
|
-
*/
|
|
67
|
-
export function serializeReplyMessage(
|
|
68
|
-
newContent: string,
|
|
69
|
-
originalContent: string,
|
|
70
|
-
originalSeeker: Uint8Array
|
|
71
|
-
): Uint8Array {
|
|
72
|
-
const newContentBytes = strToBytes(newContent);
|
|
73
|
-
const originalContentBytes = strToBytes(originalContent);
|
|
74
|
-
const originalContentLenBytes = U32.toBytes(
|
|
75
|
-
BigInt(originalContentBytes.length)
|
|
76
|
-
);
|
|
77
|
-
// Calculate total size
|
|
78
|
-
const totalSize =
|
|
79
|
-
1 + // type
|
|
80
|
-
originalContentLenBytes.length + // length prefix (4 bytes)
|
|
81
|
-
originalContentBytes.length + // original content
|
|
82
|
-
SEEKER_SIZE + // seeker
|
|
83
|
-
newContentBytes.length; // new content
|
|
84
|
-
|
|
85
|
-
const result = new Uint8Array(totalSize);
|
|
86
|
-
let offset = 0;
|
|
87
|
-
|
|
88
|
-
// Type byte
|
|
89
|
-
result[offset++] = MESSAGE_TYPE_REPLY;
|
|
90
|
-
|
|
91
|
-
// Original content length (4 bytes)
|
|
92
|
-
result.set(originalContentLenBytes, offset);
|
|
93
|
-
offset += originalContentLenBytes.length;
|
|
94
|
-
|
|
95
|
-
// Original content
|
|
96
|
-
result.set(originalContentBytes, offset);
|
|
97
|
-
offset += originalContentBytes.length;
|
|
98
|
-
|
|
99
|
-
// Seeker (34 bytes)
|
|
100
|
-
result.set(originalSeeker, offset);
|
|
101
|
-
offset += SEEKER_SIZE;
|
|
102
|
-
|
|
103
|
-
// New content
|
|
104
|
-
result.set(newContentBytes, offset);
|
|
105
|
-
|
|
106
|
-
return result;
|
|
107
|
-
}
|
|
108
|
-
|
|
109
|
-
/**
|
|
110
|
-
* Serialize a forward message
|
|
111
|
-
*
|
|
112
|
-
* Format: [type: 1 byte][forwardContentLen: 4 bytes][forwardContent][seeker: 34 bytes][newContent]
|
|
113
|
-
*
|
|
114
|
-
* @param forwardContent - The content being forwarded
|
|
115
|
-
* @param newContent - Optional new content to add (empty string if none)
|
|
116
|
-
* @param originalSeeker - The seeker of the original message
|
|
117
|
-
* @returns Serialized forward message bytes
|
|
118
|
-
*/
|
|
119
|
-
export function serializeForwardMessage(
|
|
120
|
-
forwardContent: string,
|
|
121
|
-
newContent: string,
|
|
122
|
-
originalSeeker: Uint8Array
|
|
123
|
-
): Uint8Array {
|
|
124
|
-
const newContentBytes = strToBytes(newContent);
|
|
125
|
-
const forwardContentBytes = strToBytes(forwardContent);
|
|
126
|
-
const forwardContentLenBytes = U32.toBytes(
|
|
127
|
-
BigInt(forwardContentBytes.length)
|
|
128
|
-
);
|
|
129
|
-
// Calculate total size
|
|
130
|
-
const totalSize =
|
|
131
|
-
1 + // type
|
|
132
|
-
forwardContentLenBytes.length + // length prefix (4 bytes)
|
|
133
|
-
forwardContentBytes.length + // forward content
|
|
134
|
-
SEEKER_SIZE + // seeker
|
|
135
|
-
newContentBytes.length; // new content
|
|
136
|
-
|
|
137
|
-
const result = new Uint8Array(totalSize);
|
|
138
|
-
let offset = 0;
|
|
139
|
-
|
|
140
|
-
// Type byte
|
|
141
|
-
result[offset++] = MESSAGE_TYPE_FORWARD;
|
|
142
|
-
|
|
143
|
-
// Forward content length (4 bytes)
|
|
144
|
-
result.set(forwardContentLenBytes, offset);
|
|
145
|
-
offset += forwardContentLenBytes.length;
|
|
146
|
-
|
|
147
|
-
// Forward content
|
|
148
|
-
result.set(forwardContentBytes, offset);
|
|
149
|
-
offset += forwardContentBytes.length;
|
|
150
|
-
|
|
151
|
-
// Seeker (34 bytes)
|
|
152
|
-
result.set(originalSeeker, offset);
|
|
153
|
-
offset += SEEKER_SIZE;
|
|
154
|
-
|
|
155
|
-
// New content
|
|
156
|
-
result.set(newContentBytes, offset);
|
|
157
|
-
|
|
158
|
-
return result;
|
|
159
|
-
}
|
|
160
|
-
|
|
161
|
-
/**
|
|
162
|
-
* Deserialize a message from bytes
|
|
163
|
-
*
|
|
164
|
-
* @param buffer - The serialized message bytes
|
|
165
|
-
* @returns Deserialized message object
|
|
166
|
-
* @throws Error if message format is invalid
|
|
167
|
-
*/
|
|
168
|
-
export function deserializeMessage(buffer: Uint8Array): DeserializedMessage {
|
|
169
|
-
if (buffer.length < 1) {
|
|
170
|
-
throw new Error('Empty message buffer');
|
|
171
|
-
}
|
|
172
|
-
|
|
173
|
-
const messageType = buffer[0];
|
|
174
|
-
|
|
175
|
-
switch (messageType) {
|
|
176
|
-
case MESSAGE_TYPE_KEEP_ALIVE:
|
|
177
|
-
return {
|
|
178
|
-
content: '',
|
|
179
|
-
type: MessageType.KEEP_ALIVE,
|
|
180
|
-
};
|
|
181
|
-
|
|
182
|
-
case MESSAGE_TYPE_REGULAR:
|
|
183
|
-
return {
|
|
184
|
-
content: bytesToStr(buffer.slice(1)),
|
|
185
|
-
type: MessageType.TEXT,
|
|
186
|
-
};
|
|
187
|
-
|
|
188
|
-
case MESSAGE_TYPE_REPLY: {
|
|
189
|
-
// Format: [type: 1][originalContentLen: 4][originalContent][seeker: 34][newContent]
|
|
190
|
-
let offset = 1;
|
|
191
|
-
|
|
192
|
-
// Read original content length (4 bytes)
|
|
193
|
-
const originalContentLen = Number(
|
|
194
|
-
U32.fromBytes(buffer.slice(offset, offset + 4))
|
|
195
|
-
);
|
|
196
|
-
offset += 4;
|
|
197
|
-
|
|
198
|
-
// Read original content
|
|
199
|
-
const originalContent = bytesToStr(
|
|
200
|
-
buffer.slice(offset, offset + originalContentLen)
|
|
201
|
-
);
|
|
202
|
-
offset += originalContentLen;
|
|
203
|
-
|
|
204
|
-
// Read seeker (34 bytes)
|
|
205
|
-
const originalSeeker = buffer.slice(offset, offset + SEEKER_SIZE);
|
|
206
|
-
offset += SEEKER_SIZE;
|
|
207
|
-
|
|
208
|
-
// Read new content (rest of buffer)
|
|
209
|
-
const content = bytesToStr(buffer.slice(offset));
|
|
210
|
-
|
|
211
|
-
return {
|
|
212
|
-
content,
|
|
213
|
-
replyTo: {
|
|
214
|
-
originalContent,
|
|
215
|
-
originalSeeker,
|
|
216
|
-
},
|
|
217
|
-
type: MessageType.TEXT,
|
|
218
|
-
};
|
|
219
|
-
}
|
|
220
|
-
|
|
221
|
-
case MESSAGE_TYPE_FORWARD: {
|
|
222
|
-
// Format: [type: 1][forwardContentLen: 4][forwardContent][seeker: 34][newContent]
|
|
223
|
-
let offset = 1;
|
|
224
|
-
|
|
225
|
-
// Read forward content length (4 bytes)
|
|
226
|
-
const forwardContentLen = Number(
|
|
227
|
-
U32.fromBytes(buffer.slice(offset, offset + 4))
|
|
228
|
-
);
|
|
229
|
-
offset += 4;
|
|
230
|
-
|
|
231
|
-
// Read forward content
|
|
232
|
-
const originalContent = bytesToStr(
|
|
233
|
-
buffer.slice(offset, offset + forwardContentLen)
|
|
234
|
-
);
|
|
235
|
-
offset += forwardContentLen;
|
|
236
|
-
|
|
237
|
-
// Read seeker (34 bytes)
|
|
238
|
-
const originalSeeker = buffer.slice(offset, offset + SEEKER_SIZE);
|
|
239
|
-
offset += SEEKER_SIZE;
|
|
240
|
-
|
|
241
|
-
// Read new content (rest of buffer)
|
|
242
|
-
const content = bytesToStr(buffer.slice(offset));
|
|
243
|
-
|
|
244
|
-
return {
|
|
245
|
-
content,
|
|
246
|
-
forwardOf: {
|
|
247
|
-
originalContent,
|
|
248
|
-
originalSeeker,
|
|
249
|
-
},
|
|
250
|
-
type: MessageType.TEXT,
|
|
251
|
-
};
|
|
252
|
-
}
|
|
253
|
-
|
|
254
|
-
default:
|
|
255
|
-
throw new Error(`Unknown message type: ${messageType}`);
|
|
256
|
-
}
|
|
257
|
-
}
|
package/src/utils/queue.ts
DELETED
|
@@ -1,106 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Promise Queue
|
|
3
|
-
*
|
|
4
|
-
* Ensures async operations are executed sequentially.
|
|
5
|
-
* Used to serialize session manager operations per contact.
|
|
6
|
-
*/
|
|
7
|
-
|
|
8
|
-
type QueuedTask<T> = {
|
|
9
|
-
fn: () => Promise<T>;
|
|
10
|
-
resolve: (value: T) => void;
|
|
11
|
-
reject: (error: unknown) => void;
|
|
12
|
-
};
|
|
13
|
-
|
|
14
|
-
/**
|
|
15
|
-
* A simple promise queue that executes tasks sequentially.
|
|
16
|
-
* Tasks are processed in FIFO order.
|
|
17
|
-
*/
|
|
18
|
-
export class PromiseQueue {
|
|
19
|
-
private queue: QueuedTask<unknown>[] = [];
|
|
20
|
-
private processing = false;
|
|
21
|
-
|
|
22
|
-
/**
|
|
23
|
-
* Add a task to the queue. Returns a promise that resolves
|
|
24
|
-
* when the task completes.
|
|
25
|
-
*/
|
|
26
|
-
enqueue<T>(fn: () => Promise<T>): Promise<T> {
|
|
27
|
-
return new Promise<T>((resolve, reject) => {
|
|
28
|
-
this.queue.push({
|
|
29
|
-
fn: fn as () => Promise<unknown>,
|
|
30
|
-
resolve: resolve as (value: unknown) => void,
|
|
31
|
-
reject,
|
|
32
|
-
});
|
|
33
|
-
this.processNext();
|
|
34
|
-
});
|
|
35
|
-
}
|
|
36
|
-
|
|
37
|
-
/**
|
|
38
|
-
* Process the next task in the queue.
|
|
39
|
-
*/
|
|
40
|
-
private async processNext(): Promise<void> {
|
|
41
|
-
if (this.processing || this.queue.length === 0) {
|
|
42
|
-
return;
|
|
43
|
-
}
|
|
44
|
-
|
|
45
|
-
this.processing = true;
|
|
46
|
-
const task = this.queue.shift()!;
|
|
47
|
-
|
|
48
|
-
try {
|
|
49
|
-
const result = await task.fn();
|
|
50
|
-
task.resolve(result);
|
|
51
|
-
} catch (error) {
|
|
52
|
-
task.reject(error);
|
|
53
|
-
} finally {
|
|
54
|
-
this.processing = false;
|
|
55
|
-
this.processNext();
|
|
56
|
-
}
|
|
57
|
-
}
|
|
58
|
-
|
|
59
|
-
/**
|
|
60
|
-
* Check if the queue is currently processing.
|
|
61
|
-
*/
|
|
62
|
-
get isProcessing(): boolean {
|
|
63
|
-
return this.processing;
|
|
64
|
-
}
|
|
65
|
-
|
|
66
|
-
/**
|
|
67
|
-
* Get the number of pending tasks.
|
|
68
|
-
*/
|
|
69
|
-
get pendingCount(): number {
|
|
70
|
-
return this.queue.length;
|
|
71
|
-
}
|
|
72
|
-
}
|
|
73
|
-
|
|
74
|
-
/**
|
|
75
|
-
* Manages multiple queues keyed by string (e.g., contact ID).
|
|
76
|
-
* Creates queues lazily on first use.
|
|
77
|
-
*/
|
|
78
|
-
export class QueueManager {
|
|
79
|
-
private queues = new Map<string, PromiseQueue>();
|
|
80
|
-
|
|
81
|
-
/**
|
|
82
|
-
* Get or create a queue for the given key.
|
|
83
|
-
*/
|
|
84
|
-
getQueue(key: string): PromiseQueue {
|
|
85
|
-
let queue = this.queues.get(key);
|
|
86
|
-
if (!queue) {
|
|
87
|
-
queue = new PromiseQueue();
|
|
88
|
-
this.queues.set(key, queue);
|
|
89
|
-
}
|
|
90
|
-
return queue;
|
|
91
|
-
}
|
|
92
|
-
|
|
93
|
-
/**
|
|
94
|
-
* Enqueue a task for a specific key.
|
|
95
|
-
*/
|
|
96
|
-
enqueue<T>(key: string, fn: () => Promise<T>): Promise<T> {
|
|
97
|
-
return this.getQueue(key).enqueue(fn);
|
|
98
|
-
}
|
|
99
|
-
|
|
100
|
-
/**
|
|
101
|
-
* Clear all queues.
|
|
102
|
-
*/
|
|
103
|
-
clear(): void {
|
|
104
|
-
this.queues.clear();
|
|
105
|
-
}
|
|
106
|
-
}
|
package/src/utils/type.ts
DELETED
package/src/utils/userId.ts
DELETED
|
@@ -1,114 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* User ID encoding/decoding utilities using Bech32 format
|
|
3
|
-
* Format: gossip1<encoded-32-bytes>
|
|
4
|
-
*
|
|
5
|
-
* Uses @scure/base for reliable, battle-tested Bech32 encoding
|
|
6
|
-
*/
|
|
7
|
-
|
|
8
|
-
import { bech32 } from '@scure/base';
|
|
9
|
-
import { generateUserKeys } from '../wasm';
|
|
10
|
-
|
|
11
|
-
const GOSSIP_PREFIX = 'gossip';
|
|
12
|
-
const USER_ID_BYTE_LENGTH = 32;
|
|
13
|
-
|
|
14
|
-
/**
|
|
15
|
-
* Encode a 32-byte user ID to Bech32 format with "gossip" prefix
|
|
16
|
-
* @param userId - 32-byte user ID as Uint8Array
|
|
17
|
-
* @returns Bech32-encoded string (e.g., "gossip1qpzry9x8gf2tvdw0s3jn54khce6mua7l...")
|
|
18
|
-
* @throws Error if userId is not exactly 32 bytes
|
|
19
|
-
*/
|
|
20
|
-
export function encodeUserId(userId: Uint8Array): string {
|
|
21
|
-
if (userId.length !== USER_ID_BYTE_LENGTH) {
|
|
22
|
-
throw new Error(
|
|
23
|
-
`User ID must be exactly ${USER_ID_BYTE_LENGTH} bytes, got ${userId.length}`
|
|
24
|
-
);
|
|
25
|
-
}
|
|
26
|
-
|
|
27
|
-
return bech32.encode(GOSSIP_PREFIX, bech32.toWords(userId));
|
|
28
|
-
}
|
|
29
|
-
|
|
30
|
-
/**
|
|
31
|
-
* Decode a Bech32-encoded user ID back to 32 bytes
|
|
32
|
-
* @param encoded - Bech32-encoded string (e.g., "gossip1qpzry9x8gf2tvdw0s3jn54khce6mua7l...")
|
|
33
|
-
* @returns 32-byte user ID as Uint8Array
|
|
34
|
-
* @throws Error if the format is invalid, checksum fails, or decoded length is not 32 bytes
|
|
35
|
-
*/
|
|
36
|
-
export function decodeUserId(encoded: string): Uint8Array {
|
|
37
|
-
// Type assertion needed as bech32.decode expects a template literal type
|
|
38
|
-
const { prefix, words } = bech32.decode(encoded as `${string}1${string}`, 90);
|
|
39
|
-
|
|
40
|
-
// Verify prefix
|
|
41
|
-
if (prefix !== GOSSIP_PREFIX) {
|
|
42
|
-
throw new Error(
|
|
43
|
-
`Invalid prefix: expected "${GOSSIP_PREFIX}", got "${prefix}"`
|
|
44
|
-
);
|
|
45
|
-
}
|
|
46
|
-
|
|
47
|
-
// Convert from 5-bit words back to bytes
|
|
48
|
-
const decoded = bech32.fromWords(words);
|
|
49
|
-
|
|
50
|
-
// Verify length
|
|
51
|
-
if (decoded.length !== USER_ID_BYTE_LENGTH) {
|
|
52
|
-
throw new Error(
|
|
53
|
-
`Decoded user ID must be ${USER_ID_BYTE_LENGTH} bytes, got ${decoded.length}`
|
|
54
|
-
);
|
|
55
|
-
}
|
|
56
|
-
|
|
57
|
-
return new Uint8Array(decoded);
|
|
58
|
-
}
|
|
59
|
-
|
|
60
|
-
/**
|
|
61
|
-
* Validate a Bech32-encoded user ID string
|
|
62
|
-
* @param encoded - Bech32-encoded string to validate
|
|
63
|
-
* @returns true if valid, false otherwise
|
|
64
|
-
*/
|
|
65
|
-
export function isValidUserId(encoded: string): boolean {
|
|
66
|
-
try {
|
|
67
|
-
decodeUserId(encoded);
|
|
68
|
-
return true;
|
|
69
|
-
} catch {
|
|
70
|
-
return false;
|
|
71
|
-
}
|
|
72
|
-
}
|
|
73
|
-
|
|
74
|
-
/**
|
|
75
|
-
* Format a user ID for display (shortened version)
|
|
76
|
-
* @param userId - Bech32-encoded user ID string
|
|
77
|
-
* @param prefixChars - Number of characters to show after prefix (default: 8)
|
|
78
|
-
* @param suffixChars - Number of characters to show at end (default: 6)
|
|
79
|
-
* @returns Formatted string (e.g., "gossip1qpzry9x8...mua7l")
|
|
80
|
-
*/
|
|
81
|
-
export function formatUserId(
|
|
82
|
-
userId: string,
|
|
83
|
-
prefixChars: number = 8,
|
|
84
|
-
suffixChars: number = 6
|
|
85
|
-
): string {
|
|
86
|
-
if (!userId) return '';
|
|
87
|
-
|
|
88
|
-
// Find separator position
|
|
89
|
-
const sepPos = userId.indexOf('1');
|
|
90
|
-
if (sepPos === -1) return userId;
|
|
91
|
-
|
|
92
|
-
const prefix = userId.substring(0, sepPos + 1); // "gossip1"
|
|
93
|
-
const data = userId.substring(sepPos + 1); // rest of the string
|
|
94
|
-
|
|
95
|
-
if (data.length <= prefixChars + suffixChars) {
|
|
96
|
-
return userId; // Too short to format
|
|
97
|
-
}
|
|
98
|
-
|
|
99
|
-
const start = data.slice(0, prefixChars);
|
|
100
|
-
const end = data.slice(-suffixChars);
|
|
101
|
-
|
|
102
|
-
return `${prefix}${start}...${end}`;
|
|
103
|
-
}
|
|
104
|
-
|
|
105
|
-
/**
|
|
106
|
-
* Generates a random 32-byte user ID
|
|
107
|
-
* @param password - Optional password
|
|
108
|
-
* @returns gossip Bech32 string representing a 32-byte user ID
|
|
109
|
-
*/
|
|
110
|
-
export async function generate(password?: string): Promise<string> {
|
|
111
|
-
const identity = await generateUserKeys(password || '');
|
|
112
|
-
const userId = identity.public_keys().derive_id();
|
|
113
|
-
return encodeUserId(userId);
|
|
114
|
-
}
|
package/src/utils/validation.ts
DELETED
|
@@ -1,144 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Validation Utilities
|
|
3
|
-
*
|
|
4
|
-
* Functions for validating user input like usernames, passwords, and user IDs.
|
|
5
|
-
*/
|
|
6
|
-
|
|
7
|
-
import { type UserProfile, type GossipDatabase } from '../db';
|
|
8
|
-
import { isValidUserId } from './userId';
|
|
9
|
-
|
|
10
|
-
export type ValidationResult =
|
|
11
|
-
| { valid: true; error?: never }
|
|
12
|
-
| { valid: false; error: string };
|
|
13
|
-
|
|
14
|
-
/**
|
|
15
|
-
* Validate a password meets requirements
|
|
16
|
-
*
|
|
17
|
-
* @param value - The password to validate
|
|
18
|
-
* @returns Validation result
|
|
19
|
-
*/
|
|
20
|
-
export function validatePassword(value: string): ValidationResult {
|
|
21
|
-
if (!value || value.trim().length === 0) {
|
|
22
|
-
return { valid: false, error: 'Password is required' };
|
|
23
|
-
}
|
|
24
|
-
|
|
25
|
-
if (value.length < 8) {
|
|
26
|
-
return {
|
|
27
|
-
valid: false,
|
|
28
|
-
error: 'Password must be at least 8 characters long',
|
|
29
|
-
};
|
|
30
|
-
}
|
|
31
|
-
|
|
32
|
-
return { valid: true };
|
|
33
|
-
}
|
|
34
|
-
|
|
35
|
-
/**
|
|
36
|
-
* Validate a username format (without checking availability)
|
|
37
|
-
*
|
|
38
|
-
* @param value - The username to validate
|
|
39
|
-
* @returns Validation result
|
|
40
|
-
*/
|
|
41
|
-
export function validateUsernameFormat(value: string): ValidationResult {
|
|
42
|
-
const trimmed = value.trim();
|
|
43
|
-
|
|
44
|
-
if (!trimmed) {
|
|
45
|
-
return { valid: false, error: 'Username is required' };
|
|
46
|
-
}
|
|
47
|
-
|
|
48
|
-
// Disallow any whitespace inside the username (single token only)
|
|
49
|
-
if (/\s/.test(trimmed)) {
|
|
50
|
-
return {
|
|
51
|
-
valid: false,
|
|
52
|
-
error: 'Username cannot contain spaces',
|
|
53
|
-
};
|
|
54
|
-
}
|
|
55
|
-
|
|
56
|
-
if (trimmed.length < 3) {
|
|
57
|
-
return {
|
|
58
|
-
valid: false,
|
|
59
|
-
error: 'Username must be at least 3 characters long',
|
|
60
|
-
};
|
|
61
|
-
}
|
|
62
|
-
|
|
63
|
-
return { valid: true };
|
|
64
|
-
}
|
|
65
|
-
|
|
66
|
-
/**
|
|
67
|
-
* Validate a username is available (not already in use)
|
|
68
|
-
*
|
|
69
|
-
* @param value - The username to check
|
|
70
|
-
* @param db - Database instance
|
|
71
|
-
* @returns Validation result
|
|
72
|
-
*/
|
|
73
|
-
export async function validateUsernameAvailability(
|
|
74
|
-
value: string,
|
|
75
|
-
db: GossipDatabase
|
|
76
|
-
): Promise<ValidationResult> {
|
|
77
|
-
try {
|
|
78
|
-
if (!db.isOpen()) {
|
|
79
|
-
await db.open();
|
|
80
|
-
}
|
|
81
|
-
|
|
82
|
-
const existingProfile = await db.userProfile
|
|
83
|
-
.filter(
|
|
84
|
-
(profile: UserProfile) =>
|
|
85
|
-
profile.username.trim().toLowerCase() === value.trim().toLowerCase()
|
|
86
|
-
)
|
|
87
|
-
.first();
|
|
88
|
-
|
|
89
|
-
if (existingProfile) {
|
|
90
|
-
return {
|
|
91
|
-
valid: false,
|
|
92
|
-
error: 'This username is already in use. Please choose another.',
|
|
93
|
-
};
|
|
94
|
-
}
|
|
95
|
-
|
|
96
|
-
return { valid: true };
|
|
97
|
-
} catch (error) {
|
|
98
|
-
return {
|
|
99
|
-
valid: false,
|
|
100
|
-
error:
|
|
101
|
-
error instanceof Error
|
|
102
|
-
? error.message
|
|
103
|
-
: 'Unable to verify username availability. Please try again.',
|
|
104
|
-
};
|
|
105
|
-
}
|
|
106
|
-
}
|
|
107
|
-
|
|
108
|
-
/**
|
|
109
|
-
* Validate a username format and availability
|
|
110
|
-
*
|
|
111
|
-
* @param value - The username to validate
|
|
112
|
-
* @param db - Database instance
|
|
113
|
-
* @returns Validation result
|
|
114
|
-
*/
|
|
115
|
-
export async function validateUsernameFormatAndAvailability(
|
|
116
|
-
value: string,
|
|
117
|
-
db: GossipDatabase
|
|
118
|
-
): Promise<ValidationResult> {
|
|
119
|
-
const result = validateUsernameFormat(value);
|
|
120
|
-
if (!result.valid) {
|
|
121
|
-
return result;
|
|
122
|
-
}
|
|
123
|
-
|
|
124
|
-
return await validateUsernameAvailability(value, db);
|
|
125
|
-
}
|
|
126
|
-
|
|
127
|
-
/**
|
|
128
|
-
* Validate a user ID format (Bech32 gossip1... format)
|
|
129
|
-
*
|
|
130
|
-
* @param value - The user ID to validate
|
|
131
|
-
* @returns Validation result
|
|
132
|
-
*/
|
|
133
|
-
export function validateUserIdFormat(value: string): ValidationResult {
|
|
134
|
-
const userId = value.trim();
|
|
135
|
-
|
|
136
|
-
if (!isValidUserId(userId)) {
|
|
137
|
-
return {
|
|
138
|
-
valid: false,
|
|
139
|
-
error: 'Invalid format — must be a valid user ID',
|
|
140
|
-
};
|
|
141
|
-
}
|
|
142
|
-
|
|
143
|
-
return { valid: true };
|
|
144
|
-
}
|