@arbitro/client 0.2.0 → 0.5.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.
- package/README.md +99 -21
- package/dist/chunk-C2QLJBAC.mjs +102 -0
- package/dist/chunk-C2QLJBAC.mjs.map +1 -0
- package/dist/chunk-GW36GP2C.mjs +124 -0
- package/dist/chunk-GW36GP2C.mjs.map +1 -0
- package/dist/constants-57DO6N3H.mjs +33 -0
- package/dist/constants-57DO6N3H.mjs.map +1 -0
- package/dist/index.d.mts +129 -1
- package/dist/index.d.ts +129 -1
- package/dist/index.js +680 -95
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +439 -99
- package/dist/index.mjs.map +1 -1
- package/dist/publish-BSVUMN7T.mjs +14 -0
- package/dist/publish-BSVUMN7T.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,111 @@ 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
|
-
##
|
|
222
|
+
## Cron Scheduling
|
|
223
223
|
|
|
224
|
-
|
|
224
|
+
Register distributed cron jobs with queue semantics — multiple workers, single delivery per fire.
|
|
225
225
|
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
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
|
+
```
|
|
229
238
|
|
|
230
|
-
|
|
239
|
+
Crons re-register automatically on reconnect. No persistence — if the broker restarts, clients re-register their crons when they reconnect.
|
|
231
240
|
|
|
232
|
-
|
|
241
|
+
## Delayed Publish
|
|
233
242
|
|
|
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. |
|
|
243
|
+
Schedule message delivery for the future:
|
|
239
244
|
|
|
240
|
-
|
|
245
|
+
```typescript
|
|
246
|
+
await client.publishDelayed("ORDERS", "orders.reminder", payload, 5000); // 5s delay
|
|
247
|
+
```
|
|
241
248
|
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
249
|
+
Messages are persisted immediately — survives broker restart.
|
|
250
|
+
|
|
251
|
+
## Workflow Orchestration
|
|
252
|
+
|
|
253
|
+
Client-side linear pipelines over Arbitro streams. The broker has no workflow-specific code -- everything uses streams, consumer groups, and idempotent publish.
|
|
254
|
+
|
|
255
|
+
### WorkflowBuilder API
|
|
256
|
+
|
|
257
|
+
| Method | Signature | Description |
|
|
258
|
+
|--------|-----------|-------------|
|
|
259
|
+
| `trigger` | `(subject: string) => this` | Subject pattern that triggers new instances. Required. |
|
|
260
|
+
| `triggerStream` | *(not yet implemented)* | Planned: auto-subscribe to an external stream for trigger. |
|
|
261
|
+
| `step` | `(name: string, handler: StepHandler) => this` | Append a processing step. |
|
|
262
|
+
| `compensate` | *(not yet implemented)* | Planned: rollback handler per step (saga pattern). |
|
|
263
|
+
| `maxRetries` | *(not yet implemented)* | Planned: attempts before DLQ. |
|
|
264
|
+
| `maxContextSize` | *(not yet implemented)* | Planned: max context payload in bytes. |
|
|
265
|
+
| `ackWait` | `(ms: number) => this` | Ack timeout for failover (default: 30000). |
|
|
266
|
+
| `inflight` | `(n: number) => this` | Concurrent tasks per worker (default: 10). |
|
|
267
|
+
| `start` | `() => Promise<WorkflowHandle>` | Register streams, consumer, and start processing. |
|
|
268
|
+
|
|
269
|
+
### WorkflowHandle API
|
|
270
|
+
|
|
271
|
+
| Method | Signature | Description |
|
|
272
|
+
|--------|-----------|-------------|
|
|
273
|
+
| `trigger` | `(client: ArbitroClient, context: Buffer) => Promise<number>` | Trigger a new workflow instance. Returns the instance ID. |
|
|
274
|
+
| `name` | `string` (getter) | Workflow name. |
|
|
275
|
+
|
|
276
|
+
### Complete Example
|
|
277
|
+
|
|
278
|
+
```typescript
|
|
279
|
+
import { ArbitroClient, WorkflowBuilder } from '@arbitro/client'
|
|
280
|
+
import type { StepContext, StepResult } from '@arbitro/client'
|
|
281
|
+
|
|
282
|
+
const client = new ArbitroClient({ servers: ['127.0.0.1:9898'] })
|
|
283
|
+
await client.connect()
|
|
284
|
+
|
|
285
|
+
const wf = await new WorkflowBuilder(client, 'order-process')
|
|
286
|
+
.trigger('orders.created')
|
|
287
|
+
// Step 1: validate
|
|
288
|
+
.step('validate', async (ctx: StepContext): Promise<StepResult> => {
|
|
289
|
+
const validated = await validateOrder(ctx.context)
|
|
290
|
+
return { context: validated }
|
|
291
|
+
})
|
|
292
|
+
// Step 2: charge
|
|
293
|
+
.step('charge', async (ctx: StepContext): Promise<StepResult> => {
|
|
294
|
+
const receipt = await chargePayment(ctx.context)
|
|
295
|
+
return { context: receipt }
|
|
296
|
+
})
|
|
297
|
+
// Step 3: ship
|
|
298
|
+
.step('ship', async (ctx: StepContext): Promise<StepResult> => {
|
|
299
|
+
const tracking = await createShipment(ctx.context)
|
|
300
|
+
return { context: tracking }
|
|
301
|
+
})
|
|
302
|
+
.ackWait(30_000)
|
|
303
|
+
.inflight(10)
|
|
304
|
+
.start()
|
|
305
|
+
|
|
306
|
+
// Manual trigger
|
|
307
|
+
const instanceId = await wf.trigger(client, Buffer.from('order-123-payload'))
|
|
308
|
+
console.log(`started instance ${instanceId}`)
|
|
247
309
|
```
|
|
248
310
|
|
|
311
|
+
### Internals
|
|
312
|
+
|
|
313
|
+
- Tasks flow through `_wf.{name}.tasks` stream with a consumer `_wf.{name}.workers`.
|
|
314
|
+
- Each step transition publishes with `msgId` format `wf:{instance}:{step}:{attempt}` for deduplication.
|
|
315
|
+
- `ackWait` enables failover: if a worker dies, the broker redelivers to another subscriber.
|
|
316
|
+
|
|
317
|
+
> **Note:** The TypeScript workflow module currently implements the core step pipeline. Compensation (saga), DLQ, `triggerStream`, `maxRetries`, and `maxContextSize` are available in the Rust client and planned for the TS client.
|
|
318
|
+
|
|
319
|
+
## Reconnect behavior
|
|
320
|
+
|
|
321
|
+
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:
|
|
322
|
+
|
|
323
|
+
- Docker restarts
|
|
324
|
+
- broker failover tests
|
|
325
|
+
- chaos scenarios with durable consumers
|
|
326
|
+
|
|
249
327
|
## Validation
|
|
250
328
|
|
|
251
329
|
```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-C2QLJBAC.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 // 0x09xx reserved (workflow removed — now a client-side library).\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-C2QLJBAC.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-GW36GP2C.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-C2QLJBAC.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-57DO6N3H.mjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":[],"sourcesContent":[],"mappings":"","names":[]}
|
package/dist/index.d.mts
CHANGED
|
@@ -86,6 +86,8 @@ interface SubjectInflightLimit {
|
|
|
86
86
|
}
|
|
87
87
|
interface ConsumerConfig {
|
|
88
88
|
name?: string;
|
|
89
|
+
/** Shared consumer group name for round-robin delivery. Defaults to `name`. */
|
|
90
|
+
group?: string;
|
|
89
91
|
filter?: string;
|
|
90
92
|
fanout?: boolean;
|
|
91
93
|
/** Consumer-side ACK policy. None = fire-and-forget delivery, Explicit = consumer must ACK. */
|
|
@@ -220,6 +222,35 @@ declare class ClientMetrics {
|
|
|
220
222
|
snapshot(): ClientMetricsSnapshot;
|
|
221
223
|
}
|
|
222
224
|
|
|
225
|
+
interface CreateCronBody {
|
|
226
|
+
readonly name: string;
|
|
227
|
+
readonly every: string;
|
|
228
|
+
readonly tz?: string | undefined;
|
|
229
|
+
readonly timeout_ms: number;
|
|
230
|
+
readonly overlap: boolean;
|
|
231
|
+
}
|
|
232
|
+
|
|
233
|
+
type CronHandler = (ctx: CronContext) => Promise<void>;
|
|
234
|
+
/** Context passed to the cron handler on each fire. */
|
|
235
|
+
interface CronContext {
|
|
236
|
+
/** Cron job name. */
|
|
237
|
+
readonly name: string;
|
|
238
|
+
/** UTC timestamp (ms since epoch) when the broker intended this fire. */
|
|
239
|
+
readonly fireTime: bigint;
|
|
240
|
+
/** Monotonic fire counter (1-based). */
|
|
241
|
+
readonly fireCount: bigint;
|
|
242
|
+
}
|
|
243
|
+
declare class CronState {
|
|
244
|
+
private readonly handlers;
|
|
245
|
+
register(name: string, config: CreateCronBody, handler: CronHandler): void;
|
|
246
|
+
remove(name: string): void;
|
|
247
|
+
getHandler(name: string): CronHandler | undefined;
|
|
248
|
+
allConfigs(): ReadonlyArray<{
|
|
249
|
+
name: string;
|
|
250
|
+
config: CreateCronBody;
|
|
251
|
+
}>;
|
|
252
|
+
}
|
|
253
|
+
|
|
223
254
|
type DeliveryHandler = (frame: Buffer) => void;
|
|
224
255
|
declare class Connection {
|
|
225
256
|
private readonly reconnectAddr?;
|
|
@@ -234,6 +265,7 @@ declare class Connection {
|
|
|
234
265
|
private readonly log;
|
|
235
266
|
private activeSubs;
|
|
236
267
|
private metrics?;
|
|
268
|
+
private cronState?;
|
|
237
269
|
private constructor();
|
|
238
270
|
private attachSocket;
|
|
239
271
|
static connect(addr: string, timeoutMs?: number, tlsCfg?: TlsConfig, reconnectCfg?: ReconnectConfig, logger?: Logger): Promise<Connection>;
|
|
@@ -244,13 +276,17 @@ declare class Connection {
|
|
|
244
276
|
* entry and `reconnects` on successful reconnections. Unset = no-op.
|
|
245
277
|
*/
|
|
246
278
|
setMetrics(m: ClientMetrics): void;
|
|
279
|
+
/** Attach cron state so the connection can dispatch CronFire frames. */
|
|
280
|
+
setCronState(s: CronState): void;
|
|
247
281
|
private resolvePending;
|
|
248
282
|
private rejectPending;
|
|
249
283
|
private onFrame;
|
|
250
284
|
private handleBatchDeliver;
|
|
285
|
+
private dispatchCronFire;
|
|
251
286
|
sendSubscribeV2(consumerId: number, filter: Buffer, handler: DeliveryHandler, onRenew?: (newConsumerId: number) => void): Promise<number>;
|
|
252
287
|
cancelSubscription(consumerId: number): void;
|
|
253
288
|
private resubscribeAll;
|
|
289
|
+
private replayCrons;
|
|
254
290
|
registerRoute(consumerId: number, handler: DeliveryHandler): void;
|
|
255
291
|
unregisterRoute(consumerId: number): void;
|
|
256
292
|
send(frame: Buffer): void;
|
|
@@ -431,6 +467,30 @@ declare class Consumer {
|
|
|
431
467
|
subscribe<T extends Record<string, unknown>>(codec: Encoding<T>, cb: (msg: LazyMessage<T>) => void, opts?: SubscribeOptions): Promise<Subscription>;
|
|
432
468
|
}
|
|
433
469
|
|
|
470
|
+
declare class CronBuilder {
|
|
471
|
+
private readonly conn;
|
|
472
|
+
private readonly cronState;
|
|
473
|
+
private readonly cronName;
|
|
474
|
+
private expr;
|
|
475
|
+
private timezone;
|
|
476
|
+
private timeoutMs;
|
|
477
|
+
private allowOverlap;
|
|
478
|
+
constructor(conn: Connection, cronState: CronState, cronName: string);
|
|
479
|
+
every(expression: string): this;
|
|
480
|
+
tz(timezone: string): this;
|
|
481
|
+
timeout(ms: number): this;
|
|
482
|
+
overlap(allow: boolean): this;
|
|
483
|
+
run(handler: CronHandler): Promise<CronHandle>;
|
|
484
|
+
}
|
|
485
|
+
declare class CronHandle {
|
|
486
|
+
private readonly conn;
|
|
487
|
+
private readonly cronState;
|
|
488
|
+
private readonly cronName;
|
|
489
|
+
constructor(conn: Connection, cronState: CronState, cronName: string);
|
|
490
|
+
get name(): string;
|
|
491
|
+
stop(): Promise<void>;
|
|
492
|
+
}
|
|
493
|
+
|
|
434
494
|
type MsgCallback = (msg: Message) => void;
|
|
435
495
|
declare class ArbitroClient {
|
|
436
496
|
private conn;
|
|
@@ -439,6 +499,7 @@ declare class ArbitroClient {
|
|
|
439
499
|
private readonly logger;
|
|
440
500
|
private readonly sidCache;
|
|
441
501
|
private readonly _metrics;
|
|
502
|
+
private readonly _cronState;
|
|
442
503
|
constructor(config: ClientConfig);
|
|
443
504
|
connect(): Promise<this>;
|
|
444
505
|
/**
|
|
@@ -494,6 +555,13 @@ declare class ArbitroClient {
|
|
|
494
555
|
publishBatch(streamName: string, messages: BatchPublishEntry[]): Promise<bigint>;
|
|
495
556
|
/** Request-reply. */
|
|
496
557
|
request(streamName: string, subject: string, data: Buffer, timeoutMs?: number): Promise<Buffer>;
|
|
558
|
+
/**
|
|
559
|
+
* Publish a message with a delivery delay. The broker parks the message
|
|
560
|
+
* in its delayed journal and delivers it to consumers after `delayMs`
|
|
561
|
+
* milliseconds. Returns a Promise that resolves once the broker confirms
|
|
562
|
+
* receipt.
|
|
563
|
+
*/
|
|
564
|
+
publishDelayed(streamName: string, subject: string, data: Buffer, delayMs: number): Promise<void>;
|
|
497
565
|
/** Subscribe with explicit consumer config. */
|
|
498
566
|
subscribe(streamName: string, config: ConsumerConfig, callback?: MsgCallback, opts?: SubscribeOptions): Promise<Subscription>;
|
|
499
567
|
/** Subscribe by consumer name only (consumer must already exist). */
|
|
@@ -533,6 +601,8 @@ declare class ArbitroClient {
|
|
|
533
601
|
/** Pre-resolve stream_id from server (GetStream). Required before sync publish(). */
|
|
534
602
|
resolveStream(name: string): Promise<void>;
|
|
535
603
|
stream(name: string, config?: StreamConfig): Stream;
|
|
604
|
+
/** Start building a cron job. Call `.every()` then `.run()` to register. */
|
|
605
|
+
cron(name: string): CronBuilder;
|
|
536
606
|
close(): Promise<void>;
|
|
537
607
|
}
|
|
538
608
|
|
|
@@ -586,6 +656,64 @@ declare class Topic<T extends Record<string, unknown>> {
|
|
|
586
656
|
|
|
587
657
|
declare function streamId(name: Buffer | string): number;
|
|
588
658
|
|
|
659
|
+
declare class WorkflowHandle {
|
|
660
|
+
private readonly workflowName;
|
|
661
|
+
private readonly taskStreamName;
|
|
662
|
+
private readonly dlqStreamName;
|
|
663
|
+
private readonly sub;
|
|
664
|
+
private readonly triggerSub;
|
|
665
|
+
constructor(workflowName: string, taskStreamName: string, dlqStreamName: string, sub: unknown, triggerSub: unknown | undefined);
|
|
666
|
+
get name(): string;
|
|
667
|
+
get taskStream(): string;
|
|
668
|
+
get dlqStream(): string;
|
|
669
|
+
trigger(client: ArbitroClient, context: Buffer): Promise<number>;
|
|
670
|
+
}
|
|
671
|
+
|
|
672
|
+
interface StepContext {
|
|
673
|
+
readonly name: string;
|
|
674
|
+
readonly instanceId: number;
|
|
675
|
+
readonly stepIndex: number;
|
|
676
|
+
readonly attempt: number;
|
|
677
|
+
readonly context: Buffer;
|
|
678
|
+
}
|
|
679
|
+
interface StepResult {
|
|
680
|
+
readonly context: Buffer;
|
|
681
|
+
}
|
|
682
|
+
type StepHandler = (ctx: StepContext) => Promise<StepResult>;
|
|
683
|
+
declare class WorkflowBuilder {
|
|
684
|
+
private readonly client;
|
|
685
|
+
private readonly workflowName;
|
|
686
|
+
private triggerSubject;
|
|
687
|
+
private triggerStreamName;
|
|
688
|
+
private readonly steps;
|
|
689
|
+
private ackWaitMs;
|
|
690
|
+
private maxInflightVal;
|
|
691
|
+
private maxRetriesVal;
|
|
692
|
+
private maxContextSizeVal;
|
|
693
|
+
constructor(client: ArbitroClient, workflowName: string);
|
|
694
|
+
trigger(subject: string): this;
|
|
695
|
+
triggerStream(streamName: string): this;
|
|
696
|
+
step(name: string, handler: StepHandler): this;
|
|
697
|
+
/** Compensation handler for the most recently added step. */
|
|
698
|
+
compensate(_stepName: string, handler: StepHandler): this;
|
|
699
|
+
ackWait(ms: number): this;
|
|
700
|
+
inflight(n: number): this;
|
|
701
|
+
maxRetries(n: number): this;
|
|
702
|
+
maxContextSize(bytes: number): this;
|
|
703
|
+
start(): Promise<WorkflowHandle>;
|
|
704
|
+
private subscribeWorker;
|
|
705
|
+
private subscribeTrigger;
|
|
706
|
+
}
|
|
707
|
+
|
|
708
|
+
/** Bit flag set on stepIndex to mark compensation tasks. */
|
|
709
|
+
declare const COMPENSATION_BIT = 32768;
|
|
710
|
+
interface DecodedTask {
|
|
711
|
+
readonly instanceId: number;
|
|
712
|
+
readonly stepIndex: number;
|
|
713
|
+
readonly attempt: number;
|
|
714
|
+
readonly context: Buffer;
|
|
715
|
+
}
|
|
716
|
+
|
|
589
717
|
/** Wraps a ZodObject schema as an Encoding<T>.
|
|
590
718
|
*
|
|
591
719
|
* - encode: msgpack (no Zod overhead — bytes out)
|
|
@@ -599,4 +727,4 @@ declare function zodCodec<S extends ZodRawShape>(zodSchema: ZodObject<S>): Encod
|
|
|
599
727
|
readonly fields: string[];
|
|
600
728
|
};
|
|
601
729
|
|
|
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 };
|
|
730
|
+
export { AckPolicy, ArbitroClient, ArbitroError, type BatchPublishEntry, COMPENSATION_BIT, type ClientConfig, type ClientMetricsSnapshot, Codec, Consumer, type ConsumerConfig, type ConsumerInfo, CronBuilder, type CronContext, CronHandle, type CronHandler, type DecodedTask, 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, type StepContext, type StepHandler, type StepResult, Stream, type StreamConfig, type StreamInfo, StringCodec, type SubjectInflightLimit, type SubscribeOptions, Subscription, type TlsConfig, Topic, WorkflowBuilder, WorkflowHandle, decodeJson, decodeString, encodeJson, encodeString, makeLazyMessage, schema, streamId, zodCodec };
|