apache-iggy 0.10.0 → 0.10.1-edge.2
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 +248 -28
- package/dist/client/client.config.d.ts +2 -0
- package/dist/client/client.config.js +13 -2
- package/dist/client/client.config.test.js +15 -2
- package/dist/client/client.connection-string.js +13 -1
- package/dist/client/client.connection-string.test.js +43 -1
- package/dist/client/client.connection.d.ts +33 -135
- package/dist/client/client.connection.js +190 -453
- package/dist/client/client.connection.test.js +244 -669
- package/dist/client/client.d.ts +4 -35
- package/dist/client/client.js +8 -101
- package/dist/client/client.operation.d.ts +120 -0
- package/dist/client/client.operation.js +428 -0
- package/dist/client/client.operation.test.d.ts +2 -0
- package/dist/client/client.operation.test.js +285 -0
- package/dist/client/client.socket.d.ts +92 -209
- package/dist/client/client.socket.js +1205 -981
- package/dist/client/client.socket.test.js +4865 -679
- package/dist/client/client.type.d.ts +65 -22
- package/dist/client/index.d.ts +1 -0
- package/dist/client/index.js +1 -0
- package/dist/client.e2e.js +3 -5
- package/dist/e2e/resource-options.e2e.d.ts +2 -0
- package/dist/{wire/stream/purge-stream.command.js → e2e/resource-options.e2e.js} +12 -20
- package/dist/e2e/resource-options.suite.d.ts +15 -0
- package/dist/e2e/resource-options.suite.js +272 -0
- package/dist/e2e/tcp.cluster.e2e.js +1 -0
- package/dist/e2e/tcp.consumer-stream.e2e.js +4 -0
- package/dist/e2e/tcp.parallel.e2e.js +5 -6
- package/dist/e2e/tcp.stream.e2e.js +0 -3
- package/dist/e2e/tcp.system.e2e.js +10 -1
- package/dist/e2e/tcp.token.e2e.js +8 -2
- package/dist/e2e/tcp.topic.e2e.js +0 -3
- package/dist/e2e/tcp.user.e2e.js +46 -3
- package/dist/e2e/tls.system.e2e.js +21 -12
- package/dist/index.d.ts +2 -1
- package/dist/index.js +1 -1
- package/dist/stream/consumer-stream.js +97 -12
- package/dist/wire/cluster/cluster.type.d.ts +2 -2
- package/dist/wire/cluster/cluster.utils.d.ts +1 -0
- package/dist/wire/cluster/cluster.utils.js +4 -1
- package/dist/wire/command-set.d.ts +8 -8
- package/dist/wire/command-set.js +18 -9
- package/dist/wire/command.code.d.ts +3 -10
- package/dist/wire/command.code.js +9 -11
- package/dist/wire/command.utils.d.ts +3 -2
- package/dist/wire/command.utils.js +3 -2
- package/dist/wire/error.code.js +17 -1
- package/dist/wire/error.code.test.js +28 -1
- package/dist/wire/error.utils.d.ts +4 -2
- package/dist/wire/error.utils.js +6 -3
- package/dist/wire/identifier.utils.d.ts +2 -0
- package/dist/wire/identifier.utils.js +2 -0
- package/dist/wire/index.d.ts +1 -0
- package/dist/wire/index.js +1 -0
- package/dist/wire/message/header.type.d.ts +1 -1
- package/dist/wire/message/message-batch.test.js +11 -5
- package/dist/wire/message/message.utils.d.ts +0 -10
- package/dist/wire/message/message.utils.js +1 -22
- package/dist/wire/message/poll-messages.command.d.ts +12 -1
- package/dist/wire/message/poll-messages.command.js +64 -16
- package/dist/wire/message/poll-messages.command.test.js +143 -5
- package/dist/wire/message/poll.utils.d.ts +4 -0
- package/dist/wire/message/poll.utils.js +6 -1
- package/dist/wire/message/send-messages.command.d.ts +3 -3
- package/dist/wire/message/send-messages.command.js +103 -3
- package/dist/wire/message/send-messages.command.test.js +113 -2
- package/dist/wire/offset/delete-offset.command.d.ts +11 -4
- package/dist/wire/offset/delete-offset.command.js +1 -1
- package/dist/wire/offset/get-offset.command.d.ts +1 -1
- package/dist/wire/offset/offset-write.command.test.d.ts +2 -0
- package/dist/wire/offset/offset-write.command.test.js +43 -0
- package/dist/wire/offset/offset.utils.js +10 -6
- package/dist/wire/offset/store-offset.command.d.ts +10 -0
- package/dist/wire/offset/store-offset.command.js +1 -1
- package/dist/wire/options.utils.d.ts +46 -0
- package/dist/wire/options.utils.js +57 -0
- package/dist/wire/partition/partition-route.d.ts +12 -0
- package/dist/wire/partition/partition-route.js +40 -0
- package/dist/wire/partition/partition-route.test.d.ts +2 -0
- package/dist/wire/partition/partition-route.test.js +42 -0
- package/dist/wire/session/login-with-token.command.d.ts +2 -2
- package/dist/wire/session/login-with-token.command.js +7 -2
- package/dist/wire/session/login.command.d.ts +2 -2
- package/dist/wire/session/login.command.js +7 -2
- package/dist/wire/stream/create-stream.command.d.ts +5 -2
- package/dist/wire/stream/create-stream.command.js +20 -2
- package/dist/wire/stream/index.d.ts +1 -1
- package/dist/wire/stream/index.js +1 -1
- package/dist/wire/stream/stream-options.d.ts +77 -0
- package/dist/wire/stream/stream-options.js +101 -0
- package/dist/wire/stream/stream-options.test.d.ts +2 -0
- package/dist/wire/stream/stream-options.test.js +103 -0
- package/dist/wire/stream/stream.utils.d.ts +9 -0
- package/dist/wire/stream/stream.utils.js +17 -3
- package/dist/wire/stream/update-stream.command.d.ts +23 -5
- package/dist/wire/stream/update-stream.command.js +10 -17
- package/dist/wire/system/describe-options.command.d.ts +23 -6
- package/dist/wire/system/describe-options.command.js +21 -2
- package/dist/wire/system/get-stats.command.d.ts +17 -0
- package/dist/wire/system/get-stats.command.js +69 -16
- package/dist/wire/system/get-stats.command.test.d.ts +2 -0
- package/dist/wire/system/get-stats.command.test.js +97 -0
- package/dist/wire/system/index.d.ts +2 -0
- package/dist/wire/system/index.js +2 -0
- package/dist/wire/system/snapshot.command.d.ts +64 -0
- package/dist/wire/system/snapshot.command.js +76 -0
- package/dist/wire/system/snapshot.command.test.d.ts +2 -0
- package/dist/wire/system/snapshot.command.test.js +86 -0
- package/dist/wire/system/update-resource.command.d.ts +53 -0
- package/dist/wire/system/update-resource.command.js +130 -0
- package/dist/wire/system/update-resource.command.test.d.ts +2 -0
- package/dist/wire/system/update-resource.command.test.js +497 -0
- package/dist/wire/token/create-token.command.d.ts +1 -1
- package/dist/wire/token/create-token.command.js +1 -1
- package/dist/wire/token/create-token.command.test.js +6 -0
- package/dist/wire/topic/create-topic.command.d.ts +20 -12
- package/dist/wire/topic/create-topic.command.js +11 -4
- package/dist/wire/topic/index.d.ts +1 -2
- package/dist/wire/topic/index.js +1 -2
- package/dist/wire/topic/topic.utils.d.ts +14 -5
- package/dist/wire/topic/topic.utils.js +26 -11
- package/dist/wire/topic/topic.utils.test.js +13 -13
- package/dist/wire/topic/update-topic.command.d.ts +22 -15
- package/dist/wire/topic/update-topic.command.js +12 -34
- package/dist/wire/user/create-user.command.d.ts +12 -2
- package/dist/wire/user/create-user.command.js +7 -3
- package/dist/wire/user/index.d.ts +1 -0
- package/dist/wire/user/index.js +1 -0
- package/dist/wire/user/update-user.command.d.ts +25 -7
- package/dist/wire/user/update-user.command.js +18 -31
- package/dist/wire/user/user.utils.d.ts +2 -0
- package/dist/wire/user/user.utils.js +5 -3
- package/dist/wire/vsr/header.d.ts +23 -8
- package/dist/wire/vsr/header.js +32 -10
- package/dist/wire/vsr/header.test.js +5 -1
- package/dist/wire/vsr/index.d.ts +8 -2
- package/dist/wire/vsr/index.js +44 -11
- package/dist/wire/vsr/operation.d.ts +18 -6
- package/dist/wire/vsr/operation.js +46 -16
- package/dist/wire/vsr/operation.test.js +32 -8
- package/dist/wire/vsr/register.d.ts +10 -6
- package/dist/wire/vsr/register.js +24 -7
- package/dist/wire/vsr/register.test.js +28 -8
- package/dist/wire/vsr/reply.d.ts +4 -1
- package/dist/wire/vsr/reply.js +42 -7
- package/dist/wire/vsr/reply.test.js +11 -1
- package/dist/wire/vsr/session.d.ts +9 -10
- package/dist/wire/vsr/session.js +32 -23
- package/dist/wire/vsr/session.test.js +33 -7
- package/dist/wire/vsr/vsr.test.js +57 -6
- package/package.json +6 -7
- package/dist/wire/message/flush-unsaved-buffers.command.d.ts +0 -26
- package/dist/wire/message/flush-unsaved-buffers.command.js +0 -36
- package/dist/wire/stream/purge-stream.command.d.ts +0 -22
- package/dist/wire/topic/purge-topic.command.d.ts +0 -24
- package/dist/wire/topic/purge-topic.command.js +0 -39
|
@@ -16,30 +16,50 @@
|
|
|
16
16
|
// under the License.
|
|
17
17
|
import assert from 'node:assert/strict';
|
|
18
18
|
import { describe, it } from 'node:test';
|
|
19
|
-
import { deserializeLoginRegister, IGGY_PROTOCOL_VERSION, serializeLoginRegister, serializeLoginRegisterWithPat } from './register.js';
|
|
19
|
+
import { deserializeLoginRegister, BIND_SECRET_BYTES, IGGY_PROTOCOL_VERSION, serializeBindSession, serializeLoginRegister, serializeLoginRegisterWithPat } from './register.js';
|
|
20
|
+
const BIND_SECRET = Buffer.alloc(BIND_SECRET_BYTES, 0x5a);
|
|
20
21
|
describe('VSR register payloads', () => {
|
|
21
22
|
it('encodes protocol, SDK, credentials, and empty context', () => {
|
|
22
|
-
const body = serializeLoginRegister('iggy', 'secret', '0.8.1-edge.3');
|
|
23
|
+
const body = serializeLoginRegister('iggy', 'secret', '0.8.1-edge.3', BIND_SECRET);
|
|
23
24
|
assert.equal(body.readUInt32LE(0), IGGY_PROTOCOL_VERSION);
|
|
24
25
|
assert.match(body.toString('utf8'), /node-sdk/);
|
|
25
26
|
assert.match(body.toString('utf8'), /0\.8\.1-edge\.3/);
|
|
26
27
|
assert.match(body.toString('utf8'), /iggy/);
|
|
27
28
|
assert.match(body.toString('utf8'), /secret/);
|
|
29
|
+
const secretOffset = 4 + 1 + Buffer.byteLength('node-sdk')
|
|
30
|
+
+ 1 + Buffer.byteLength('0.8.1-edge.3');
|
|
31
|
+
assert.deepEqual(body.subarray(secretOffset, secretOffset + BIND_SECRET_BYTES), BIND_SECRET);
|
|
28
32
|
assert.equal(body.readUInt32LE(body.length - 4), 0);
|
|
29
33
|
});
|
|
30
34
|
it('uses UTF-8 byte lengths and enforces one-byte bounds', () => {
|
|
31
|
-
const body = serializeLoginRegister('żółw', 'hasło', '版本');
|
|
35
|
+
const body = serializeLoginRegister('żółw', 'hasło', '版本', BIND_SECRET);
|
|
32
36
|
assert.equal(body.readUInt8(4), Buffer.byteLength('node-sdk'));
|
|
33
37
|
assert.match(body.toString('utf8'), /żółw/);
|
|
34
|
-
assert.throws(() => serializeLoginRegister('', 'secret', '1.0.0'), /1\.\.255 bytes/);
|
|
35
|
-
assert.throws(() => serializeLoginRegister('x'.repeat(256), 'secret', '1.0.0'), /1\.\.255 bytes/);
|
|
36
|
-
const maximum = serializeLoginRegister('x'.repeat(255), 'secret', '1.0.0');
|
|
38
|
+
assert.throws(() => serializeLoginRegister('', 'secret', '1.0.0', BIND_SECRET), /1\.\.255 bytes/);
|
|
39
|
+
assert.throws(() => serializeLoginRegister('x'.repeat(256), 'secret', '1.0.0', BIND_SECRET), /1\.\.255 bytes/);
|
|
40
|
+
const maximum = serializeLoginRegister('x'.repeat(255), 'secret', '1.0.0', BIND_SECRET);
|
|
37
41
|
assert.match(maximum.toString('utf8'), /x{255}/);
|
|
38
42
|
});
|
|
39
43
|
it('encodes PAT registration and rejects oversized tokens', () => {
|
|
40
|
-
const body = serializeLoginRegisterWithPat('token', '0.8.1-edge.3');
|
|
44
|
+
const body = serializeLoginRegisterWithPat('token', '0.8.1-edge.3', BIND_SECRET);
|
|
41
45
|
assert.match(body.toString('utf8'), /token/);
|
|
42
|
-
assert.throws(() => serializeLoginRegisterWithPat('x'.repeat(256), '0.8.1-edge.3'), /token exceeds/);
|
|
46
|
+
assert.throws(() => serializeLoginRegisterWithPat('x'.repeat(256), '0.8.1-edge.3', BIND_SECRET), /token exceeds/);
|
|
47
|
+
const secretOffset = 4 + 1 + Buffer.byteLength('node-sdk')
|
|
48
|
+
+ 1 + Buffer.byteLength('0.8.1-edge.3');
|
|
49
|
+
assert.deepEqual(body.subarray(secretOffset, secretOffset + BIND_SECRET_BYTES), BIND_SECRET);
|
|
50
|
+
assert.equal(body.readUInt32LE(body.length - 4), 0);
|
|
51
|
+
});
|
|
52
|
+
it('encodes version, shared identity and proof and rejects invalid bounds', () => {
|
|
53
|
+
const identity = Buffer.alloc(32);
|
|
54
|
+
identity.writeBigUInt64LE(42n, 16);
|
|
55
|
+
const body = serializeBindSession(identity, '1.0.0', BIND_SECRET);
|
|
56
|
+
assert.equal(body.readUInt32LE(0), IGGY_PROTOCOL_VERSION);
|
|
57
|
+
assert.deepEqual(body.subarray(-64, -32), identity);
|
|
58
|
+
assert.deepEqual(body.subarray(-32), BIND_SECRET);
|
|
59
|
+
for (const length of [0, 31, 33]) {
|
|
60
|
+
assert.throws(() => serializeBindSession(Buffer.alloc(length), '1.0.0', BIND_SECRET), /identity must be 32/);
|
|
61
|
+
assert.throws(() => serializeLoginRegister('iggy', 'iggy', '1.0.0', Buffer.alloc(length)), /secret must be 32/);
|
|
62
|
+
}
|
|
43
63
|
});
|
|
44
64
|
it('decodes a complete login response', () => {
|
|
45
65
|
const version = Buffer.from('0.10.3');
|
package/dist/wire/vsr/reply.d.ts
CHANGED
|
@@ -7,8 +7,11 @@
|
|
|
7
7
|
*/
|
|
8
8
|
import { ResponseError } from '../error.utils.js';
|
|
9
9
|
export declare class VsrEvictionError extends ResponseError {
|
|
10
|
-
|
|
10
|
+
readonly reason?: number | undefined;
|
|
11
|
+
constructor(errorCode: number, reason?: number | undefined);
|
|
11
12
|
}
|
|
13
|
+
/** Validate correlation before a marked receipt can affect operation or session state. */
|
|
14
|
+
export declare const validateReplyForRequest: (reply: Buffer, request: Buffer, command: number) => void;
|
|
12
15
|
/**
|
|
13
16
|
* Decodes one complete consensus frame into the reply body, stripping the
|
|
14
17
|
* committed result section for result-framed operations and mapping every
|
package/dist/wire/vsr/reply.js
CHANGED
|
@@ -22,8 +22,8 @@
|
|
|
22
22
|
* result section for result-framed operations.
|
|
23
23
|
*/
|
|
24
24
|
import { ResponseError, responseError } from '../error.utils.js';
|
|
25
|
-
import { Command, EvictionReason, HEADER_SIZE, peekCommand, readEviction, readReplyOperation, readSize, readStatus } from './header.js';
|
|
26
|
-
import { Operation, isKnownOperation, isResultFramed } from './operation.js';
|
|
25
|
+
import { Command, EvictionReason, HEADER_SIZE, peekCommand, readEviction, readReplyOperation, readSize, readStatus, hasCommittedResult, REQUEST_OFFSET, REPLY_OFFSET } from './header.js';
|
|
26
|
+
import { Operation, isKnownOperation, isResultFramed, requestOperation } from './operation.js';
|
|
27
27
|
/** `IggyError` codes this funnel raises client-side. */
|
|
28
28
|
const INVALID_COMMAND = 3;
|
|
29
29
|
const UNAUTHENTICATED = 40;
|
|
@@ -33,15 +33,43 @@ const STALE_CLIENT = 30;
|
|
|
33
33
|
const INVALID_FORMAT = 4;
|
|
34
34
|
const EMPTY_RESPONSE = 304;
|
|
35
35
|
const INCOMPATIBLE_PROTOCOL_VERSION = 14003;
|
|
36
|
+
const TRANSIENT_NOT_COMMITTED = 57;
|
|
37
|
+
const TRANSIENT_NOT_ACCEPTED = 58;
|
|
38
|
+
const NOT_PRIMARY = 89;
|
|
36
39
|
const RESULT_COUNT_LEN = 4;
|
|
37
40
|
const RESULT_ENTRY_LEN = 8;
|
|
38
41
|
export class VsrEvictionError extends ResponseError {
|
|
39
|
-
|
|
42
|
+
reason;
|
|
43
|
+
constructor(errorCode, reason) {
|
|
40
44
|
super(0, errorCode);
|
|
45
|
+
this.reason = reason;
|
|
41
46
|
this.name = 'VsrEvictionError';
|
|
42
47
|
Object.setPrototypeOf(this, VsrEvictionError.prototype);
|
|
43
48
|
}
|
|
44
49
|
}
|
|
50
|
+
const serverError = (command, code) => {
|
|
51
|
+
const error = responseError(command, code);
|
|
52
|
+
error.fromServer = true;
|
|
53
|
+
return error;
|
|
54
|
+
};
|
|
55
|
+
/** Validate correlation before a marked receipt can affect operation or session state. */
|
|
56
|
+
export const validateReplyForRequest = (reply, request, command) => {
|
|
57
|
+
const operation = request[REQUEST_OFFSET.operation];
|
|
58
|
+
const actual = readReplyOperation(reply);
|
|
59
|
+
if (reply.readBigUInt64LE(REPLY_OFFSET.request) !== request.readBigUInt64LE(REQUEST_OFFSET.request) ||
|
|
60
|
+
requestOperation(actual) !== operation)
|
|
61
|
+
throw responseError(command, INVALID_COMMAND, 'reply does not match request');
|
|
62
|
+
if (!hasCommittedResult(reply))
|
|
63
|
+
return;
|
|
64
|
+
const op = reply.readBigUInt64LE(REPLY_OFFSET.op);
|
|
65
|
+
if (!reply.subarray(REPLY_OFFSET.client, REPLY_OFFSET.client + 16)
|
|
66
|
+
.equals(request.subarray(REQUEST_OFFSET.client, REQUEST_OFFSET.client + 16)) ||
|
|
67
|
+
!reply.subarray(REPLY_OFFSET.requestChecksum, REPLY_OFFSET.requestChecksum + 16)
|
|
68
|
+
.equals(request.subarray(REQUEST_OFFSET.requestChecksum, REQUEST_OFFSET.requestChecksum + 16)) ||
|
|
69
|
+
reply.readBigUInt64LE(REPLY_OFFSET.partitionIncarnation) !== request.readBigUInt64LE(REQUEST_OFFSET.partitionIncarnation) ||
|
|
70
|
+
op === 0n || op !== reply.readBigUInt64LE(REPLY_OFFSET.commit))
|
|
71
|
+
throw responseError(command, INVALID_COMMAND, 'committed receipt does not match original request');
|
|
72
|
+
};
|
|
45
73
|
/**
|
|
46
74
|
* Decodes one complete consensus frame into the reply body, stripping the
|
|
47
75
|
* committed result section for result-framed operations and mapping every
|
|
@@ -65,12 +93,19 @@ export const decodeResponse = (frame, commandCode = 0) => {
|
|
|
65
93
|
// A pre-commit denial always ships an empty body; the status channel and
|
|
66
94
|
// the committed result section are mutually exclusive.
|
|
67
95
|
if (status !== 0)
|
|
68
|
-
throw
|
|
96
|
+
throw serverError(commandCode, status);
|
|
69
97
|
const operation = readReplyOperation(frame);
|
|
70
98
|
if (!isKnownOperation(operation))
|
|
71
99
|
throw responseError(commandCode, INVALID_COMMAND);
|
|
72
100
|
const body = frame.subarray(HEADER_SIZE, size);
|
|
73
|
-
|
|
101
|
+
try {
|
|
102
|
+
return splitMetadataResult(operation, body, commandCode);
|
|
103
|
+
}
|
|
104
|
+
catch (error) {
|
|
105
|
+
if (error instanceof ResponseError && error.fromServer)
|
|
106
|
+
error.committed = hasCommittedResult(frame) && ![TRANSIENT_NOT_COMMITTED, TRANSIENT_NOT_ACCEPTED, NOT_PRIMARY].includes(error.errorCode);
|
|
107
|
+
throw error;
|
|
108
|
+
}
|
|
74
109
|
};
|
|
75
110
|
/**
|
|
76
111
|
* Strips the committed result section leading a result-framed reply body.
|
|
@@ -95,7 +130,7 @@ export const splitMetadataResult = (operation, body, commandCode = 0) => {
|
|
|
95
130
|
// IggyError space.
|
|
96
131
|
const code = body.readUInt32LE(RESULT_COUNT_LEN + 4);
|
|
97
132
|
if (code !== 0)
|
|
98
|
-
throw
|
|
133
|
+
throw serverError(commandCode, code);
|
|
99
134
|
}
|
|
100
135
|
return body.subarray(sectionLength);
|
|
101
136
|
};
|
|
@@ -145,6 +180,6 @@ export const evictionError = (frame) => {
|
|
|
145
180
|
default:
|
|
146
181
|
errorCode = UNAUTHENTICATED;
|
|
147
182
|
}
|
|
148
|
-
return new VsrEvictionError(errorCode);
|
|
183
|
+
return new VsrEvictionError(errorCode, eviction.reason);
|
|
149
184
|
};
|
|
150
185
|
//# sourceMappingURL=reply.js.map
|
|
@@ -53,7 +53,7 @@ describe('VSR reply decoding', () => {
|
|
|
53
53
|
assert.throws(() => decodeResponse(frame), ResponseError);
|
|
54
54
|
});
|
|
55
55
|
it('rejects an undeclared reply operation discriminant', () => {
|
|
56
|
-
for (const operation of [69,
|
|
56
|
+
for (const operation of [69, 129, 133, 142, 163, 255])
|
|
57
57
|
assert.throws(() => decodeResponse(reply(operation)), (error) => error instanceof ResponseError && error.errorCode === 3);
|
|
58
58
|
});
|
|
59
59
|
it('passes an empty register body through as a terminal failure', () => {
|
|
@@ -73,6 +73,16 @@ describe('VSR reply decoding', () => {
|
|
|
73
73
|
const body = Buffer.concat([Buffer.alloc(4), Buffer.from('stream')]);
|
|
74
74
|
assert.deepEqual(decodeResponse(reply(Operation.CreateStream, body)), Buffer.from('stream'));
|
|
75
75
|
});
|
|
76
|
+
it('strips the result section of every resolved update reply', () => {
|
|
77
|
+
for (const operation of [Operation.UpdateStreamResolved, Operation.UpdateTopicResolved,
|
|
78
|
+
Operation.UpdateUserResolved]) {
|
|
79
|
+
assert.deepEqual(decodeResponse(reply(operation, Buffer.alloc(4))), Buffer.alloc(0));
|
|
80
|
+
const refused = Buffer.alloc(12);
|
|
81
|
+
refused.writeUInt32LE(1, 0);
|
|
82
|
+
refused.writeUInt32LE(4103, 8);
|
|
83
|
+
assert.throws(() => decodeResponse(reply(operation, refused), COMMAND_CODE.UpdateResource), (error) => error instanceof ResponseError && error.errorCode === 4103);
|
|
84
|
+
}
|
|
85
|
+
});
|
|
76
86
|
it('strips a non-empty successful result section', () => {
|
|
77
87
|
const body = Buffer.alloc(12 + 6);
|
|
78
88
|
body.writeUInt32LE(1, 0);
|
|
@@ -3,16 +3,19 @@
|
|
|
3
3
|
*
|
|
4
4
|
* Each client instance generates an ephemeral random `clientId` (u128).
|
|
5
5
|
* After a Register commits, the server assigns a `session` number (commit op
|
|
6
|
-
* number). Every
|
|
7
|
-
*
|
|
8
|
-
* value because they bypass server-side request sequencing.
|
|
6
|
+
* number). Every request in a bound session advances its shared counter, including
|
|
7
|
+
* reads and requests on auxiliary connections.
|
|
9
8
|
*/
|
|
10
9
|
export declare class ConsensusSession {
|
|
11
10
|
private _clientId;
|
|
12
11
|
private _session;
|
|
13
12
|
private requestCounter;
|
|
14
13
|
private registerConsumed;
|
|
14
|
+
private sessionRequestStart;
|
|
15
|
+
readonly bindSecret: Buffer;
|
|
16
|
+
readonly registerRequestIdentifier: Buffer;
|
|
15
17
|
constructor(clientId?: bigint);
|
|
18
|
+
reset(): void;
|
|
16
19
|
get clientId(): bigint;
|
|
17
20
|
/** The bound session number, or null before Register commits. */
|
|
18
21
|
get session(): bigint | null;
|
|
@@ -21,18 +24,14 @@ export declare class ConsensusSession {
|
|
|
21
24
|
/** Binds the session after Register commits through consensus. */
|
|
22
25
|
bind(session: bigint): void;
|
|
23
26
|
/**
|
|
24
|
-
*
|
|
25
|
-
*
|
|
26
|
-
* unbound), so a repeat login encodes a clean Register instead of tripping
|
|
27
|
-
* the one-shot guard. Returns the register request id, always 0.
|
|
27
|
+
* Retains the identity and secret so a lost login reply can be retried.
|
|
28
|
+
* A new logical session is created only after explicit logout.
|
|
28
29
|
*/
|
|
29
30
|
beginRegister(): bigint;
|
|
30
31
|
/**
|
|
31
32
|
* Next application request id, advancing the counter: 1, 2, 3, ...
|
|
32
33
|
* Request 0 is reserved for Register.
|
|
33
34
|
*/
|
|
34
|
-
nextRequestId(): bigint;
|
|
35
|
-
/** Current counter value without advancing it. */
|
|
36
|
-
currentRequestId(): bigint;
|
|
35
|
+
nextRequestId(requireBound?: boolean): bigint;
|
|
37
36
|
}
|
|
38
37
|
//# sourceMappingURL=session.d.ts.map
|
package/dist/wire/vsr/session.js
CHANGED
|
@@ -15,26 +15,40 @@
|
|
|
15
15
|
// specific language governing permissions and limitations
|
|
16
16
|
// under the License.
|
|
17
17
|
import { randomBytes } from 'node:crypto';
|
|
18
|
+
import { BIND_SECRET_BYTES } from './register.js';
|
|
18
19
|
const MAX_U64 = 0xffffffffffffffffn;
|
|
20
|
+
const CLIENT_ID_MASK = (1n << 127n) - 1n;
|
|
19
21
|
/**
|
|
20
22
|
* Consensus-level session state, ported from `core/sdk/src/session.rs`.
|
|
21
23
|
*
|
|
22
24
|
* Each client instance generates an ephemeral random `clientId` (u128).
|
|
23
25
|
* After a Register commits, the server assigns a `session` number (commit op
|
|
24
|
-
* number). Every
|
|
25
|
-
*
|
|
26
|
-
* value because they bypass server-side request sequencing.
|
|
26
|
+
* number). Every request in a bound session advances its shared counter, including
|
|
27
|
+
* reads and requests on auxiliary connections.
|
|
27
28
|
*/
|
|
28
29
|
export class ConsensusSession {
|
|
29
30
|
_clientId;
|
|
30
31
|
_session;
|
|
31
32
|
requestCounter;
|
|
32
33
|
registerConsumed;
|
|
34
|
+
sessionRequestStart = 1n;
|
|
35
|
+
bindSecret;
|
|
36
|
+
registerRequestIdentifier;
|
|
33
37
|
constructor(clientId) {
|
|
34
38
|
this._clientId = clientId ?? generateClientId();
|
|
35
39
|
this._session = null;
|
|
36
40
|
this.requestCounter = 1n;
|
|
37
41
|
this.registerConsumed = false;
|
|
42
|
+
this.bindSecret = randomBytes(BIND_SECRET_BYTES);
|
|
43
|
+
this.registerRequestIdentifier = nonzeroIdentifier();
|
|
44
|
+
}
|
|
45
|
+
reset() {
|
|
46
|
+
this._clientId = generateClientId();
|
|
47
|
+
this._session = null;
|
|
48
|
+
this.registerConsumed = false;
|
|
49
|
+
this.sessionRequestStart = this.requestCounter;
|
|
50
|
+
randomBytes(BIND_SECRET_BYTES).copy(this.bindSecret);
|
|
51
|
+
nonzeroIdentifier().copy(this.registerRequestIdentifier);
|
|
38
52
|
}
|
|
39
53
|
get clientId() {
|
|
40
54
|
return this._clientId;
|
|
@@ -49,29 +63,21 @@ export class ConsensusSession {
|
|
|
49
63
|
get hasActivity() {
|
|
50
64
|
return this.registerConsumed ||
|
|
51
65
|
this._session !== null ||
|
|
52
|
-
this.requestCounter >
|
|
66
|
+
this.requestCounter > this.sessionRequestStart;
|
|
53
67
|
}
|
|
54
68
|
/** Binds the session after Register commits through consensus. */
|
|
55
69
|
bind(session) {
|
|
56
|
-
if (this._session !== null)
|
|
70
|
+
if (this._session !== null && this._session !== session)
|
|
57
71
|
throw new Error(`session already bound (session=${this._session})`);
|
|
58
72
|
if (session <= 0n)
|
|
59
73
|
throw new Error('session must be > 0');
|
|
60
74
|
this._session = session;
|
|
61
75
|
}
|
|
62
76
|
/**
|
|
63
|
-
*
|
|
64
|
-
*
|
|
65
|
-
* unbound), so a repeat login encodes a clean Register instead of tripping
|
|
66
|
-
* the one-shot guard. Returns the register request id, always 0.
|
|
77
|
+
* Retains the identity and secret so a lost login reply can be retried.
|
|
78
|
+
* A new logical session is created only after explicit logout.
|
|
67
79
|
*/
|
|
68
80
|
beginRegister() {
|
|
69
|
-
if (this.registerConsumed || this.isBound) {
|
|
70
|
-
this._clientId = generateClientId();
|
|
71
|
-
this._session = null;
|
|
72
|
-
this.requestCounter = 1n;
|
|
73
|
-
this.registerConsumed = false;
|
|
74
|
-
}
|
|
75
81
|
this.registerConsumed = true;
|
|
76
82
|
return 0n;
|
|
77
83
|
}
|
|
@@ -79,8 +85,8 @@ export class ConsensusSession {
|
|
|
79
85
|
* Next application request id, advancing the counter: 1, 2, 3, ...
|
|
80
86
|
* Request 0 is reserved for Register.
|
|
81
87
|
*/
|
|
82
|
-
nextRequestId() {
|
|
83
|
-
if (!this.isBound)
|
|
88
|
+
nextRequestId(requireBound = true) {
|
|
89
|
+
if (requireBound && !this.isBound)
|
|
84
90
|
throw new Error('nextRequestId called before bind');
|
|
85
91
|
if (this.requestCounter === MAX_U64)
|
|
86
92
|
throw new RangeError('VSR request counter exhausted');
|
|
@@ -88,15 +94,18 @@ export class ConsensusSession {
|
|
|
88
94
|
this.requestCounter += 1n;
|
|
89
95
|
return id;
|
|
90
96
|
}
|
|
91
|
-
/** Current counter value without advancing it. */
|
|
92
|
-
currentRequestId() {
|
|
93
|
-
return this.requestCounter;
|
|
94
|
-
}
|
|
95
97
|
}
|
|
96
|
-
|
|
98
|
+
function nonzeroIdentifier() {
|
|
99
|
+
let identifier;
|
|
100
|
+
do {
|
|
101
|
+
identifier = randomBytes(16);
|
|
102
|
+
} while (identifier.every(byte => byte === 0));
|
|
103
|
+
return identifier;
|
|
104
|
+
}
|
|
105
|
+
/** Keep the top bit clear so generated IDs cannot enter the server-only range. */
|
|
97
106
|
const generateClientId = () => {
|
|
98
107
|
for (;;) {
|
|
99
|
-
const id = randomBytes(16).reduce((acc, byte) => (acc << 8n) | BigInt(byte), 0n);
|
|
108
|
+
const id = randomBytes(16).reduce((acc, byte) => (acc << 8n) | BigInt(byte), 0n) & CLIENT_ID_MASK;
|
|
100
109
|
if (id !== 0n)
|
|
101
110
|
return id;
|
|
102
111
|
}
|
|
@@ -22,7 +22,15 @@ describe('ConsensusSession', () => {
|
|
|
22
22
|
const session = new ConsensusSession();
|
|
23
23
|
assert.notEqual(session.clientId, 0n);
|
|
24
24
|
assert.equal(session.session, null);
|
|
25
|
-
assert.equal(session.
|
|
25
|
+
assert.equal(session.nextRequestId(false), 1n);
|
|
26
|
+
});
|
|
27
|
+
it('generates client IDs with the top bit clear', () => {
|
|
28
|
+
const samples = 64;
|
|
29
|
+
const clientIdLimit = 1n << 127n;
|
|
30
|
+
for (let sample = 0; sample < samples; sample++) {
|
|
31
|
+
const session = new ConsensusSession();
|
|
32
|
+
assert.ok(session.clientId > 0n && session.clientId < clientIdLimit);
|
|
33
|
+
}
|
|
26
34
|
});
|
|
27
35
|
it('binds once to a positive server session', () => {
|
|
28
36
|
const session = new ConsensusSession(7n);
|
|
@@ -30,6 +38,7 @@ describe('ConsensusSession', () => {
|
|
|
30
38
|
assert.throws(() => session.bind(0n), /must be > 0/);
|
|
31
39
|
session.bind(42n);
|
|
32
40
|
assert.equal(session.session, 42n);
|
|
41
|
+
session.bind(42n);
|
|
33
42
|
assert.throws(() => session.bind(43n), /already bound/);
|
|
34
43
|
});
|
|
35
44
|
it('advances metadata IDs monotonically', () => {
|
|
@@ -38,7 +47,18 @@ describe('ConsensusSession', () => {
|
|
|
38
47
|
session.bind(42n);
|
|
39
48
|
assert.equal(session.nextRequestId(), 1n);
|
|
40
49
|
assert.equal(session.nextRequestId(), 2n);
|
|
41
|
-
assert.equal(session.
|
|
50
|
+
assert.equal(session.nextRequestId(), 3n);
|
|
51
|
+
});
|
|
52
|
+
it('resets activity and identity while preserving the shared request counter', () => {
|
|
53
|
+
const session = new ConsensusSession(7n);
|
|
54
|
+
assert.equal(session.hasActivity, false);
|
|
55
|
+
assert.equal(session.nextRequestId(false), 1n);
|
|
56
|
+
assert.equal(session.hasActivity, true);
|
|
57
|
+
session.reset();
|
|
58
|
+
assert.equal(session.hasActivity, false);
|
|
59
|
+
assert.notEqual(session.clientId, 7n);
|
|
60
|
+
assert.equal(session.nextRequestId(false), 2n);
|
|
61
|
+
assert.equal(session.hasActivity, true);
|
|
42
62
|
});
|
|
43
63
|
it('rejects request IDs before binding and after exhaustion', () => {
|
|
44
64
|
const session = new ConsensusSession(7n);
|
|
@@ -47,14 +67,20 @@ describe('ConsensusSession', () => {
|
|
|
47
67
|
session.requestCounter = 0xffffffffffffffffn;
|
|
48
68
|
assert.throws(() => session.nextRequestId(), /counter exhausted/);
|
|
49
69
|
});
|
|
50
|
-
it('
|
|
70
|
+
it('retains identity, proof and request numbering across login retries', () => {
|
|
51
71
|
const session = new ConsensusSession(7n);
|
|
72
|
+
const proof = Buffer.from(session.bindSecret);
|
|
52
73
|
session.beginRegister();
|
|
53
|
-
session.bind(42n);
|
|
54
74
|
session.beginRegister();
|
|
55
|
-
assert.equal(session.
|
|
56
|
-
assert.
|
|
57
|
-
|
|
75
|
+
assert.equal(session.clientId, 7n);
|
|
76
|
+
assert.deepEqual(session.bindSecret, proof);
|
|
77
|
+
session.bind(42n);
|
|
78
|
+
assert.equal(session.nextRequestId(), 1n);
|
|
79
|
+
assert.equal(session.beginRegister(), 0n);
|
|
80
|
+
assert.equal(session.session, 42n);
|
|
81
|
+
assert.equal(session.nextRequestId(), 2n);
|
|
82
|
+
assert.equal(session.clientId, 7n);
|
|
83
|
+
assert.deepEqual(session.bindSecret, proof);
|
|
58
84
|
});
|
|
59
85
|
});
|
|
60
86
|
//# sourceMappingURL=session.test.js.map
|
|
@@ -17,13 +17,64 @@
|
|
|
17
17
|
import { describe, it } from 'node:test';
|
|
18
18
|
import assert from 'node:assert/strict';
|
|
19
19
|
import { ResponseError } from '../error.utils.js';
|
|
20
|
-
import { HEADER_SIZE, REQUEST_OFFSET } from './header.js';
|
|
20
|
+
import { Command, COMMITTED_RESULT_MARKER, HEADER_SIZE, REPLY_OFFSET, REQUEST_OFFSET } from './header.js';
|
|
21
21
|
import { prepareVsrCommand, VsrSession } from './index.js';
|
|
22
22
|
import { Operation } from './operation.js';
|
|
23
|
+
import { validateReplyForRequest } from './reply.js';
|
|
23
24
|
import { COMMAND_CODE } from '../command.code.js';
|
|
24
25
|
import { serializeSendMessages } from '../message/message.utils.js';
|
|
25
26
|
import { Partitioning } from '../message/partitioning.utils.js';
|
|
27
|
+
const BIND_SECRET = Buffer.alloc(32, 0x5a);
|
|
26
28
|
describe('VSR custom request framing', () => {
|
|
29
|
+
it('stamps distinct random identifiers for metadata and session requests', () => {
|
|
30
|
+
const payloads = [Buffer.alloc(0), Buffer.from([1, 2, 3]), Buffer.from('abc')];
|
|
31
|
+
for (const command of [COMMAND_CODE.LoginRegister, COMMAND_CODE.LoginRegisterWithAccessToken,
|
|
32
|
+
COMMAND_CODE.LogoutUser, COMMAND_CODE.CreateStream, COMMAND_CODE.DeleteSegments]) {
|
|
33
|
+
const session = new VsrSession(7n);
|
|
34
|
+
session.bind(42n);
|
|
35
|
+
for (const payload of payloads) {
|
|
36
|
+
const frame = session.encode(command, payload);
|
|
37
|
+
const another = session.encode(command, payload);
|
|
38
|
+
const identifier = frame.subarray(REQUEST_OFFSET.requestChecksum, REQUEST_OFFSET.requestChecksum + 16);
|
|
39
|
+
assert.notDeepEqual(identifier, Buffer.alloc(16));
|
|
40
|
+
const repeated = another.subarray(REQUEST_OFFSET.requestChecksum, REQUEST_OFFSET.requestChecksum + 16);
|
|
41
|
+
if (command === COMMAND_CODE.LoginRegister || command === COMMAND_CODE.LoginRegisterWithAccessToken)
|
|
42
|
+
assert.deepEqual(identifier, repeated);
|
|
43
|
+
else
|
|
44
|
+
assert.notDeepEqual(identifier, repeated);
|
|
45
|
+
assert.deepEqual(frame.subarray(HEADER_SIZE), payload);
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
});
|
|
49
|
+
it('leaves partition and non-replicated request checksums unstamped', () => {
|
|
50
|
+
const session = new VsrSession(7n);
|
|
51
|
+
session.bind(42n);
|
|
52
|
+
for (const command of [COMMAND_CODE.SendMessages, COMMAND_CODE.StoreOffset,
|
|
53
|
+
COMMAND_CODE.DeleteConsumerOffset, COMMAND_CODE.Ping, COMMAND_CODE.GetPartitionRoute,
|
|
54
|
+
COMMAND_CODE.BindSession, 60_000]) {
|
|
55
|
+
const frame = session.encode(command, Buffer.from([1, 2, 3]));
|
|
56
|
+
assert.deepEqual(frame.subarray(REQUEST_OFFSET.requestChecksum, REQUEST_OFFSET.requestChecksum + 16), Buffer.alloc(16), `command ${command}`);
|
|
57
|
+
}
|
|
58
|
+
});
|
|
59
|
+
it('checks marked metadata receipts against their original payload stamp', () => {
|
|
60
|
+
const session = new VsrSession(7n);
|
|
61
|
+
session.bind(42n);
|
|
62
|
+
const request = session.encode(COMMAND_CODE.CreateStream, Buffer.from([1, 2, 3]));
|
|
63
|
+
const differentPayload = session.encode(COMMAND_CODE.CreateStream, Buffer.from('abc'));
|
|
64
|
+
const receipt = Buffer.alloc(HEADER_SIZE);
|
|
65
|
+
receipt.writeUInt32LE(HEADER_SIZE, REPLY_OFFSET.size);
|
|
66
|
+
receipt[REPLY_OFFSET.command] = Command.Reply;
|
|
67
|
+
receipt[REPLY_OFFSET.operation] = Operation.CreateStream;
|
|
68
|
+
receipt[REPLY_OFFSET.reserved] = COMMITTED_RESULT_MARKER;
|
|
69
|
+
receipt.writeBigUInt64LE(1n, REPLY_OFFSET.op);
|
|
70
|
+
receipt.writeBigUInt64LE(1n, REPLY_OFFSET.commit);
|
|
71
|
+
receipt.writeBigUInt64LE(request.readBigUInt64LE(REQUEST_OFFSET.request), REPLY_OFFSET.request);
|
|
72
|
+
request.copy(receipt, REPLY_OFFSET.client, REQUEST_OFFSET.client, REQUEST_OFFSET.client + 16);
|
|
73
|
+
request.copy(receipt, REPLY_OFFSET.requestChecksum, REQUEST_OFFSET.requestChecksum, REQUEST_OFFSET.requestChecksum + 16);
|
|
74
|
+
assert.doesNotThrow(() => validateReplyForRequest(receipt, request, COMMAND_CODE.CreateStream));
|
|
75
|
+
differentPayload.copy(receipt, REPLY_OFFSET.requestChecksum, REQUEST_OFFSET.requestChecksum, REQUEST_OFFSET.requestChecksum + 16);
|
|
76
|
+
assert.throws(() => validateReplyForRequest(receipt, request, COMMAND_CODE.CreateStream), (error) => error instanceof ResponseError && error.commandCode === COMMAND_CODE.CreateStream && error.errorCode === 3);
|
|
77
|
+
});
|
|
27
78
|
it('encodes a custom non-replicated code and opaque payload', () => {
|
|
28
79
|
const session = new VsrSession();
|
|
29
80
|
const payload = Buffer.from([0xAA, 0xBB, 0xCC]);
|
|
@@ -37,24 +88,24 @@ describe('VSR custom request framing', () => {
|
|
|
37
88
|
assert.throws(() => session.encode(COMMAND_CODE.CreateStream, Buffer.alloc(0)), (error) => error instanceof ResponseError && error.errorCode === 40);
|
|
38
89
|
});
|
|
39
90
|
it('rejects a truncated legacy login payload locally', () => {
|
|
40
|
-
assert.throws(() => prepareVsrCommand(COMMAND_CODE.LoginUser, Buffer.alloc(0)));
|
|
41
|
-
assert.throws(() => prepareVsrCommand(COMMAND_CODE.LoginUser, Buffer.from([4, 0x69, 0x67])));
|
|
91
|
+
assert.throws(() => prepareVsrCommand(COMMAND_CODE.LoginUser, Buffer.alloc(0), BIND_SECRET));
|
|
92
|
+
assert.throws(() => prepareVsrCommand(COMMAND_CODE.LoginUser, Buffer.from([4, 0x69, 0x67]), BIND_SECRET));
|
|
42
93
|
});
|
|
43
94
|
it('converts legacy token login to VSR registration', () => {
|
|
44
95
|
const token = Buffer.from('secret');
|
|
45
|
-
const prepared = prepareVsrCommand(COMMAND_CODE.LoginWithAccessToken, Buffer.concat([Buffer.from([token.length]), token]));
|
|
96
|
+
const prepared = prepareVsrCommand(COMMAND_CODE.LoginWithAccessToken, Buffer.concat([Buffer.from([token.length]), token]), BIND_SECRET);
|
|
46
97
|
assert.equal(prepared.command, COMMAND_CODE.LoginRegisterWithAccessToken);
|
|
47
98
|
assert.ok(prepared.payload.includes(token));
|
|
48
99
|
const frame = new VsrSession(7n).encode(prepared.command, prepared.payload);
|
|
49
100
|
assert.equal(frame.readUInt8(REQUEST_OFFSET.operation), Operation.Register);
|
|
50
101
|
});
|
|
51
|
-
it('
|
|
102
|
+
it('assigns distinct request ids to bound reads and writes', () => {
|
|
52
103
|
const session = new VsrSession(7n);
|
|
53
104
|
session.bind(42n);
|
|
54
105
|
const custom = session.encode(60_001, Buffer.alloc(0));
|
|
55
106
|
assert.equal(custom.readBigUInt64LE(REQUEST_OFFSET.request), 1n);
|
|
56
107
|
const metadata = session.encode(COMMAND_CODE.CreateStream, Buffer.alloc(0));
|
|
57
|
-
assert.equal(metadata.readBigUInt64LE(REQUEST_OFFSET.request),
|
|
108
|
+
assert.equal(metadata.readBigUInt64LE(REQUEST_OFFSET.request), 2n);
|
|
58
109
|
assert.equal(metadata.length, HEADER_SIZE);
|
|
59
110
|
});
|
|
60
111
|
it('partition operations consume a distinct id per send', () => {
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "apache-iggy",
|
|
3
3
|
"type": "module",
|
|
4
|
-
"version": "0.10.
|
|
4
|
+
"version": "0.10.1-edge.2",
|
|
5
5
|
"description": "Official Apache Iggy NodeJS SDK",
|
|
6
6
|
"keywords": [
|
|
7
7
|
"iggy",
|
|
@@ -50,18 +50,17 @@
|
|
|
50
50
|
"author": "github.com/T1B0",
|
|
51
51
|
"license": "Apache-2.0",
|
|
52
52
|
"dependencies": {
|
|
53
|
-
"@node-rs/xxhash": "1.7.
|
|
53
|
+
"@node-rs/xxhash": "1.7.8",
|
|
54
54
|
"debug": "4.4.3",
|
|
55
|
-
"generic-pool": "3.9.0",
|
|
56
55
|
"uuidv7": "1.2.1"
|
|
57
56
|
},
|
|
58
57
|
"devDependencies": {
|
|
59
|
-
"@commitlint/cli": "21.2.
|
|
60
|
-
"@commitlint/config-conventional": "21.2.
|
|
61
|
-
"@cucumber/cucumber": "13.
|
|
58
|
+
"@commitlint/cli": "21.2.3",
|
|
59
|
+
"@commitlint/config-conventional": "21.2.3",
|
|
60
|
+
"@cucumber/cucumber": "13.3.0",
|
|
62
61
|
"@swc-node/register": "1.12.1",
|
|
63
62
|
"@types/debug": "4.1.13",
|
|
64
|
-
"@types/node": "26.4
|
|
63
|
+
"@types/node": "26.6.4",
|
|
65
64
|
"c8": "^12.0.0",
|
|
66
65
|
"husky": "9.1.7",
|
|
67
66
|
"typescript": "6.0.3",
|
|
@@ -1,26 +0,0 @@
|
|
|
1
|
-
import { type Id } from '../identifier.utils.js';
|
|
2
|
-
/**
|
|
3
|
-
* Parameters for the flush unsaved buffers command.
|
|
4
|
-
*/
|
|
5
|
-
export type FlushUnsavedBuffer = {
|
|
6
|
-
/** Stream identifier */
|
|
7
|
-
streamId: Id;
|
|
8
|
-
/** Topic identifier */
|
|
9
|
-
topicId: Id;
|
|
10
|
-
/** Partition ID to flush */
|
|
11
|
-
partitionId: number;
|
|
12
|
-
};
|
|
13
|
-
/**
|
|
14
|
-
* Flush unsaved buffers command definition.
|
|
15
|
-
* Forces buffered messages to be persisted to disk.
|
|
16
|
-
*/
|
|
17
|
-
export declare const FLUSH_UNSAVED_BUFFERS: {
|
|
18
|
-
code: number;
|
|
19
|
-
serialize: ({ streamId, topicId, partitionId }: FlushUnsavedBuffer) => Buffer<ArrayBuffer>;
|
|
20
|
-
deserialize: (r: import("../../index.js").CommandResponse) => boolean;
|
|
21
|
-
};
|
|
22
|
-
/**
|
|
23
|
-
* Executable flush unsaved buffers command function.
|
|
24
|
-
*/
|
|
25
|
-
export declare const flushUnsavedBuffers: (getClient: import("../../index.js").ClientProvider) => (arg: FlushUnsavedBuffer) => Promise<boolean>;
|
|
26
|
-
//# sourceMappingURL=flush-unsaved-buffers.command.d.ts.map
|
|
@@ -1,36 +0,0 @@
|
|
|
1
|
-
// Licensed to the Apache Software Foundation (ASF) under one
|
|
2
|
-
// or more contributor license agreements. See the NOTICE file
|
|
3
|
-
// distributed with this work for additional information
|
|
4
|
-
// regarding copyright ownership. The ASF licenses this file
|
|
5
|
-
// to you under the Apache License, Version 2.0 (the
|
|
6
|
-
// "License"); you may not use this file except in compliance
|
|
7
|
-
// with the License. You may obtain a copy of the License at
|
|
8
|
-
//
|
|
9
|
-
// http://www.apache.org/licenses/LICENSE-2.0
|
|
10
|
-
//
|
|
11
|
-
// Unless required by applicable law or agreed to in writing,
|
|
12
|
-
// software distributed under the License is distributed on an
|
|
13
|
-
// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
|
14
|
-
// KIND, either express or implied. See the License for the
|
|
15
|
-
// specific language governing permissions and limitations
|
|
16
|
-
// under the License.
|
|
17
|
-
import { serializeFlushUnsavedBuffers } from './message.utils.js';
|
|
18
|
-
import { deserializeVoidResponse } from '../../client/client.utils.js';
|
|
19
|
-
import { wrapCommand } from '../command.utils.js';
|
|
20
|
-
import { COMMAND_CODE } from '../command.code.js';
|
|
21
|
-
/**
|
|
22
|
-
* Flush unsaved buffers command definition.
|
|
23
|
-
* Forces buffered messages to be persisted to disk.
|
|
24
|
-
*/
|
|
25
|
-
export const FLUSH_UNSAVED_BUFFERS = {
|
|
26
|
-
code: COMMAND_CODE.FlushUnsavedBuffers,
|
|
27
|
-
serialize: ({ streamId, topicId, partitionId }) => {
|
|
28
|
-
return serializeFlushUnsavedBuffers(streamId, topicId, partitionId);
|
|
29
|
-
},
|
|
30
|
-
deserialize: deserializeVoidResponse
|
|
31
|
-
};
|
|
32
|
-
/**
|
|
33
|
-
* Executable flush unsaved buffers command function.
|
|
34
|
-
*/
|
|
35
|
-
export const flushUnsavedBuffers = wrapCommand(FLUSH_UNSAVED_BUFFERS);
|
|
36
|
-
//# sourceMappingURL=flush-unsaved-buffers.command.js.map
|
|
@@ -1,22 +0,0 @@
|
|
|
1
|
-
import { type Id } from '../identifier.utils.js';
|
|
2
|
-
/**
|
|
3
|
-
* Parameters for the purge stream command.
|
|
4
|
-
*/
|
|
5
|
-
export type PurgeStream = {
|
|
6
|
-
/** Stream identifier (ID or name) */
|
|
7
|
-
streamId: Id;
|
|
8
|
-
};
|
|
9
|
-
/**
|
|
10
|
-
* Purge stream command definition.
|
|
11
|
-
* Deletes all messages from a stream while keeping the stream itself.
|
|
12
|
-
*/
|
|
13
|
-
export declare const PURGE_STREAM: {
|
|
14
|
-
code: number;
|
|
15
|
-
serialize: ({ streamId }: PurgeStream) => Buffer<ArrayBufferLike>;
|
|
16
|
-
deserialize: (r: import("../../index.js").CommandResponse) => boolean;
|
|
17
|
-
};
|
|
18
|
-
/**
|
|
19
|
-
* Executable purge stream command function.
|
|
20
|
-
*/
|
|
21
|
-
export declare const purgeStream: (getClient: import("../../index.js").ClientProvider) => (arg: PurgeStream) => Promise<boolean>;
|
|
22
|
-
//# sourceMappingURL=purge-stream.command.d.ts.map
|