@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/dist/index.js CHANGED
@@ -5,6 +5,9 @@ var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
5
5
  var __getOwnPropNames = Object.getOwnPropertyNames;
6
6
  var __getProtoOf = Object.getPrototypeOf;
7
7
  var __hasOwnProp = Object.prototype.hasOwnProperty;
8
+ var __esm = (fn, res) => function __init() {
9
+ return fn && (res = (0, fn[__getOwnPropNames(fn)[0]])(fn = 0)), res;
10
+ };
8
11
  var __export = (target, all) => {
9
12
  for (var name in all)
10
13
  __defProp(target, name, { get: all[name], enumerable: true });
@@ -27,6 +30,237 @@ var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__ge
27
30
  ));
28
31
  var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
29
32
 
33
+ // src/proto/constants.ts
34
+ var constants_exports = {};
35
+ __export(constants_exports, {
36
+ Action: () => Action,
37
+ CURRENT_VERSION: () => CURRENT_VERSION,
38
+ Cap: () => Cap,
39
+ EntryFlag: () => EntryFlag,
40
+ Flag: () => Flag,
41
+ HEADER_SIZE: () => HEADER_SIZE,
42
+ HELLO_SIZE: () => HELLO_SIZE,
43
+ MAGIC_V2: () => MAGIC_V2,
44
+ OFF_ACTION: () => OFF_ACTION,
45
+ OFF_ENTRY_FLAGS: () => OFF_ENTRY_FLAGS,
46
+ OFF_FLAGS: () => OFF_FLAGS,
47
+ OFF_MSG_LEN: () => OFF_MSG_LEN,
48
+ OFF_SEQ: () => OFF_SEQ,
49
+ Role: () => Role
50
+ });
51
+ var MAGIC_V2, HELLO_SIZE, CURRENT_VERSION, Role, Cap, HEADER_SIZE, OFF_ACTION, OFF_FLAGS, OFF_ENTRY_FLAGS, OFF_MSG_LEN, OFF_SEQ, Flag, EntryFlag, Action;
52
+ var init_constants = __esm({
53
+ "src/proto/constants.ts"() {
54
+ "use strict";
55
+ MAGIC_V2 = 843207233;
56
+ HELLO_SIZE = 8;
57
+ CURRENT_VERSION = 2;
58
+ Role = /* @__PURE__ */ ((Role2) => {
59
+ Role2[Role2["Client"] = 0] = "Client";
60
+ Role2[Role2["Server"] = 1] = "Server";
61
+ return Role2;
62
+ })(Role || {});
63
+ Cap = /* @__PURE__ */ ((Cap2) => {
64
+ Cap2[Cap2["Headers"] = 1] = "Headers";
65
+ Cap2[Cap2["Reply"] = 2] = "Reply";
66
+ Cap2[Cap2["BatchHeaders"] = 4] = "BatchHeaders";
67
+ Cap2[Cap2["CompressedPayload"] = 8] = "CompressedPayload";
68
+ return Cap2;
69
+ })(Cap || {});
70
+ HEADER_SIZE = 16;
71
+ OFF_ACTION = 0;
72
+ OFF_FLAGS = 2;
73
+ OFF_ENTRY_FLAGS = 3;
74
+ OFF_MSG_LEN = 4;
75
+ OFF_SEQ = 8;
76
+ Flag = /* @__PURE__ */ ((Flag3) => {
77
+ Flag3[Flag3["None"] = 0] = "None";
78
+ Flag3[Flag3["AckReq"] = 1] = "AckReq";
79
+ Flag3[Flag3["Dup"] = 2] = "Dup";
80
+ Flag3[Flag3["PriorityHigh"] = 4] = "PriorityHigh";
81
+ return Flag3;
82
+ })(Flag || {});
83
+ EntryFlag = /* @__PURE__ */ ((EntryFlag2) => {
84
+ EntryFlag2[EntryFlag2["None"] = 0] = "None";
85
+ EntryFlag2[EntryFlag2["Retain"] = 1] = "Retain";
86
+ EntryFlag2[EntryFlag2["Compressed"] = 2] = "Compressed";
87
+ EntryFlag2[EntryFlag2["NoBackpressure"] = 4] = "NoBackpressure";
88
+ return EntryFlag2;
89
+ })(EntryFlag || {});
90
+ Action = /* @__PURE__ */ ((Action2) => {
91
+ Action2[Action2["Hello"] = 1] = "Hello";
92
+ Action2[Action2["Auth"] = 2] = "Auth";
93
+ Action2[Action2["Publish"] = 257] = "Publish";
94
+ Action2[Action2["PublishAccumulate"] = 258] = "PublishAccumulate";
95
+ Action2[Action2["PublishBatch"] = 259] = "PublishBatch";
96
+ Action2[Action2["PublishWithReply"] = 260] = "PublishWithReply";
97
+ Action2[Action2["PublishWithHeaders"] = 261] = "PublishWithHeaders";
98
+ Action2[Action2["PublishBatchWithHeaders"] = 262] = "PublishBatchWithHeaders";
99
+ Action2[Action2["Deliver"] = 512] = "Deliver";
100
+ Action2[Action2["Ack"] = 513] = "Ack";
101
+ Action2[Action2["Nack"] = 514] = "Nack";
102
+ Action2[Action2["RepOk"] = 515] = "RepOk";
103
+ Action2[Action2["RepError"] = 516] = "RepError";
104
+ Action2[Action2["RepBatch"] = 517] = "RepBatch";
105
+ Action2[Action2["BatchAck"] = 518] = "BatchAck";
106
+ Action2[Action2["FanoutBatch"] = 519] = "FanoutBatch";
107
+ Action2[Action2["AckSync"] = 520] = "AckSync";
108
+ Action2[Action2["BatchAckSync"] = 521] = "BatchAckSync";
109
+ Action2[Action2["BatchNack"] = 522] = "BatchNack";
110
+ Action2[Action2["Subscribe"] = 769] = "Subscribe";
111
+ Action2[Action2["Unsubscribe"] = 770] = "Unsubscribe";
112
+ Action2[Action2["CreateStream"] = 1025] = "CreateStream";
113
+ Action2[Action2["DeleteStream"] = 1026] = "DeleteStream";
114
+ Action2[Action2["GetStream"] = 1027] = "GetStream";
115
+ Action2[Action2["ListStreams"] = 1028] = "ListStreams";
116
+ Action2[Action2["PurgeStream"] = 1029] = "PurgeStream";
117
+ Action2[Action2["DrainSubject"] = 1030] = "DrainSubject";
118
+ Action2[Action2["CreateConsumer"] = 1281] = "CreateConsumer";
119
+ Action2[Action2["DeleteConsumer"] = 1282] = "DeleteConsumer";
120
+ Action2[Action2["GetConsumer"] = 1283] = "GetConsumer";
121
+ Action2[Action2["ListConsumers"] = 1284] = "ListConsumers";
122
+ Action2[Action2["ConsumerStats"] = 1285] = "ConsumerStats";
123
+ Action2[Action2["PauseConsumer"] = 1286] = "PauseConsumer";
124
+ Action2[Action2["ResumeConsumer"] = 1287] = "ResumeConsumer";
125
+ Action2[Action2["PublishDelayed"] = 2049] = "PublishDelayed";
126
+ Action2[Action2["Ping"] = 1537] = "Ping";
127
+ Action2[Action2["Pong"] = 1538] = "Pong";
128
+ Action2[Action2["Connect"] = 1539] = "Connect";
129
+ Action2[Action2["Connected"] = 1540] = "Connected";
130
+ Action2[Action2["Disconnect"] = 1541] = "Disconnect";
131
+ Action2[Action2["CreateCron"] = 1793] = "CreateCron";
132
+ Action2[Action2["DeleteCron"] = 1794] = "DeleteCron";
133
+ Action2[Action2["ListCrons"] = 1795] = "ListCrons";
134
+ Action2[Action2["CronFire"] = 1796] = "CronFire";
135
+ Action2[Action2["CronAck"] = 1797] = "CronAck";
136
+ return Action2;
137
+ })(Action || {});
138
+ }
139
+ });
140
+
141
+ // src/proto/frame.ts
142
+ function frame(action, seq, bodyLen, flags = 0, entryFlags = 0) {
143
+ const buf = Buffer.allocUnsafe(HEADER_SIZE + bodyLen);
144
+ buf.writeUInt16LE(action, OFF_ACTION);
145
+ buf[OFF_FLAGS] = flags;
146
+ buf[OFF_ENTRY_FLAGS] = entryFlags;
147
+ buf.writeUInt32LE(bodyLen, OFF_MSG_LEN);
148
+ buf.writeBigUInt64LE(seq, OFF_SEQ);
149
+ return buf;
150
+ }
151
+ function packHello(caps = 2 /* Reply */) {
152
+ const buf = Buffer.allocUnsafe(HELLO_SIZE);
153
+ buf.writeUInt32LE(MAGIC_V2, 0);
154
+ buf[4] = CURRENT_VERSION;
155
+ buf[5] = 0 /* Client */;
156
+ buf.writeUInt16LE(caps, 6);
157
+ return buf;
158
+ }
159
+ function packDisconnect(seq) {
160
+ return frame(1541 /* Disconnect */, seq, 0);
161
+ }
162
+ var init_frame = __esm({
163
+ "src/proto/frame.ts"() {
164
+ "use strict";
165
+ init_constants();
166
+ }
167
+ });
168
+
169
+ // src/proto/publish.ts
170
+ var publish_exports = {};
171
+ __export(publish_exports, {
172
+ packPublish: () => packPublish,
173
+ packPublishBatch: () => packPublishBatch,
174
+ packPublishDelayed: () => packPublishDelayed,
175
+ packPublishWithReply: () => packPublishWithReply
176
+ });
177
+ function packPublish(seq, streamId2, subject, payload, flags = 0, entryFlags = 0, msgId = EMPTY) {
178
+ const buf = frame(
179
+ 257 /* Publish */,
180
+ seq,
181
+ 8 + subject.length + msgId.length + payload.length,
182
+ flags,
183
+ entryFlags
184
+ );
185
+ buf.writeUInt32LE(streamId2, HEADER_SIZE);
186
+ buf.writeUInt16LE(subject.length, HEADER_SIZE + 4);
187
+ buf.writeUInt16LE(msgId.length, HEADER_SIZE + 6);
188
+ let off = HEADER_SIZE + 8;
189
+ subject.copy(buf, off);
190
+ off += subject.length;
191
+ msgId.copy(buf, off);
192
+ off += msgId.length;
193
+ payload.copy(buf, off);
194
+ return buf;
195
+ }
196
+ function packPublishWithReply(seq, streamId2, subject, replyTo, payload, flags = 0, entryFlags = 0) {
197
+ const tail = subject.length + replyTo.length + payload.length;
198
+ const buf = frame(260 /* PublishWithReply */, seq, 12 + tail, flags, entryFlags);
199
+ buf.writeUInt32LE(streamId2, HEADER_SIZE);
200
+ buf.writeUInt16LE(subject.length, HEADER_SIZE + 4);
201
+ buf.writeUInt16LE(replyTo.length, HEADER_SIZE + 6);
202
+ buf.writeUInt32LE(0, HEADER_SIZE + 8);
203
+ let off = HEADER_SIZE + 12;
204
+ subject.copy(buf, off);
205
+ off += subject.length;
206
+ replyTo.copy(buf, off);
207
+ off += replyTo.length;
208
+ payload.copy(buf, off);
209
+ return buf;
210
+ }
211
+ function packPublishDelayed(seq, streamId2, subject, payload, delayMs, flags = 0, entryFlags = 0) {
212
+ const buf = frame(
213
+ 2049 /* PublishDelayed */,
214
+ seq,
215
+ 16 + subject.length + payload.length,
216
+ flags,
217
+ entryFlags
218
+ );
219
+ buf.writeUInt32LE(streamId2, HEADER_SIZE);
220
+ buf.writeUInt16LE(subject.length, HEADER_SIZE + 4);
221
+ buf.writeUInt16LE(0, HEADER_SIZE + 6);
222
+ buf.writeBigUInt64LE(delayMs, HEADER_SIZE + 8);
223
+ let off = HEADER_SIZE + 16;
224
+ subject.copy(buf, off);
225
+ off += subject.length;
226
+ payload.copy(buf, off);
227
+ return buf;
228
+ }
229
+ function packPublishBatch(seq, streamId2, entries, flags = 0, entryFlags = 0) {
230
+ let tail = 0;
231
+ for (const e of entries) {
232
+ const midLen = e.msgId ? e.msgId.length : 0;
233
+ tail += 8 + e.subject.length + midLen + e.payload.length;
234
+ }
235
+ const buf = frame(259 /* PublishBatch */, seq, 8 + tail, flags, entryFlags);
236
+ buf.writeUInt32LE(streamId2, HEADER_SIZE);
237
+ buf.writeUInt32LE(entries.length, HEADER_SIZE + 4);
238
+ let off = HEADER_SIZE + 8;
239
+ for (const e of entries) {
240
+ const mid = e.msgId ?? EMPTY;
241
+ buf.writeUInt16LE(e.subject.length, off);
242
+ buf.writeUInt16LE(mid.length, off + 2);
243
+ buf.writeUInt32LE(e.payload.length, off + 4);
244
+ off += 8;
245
+ buf.write(e.subject, off);
246
+ off += e.subject.length;
247
+ mid.copy(buf, off);
248
+ off += mid.length;
249
+ e.payload.copy(buf, off);
250
+ off += e.payload.length;
251
+ }
252
+ return buf;
253
+ }
254
+ var EMPTY;
255
+ var init_publish = __esm({
256
+ "src/proto/publish.ts"() {
257
+ "use strict";
258
+ init_constants();
259
+ init_frame();
260
+ EMPTY = Buffer.alloc(0);
261
+ }
262
+ });
263
+
30
264
  // src/index.ts
