apache-iggy 0.10.0-edge.2 → 0.10.0-edge.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.
- package/README.md +7 -1
- package/dist/client/client.config.d.ts +7 -0
- package/dist/client/client.config.js +17 -0
- package/dist/client/client.config.test.js +18 -1
- package/dist/client/client.connection.js +2 -2
- package/dist/client/client.connection.test.js +2 -2
- package/dist/client/client.frame.test.js +2 -2
- package/dist/client/client.socket.js +3 -0
- package/dist/client/client.socket.test.js +3 -3
- package/dist/client/client.type.d.ts +6 -1
- package/dist/debug.js +2 -2
- package/dist/e2e/tcp.topic.e2e.js +1 -2
- package/dist/wire/command-set.d.ts +1 -0
- package/dist/wire/command-set.js +3 -1
- package/dist/wire/command.code.d.ts +1 -2
- package/dist/wire/command.code.js +1 -2
- package/dist/wire/error.code.js +88 -13
- package/dist/wire/error.code.test.js +7 -7
- package/dist/wire/message/header.type.d.ts +1 -1
- package/dist/wire/message/iggy-header.utils.d.ts +95 -24
- package/dist/wire/message/iggy-header.utils.js +90 -43
- package/dist/wire/message/message-batch.test.d.ts +2 -0
- package/dist/wire/message/message-batch.test.js +173 -0
- package/dist/wire/message/message.utils.d.ts +25 -22
- package/dist/wire/message/message.utils.js +81 -50
- package/dist/wire/message/poll-messages.command.test.js +1 -1
- package/dist/wire/message/poll.utils.d.ts +36 -3
- package/dist/wire/message/poll.utils.js +62 -27
- package/dist/wire/message/send-messages.command.test.js +3 -1
- package/dist/wire/offset/delete-offset.command.js +2 -2
- package/dist/wire/offset/offset.utils.d.ts +10 -0
- package/dist/wire/offset/offset.utils.js +17 -1
- package/dist/wire/options.utils.d.ts +62 -0
- package/dist/wire/options.utils.js +144 -0
- package/dist/wire/options.utils.test.d.ts +2 -0
- package/dist/wire/options.utils.test.js +102 -0
- package/dist/wire/stream/stream.utils.d.ts +3 -0
- package/dist/wire/stream/stream.utils.js +4 -2
- package/dist/wire/system/describe-options.command.d.ts +33 -0
- package/dist/wire/system/describe-options.command.js +63 -0
- package/dist/wire/system/index.d.ts +1 -0
- package/dist/wire/system/index.js +1 -0
- package/dist/wire/topic/create-topic.command.d.ts +31 -5
- package/dist/wire/topic/create-topic.command.js +52 -10
- package/dist/wire/topic/create-topic.command.test.js +63 -12
- package/dist/wire/topic/topic.utils.d.ts +20 -4
- package/dist/wire/topic/topic.utils.js +35 -15
- package/dist/wire/topic/topic.utils.test.js +50 -13
- package/dist/wire/topic/update-topic.command.d.ts +17 -5
- package/dist/wire/topic/update-topic.command.js +27 -9
- package/dist/wire/user/user.utils.d.ts +3 -0
- package/dist/wire/user/user.utils.js +4 -1
- package/dist/wire/vsr/header.d.ts +2 -2
- package/dist/wire/vsr/header.js +3 -3
- package/dist/wire/vsr/header.test.js +3 -3
- package/dist/wire/vsr/operation.d.ts +0 -2
- package/dist/wire/vsr/operation.js +2 -8
- package/dist/wire/vsr/operation.test.js +2 -6
- package/dist/wire/vsr/reply.js +3 -3
- package/dist/wire/vsr/reply.test.js +4 -4
- package/dist/wire/vsr/vsr.test.js +1 -1
- package/package.json +2 -1
package/README.md
CHANGED
|
@@ -71,7 +71,13 @@ new session. Transient not-committed responses retry the exact encoded request
|
|
|
71
71
|
within one bounded deadline. A disconnected mutation is never replayed under a
|
|
72
72
|
new session.
|
|
73
73
|
|
|
74
|
-
|
|
74
|
+
The client pings every `heartbeatInterval` milliseconds, 5000 by default, which
|
|
75
|
+
keeps an idle session alive when the server's `[heartbeat]` eviction is enabled.
|
|
76
|
+
The server evicts a connection silent for 36 s, which is 1.2 x its 30 s
|
|
77
|
+
heartbeat interval. Raising the client interval past that window, or setting it
|
|
78
|
+
to 0 to disable client heartbeats, exposes an idle consumer-group member to
|
|
79
|
+
eviction; a connection holding no group membership is left alone. Any other
|
|
80
|
+
unusable value is rejected instead of silently disabling the heartbeat.
|
|
75
81
|
|
|
76
82
|
`sendBinaryRequest(code, payload)` sends an arbitrary command code. Known replicated commands use their registered operation, while unknown codes reach the server as non-replicated requests and are rejected by servers that do not register them.
|
|
77
83
|
|
|
@@ -1,4 +1,11 @@
|
|
|
1
1
|
import type { ClientConfig } from './client.type.js';
|
|
2
2
|
export declare const DEFAULT_MAX_RESPONSE_FRAME_SIZE: number;
|
|
3
|
+
/**
|
|
4
|
+
* The server evicts a connection silent for 36 s (1.2 x its 30 s heartbeat
|
|
5
|
+
* interval). 5 s matches the other SDKs and survives several skipped pings.
|
|
6
|
+
*/
|
|
7
|
+
export declare const DEFAULT_HEARTBEAT_INTERVAL: number;
|
|
8
|
+
/** Node's largest timer delay: setInterval clamps anything above it to 1 ms. */
|
|
9
|
+
export declare const MAX_HEARTBEAT_INTERVAL = 2147483647;
|
|
3
10
|
export declare const normalizeClientConfig: (config: ClientConfig) => ClientConfig;
|
|
4
11
|
//# sourceMappingURL=client.config.d.ts.map
|
|
@@ -15,6 +15,13 @@
|
|
|
15
15
|
// specific language governing permissions and limitations
|
|
16
16
|
// under the License.
|
|
17
17
|
export const DEFAULT_MAX_RESPONSE_FRAME_SIZE = 64 * 1024 * 1024;
|
|
18
|
+
/**
|
|
19
|
+
* The server evicts a connection silent for 36 s (1.2 x its 30 s heartbeat
|
|
20
|
+
* interval). 5 s matches the other SDKs and survives several skipped pings.
|
|
21
|
+
*/
|
|
22
|
+
export const DEFAULT_HEARTBEAT_INTERVAL = 5 * 1000;
|
|
23
|
+
/** Node's largest timer delay: setInterval clamps anything above it to 1 ms. */
|
|
24
|
+
export const MAX_HEARTBEAT_INTERVAL = 2_147_483_647;
|
|
18
25
|
export const normalizeClientConfig = (config) => {
|
|
19
26
|
const maxResponseFrameSize = config.maxResponseFrameSize ?? DEFAULT_MAX_RESPONSE_FRAME_SIZE;
|
|
20
27
|
if (!Number.isSafeInteger(maxResponseFrameSize) ||
|
|
@@ -22,10 +29,20 @@ export const normalizeClientConfig = (config) => {
|
|
|
22
29
|
throw new TypeError('maxResponseFrameSize must be a safe integer of at least 256 bytes');
|
|
23
30
|
if ((config.poolSize?.min ?? 1) > 1 || (config.poolSize?.max ?? 1) > 1)
|
|
24
31
|
throw new TypeError('VSR clients currently support exactly one pooled connection');
|
|
32
|
+
// Only 0 disables the heartbeat. Anything else unusable has to throw here:
|
|
33
|
+
// the default below is nullish-only, so a value that is negative, fractional
|
|
34
|
+
// or above MAX_HEARTBEAT_INTERVAL reaches setInterval, which clamps it to
|
|
35
|
+
// 1 ms and floods the server.
|
|
36
|
+
const heartbeatInterval = config.heartbeatInterval ?? DEFAULT_HEARTBEAT_INTERVAL;
|
|
37
|
+
if (!Number.isSafeInteger(heartbeatInterval) ||
|
|
38
|
+
heartbeatInterval < 0 ||
|
|
39
|
+
heartbeatInterval > MAX_HEARTBEAT_INTERVAL)
|
|
40
|
+
throw new TypeError(`heartbeatInterval must be a safe integer of milliseconds between 0 and ${MAX_HEARTBEAT_INTERVAL} (0 disables heartbeats)`);
|
|
25
41
|
return {
|
|
26
42
|
...config,
|
|
27
43
|
options: { ...config.options },
|
|
28
44
|
maxResponseFrameSize,
|
|
45
|
+
heartbeatInterval,
|
|
29
46
|
poolSize: { min: 1, max: 1 }
|
|
30
47
|
};
|
|
31
48
|
};
|
|
@@ -16,7 +16,7 @@
|
|
|
16
16
|
// under the License.
|
|
17
17
|
import assert from 'node:assert/strict';
|
|
18
18
|
import { describe, it } from 'node:test';
|
|
19
|
-
import { DEFAULT_MAX_RESPONSE_FRAME_SIZE, normalizeClientConfig } from './client.config.js';
|
|
19
|
+
import { DEFAULT_HEARTBEAT_INTERVAL, DEFAULT_MAX_RESPONSE_FRAME_SIZE, normalizeClientConfig } from './client.config.js';
|
|
20
20
|
const config = () => ({
|
|
21
21
|
transport: 'TCP',
|
|
22
22
|
options: { host: '127.0.0.1', port: 8090 },
|
|
@@ -27,6 +27,23 @@ describe('normalizeClientConfig', () => {
|
|
|
27
27
|
const normalized = normalizeClientConfig(config());
|
|
28
28
|
assert.equal(normalized.maxResponseFrameSize, DEFAULT_MAX_RESPONSE_FRAME_SIZE);
|
|
29
29
|
});
|
|
30
|
+
it('enables the heartbeat by default and honours an explicit interval', () => {
|
|
31
|
+
assert.equal(DEFAULT_HEARTBEAT_INTERVAL, 5000);
|
|
32
|
+
assert.equal(normalizeClientConfig(config()).heartbeatInterval, DEFAULT_HEARTBEAT_INTERVAL);
|
|
33
|
+
assert.equal(normalizeClientConfig({ ...config(), heartbeatInterval: 1000 })
|
|
34
|
+
.heartbeatInterval, 1000);
|
|
35
|
+
assert.equal(normalizeClientConfig({ ...config(), heartbeatInterval: 0 })
|
|
36
|
+
.heartbeatInterval, 0);
|
|
37
|
+
});
|
|
38
|
+
it('rejects unusable heartbeat intervals', () => {
|
|
39
|
+
for (const heartbeatInterval of [
|
|
40
|
+
-1, -5000, Number.NaN, 1.5, 2_147_483_648, 2 ** 32, Number.MAX_VALUE
|
|
41
|
+
])
|
|
42
|
+
assert.throws(() => normalizeClientConfig({
|
|
43
|
+
...config(),
|
|
44
|
+
heartbeatInterval
|
|
45
|
+
}), /heartbeatInterval/);
|
|
46
|
+
});
|
|
30
47
|
it('restricts the client to one pooled connection', () => {
|
|
31
48
|
const normalized = normalizeClientConfig(config());
|
|
32
49
|
assert.deepEqual(normalized.poolSize, { min: 1, max: 1 });
|
|
@@ -21,7 +21,7 @@ import { connect as TLSConnect } from 'node:tls';
|
|
|
21
21
|
import { debug } from './client.debug.js';
|
|
22
22
|
import { DEFAULT_MAX_RESPONSE_FRAME_SIZE } from './client.config.js';
|
|
23
23
|
import { ProtocolFrameError, ResponseFrameDecoder } from './client.frame.js';
|
|
24
|
-
import {
|
|
24
|
+
import { Command, peekCommand } from '../wire/vsr/header.js';
|
|
25
25
|
import { evictionError } from '../wire/vsr/reply.js';
|
|
26
26
|
/**
|
|
27
27
|
* Creates a TCP socket connection.
|
|
@@ -360,7 +360,7 @@ export class IggyConnection extends EventEmitter {
|
|
|
360
360
|
debug('ONDATA', typeof data, Buffer.isBuffer(data), data?.length, this.responseDecoder.hasBufferedData);
|
|
361
361
|
try {
|
|
362
362
|
for (const response of this.responseDecoder.push(data)) {
|
|
363
|
-
if (peekCommand(response) ===
|
|
363
|
+
if (peekCommand(response) === Command.Eviction)
|
|
364
364
|
this.emit('eviction', evictionError(response));
|
|
365
365
|
else
|
|
366
366
|
this.emit('response', response);
|
|
@@ -21,7 +21,7 @@ import { createServer, } from 'node:net';
|
|
|
21
21
|
import { describe, it } from 'node:test';
|
|
22
22
|
import { ProtocolFrameError } from './client.frame.js';
|
|
23
23
|
import { IggyConnection } from './client.connection.js';
|
|
24
|
-
import {
|
|
24
|
+
import { Command, HEADER_SIZE, REPLY_OFFSET } from '../wire/vsr/header.js';
|
|
25
25
|
const FRAME_LIMIT = 2 * HEADER_SIZE;
|
|
26
26
|
const startServer = async () => {
|
|
27
27
|
const server = createServer();
|
|
@@ -42,7 +42,7 @@ const connectionConfig = (server) => ({
|
|
|
42
42
|
const replyFrame = (body) => {
|
|
43
43
|
const frame = Buffer.alloc(HEADER_SIZE + body.length);
|
|
44
44
|
frame.writeUInt32LE(frame.length, REPLY_OFFSET.size);
|
|
45
|
-
frame.writeUInt8(
|
|
45
|
+
frame.writeUInt8(Command.Reply, REPLY_OFFSET.command);
|
|
46
46
|
body.copy(frame, HEADER_SIZE);
|
|
47
47
|
return frame;
|
|
48
48
|
};
|
|
@@ -17,12 +17,12 @@
|
|
|
17
17
|
import assert from 'node:assert/strict';
|
|
18
18
|
import { describe, it } from 'node:test';
|
|
19
19
|
import { extractResponseFrames, ProtocolFrameError, ResponseFrameDecoder } from './client.frame.js';
|
|
20
|
-
import {
|
|
20
|
+
import { Command, HEADER_SIZE, REPLY_OFFSET } from '../wire/vsr/header.js';
|
|
21
21
|
const LIMIT = 1024;
|
|
22
22
|
const vsrFrame = (body) => {
|
|
23
23
|
const frame = Buffer.alloc(HEADER_SIZE + body.length);
|
|
24
24
|
frame.writeUInt32LE(frame.length, REPLY_OFFSET.size);
|
|
25
|
-
frame.writeUInt8(
|
|
25
|
+
frame.writeUInt8(Command.Reply, REPLY_OFFSET.command);
|
|
26
26
|
body.copy(frame, HEADER_SIZE);
|
|
27
27
|
return frame;
|
|
28
28
|
};
|
|
@@ -519,6 +519,9 @@ export class CommandResponseStream extends EventEmitter {
|
|
|
519
519
|
}
|
|
520
520
|
}
|
|
521
521
|
}, interval);
|
|
522
|
+
// A pending heartbeat must not be the reason the process stays up: a script
|
|
523
|
+
// that never calls destroy() would otherwise hang on exit.
|
|
524
|
+
this.heartbeatIntervalHandler.unref();
|
|
522
525
|
}
|
|
523
526
|
/**
|
|
524
527
|
* Returns the underlying socket as a readable stream.
|
|
@@ -23,7 +23,7 @@ import { describe, it } from 'node:test';
|
|
|
23
23
|
import { createServer as createTlsServer } from 'node:tls';
|
|
24
24
|
import { COMMAND_CODE } from '../wire/command.code.js';
|
|
25
25
|
import { ResponseError } from '../wire/error.utils.js';
|
|
26
|
-
import {
|
|
26
|
+
import { Command, EVICTION_OFFSET, EvictionReason, HEADER_SIZE, REPLY_OFFSET, REQUEST_OFFSET } from '../wire/vsr/header.js';
|
|
27
27
|
import { Operation } from '../wire/vsr/operation.js';
|
|
28
28
|
import { VsrEvictionError } from '../wire/vsr/reply.js';
|
|
29
29
|
import { CommandResponseStream } from './client.socket.js';
|
|
@@ -64,7 +64,7 @@ const startVsrServer = async (handler, transport = 'TCP') => {
|
|
|
64
64
|
const replyFrame = (operation, body = Buffer.alloc(0), status = 0) => {
|
|
65
65
|
const frame = Buffer.alloc(HEADER_SIZE + body.length);
|
|
66
66
|
frame.writeUInt32LE(frame.length, REPLY_OFFSET.size);
|
|
67
|
-
frame.writeUInt8(
|
|
67
|
+
frame.writeUInt8(Command.Reply, REPLY_OFFSET.command);
|
|
68
68
|
frame.writeUInt8(operation, REPLY_OFFSET.operation);
|
|
69
69
|
frame.writeUInt32LE(status, REPLY_OFFSET.status);
|
|
70
70
|
body.copy(frame, HEADER_SIZE);
|
|
@@ -73,7 +73,7 @@ const replyFrame = (operation, body = Buffer.alloc(0), status = 0) => {
|
|
|
73
73
|
const evictionFrame = (reason) => {
|
|
74
74
|
const frame = Buffer.alloc(HEADER_SIZE);
|
|
75
75
|
frame.writeUInt32LE(HEADER_SIZE, REPLY_OFFSET.size);
|
|
76
|
-
frame.writeUInt8(
|
|
76
|
+
frame.writeUInt8(Command.Eviction, REPLY_OFFSET.command);
|
|
77
77
|
frame.writeUInt8(reason, EVICTION_OFFSET.reason);
|
|
78
78
|
return frame;
|
|
79
79
|
};
|
|
@@ -125,7 +125,12 @@ export type ClientConfig = {
|
|
|
125
125
|
poolSize?: PoolSizeOption;
|
|
126
126
|
/** Automatic reconnection configuration */
|
|
127
127
|
reconnect?: ReconnectOption;
|
|
128
|
-
/**
|
|
128
|
+
/**
|
|
129
|
+
* Interval for sending heartbeat pings in milliseconds, as an integer
|
|
130
|
+
* between 0 and Node's timer ceiling. Defaults to 5000. Set to 0 to disable
|
|
131
|
+
* client heartbeats; any other unusable value is rejected rather than
|
|
132
|
+
* silently disabling them.
|
|
133
|
+
*/
|
|
129
134
|
heartbeatInterval?: number;
|
|
130
135
|
/** Maximum accepted response frame size in bytes */
|
|
131
136
|
maxResponseFrameSize?: number;
|
package/dist/debug.js
CHANGED
|
@@ -16,7 +16,7 @@
|
|
|
16
16
|
// under the License.
|
|
17
17
|
//
|
|
18
18
|
import assert from 'node:assert/strict';
|
|
19
|
-
import { Client } from './client/index.js';
|
|
19
|
+
import { Client, DEFAULT_HEARTBEAT_INTERVAL } from './client/index.js';
|
|
20
20
|
import { uuidv7, uuidv4 } from 'uuidv7';
|
|
21
21
|
import { groupConsumerStream } from './stream/consumer-stream.js';
|
|
22
22
|
import { PollingStrategy } from './wire/index.js';
|
|
@@ -41,7 +41,7 @@ const opt = {
|
|
|
41
41
|
interval: 10 * 1000,
|
|
42
42
|
maxRetries: 10
|
|
43
43
|
},
|
|
44
|
-
heartbeatInterval:
|
|
44
|
+
heartbeatInterval: DEFAULT_HEARTBEAT_INTERVAL
|
|
45
45
|
};
|
|
46
46
|
const c = new Client(opt);
|
|
47
47
|
const cleanup = async () => {
|
|
@@ -82,6 +82,7 @@ type MessageAPI = ReturnType<typeof messageAPI>;
|
|
|
82
82
|
declare const systemAPI: (c: ClientProvider) => {
|
|
83
83
|
ping: (arg: void) => Promise<boolean>;
|
|
84
84
|
getStats: (arg: void) => Promise<import("./system/get-stats.command.js").Stats>;
|
|
85
|
+
describeOptions: (arg: import("./system/describe-options.command.js").DescribeOptions) => Promise<import("./system/describe-options.command.js").OptionSpec[]>;
|
|
85
86
|
};
|
|
86
87
|
type SystemAPI = ReturnType<typeof systemAPI>;
|
|
87
88
|
declare const clusterAPI: (c: ClientProvider) => {
|
package/dist/wire/command-set.js
CHANGED
|
@@ -53,6 +53,7 @@ import { ensureStream } from './stream/ensure-stream.virtual.command.js';
|
|
|
53
53
|
import { createPartition } from './partition/create-partition.command.js';
|
|
54
54
|
import { deletePartition } from './partition/delete-partition.command.js';
|
|
55
55
|
import { deleteSegments } from './segment/delete-segments.command.js';
|
|
56
|
+
import { describeOptions } from './system/describe-options.command.js';
|
|
56
57
|
import { getStats } from './system/get-stats.command.js';
|
|
57
58
|
import { ping } from './system/ping.command.js';
|
|
58
59
|
import { getClusterMetadata } from './cluster/get-cluster-metadata.command.js';
|
|
@@ -137,7 +138,8 @@ const messageAPI = (c) => ({
|
|
|
137
138
|
});
|
|
138
139
|
const systemAPI = (c) => ({
|
|
139
140
|
ping: ping(c),
|
|
140
|
-
getStats: getStats(c)
|
|
141
|
+
getStats: getStats(c),
|
|
142
|
+
describeOptions: describeOptions(c)
|
|
141
143
|
});
|
|
142
144
|
const clusterAPI = (c) => ({
|
|
143
145
|
getClusterMetadata: getClusterMetadata(c)
|
|
@@ -3,6 +3,7 @@ export declare const COMMAND_CODE: {
|
|
|
3
3
|
GetStats: number;
|
|
4
4
|
GetSnapshot: number;
|
|
5
5
|
GetClusterMetadata: number;
|
|
6
|
+
DescribeOptions: number;
|
|
6
7
|
GetMe: number;
|
|
7
8
|
GetClient: number;
|
|
8
9
|
GetClients: number;
|
|
@@ -27,8 +28,6 @@ export declare const COMMAND_CODE: {
|
|
|
27
28
|
GetOffset: number;
|
|
28
29
|
StoreOffset: number;
|
|
29
30
|
DeleteConsumerOffset: number;
|
|
30
|
-
StoreOffset2: number;
|
|
31
|
-
DeleteConsumerOffset2: number;
|
|
32
31
|
GetStream: number;
|
|
33
32
|
GetStreams: number;
|
|
34
33
|
CreateStream: number;
|
|
@@ -21,6 +21,7 @@ export const COMMAND_CODE = {
|
|
|
21
21
|
GetStats: 10,
|
|
22
22
|
GetSnapshot: 11, // @TODO GET_SNAPSHOT_FILE_CODE: u32 = 11
|
|
23
23
|
GetClusterMetadata: 12,
|
|
24
|
+
DescribeOptions: 13,
|
|
24
25
|
GetMe: 20,
|
|
25
26
|
GetClient: 21,
|
|
26
27
|
GetClients: 22,
|
|
@@ -45,8 +46,6 @@ export const COMMAND_CODE = {
|
|
|
45
46
|
GetOffset: 120,
|
|
46
47
|
StoreOffset: 121,
|
|
47
48
|
DeleteConsumerOffset: 122,
|
|
48
|
-
StoreOffset2: 123,
|
|
49
|
-
DeleteConsumerOffset2: 124,
|
|
50
49
|
GetStream: 200,
|
|
51
50
|
GetStreams: 201,
|
|
52
51
|
CreateStream: 202,
|
package/dist/wire/error.code.js
CHANGED
|
@@ -35,11 +35,15 @@ export const translateErrorCode = (code) => {
|
|
|
35
35
|
case '16': return "Invalid state entry checksum: {0}, expected: {1}, for index: {2}";
|
|
36
36
|
case '19': return "Cannot open database, Path: {0}";
|
|
37
37
|
case '20': return "Resource with key: {0} was not found.";
|
|
38
|
+
case '21': return "Cannot close WebSocket connection";
|
|
38
39
|
case '30': return "Stale client";
|
|
39
40
|
case '31': return "TCP error";
|
|
40
41
|
case '32': return "QUIC error";
|
|
41
42
|
case '33': return "Invalid server address";
|
|
42
43
|
case '34': return "Invalid client address";
|
|
44
|
+
case '35': return "Invalid IP address: {0}:{1}";
|
|
45
|
+
case '36': return "Http error {0}";
|
|
46
|
+
case '37': return "Invalid API URL: {0}";
|
|
43
47
|
case '40': return "Unauthenticated";
|
|
44
48
|
case '41': return "Unauthorized";
|
|
45
49
|
case '42': return "Invalid credentials";
|
|
@@ -56,6 +60,10 @@ export const translateErrorCode = (code) => {
|
|
|
56
60
|
case '53': return "Invalid personal access token";
|
|
57
61
|
case '54': return "Personal access token: {0} for user with ID: {1} has expired.";
|
|
58
62
|
case '55': return "Users limit reached.";
|
|
63
|
+
case '56': return "Invalid personal access token expiry";
|
|
64
|
+
case '57': return "Request transiently not committed; retry";
|
|
65
|
+
case '58': return "Request transiently not accepted; retry, on any replica";
|
|
66
|
+
case '59': return "Request already applied; its reply is no longer available";
|
|
59
67
|
case '61': return "Not connected";
|
|
60
68
|
case '63': return "Client shutdown";
|
|
61
69
|
case '64': return "Invalid TLS domain";
|
|
@@ -71,6 +79,7 @@ export const translateErrorCode = (code) => {
|
|
|
71
79
|
case '76': return "Cannot generate JWT";
|
|
72
80
|
case '77': return "Access token is missing";
|
|
73
81
|
case '78': return "Invalid access token";
|
|
82
|
+
case '79': return "Cannot fetch JWKS from URL: {0}";
|
|
74
83
|
case '80': return "Invalid size bytes";
|
|
75
84
|
case '81': return "Invalid UTF-8";
|
|
76
85
|
case '82': return "Invalid number encoding";
|
|
@@ -87,6 +96,11 @@ export const translateErrorCode = (code) => {
|
|
|
87
96
|
case '304': return "Empty response";
|
|
88
97
|
case '305': return "Cannot create endpoint";
|
|
89
98
|
case '306': return "Cannot parse URL";
|
|
99
|
+
case '400': return "WebSocket error";
|
|
100
|
+
case '401': return "WebSocket connection error";
|
|
101
|
+
case '402': return "WebSocket close error";
|
|
102
|
+
case '403': return "WebSocket receive error";
|
|
103
|
+
case '404': return "WebSocket send error";
|
|
90
104
|
// STREAM
|
|
91
105
|
case '1000': return "Cannot create streams directory, Path: {0}";
|
|
92
106
|
case '1001': return "Cannot create stream with ID: {0} directory, Path: {1}";
|
|
@@ -99,15 +113,13 @@ export const translateErrorCode = (code) => {
|
|
|
99
113
|
case '1008': return "Failed to delete stream directory with ID: {0}";
|
|
100
114
|
case '1009': return "Stream with ID: {0} was not found.";
|
|
101
115
|
case '1010': return "Stream with name: {0} was not found.";
|
|
102
|
-
case '1011': return "Stream with
|
|
116
|
+
case '1011': return "Stream with directory {0} was not found.";
|
|
103
117
|
case '1012': return "Stream with name: {0} already exists.";
|
|
104
118
|
case '1013': return "Invalid stream name";
|
|
105
119
|
case '1014': return "Invalid stream ID";
|
|
106
120
|
case '1015': return "Cannot read streams";
|
|
107
|
-
case '1016': return "Missing streams";
|
|
108
|
-
case '1017': return "Missing topics for stream with ID: {0}";
|
|
109
|
-
case '1018': return "Missing partitions for topic with ID: {0} for stream with ID: {1}.";
|
|
110
121
|
case '1019': return "Max topic size cannot be lower than segment size. Max topic size: {0} < segment size: {1}.";
|
|
122
|
+
case '1020': return "Too many streams";
|
|
111
123
|
case '2000': return "Cannot create topics directory for stream with ID: {0}, Path: {1}";
|
|
112
124
|
case '2001': return "Failed to create directory for topic with ID: {0} for stream with ID: {1}, Path: {2}";
|
|
113
125
|
case '2002': return "Failed to create topic info file for topic with ID: {0} for stream with ID: {1}.";
|
|
@@ -120,13 +132,15 @@ export const translateErrorCode = (code) => {
|
|
|
120
132
|
case '2009': return "Cannot poll topic";
|
|
121
133
|
case '2010': return "Topic with ID: {0} for stream with ID: {1} was not found.";
|
|
122
134
|
case '2011': return "Topic with name: {0} for stream with ID: {1} was not found.";
|
|
123
|
-
case '2012': return "Topic with ID: {0} for stream with ID: {1} already exists.";
|
|
124
135
|
case '2013': return "Topic with name: {0} for stream with ID: {1} already exists.";
|
|
125
136
|
case '2014': return "Invalid topic name";
|
|
126
137
|
case '2015': return "Too many partitions";
|
|
127
138
|
case '2016': return "Invalid topic ID";
|
|
128
139
|
case '2017': return "Cannot read topics for stream with ID: {0}";
|
|
129
140
|
case '2018': return "Invalid replication factor";
|
|
141
|
+
case '2019': return "Invalid partitions count";
|
|
142
|
+
case '2020': return "Topic directory: {0} not found";
|
|
143
|
+
case '2021': return "Too many topics";
|
|
130
144
|
// TOPIC
|
|
131
145
|
case '3000': return "Cannot create partition with ID: {0} for stream with ID: {1} and topic with ID: {2}";
|
|
132
146
|
case '3001': return "Failed to create directory for partitions for stream with ID: {0} and topic with ID: {1}";
|
|
@@ -143,6 +157,9 @@ export const translateErrorCode = (code) => {
|
|
|
143
157
|
case '3012': return "Failed to create consumer offsets directory for path: {0}";
|
|
144
158
|
case '3020': return "Failed to read consumers offsets from path: {0}";
|
|
145
159
|
case '3021': return "Consumer offset for consumer with ID: {0} was not found.";
|
|
160
|
+
case '3022': return "Failed to resolve consumer with ID: {0}";
|
|
161
|
+
case '3023': return "Cannot open consumer offsets file for path: {0}";
|
|
162
|
+
case '3013': return "Partition id space exhausted for this topic";
|
|
146
163
|
// MESSAGE
|
|
147
164
|
case '4000': return "Segment not found";
|
|
148
165
|
case '4001': return "Segment with start offset: {0} and partition with ID: {1} is closed";
|
|
@@ -172,17 +189,75 @@ export const translateErrorCode = (code) => {
|
|
|
172
189
|
case '4025': return "Invalid message payload length";
|
|
173
190
|
case '4026': return "Cannot read message checksum";
|
|
174
191
|
case '4027': return "Invalid message checksum: {0}, expected: {1}, for offset: {2}";
|
|
175
|
-
case '4028': return "
|
|
192
|
+
case '4028': return "Invalid key value length";
|
|
193
|
+
case '4029': return "Command length error: {0}";
|
|
194
|
+
case '4030': return "Incorrect Segments Count size: {0}";
|
|
195
|
+
case '4031': return "Non-zero offset: {0} at index: {1}";
|
|
196
|
+
case '4032': return "Non-zero timestamp: {0} at index: {1}";
|
|
197
|
+
case '4033': return "Missing index: {0}";
|
|
198
|
+
case '4034': return "Invalid indexes byte size: {0}B, should be divisible by 16";
|
|
199
|
+
case '4035': return "Invalid indexes count: {0}, expected: {1}";
|
|
200
|
+
case '4036': return "Invalid messages byte size: {0}B, expected: {1}B";
|
|
201
|
+
case '4037': return "Too small message: {0}B, expected: {1}B";
|
|
202
|
+
case '4038': return "Invalid message timestamp delta: {0} microseconds exceeds the per-batch limit";
|
|
203
|
+
case '4039': return "Invalid batch checksum: {0}, expected: {1}, for base offset: {2}";
|
|
204
|
+
case '4040': return "Invalid header kind code: {0}";
|
|
205
|
+
case '4041': return "Unsupported option key: {0}";
|
|
206
|
+
case '4042': return "Invalid option value for key: {0}";
|
|
207
|
+
case '4043': return "Options block exceeds its limits: {0}";
|
|
208
|
+
case '4050': return "Cannot sed messages due to client disconnection";
|
|
209
|
+
case '4051': return "Background send error";
|
|
210
|
+
case '4052': return "Background send timeout";
|
|
211
|
+
case '4053': return "Background send buffer is full";
|
|
212
|
+
case '4054': return "Background worker disconnected";
|
|
213
|
+
case '4055': return "Background send buffer overflow";
|
|
214
|
+
case '4057': return "Producer closed";
|
|
215
|
+
case '4100': return "Invalid offset: {0}";
|
|
216
|
+
case '4101': return "Invalid reserved field value: {0}, expected: 0";
|
|
176
217
|
// CONSUMER GROUP
|
|
177
|
-
case '5000': return "Consumer group ID not found";
|
|
218
|
+
case '5000': return "Consumer group with ID: {0} for topic with ID: {1} was not found.";
|
|
178
219
|
case '5002': return "Invalid consumer group ID";
|
|
179
|
-
case '5003': return "Consumer group name not found";
|
|
180
|
-
case '5004': return "Consumer group name already exists";
|
|
220
|
+
case '5003': return "Consumer group with name: {0} for topic with ID: {1} was not found.";
|
|
221
|
+
case '5004': return "Consumer group with name: {0} for topic with ID: {1} already exists.";
|
|
181
222
|
case '5005': return "Invalid consumer group name";
|
|
182
|
-
case '5006': return "Consumer group member not found";
|
|
183
|
-
case '5007': return "
|
|
184
|
-
case '5008': return "
|
|
185
|
-
case '5009': return "Consumer group
|
|
223
|
+
case '5006': return "Consumer group member with client ID: {0} for group with ID: {1} for topic with ID: {2} was not found.";
|
|
224
|
+
case '5007': return "Failed to create consumer group info file for ID: {0} for topic with ID: {1} for stream with ID: {2}.";
|
|
225
|
+
case '5008': return "Failed to delete consumer group info file for ID: {0} for topic with ID: {1} for stream with ID: {2}.";
|
|
226
|
+
case '5009': return "Consumer group member with client ID: {0} does not own partition: {1} at the current generation (rebalance in progress).";
|
|
227
|
+
case '6000': return "Base offset is missing";
|
|
228
|
+
case '6001': return "Last offset delta is missing";
|
|
229
|
+
case '6002': return "Max timestamp is missing";
|
|
230
|
+
case '6003': return "Length is missing";
|
|
231
|
+
case '6004': return "Payload is missing";
|
|
232
|
+
case '7000': return "Cannot read batch base offset";
|
|
233
|
+
case '7001': return "Cannot read batch length";
|
|
234
|
+
case '7002': return "Cannot read batch last offset delta";
|
|
235
|
+
case '7003': return "Cannot read batch maximum timestamp";
|
|
236
|
+
case '7004': return "Cannot read batch payload";
|
|
237
|
+
case '8000': return "Invalid connection string";
|
|
238
|
+
case '9000': return "Snapshot file completion failed";
|
|
239
|
+
case '10000': return "Cannot serialize resource";
|
|
240
|
+
case '10001': return "Cannot deserialize resource";
|
|
241
|
+
case '10002': return "Cannot read file";
|
|
242
|
+
case '10003': return "Cannot read file metadata";
|
|
243
|
+
case '10004': return "Cannot seek file";
|
|
244
|
+
case '10005': return "Cannot append to file";
|
|
245
|
+
case '10006': return "Cannot write to file";
|
|
246
|
+
case '10007': return "Cannot overwrite file";
|
|
247
|
+
case '10008': return "Cannot delete file";
|
|
248
|
+
case '10009': return "Cannot sync file";
|
|
249
|
+
case '10010': return "Cannot read index offset";
|
|
250
|
+
case '10011': return "Cannot read index position";
|
|
251
|
+
case '10012': return "Cannot read index timestamp";
|
|
252
|
+
case '10013': return "Timestamp out of range: {0}";
|
|
253
|
+
case '11000': return "Shard not found for stream ID: {0}, topic ID: {1}, partition ID: {2}";
|
|
254
|
+
case '11001': return "Shard communication error";
|
|
255
|
+
case '12000': return "Cannot bind to socket with addr: {0}";
|
|
256
|
+
case '12001': return "Task execution timeout";
|
|
257
|
+
case '13000': return "IO error: {0}";
|
|
258
|
+
case '14000': return "VSR session already bound; reset before re-binding";
|
|
259
|
+
case '14001': return "VSR session value {0} is invalid (must be non-zero)";
|
|
260
|
+
case '14003': return "Incompatible binary protocol version: client {}, server accepts [{}, {}]";
|
|
186
261
|
default: return 'error';
|
|
187
262
|
}
|
|
188
263
|
};
|
|
@@ -18,16 +18,16 @@ import assert from 'node:assert/strict';
|
|
|
18
18
|
import { it } from 'node:test';
|
|
19
19
|
import { translateErrorCode } from './error.code.js';
|
|
20
20
|
it('translates the consumer-group error range', () => {
|
|
21
|
-
assert.equal(translateErrorCode(5000), 'Consumer group ID not found');
|
|
21
|
+
assert.equal(translateErrorCode(5000), 'Consumer group with ID: {0} for topic with ID: {1} was not found.');
|
|
22
22
|
assert.equal(translateErrorCode(5001), 'error');
|
|
23
23
|
assert.equal(translateErrorCode(5002), 'Invalid consumer group ID');
|
|
24
|
-
assert.equal(translateErrorCode(5003), 'Consumer group name not found');
|
|
25
|
-
assert.equal(translateErrorCode(5004), 'Consumer group name already exists');
|
|
24
|
+
assert.equal(translateErrorCode(5003), 'Consumer group with name: {0} for topic with ID: {1} was not found.');
|
|
25
|
+
assert.equal(translateErrorCode(5004), 'Consumer group with name: {0} for topic with ID: {1} already exists.');
|
|
26
26
|
assert.equal(translateErrorCode(5005), 'Invalid consumer group name');
|
|
27
|
-
assert.equal(translateErrorCode(5006), 'Consumer group member not found');
|
|
28
|
-
assert.equal(translateErrorCode(5007), '
|
|
29
|
-
assert.equal(translateErrorCode(5008), '
|
|
30
|
-
assert.equal(translateErrorCode(5009), 'Consumer group
|
|
27
|
+
assert.equal(translateErrorCode(5006), 'Consumer group member with client ID: {0} for group with ID: {1} for topic with ID: {2} was not found.');
|
|
28
|
+
assert.equal(translateErrorCode(5007), 'Failed to create consumer group info file for ID: {0} for topic with ID: {1} for stream with ID: {2}.');
|
|
29
|
+
assert.equal(translateErrorCode(5008), 'Failed to delete consumer group info file for ID: {0} for topic with ID: {1} for stream with ID: {2}.');
|
|
30
|
+
assert.equal(translateErrorCode(5009), 'Consumer group member with client ID: {0} does not own partition: {1} at the current generation (rebalance in progress).');
|
|
31
31
|
assert.equal(translateErrorCode(5010), 'error');
|
|
32
32
|
});
|
|
33
33
|
//# sourceMappingURL=error.code.test.js.map
|
|
@@ -27,7 +27,7 @@ export type HeaderKindId = keyof HeaderKind;
|
|
|
27
27
|
/** Numeric values of header kinds */
|
|
28
28
|
export type HeaderKindValue = ValueOf<HeaderKind>;
|
|
29
29
|
/** Reverse mapping from numeric value to header kind name */
|
|
30
|
-
export declare const ReverseHeaderKind: Record<2 | 1 | 10 | 11 | 12 |
|
|
30
|
+
export declare const ReverseHeaderKind: Record<2 | 1 | 10 | 11 | 12 | 13 | 5 | 8 | 3 | 4 | 6 | 7 | 9 | 14 | 15, "Raw" | "String" | "Bool" | "Int8" | "Int16" | "Int32" | "Int64" | "Int128" | "Uint8" | "Uint16" | "Uint32" | "Uint64" | "Uint128" | "Float" | "Double">;
|
|
31
31
|
/** Returns expected byte size for a header kind, or -1 for variable-size kinds */
|
|
32
32
|
export declare const expectedSize: (kind: number) => number;
|
|
33
33
|
/** Raw binary header value */
|