signal-sdk 0.0.9 → 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/config.js ADDED
@@ -0,0 +1,111 @@
1
+ "use strict";
2
+ /**
3
+ * Configuration management for Signal SDK
4
+ * Provides centralized configuration with validation
5
+ */
6
+ Object.defineProperty(exports, "__esModule", { value: true });
7
+ exports.Logger = exports.DEFAULT_LOGGER_CONFIG = exports.DEFAULT_CONFIG = void 0;
8
+ exports.validateConfig = validateConfig;
9
+ exports.DEFAULT_CONFIG = {
10
+ signalCliPath: '',
11
+ account: '',
12
+ connectionTimeout: 30000,
13
+ requestTimeout: 60000,
14
+ enableRetry: true,
15
+ maxRetries: 3,
16
+ retryDelay: 1000,
17
+ verbose: false,
18
+ logFile: '',
19
+ maxConcurrentRequests: 5,
20
+ minRequestInterval: 100,
21
+ autoReconnect: true,
22
+ trustNewIdentities: 'on-first-use',
23
+ disableSendLog: false
24
+ };
25
+ /**
26
+ * Validates and merges configuration with defaults
27
+ * @param userConfig User-provided configuration
28
+ * @returns Validated configuration
29
+ */
30
+ function validateConfig(userConfig = {}) {
31
+ const config = { ...exports.DEFAULT_CONFIG, ...userConfig };
32
+ // Validate numeric values
33
+ if (config.connectionTimeout < 0) {
34
+ throw new Error('connectionTimeout must be non-negative');
35
+ }
36
+ if (config.requestTimeout < 0) {
37
+ throw new Error('requestTimeout must be non-negative');
38
+ }
39
+ if (config.maxRetries < 0) {
40
+ throw new Error('maxRetries must be non-negative');
41
+ }
42
+ if (config.retryDelay < 0) {
43
+ throw new Error('retryDelay must be non-negative');
44
+ }
45
+ if (config.maxConcurrentRequests < 1) {
46
+ throw new Error('maxConcurrentRequests must be at least 1');
47
+ }
48
+ if (config.minRequestInterval < 0) {
49
+ throw new Error('minRequestInterval must be non-negative');
50
+ }
51
+ return config;
52
+ }
53
+ exports.DEFAULT_LOGGER_CONFIG = {
54
+ level: 'info',
55
+ enableConsole: true,
56
+ enableFile: false,
57
+ includeTimestamp: true,
58
+ includeLevel: true
59
+ };
60
+ /**
61
+ * Simple logger implementation
62
+ */
63
+ class Logger {
64
+ constructor(config = {}) {
65
+ this.levels = {
66
+ debug: 0,
67
+ info: 1,
68
+ warn: 2,
69
+ error: 3
70
+ };
71
+ this.config = { ...exports.DEFAULT_LOGGER_CONFIG, ...config };
72
+ }
73
+ shouldLog(level) {
74
+ return this.levels[level] >= this.levels[this.config.level];
75
+ }
76
+ format(level, message, data) {
77
+ const parts = [];
78
+ if (this.config.includeTimestamp) {
79
+ parts.push(`[${new Date().toISOString()}]`);
80
+ }
81
+ if (this.config.includeLevel) {
82
+ parts.push(`[${level.toUpperCase()}]`);
83
+ }
84
+ parts.push(message);
85
+ if (data !== undefined) {
86
+ parts.push(JSON.stringify(data, null, 2));
87
+ }
88
+ return parts.join(' ');
89
+ }
90
+ debug(message, data) {
91
+ if (this.shouldLog('debug') && this.config.enableConsole) {
92
+ console.debug(this.format('debug', message, data));
93
+ }
94
+ }
95
+ info(message, data) {
96
+ if (this.shouldLog('info') && this.config.enableConsole) {
97
+ console.info(this.format('info', message, data));
98
+ }
99
+ }
100
+ warn(message, data) {
101
+ if (this.shouldLog('warn') && this.config.enableConsole) {
102
+ console.warn(this.format('warn', message, data));
103
+ }
104
+ }
105
+ error(message, data) {
106
+ if (this.shouldLog('error') && this.config.enableConsole) {
107
+ console.error(this.format('error', message, data));
108
+ }
109
+ }
110
+ }
111
+ exports.Logger = Logger;
@@ -0,0 +1,32 @@
1
+ /**
2
+ * Custom error classes for Signal SDK
3
+ * Provides typed errors for better error handling
4
+ */
5
+ export declare class SignalError extends Error {
6
+ code?: string | undefined;
7
+ constructor(message: string, code?: string | undefined);
8
+ }
9
+ export declare class ConnectionError extends SignalError {
10
+ constructor(message: string);
11
+ }
12
+ export declare class AuthenticationError extends SignalError {
13
+ constructor(message: string);
14
+ }
15
+ export declare class RateLimitError extends SignalError {
16
+ retryAfter?: number | undefined;
17
+ challenge?: string | undefined;
18
+ constructor(message: string, retryAfter?: number | undefined, challenge?: string | undefined);
19
+ }
20
+ export declare class ValidationError extends SignalError {
21
+ field?: string | undefined;
22
+ constructor(message: string, field?: string | undefined);
23
+ }
24
+ export declare class TimeoutError extends SignalError {
25
+ constructor(message?: string);
26
+ }
27
+ export declare class GroupError extends SignalError {
28
+ constructor(message: string);
29
+ }
30
+ export declare class MessageError extends SignalError {
31
+ constructor(message: string);
32
+ }
package/dist/errors.js ADDED
@@ -0,0 +1,75 @@
1
+ "use strict";
2
+ /**
3
+ * Custom error classes for Signal SDK
4
+ * Provides typed errors for better error handling
5
+ */
6
+ Object.defineProperty(exports, "__esModule", { value: true });
7
+ exports.MessageError = exports.GroupError = exports.TimeoutError = exports.ValidationError = exports.RateLimitError = exports.AuthenticationError = exports.ConnectionError = exports.SignalError = void 0;
8
+ class SignalError extends Error {
9
+ constructor(message, code) {
10
+ super(message);
11
+ this.code = code;
12
+ this.name = 'SignalError';
13
+ Object.setPrototypeOf(this, SignalError.prototype);
14
+ }
15
+ }
16
+ exports.SignalError = SignalError;
17
+ class ConnectionError extends SignalError {
18
+ constructor(message) {
19
+ super(message, 'CONNECTION_ERROR');
20
+ this.name = 'ConnectionError';
21
+ Object.setPrototypeOf(this, ConnectionError.prototype);
22
+ }
23
+ }
24
+ exports.ConnectionError = ConnectionError;
25
+ class AuthenticationError extends SignalError {
26
+ constructor(message) {
27
+ super(message, 'AUTH_ERROR');
28
+ this.name = 'AuthenticationError';
29
+ Object.setPrototypeOf(this, AuthenticationError.prototype);
30
+ }
31
+ }
32
+ exports.AuthenticationError = AuthenticationError;
33
+ class RateLimitError extends SignalError {
34
+ constructor(message, retryAfter, challenge) {
35
+ super(message, 'RATE_LIMIT');
36
+ this.retryAfter = retryAfter;
37
+ this.challenge = challenge;
38
+ this.name = 'RateLimitError';
39
+ Object.setPrototypeOf(this, RateLimitError.prototype);
40
+ }
41
+ }
42
+ exports.RateLimitError = RateLimitError;
43
+ class ValidationError extends SignalError {
44
+ constructor(message, field) {
45
+ super(message, 'VALIDATION_ERROR');
46
+ this.field = field;
47
+ this.name = 'ValidationError';
48
+ Object.setPrototypeOf(this, ValidationError.prototype);
49
+ }
50
+ }
51
+ exports.ValidationError = ValidationError;
52
+ class TimeoutError extends SignalError {
53
+ constructor(message = 'Operation timed out') {
54
+ super(message, 'TIMEOUT');
55
+ this.name = 'TimeoutError';
56
+ Object.setPrototypeOf(this, TimeoutError.prototype);
57
+ }
58
+ }
59
+ exports.TimeoutError = TimeoutError;
60
+ class GroupError extends SignalError {
61
+ constructor(message) {
62
+ super(message, 'GROUP_ERROR');
63
+ this.name = 'GroupError';
64
+ Object.setPrototypeOf(this, GroupError.prototype);
65
+ }
66
+ }
67
+ exports.GroupError = GroupError;
68
+ class MessageError extends SignalError {
69
+ constructor(message) {
70
+ super(message, 'MESSAGE_ERROR');
71
+ this.name = 'MessageError';
72
+ Object.setPrototypeOf(this, MessageError.prototype);
73
+ }
74
+ }
75
+ exports.MessageError = MessageError;
package/dist/index.d.ts CHANGED
@@ -1,3 +1,7 @@
1
1
  export { SignalCli } from './SignalCli';
