@arbitro/client 0.2.0 → 0.4.1
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 +32 -22
- package/dist/chunk-6BCX2E2R.mjs +102 -0
- package/dist/chunk-6BCX2E2R.mjs.map +1 -0
- package/dist/chunk-SKCXQO7R.mjs +124 -0
- package/dist/chunk-SKCXQO7R.mjs.map +1 -0
- package/dist/constants-KF57DJ2L.mjs +33 -0
- package/dist/constants-KF57DJ2L.mjs.map +1 -0
- package/dist/index.d.mts +69 -1
- package/dist/index.d.ts +69 -1
- package/dist/index.js +422 -94
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +184 -98
- package/dist/index.mjs.map +1 -1
- package/dist/publish-UA3YZGMK.mjs +14 -0
- package/dist/publish-UA3YZGMK.mjs.map +1 -0
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -41,14 +41,14 @@ npm install @arbitro/client
|
|
|
41
41
|
The broker ships as a public Docker image (musl-static, ~3 MB, scratch base):
|
|
42
42
|
|
|
43
43
|
```bash
|
|
44
|
-
docker run --rm -p 9898:9898 ghcr.io/arbitro-io/arbitro-server:
|
|
44
|
+
docker run --rm -p 9898:9898 ghcr.io/arbitro-io/arbitro-server:latest
|
|
45
45
|
```
|
|
46
46
|
|
|
47
47
|
Pin a major/minor tag for production:
|
|
48
48
|
|
|
49
|
-
- `ghcr.io/arbitro-io/arbitro-server:0.1.0` — immutable
|
|
49
|
+
- `ghcr.io/arbitro-io/arbitro-server:0.1.0` — immutable release tag
|
|
50
50
|
- `ghcr.io/arbitro-io/arbitro-server:0.1` — auto-updates within `0.1.*`
|
|
51
|
-
- `ghcr.io/arbitro-io/arbitro-server:latest` —
|
|
51
|
+
- `ghcr.io/arbitro-io/arbitro-server:latest` — latest tagged release
|
|
52
52
|
|
|
53
53
|
## Quick start
|
|
54
54
|
|
|
@@ -219,33 +219,43 @@ const sub = await client
|
|
|
219
219
|
|
|
220
220
|
`zod` is an optional peer dependency. `@arbitro/client` references zod only via `import type`, so users who never call `zodCodec` pay zero runtime cost and don't need zod installed.
|
|
221
221
|
|
|
222
|
-
##
|
|
223
|
-
|
|
224
|
-
The TS client reconnects transport automatically and reattaches active subscriptions after reconnect. That behavior lives in the client, not in the benchmarks. This matters for:
|
|
222
|
+
## Cron Scheduling
|
|
225
223
|
|
|
226
|
-
|
|
227
|
-
- broker failover tests
|
|
228
|
-
- chaos scenarios with durable consumers
|
|
224
|
+
Register distributed cron jobs with queue semantics — multiple workers, single delivery per fire.
|
|
229
225
|
|
|
230
|
-
|
|
226
|
+
```typescript
|
|
227
|
+
const cron = await client.cron("billing-monthly")
|
|
228
|
+
.every("0 0 1 * *")
|
|
229
|
+
.tz("America/New_York")
|
|
230
|
+
.run(async (ctx) => {
|
|
231
|
+
console.log(`fire #${ctx.fireCount} at ${ctx.fireTime}`);
|
|
232
|
+
await processBilling();
|
|
233
|
+
});
|
|
234
|
+
|
|
235
|
+
// Stop when done
|
|
236
|
+
await cron.stop();
|
|
237
|
+
```
|
|
231
238
|
|
|
232
|
-
|
|
239
|
+
Crons re-register automatically on reconnect. No persistence — if the broker restarts, clients re-register their crons when they reconnect.
|
|
233
240
|
|
|
234
|
-
|
|
235
|
-
|---|---|---|
|
|
236
|
-
| `benches/throughput.ts` | `npm run bench` | Publish + delivery throughput across modes (fire-and-forget, batch, sync, replay-ack, replay-noack). |
|
|
237
|
-
| `benches/limits.ts` | `npm run bench:limit` | Behaviour under `maxSubjectInflights` saturation. |
|
|
238
|
-
| `benches/chaos.ts` | `npm run bench:chaos` | Restart / reconnect / persistence under failure injection. |
|
|
241
|
+
## Delayed Publish
|
|
239
242
|
|
|
240
|
-
|
|
243
|
+
Schedule message delivery for the future:
|
|
241
244
|
|
|
242
|
-
```
|
|
243
|
-
|
|
244
|
-
npx tsx benches/throughput.ts --mode perf --seconds 10 --rate 20000 --container arbitro-server
|
|
245
|
-
npx tsx benches/limits.ts
|
|
246
|
-
npx tsx benches/chaos.ts --duration 8 --rate 50 --container arbitro-server
|
|
245
|
+
```typescript
|
|
246
|
+
await client.publishDelayed("ORDERS", "orders.reminder", payload, 5000); // 5s delay
|
|
247
247
|
```
|
|
248
248
|
|
|
249
|
+
Messages are persisted immediately — survives broker restart.
|
|
250
|
+
|
|
251
|
+
## Reconnect behavior
|
|
252
|
+
|
|
253
|
+
The TS client reconnects transport automatically and reattaches active subscriptions and cron jobs after reconnect. That behavior lives in the client, not in the benchmarks. This matters for:
|
|
254
|
+
|
|
255
|
+
- Docker restarts
|
|
256
|
+
- broker failover tests
|
|
257
|
+
- chaos scenarios with durable consumers
|
|
258
|
+
|
|
249
259
|
## Validation
|
|
250
260
|
|
|
251
261
|
```bash
|
|
@@ -0,0 +1,102 @@
|
|
|
1
|
+
// src/proto/constants.ts
|
|
2
|
+
var MAGIC_V2 = 843207233;
|
|
3
|
+
var HELLO_SIZE = 8;
|
|
4
|
+
var CURRENT_VERSION = 2;
|
|
5
|
+
var Role = /* @__PURE__ */ ((Role2) => {
|
|
6
|
+
Role2[Role2["Client"] = 0] = "Client";
|
|
7
|
+
Role2[Role2["Server"] = 1] = "Server";
|
|
8
|
+
return Role2;
|
|
9
|
+
})(Role || {});
|
|
10
|
+
var Cap = /* @__PURE__ */ ((Cap2) => {
|
|
11
|
+
Cap2[Cap2["Headers"] = 1] = "Headers";
|
|
12
|
+
Cap2[Cap2["Reply"] = 2] = "Reply";
|
|
13
|
+
Cap2[Cap2["BatchHeaders"] = 4] = "BatchHeaders";
|
|
14
|
+
Cap2[Cap2["CompressedPayload"] = 8] = "CompressedPayload";
|
|
15
|
+
return Cap2;
|
|
16
|
+
})(Cap || {});
|
|
17
|
+
var HEADER_SIZE = 16;
|
|
18
|
+
var OFF_ACTION = 0;
|
|
19
|
+
var OFF_FLAGS = 2;
|
|
20
|
+
var OFF_ENTRY_FLAGS = 3;
|
|
21
|
+
var OFF_MSG_LEN = 4;
|
|
22
|
+
var OFF_SEQ = 8;
|
|
23
|
+
var Flag = /* @__PURE__ */ ((Flag2) => {
|
|
24
|
+
Flag2[Flag2["None"] = 0] = "None";
|
|
25
|
+
Flag2[Flag2["AckReq"] = 1] = "AckReq";
|
|
26
|
+
Flag2[Flag2["Dup"] = 2] = "Dup";
|
|
27
|
+
Flag2[Flag2["PriorityHigh"] = 4] = "PriorityHigh";
|
|
28
|
+
return Flag2;
|
|
29
|
+
})(Flag || {});
|
|
30
|
+
var EntryFlag = /* @__PURE__ */ ((EntryFlag2) => {
|
|
31
|
+
EntryFlag2[EntryFlag2["None"] = 0] = "None";
|
|
32
|
+
EntryFlag2[EntryFlag2["Retain"] = 1] = "Retain";
|
|
33
|
+
EntryFlag2[EntryFlag2["Compressed"] = 2] = "Compressed";
|
|
34
|
+
EntryFlag2[EntryFlag2["NoBackpressure"] = 4] = "NoBackpressure";
|
|
35
|
+
return EntryFlag2;
|
|
36
|
+
})(EntryFlag || {});
|
|
37
|
+
var Action = /* @__PURE__ */ ((Action2) => {
|
|
38
|
+
Action2[Action2["Hello"] = 1] = "Hello";
|
|
39
|
+
Action2[Action2["Auth"] = 2] = "Auth";
|
|
40
|
+
Action2[Action2["Publish"] = 257] = "Publish";
|
|
41
|
+
Action2[Action2["PublishAccumulate"] = 258] = "PublishAccumulate";
|
|
42
|
+
Action2[Action2["PublishBatch"] = 259] = "PublishBatch";
|
|
43
|
+
Action2[Action2["PublishWithReply"] = 260] = "PublishWithReply";
|
|
44
|
+
Action2[Action2["PublishWithHeaders"] = 261] = "PublishWithHeaders";
|
|
45
|
+
Action2[Action2["PublishBatchWithHeaders"] = 262] = "PublishBatchWithHeaders";
|
|
46
|
+
Action2[Action2["Deliver"] = 512] = "Deliver";
|
|
47
|
+
Action2[Action2["Ack"] = 513] = "Ack";
|
|
48
|
+
Action2[Action2["Nack"] = 514] = "Nack";
|
|
49
|
+
Action2[Action2["RepOk"] = 515] = "RepOk";
|
|
50
|
+
Action2[Action2["RepError"] = 516] = "RepError";
|
|
51
|
+
Action2[Action2["RepBatch"] = 517] = "RepBatch";
|
|
52
|
+
Action2[Action2["BatchAck"] = 518] = "BatchAck";
|
|
53
|
+
Action2[Action2["FanoutBatch"] = 519] = "FanoutBatch";
|
|
54
|
+
Action2[Action2["AckSync"] = 520] = "AckSync";
|
|
55
|
+
Action2[Action2["BatchAckSync"] = 521] = "BatchAckSync";
|
|
56
|
+
Action2[Action2["BatchNack"] = 522] = "BatchNack";
|
|
57
|
+
Action2[Action2["Subscribe"] = 769] = "Subscribe";
|
|
58
|
+
Action2[Action2["Unsubscribe"] = 770] = "Unsubscribe";
|
|
59
|
+
Action2[Action2["CreateStream"] = 1025] = "CreateStream";
|
|
60
|
+
Action2[Action2["DeleteStream"] = 1026] = "DeleteStream";
|
|
61
|
+
Action2[Action2["GetStream"] = 1027] = "GetStream";
|
|
62
|
+
Action2[Action2["ListStreams"] = 1028] = "ListStreams";
|
|
63
|
+
Action2[Action2["PurgeStream"] = 1029] = "PurgeStream";
|
|
64
|
+
Action2[Action2["DrainSubject"] = 1030] = "DrainSubject";
|
|
65
|
+
Action2[Action2["CreateConsumer"] = 1281] = "CreateConsumer";
|
|
66
|
+
Action2[Action2["DeleteConsumer"] = 1282] = "DeleteConsumer";
|
|
67
|
+
Action2[Action2["GetConsumer"] = 1283] = "GetConsumer";
|
|
68
|
+
Action2[Action2["ListConsumers"] = 1284] = "ListConsumers";
|
|
69
|
+
Action2[Action2["ConsumerStats"] = 1285] = "ConsumerStats";
|
|
70
|
+
Action2[Action2["PauseConsumer"] = 1286] = "PauseConsumer";
|
|
71
|
+
Action2[Action2["ResumeConsumer"] = 1287] = "ResumeConsumer";
|
|
72
|
+
Action2[Action2["PublishDelayed"] = 2049] = "PublishDelayed";
|
|
73
|
+
Action2[Action2["Ping"] = 1537] = "Ping";
|
|
74
|
+
Action2[Action2["Pong"] = 1538] = "Pong";
|
|
75
|
+
Action2[Action2["Connect"] = 1539] = "Connect";
|
|
76
|
+
Action2[Action2["Connected"] = 1540] = "Connected";
|
|
77
|
+
Action2[Action2["Disconnect"] = 1541] = "Disconnect";
|
|
78
|
+
Action2[Action2["CreateCron"] = 1793] = "CreateCron";
|
|
79
|
+
Action2[Action2["DeleteCron"] = 1794] = "DeleteCron";
|
|
80
|
+
Action2[Action2["ListCrons"] = 1795] = "ListCrons";
|
|
81
|
+
Action2[Action2["CronFire"] = 1796] = "CronFire";
|
|
82
|
+
Action2[Action2["CronAck"] = 1797] = "CronAck";
|
|
83
|
+
return Action2;
|
|
84
|
+
})(Action || {});
|
|
85
|
+
|
|
86
|
+
export {
|
|
87
|
+
MAGIC_V2,
|
|
88
|
+
HELLO_SIZE,
|
|
89
|
+
CURRENT_VERSION,
|
|
90
|
+
Role,
|
|
91
|
+
Cap,
|
|
92
|
+
HEADER_SIZE,
|
|
93
|
+
OFF_ACTION,
|
|
94
|
+
OFF_FLAGS,
|
|
95
|
+
OFF_ENTRY_FLAGS,
|
|
96
|
+
OFF_MSG_LEN,
|
|
97
|
+
OFF_SEQ,
|
|
98
|
+
Flag,
|
|
99
|
+
EntryFlag,
|
|
100
|
+
Action
|
|
101
|
+
};
|
|
102
|
+
//# sourceMappingURL=chunk-6BCX2E2R.mjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/proto/constants.ts"],"sourcesContent":["// V2 wire protocol constants — must match arbitro-proto/src/action.rs exactly.\n\n// ── Hello handshake (only frame without a Header) ───────────────────────\nexport const MAGIC_V2 = 0x32425241 // \"ARB2\" as u32 LE\nexport const HELLO_SIZE = 8\nexport const CURRENT_VERSION = 2\n\nexport const enum Role {\n Client = 0,\n Server = 1,\n}\n\nexport const enum Cap {\n Headers = 1 << 0,\n Reply = 1 << 1,\n BatchHeaders = 1 << 2,\n CompressedPayload = 1 << 3,\n}\n\n// ── Header (16 bytes, every frame after Hello) ──────────────────────────\nexport const HEADER_SIZE = 16\n\n// Byte offsets within the 16-byte header\nexport const OFF_ACTION = 0 // u16 LE\nexport const OFF_FLAGS = 2 // u8\nexport const OFF_ENTRY_FLAGS = 3 // u8\nexport const OFF_MSG_LEN = 4 // u32 LE\nexport const OFF_SEQ = 8 // u64 LE\n\n// ── Transport flags (header.flags, offset 2) ────────────────────────────\nexport const enum Flag {\n None = 0x00,\n AckReq = 0x01,\n Dup = 0x02,\n PriorityHigh = 0x04,\n}\n\n// ── Per-message flags (header.entry_flags, offset 3) ────────────────────\nexport const enum EntryFlag {\n None = 0x00,\n Retain = 0x01,\n Compressed = 0x02,\n NoBackpressure = 0x04,\n}\n\n// ── Action codes (0xFFGG: FF=family, GG=variant) ────────────────────────\nexport const enum Action {\n // 0x00xx — Handshake / control\n Hello = 0x0001,\n Auth = 0x0002,\n\n // 0x01xx — Publish family\n Publish = 0x0101,\n PublishAccumulate = 0x0102,\n PublishBatch = 0x0103,\n PublishWithReply = 0x0104,\n PublishWithHeaders = 0x0105,\n PublishBatchWithHeaders = 0x0106,\n\n // 0x02xx — Delivery / Ack\n Deliver = 0x0200,\n Ack = 0x0201,\n Nack = 0x0202,\n RepOk = 0x0203,\n RepError = 0x0204,\n RepBatch = 0x0205,\n BatchAck = 0x0206,\n FanoutBatch = 0x0207,\n AckSync = 0x0208,\n BatchAckSync = 0x0209,\n BatchNack = 0x020A,\n\n // 0x03xx — Subscription\n Subscribe = 0x0301,\n Unsubscribe = 0x0302,\n\n // 0x04xx — Stream management\n CreateStream = 0x0401,\n DeleteStream = 0x0402,\n GetStream = 0x0403,\n ListStreams = 0x0404,\n PurgeStream = 0x0405,\n DrainSubject = 0x0406,\n\n // 0x05xx — Consumer management\n CreateConsumer = 0x0501,\n DeleteConsumer = 0x0502,\n GetConsumer = 0x0503,\n ListConsumers = 0x0504,\n ConsumerStats = 0x0505,\n PauseConsumer = 0x0506,\n ResumeConsumer = 0x0507,\n\n // 0x08xx — Delayed publish\n PublishDelayed = 0x0801,\n\n // 0x06xx — System\n Ping = 0x0601,\n Pong = 0x0602,\n Connect = 0x0603,\n Connected = 0x0604,\n Disconnect = 0x0605,\n\n // 0x07xx — Cron scheduling\n CreateCron = 0x0701,\n DeleteCron = 0x0702,\n ListCrons = 0x0703,\n CronFire = 0x0704,\n CronAck = 0x0705,\n}\n"],"mappings":"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","names":["Role","Cap","Flag","EntryFlag","Action"]}
|
|
@@ -0,0 +1,124 @@
|
|
|
1
|
+
import {
|
|
2
|
+
CURRENT_VERSION,
|
|
3
|
+
HEADER_SIZE,
|
|
4
|
+
HELLO_SIZE,
|
|
5
|
+
MAGIC_V2,
|
|
6
|
+
OFF_ACTION,
|
|
7
|
+
OFF_ENTRY_FLAGS,
|
|
8
|
+
OFF_FLAGS,
|
|
9
|
+
OFF_MSG_LEN,
|
|
10
|
+
OFF_SEQ
|
|
11
|
+
} from "./chunk-6BCX2E2R.mjs";
|
|
12
|
+
|
|
13
|
+
// src/proto/frame.ts
|
|
14
|
+
function frame(action, seq, bodyLen, flags = 0, entryFlags = 0) {
|
|
15
|
+
const buf = Buffer.allocUnsafe(HEADER_SIZE + bodyLen);
|
|
16
|
+
buf.writeUInt16LE(action, OFF_ACTION);
|
|
17
|
+
buf[OFF_FLAGS] = flags;
|
|
18
|
+
buf[OFF_ENTRY_FLAGS] = entryFlags;
|
|
19
|
+
buf.writeUInt32LE(bodyLen, OFF_MSG_LEN);
|
|
20
|
+
buf.writeBigUInt64LE(seq, OFF_SEQ);
|
|
21
|
+
return buf;
|
|
22
|
+
}
|
|
23
|
+
function packHello(caps = 2 /* Reply */) {
|
|
24
|
+
const buf = Buffer.allocUnsafe(HELLO_SIZE);
|
|
25
|
+
buf.writeUInt32LE(MAGIC_V2, 0);
|
|
26
|
+
buf[4] = CURRENT_VERSION;
|
|
27
|
+
buf[5] = 0 /* Client */;
|
|
28
|
+
buf.writeUInt16LE(caps, 6);
|
|
29
|
+
return buf;
|
|
30
|
+
}
|
|
31
|
+
function packDisconnect(seq) {
|
|
32
|
+
return frame(1541 /* Disconnect */, seq, 0);
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
// src/proto/publish.ts
|
|
36
|
+
var EMPTY = Buffer.alloc(0);
|
|
37
|
+
function packPublish(seq, streamId, subject, payload, flags = 0, entryFlags = 0, msgId = EMPTY) {
|
|
38
|
+
const buf = frame(
|
|
39
|
+
257 /* Publish */,
|
|
40
|
+
seq,
|
|
41
|
+
8 + subject.length + msgId.length + payload.length,
|
|
42
|
+
flags,
|
|
43
|
+
entryFlags
|
|
44
|
+
);
|
|
45
|
+
buf.writeUInt32LE(streamId, HEADER_SIZE);
|
|
46
|
+
buf.writeUInt16LE(subject.length, HEADER_SIZE + 4);
|
|
47
|
+
buf.writeUInt16LE(msgId.length, HEADER_SIZE + 6);
|
|
48
|
+
let off = HEADER_SIZE + 8;
|
|
49
|
+
subject.copy(buf, off);
|
|
50
|
+
off += subject.length;
|
|
51
|
+
msgId.copy(buf, off);
|
|
52
|
+
off += msgId.length;
|
|
53
|
+
payload.copy(buf, off);
|
|
54
|
+
return buf;
|
|
55
|
+
}
|
|
56
|
+
function packPublishWithReply(seq, streamId, subject, replyTo, payload, flags = 0, entryFlags = 0) {
|
|
57
|
+
const tail = subject.length + replyTo.length + payload.length;
|
|
58
|
+
const buf = frame(260 /* PublishWithReply */, seq, 12 + tail, flags, entryFlags);
|
|
59
|
+
buf.writeUInt32LE(streamId, HEADER_SIZE);
|
|
60
|
+
buf.writeUInt16LE(subject.length, HEADER_SIZE + 4);
|
|
61
|
+
buf.writeUInt16LE(replyTo.length, HEADER_SIZE + 6);
|
|
62
|
+
buf.writeUInt32LE(0, HEADER_SIZE + 8);
|
|
63
|
+
let off = HEADER_SIZE + 12;
|
|
64
|
+
subject.copy(buf, off);
|
|
65
|
+
off += subject.length;
|
|
66
|
+
replyTo.copy(buf, off);
|
|
67
|
+
off += replyTo.length;
|
|
68
|
+
payload.copy(buf, off);
|
|
69
|
+
return buf;
|
|
70
|
+
}
|
|
71
|
+
function packPublishDelayed(seq, streamId, subject, payload, delayMs, flags = 0, entryFlags = 0) {
|
|
72
|
+
const buf = frame(
|
|
73
|
+
2049 /* PublishDelayed */,
|
|
74
|
+
seq,
|
|
75
|
+
16 + subject.length + payload.length,
|
|
76
|
+
flags,
|
|
77
|
+
entryFlags
|
|
78
|
+
);
|
|
79
|
+
buf.writeUInt32LE(streamId, HEADER_SIZE);
|
|
80
|
+
buf.writeUInt16LE(subject.length, HEADER_SIZE + 4);
|
|
81
|
+
buf.writeUInt16LE(0, HEADER_SIZE + 6);
|
|
82
|
+
buf.writeBigUInt64LE(delayMs, HEADER_SIZE + 8);
|
|
83
|
+
let off = HEADER_SIZE + 16;
|
|
84
|
+
subject.copy(buf, off);
|
|
85
|
+
off += subject.length;
|
|
86
|
+
payload.copy(buf, off);
|
|
87
|
+
return buf;
|
|
88
|
+
}
|
|
89
|
+
function packPublishBatch(seq, streamId, entries, flags = 0, entryFlags = 0) {
|
|
90
|
+
let tail = 0;
|
|
91
|
+
for (const e of entries) {
|
|
92
|
+
const midLen = e.msgId ? e.msgId.length : 0;
|
|
93
|
+
tail += 8 + e.subject.length + midLen + e.payload.length;
|
|
94
|
+
}
|
|
95
|
+
const buf = frame(259 /* PublishBatch */, seq, 8 + tail, flags, entryFlags);
|
|
96
|
+
buf.writeUInt32LE(streamId, HEADER_SIZE);
|
|
97
|
+
buf.writeUInt32LE(entries.length, HEADER_SIZE + 4);
|
|
98
|
+
let off = HEADER_SIZE + 8;
|
|
99
|
+
for (const e of entries) {
|
|
100
|
+
const mid = e.msgId ?? EMPTY;
|
|
101
|
+
buf.writeUInt16LE(e.subject.length, off);
|
|
102
|
+
buf.writeUInt16LE(mid.length, off + 2);
|
|
103
|
+
buf.writeUInt32LE(e.payload.length, off + 4);
|
|
104
|
+
off += 8;
|
|
105
|
+
buf.write(e.subject, off);
|
|
106
|
+
off += e.subject.length;
|
|
107
|
+
mid.copy(buf, off);
|
|
108
|
+
off += mid.length;
|
|
109
|
+
e.payload.copy(buf, off);
|
|
110
|
+
off += e.payload.length;
|
|
111
|
+
}
|
|
112
|
+
return buf;
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
export {
|
|
116
|
+
frame,
|
|
117
|
+
packHello,
|
|
118
|
+
packDisconnect,
|
|
119
|
+
packPublish,
|
|
120
|
+
packPublishWithReply,
|
|
121
|
+
packPublishDelayed,
|
|
122
|
+
packPublishBatch
|
|
123
|
+
};
|
|
124
|
+
//# sourceMappingURL=chunk-SKCXQO7R.mjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/proto/frame.ts","../src/proto/publish.ts"],"sourcesContent":["// Core frame allocator — single source for header layout.\n\nimport {\n HEADER_SIZE, HELLO_SIZE, MAGIC_V2, CURRENT_VERSION,\n OFF_ACTION, OFF_FLAGS, OFF_ENTRY_FLAGS, OFF_MSG_LEN, OFF_SEQ,\n Action, Role, Cap,\n} from './constants'\n\n/** Allocate HEADER_SIZE + bodyLen, write the 16-byte header, return buf. */\nexport function frame(\n action: Action, seq: bigint, bodyLen: number,\n flags = 0, entryFlags = 0,\n): Buffer {\n const buf = Buffer.allocUnsafe(HEADER_SIZE + bodyLen)\n buf.writeUInt16LE(action, OFF_ACTION)\n buf[OFF_FLAGS] = flags\n buf[OFF_ENTRY_FLAGS] = entryFlags\n buf.writeUInt32LE(bodyLen, OFF_MSG_LEN)\n buf.writeBigUInt64LE(seq, OFF_SEQ)\n return buf\n}\n\n/** Hello handshake — 8 bytes, no Header prefix. */\nexport function packHello(caps: number = Cap.Reply): Buffer {\n const buf = Buffer.allocUnsafe(HELLO_SIZE)\n buf.writeUInt32LE(MAGIC_V2, 0)\n buf[4] = CURRENT_VERSION\n buf[5] = Role.Client\n buf.writeUInt16LE(caps, 6)\n return buf\n}\n\nexport function packPing(seq: bigint): Buffer {\n return frame(Action.Ping, seq, 0)\n}\n\nexport function packDisconnect(seq: bigint): Buffer {\n return frame(Action.Disconnect, seq, 0)\n}\n","// Publish family — Publish (0x0101), PublishWithReply (0x0104), PublishBatch (0x0103).\n\nimport { HEADER_SIZE, Action } from './constants'\nimport { frame } from './frame'\n\n/**\n * One entry of a `publishBatch` call.\n *\n * `msgId` (optional) is an opaque byte string the broker uses for\n * per-stream deduplication when the target stream was created with\n * `idempotencyWindowMs > 0`. Leaving it empty/undefined disables\n * dedup for this entry (mixing dedup + non-dedup entries in a single\n * batch is allowed).\n */\nexport interface BatchPublishEntry {\n subject: string\n payload: Buffer\n msgId?: Buffer\n}\n\nconst EMPTY = Buffer.alloc(0)\n\n// Body: stream_id(4) + subject_len(2) + msg_id_len(2) = 8B + subject + msg_id + payload\nexport function packPublish(\n seq: bigint, streamId: number,\n subject: Buffer, payload: Buffer,\n flags = 0, entryFlags = 0,\n msgId: Buffer = EMPTY,\n): Buffer {\n const buf = frame(\n Action.Publish, seq,\n 8 + subject.length + msgId.length + payload.length,\n flags, entryFlags,\n )\n buf.writeUInt32LE(streamId, HEADER_SIZE)\n buf.writeUInt16LE(subject.length, HEADER_SIZE + 4)\n buf.writeUInt16LE(msgId.length, HEADER_SIZE + 6)\n let off = HEADER_SIZE + 8\n subject.copy(buf, off); off += subject.length\n msgId.copy(buf, off); off += msgId.length\n payload.copy(buf, off)\n return buf\n}\n\n// Body: stream_id(4) + subject_len(2) + reply_len(2) + _pad(4) = 12B\nexport function packPublishWithReply(\n seq: bigint, streamId: number,\n subject: Buffer, replyTo: Buffer, payload: Buffer,\n flags = 0, entryFlags = 0,\n): Buffer {\n const tail = subject.length + replyTo.length + payload.length\n const buf = frame(Action.PublishWithReply, seq, 12 + tail, flags, entryFlags)\n buf.writeUInt32LE(streamId, HEADER_SIZE)\n buf.writeUInt16LE(subject.length, HEADER_SIZE + 4)\n buf.writeUInt16LE(replyTo.length, HEADER_SIZE + 6)\n buf.writeUInt32LE(0, HEADER_SIZE + 8)\n let off = HEADER_SIZE + 12\n subject.copy(buf, off); off += subject.length\n replyTo.copy(buf, off); off += replyTo.length\n payload.copy(buf, off)\n return buf\n}\n\n// Body: stream_id(4) + subject_len(2) + msg_id_len(2) + delay_ms(8) = 16B + subject + msg_id + payload\nexport function packPublishDelayed(\n seq: bigint, streamId: number,\n subject: Buffer, payload: Buffer,\n delayMs: bigint,\n flags = 0, entryFlags = 0,\n): Buffer {\n const buf = frame(\n Action.PublishDelayed, seq,\n 16 + subject.length + payload.length,\n flags, entryFlags,\n )\n buf.writeUInt32LE(streamId, HEADER_SIZE)\n buf.writeUInt16LE(subject.length, HEADER_SIZE + 4)\n buf.writeUInt16LE(0, HEADER_SIZE + 6) // msg_id_len = 0\n buf.writeBigUInt64LE(delayMs, HEADER_SIZE + 8)\n let off = HEADER_SIZE + 16\n subject.copy(buf, off); off += subject.length\n payload.copy(buf, off)\n return buf\n}\n\n// Body: stream_id(4) + count(4) = 8B\n// + entries[subject_len(2)+msg_id_len(2)+payload_len(4) + subject + msg_id + payload]\nexport function packPublishBatch(\n seq: bigint, streamId: number,\n entries: ReadonlyArray<BatchPublishEntry>,\n flags = 0, entryFlags = 0,\n): Buffer {\n let tail = 0\n for (const e of entries) {\n const midLen = e.msgId ? e.msgId.length : 0\n tail += 8 + e.subject.length + midLen + e.payload.length\n }\n const buf = frame(Action.PublishBatch, seq, 8 + tail, flags, entryFlags)\n buf.writeUInt32LE(streamId, HEADER_SIZE)\n buf.writeUInt32LE(entries.length, HEADER_SIZE + 4)\n let off = HEADER_SIZE + 8\n for (const e of entries) {\n const mid = e.msgId ?? EMPTY\n buf.writeUInt16LE(e.subject.length, off)\n buf.writeUInt16LE(mid.length, off + 2)\n buf.writeUInt32LE(e.payload.length, off + 4)\n off += 8\n buf.write(e.subject, off); off += e.subject.length\n mid.copy(buf, off); off += mid.length\n e.payload.copy(buf, off); off += e.payload.length\n }\n return buf\n}\n"],"mappings":"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","names":[]}
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
import {
|
|
2
|
+
Action,
|
|
3
|
+
CURRENT_VERSION,
|
|
4
|
+
Cap,
|
|
5
|
+
EntryFlag,
|
|
6
|
+
Flag,
|
|
7
|
+
HEADER_SIZE,
|
|
8
|
+
HELLO_SIZE,
|
|
9
|
+
MAGIC_V2,
|
|
10
|
+
OFF_ACTION,
|
|
11
|
+
OFF_ENTRY_FLAGS,
|
|
12
|
+
OFF_FLAGS,
|
|
13
|
+
OFF_MSG_LEN,
|
|
14
|
+
OFF_SEQ,
|
|
15
|
+
Role
|
|
16
|
+
} from "./chunk-6BCX2E2R.mjs";
|
|
17
|
+
export {
|
|
18
|
+
Action,
|
|
19
|
+
CURRENT_VERSION,
|
|
20
|
+
Cap,
|
|
21
|
+
EntryFlag,
|
|
22
|
+
Flag,
|
|
23
|
+
HEADER_SIZE,
|
|
24
|
+
HELLO_SIZE,
|
|
25
|
+
MAGIC_V2,
|
|
26
|
+
OFF_ACTION,
|
|
27
|
+
OFF_ENTRY_FLAGS,
|
|
28
|
+
OFF_FLAGS,
|
|
29
|
+
OFF_MSG_LEN,
|
|
30
|
+
OFF_SEQ,
|
|
31
|
+
Role
|
|
32
|
+
};
|
|
33
|
+
//# sourceMappingURL=constants-KF57DJ2L.mjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":[],"sourcesContent":[],"mappings":"","names":[]}
|
package/dist/index.d.mts
CHANGED
|
@@ -220,6 +220,35 @@ declare class ClientMetrics {
|
|
|
220
220
|
snapshot(): ClientMetricsSnapshot;
|
|
221
221
|
}
|
|
222
222
|
|
|
223
|
+
interface CreateCronBody {
|
|
224
|
+
readonly name: string;
|
|
225
|
+
readonly every: string;
|
|
226
|
+
readonly tz?: string | undefined;
|
|
227
|
+
readonly timeout_ms: number;
|
|
228
|
+
readonly overlap: boolean;
|
|
229
|
+
}
|
|
230
|
+
|
|
231
|
+
type CronHandler = (ctx: CronContext) => Promise<void>;
|
|
232
|
+
/** Context passed to the cron handler on each fire. */
|
|
233
|
+
interface CronContext {
|
|
234
|
+
/** Cron job name. */
|
|
235
|
+
readonly name: string;
|
|
236
|
+
/** UTC timestamp (ms since epoch) when the broker intended this fire. */
|
|
237
|
+
readonly fireTime: bigint;
|
|
238
|
+
/** Monotonic fire counter (1-based). */
|
|
239
|
+
readonly fireCount: bigint;
|
|
240
|
+
}
|
|
241
|
+
declare class CronState {
|
|
242
|
+
private readonly handlers;
|
|
243
|
+
register(name: string, config: CreateCronBody, handler: CronHandler): void;
|
|
244
|
+
remove(name: string): void;
|
|
245
|
+
getHandler(name: string): CronHandler | undefined;
|
|
246
|
+
allConfigs(): ReadonlyArray<{
|
|
247
|
+
name: string;
|
|
248
|
+
config: CreateCronBody;
|
|
249
|
+
}>;
|
|
250
|
+
}
|
|
251
|
+
|
|
223
252
|
type DeliveryHandler = (frame: Buffer) => void;
|
|
224
253
|
declare class Connection {
|
|
225
254
|
private readonly reconnectAddr?;
|
|
@@ -234,6 +263,7 @@ declare class Connection {
|
|
|
234
263
|
private readonly log;
|
|
235
264
|
private activeSubs;
|
|
236
265
|
private metrics?;
|
|
266
|
+
private cronState?;
|
|
237
267
|
private constructor();
|
|
238
268
|
private attachSocket;
|
|
239
269
|
static connect(addr: string, timeoutMs?: number, tlsCfg?: TlsConfig, reconnectCfg?: ReconnectConfig, logger?: Logger): Promise<Connection>;
|
|
@@ -244,13 +274,17 @@ declare class Connection {
|
|
|
244
274
|
* entry and `reconnects` on successful reconnections. Unset = no-op.
|
|
245
275
|
*/
|
|
246
276
|
setMetrics(m: ClientMetrics): void;
|
|
277
|
+
/** Attach cron state so the connection can dispatch CronFire frames. */
|
|
278
|
+
setCronState(s: CronState): void;
|
|
247
279
|
private resolvePending;
|
|
248
280
|
private rejectPending;
|
|
249
281
|
private onFrame;
|
|
250
282
|
private handleBatchDeliver;
|
|
283
|
+
private dispatchCronFire;
|
|
251
284
|
sendSubscribeV2(consumerId: number, filter: Buffer, handler: DeliveryHandler, onRenew?: (newConsumerId: number) => void): Promise<number>;
|
|
252
285
|
cancelSubscription(consumerId: number): void;
|
|
253
286
|
private resubscribeAll;
|
|
287
|
+
private replayCrons;
|
|
254
288
|
registerRoute(consumerId: number, handler: DeliveryHandler): void;
|
|
255
289
|
unregisterRoute(consumerId: number): void;
|
|
256
290
|
send(frame: Buffer): void;
|
|
@@ -431,6 +465,30 @@ declare class Consumer {
|
|
|
431
465
|
subscribe<T extends Record<string, unknown>>(codec: Encoding<T>, cb: (msg: LazyMessage<T>) => void, opts?: SubscribeOptions): Promise<Subscription>;
|
|
432
466
|
}
|
|
433
467
|
|
|
468
|
+
declare class CronBuilder {
|
|
469
|
+
private readonly conn;
|
|
470
|
+
private readonly cronState;
|
|
471
|
+
private readonly cronName;
|
|
472
|
+
private expr;
|
|
473
|
+
private timezone;
|
|
474
|
+
private timeoutMs;
|
|
475
|
+
private allowOverlap;
|
|
476
|
+
constructor(conn: Connection, cronState: CronState, cronName: string);
|
|
477
|
+
every(expression: string): this;
|
|
478
|
+
tz(timezone: string): this;
|
|
479
|
+
timeout(ms: number): this;
|
|
480
|
+
overlap(allow: boolean): this;
|
|
481
|
+
run(handler: CronHandler): Promise<CronHandle>;
|
|
482
|
+
}
|
|
483
|
+
declare class CronHandle {
|
|
484
|
+
private readonly conn;
|
|
485
|
+
private readonly cronState;
|
|
486
|
+
private readonly cronName;
|
|
487
|
+
constructor(conn: Connection, cronState: CronState, cronName: string);
|
|
488
|
+
get name(): string;
|
|
489
|
+
stop(): Promise<void>;
|
|
490
|
+
}
|
|
491
|
+
|
|
434
492
|
type MsgCallback = (msg: Message) => void;
|
|
435
493
|
declare class ArbitroClient {
|
|
436
494
|
private conn;
|
|
@@ -439,6 +497,7 @@ declare class ArbitroClient {
|
|
|
439
497
|
private readonly logger;
|
|
440
498
|
private readonly sidCache;
|
|
441
499
|
private readonly _metrics;
|
|
500
|
+
private readonly _cronState;
|
|
442
501
|
constructor(config: ClientConfig);
|
|
443
502
|
connect(): Promise<this>;
|
|
444
503
|
/**
|
|
@@ -494,6 +553,13 @@ declare class ArbitroClient {
|
|
|
494
553
|
publishBatch(streamName: string, messages: BatchPublishEntry[]): Promise<bigint>;
|
|
495
554
|
/** Request-reply. */
|
|
496
555
|
request(streamName: string, subject: string, data: Buffer, timeoutMs?: number): Promise<Buffer>;
|
|
556
|
+
/**
|
|
557
|
+
* Publish a message with a delivery delay. The broker parks the message
|
|
558
|
+
* in its delayed journal and delivers it to consumers after `delayMs`
|
|
559
|
+
* milliseconds. Returns a Promise that resolves once the broker confirms
|
|
560
|
+
* receipt.
|
|
561
|
+
*/
|
|
562
|
+
publishDelayed(streamName: string, subject: string, data: Buffer, delayMs: number): Promise<void>;
|
|
497
563
|
/** Subscribe with explicit consumer config. */
|
|
498
564
|
subscribe(streamName: string, config: ConsumerConfig, callback?: MsgCallback, opts?: SubscribeOptions): Promise<Subscription>;
|
|
499
565
|
/** Subscribe by consumer name only (consumer must already exist). */
|
|
@@ -533,6 +599,8 @@ declare class ArbitroClient {
|
|
|
533
599
|
/** Pre-resolve stream_id from server (GetStream). Required before sync publish(). */
|
|
534
600
|
resolveStream(name: string): Promise<void>;
|
|
535
601
|
stream(name: string, config?: StreamConfig): Stream;
|
|
602
|
+
/** Start building a cron job. Call `.every()` then `.run()` to register. */
|
|
603
|
+
cron(name: string): CronBuilder;
|
|
536
604
|
close(): Promise<void>;
|
|
537
605
|
}
|
|
538
606
|
|
|
@@ -599,4 +667,4 @@ declare function zodCodec<S extends ZodRawShape>(zodSchema: ZodObject<S>): Encod
|
|
|
599
667
|
readonly fields: string[];
|
|
600
668
|
};
|
|
601
669
|
|
|
602
|
-
export { AckPolicy, ArbitroClient, ArbitroError, type BatchPublishEntry, type ClientConfig, type ClientMetricsSnapshot, Codec, Consumer, type ConsumerConfig, type ConsumerInfo, type DeleteStreamOpts, DeliverPolicy, type Encoding, ErrorCode, type ErrorCodeValue, type FieldType, type FieldTypeMap, type FlushConfig, type InferSchema, type JournalConfig, JournalType, JsonCodec, type LazyMessage, type LogFn, type Logger, Message, type PublishOpts, type ReconnectConfig, type Schema, Stream, type StreamConfig, type StreamInfo, StringCodec, type SubjectInflightLimit, type SubscribeOptions, Subscription, type TlsConfig, Topic, decodeJson, decodeString, encodeJson, encodeString, makeLazyMessage, schema, streamId, zodCodec };
|
|
670
|
+
export { AckPolicy, ArbitroClient, ArbitroError, type BatchPublishEntry, type ClientConfig, type ClientMetricsSnapshot, Codec, Consumer, type ConsumerConfig, type ConsumerInfo, CronBuilder, type CronContext, CronHandle, type CronHandler, type DeleteStreamOpts, DeliverPolicy, type Encoding, ErrorCode, type ErrorCodeValue, type FieldType, type FieldTypeMap, type FlushConfig, type InferSchema, type JournalConfig, JournalType, JsonCodec, type LazyMessage, type LogFn, type Logger, Message, type PublishOpts, type ReconnectConfig, type Schema, Stream, type StreamConfig, type StreamInfo, StringCodec, type SubjectInflightLimit, type SubscribeOptions, Subscription, type TlsConfig, Topic, decodeJson, decodeString, encodeJson, encodeString, makeLazyMessage, schema, streamId, zodCodec };
|
package/dist/index.d.ts
CHANGED
|
@@ -220,6 +220,35 @@ declare class ClientMetrics {
|
|
|
220
220
|
snapshot(): ClientMetricsSnapshot;
|
|
221
221
|
}
|
|
222
222
|
|
|
223
|
+
interface CreateCronBody {
|
|
224
|
+
readonly name: string;
|
|
225
|
+
readonly every: string;
|
|
226
|
+
readonly tz?: string | undefined;
|
|
227
|
+
readonly timeout_ms: number;
|
|
228
|
+
readonly overlap: boolean;
|
|
229
|
+
}
|
|
230
|
+
|
|
231
|
+
type CronHandler = (ctx: CronContext) => Promise<void>;
|
|
232
|
+
/** Context passed to the cron handler on each fire. */
|
|
233
|
+
interface CronContext {
|
|
234
|
+
/** Cron job name. */
|
|
235
|
+
readonly name: string;
|
|
236
|
+
/** UTC timestamp (ms since epoch) when the broker intended this fire. */
|
|
237
|
+
readonly fireTime: bigint;
|
|
238
|
+
/** Monotonic fire counter (1-based). */
|
|
239
|
+
readonly fireCount: bigint;
|
|
240
|
+
}
|
|
241
|
+
declare class CronState {
|
|
242
|
+
private readonly handlers;
|
|
243
|
+
register(name: string, config: CreateCronBody, handler: CronHandler): void;
|
|
244
|
+
remove(name: string): void;
|
|
245
|
+
getHandler(name: string): CronHandler | undefined;
|
|
246
|
+
allConfigs(): ReadonlyArray<{
|
|
247
|
+
name: string;
|
|
248
|
+
config: CreateCronBody;
|
|
249
|
+
}>;
|
|
250
|
+
}
|
|
251
|
+
|
|
223
252
|
type DeliveryHandler = (frame: Buffer) => void;
|
|
224
253
|
declare class Connection {
|
|
225
254
|
private readonly reconnectAddr?;
|
|
@@ -234,6 +263,7 @@ declare class Connection {
|
|
|
234
263
|
private readonly log;
|
|
235
264
|
private activeSubs;
|
|
236
265
|
private metrics?;
|
|
266
|
+
private cronState?;
|
|
237
267
|
private constructor();
|
|
238
268
|
private attachSocket;
|
|
239
269
|
static connect(addr: string, timeoutMs?: number, tlsCfg?: TlsConfig, reconnectCfg?: ReconnectConfig, logger?: Logger): Promise<Connection>;
|
|
@@ -244,13 +274,17 @@ declare class Connection {
|
|
|
244
274
|
* entry and `reconnects` on successful reconnections. Unset = no-op.
|
|
245
275
|
*/
|
|
246
276
|
setMetrics(m: ClientMetrics): void;
|
|
277
|
+
/** Attach cron state so the connection can dispatch CronFire frames. */
|
|
278
|
+
setCronState(s: CronState): void;
|
|
247
279
|
private resolvePending;
|
|
248
280
|
private rejectPending;
|
|
249
281
|
private onFrame;
|
|
250
282
|
private handleBatchDeliver;
|
|
283
|
+
private dispatchCronFire;
|
|
251
284
|
sendSubscribeV2(consumerId: number, filter: Buffer, handler: DeliveryHandler, onRenew?: (newConsumerId: number) => void): Promise<number>;
|
|
252
285
|
cancelSubscription(consumerId: number): void;
|
|
253
286
|
private resubscribeAll;
|
|
287
|
+
private replayCrons;
|
|
254
288
|
registerRoute(consumerId: number, handler: DeliveryHandler): void;
|
|
255
289
|
unregisterRoute(consumerId: number): void;
|
|
256
290
|
send(frame: Buffer): void;
|
|
@@ -431,6 +465,30 @@ declare class Consumer {
|
|
|
431
465
|
subscribe<T extends Record<string, unknown>>(codec: Encoding<T>, cb: (msg: LazyMessage<T>) => void, opts?: SubscribeOptions): Promise<Subscription>;
|
|
432
466
|
}
|
|
433
467
|
|
|
468
|
+
declare class CronBuilder {
|
|
469
|
+
private readonly conn;
|
|
470
|
+
private readonly cronState;
|
|
471
|
+
private readonly cronName;
|
|
472
|
+
private expr;
|
|
473
|
+
private timezone;
|
|
474
|
+
private timeoutMs;
|
|
475
|
+
private allowOverlap;
|
|
476
|
+
constructor(conn: Connection, cronState: CronState, cronName: string);
|
|
477
|
+
every(expression: string): this;
|
|
478
|
+
tz(timezone: string): this;
|
|
479
|
+
timeout(ms: number): this;
|
|
480
|
+
overlap(allow: boolean): this;
|
|
481
|
+
run(handler: CronHandler): Promise<CronHandle>;
|
|
482
|
+
}
|
|
483
|
+
declare class CronHandle {
|
|
484
|
+
private readonly conn;
|
|
485
|
+
private readonly cronState;
|
|
486
|
+
private readonly cronName;
|
|
487
|
+
constructor(conn: Connection, cronState: CronState, cronName: string);
|
|
488
|
+
get name(): string;
|
|
489
|
+
stop(): Promise<void>;
|
|
490
|
+
}
|
|
491
|
+
|
|
434
492
|
type MsgCallback = (msg: Message) => void;
|
|
435
493
|
declare class ArbitroClient {
|
|
436
494
|
private conn;
|
|
@@ -439,6 +497,7 @@ declare class ArbitroClient {
|
|
|
439
497
|
private readonly logger;
|
|
440
498
|
private readonly sidCache;
|
|
441
499
|
private readonly _metrics;
|
|
500
|
+
private readonly _cronState;
|
|
442
501
|
constructor(config: ClientConfig);
|
|
443
502
|
connect(): Promise<this>;
|
|
444
503
|
/**
|
|
@@ -494,6 +553,13 @@ declare class ArbitroClient {
|
|
|
494
553
|
publishBatch(streamName: string, messages: BatchPublishEntry[]): Promise<bigint>;
|
|
495
554
|
/** Request-reply. */
|
|
496
555
|
request(streamName: string, subject: string, data: Buffer, timeoutMs?: number): Promise<Buffer>;
|
|
556
|
+
/**
|
|
557
|
+
* Publish a message with a delivery delay. The broker parks the message
|
|
558
|
+
* in its delayed journal and delivers it to consumers after `delayMs`
|
|
559
|
+
* milliseconds. Returns a Promise that resolves once the broker confirms
|
|
560
|
+
* receipt.
|
|
561
|
+
*/
|
|
562
|
+
publishDelayed(streamName: string, subject: string, data: Buffer, delayMs: number): Promise<void>;
|
|
497
563
|
/** Subscribe with explicit consumer config. */
|
|
498
564
|
subscribe(streamName: string, config: ConsumerConfig, callback?: MsgCallback, opts?: SubscribeOptions): Promise<Subscription>;
|
|
499
565
|
/** Subscribe by consumer name only (consumer must already exist). */
|
|
@@ -533,6 +599,8 @@ declare class ArbitroClient {
|
|
|
533
599
|
/** Pre-resolve stream_id from server (GetStream). Required before sync publish(). */
|
|
534
600
|
resolveStream(name: string): Promise<void>;
|
|
535
601
|
stream(name: string, config?: StreamConfig): Stream;
|
|
602
|
+
/** Start building a cron job. Call `.every()` then `.run()` to register. */
|
|
603
|
+
cron(name: string): CronBuilder;
|
|
536
604
|
close(): Promise<void>;
|
|
537
605
|
}
|
|
538
606
|
|
|
@@ -599,4 +667,4 @@ declare function zodCodec<S extends ZodRawShape>(zodSchema: ZodObject<S>): Encod
|
|
|
599
667
|
readonly fields: string[];
|
|
600
668
|
};
|
|
601
669
|
|
|
602
|
-
export { AckPolicy, ArbitroClient, ArbitroError, type BatchPublishEntry, type ClientConfig, type ClientMetricsSnapshot, Codec, Consumer, type ConsumerConfig, type ConsumerInfo, type DeleteStreamOpts, DeliverPolicy, type Encoding, ErrorCode, type ErrorCodeValue, type FieldType, type FieldTypeMap, type FlushConfig, type InferSchema, type JournalConfig, JournalType, JsonCodec, type LazyMessage, type LogFn, type Logger, Message, type PublishOpts, type ReconnectConfig, type Schema, Stream, type StreamConfig, type StreamInfo, StringCodec, type SubjectInflightLimit, type SubscribeOptions, Subscription, type TlsConfig, Topic, decodeJson, decodeString, encodeJson, encodeString, makeLazyMessage, schema, streamId, zodCodec };
|
|
670
|
+
export { AckPolicy, ArbitroClient, ArbitroError, type BatchPublishEntry, type ClientConfig, type ClientMetricsSnapshot, Codec, Consumer, type ConsumerConfig, type ConsumerInfo, CronBuilder, type CronContext, CronHandle, type CronHandler, type DeleteStreamOpts, DeliverPolicy, type Encoding, ErrorCode, type ErrorCodeValue, type FieldType, type FieldTypeMap, type FlushConfig, type InferSchema, type JournalConfig, JournalType, JsonCodec, type LazyMessage, type LogFn, type Logger, Message, type PublishOpts, type ReconnectConfig, type Schema, Stream, type StreamConfig, type StreamInfo, StringCodec, type SubjectInflightLimit, type SubscribeOptions, Subscription, type TlsConfig, Topic, decodeJson, decodeString, encodeJson, encodeString, makeLazyMessage, schema, streamId, zodCodec };
|