31
265
  var index_exports = {};
32
266
  __export(index_exports, {
@@ -35,6 +269,8 @@ __export(index_exports, {
35
269
  ArbitroError: () => ArbitroError,
36
270
  Codec: () => Codec,
37
271
  Consumer: () => Consumer,
272
+ CronBuilder: () => CronBuilder,
273
+ CronHandle: () => CronHandle,
38
274
  DeliverPolicy: () => DeliverPolicy,
39
275
  ErrorCode: () => ErrorCode,
40
276
  JournalType: () => JournalType,
@@ -234,102 +470,16 @@ function makeLazyMessage(raw, codec, fields, onAck, onNack, onNackDelay) {
234
470
  return msg;
235
471
  }
236
472
 
237
- // src/proto/constants.ts
238
- var MAGIC_V2 = 843207233;
239
- var HELLO_SIZE = 8;
240
- var CURRENT_VERSION = 2;
241
- var HEADER_SIZE = 16;
242
- var OFF_ACTION = 0;
243
- var OFF_FLAGS = 2;
244
- var OFF_ENTRY_FLAGS = 3;
245
- var OFF_MSG_LEN = 4;
246
- var OFF_SEQ = 8;
247
-
248
- // src/proto/frame.ts
249
- function frame(action, seq, bodyLen, flags = 0, entryFlags = 0) {
250
- const buf = Buffer.allocUnsafe(HEADER_SIZE + bodyLen);
251
- buf.writeUInt16LE(action, OFF_ACTION);
252
- buf[OFF_FLAGS] = flags;
253
- buf[OFF_ENTRY_FLAGS] = entryFlags;
254
- buf.writeUInt32LE(bodyLen, OFF_MSG_LEN);
255
- buf.writeBigUInt64LE(seq, OFF_SEQ);
256
- return buf;
257
- }
258
- function packHello(caps = 2 /* Reply */) {
259
- const buf = Buffer.allocUnsafe(HELLO_SIZE);
260
- buf.writeUInt32LE(MAGIC_V2, 0);
261
- buf[4] = CURRENT_VERSION;
262
- buf[5] = 0 /* Client */;
263
- buf.writeUInt16LE(caps, 6);
264
- return buf;
265
- }
266
- function packDisconnect(seq) {
267
- return frame(1541 /* Disconnect */, seq, 0);
268
- }
473
+ // src/message/message.ts
474
+ init_constants();
269
475
 
270
- // src/proto/publish.ts
271
- var EMPTY = Buffer.alloc(0);
272
- function packPublish(seq, streamId2, subject, payload, flags = 0, entryFlags = 0, msgId = EMPTY) {
273
- const buf = frame(
274
- 257 /* Publish */,
275
- seq,
276
- 8 + subject.length + msgId.length + payload.length,
277
- flags,
278
- entryFlags
279
- );
280
- buf.writeUInt32LE(streamId2, HEADER_SIZE);
281
- buf.writeUInt16LE(subject.length, HEADER_SIZE + 4);
282
- buf.writeUInt16LE(msgId.length, HEADER_SIZE + 6);
283
- let off = HEADER_SIZE + 8;
284
- subject.copy(buf, off);
285
- off += subject.length;
286
- msgId.copy(buf, off);
287
- off += msgId.length;
288
- payload.copy(buf, off);
289
- return buf;
290
- }
291
- function packPublishWithReply(seq, streamId2, subject, replyTo, payload, flags = 0, entryFlags = 0) {
292
- const tail = subject.length + replyTo.length + payload.length;
293
- const buf = frame(260 /* PublishWithReply */, seq, 12 + tail, flags, entryFlags);
294
- buf.writeUInt32LE(streamId2, HEADER_SIZE);
295
- buf.writeUInt16LE(subject.length, HEADER_SIZE + 4);
296
- buf.writeUInt16LE(replyTo.length, HEADER_SIZE + 6);
297
- buf.writeUInt32LE(0, HEADER_SIZE + 8);
298
- let off = HEADER_SIZE + 12;
299
- subject.copy(buf, off);
300
- off += subject.length;
301
- replyTo.copy(buf, off);
302
- off += replyTo.length;
303
- payload.copy(buf, off);
304
- return buf;
305
- }
306
- function packPublishBatch(seq, streamId2, entries, flags = 0, entryFlags = 0) {
307
- let tail = 0;
308
- for (const e of entries) {
309
- const midLen = e.msgId ? e.msgId.length : 0;
310
- tail += 8 + e.subject.length + midLen + e.payload.length;
311
- }
312
- const buf = frame(259 /* PublishBatch */, seq, 8 + tail, flags, entryFlags);
313
- buf.writeUInt32LE(streamId2, HEADER_SIZE);
314
- buf.writeUInt32LE(entries.length, HEADER_SIZE + 4);
315
- let off = HEADER_SIZE + 8;
316
- for (const e of entries) {
317
- const mid = e.msgId ?? EMPTY;
318
- buf.writeUInt16LE(e.subject.length, off);
319
- buf.writeUInt16LE(mid.length, off + 2);
320
- buf.writeUInt32LE(e.payload.length, off + 4);
321
- off += 8;
322
- buf.write(e.subject, off);
323
- off += e.subject.length;
324
- mid.copy(buf, off);
325
- off += mid.length;
326
- e.payload.copy(buf, off);
327
- off += e.payload.length;
328
- }
329
- return buf;
330
- }
476
+ // src/proto/v2.ts
477
+ init_frame();
478
+ init_publish();
331
479
 
332
480
  // src/proto/delivery.ts
481
+ init_constants();
482
+ init_frame();
333
483
  function packCold(action, seq, body) {
334
484
  const utf8 = Buffer.from(JSON.stringify(body), "utf8");
335
485
  const buf = frame(action, seq, utf8.length);
@@ -376,6 +526,8 @@ function packBatchNack(seq, consumerId, entries) {
376
526
  }
377
527
 
378
528
  // src/proto/manage.ts
529
+ init_constants();
530
+ init_frame();
379
531
  function packCold2(action, seq, body) {
380
532
  const utf8 = Buffer.from(JSON.stringify(body), "utf8");
381
533
  const buf = frame(action, seq, utf8.length);
@@ -583,6 +735,7 @@ var net = __toESM(require("net"));
583
735
  var tls = __toESM(require("tls"));
584
736
 
585
737
  // src/proto/framer.ts
738
+ init_constants();
586
739
  var ENVELOPE_MSG_LEN_OFF = 8;
587
740
  var Framer = class {
588
741
  buf = Buffer.allocUnsafe(65536);
@@ -614,6 +767,9 @@ var Framer = class {
614
767
  }
615
768
  };
616
769
 
770
+ // src/net/connection.ts
771
+ init_constants();
772
+
617
773
  // src/common/logger.ts
618
774
  var noop = (() => {
619
775
  });
@@ -629,6 +785,39 @@ function resolveLogger(logger) {
629
785
  return logger ?? NOOP;
630
786
  }
631
787
 
788
+ // src/cron/cron-frame.ts
789
+ init_constants();
790
+ init_frame();
791
+ function packCreateCron(seq, body) {
792
+ const json = Buffer.from(JSON.stringify(body), "utf8");
793
+ const buf = frame(1793 /* CreateCron */, seq, json.length);
794
+ json.copy(buf, HEADER_SIZE);
795
+ return buf;
796
+ }
797
+ function packDeleteCron(seq, name) {
798
+ const buf = frame(1794 /* DeleteCron */, seq, name.length);
799
+ name.copy(buf, HEADER_SIZE);
800
+ return buf;
801
+ }
802
+ var CRON_FIRE_FIXED = 2 + 8 + 8;
803
+ function decodeCronFire(body) {
804
+ if (body.length < CRON_FIRE_FIXED) return void 0;
805
+ const nameLen = body.readUInt16LE(0);
806
+ if (body.length < CRON_FIRE_FIXED + nameLen) return void 0;
807
+ const fireTimeMs = body.readBigUInt64LE(2);
808
+ const fireCount = body.readBigUInt64LE(10);
809
+ const name = body.subarray(18, 18 + nameLen).toString();
810
+ return { name, fireTimeMs, fireCount };
811
+ }
812
+ function packCronAck(seq, name, ok) {
813
+ const bodyLen = 3 + name.length;
814
+ const buf = frame(1797 /* CronAck */, seq, bodyLen);
815
+ buf.writeUInt16LE(name.length, HEADER_SIZE);
816
+ buf[HEADER_SIZE + 2] = ok ? 0 : 1;
817
+ name.copy(buf, HEADER_SIZE + 3);
818
+ return buf;
819
+ }
820
+
632
821
  // src/net/connection.ts
633
822
  function parseAddr(addr) {
634
823
  const i = addr.lastIndexOf(":");
@@ -652,6 +841,7 @@ var Connection = class _Connection {
652
841
  log;
653
842
  activeSubs = /* @__PURE__ */ new Map();
654
843
  metrics;
844
+ cronState;
655
845
  attachSocket(socket) {
656
846
  socket.setNoDelay(true);
657
847
  socket.on("data", (chunk) => this.framer.push(chunk, (f) => this.onFrame(f)));
@@ -706,6 +896,10 @@ var Connection = class _Connection {
706
896
  setMetrics(m) {
707
897
  this.metrics = m;
708
898
  }
899
+ /** Attach cron state so the connection can dispatch CronFire frames. */
900
+ setCronState(s) {
901
+ this.cronState = s;
902
+ }
709
903
  // ── Frame routing ─────────────────────────────────────────────────────────
710
904
  // Seq-based dispatch: match reply.header.seq → pending request. O(1).
711
905
  resolvePending(frame2) {
@@ -757,6 +951,10 @@ var Connection = class _Connection {
757
951
  this.handleBatchDeliver(frame2);
758
952
  return;
759
953
  }
954
+ case 1796 /* CronFire */: {
955
+ this.dispatchCronFire(frame2);
956
+ return;
957
+ }
760
958
  case 1538 /* Pong */:
761
959
  return;
762
960
  default: {
@@ -806,6 +1004,19 @@ var Connection = class _Connection {
806
1004
  off = tailEnd;
807
1005
  }
808
1006
  }
1007
+ // ── Cron dispatch ──────────────────────────────────────────────────────────
1008
+ dispatchCronFire(frame2) {
1009
+ const body = frame2.subarray(HEADER_SIZE);
1010
+ const view = decodeCronFire(body);
1011
+ if (!view) return;
1012
+ const handler = this.cronState?.getHandler(view.name);
1013
+ const nameBuf = Buffer.from(view.name);
1014
+ if (!handler) {
1015
+ this.send(packCronAck(this.nextSeq(), nameBuf, false));
1016
+ return;
1017
+ }
1018
+ handler({ name: view.name, fireTime: view.fireTimeMs, fireCount: view.fireCount }).then(() => this.send(packCronAck(this.nextSeq(), nameBuf, true))).catch(() => this.send(packCronAck(this.nextSeq(), nameBuf, false)));
1019
+ }
809
1020
  // ── Subscriptions ─────────────────────────────────────────────────────────
810
1021
  async sendSubscribeV2(consumerId, filter, handler, onRenew) {
811
1022
  return new Promise((resolve, reject) => {
@@ -848,6 +1059,14 @@ var Connection = class _Connection {
848
1059
  }).catch(() => {
849
1060
  });
850
1061
  }
1062
+ this.replayCrons();
1063
+ }
1064
+ replayCrons() {
1065
+ if (!this.cronState) return;
1066
+ for (const { config } of this.cronState.allConfigs()) {
1067
+ const seq = this.nextSeq();
1068
+ this.socket.write(packCreateCron(seq, config));
1069
+ }
851
1070
  }
852
1071
  // ── Routes (internal use) ─────────────────────────────────────────────────
853
1072
  registerRoute(consumerId, handler) {
@@ -1131,6 +1350,7 @@ var ClientMetrics = class {
1131
1350
  };
1132
1351
 
1133
1352
  // src/stream/publish.ts
1353
+ init_constants();
1134
1354
  var EMPTY2 = Buffer.alloc(0);
1135
1355
  function toMsgIdBuf(id) {
1136
1356
  if (id == null) return EMPTY2;
@@ -1162,6 +1382,89 @@ async function streamRequest(conn, sid, subject, data, timeoutMs) {
1162
1382
  return Buffer.alloc(0);
1163
1383
  }
1164
1384
 
1385
+ // src/client/client.ts
1386
+ init_constants();
1387
+
1388
+ // src/cron/cron-builder.ts
1389
+ var CronBuilder = class {
1390
+ constructor(conn, cronState, cronName) {
1391
+ this.conn = conn;
1392
+ this.cronState = cronState;
1393
+ this.cronName = cronName;
1394
+ }
1395
+ expr;
1396
+ timezone;
1397
+ timeoutMs = 3e4;
1398
+ allowOverlap = false;
1399
+ every(expression) {
1400
+ this.expr = expression;
1401
+ return this;
1402
+ }
1403
+ tz(timezone) {
1404
+ this.timezone = timezone;
1405
+ return this;
1406
+ }
1407
+ timeout(ms) {
1408
+ this.timeoutMs = ms;
1409
+ return this;
1410
+ }
1411
+ overlap(allow) {
1412
+ this.allowOverlap = allow;
1413
+ return this;
1414
+ }
1415
+ async run(handler) {
1416
+ if (!this.expr) throw new Error("cron expression required \u2014 call .every()");
1417
+ const body = {
1418
+ name: this.cronName,
1419
+ every: this.expr,
1420
+ tz: this.timezone,
1421
+ timeout_ms: this.timeoutMs,
1422
+ overlap: this.allowOverlap
1423
+ };
1424
+ const seq = this.conn.nextSeq();
1425
+ await this.conn.sendExpectReply(packCreateCron(seq, body));
1426
+ this.cronState.register(this.cronName, body, handler);
1427
+ return new CronHandle(this.conn, this.cronState, this.cronName);
1428
+ }
1429
+ };
1430
+ var CronHandle = class {
1431
+ constructor(conn, cronState, cronName) {
1432
+ this.conn = conn;
1433
+ this.cronState = cronState;
1434
+ this.cronName = cronName;
1435
+ }
1436
+ get name() {
1437
+ return this.cronName;
1438
+ }
1439
+ async stop() {
1440
+ const nameBuf = Buffer.from(this.cronName);
1441
+ const seq = this.conn.nextSeq();
1442
+ await this.conn.sendExpectReply(packDeleteCron(seq, nameBuf));
1443
+ this.cronState.remove(this.cronName);
1444
+ }
1445
+ };
1446
+
1447
+ // src/cron/cron-state.ts
1448
+ var CronState = class {
1449
+ handlers = /* @__PURE__ */ new Map();
1450
+ register(name, config, handler) {
1451
+ this.handlers.set(name, { handler, config });
1452
+ }
1453
+ remove(name) {
1454
+ this.handlers.delete(name);
1455
+ }
1456
+ getHandler(name) {
1457
+ return this.handlers.get(name)?.handler;
1458
+ }
1459
+ allConfigs() {
1460
+ const out = [];
1461
+ for (const [name, entry] of this.handlers) {
1462
+ out.push({ name, config: entry.config });
1463
+ }
1464
+ return out;
1465
+ }
1466
+ };
1467
+
1165
1468
  // src/client/client.ts
1166
1469
  var DEFAULT_CONFIG = {
1167
1470
  servers: ["127.0.0.1:9898"],
@@ -1176,6 +1479,7 @@ var ArbitroClient = class {
1176
1479
  logger;
1177
1480
  sidCache = /* @__PURE__ */ new Map();
1178
1481
  _metrics = new ClientMetrics();
1482
+ _cronState = new CronState();
1179
1483
  constructor(config) {
1180
1484
  this.cfg = { ...DEFAULT_CONFIG, ...config };
1181
1485
  this.tls = config.tls;
@@ -1192,6 +1496,7 @@ var ArbitroClient = class {
1192
1496
  this.logger
1193
1497
  );
1194
1498
  this.conn.setMetrics(this._metrics);
1499
+ this.conn.setCronState(this._cronState);
1195
1500
  return this;
1196
1501
  }
1197
1502
  /**
@@ -1277,6 +1582,22 @@ var ArbitroClient = class {
1277
1582
  const sid = await this.resolveStreamId(streamName);
1278
1583
  return streamRequest(this.conn, sid, subject, data, timeoutMs);
1279
1584
  }
1585
+ /**
1586
+ * Publish a message with a delivery delay. The broker parks the message
1587
+ * in its delayed journal and delivers it to consumers after `delayMs`
1588
+ * milliseconds. Returns a Promise that resolves once the broker confirms
1589
+ * receipt.
1590
+ */
1591
+ async publishDelayed(streamName, subject, data, delayMs) {
1592
+ const sid = await this.resolveStreamId(streamName);
1593
+ const { packPublishDelayed: packPublishDelayed2 } = await Promise.resolve().then(() => (init_publish(), publish_exports));
1594
+ const { Flag: Flag3 } = await Promise.resolve().then(() => (init_constants(), constants_exports));
1595
+ const subj = Buffer.from(this.prefixed(subject));
1596
+ await this.conn.sendExpectReply(
1597
+ packPublishDelayed2(this.conn.nextSeq(), sid, subj, data, BigInt(delayMs), Flag3.AckReq)
1598
+ );
1599
+ this._metrics.publishesSent++;
1600
+ }
1280
1601
  async subscribe(streamName, configOrCb, callbackOrOpts, opts) {
1281
1602
  let config;
1282
1603
  let callback;
@@ -1396,7 +1717,7 @@ var ArbitroClient = class {
1396
1717
  maxInflight: config.maxAckPending ?? 0,
1397
1718
  ackPolicy: ackPolicyByte,
1398
1719
  deliverPolicy: deliverPolicyToU8(config.deliverPolicy),
1399
- deliverMode: config.fanout ? 1 : 0,
1720
+ deliverMode: config.fanout ? 0 : 1,
1400
1721
  ackWaitMs: config.ackWaitMs ?? 0,
1401
1722
  startSeq: BigInt(config.startSeq ?? 0)
1402
1723
  };
@@ -1511,6 +1832,11 @@ var ArbitroClient = class {
1511
1832
  stream(name, config) {
1512
1833
  return new Stream(this, name, config);
1513
1834
  }
1835
+ // ── Cron ──────────────────────────────────────────────────────────────────
1836
+ /** Start building a cron job. Call `.every()` then `.run()` to register. */
1837
+ cron(name) {
1838
+ return new CronBuilder(this.conn, this._cronState, name);
1839
+ }
1514
1840
  // ── Lifecycle ─────────────────────────────────────────────────────────────
1515
1841
  async close() {
1516
1842
  await this.conn.close();
@@ -1613,6 +1939,8 @@ function zodCodec(zodSchema) {
1613
1939
  ArbitroError,
1614
1940
  Codec,
1615
1941
  Consumer,
1942
+ CronBuilder,
1943
+ CronHandle,
1616
1944
  DeliverPolicy,
1617
1945
  ErrorCode,
1618
1946
  JournalType,