flashq 0.3.3 → 0.3.4

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.
@@ -0,0 +1,100 @@
1
+ /**
2
+ * Reconnection logic for FlashQ client.
3
+ *
4
+ * Implements exponential backoff with jitter for automatic reconnection
5
+ * after connection loss.
6
+ */
7
+ import { EventEmitter } from 'events';
8
+ import type { Logger } from '../utils/logger';
9
+ /** Connection state machine states */
10
+ export type ConnectionState = 'disconnected' | 'connecting' | 'connected' | 'reconnecting' | 'closed';
11
+ /** Configuration for reconnection behavior */
12
+ export interface ReconnectConfig {
13
+ /** Whether auto-reconnect is enabled */
14
+ enabled: boolean;
15
+ /** Maximum number of reconnection attempts (0 = unlimited) */
16
+ maxAttempts: number;
17
+ /** Initial delay between reconnection attempts (ms) */
18
+ initialDelay: number;
19
+ /** Maximum delay between reconnection attempts (ms) */
20
+ maxDelay: number;
21
+ }
22
+ /** Event data emitted during reconnection */
23
+ export interface ReconnectEventData {
24
+ /** Current attempt number */
25
+ attempt: number;
26
+ /** Delay before this attempt (ms) */
27
+ delay: number;
28
+ }
29
+ /**
30
+ * Manages reconnection state and scheduling.
31
+ *
32
+ * Implements exponential backoff with jitter:
33
+ * delay = min(initialDelay * 2^attempt, maxDelay) + random jitter
34
+ *
35
+ * @example
36
+ * ```typescript
37
+ * const manager = new ReconnectManager(config, logger, emitter);
38
+ *
39
+ * manager.schedule(async () => {
40
+ * await connect();
41
+ * manager.reset();
42
+ * });
43
+ * ```
44
+ */
45
+ export declare class ReconnectManager {
46
+ private readonly config;
47
+ private readonly logger;
48
+ private readonly emitter;
49
+ /** Current reconnection attempt number */
50
+ private attempts;
51
+ /** Timer handle for scheduled reconnection */
52
+ private timer;
53
+ /** Whether reconnection was cancelled */
54
+ private cancelled;
55
+ constructor(config: ReconnectConfig, logger: Logger, emitter: EventEmitter);
56
+ /**
57
+ * Calculates the delay for the next reconnection attempt.
58
+ *
59
+ * Uses exponential backoff with 30% jitter to prevent
60
+ * thundering herd problems.
61
+ *
62
+ * @param attempt - Current attempt number (1-based)
63
+ * @returns Delay in milliseconds
64
+ */
65
+ calculateDelay(attempt: number): number;
66
+ /**
67
+ * Checks if more reconnection attempts are allowed.
68
+ *
69
+ * @returns true if another attempt can be made
70
+ */
71
+ canRetry(): boolean;
72
+ /**
73
+ * Schedules a reconnection attempt.
74
+ *
75
+ * @param connectFn - Async function that performs the connection
76
+ */
77
+ schedule(connectFn: () => Promise<void>): void;
78
+ /**
79
+ * Resets the reconnection state after successful connection.
80
+ */
81
+ reset(): void;
82
+ /**
83
+ * Cancels any pending reconnection attempt.
84
+ */
85
+ cancel(): void;
86
+ /**
87
+ * Gets the current attempt count.
88
+ */
89
+ getAttempts(): number;
90
+ }
91
+ /**
92
+ * Waits for a reconnection to complete.
93
+ *
94
+ * @param emitter - Event emitter that fires reconnection events
95
+ * @param timeout - Maximum time to wait (ms)
96
+ * @returns Promise that resolves when reconnected
97
+ * @throws ConnectionError if reconnection fails or times out
98
+ */
99
+ export declare function waitForReconnection(emitter: EventEmitter, timeout: number): Promise<void>;
100
+ //# sourceMappingURL=reconnect.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"reconnect.d.ts","sourceRoot":"","sources":["../../src/client/reconnect.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAEH,OAAO,EAAE,YAAY,EAAE,MAAM,QAAQ,CAAC;AAEtC,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,iBAAiB,CAAC;AAE9C,sCAAsC;AACtC,MAAM,MAAM,eAAe,GACvB,cAAc,GACd,YAAY,GACZ,WAAW,GACX,cAAc,GACd,QAAQ,CAAC;AAEb,8CAA8C;AAC9C,MAAM,WAAW,eAAe;IAC9B,wCAAwC;IACxC,OAAO,EAAE,OAAO,CAAC;IACjB,8DAA8D;IAC9D,WAAW,EAAE,MAAM,CAAC;IACpB,uDAAuD;IACvD,YAAY,EAAE,MAAM,CAAC;IACrB,uDAAuD;IACvD,QAAQ,EAAE,MAAM,CAAC;CAClB;AAED,6CAA6C;AAC7C,MAAM,WAAW,kBAAkB;IACjC,6BAA6B;IAC7B,OAAO,EAAE,MAAM,CAAC;IAChB,qCAAqC;IACrC,KAAK,EAAE,MAAM,CAAC;CACf;AAED;;;;;;;;;;;;;;;GAeG;AACH,qBAAa,gBAAgB;IASzB,OAAO,CAAC,QAAQ,CAAC,MAAM;IACvB,OAAO,CAAC,QAAQ,CAAC,MAAM;IACvB,OAAO,CAAC,QAAQ,CAAC,OAAO;IAV1B,0CAA0C;IAC1C,OAAO,CAAC,QAAQ,CAAK;IACrB,8CAA8C;IAC9C,OAAO,CAAC,KAAK,CAA8C;IAC3D,yCAAyC;IACzC,OAAO,CAAC,SAAS,CAAS;gBAGP,MAAM,EAAE,eAAe,EACvB,MAAM,EAAE,MAAM,EACd,OAAO,EAAE,YAAY;IAGxC;;;;;;;;OAQG;IACH,cAAc,CAAC,OAAO,EAAE,MAAM,GAAG,MAAM;IASvC;;;;OAIG;IACH,QAAQ,IAAI,OAAO;IAMnB;;;;OAIG;IACH,QAAQ,CAAC,SAAS,EAAE,MAAM,OAAO,CAAC,IAAI,CAAC,GAAG,IAAI;IAgC9C;;OAEG;IACH,KAAK,IAAI,IAAI;IAKb;;OAEG;IACH,MAAM,IAAI,IAAI;IAQd;;OAEG;IACH,WAAW,IAAI,MAAM;CAGtB;AAED;;;;;;;GAOG;AACH,wBAAgB,mBAAmB,CAAC,OAAO,EAAE,YAAY,EAAE,OAAO,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAuBzF"}
@@ -0,0 +1,154 @@
1
+ "use strict";
2
+ /**
3
+ * Reconnection logic for FlashQ client.
4
+ *
5
+ * Implements exponential backoff with jitter for automatic reconnection
6
+ * after connection loss.
7
+ */
8
+ Object.defineProperty(exports, "__esModule", { value: true });
9
+ exports.ReconnectManager = void 0;
10
+ exports.waitForReconnection = waitForReconnection;
11
+ const errors_1 = require("../errors");
12
+ /**
13
+ * Manages reconnection state and scheduling.
14
+ *
15
+ * Implements exponential backoff with jitter:
16
+ * delay = min(initialDelay * 2^attempt, maxDelay) + random jitter
17
+ *
18
+ * @example
19
+ * ```typescript
20
+ * const manager = new ReconnectManager(config, logger, emitter);
21
+ *
22
+ * manager.schedule(async () => {
23
+ * await connect();
24
+ * manager.reset();
25
+ * });
26
+ * ```
27
+ */
28
+ class ReconnectManager {
29
+ config;
30
+ logger;
31
+ emitter;
32
+ /** Current reconnection attempt number */
33
+ attempts = 0;
34
+ /** Timer handle for scheduled reconnection */
35
+ timer = null;
36
+ /** Whether reconnection was cancelled */
37
+ cancelled = false;
38
+ constructor(config, logger, emitter) {
39
+ this.config = config;
40
+ this.logger = logger;
41
+ this.emitter = emitter;
42
+ }
43
+ /**
44
+ * Calculates the delay for the next reconnection attempt.
45
+ *
46
+ * Uses exponential backoff with 30% jitter to prevent
47
+ * thundering herd problems.
48
+ *
49
+ * @param attempt - Current attempt number (1-based)
50
+ * @returns Delay in milliseconds
51
+ */
52
+ calculateDelay(attempt) {
53
+ const baseDelay = Math.min(this.config.initialDelay * Math.pow(2, attempt - 1), this.config.maxDelay);
54
+ // Add 0-30% jitter
55
+ return baseDelay + Math.random() * 0.3 * baseDelay;
56
+ }
57
+ /**
58
+ * Checks if more reconnection attempts are allowed.
59
+ *
60
+ * @returns true if another attempt can be made
61
+ */
62
+ canRetry() {
63
+ if (!this.config.enabled || this.cancelled)
64
+ return false;
65
+ if (this.config.maxAttempts === 0)
66
+ return true; // Unlimited
67
+ return this.attempts < this.config.maxAttempts;
68
+ }
69
+ /**
70
+ * Schedules a reconnection attempt.
71
+ *
72
+ * @param connectFn - Async function that performs the connection
73
+ */
74
+ schedule(connectFn) {
75
+ if (!this.canRetry()) {
76
+ this.logger.error('Max reconnection attempts reached', { attempts: this.attempts });
77
+ this.emitter.emit('reconnect_failed', new errors_1.ConnectionError('Max reconnection attempts reached', 'RECONNECTION_FAILED'));
78
+ return;
79
+ }
80
+ this.attempts++;
81
+ const delay = this.calculateDelay(this.attempts);
82
+ this.logger.info('Scheduling reconnection', { attempt: this.attempts, delay: Math.round(delay) });
83
+ this.emitter.emit('reconnecting', { attempt: this.attempts, delay });
84
+ this.timer = setTimeout(async () => {
85
+ try {
86
+ await connectFn();
87
+ this.logger.info('Reconnected successfully', { attempt: this.attempts });
88
+ this.emitter.emit('reconnected');
89
+ }
90
+ catch (error) {
91
+ this.logger.warn('Reconnection attempt failed', {
92
+ attempt: this.attempts,
93
+ error: error instanceof Error ? error.message : error,
94
+ });
95
+ // Schedule next attempt
96
+ this.schedule(connectFn);
97
+ }
98
+ }, delay);
99
+ }
100
+ /**
101
+ * Resets the reconnection state after successful connection.
102
+ */
103
+ reset() {
104
+ this.attempts = 0;
105
+ this.cancelled = false;
106
+ }
107
+ /**
108
+ * Cancels any pending reconnection attempt.
109
+ */
110
+ cancel() {
111
+ this.cancelled = true;
112
+ if (this.timer) {
113
+ clearTimeout(this.timer);
114
+ this.timer = null;
115
+ }
116
+ }
117
+ /**
118
+ * Gets the current attempt count.
119
+ */
120
+ getAttempts() {
121
+ return this.attempts;
122
+ }
123
+ }
124
+ exports.ReconnectManager = ReconnectManager;
125
+ /**
126
+ * Waits for a reconnection to complete.
127
+ *
128
+ * @param emitter - Event emitter that fires reconnection events
129
+ * @param timeout - Maximum time to wait (ms)
130
+ * @returns Promise that resolves when reconnected
131
+ * @throws ConnectionError if reconnection fails or times out
132
+ */
133
+ function waitForReconnection(emitter, timeout) {
134
+ return new Promise((resolve, reject) => {
135
+ const timer = setTimeout(() => {
136
+ emitter.removeListener('reconnected', onReconnect);
137
+ emitter.removeListener('reconnect_failed', onFailed);
138
+ reject(new errors_1.ConnectionError('Reconnection timeout', 'RECONNECTION_FAILED'));
139
+ }, timeout);
140
+ const onReconnect = () => {
141
+ clearTimeout(timer);
142
+ emitter.removeListener('reconnect_failed', onFailed);
143
+ resolve();
144
+ };
145
+ const onFailed = (err) => {
146
+ clearTimeout(timer);
147
+ emitter.removeListener('reconnected', onReconnect);
148
+ reject(err);
149
+ };
150
+ emitter.once('reconnected', onReconnect);
151
+ emitter.once('reconnect_failed', onFailed);
152
+ });
153
+ }
154
+ //# sourceMappingURL=reconnect.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"reconnect.js","sourceRoot":"","sources":["../../src/client/reconnect.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,106 @@
1
+ /**
2
+ * TCP protocol handler for FlashQ client.
3
+ *
4
+ * Handles buffer management, message framing, and protocol serialization
5
+ * for both JSON (newline-delimited) and binary (MessagePack) protocols.
6
+ */
7
+ /**
8
+ * Pending request tracking for async responses.
9
+ */
10
+ export interface PendingRequest {
11
+ /** Resolves with the response data */
12
+ resolve: (value: unknown) => void;
13
+ /** Rejects with an error */
14
+ reject: (error: Error) => void;
15
+ /** Timeout timer handle */
16
+ timer: ReturnType<typeof setTimeout>;
17
+ }
18
+ /**
19
+ * Handles JSON protocol buffer parsing.
20
+ *
21
+ * Accumulates incoming data chunks and extracts complete
22
+ * newline-delimited JSON messages.
23
+ */
24
+ export declare class JsonBufferHandler {
25
+ /** Accumulated string chunks waiting to be joined */
26
+ private chunks;
27
+ /** Partial line from previous parse */
28
+ private remainder;
29
+ /**
30
+ * Appends new data to the buffer.
31
+ *
32
+ * @param data - Raw data from socket
33
+ */
34
+ append(data: Buffer | string): void;
35
+ /**
36
+ * Extracts all complete lines from the buffer.
37
+ *
38
+ * Complete lines are separated by newlines. Any partial line
39
+ * at the end is kept for the next call.
40
+ *
41
+ * @returns Array of complete JSON strings
42
+ */
43
+ extractLines(): string[];
44
+ /**
45
+ * Resets the buffer state.
46
+ */
47
+ reset(): void;
48
+ }
49
+ /**
50
+ * Handles binary (MessagePack) protocol buffer parsing.
51
+ *
52
+ * Each message is framed with a 4-byte big-endian length prefix
53
+ * followed by the MessagePack-encoded payload.
54
+ */
55
+ export declare class BinaryBufferHandler {
56
+ /** Accumulated binary data */
57
+ private buffer;
58
+ /**
59
+ * Appends new data to the buffer.
60
+ *
61
+ * @param data - Raw data from socket
62
+ */
63
+ append(data: Buffer): void;
64
+ /**
65
+ * Extracts all complete frames from the buffer.
66
+ *
67
+ * Frame format: [4-byte length BE][payload]
68
+ *
69
+ * @returns Array of decoded message objects
70
+ */
71
+ extractFrames(): Record<string, unknown>[];
72
+ /**
73
+ * Resets the buffer state.
74
+ */
75
+ reset(): void;
76
+ }
77
+ /**
78
+ * Encodes a command for TCP transmission.
79
+ *
80
+ * @param command - Command object to encode
81
+ * @param reqId - Request ID to include
82
+ * @param useBinary - Whether to use MessagePack encoding
83
+ * @returns Buffer ready for socket.write()
84
+ */
85
+ export declare function encodeCommand(command: Record<string, unknown>, reqId: string, useBinary: boolean): Buffer;
86
+ /**
87
+ * Parses a JSON response from a string.
88
+ *
89
+ * @param line - JSON string to parse
90
+ * @returns Parsed response object or null if invalid
91
+ */
92
+ export declare function parseJsonResponse(line: string): Record<string, unknown> | null;
93
+ /**
94
+ * Generates a unique request ID.
95
+ *
96
+ * Format: "r{incrementing number}"
97
+ *
98
+ * @returns Unique request ID string
99
+ */
100
+ export declare function generateRequestId(): string;
101
+ /**
102
+ * Resets the request ID counter.
103
+ * Primarily used for testing.
104
+ */
105
+ export declare function resetRequestIdCounter(): void;
106
+ //# sourceMappingURL=handler.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"handler.d.ts","sourceRoot":"","sources":["../../../src/client/tcp/handler.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAIH;;GAEG;AACH,MAAM,WAAW,cAAc;IAC7B,sCAAsC;IACtC,OAAO,EAAE,CAAC,KAAK,EAAE,OAAO,KAAK,IAAI,CAAC;IAClC,4BAA4B;IAC5B,MAAM,EAAE,CAAC,KAAK,EAAE,KAAK,KAAK,IAAI,CAAC;IAC/B,2BAA2B;IAC3B,KAAK,EAAE,UAAU,CAAC,OAAO,UAAU,CAAC,CAAC;CACtC;AAED;;;;;GAKG;AACH,qBAAa,iBAAiB;IAC5B,qDAAqD;IACrD,OAAO,CAAC,MAAM,CAAgB;IAC9B,uCAAuC;IACvC,OAAO,CAAC,SAAS,CAAM;IAEvB;;;;OAIG;IACH,MAAM,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,GAAG,IAAI;IAInC;;;;;;;OAOG;IACH,YAAY,IAAI,MAAM,EAAE;IAWxB;;OAEG;IACH,KAAK,IAAI,IAAI;CAId;AAED;;;;;GAKG;AACH,qBAAa,mBAAmB;IAC9B,8BAA8B;IAC9B,OAAO,CAAC,MAAM,CAA2B;IAEzC;;;;OAIG;IACH,MAAM,CAAC,IAAI,EAAE,MAAM,GAAG,IAAI;IAI1B;;;;;;OAMG;IACH,aAAa,IAAI,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EAAE;IAgB1C;;OAEG;IACH,KAAK,IAAI,IAAI;CAGd;AAED;;;;;;;GAOG;AACH,wBAAgB,aAAa,CAC3B,OAAO,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EAChC,KAAK,EAAE,MAAM,EACb,SAAS,EAAE,OAAO,GACjB,MAAM,CAYR;AAED;;;;;GAKG;AACH,wBAAgB,iBAAiB,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,IAAI,CAM9E;AAKD;;;;;;GAMG;AACH,wBAAgB,iBAAiB,IAAI,MAAM,CAE1C;AAED;;;GAGG;AACH,wBAAgB,qBAAqB,IAAI,IAAI,CAE5C"}
@@ -0,0 +1,155 @@
1
+ "use strict";
2
+ /**
3
+ * TCP protocol handler for FlashQ client.
4
+ *
5
+ * Handles buffer management, message framing, and protocol serialization
6
+ * for both JSON (newline-delimited) and binary (MessagePack) protocols.
7
+ */
8
+ Object.defineProperty(exports, "__esModule", { value: true });
9
+ exports.BinaryBufferHandler = exports.JsonBufferHandler = void 0;
10
+ exports.encodeCommand = encodeCommand;
11
+ exports.parseJsonResponse = parseJsonResponse;
12
+ exports.generateRequestId = generateRequestId;
13
+ exports.resetRequestIdCounter = resetRequestIdCounter;
14
+ const msgpack_1 = require("@msgpack/msgpack");
15
+ /**
16
+ * Handles JSON protocol buffer parsing.
17
+ *
18
+ * Accumulates incoming data chunks and extracts complete
19
+ * newline-delimited JSON messages.
20
+ */
21
+ class JsonBufferHandler {
22
+ /** Accumulated string chunks waiting to be joined */
23
+ chunks = [];
24
+ /** Partial line from previous parse */
25
+ remainder = '';
26
+ /**
27
+ * Appends new data to the buffer.
28
+ *
29
+ * @param data - Raw data from socket
30
+ */
31
+ append(data) {
32
+ this.chunks.push(typeof data === 'string' ? data : data.toString());
33
+ }
34
+ /**
35
+ * Extracts all complete lines from the buffer.
36
+ *
37
+ * Complete lines are separated by newlines. Any partial line
38
+ * at the end is kept for the next call.
39
+ *
40
+ * @returns Array of complete JSON strings
41
+ */
42
+ extractLines() {
43
+ // Join chunks only when processing (more efficient than += on each chunk)
44
+ const fullBuffer = this.remainder + this.chunks.join('');
45
+ this.chunks.length = 0; // Clear array without reallocating
46
+ const lines = fullBuffer.split('\n');
47
+ this.remainder = lines.pop() ?? '';
48
+ return lines.filter((line) => line.trim());
49
+ }
50
+ /**
51
+ * Resets the buffer state.
52
+ */
53
+ reset() {
54
+ this.chunks.length = 0;
55
+ this.remainder = '';
56
+ }
57
+ }
58
+ exports.JsonBufferHandler = JsonBufferHandler;
59
+ /**
60
+ * Handles binary (MessagePack) protocol buffer parsing.
61
+ *
62
+ * Each message is framed with a 4-byte big-endian length prefix
63
+ * followed by the MessagePack-encoded payload.
64
+ */
65
+ class BinaryBufferHandler {
66
+ /** Accumulated binary data */
67
+ buffer = Buffer.alloc(0);
68
+ /**
69
+ * Appends new data to the buffer.
70
+ *
71
+ * @param data - Raw data from socket
72
+ */
73
+ append(data) {
74
+ this.buffer = Buffer.concat([this.buffer, data]);
75
+ }
76
+ /**
77
+ * Extracts all complete frames from the buffer.
78
+ *
79
+ * Frame format: [4-byte length BE][payload]
80
+ *
81
+ * @returns Array of decoded message objects
82
+ */
83
+ extractFrames() {
84
+ const frames = [];
85
+ while (this.buffer.length >= 4) {
86
+ const len = this.buffer.readUInt32BE(0);
87
+ if (this.buffer.length < 4 + len)
88
+ break;
89
+ const frameData = this.buffer.subarray(4, 4 + len);
90
+ this.buffer = this.buffer.subarray(4 + len);
91
+ frames.push((0, msgpack_1.decode)(frameData));
92
+ }
93
+ return frames;
94
+ }
95
+ /**
96
+ * Resets the buffer state.
97
+ */
98
+ reset() {
99
+ this.buffer = Buffer.alloc(0);
100
+ }
101
+ }
102
+ exports.BinaryBufferHandler = BinaryBufferHandler;
103
+ /**
104
+ * Encodes a command for TCP transmission.
105
+ *
106
+ * @param command - Command object to encode
107
+ * @param reqId - Request ID to include
108
+ * @param useBinary - Whether to use MessagePack encoding
109
+ * @returns Buffer ready for socket.write()
110
+ */
111
+ function encodeCommand(command, reqId, useBinary) {
112
+ const payload = { ...command, reqId };
113
+ if (useBinary) {
114
+ const encoded = (0, msgpack_1.encode)(payload);
115
+ const frame = Buffer.alloc(4 + encoded.length);
116
+ frame.writeUInt32BE(encoded.length, 0);
117
+ frame.set(encoded, 4);
118
+ return frame;
119
+ }
120
+ return Buffer.from(JSON.stringify(payload) + '\n');
121
+ }
122
+ /**
123
+ * Parses a JSON response from a string.
124
+ *
125
+ * @param line - JSON string to parse
126
+ * @returns Parsed response object or null if invalid
127
+ */
128
+ function parseJsonResponse(line) {
129
+ try {
130
+ return JSON.parse(line);
131
+ }
132
+ catch {
133
+ return null;
134
+ }
135
+ }
136
+ /** Counter for generating unique request IDs */
137
+ let requestIdCounter = 0;
138
+ /**
139
+ * Generates a unique request ID.
140
+ *
141
+ * Format: "r{incrementing number}"
142
+ *
143
+ * @returns Unique request ID string
144
+ */
145
+ function generateRequestId() {
146
+ return `r${++requestIdCounter}`;
147
+ }
148
+ /**
149
+ * Resets the request ID counter.
150
+ * Primarily used for testing.
151
+ */
152
+ function resetRequestIdCounter() {
153
+ requestIdCounter = 0;
154
+ }
155
+ //# sourceMappingURL=handler.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"handler.js","sourceRoot":"","sources":["../../../src/client/tcp/handler.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,5 @@
1
+ /**
2
+ * TCP protocol module exports.
3
+ */
4
+ export { type PendingRequest, JsonBufferHandler, BinaryBufferHandler, encodeCommand, parseJsonResponse, generateRequestId, resetRequestIdCounter, } from './handler';
5
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/client/tcp/index.ts"],"names":[],"mappings":"AAAA;;GAEG;AACH,OAAO,EACL,KAAK,cAAc,EACnB,iBAAiB,EACjB,mBAAmB,EACnB,aAAa,EACb,iBAAiB,EACjB,iBAAiB,EACjB,qBAAqB,GACtB,MAAM,WAAW,CAAC"}
@@ -0,0 +1,14 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.resetRequestIdCounter = exports.generateRequestId = exports.parseJsonResponse = exports.encodeCommand = exports.BinaryBufferHandler = exports.JsonBufferHandler = void 0;
4
+ /**
5
+ * TCP protocol module exports.
6
+ */
7
+ var handler_1 = require("./handler");
8
+ Object.defineProperty(exports, "JsonBufferHandler", { enumerable: true, get: function () { return handler_1.JsonBufferHandler; } });
9
+ Object.defineProperty(exports, "BinaryBufferHandler", { enumerable: true, get: function () { return handler_1.BinaryBufferHandler; } });
10
+ Object.defineProperty(exports, "encodeCommand", { enumerable: true, get: function () { return handler_1.encodeCommand; } });
11
+ Object.defineProperty(exports, "parseJsonResponse", { enumerable: true, get: function () { return handler_1.parseJsonResponse; } });
12
+ Object.defineProperty(exports, "generateRequestId", { enumerable: true, get: function () { return handler_1.generateRequestId; } });
13
+ Object.defineProperty(exports, "resetRequestIdCounter", { enumerable: true, get: function () { return handler_1.resetRequestIdCounter; } });
14
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../../../src/client/tcp/index.ts"],"names":[],"mappings":";;;AAAA;;GAEG;AACH,qCAQmB;AANjB,4GAAA,iBAAiB,OAAA;AACjB,8GAAA,mBAAmB,OAAA;AACnB,wGAAA,aAAa,OAAA;AACb,4GAAA,iBAAiB,OAAA;AACjB,4GAAA,iBAAiB,OAAA;AACjB,gHAAA,qBAAqB,OAAA"}
@@ -0,0 +1,77 @@
1
+ /**
2
+ * Input validation utilities for FlashQ client.
3
+ *
4
+ * Provides validation functions for queue names, job data sizes,
5
+ * and batch operation limits.
6
+ */
7
+ /** Maximum allowed job data size in bytes (1MB) */
8
+ export declare const MAX_JOB_DATA_SIZE: number;
9
+ /** Maximum number of jobs per batch operation */
10
+ export declare const MAX_BATCH_SIZE = 1000;
11
+ /**
12
+ * Validates a queue name against naming rules.
13
+ *
14
+ * Queue names must:
15
+ * - Be non-empty strings
16
+ * - Contain only alphanumeric characters, underscores, hyphens, or dots
17
+ * - Be between 1 and 256 characters long
18
+ *
19
+ * @param queue - The queue name to validate
20
+ * @throws ValidationError if the queue name is invalid
21
+ *
22
+ * @example
23
+ * ```typescript
24
+ * validateQueueName('my-queue'); // OK
25
+ * validateQueueName('queue.name'); // OK
26
+ * validateQueueName('queue name'); // Throws: contains space
27
+ * validateQueueName(''); // Throws: empty
28
+ * ```
29
+ */
30
+ export declare function validateQueueName(queue: string): void;
31
+ /**
32
+ * Validates that job data does not exceed the maximum allowed size.
33
+ *
34
+ * The size is calculated by JSON-stringifying the data and measuring
35
+ * the resulting string length in bytes.
36
+ *
37
+ * @param data - The job data to validate
38
+ * @throws ValidationError if the data exceeds MAX_JOB_DATA_SIZE
39
+ *
40
+ * @example
41
+ * ```typescript
42
+ * validateJobDataSize({ small: 'data' }); // OK
43
+ * validateJobDataSize(largeBuffer); // Throws if > 1MB
44
+ * ```
45
+ */
46
+ export declare function validateJobDataSize(data: unknown): void;
47
+ /**
48
+ * Validates batch operation size.
49
+ *
50
+ * @param count - Number of items in the batch
51
+ * @param operation - Name of the operation for error messages
52
+ * @throws ValidationError if count exceeds MAX_BATCH_SIZE
53
+ *
54
+ * @example
55
+ * ```typescript
56
+ * validateBatchSize(100, 'push'); // OK
57
+ * validateBatchSize(1500, 'push'); // Throws: exceeds 1000
58
+ * ```
59
+ */
60
+ export declare function validateBatchSize(count: number, operation: string): void;
61
+ /**
62
+ * Validates a job ID is a positive integer.
63
+ *
64
+ * @param jobId - The job ID to validate
65
+ * @throws ValidationError if jobId is not a positive integer
66
+ */
67
+ export declare function validateJobId(jobId: number): void;
68
+ /**
69
+ * Validates a timeout value is within acceptable bounds.
70
+ *
71
+ * @param timeout - Timeout in milliseconds
72
+ * @param min - Minimum allowed value (default: 0)
73
+ * @param max - Maximum allowed value (default: 10 minutes)
74
+ * @throws ValidationError if timeout is out of bounds
75
+ */
76
+ export declare function validateTimeout(timeout: number, min?: number, max?: number): void;
77
+ //# sourceMappingURL=validation.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"validation.d.ts","sourceRoot":"","sources":["../../src/client/validation.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAIH,mDAAmD;AACnD,eAAO,MAAM,iBAAiB,QAAc,CAAC;AAE7C,iDAAiD;AACjD,eAAO,MAAM,cAAc,OAAO,CAAC;AAKnC;;;;;;;;;;;;;;;;;;GAkBG;AACH,wBAAgB,iBAAiB,CAAC,KAAK,EAAE,MAAM,GAAG,IAAI,CAUrD;AAED;;;;;;;;;;;;;;GAcG;AACH,wBAAgB,mBAAmB,CAAC,IAAI,EAAE,OAAO,GAAG,IAAI,CAQvD;AAED;;;;;;;;;;;;GAYG;AACH,wBAAgB,iBAAiB,CAAC,KAAK,EAAE,MAAM,EAAE,SAAS,EAAE,MAAM,GAAG,IAAI,CAOxE;AAED;;;;;GAKG;AACH,wBAAgB,aAAa,CAAC,KAAK,EAAE,MAAM,GAAG,IAAI,CAIjD;AAED;;;;;;;GAOG;AACH,wBAAgB,eAAe,CAAC,OAAO,EAAE,MAAM,EAAE,GAAG,SAAI,EAAE,GAAG,SAAS,GAAG,IAAI,CAO5E"}