2
2
  export { SignalBot } from './SignalBot';
3
3
  export * from './interfaces';
4
+ export * from './errors';
5
+ export * from './validators';
6
+ export * from './retry';
7
+ export * from './config';
package/dist/index.js CHANGED
@@ -20,3 +20,7 @@ Object.defineProperty(exports, "SignalCli", { enumerable: true, get: function ()
20
20
  var SignalBot_1 = require("./SignalBot");
21
21
  Object.defineProperty(exports, "SignalBot", { enumerable: true, get: function () { return SignalBot_1.SignalBot; } });
22
22
  __exportStar(require("./interfaces"), exports);
23
+ __exportStar(require("./errors"), exports);
24
+ __exportStar(require("./validators"), exports);
25
+ __exportStar(require("./retry"), exports);
26
+ __exportStar(require("./config"), exports);
@@ -5,7 +5,7 @@
5
5
  * which uses JSON-RPC communication with signal-cli for optimal performance.
6
6
  *
7
7
  * @author Signal SDK Team
8
- * @version 2.0.0
8
+ * @version 0.1.0
9
9
  */
10
10
  /**
11
11
  * @deprecated This interface is no longer used since switching to JSON-RPC format.
@@ -936,3 +936,138 @@ export interface UploadProgress {
936
936
  /** Estimated time remaining in seconds */
937
937
  timeRemaining?: number;
938
938
  }
939
+ /**
940
+ * Options for creating a poll
941
+ */
942
+ export interface PollCreateOptions {
943
+ /** The poll question */
944
+ question: string;
945
+ /** Array of poll options */
946
+ options: string[];
947
+ /** Allow multiple selections (default: true) */
948
+ multiSelect?: boolean;
949
+ /** Recipients to send the poll to */
950
+ recipients?: string[];
951
+ /** Group ID to send the poll to */
952
+ groupId?: string;
953
+ }
954
+ /**
955
+ * Options for voting on a poll
956
+ */
957
+ export interface PollVoteOptions {
958
+ /** Author of the poll */
959
+ pollAuthor: string;
960
+ /** Timestamp of the poll message */
961
+ pollTimestamp: number;
962
+ /** Array of option indexes to vote for */
963
+ optionIndexes: number[];
964
+ /** Vote count (increase for each vote) */
965
+ voteCount?: number;
966
+ }
967
+ /**
968
+ * Options for terminating a poll
969
+ */
970
+ export interface PollTerminateOptions {
971
+ /** Timestamp of the poll message to terminate */
972
+ pollTimestamp: number;
973
+ }
974
+ /**
975
+ * Options for sending a story
976
+ */
977
+ export interface StoryOptions {
978
+ /** Story content (text or attachment path) */
979
+ content?: string;
980
+ /** Attachment for the story */
981
+ attachment?: string;
982
+ /** Text attachment with style */
983
+ textAttachment?: {
984
+ text: string;
985
+ textStyle?: 'DEFAULT' | 'REGULAR' | 'BOLD' | 'SERIF' | 'SCRIPT' | 'CONDENSED';
986
+ textForegroundColor?: string;
987
+ textBackgroundColor?: string;
988
+ preview?: {
989
+ url: string;
990
+ title?: string;
991
+ description?: string;
992
+ };
993
+ };
994
+ /** Allow replies (default: true) */
995
+ allowReplies?: boolean;
996
+ }
997
+ /**
998
+ * Options for getting attachment data
999
+ */
1000
+ export interface GetAttachmentOptions {
1001
+ /** Attachment ID */
1002
+ id: string;
1003
+ /** Recipient who sent the attachment */
1004
+ recipient?: string;
1005
+ /** Group ID where attachment was sent */
1006
+ groupId?: string;
1007
+ }
1008
+ /**
1009
+ * Options for getting avatar data
1010
+ */
1011
+ export interface GetAvatarOptions {
1012
+ /** Contact number for contact avatar */
1013
+ contact?: string;
1014
+ /** Profile number for profile avatar */
1015
+ profile?: string;
1016
+ /** Group ID for group avatar */
1017
+ groupId?: string;
1018
+ }
1019
+ /**
1020
+ * Options for getting sticker data
1021
+ */
1022
+ export interface GetStickerOptions {
1023
+ /** Sticker pack ID (hex encoded) */
1024
+ packId: string;
1025
+ /** Sticker index in the pack */
1026
+ stickerId: number;
1027
+ }
1028
+ /**
1029
+ * Options for updating account information
1030
+ */
1031
+ export interface UpdateAccountOptions {
1032
+ /** New device name */
1033
+ deviceName?: string;
1034
+ /** Username to set (with or without discriminator) */
1035
+ username?: string;
1036
+ /** Delete the current username */
1037
+ deleteUsername?: boolean;
1038
+ /** Enable unrestricted unidentified sender */
1039
+ unrestrictedUnidentifiedSender?: boolean;
1040
+ /** Enable discoverability by phone number */
1041
+ discoverableByNumber?: boolean;
1042
+ /** Enable number sharing */
1043
+ numberSharing?: boolean;
1044
+ }
1045
+ /**
1046
+ * Result from account update with username
1047
+ */
1048
+ export interface AccountUpdateResult {
1049
+ /** Success status */
1050
+ success: boolean;
1051
+ /** New username with discriminator */
1052
+ username?: string;
1053
+ /** Username link URL */
1054
+ usernameLink?: string;
1055
+ /** Error message if failed */
1056
+ error?: string;
1057
+ }
1058
+ /**
1059
+ * Options for sending contacts sync
1060
+ */
1061
+ export interface SendContactsOptions {
1062
+ /** Include all recipients, not just contacts */
1063
+ includeAllRecipients?: boolean;
1064
+ }
1065
+ /**
1066
+ * Options for listing groups
1067
+ */
1068
+ export interface ListGroupsOptions {
1069
+ /** Show detailed information */
1070
+ detailed?: boolean;
1071
+ /** Filter by specific group IDs */
1072
+ groupIds?: string[];
1073
+ }
@@ -6,6 +6,6 @@
6
6
  * which uses JSON-RPC communication with signal-cli for optimal performance.
7
7
  *
8
8
  * @author Signal SDK Team
9
- * @version 2.0.0
9
+ * @version 0.1.0
10
10
  */
11
11
  Object.defineProperty(exports, "__esModule", { value: true });
@@ -0,0 +1,56 @@
1
+ /**
2
+ * Retry utilities with exponential backoff
3
+ * Provides robust retry mechanisms for operations
4
+ */
5
+ export interface RetryOptions {
6
+ /** Maximum number of retry attempts */
7
+ maxAttempts?: number;
8
+ /** Initial delay in milliseconds */
9
+ initialDelay?: number;
10
+ /** Maximum delay in milliseconds */
11
+ maxDelay?: number;
12
+ /** Multiplier for exponential backoff */
13
+ backoffMultiplier?: number;
14
+ /** Timeout for each attempt in milliseconds */
15
+ timeout?: number;
16
+ /** Function to determine if error is retryable */
17
+ isRetryable?: (error: any) => boolean;
18
+ /** Callback for each retry attempt */
19
+ onRetry?: (attempt: number, error: any) => void;
20
+ }
21
+ /**
22
+ * Executes an operation with retry logic and exponential backoff
23
+ * @param operation Function to execute
24
+ * @param options Retry configuration options
25
+ * @returns Result of the operation
26
+ */
27
+ export declare function withRetry<T>(operation: () => Promise<T>, options?: RetryOptions): Promise<T>;
28
+ /**
29
+ * Executes an operation with a timeout
30
+ * @param promise Promise to execute
31
+ * @param timeoutMs Timeout in milliseconds
32
+ * @returns Result of the promise
33
+ */
34
+ export declare function withTimeout<T>(promise: Promise<T>, timeoutMs: number): Promise<T>;
35
+ /**
36
+ * Sleep for a specified duration
37
+ * @param ms Duration in milliseconds
38
+ */
39
+ export declare function sleep(ms: number): Promise<void>;
40
+ /**
41
+ * Rate limiter to prevent exceeding API limits
42
+ */
43
+ export declare class RateLimiter {
44
+ private maxConcurrent;
45
+ private minInterval;
46
+ private queue;
47
+ private activeRequests;
48
+ constructor(maxConcurrent?: number, minInterval?: number);
49
+ /**
50
+ * Execute an operation with rate limiting
51
+ * @param operation Function to execute
52
+ * @returns Result of the operation
53
+ */
54
+ execute<T>(operation: () => Promise<T>): Promise<T>;
55
+ private reserveSlot;
56
+ }
package/dist/retry.js ADDED
@@ -0,0 +1,135 @@
1
+ "use strict";
2
+ /**
3
+ * Retry utilities with exponential backoff
4
+ * Provides robust retry mechanisms for operations
5
+ */
6
+ Object.defineProperty(exports, "__esModule", { value: true });
7
+ exports.RateLimiter = void 0;
8
+ exports.withRetry = withRetry;
9
+ exports.withTimeout = withTimeout;
10
+ exports.sleep = sleep;
11
+ const errors_1 = require("./errors");
12
+ const DEFAULT_RETRY_OPTIONS = {
13
+ maxAttempts: 3,
14
+ initialDelay: 1000,
15
+ maxDelay: 30000,
16
+ backoffMultiplier: 2,
17
+ timeout: 60000,
18
+ isRetryable: (error) => {
19
+ // Retry on connection errors, timeouts, and certain server errors
20
+ if (!error)
21
+ return false;
22
+ const errorMessage = error.message?.toLowerCase() || '';
23
+ const isConnectionError = errorMessage.includes('connection') ||
24
+ errorMessage.includes('timeout') ||
25
+ errorMessage.includes('econnrefused') ||
26
+ errorMessage.includes('econnreset');
27
+ const isServerError = error.code === 500 || error.code === 502 || error.code === 503;
28
+ // Don't retry on authentication or validation errors
29
+ const isClientError = error.code === 401 || error.code === 403 || error.code === 400;
30
+ return (isConnectionError || isServerError) && !isClientError;
31
+ },
32
+ onRetry: () => { }
33
+ };
34
+ /**
35
+ * Executes an operation with retry logic and exponential backoff
36
+ * @param operation Function to execute
37
+ * @param options Retry configuration options
38
+ * @returns Result of the operation
39
+ */
40
+ async function withRetry(operation, options = {}) {
41
+ const config = { ...DEFAULT_RETRY_OPTIONS, ...options };
42
+ let lastError;
43
+ for (let attempt = 1; attempt <= config.maxAttempts; attempt++) {
44
+ try {
45
+ // Execute with timeout
46
+ const result = await withTimeout(operation(), config.timeout);
47
+ return result;
48
+ }
49
+ catch (error) {
50
+ lastError = error;
51
+ // Check if we should retry
52
+ const shouldRetry = attempt < config.maxAttempts && config.isRetryable(error);
53
+ if (!shouldRetry) {
54
+ throw error;
55
+ }
56
+ // Calculate delay with exponential backoff
57
+ const delay = Math.min(config.initialDelay * Math.pow(config.backoffMultiplier, attempt - 1), config.maxDelay);
58
+ // Notify about retry
59
+ config.onRetry(attempt, error);
60
+ // Wait before retrying
61
+ await sleep(delay);
62
+ }
63
+ }
64
+ throw lastError;
65
+ }
66
+ /**
67
+ * Executes an operation with a timeout
68
+ * @param promise Promise to execute
69
+ * @param timeoutMs Timeout in milliseconds
70
+ * @returns Result of the promise
71
+ */
72
+ async function withTimeout(promise, timeoutMs) {
73
+ return Promise.race([
74
+ promise,
75
+ new Promise((_, reject) => {
76
+ setTimeout(() => {
77
+ reject(new errors_1.TimeoutError(`Operation timed out after ${timeoutMs}ms`));
78
+ }, timeoutMs);
79
+ })
80
+ ]);
81
+ }
82
+ /**
83
+ * Sleep for a specified duration
84
+ * @param ms Duration in milliseconds
85
+ */
86
+ function sleep(ms) {
87
+ return new Promise(resolve => setTimeout(resolve, ms));
88
+ }
89
+ /**
90
+ * Rate limiter to prevent exceeding API limits
91
+ */
92
+ class RateLimiter {
93
+ constructor(maxConcurrent = 5, minInterval = 100) {
94
+ this.maxConcurrent = maxConcurrent;
95
+ this.minInterval = minInterval;
96
+ this.queue = [];
97
+ this.activeRequests = 0;
98
+ }
99
+ /**
100
+ * Execute an operation with rate limiting
101
+ * @param operation Function to execute
102
+ * @returns Result of the operation
103
+ */
104
+ async execute(operation) {
105
+ // Wait for slot and reserve it
106
+ await this.reserveSlot();
107
+ try {
108
+ const result = await operation();
109
+ return result;
110
+ }
111
+ finally {
112
+ // Wait minimum interval before allowing next request
113
+ await sleep(this.minInterval);
114
+ // Release slot
115
+ this.activeRequests--;
116
+ const next = this.queue.shift();
117
+ if (next) {
118
+ next();
119
+ }
120
+ }
121
+ }
122
+ reserveSlot() {
123
+ if (this.activeRequests < this.maxConcurrent) {
124
+ this.activeRequests++;
125
+ return Promise.resolve();
126
+ }
127
+ return new Promise(resolve => {
128
+ this.queue.push(() => {
129
+ this.activeRequests++;
130
+ resolve();
131
+ });
132
+ });
133
+ }
134
+ }
135
+ exports.RateLimiter = RateLimiter;
@@ -0,0 +1,59 @@
1
+ /**
2
+ * Input validation utilities for Signal SDK
3
+ * Provides strict validation for all inputs
4
+ */
5
+ /**
6
+ * Validates a phone number format (E.164)
7
+ * @param phoneNumber Phone number to validate
8
+ * @throws ValidationError if invalid
9
+ */
10
+ export declare function validatePhoneNumber(phoneNumber: string): void;
11
+ /**
12
+ * Validates a group ID format
13
+ * @param groupId Group ID to validate
14
+ * @throws ValidationError if invalid
15
+ */
16
+ export declare function validateGroupId(groupId: string): void;
17
+ /**
18
+ * Validates a recipient (phone number, UUID, or username)
19
+ * @param recipient Recipient to validate
20
+ * @throws ValidationError if invalid
21
+ */
22
+ export declare function validateRecipient(recipient: string): void;
23
+ /**
24
+ * Validates a message text
25
+ * @param message Message to validate
26
+ * @param maxLength Maximum message length
27
+ * @throws ValidationError if invalid
28
+ */
29
+ export declare function validateMessage(message: string, maxLength?: number): void;
30
+ /**
31
+ * Validates file attachments
32
+ * @param attachments Array of attachment paths
33
+ * @throws ValidationError if invalid
34
+ */
35
+ export declare function validateAttachments(attachments: string[]): void;
36
+ /**
37
+ * Validates a timestamp
38
+ * @param timestamp Timestamp to validate
39
+ * @throws ValidationError if invalid
40
+ */
41
+ export declare function validateTimestamp(timestamp: number): void;
42
+ /**
43
+ * Validates an emoji string
44
+ * @param emoji Emoji to validate
45
+ * @throws ValidationError if invalid
46
+ */
47
+ export declare function validateEmoji(emoji: string): void;
48
+ /**
49
+ * Validates device ID
50
+ * @param deviceId Device ID to validate
51
+ * @throws ValidationError if invalid
52
+ */
53
+ export declare function validateDeviceId(deviceId: number): void;
54
+ /**
55
+ * Sanitizes user input to prevent injection attacks
56
+ * @param input Input string to sanitize
57
+ * @returns Sanitized string
58
+ */
59
+ export declare function sanitizeInput(input: string): string;