@arbitro/client 0.2.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,602 @@
1
+ import { ZodRawShape, ZodObject, output } from 'zod';
2
+
3
+ type LogFn = {
4
+ (msg: string): void;
5
+ (obj: object, msg: string): void;
6
+ };
7
+ interface Logger {
8
+ trace: LogFn;
9
+ debug: LogFn;
10
+ info: LogFn;
11
+ warn: LogFn;
12
+ error: LogFn;
13
+ child(bindings: Record<string, unknown>): Logger;
14
+ }
15
+
16
+ declare enum DeliverPolicy {
17
+ All = "All",
18
+ Last = "Last",
19
+ New = "New",
20
+ BySeq = "ByStartSeq",
21
+ ByTime = "ByStartTime"
22
+ }
23
+ declare enum JournalType {
24
+ Memory = "Memory",
25
+ Tolerant = "Tolerant",
26
+ Strict = "Strict"
27
+ }
28
+ declare enum AckPolicy {
29
+ Explicit = "explicit",
30
+ None = "none"
31
+ }
32
+ interface FlushConfig {
33
+ intervalMs?: number;
34
+ maxMessages?: number;
35
+ maxBytes?: number;
36
+ }
37
+ type JournalConfig = {
38
+ type: JournalType.Memory;
39
+ } | {
40
+ type: JournalType.Tolerant;
41
+ } | {
42
+ type: JournalType.Strict;
43
+ flush?: FlushConfig;
44
+ };
45
+ interface StreamConfig {
46
+ subjectFilter: string;
47
+ journal?: JournalConfig;
48
+ maxMsgs?: number;
49
+ maxBytes?: number;
50
+ maxAgeMs?: number;
51
+ /**
52
+ * Per-stream broker-side dedup window in milliseconds.
53
+ *
54
+ * - `0` or undefined (default): no dedup. Every publish is stored;
55
+ * `msgId` is ignored.
56
+ * - `>0`: any publish that carries a `msgId` matching one the
57
+ * broker has stored for THIS stream within the last
58
+ * `idempotencyWindowMs` is rejected with the `IdempotencyDuplicate`
59
+ * wire error. Useful for safe retries — the first publish wins.
60
+ *
61
+ * The broker clamps requested windows above 5 minutes (300 000 ms)
62
+ * down to that ceiling, matching JetStream behaviour.
63
+ */
64
+ idempotencyWindowMs?: number;
65
+ }
66
+ interface DeleteStreamOpts {
67
+ deleteData?: boolean;
68
+ }
69
+ interface StreamInfo {
70
+ name: string;
71
+ config: StreamConfig;
72
+ lastSeq: number;
73
+ }
74
+ /**
75
+ * Per-subject inflight cap. Each entry caps the number of in-flight
76
+ * (delivered, unacked) messages on subjects matching `pattern`. Patterns
77
+ * may use NATS-style wildcards (`*`, `>`).
78
+ *
79
+ * Only enforced when the owning consumer's `ackPolicy` is `Explicit`;
80
+ * silently dropped server-side for fire-and-forget consumers (because
81
+ * fire-and-forget bindings skip inflight tracking entirely).
82
+ */
83
+ interface SubjectInflightLimit {
84
+ pattern: string;
85
+ limit: number;
86
+ }
87
+ interface ConsumerConfig {
88
+ name?: string;
89
+ filter?: string;
90
+ fanout?: boolean;
91
+ /** Consumer-side ACK policy. None = fire-and-forget delivery, Explicit = consumer must ACK. */
92
+ ackPolicy?: AckPolicy;
93
+ deliverPolicy?: DeliverPolicy;
94
+ startSeq?: bigint;
95
+ startTime?: bigint;
96
+ maxAckPending?: number;
97
+ ackWaitMs?: number;
98
+ maxDeliver?: number;
99
+ removeUnusedAfterMs?: number;
100
+ /**
101
+ * Per-subject max inflight (list of pattern → limit pairs).
102
+ * Only effective with `ackPolicy: Explicit`.
103
+ */
104
+ maxSubjectInflights?: SubjectInflightLimit[];
105
+ }
106
+ interface ConsumerInfo {
107
+ group: string;
108
+ stream: string;
109
+ config: ConsumerConfig;
110
+ }
111
+ interface SubscribeOptions {
112
+ fetchTimeoutMs?: number;
113
+ }
114
+ interface ReconnectConfig {
115
+ enabled?: boolean;
116
+ maxAttempts?: number;
117
+ intervalMs?: number;
118
+ jitter?: boolean;
119
+ }
120
+ interface TlsConfig {
121
+ enabled?: boolean;
122
+ ca?: Buffer | string;
123
+ cert?: Buffer | string;
124
+ key?: Buffer | string;
125
+ }
126
+ interface ClientConfig {
127
+ servers: string[];
128
+ prefix?: string;
129
+ timeout?: number;
130
+ reconnect?: ReconnectConfig;
131
+ tls?: TlsConfig;
132
+ logger?: Logger;
133
+ }
134
+
135
+ declare class ArbitroError extends Error {
136
+ readonly code: 'connect' | 'timeout' | 'protocol' | 'server' | 'closed';
137
+ readonly brokerName?: string | undefined;
138
+ readonly brokerDetails?: unknown | undefined;
139
+ /**
140
+ * Numeric wire error code from the broker's `RepError` frame. Only
141
+ * populated when `code === 'server'`. Use the `ErrorCode` enum to
142
+ * compare (e.g. `err.wireCode === ErrorCode.IdempotencyDuplicate`).
143
+ */
144
+ readonly wireCode?: number | undefined;
145
+ constructor(message: string, code: 'connect' | 'timeout' | 'protocol' | 'server' | 'closed', brokerName?: string | undefined, brokerDetails?: unknown | undefined,
146
+ /**
147
+ * Numeric wire error code from the broker's `RepError` frame. Only
148
+ * populated when `code === 'server'`. Use the `ErrorCode` enum to
149
+ * compare (e.g. `err.wireCode === ErrorCode.IdempotencyDuplicate`).
150
+ */
151
+ wireCode?: number | undefined);
152
+ }
153
+ /**
154
+ * Wire-level error codes. Mirrors the `ErrorCode` enum in
155
+ * `arbitro-proto`. New codes added to the broker MUST be appended here
156
+ * with the matching numeric value.
157
+ */
158
+ declare const ErrorCode: {
159
+ readonly UnknownAction: 1;
160
+ readonly BufferTooShort: 2;
161
+ readonly InvalidLength: 3;
162
+ readonly InvalidEntryCount: 4;
163
+ readonly AuthRequired: 257;
164
+ readonly AuthFailed: 258;
165
+ readonly StreamNotFound: 513;
166
+ readonly StreamAlreadyExists: 514;
167
+ readonly StreamFull: 515;
168
+ readonly StreamFilterOverlap: 516;
169
+ readonly SubjectNotFound: 517;
170
+ /** Publish carried a `msgId` already seen for this stream within
171
+ * `idempotencyWindowMs`. Original write stands; safe to treat as
172
+ * a successful publish at the application level. */
173
+ readonly IdempotencyDuplicate: 518;
174
+ readonly ConsumerNotFound: 769;
175
+ readonly ConsumerAlreadyExists: 770;
176
+ readonly ConsumerFilterOverlap: 771;
177
+ readonly InvalidSequence: 1025;
178
+ readonly MaxInflightReached: 1026;
179
+ readonly AckTimeout: 1027;
180
+ readonly ServerShuttingDown: 1281;
181
+ readonly InternalError: 1282;
182
+ };
183
+ type ErrorCodeValue = (typeof ErrorCode)[keyof typeof ErrorCode];
184
+
185
+ interface ClientMetricsSnapshot {
186
+ /** Calls to `publish` / `publishAck` / `publishBatch` entries. */
187
+ publishesSent: number;
188
+ /** Total entries inside `publishBatch` calls (summed across batches). */
189
+ publishBatchEntries: number;
190
+ /** `Deliver` frames received from the broker (one per message). */
191
+ deliveriesReceived: number;
192
+ /** Currently-open subscriptions (gauge — inc at subscribe, dec at close). */
193
+ activeSubscriptions: number;
194
+ /** `Ack` frames sent to the broker. */
195
+ acksSent: number;
196
+ /** `Nack` frames sent to the broker. */
197
+ nacksSent: number;
198
+ /** Successful reconnections after a session drop. */
199
+ reconnects: number;
200
+ /** Outstanding pending request-reply slots (gauge). */
201
+ pendingReplies: number;
202
+ }
203
+ /**
204
+ * Live atomic-ish counters. JavaScript is single-threaded per worker so
205
+ * plain `++` is sufficient — no `Atomics` needed.
206
+ *
207
+ * Held on `ArbitroClient` and shared with `Connection` so the demux loop
208
+ * can bump `deliveriesReceived` directly. Public `snapshot()` returns a
209
+ * frozen copy for logging or external scraping.
210
+ */
211
+ declare class ClientMetrics {
212
+ publishesSent: number;
213
+ publishBatchEntries: number;
214
+ deliveriesReceived: number;
215
+ activeSubscriptions: number;
216
+ acksSent: number;
217
+ nacksSent: number;
218
+ reconnects: number;
219
+ pendingReplies: number;
220
+ snapshot(): ClientMetricsSnapshot;
221
+ }
222
+
223
+ type DeliveryHandler = (frame: Buffer) => void;
224
+ declare class Connection {
225
+ private readonly reconnectAddr?;
226
+ private readonly reconnectCfg?;
227
+ private seq;
228
+ private connId;
229
+ private framer;
230
+ private routes;
231
+ private pending;
232
+ private socket;
233
+ private closing;
234
+ private readonly log;
235
+ private activeSubs;
236
+ private metrics?;
237
+ private constructor();
238
+ private attachSocket;
239
+ static connect(addr: string, timeoutMs?: number, tlsCfg?: TlsConfig, reconnectCfg?: ReconnectConfig, logger?: Logger): Promise<Connection>;
240
+ nextSeq(): bigint;
241
+ /**
242
+ * Attach a metrics sink. Called by `ArbitroClient` after `connect()`.
243
+ * The connection bumps `deliveriesReceived` on every Deliver/RepBatch
244
+ * entry and `reconnects` on successful reconnections. Unset = no-op.
245
+ */
246
+ setMetrics(m: ClientMetrics): void;
247
+ private resolvePending;
248
+ private rejectPending;
249
+ private onFrame;
250
+ private handleBatchDeliver;
251
+ sendSubscribeV2(consumerId: number, filter: Buffer, handler: DeliveryHandler, onRenew?: (newConsumerId: number) => void): Promise<number>;
252
+ cancelSubscription(consumerId: number): void;
253
+ private resubscribeAll;
254
+ registerRoute(consumerId: number, handler: DeliveryHandler): void;
255
+ unregisterRoute(consumerId: number): void;
256
+ send(frame: Buffer): void;
257
+ /** Send frame and wait for RepOk. Returns the ref_seq from body. */
258
+ sendExpectReply(frame: Buffer, timeoutMs?: number): Promise<bigint>;
259
+ /** Send frame and wait for full reply frame buffer. */
260
+ sendExpectReplyRaw(frame: Buffer, timeoutMs?: number): Promise<Buffer>;
261
+ close(): Promise<void>;
262
+ private drain;
263
+ private handleClose;
264
+ private tryReconnect;
265
+ }
266
+
267
+ /**
268
+ * One entry of a `publishBatch` call.
269
+ *
270
+ * `msgId` (optional) is an opaque byte string the broker uses for
271
+ * per-stream deduplication when the target stream was created with
272
+ * `idempotencyWindowMs > 0`. Leaving it empty/undefined disables
273
+ * dedup for this entry (mixing dedup + non-dedup entries in a single
274
+ * batch is allowed).
275
+ */
276
+ interface BatchPublishEntry {
277
+ subject: string;
278
+ payload: Buffer;
279
+ msgId?: Buffer;
280
+ }
281
+
282
+ /**
283
+ * Optional per-publish options.
284
+ *
285
+ * `msgId` is an opaque byte string the broker uses for dedup when the
286
+ * target stream was created with `idempotencyWindowMs > 0`. Empty or
287
+ * undefined means "no dedup for this publish" — safe to omit on
288
+ * non-idempotent streams.
289
+ */
290
+ interface PublishOpts {
291
+ msgId?: Buffer | string;
292
+ }
293
+
294
+ declare function encodeString(s: string): Buffer;
295
+ declare function decodeString(buf: Buffer): string;
296
+
297
+ declare function encodeJson(value: unknown): Buffer;
298
+ declare function decodeJson<T = unknown>(buf: Buffer): T;
299
+
300
+ /** Core encode/decode contract. `fields` is required only for schema-based codecs
301
+ * that power LazyMessage<T> getters — other encodings may omit it. */
302
+ interface Encoding<T> {
303
+ encode(value: T): Buffer;
304
+ decode(buf: Buffer): T;
305
+ readonly fields?: string[];
306
+ }
307
+ /** Maps a FieldType string literal to its corresponding TypeScript type. */
308
+ type FieldTypeMap = {
309
+ string: string;
310
+ number: number;
311
+ boolean: boolean;
312
+ bigint: bigint;
313
+ buffer: Buffer;
314
+ unknown: unknown;
315
+ };
316
+ /** Infers the TypeScript record type from a schema definition.
317
+ * Eliminates the need to define both an interface and a Schema<T>. */
318
+ type InferSchema<S extends Record<string, FieldType>> = {
319
+ [K in keyof S]: FieldTypeMap[S[K]];
320
+ };
321
+ declare abstract class TextEncoding implements Encoding<string> {
322
+ abstract readonly encoding: BufferEncoding;
323
+ encode(value: string): Buffer;
324
+ decode(buf: Buffer): string;
325
+ }
326
+ declare class StringCodec extends TextEncoding {
327
+ readonly encoding: BufferEncoding;
328
+ constructor(encoding?: BufferEncoding);
329
+ }
330
+ declare class JsonCodec<T> implements Encoding<T> {
331
+ private readonly text;
332
+ constructor(encoding?: BufferEncoding);
333
+ encode(value: T): Buffer;
334
+ decode(buf: Buffer): T;
335
+ }
336
+ type FieldType = 'string' | 'number' | 'boolean' | 'bigint' | 'buffer' | 'unknown';
337
+ type Schema<T> = {
338
+ [K in keyof Required<T>]: FieldType;
339
+ };
340
+ declare class Codec<T extends Record<string, unknown>> implements Encoding<T> {
341
+ readonly fields: (keyof T & string)[];
342
+ private readonly packr;
343
+ private readonly unpackr;
344
+ constructor(schema: Schema<T>);
345
+ encode(value: T): Buffer;
346
+ decode(buf: Buffer): T;
347
+ }
348
+ /** Creates a msgpack Codec with the TypeScript type inferred from the schema definition. */
349
+ declare function schema<S extends Record<string, FieldType>>(def: S): Codec<InferSchema<S>>;
350
+
351
+ type SendFn = (frame: Buffer) => void;
352
+ declare class Message {
353
+ private readonly frame;
354
+ private readonly send;
355
+ private readonly seqFn;
356
+ private _subjectLen;
357
+ constructor(frame: Buffer, send: SendFn, seqFn: () => bigint);
358
+ /** Delivery sequence — used to ack/nack this message. */
359
+ seq(): bigint;
360
+ /** Consumer ID that received this delivery. */
361
+ consumerId(): number;
362
+ /** Subject hash — echoed back in ack for O(1) credit release. */
363
+ subjectHash(): number;
364
+ private subjLen;
365
+ /** Zero-copy view of the subject bytes. */
366
+ subject(): Buffer;
367
+ /** Zero-copy view of the payload bytes. */
368
+ data(): Buffer;
369
+ /** Acknowledge — fire-and-forget to broker. */
370
+ ack(): void;
371
+ /** Negative acknowledge — immediate requeue. */
372
+ nack(): void;
373
+ /** Negative acknowledge with redelivery delay (ms). */
374
+ nackDelay(ms: number): void;
375
+ }
376
+
377
+ type MsgCallback$1 = (msg: Message) => void;
378
+ declare class Subscription {
379
+ private consumerId;
380
+ private readonly conn;
381
+ private readonly streamName;
382
+ private readonly fetchTimeoutMs;
383
+ private callback;
384
+ private fetchQueue;
385
+ private msgBuf;
386
+ private closed;
387
+ constructor(consumerId: number, conn: Connection, streamName: string, fetchTimeoutMs: number);
388
+ /** Consumer ID assigned by the server. */
389
+ id(): number;
390
+ deliver(frame: Buffer): void;
391
+ onMessage(cb: MsgCallback$1): void;
392
+ fetch(count: number, timeoutMs?: number): Promise<Message[]>;
393
+ close(): void;
394
+ }
395
+
396
+ type LazyMessage<T> = T & {
397
+ readonly _raw: Buffer;
398
+ decode(): T;
399
+ ack(): void;
400
+ nack(): void;
401
+ nackDelay(delayMs: number): void;
402
+ };
403
+ declare function makeLazyMessage<T extends Record<string, unknown>>(raw: Buffer, codec: Encoding<T>, fields: string[], onAck: () => void, onNack: () => void, onNackDelay?: (ms: number) => void): LazyMessage<T>;
404
+
405
+ type RawCallback = (msg: Message) => void;
406
+ declare class Consumer {
407
+ private readonly client;
408
+ readonly streamName: string;
409
+ readonly config: ConsumerConfig;
410
+ private _consumerId?;
411
+ constructor(client: ArbitroClient, streamName: string, config: ConsumerConfig, _consumerId?: number | undefined);
412
+ get name(): string;
413
+ get consumerId(): number | undefined;
414
+ /**
415
+ * Publish through this consumer's stream. Returns `Promise<void>` that
416
+ * resolves on broker `RepOk`. Await to wait, or ignore for
417
+ * fire-and-forget semantics — the caller's choice, on the same call.
418
+ */
419
+ publish(subject: string, data: Buffer): Promise<void>;
420
+ create(): Promise<this>;
421
+ upsert(): Promise<this>;
422
+ delete(): Promise<void>;
423
+ exists(): Promise<boolean>;
424
+ /**
425
+ * Live pending-ack count for this consumer (delivered, not yet acked).
426
+ * Uses the cached consumer ID when available; otherwise looks it up
427
+ * from `streamName + name`. Equivalent of NATS `num_ack_pending`.
428
+ */
429
+ getPendings(): Promise<number>;
430
+ subscribe(cb?: RawCallback, opts?: SubscribeOptions): Promise<Subscription>;
431
+ subscribe<T extends Record<string, unknown>>(codec: Encoding<T>, cb: (msg: LazyMessage<T>) => void, opts?: SubscribeOptions): Promise<Subscription>;
432
+ }
433
+
434
+ type MsgCallback = (msg: Message) => void;
435
+ declare class ArbitroClient {
436
+ private conn;
437
+ private readonly cfg;
438
+ private readonly tls;
439
+ private readonly logger;
440
+ private readonly sidCache;
441
+ private readonly _metrics;
442
+ constructor(config: ClientConfig);
443
+ connect(): Promise<this>;
444
+ /**
445
+ * Point-in-time snapshot of client counters: publishes sent, deliveries
446
+ * received, acks/nacks, active subscriptions, reconnects. Cheap — just
447
+ * reads plain integer fields. Call on a timer to chart throughput.
448
+ */
449
+ metrics(): ClientMetricsSnapshot;
450
+ /** Internal connection accessor for Stream/Consumer publish methods. */
451
+ _conn(): Connection;
452
+ /** Default timeout from config. */
453
+ get timeout(): number;
454
+ /** Apply prefix to subject if configured. */
455
+ private prefixed;
456
+ /**
457
+ * Publish a message. Returns a `Promise<void>` that resolves once the
458
+ * broker confirms receipt (`RepOk`). The TS idiom is "everything async,
459
+ * the caller chooses to await":
460
+ *
461
+ * await client.publish(s, subj, data) // wait for ack
462
+ * client.publish(s, subj, data) // fire-and-forget
463
+ * client.publish(s, subj, data).catch(handleError) // async error path
464
+ *
465
+ * The broker always emits `RepOk` regardless of whether the caller
466
+ * awaits — that's what enables the same call site to support both
467
+ * semantics. A "no-reply" path doesn't save wire bytes, so it isn't
468
+ * exposed.
469
+ */
470
+ publish(streamName: string, subject: string, data: Buffer, opts?: PublishOpts): Promise<void>;
471
+ /**
472
+ * @deprecated alias for {@link publish}. The default `publish` already
473
+ * waits for `RepOk` and returns a Promise, so this method is identical.
474
+ */
475
+ publishAck(streamName: string, subject: string, data: Buffer, opts?: PublishOpts): Promise<void>;
476
+ /** Batch publish — single V2 BatchPubFrame, ONE round-trip. Resolves
477
+ * to `first_seq` (sequence of the first message in the batch; the N
478
+ * messages occupy `[first_seq, first_seq + N - 1]`).
479
+ *
480
+ * Mirrors `publish`: the call always exchanges request/response with
481
+ * the broker. The caller decides whether to actually wait:
482
+ *
483
+ * await client.publishBatch(stream, msgs) // barrier
484
+ * client.publishBatch(stream, msgs) // fire-and-forget (promise dropped)
485
+ * client.publishBatch(stream, msgs) // (suppress unhandled rejection)
486
+ * .catch(() => {})
487
+ *
488
+ * Each entry may carry an optional `msgId` for broker-side dedup on
489
+ * streams created with `idempotencyWindowMs > 0`. If any entry
490
+ * collides with a previously-stored id (or another entry in the
491
+ * SAME batch), the whole batch is rejected — `publishBatch` is
492
+ * atomic.
493
+ */
494
+ publishBatch(streamName: string, messages: BatchPublishEntry[]): Promise<bigint>;
495
+ /** Request-reply. */
496
+ request(streamName: string, subject: string, data: Buffer, timeoutMs?: number): Promise<Buffer>;
497
+ /** Subscribe with explicit consumer config. */
498
+ subscribe(streamName: string, config: ConsumerConfig, callback?: MsgCallback, opts?: SubscribeOptions): Promise<Subscription>;
499
+ /** Subscribe by consumer name only (consumer must already exist). */
500
+ subscribe(streamName: string, callback: MsgCallback): Promise<Subscription>;
501
+ createStream(name: string, config: StreamConfig): Promise<Stream>;
502
+ upsertStream(name: string, config: StreamConfig): Promise<Stream>;
503
+ deleteStream(name: string, _opts?: DeleteStreamOpts): Promise<void>;
504
+ getStreamInfo(name: string): Promise<StreamInfo | null>;
505
+ listStreams(): Promise<StreamInfo[]>;
506
+ streamExists(name: string): Promise<boolean>;
507
+ purgeStream(name: string): Promise<number>;
508
+ drainSubject(streamName: string, subject: string): Promise<number>;
509
+ createConsumer(streamName: string, config: ConsumerConfig): Promise<Consumer>;
510
+ private createConsumerRaw;
511
+ upsertConsumer(streamName: string, config: ConsumerConfig): Promise<Consumer>;
512
+ /** Delete consumer by server-assigned ID. */
513
+ deleteConsumer(consumerId: number): Promise<void>;
514
+ /** Delete consumer by stream + name (lookup ID first). */
515
+ deleteConsumer(streamName: string, name: string): Promise<void>;
516
+ getConsumerId(streamName: string, name: string): Promise<number | null>;
517
+ consumerExists(streamName: string, name: string): Promise<boolean>;
518
+ /**
519
+ * Live pending-ack count for one consumer — the number of messages the
520
+ * consumer has been delivered but not yet acked. Equivalent of NATS
521
+ * JetStream's `num_ack_pending`. Single broker round-trip; engine cost
522
+ * is one O(1) Vec read per shard.
523
+ */
524
+ getPending(consumerId: number): Promise<number>;
525
+ getPending(streamName: string, name: string): Promise<number>;
526
+ getConsumerInfo(streamName: string, name: string): Promise<ConsumerInfo | null>;
527
+ listConsumers(streamName?: string): Promise<ConsumerInfo[]>;
528
+ /** Resolve stream name → server wire_hash_32. Caches the result. */
529
+ private resolveStreamId;
530
+ /** Get cached stream_id or throw (for sync fire-and-forget paths). */
531
+ private cachedSid;
532
+ private ensureConsumer;
533
+ /** Pre-resolve stream_id from server (GetStream). Required before sync publish(). */
534
+ resolveStream(name: string): Promise<void>;
535
+ stream(name: string, config?: StreamConfig): Stream;
536
+ close(): Promise<void>;
537
+ }
538
+
539
+ declare class Stream {
540
+ private readonly client;
541
+ readonly name: string;
542
+ private _config;
543
+ constructor(client: ArbitroClient, name: string, config?: StreamConfig);
544
+ get config(): StreamConfig | undefined;
545
+ create(config?: StreamConfig): Promise<this>;
546
+ upsert(config?: StreamConfig): Promise<this>;
547
+ delete(opts?: DeleteStreamOpts): Promise<void>;
548
+ exists(): Promise<boolean>;
549
+ info(): Promise<StreamInfo | null>;
550
+ /**
551
+ * Publish to this stream. Returns a `Promise<void>` that resolves on
552
+ * broker `RepOk`. Await it to wait for confirmation, or ignore the
553
+ * returned promise for fire-and-forget semantics.
554
+ *
555
+ * Pass `opts.msgId` to opt this publish into broker-side dedup on
556
+ * streams created with `idempotencyWindowMs > 0`. Duplicate ids
557
+ * within the window are rejected with a `ClientError` carrying the
558
+ * `IdempotencyDuplicate` code.
559
+ */
560
+ publish(subject: string, data: Buffer, opts?: PublishOpts): Promise<void>;
561
+ /** @deprecated alias for {@link publish}. */
562
+ publishAck(subject: string, data: Buffer, opts?: PublishOpts): Promise<void>;
563
+ /** Batch publish — single V2 BatchPubFrame, ONE round-trip.
564
+ * Resolves with `first_seq` (the N messages occupy
565
+ * `[first_seq, first_seq + N - 1]`).
566
+ *
567
+ * Like {@link publish}, the call always exchanges request/response
568
+ * with the broker; the caller decides whether to wait via `await`. */
569
+ publishBatch(messages: BatchPublishEntry[]): Promise<bigint>;
570
+ request(subject: string, data: Buffer, timeoutMs?: number): Promise<Buffer>;
571
+ consumer(overrides?: Partial<ConsumerConfig>): Consumer;
572
+ topic<T extends Record<string, unknown>>(subject: string, codec: Encoding<T>): Topic<T>;
573
+ }
574
+
575
+ declare class Topic<T extends Record<string, unknown>> {
576
+ private readonly stream;
577
+ private readonly subject;
578
+ private readonly codec;
579
+ private readonly fields;
580
+ constructor(stream: Stream, subject: string, codec: Encoding<T>);
581
+ publish(value: T): void;
582
+ publishAck(value: T): Promise<void>;
583
+ publishBatch(values: T[]): void;
584
+ subscribe(group: string, cb: (msg: LazyMessage<T>) => void): Promise<Subscription>;
585
+ }
586
+
587
+ declare function streamId(name: Buffer | string): number;
588
+
589
+ /** Wraps a ZodObject schema as an Encoding<T>.
590
+ *
591
+ * - encode: msgpack (no Zod overhead — bytes out)
592
+ * - decode: msgpack unpack → schema.parse() (validates on inbound data)
593
+ * - fields: Object.keys(schema.shape) — powers LazyMessage<T> getters
594
+ *
595
+ * Validation cost on decode is intentional and acceptable on the management
596
+ * path. Do NOT use zodCodec on a hot publish loop.
597
+ */
598
+ declare function zodCodec<S extends ZodRawShape>(zodSchema: ZodObject<S>): Encoding<output<ZodObject<S>>> & {
599
+ readonly fields: string[];
600
+ };
601
+
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 };