@massalabs/gossip-sdk 0.0.2-dev.20260128094509 → 0.0.2-dev.20260128160824
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 +638 -0
- package/dist/assets/generated/wasm/gossip_wasm.js +1557 -0
- package/dist/assets/generated/wasm/gossip_wasm_bg.wasm +0 -0
- package/dist/assets/generated/wasm/gossip_wasm_bg.wasm.d.ts +164 -0
- package/dist/assets/generated/wasm/package.json +15 -0
- package/dist/assets/generated/wasm-node/README.md +281 -0
- package/dist/assets/generated/wasm-node/gossip_wasm.d.ts +443 -0
- package/dist/assets/generated/wasm-node/gossip_wasm.js +1488 -0
- package/dist/assets/generated/wasm-node/gossip_wasm_bg.wasm +0 -0
- package/dist/assets/generated/wasm-node/gossip_wasm_bg.wasm.d.ts +164 -0
- package/dist/assets/generated/wasm-node/package.json +11 -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} +11 -95
- 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 +59 -0
- package/dist/index.js +61 -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 +22 -0
- package/dist/wasm/loader.js +78 -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 +15 -2
- 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
|
@@ -0,0 +1,19 @@
|
|
|
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
|
+
export declare class Logger {
|
|
8
|
+
private module;
|
|
9
|
+
private context;
|
|
10
|
+
constructor(module: string, context?: string);
|
|
11
|
+
private getSource;
|
|
12
|
+
private formatMainMessage;
|
|
13
|
+
info(message: string, extra?: unknown): void;
|
|
14
|
+
error(messageOrError: string | Error | unknown, extra?: unknown): void;
|
|
15
|
+
debug(message: string, extra?: unknown): void;
|
|
16
|
+
warn(message: string, extra?: unknown): void;
|
|
17
|
+
withContext(newContext: string): Logger;
|
|
18
|
+
forMethod(methodName: string): Logger;
|
|
19
|
+
}
|
|
@@ -0,0 +1,89 @@
|
|
|
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
|
+
export class Logger {
|
|
8
|
+
constructor(module, context = '') {
|
|
9
|
+
Object.defineProperty(this, "module", {
|
|
10
|
+
enumerable: true,
|
|
11
|
+
configurable: true,
|
|
12
|
+
writable: true,
|
|
13
|
+
value: void 0
|
|
14
|
+
});
|
|
15
|
+
Object.defineProperty(this, "context", {
|
|
16
|
+
enumerable: true,
|
|
17
|
+
configurable: true,
|
|
18
|
+
writable: true,
|
|
19
|
+
value: void 0
|
|
20
|
+
});
|
|
21
|
+
this.module = module;
|
|
22
|
+
this.context = context;
|
|
23
|
+
}
|
|
24
|
+
getSource() {
|
|
25
|
+
return this.context ? `${this.module}:${this.context}` : this.module;
|
|
26
|
+
}
|
|
27
|
+
formatMainMessage(message) {
|
|
28
|
+
const source = this.getSource();
|
|
29
|
+
return `[${source}] ${message}`;
|
|
30
|
+
}
|
|
31
|
+
info(message, extra) {
|
|
32
|
+
const main = this.formatMainMessage(message);
|
|
33
|
+
if (extra !== undefined) {
|
|
34
|
+
console.log(main, extra);
|
|
35
|
+
}
|
|
36
|
+
else {
|
|
37
|
+
console.log(main);
|
|
38
|
+
}
|
|
39
|
+
}
|
|
40
|
+
error(messageOrError, extra) {
|
|
41
|
+
const source = this.getSource();
|
|
42
|
+
if (messageOrError instanceof Error) {
|
|
43
|
+
const main = `[${source}] ${messageOrError.message}`;
|
|
44
|
+
console.error(main, messageOrError, extra);
|
|
45
|
+
}
|
|
46
|
+
else {
|
|
47
|
+
const message = typeof messageOrError === 'string'
|
|
48
|
+
? messageOrError
|
|
49
|
+
: messageOrError instanceof Error
|
|
50
|
+
? messageOrError.message
|
|
51
|
+
: JSON.stringify(messageOrError);
|
|
52
|
+
const main = `[${source}] ${message}`;
|
|
53
|
+
if (extra !== undefined) {
|
|
54
|
+
console.error(main, extra);
|
|
55
|
+
}
|
|
56
|
+
else {
|
|
57
|
+
console.error(main);
|
|
58
|
+
}
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
debug(message, extra) {
|
|
62
|
+
const main = this.formatMainMessage(message);
|
|
63
|
+
if (extra !== undefined) {
|
|
64
|
+
console.debug(main, extra);
|
|
65
|
+
}
|
|
66
|
+
else {
|
|
67
|
+
console.debug(main);
|
|
68
|
+
}
|
|
69
|
+
}
|
|
70
|
+
warn(message, extra) {
|
|
71
|
+
const main = this.formatMainMessage(message);
|
|
72
|
+
if (extra !== undefined) {
|
|
73
|
+
console.warn(main, extra);
|
|
74
|
+
}
|
|
75
|
+
else {
|
|
76
|
+
console.warn(main);
|
|
77
|
+
}
|
|
78
|
+
}
|
|
79
|
+
// Chainable context builder
|
|
80
|
+
withContext(newContext) {
|
|
81
|
+
const fullContext = this.context
|
|
82
|
+
? `${this.context}:${newContext}`
|
|
83
|
+
: newContext;
|
|
84
|
+
return new Logger(this.module, fullContext);
|
|
85
|
+
}
|
|
86
|
+
forMethod(methodName) {
|
|
87
|
+
return this.withContext(methodName);
|
|
88
|
+
}
|
|
89
|
+
}
|
|
@@ -0,0 +1,64 @@
|
|
|
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
|
+
import { MessageType } from '../db';
|
|
8
|
+
export declare const MESSAGE_TYPE_KEEP_ALIVE = 3;
|
|
9
|
+
export interface DeserializedMessage {
|
|
10
|
+
content: string;
|
|
11
|
+
replyTo?: {
|
|
12
|
+
originalContent: string;
|
|
13
|
+
originalSeeker: Uint8Array;
|
|
14
|
+
};
|
|
15
|
+
forwardOf?: {
|
|
16
|
+
originalContent: string;
|
|
17
|
+
originalSeeker: Uint8Array;
|
|
18
|
+
};
|
|
19
|
+
type: MessageType;
|
|
20
|
+
}
|
|
21
|
+
/**
|
|
22
|
+
* Serialize a keep-alive message
|
|
23
|
+
* Keep-alive messages are used to maintain session activity
|
|
24
|
+
*/
|
|
25
|
+
export declare function serializeKeepAliveMessage(): Uint8Array;
|
|
26
|
+
/**
|
|
27
|
+
* Serialize a regular text message
|
|
28
|
+
*
|
|
29
|
+
* Format: [type: 1 byte][content: variable]
|
|
30
|
+
*
|
|
31
|
+
* @param content - The message content string
|
|
32
|
+
* @returns Serialized message bytes
|
|
33
|
+
*/
|
|
34
|
+
export declare function serializeRegularMessage(content: string): Uint8Array;
|
|
35
|
+
/**
|
|
36
|
+
* Serialize a reply message
|
|
37
|
+
*
|
|
38
|
+
* Format: [type: 1 byte][originalContentLen: 4 bytes][originalContent][seeker: 34 bytes][newContent]
|
|
39
|
+
*
|
|
40
|
+
* @param newContent - The reply content
|
|
41
|
+
* @param originalContent - The content being replied to
|
|
42
|
+
* @param originalSeeker - The seeker of the original message
|
|
43
|
+
* @returns Serialized reply message bytes
|
|
44
|
+
*/
|
|
45
|
+
export declare function serializeReplyMessage(newContent: string, originalContent: string, originalSeeker: Uint8Array): Uint8Array;
|
|
46
|
+
/**
|
|
47
|
+
* Serialize a forward message
|
|
48
|
+
*
|
|
49
|
+
* Format: [type: 1 byte][forwardContentLen: 4 bytes][forwardContent][seeker: 34 bytes][newContent]
|
|
50
|
+
*
|
|
51
|
+
* @param forwardContent - The content being forwarded
|
|
52
|
+
* @param newContent - Optional new content to add (empty string if none)
|
|
53
|
+
* @param originalSeeker - The seeker of the original message
|
|
54
|
+
* @returns Serialized forward message bytes
|
|
55
|
+
*/
|
|
56
|
+
export declare function serializeForwardMessage(forwardContent: string, newContent: string, originalSeeker: Uint8Array): Uint8Array;
|
|
57
|
+
/**
|
|
58
|
+
* Deserialize a message from bytes
|
|
59
|
+
*
|
|
60
|
+
* @param buffer - The serialized message bytes
|
|
61
|
+
* @returns Deserialized message object
|
|
62
|
+
* @throws Error if message format is invalid
|
|
63
|
+
*/
|
|
64
|
+
export declare function deserializeMessage(buffer: Uint8Array): DeserializedMessage;
|
|
@@ -0,0 +1,184 @@
|
|
|
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
|
+
import { strToBytes, bytesToStr, U32 } from '@massalabs/massa-web3';
|
|
8
|
+
import { MessageType } from '../db';
|
|
9
|
+
// Message type constants (protocol-level)
|
|
10
|
+
const MESSAGE_TYPE_REGULAR = 0x00;
|
|
11
|
+
const MESSAGE_TYPE_REPLY = 0x01;
|
|
12
|
+
const MESSAGE_TYPE_FORWARD = 0x02;
|
|
13
|
+
export const MESSAGE_TYPE_KEEP_ALIVE = 0x03;
|
|
14
|
+
// Seeker size: 1 byte length prefix + 32 bytes hash + 1 byte key index
|
|
15
|
+
const SEEKER_SIZE = 34;
|
|
16
|
+
/**
|
|
17
|
+
* Serialize a keep-alive message
|
|
18
|
+
* Keep-alive messages are used to maintain session activity
|
|
19
|
+
*/
|
|
20
|
+
export function serializeKeepAliveMessage() {
|
|
21
|
+
return new Uint8Array([MESSAGE_TYPE_KEEP_ALIVE]);
|
|
22
|
+
}
|
|
23
|
+
/**
|
|
24
|
+
* Serialize a regular text message
|
|
25
|
+
*
|
|
26
|
+
* Format: [type: 1 byte][content: variable]
|
|
27
|
+
*
|
|
28
|
+
* @param content - The message content string
|
|
29
|
+
* @returns Serialized message bytes
|
|
30
|
+
*/
|
|
31
|
+
export function serializeRegularMessage(content) {
|
|
32
|
+
const contentBytes = strToBytes(content);
|
|
33
|
+
const result = new Uint8Array(1 + contentBytes.length);
|
|
34
|
+
result[0] = MESSAGE_TYPE_REGULAR;
|
|
35
|
+
result.set(contentBytes, 1);
|
|
36
|
+
return result;
|
|
37
|
+
}
|
|
38
|
+
/**
|
|
39
|
+
* Serialize a reply message
|
|
40
|
+
*
|
|
41
|
+
* Format: [type: 1 byte][originalContentLen: 4 bytes][originalContent][seeker: 34 bytes][newContent]
|
|
42
|
+
*
|
|
43
|
+
* @param newContent - The reply content
|
|
44
|
+
* @param originalContent - The content being replied to
|
|
45
|
+
* @param originalSeeker - The seeker of the original message
|
|
46
|
+
* @returns Serialized reply message bytes
|
|
47
|
+
*/
|
|
48
|
+
export function serializeReplyMessage(newContent, originalContent, originalSeeker) {
|
|
49
|
+
const newContentBytes = strToBytes(newContent);
|
|
50
|
+
const originalContentBytes = strToBytes(originalContent);
|
|
51
|
+
const originalContentLenBytes = U32.toBytes(BigInt(originalContentBytes.length));
|
|
52
|
+
// Calculate total size
|
|
53
|
+
const totalSize = 1 + // type
|
|
54
|
+
originalContentLenBytes.length + // length prefix (4 bytes)
|
|
55
|
+
originalContentBytes.length + // original content
|
|
56
|
+
SEEKER_SIZE + // seeker
|
|
57
|
+
newContentBytes.length; // new content
|
|
58
|
+
const result = new Uint8Array(totalSize);
|
|
59
|
+
let offset = 0;
|
|
60
|
+
// Type byte
|
|
61
|
+
result[offset++] = MESSAGE_TYPE_REPLY;
|
|
62
|
+
// Original content length (4 bytes)
|
|
63
|
+
result.set(originalContentLenBytes, offset);
|
|
64
|
+
offset += originalContentLenBytes.length;
|
|
65
|
+
// Original content
|
|
66
|
+
result.set(originalContentBytes, offset);
|
|
67
|
+
offset += originalContentBytes.length;
|
|
68
|
+
// Seeker (34 bytes)
|
|
69
|
+
result.set(originalSeeker, offset);
|
|
70
|
+
offset += SEEKER_SIZE;
|
|
71
|
+
// New content
|
|
72
|
+
result.set(newContentBytes, offset);
|
|
73
|
+
return result;
|
|
74
|
+
}
|
|
75
|
+
/**
|
|
76
|
+
* Serialize a forward message
|
|
77
|
+
*
|
|
78
|
+
* Format: [type: 1 byte][forwardContentLen: 4 bytes][forwardContent][seeker: 34 bytes][newContent]
|
|
79
|
+
*
|
|
80
|
+
* @param forwardContent - The content being forwarded
|
|
81
|
+
* @param newContent - Optional new content to add (empty string if none)
|
|
82
|
+
* @param originalSeeker - The seeker of the original message
|
|
83
|
+
* @returns Serialized forward message bytes
|
|
84
|
+
*/
|
|
85
|
+
export function serializeForwardMessage(forwardContent, newContent, originalSeeker) {
|
|
86
|
+
const newContentBytes = strToBytes(newContent);
|
|
87
|
+
const forwardContentBytes = strToBytes(forwardContent);
|
|
88
|
+
const forwardContentLenBytes = U32.toBytes(BigInt(forwardContentBytes.length));
|
|
89
|
+
// Calculate total size
|
|
90
|
+
const totalSize = 1 + // type
|
|
91
|
+
forwardContentLenBytes.length + // length prefix (4 bytes)
|
|
92
|
+
forwardContentBytes.length + // forward content
|
|
93
|
+
SEEKER_SIZE + // seeker
|
|
94
|
+
newContentBytes.length; // new content
|
|
95
|
+
const result = new Uint8Array(totalSize);
|
|
96
|
+
let offset = 0;
|
|
97
|
+
// Type byte
|
|
98
|
+
result[offset++] = MESSAGE_TYPE_FORWARD;
|
|
99
|
+
// Forward content length (4 bytes)
|
|
100
|
+
result.set(forwardContentLenBytes, offset);
|
|
101
|
+
offset += forwardContentLenBytes.length;
|
|
102
|
+
// Forward content
|
|
103
|
+
result.set(forwardContentBytes, offset);
|
|
104
|
+
offset += forwardContentBytes.length;
|
|
105
|
+
// Seeker (34 bytes)
|
|
106
|
+
result.set(originalSeeker, offset);
|
|
107
|
+
offset += SEEKER_SIZE;
|
|
108
|
+
// New content
|
|
109
|
+
result.set(newContentBytes, offset);
|
|
110
|
+
return result;
|
|
111
|
+
}
|
|
112
|
+
/**
|
|
113
|
+
* Deserialize a message from bytes
|
|
114
|
+
*
|
|
115
|
+
* @param buffer - The serialized message bytes
|
|
116
|
+
* @returns Deserialized message object
|
|
117
|
+
* @throws Error if message format is invalid
|
|
118
|
+
*/
|
|
119
|
+
export function deserializeMessage(buffer) {
|
|
120
|
+
if (buffer.length < 1) {
|
|
121
|
+
throw new Error('Empty message buffer');
|
|
122
|
+
}
|
|
123
|
+
const messageType = buffer[0];
|
|
124
|
+
switch (messageType) {
|
|
125
|
+
case MESSAGE_TYPE_KEEP_ALIVE:
|
|
126
|
+
return {
|
|
127
|
+
content: '',
|
|
128
|
+
type: MessageType.KEEP_ALIVE,
|
|
129
|
+
};
|
|
130
|
+
case MESSAGE_TYPE_REGULAR:
|
|
131
|
+
return {
|
|
132
|
+
content: bytesToStr(buffer.slice(1)),
|
|
133
|
+
type: MessageType.TEXT,
|
|
134
|
+
};
|
|
135
|
+
case MESSAGE_TYPE_REPLY: {
|
|
136
|
+
// Format: [type: 1][originalContentLen: 4][originalContent][seeker: 34][newContent]
|
|
137
|
+
let offset = 1;
|
|
138
|
+
// Read original content length (4 bytes)
|
|
139
|
+
const originalContentLen = Number(U32.fromBytes(buffer.slice(offset, offset + 4)));
|
|
140
|
+
offset += 4;
|
|
141
|
+
// Read original content
|
|
142
|
+
const originalContent = bytesToStr(buffer.slice(offset, offset + originalContentLen));
|
|
143
|
+
offset += originalContentLen;
|
|
144
|
+
// Read seeker (34 bytes)
|
|
145
|
+
const originalSeeker = buffer.slice(offset, offset + SEEKER_SIZE);
|
|
146
|
+
offset += SEEKER_SIZE;
|
|
147
|
+
// Read new content (rest of buffer)
|
|
148
|
+
const content = bytesToStr(buffer.slice(offset));
|
|
149
|
+
return {
|
|
150
|
+
content,
|
|
151
|
+
replyTo: {
|
|
152
|
+
originalContent,
|
|
153
|
+
originalSeeker,
|
|
154
|
+
},
|
|
155
|
+
type: MessageType.TEXT,
|
|
156
|
+
};
|
|
157
|
+
}
|
|
158
|
+
case MESSAGE_TYPE_FORWARD: {
|
|
159
|
+
// Format: [type: 1][forwardContentLen: 4][forwardContent][seeker: 34][newContent]
|
|
160
|
+
let offset = 1;
|
|
161
|
+
// Read forward content length (4 bytes)
|
|
162
|
+
const forwardContentLen = Number(U32.fromBytes(buffer.slice(offset, offset + 4)));
|
|
163
|
+
offset += 4;
|
|
164
|
+
// Read forward content
|
|
165
|
+
const originalContent = bytesToStr(buffer.slice(offset, offset + forwardContentLen));
|
|
166
|
+
offset += forwardContentLen;
|
|
167
|
+
// Read seeker (34 bytes)
|
|
168
|
+
const originalSeeker = buffer.slice(offset, offset + SEEKER_SIZE);
|
|
169
|
+
offset += SEEKER_SIZE;
|
|
170
|
+
// Read new content (rest of buffer)
|
|
171
|
+
const content = bytesToStr(buffer.slice(offset));
|
|
172
|
+
return {
|
|
173
|
+
content,
|
|
174
|
+
forwardOf: {
|
|
175
|
+
originalContent,
|
|
176
|
+
originalSeeker,
|
|
177
|
+
},
|
|
178
|
+
type: MessageType.TEXT,
|
|
179
|
+
};
|
|
180
|
+
}
|
|
181
|
+
default:
|
|
182
|
+
throw new Error(`Unknown message type: ${messageType}`);
|
|
183
|
+
}
|
|
184
|
+
}
|
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Promise Queue
|
|
3
|
+
*
|
|
4
|
+
* Ensures async operations are executed sequentially.
|
|
5
|
+
* Used to serialize session manager operations per contact.
|
|
6
|
+
*/
|
|
7
|
+
/**
|
|
8
|
+
* A simple promise queue that executes tasks sequentially.
|
|
9
|
+
* Tasks are processed in FIFO order.
|
|
10
|
+
*/
|
|
11
|
+
export declare class PromiseQueue {
|
|
12
|
+
private queue;
|
|
13
|
+
private processing;
|
|
14
|
+
/**
|
|
15
|
+
* Add a task to the queue. Returns a promise that resolves
|
|
16
|
+
* when the task completes.
|
|
17
|
+
*/
|
|
18
|
+
enqueue<T>(fn: () => Promise<T>): Promise<T>;
|
|
19
|
+
/**
|
|
20
|
+
* Process the next task in the queue.
|
|
21
|
+
*/
|
|
22
|
+
private processNext;
|
|
23
|
+
/**
|
|
24
|
+
* Check if the queue is currently processing.
|
|
25
|
+
*/
|
|
26
|
+
get isProcessing(): boolean;
|
|
27
|
+
/**
|
|
28
|
+
* Get the number of pending tasks.
|
|
29
|
+
*/
|
|
30
|
+
get pendingCount(): number;
|
|
31
|
+
}
|
|
32
|
+
/**
|
|
33
|
+
* Manages multiple queues keyed by string (e.g., contact ID).
|
|
34
|
+
* Creates queues lazily on first use.
|
|
35
|
+
*/
|
|
36
|
+
export declare class QueueManager {
|
|
37
|
+
private queues;
|
|
38
|
+
/**
|
|
39
|
+
* Get or create a queue for the given key.
|
|
40
|
+
*/
|
|
41
|
+
getQueue(key: string): PromiseQueue;
|
|
42
|
+
/**
|
|
43
|
+
* Enqueue a task for a specific key.
|
|
44
|
+
*/
|
|
45
|
+
enqueue<T>(key: string, fn: () => Promise<T>): Promise<T>;
|
|
46
|
+
/**
|
|
47
|
+
* Clear all queues.
|
|
48
|
+
*/
|
|
49
|
+
clear(): void;
|
|
50
|
+
}
|
|
@@ -0,0 +1,110 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Promise Queue
|
|
3
|
+
*
|
|
4
|
+
* Ensures async operations are executed sequentially.
|
|
5
|
+
* Used to serialize session manager operations per contact.
|
|
6
|
+
*/
|
|
7
|
+
/**
|
|
8
|
+
* A simple promise queue that executes tasks sequentially.
|
|
9
|
+
* Tasks are processed in FIFO order.
|
|
10
|
+
*/
|
|
11
|
+
export class PromiseQueue {
|
|
12
|
+
constructor() {
|
|
13
|
+
Object.defineProperty(this, "queue", {
|
|
14
|
+
enumerable: true,
|
|
15
|
+
configurable: true,
|
|
16
|
+
writable: true,
|
|
17
|
+
value: []
|
|
18
|
+
});
|
|
19
|
+
Object.defineProperty(this, "processing", {
|
|
20
|
+
enumerable: true,
|
|
21
|
+
configurable: true,
|
|
22
|
+
writable: true,
|
|
23
|
+
value: false
|
|
24
|
+
});
|
|
25
|
+
}
|
|
26
|
+
/**
|
|
27
|
+
* Add a task to the queue. Returns a promise that resolves
|
|
28
|
+
* when the task completes.
|
|
29
|
+
*/
|
|
30
|
+
enqueue(fn) {
|
|
31
|
+
return new Promise((resolve, reject) => {
|
|
32
|
+
this.queue.push({
|
|
33
|
+
fn: fn,
|
|
34
|
+
resolve: resolve,
|
|
35
|
+
reject,
|
|
36
|
+
});
|
|
37
|
+
this.processNext();
|
|
38
|
+
});
|
|
39
|
+
}
|
|
40
|
+
/**
|
|
41
|
+
* Process the next task in the queue.
|
|
42
|
+
*/
|
|
43
|
+
async processNext() {
|
|
44
|
+
if (this.processing || this.queue.length === 0) {
|
|
45
|
+
return;
|
|
46
|
+
}
|
|
47
|
+
this.processing = true;
|
|
48
|
+
const task = this.queue.shift();
|
|
49
|
+
try {
|
|
50
|
+
const result = await task.fn();
|
|
51
|
+
task.resolve(result);
|
|
52
|
+
}
|
|
53
|
+
catch (error) {
|
|
54
|
+
task.reject(error);
|
|
55
|
+
}
|
|
56
|
+
finally {
|
|
57
|
+
this.processing = false;
|
|
58
|
+
this.processNext();
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
/**
|
|
62
|
+
* Check if the queue is currently processing.
|
|
63
|
+
*/
|
|
64
|
+
get isProcessing() {
|
|
65
|
+
return this.processing;
|
|
66
|
+
}
|
|
67
|
+
/**
|
|
68
|
+
* Get the number of pending tasks.
|
|
69
|
+
*/
|
|
70
|
+
get pendingCount() {
|
|
71
|
+
return this.queue.length;
|
|
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
|
+
constructor() {
|
|
80
|
+
Object.defineProperty(this, "queues", {
|
|
81
|
+
enumerable: true,
|
|
82
|
+
configurable: true,
|
|
83
|
+
writable: true,
|
|
84
|
+
value: new Map()
|
|
85
|
+
});
|
|
86
|
+
}
|
|
87
|
+
/**
|
|
88
|
+
* Get or create a queue for the given key.
|
|
89
|
+
*/
|
|
90
|
+
getQueue(key) {
|
|
91
|
+
let queue = this.queues.get(key);
|
|
92
|
+
if (!queue) {
|
|
93
|
+
queue = new PromiseQueue();
|
|
94
|
+
this.queues.set(key, queue);
|
|
95
|
+
}
|
|
96
|
+
return queue;
|
|
97
|
+
}
|
|
98
|
+
/**
|
|
99
|
+
* Enqueue a task for a specific key.
|
|
100
|
+
*/
|
|
101
|
+
enqueue(key, fn) {
|
|
102
|
+
return this.getQueue(key).enqueue(fn);
|
|
103
|
+
}
|
|
104
|
+
/**
|
|
105
|
+
* Clear all queues.
|
|
106
|
+
*/
|
|
107
|
+
clear() {
|
|
108
|
+
this.queues.clear();
|
|
109
|
+
}
|
|
110
|
+
}
|
|
@@ -0,0 +1,40 @@
|
|
|
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
|
+
* Encode a 32-byte user ID to Bech32 format with "gossip" prefix
|
|
9
|
+
* @param userId - 32-byte user ID as Uint8Array
|
|
10
|
+
* @returns Bech32-encoded string (e.g., "gossip1qpzry9x8gf2tvdw0s3jn54khce6mua7l...")
|
|
11
|
+
* @throws Error if userId is not exactly 32 bytes
|
|
12
|
+
*/
|
|
13
|
+
export declare function encodeUserId(userId: Uint8Array): string;
|
|
14
|
+
/**
|
|
15
|
+
* Decode a Bech32-encoded user ID back to 32 bytes
|
|
16
|
+
* @param encoded - Bech32-encoded string (e.g., "gossip1qpzry9x8gf2tvdw0s3jn54khce6mua7l...")
|
|
17
|
+
* @returns 32-byte user ID as Uint8Array
|
|
18
|
+
* @throws Error if the format is invalid, checksum fails, or decoded length is not 32 bytes
|
|
19
|
+
*/
|
|
20
|
+
export declare function decodeUserId(encoded: string): Uint8Array;
|
|
21
|
+
/**
|
|
22
|
+
* Validate a Bech32-encoded user ID string
|
|
23
|
+
* @param encoded - Bech32-encoded string to validate
|
|
24
|
+
* @returns true if valid, false otherwise
|
|
25
|
+
*/
|
|
26
|
+
export declare function isValidUserId(encoded: string): boolean;
|
|
27
|
+
/**
|
|
28
|
+
* Format a user ID for display (shortened version)
|
|
29
|
+
* @param userId - Bech32-encoded user ID string
|
|
30
|
+
* @param prefixChars - Number of characters to show after prefix (default: 8)
|
|
31
|
+
* @param suffixChars - Number of characters to show at end (default: 6)
|
|
32
|
+
* @returns Formatted string (e.g., "gossip1qpzry9x8...mua7l")
|
|
33
|
+
*/
|
|
34
|
+
export declare function formatUserId(userId: string, prefixChars?: number, suffixChars?: number): string;
|
|
35
|
+
/**
|
|
36
|
+
* Generates a random 32-byte user ID
|
|
37
|
+
* @param password - Optional password
|
|
38
|
+
* @returns gossip Bech32 string representing a 32-byte user ID
|
|
39
|
+
*/
|
|
40
|
+
export declare function generate(password?: string): Promise<string>;
|
|
@@ -0,0 +1,90 @@
|
|
|
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
|
+
import { bech32 } from '@scure/base';
|
|
8
|
+
import { generateUserKeys } from '../wasm';
|
|
9
|
+
const GOSSIP_PREFIX = 'gossip';
|
|
10
|
+
const USER_ID_BYTE_LENGTH = 32;
|
|
11
|
+
/**
|
|
12
|
+
* Encode a 32-byte user ID to Bech32 format with "gossip" prefix
|
|
13
|
+
* @param userId - 32-byte user ID as Uint8Array
|
|
14
|
+
* @returns Bech32-encoded string (e.g., "gossip1qpzry9x8gf2tvdw0s3jn54khce6mua7l...")
|
|
15
|
+
* @throws Error if userId is not exactly 32 bytes
|
|
16
|
+
*/
|
|
17
|
+
export function encodeUserId(userId) {
|
|
18
|
+
if (userId.length !== USER_ID_BYTE_LENGTH) {
|
|
19
|
+
throw new Error(`User ID must be exactly ${USER_ID_BYTE_LENGTH} bytes, got ${userId.length}`);
|
|
20
|
+
}
|
|
21
|
+
return bech32.encode(GOSSIP_PREFIX, bech32.toWords(userId));
|
|
22
|
+
}
|
|
23
|
+
/**
|
|
24
|
+
* Decode a Bech32-encoded user ID back to 32 bytes
|
|
25
|
+
* @param encoded - Bech32-encoded string (e.g., "gossip1qpzry9x8gf2tvdw0s3jn54khce6mua7l...")
|
|
26
|
+
* @returns 32-byte user ID as Uint8Array
|
|
27
|
+
* @throws Error if the format is invalid, checksum fails, or decoded length is not 32 bytes
|
|
28
|
+
*/
|
|
29
|
+
export function decodeUserId(encoded) {
|
|
30
|
+
// Type assertion needed as bech32.decode expects a template literal type
|
|
31
|
+
const { prefix, words } = bech32.decode(encoded, 90);
|
|
32
|
+
// Verify prefix
|
|
33
|
+
if (prefix !== GOSSIP_PREFIX) {
|
|
34
|
+
throw new Error(`Invalid prefix: expected "${GOSSIP_PREFIX}", got "${prefix}"`);
|
|
35
|
+
}
|
|
36
|
+
// Convert from 5-bit words back to bytes
|
|
37
|
+
const decoded = bech32.fromWords(words);
|
|
38
|
+
// Verify length
|
|
39
|
+
if (decoded.length !== USER_ID_BYTE_LENGTH) {
|
|
40
|
+
throw new Error(`Decoded user ID must be ${USER_ID_BYTE_LENGTH} bytes, got ${decoded.length}`);
|
|
41
|
+
}
|
|
42
|
+
return new Uint8Array(decoded);
|
|
43
|
+
}
|
|
44
|
+
/**
|
|
45
|
+
* Validate a Bech32-encoded user ID string
|
|
46
|
+
* @param encoded - Bech32-encoded string to validate
|
|
47
|
+
* @returns true if valid, false otherwise
|
|
48
|
+
*/
|
|
49
|
+
export function isValidUserId(encoded) {
|
|
50
|
+
try {
|
|
51
|
+
decodeUserId(encoded);
|
|
52
|
+
return true;
|
|
53
|
+
}
|
|
54
|
+
catch {
|
|
55
|
+
return false;
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
/**
|
|
59
|
+
* Format a user ID for display (shortened version)
|
|
60
|
+
* @param userId - Bech32-encoded user ID string
|
|
61
|
+
* @param prefixChars - Number of characters to show after prefix (default: 8)
|
|
62
|
+
* @param suffixChars - Number of characters to show at end (default: 6)
|
|
63
|
+
* @returns Formatted string (e.g., "gossip1qpzry9x8...mua7l")
|
|
64
|
+
*/
|
|
65
|
+
export function formatUserId(userId, prefixChars = 8, suffixChars = 6) {
|
|
66
|
+
if (!userId)
|
|
67
|
+
return '';
|
|
68
|
+
// Find separator position
|
|
69
|
+
const sepPos = userId.indexOf('1');
|
|
70
|
+
if (sepPos === -1)
|
|
71
|
+
return userId;
|
|
72
|
+
const prefix = userId.substring(0, sepPos + 1); // "gossip1"
|
|
73
|
+
const data = userId.substring(sepPos + 1); // rest of the string
|
|
74
|
+
if (data.length <= prefixChars + suffixChars) {
|
|
75
|
+
return userId; // Too short to format
|
|
76
|
+
}
|
|
77
|
+
const start = data.slice(0, prefixChars);
|
|
78
|
+
const end = data.slice(-suffixChars);
|
|
79
|
+
return `${prefix}${start}...${end}`;
|
|
80
|
+
}
|
|
81
|
+
/**
|
|
82
|
+
* Generates a random 32-byte user ID
|
|
83
|
+
* @param password - Optional password
|
|
84
|
+
* @returns gossip Bech32 string representing a 32-byte user ID
|
|
85
|
+
*/
|
|
86
|
+
export async function generate(password) {
|
|
87
|
+
const identity = await generateUserKeys(password || '');
|
|
88
|
+
const userId = identity.public_keys().derive_id();
|
|
89
|
+
return encodeUserId(userId);
|
|
90
|
+
}
|