@eventferry/kafka 3.3.1 → 3.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/CHANGELOG.md +60 -0
- package/README.md +363 -0
- package/dist/consume.cjs +115 -0
- package/dist/consume.cjs.map +1 -0
- package/dist/consume.d.cts +114 -0
- package/dist/consume.d.ts +114 -0
- package/dist/consume.js +88 -0
- package/dist/consume.js.map +1 -0
- package/dist/index.cjs +449 -17
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +364 -3
- package/dist/index.d.ts +364 -3
- package/dist/index.js +449 -17
- package/dist/index.js.map +1 -1
- package/package.json +12 -4
package/dist/index.d.cts
CHANGED
|
@@ -1,5 +1,97 @@
|
|
|
1
1
|
import { PublishableMessage, PublishResult, Logger, PublishErrorKind, Publisher } from '@eventferry/core';
|
|
2
2
|
|
|
3
|
+
/**
|
|
4
|
+
* Admin surface for `KafkaPublisher`. A typed wrapper over each driver's
|
|
5
|
+
* underlying admin client — implementations live in `kafkajs-driver.ts` and
|
|
6
|
+
* `confluent-driver.ts`. The publisher exposes this via `publisher.admin()`
|
|
7
|
+
* (returns a connected admin) and `publisher.ensureTopics()` (idempotent
|
|
8
|
+
* topic provisioning built on top).
|
|
9
|
+
*
|
|
10
|
+
* Scope is deliberately the most-used subset of the Kafka AdminClient
|
|
11
|
+
* protocol — listing, describing, creating topics and partitions. ACL
|
|
12
|
+
* management, quota inspection, and consumer-group operations are left to
|
|
13
|
+
* the underlying client (reach for kafkajs's `Admin` directly if needed).
|
|
14
|
+
*/
|
|
15
|
+
/** Specification for creating one topic. */
|
|
16
|
+
interface TopicCreateSpec {
|
|
17
|
+
topic: string;
|
|
18
|
+
/** Default: cluster's `num.partitions` broker setting. */
|
|
19
|
+
numPartitions?: number;
|
|
20
|
+
/** Default: cluster's `default.replication.factor` broker setting. */
|
|
21
|
+
replicationFactor?: number;
|
|
22
|
+
/**
|
|
23
|
+
* Per-topic config entries (e.g. `{ "retention.ms": "604800000" }`).
|
|
24
|
+
* See Kafka broker docs for the full set.
|
|
25
|
+
*/
|
|
26
|
+
configEntries?: Record<string, string>;
|
|
27
|
+
}
|
|
28
|
+
/** Topic + partition descriptor returned by describeTopics. */
|
|
29
|
+
interface TopicMetadata {
|
|
30
|
+
topic: string;
|
|
31
|
+
/** Empty when the topic doesn't exist (so callers can detect absence cheaply). */
|
|
32
|
+
partitions: PartitionMetadata[];
|
|
33
|
+
}
|
|
34
|
+
interface PartitionMetadata {
|
|
35
|
+
partitionId: number;
|
|
36
|
+
/** Broker id of the partition leader; -1 if no leader is known. */
|
|
37
|
+
leader: number;
|
|
38
|
+
/** Replica broker ids. */
|
|
39
|
+
replicas: number[];
|
|
40
|
+
/** In-sync replica broker ids. */
|
|
41
|
+
isr: number[];
|
|
42
|
+
}
|
|
43
|
+
/** Specification for growing a topic's partition count (never shrink). */
|
|
44
|
+
interface PartitionGrowSpec {
|
|
45
|
+
topic: string;
|
|
46
|
+
/** Total partition count after the change. MUST be ≥ the current count. */
|
|
47
|
+
totalCount: number;
|
|
48
|
+
}
|
|
49
|
+
/**
|
|
50
|
+
* Typed admin surface exposed by {@link KafkaPublisher.admin}.
|
|
51
|
+
*
|
|
52
|
+
* The returned object is already connected — call `.close()` (or let the
|
|
53
|
+
* publisher's `disconnect()` cascade close it) when done.
|
|
54
|
+
*
|
|
55
|
+
* All methods may throw native errors from the underlying client. The
|
|
56
|
+
* publisher does not classify admin errors via the `errorKind` machinery
|
|
57
|
+
* because admin failures are operator-facing and don't flow through the
|
|
58
|
+
* relay's retry path.
|
|
59
|
+
*/
|
|
60
|
+
interface KafkaAdmin {
|
|
61
|
+
/** All topic names visible to this principal, including internal topics. */
|
|
62
|
+
listTopics(): Promise<string[]>;
|
|
63
|
+
/**
|
|
64
|
+
* Metadata for the given topics. Topics that don't exist on the cluster
|
|
65
|
+
* are returned with an empty `partitions` array — callers detect absence
|
|
66
|
+
* cheaply without a try/catch.
|
|
67
|
+
*/
|
|
68
|
+
describeTopics(topics: string[]): Promise<TopicMetadata[]>;
|
|
69
|
+
/**
|
|
70
|
+
* Create topics. Idempotent at this layer: topics that already exist are
|
|
71
|
+
* silently skipped (the underlying client may throw `TopicExistsError`;
|
|
72
|
+
* we swallow it). Use `ensureTopics` on the publisher for partition-count
|
|
73
|
+
* + replication-factor coherence checks.
|
|
74
|
+
*/
|
|
75
|
+
createTopics(specs: TopicCreateSpec[]): Promise<void>;
|
|
76
|
+
/**
|
|
77
|
+
* Grow each topic's partition count. Never shrinks (Kafka does not
|
|
78
|
+
* support shrinking partitions). Specs whose `totalCount` equals the
|
|
79
|
+
* current partition count are silently skipped.
|
|
80
|
+
*/
|
|
81
|
+
createPartitions(specs: PartitionGrowSpec[]): Promise<void>;
|
|
82
|
+
/** Disconnect the admin client. */
|
|
83
|
+
close(): Promise<void>;
|
|
84
|
+
}
|
|
85
|
+
/**
|
|
86
|
+
* Optional driver-level hook returned by {@link KafkaDriver.admin}. Drivers
|
|
87
|
+
* implement this thin contract; the publisher composes the higher-level
|
|
88
|
+
* `KafkaAdmin` and `ensureTopics` on top.
|
|
89
|
+
*/
|
|
90
|
+
interface KafkaDriverAdmin extends KafkaAdmin {
|
|
91
|
+
/** Called by the publisher before any method is invoked. */
|
|
92
|
+
connect(): Promise<void>;
|
|
93
|
+
}
|
|
94
|
+
|
|
3
95
|
/**
|
|
4
96
|
* Low-level driver contract. Each concrete driver (kafkajs, confluent)
|
|
5
97
|
* adapts its native client to this minimal surface. The KafkaPublisher
|
|
@@ -19,6 +111,14 @@ interface KafkaDriver {
|
|
|
19
111
|
* uses this to decide whether `sendBatch` is atomic.
|
|
20
112
|
*/
|
|
21
113
|
readonly transactional: boolean;
|
|
114
|
+
/**
|
|
115
|
+
* Construct a NEW admin client. The returned admin is not yet connected —
|
|
116
|
+
* the publisher calls `.connect()` before handing it to the user.
|
|
117
|
+
*
|
|
118
|
+
* Optional: drivers without an admin surface may omit this; the publisher
|
|
119
|
+
* throws a clear error when `publisher.admin()` is called on such a driver.
|
|
120
|
+
*/
|
|
121
|
+
admin?(): Promise<KafkaDriverAdmin>;
|
|
22
122
|
}
|
|
23
123
|
/**
|
|
24
124
|
* TLS configuration for client connections. Pass a full {@link TlsConfig}
|
|
@@ -27,7 +127,20 @@ interface KafkaDriver {
|
|
|
27
127
|
* TLS using the driver's default trust store).
|
|
28
128
|
*
|
|
29
129
|
* `rejectUnauthorized` is intentionally NOT a knob here — TLS verification is
|
|
30
|
-
* non-negotiable. Dev clusters with self-signed certs pass their CA via
|
|
130
|
+
* non-negotiable. Dev clusters with self-signed certs MUST pass their CA via
|
|
131
|
+
* `ca` (validation still happens, against your CA instead of the system
|
|
132
|
+
* trust store). If the broker is addressed by an IP literal or a hostname
|
|
133
|
+
* that doesn't match the cert SAN, set `servername` to the hostname the
|
|
134
|
+
* cert was issued for so SNI + verification align.
|
|
135
|
+
*
|
|
136
|
+
* **Driver parity:**
|
|
137
|
+
* - `ca`, `cert`, `key`, `passphrase` work on both kafkajs and confluent.
|
|
138
|
+
* - `servername` is honored by **kafkajs** (Node `tls.connect` reads
|
|
139
|
+
* `servername` directly). On the **confluent** driver it's a documented
|
|
140
|
+
* no-op — librdkafka derives SNI from the broker address and v1.x's
|
|
141
|
+
* kafkaJS-compat layer does not surface an override. Use the kafkajs
|
|
142
|
+
* driver for clusters where you need the SNI lever, or wait for
|
|
143
|
+
* librdkafka to expose it.
|
|
31
144
|
*/
|
|
32
145
|
interface TlsConfig {
|
|
33
146
|
/** PEM-encoded CA bundle. Buffers and strings both accepted. */
|
|
@@ -38,7 +151,13 @@ interface TlsConfig {
|
|
|
38
151
|
key?: string | Buffer;
|
|
39
152
|
/** Passphrase for an encrypted private key. */
|
|
40
153
|
passphrase?: string;
|
|
41
|
-
/**
|
|
154
|
+
/**
|
|
155
|
+
* SNI host. Set this when the broker address (e.g. an IP literal or an
|
|
156
|
+
* internal DNS name) does NOT match the certificate's Subject
|
|
157
|
+
* Alternative Names. Honored on the kafkajs driver; no-op on the
|
|
158
|
+
* confluent driver (librdkafka does not expose an SNI override at
|
|
159
|
+
* v1.x).
|
|
160
|
+
*/
|
|
42
161
|
servername?: string;
|
|
43
162
|
}
|
|
44
163
|
/**
|
|
@@ -149,6 +268,14 @@ interface ProducerBehaviorConfig {
|
|
|
149
268
|
acks?: number;
|
|
150
269
|
/** Compression codec. Driver maps to its native enum. */
|
|
151
270
|
compression?: "none" | "gzip" | "snappy" | "lz4" | "zstd";
|
|
271
|
+
/**
|
|
272
|
+
* (confluent only) Compression level for the chosen codec. Defaults vary
|
|
273
|
+
* per codec — librdkafka picks the broker-friendly default when unset.
|
|
274
|
+
* Common ranges: gzip 1–9, lz4 0–12, zstd 1–22 (higher = smaller + slower).
|
|
275
|
+
*
|
|
276
|
+
* No-op on the kafkajs driver (kafkajs does not expose codec levels).
|
|
277
|
+
*/
|
|
278
|
+
compressionLevel?: number;
|
|
152
279
|
/**
|
|
153
280
|
* (confluent only) How long the producer waits to accumulate records before
|
|
154
281
|
* flushing a partition batch. Default 0 (ship-immediately). Increase to
|
|
@@ -199,7 +326,70 @@ interface ProducerBehaviorConfig {
|
|
|
199
326
|
* whether this callback throws.
|
|
200
327
|
*/
|
|
201
328
|
onTransactionAbort?: (error: Error) => void;
|
|
329
|
+
/**
|
|
330
|
+
* (confluent only) Raw librdkafka producer-config keys merged on top of
|
|
331
|
+
* eventferry's translated config. Use for tuning surface area we don't
|
|
332
|
+
* expose typed (e.g. `queue.buffering.max.messages`, `socket.keepalive.enable`,
|
|
333
|
+
* `statistics.interval.ms`). Native keys win against the translated ones,
|
|
334
|
+
* so this can also be used to override defaults.
|
|
335
|
+
*
|
|
336
|
+
* Ignored by the kafkajs driver — log a one-time warning instead of
|
|
337
|
+
* silently dropping. Use `rawKafkaJsProducerConfig` for kafkajs-side tuning.
|
|
338
|
+
*/
|
|
339
|
+
rawProducerConfig?: Record<string, unknown>;
|
|
340
|
+
/**
|
|
341
|
+
* (kafkajs only) Raw producer-config keys merged into kafkajs's
|
|
342
|
+
* `kafka.producer({...})` call. Native keys win against the translated
|
|
343
|
+
* ones. Use for kafkajs-internal knobs like `retry`, `metadataMaxAge`,
|
|
344
|
+
* `idempotent` overrides, etc.
|
|
345
|
+
*
|
|
346
|
+
* No-op on the confluent driver — use `rawProducerConfig` there.
|
|
347
|
+
*/
|
|
348
|
+
rawKafkaJsProducerConfig?: Record<string, unknown>;
|
|
349
|
+
/**
|
|
350
|
+
* (kafkajs only) Custom partitioner factory passed straight to
|
|
351
|
+
* `kafka.producer({ createPartitioner })`. Overrides {@link partitioner}
|
|
352
|
+
* preset entirely. See kafkajs docs for the factory signature:
|
|
353
|
+
* `() => (args: { topic, partitionMetadata, message }) => number`.
|
|
354
|
+
*
|
|
355
|
+
* Ignored by the confluent driver — librdkafka's partitioner is a C
|
|
356
|
+
* extension point, not a JS callback.
|
|
357
|
+
*/
|
|
358
|
+
customPartitioner?: () => (args: unknown) => number;
|
|
359
|
+
/**
|
|
360
|
+
* (confluent only) Periodic librdkafka statistics callback. When set,
|
|
361
|
+
* eventferry wires `stats_cb` on the underlying producer and parses the
|
|
362
|
+
* JSON payload librdkafka emits every {@link statsIntervalMs} ms.
|
|
363
|
+
*
|
|
364
|
+
* The shape is intentionally opaque — librdkafka's stats schema is huge
|
|
365
|
+
* (txmsgs, rxbytes, queue depth, broker timeouts, per-topic / per-partition
|
|
366
|
+
* counters…) and evolves across versions. Documented at
|
|
367
|
+
* https://github.com/confluentinc/librdkafka/blob/master/STATISTICS.md.
|
|
368
|
+
* Cast to your own narrower type if you're consuming a known subset.
|
|
369
|
+
*
|
|
370
|
+
* No-op on the kafkajs driver — kafkajs has no equivalent surface.
|
|
371
|
+
* Pair with {@link statsIntervalMs} (defaults to 30000 ms when this hook
|
|
372
|
+
* is set but `rawProducerConfig['statistics.interval.ms']` isn't).
|
|
373
|
+
*/
|
|
374
|
+
onStats?: (stats: LibrdkafkaStats) => void;
|
|
375
|
+
/**
|
|
376
|
+
* (confluent only) Override the polling interval the librdkafka stats
|
|
377
|
+
* callback fires at. Maps to `statistics.interval.ms`. Defaults to
|
|
378
|
+
* 30000 ms when {@link onStats} is set; defaults to 0 (disabled)
|
|
379
|
+
* otherwise — librdkafka spends CPU on this and we don't want to enable
|
|
380
|
+
* it silently. Set to 0 to suppress emission while keeping the hook
|
|
381
|
+
* defined (useful for tests).
|
|
382
|
+
*/
|
|
383
|
+
statsIntervalMs?: number;
|
|
202
384
|
}
|
|
385
|
+
/**
|
|
386
|
+
* Opaque envelope for librdkafka's stats JSON. The schema is
|
|
387
|
+
* version-specific and large; eventferry surfaces it untyped so you can
|
|
388
|
+
* cast to whatever subset you care about.
|
|
389
|
+
*
|
|
390
|
+
* Reference: https://github.com/confluentinc/librdkafka/blob/master/STATISTICS.md
|
|
391
|
+
*/
|
|
392
|
+
type LibrdkafkaStats = Record<string, unknown>;
|
|
203
393
|
type DriverKind = "kafkajs" | "confluent";
|
|
204
394
|
|
|
205
395
|
interface KjsProducer {
|
|
@@ -213,6 +403,11 @@ interface KjsTransaction {
|
|
|
213
403
|
commit(): Promise<void>;
|
|
214
404
|
abort(): Promise<void>;
|
|
215
405
|
}
|
|
406
|
+
interface KjsPartitionersNamespace {
|
|
407
|
+
DefaultPartitioner: () => unknown;
|
|
408
|
+
LegacyPartitioner: () => unknown;
|
|
409
|
+
JavaCompatiblePartitioner: () => unknown;
|
|
410
|
+
}
|
|
216
411
|
interface KafkaJsDriverOptions extends KafkaConnectionConfig, ProducerBehaviorConfig {
|
|
217
412
|
/**
|
|
218
413
|
* Optional logger for the driver's own diagnostics (e.g. warnings about
|
|
@@ -235,7 +430,19 @@ declare class KafkaJsDriver implements KafkaDriver {
|
|
|
235
430
|
* the send/transaction logic can be exercised without a real broker.
|
|
236
431
|
*/
|
|
237
432
|
protected createProducer(): Promise<KjsProducer>;
|
|
433
|
+
/**
|
|
434
|
+
* Compute the options object passed to `kafka.producer({...})`. Exposed
|
|
435
|
+
* as a test seam so power-user escape hatches (customPartitioner,
|
|
436
|
+
* rawKafkaJsProducerConfig) can be asserted without a live broker.
|
|
437
|
+
*/
|
|
438
|
+
protected buildProducerOptions(partitioners: KjsPartitionersNamespace | undefined): Promise<Record<string, unknown>>;
|
|
238
439
|
disconnect(): Promise<void>;
|
|
440
|
+
/**
|
|
441
|
+
* Construct a kafkajs admin client wrapped in the eventferry-facing
|
|
442
|
+
* `KafkaDriverAdmin` shape. The publisher calls `.connect()` on the
|
|
443
|
+
* returned object before exposing it via `publisher.admin()`.
|
|
444
|
+
*/
|
|
445
|
+
admin(): Promise<KafkaDriverAdmin>;
|
|
239
446
|
sendBatch(messages: PublishableMessage[]): Promise<PublishResult[]>;
|
|
240
447
|
}
|
|
241
448
|
/** Internal — used by tests. Resets the dedup so warnings can be observed in isolation. */
|
|
@@ -288,6 +495,12 @@ declare class ConfluentDriver implements KafkaDriver {
|
|
|
288
495
|
*/
|
|
289
496
|
protected createProducer(): Promise<CkProducer>;
|
|
290
497
|
disconnect(): Promise<void>;
|
|
498
|
+
/**
|
|
499
|
+
* Construct a librdkafka-backed admin client wrapped in the eventferry
|
|
500
|
+
* `KafkaDriverAdmin` shape. The publisher's `connect()` is called before
|
|
501
|
+
* the admin reaches the user.
|
|
502
|
+
*/
|
|
503
|
+
admin(): Promise<KafkaDriverAdmin>;
|
|
291
504
|
sendBatch(messages: PublishableMessage[]): Promise<PublishResult[]>;
|
|
292
505
|
}
|
|
293
506
|
|
|
@@ -361,6 +574,18 @@ interface KafkaPublisherHooks {
|
|
|
361
574
|
* Useful for observability dashboards that track EOS failure rates.
|
|
362
575
|
*/
|
|
363
576
|
onTransactionAbort?(error: Error): void | Promise<void>;
|
|
577
|
+
/**
|
|
578
|
+
* Fires when the broker fences this producer — the previous publish
|
|
579
|
+
* batch reported at least one `errorKind: "fenced"` result. Receives
|
|
580
|
+
* the first fenced `Error` so dashboards can attribute the incident.
|
|
581
|
+
*
|
|
582
|
+
* - When `autoRecoverFromFence` is on, this hook fires BEFORE the
|
|
583
|
+
* transparent reconnect attempt. Use it to decrement a transactional
|
|
584
|
+
* producer's leader-election counter or warn the operator.
|
|
585
|
+
* - When `autoRecoverFromFence` is off, the publisher surfaces the
|
|
586
|
+
* fenced result unchanged and the hook is still fired for visibility.
|
|
587
|
+
*/
|
|
588
|
+
onProducerFenced?(error: Error): void | Promise<void>;
|
|
364
589
|
}
|
|
365
590
|
/**
|
|
366
591
|
* Invoke a hook safely. Never throws back into the caller — logs the hook's
|
|
@@ -423,6 +648,21 @@ interface KafkaTracer {
|
|
|
423
648
|
* server.address/port).
|
|
424
649
|
*/
|
|
425
650
|
startPublishSpan(name: string, attributes: Record<string, SpanAttributeValue>): SpanLike;
|
|
651
|
+
/**
|
|
652
|
+
* OPTIONAL: inject the active trace context (W3C `traceparent` +
|
|
653
|
+
* `tracestate`) into a per-message header map. Called by the publisher
|
|
654
|
+
* AFTER the batch span is created and BEFORE the records hit the wire.
|
|
655
|
+
*
|
|
656
|
+
* Implementations typically wrap OpenTelemetry's `propagation.inject(...)`
|
|
657
|
+
* or your tracing SDK's equivalent. Mutate the `headers` object in
|
|
658
|
+
* place — the publisher allocates a fresh copy per message so this is
|
|
659
|
+
* safe and matches the propagation API of every major SDK.
|
|
660
|
+
*
|
|
661
|
+
* Tracers without distributed-context propagation (or that only care
|
|
662
|
+
* about local spans) may leave this off — consumers can still derive
|
|
663
|
+
* trace headers themselves by other means.
|
|
664
|
+
*/
|
|
665
|
+
inject?(span: SpanLike, headers: Record<string, string>): void;
|
|
426
666
|
}
|
|
427
667
|
/**
|
|
428
668
|
* No-op tracer. Used when the user does not configure one. Cheap allocation
|
|
@@ -457,6 +697,42 @@ interface KafkaPublisherOptions extends KafkaConnectionConfig, ProducerBehaviorC
|
|
|
457
697
|
* Use a thin adapter over your tracing SDK (see {@link KafkaTracer}).
|
|
458
698
|
*/
|
|
459
699
|
tracer?: KafkaTracer;
|
|
700
|
+
/**
|
|
701
|
+
* If set, `connect()` checks that every topic in this list exists on the
|
|
702
|
+
* cluster and throws a descriptive error if any are missing. Use this to
|
|
703
|
+
* fail-fast at startup instead of letting the first send-time error
|
|
704
|
+
* surprise you.
|
|
705
|
+
*
|
|
706
|
+
* Validation runs AFTER the producer connects but BEFORE `onConnect` hooks
|
|
707
|
+
* fire. Driver must implement `admin()` (the built-ins do).
|
|
708
|
+
*/
|
|
709
|
+
validateTopicsOnConnect?: string[];
|
|
710
|
+
/**
|
|
711
|
+
* Transparently recover from a producer-fence error. When set to `true`,
|
|
712
|
+
* a `publish()` call whose batch comes back with at least one
|
|
713
|
+
* `errorKind: "fenced"` result triggers ONE round of:
|
|
714
|
+
*
|
|
715
|
+
* 1. disconnect the driver
|
|
716
|
+
* 2. connect it again (re-running `initTransactions` for transactional producers)
|
|
717
|
+
* 3. re-send the same batch
|
|
718
|
+
*
|
|
719
|
+
* If the second send still produces a fenced result, the publisher gives
|
|
720
|
+
* up and surfaces the failures unchanged — at that point the fence is
|
|
721
|
+
* almost certainly caused by another instance taking the same
|
|
722
|
+
* `transactionalId`, and silently retrying again would mask the
|
|
723
|
+
* misconfiguration.
|
|
724
|
+
*
|
|
725
|
+
* Default `false` to preserve the previous "fenced → fatal" semantics.
|
|
726
|
+
* Turn it on when running a single producer instance against transient
|
|
727
|
+
* brokers (rolling restarts, network blips) where a fence is usually
|
|
728
|
+
* just a transient epoch mismatch.
|
|
729
|
+
*
|
|
730
|
+
* For MULTI-INSTANCE EOS, leave this OFF and use a callable
|
|
731
|
+
* `transactionalId` derived from per-instance context (pod name, k8s
|
|
732
|
+
* ordinal, AZ + replica index) so each instance has a stable, unique
|
|
733
|
+
* id — fences will then correctly stop the loser instance.
|
|
734
|
+
*/
|
|
735
|
+
autoRecoverFromFence?: boolean;
|
|
460
736
|
}
|
|
461
737
|
/**
|
|
462
738
|
* The Publisher the Relay talks to. Wraps a pluggable KafkaDriver and adds
|
|
@@ -469,8 +745,41 @@ declare class KafkaPublisher implements Publisher {
|
|
|
469
745
|
private readonly logger;
|
|
470
746
|
private readonly hooks;
|
|
471
747
|
private readonly tracer;
|
|
748
|
+
private readonly validateTopicsOnConnect;
|
|
749
|
+
private readonly autoRecoverFromFence;
|
|
750
|
+
private fenceRecovery;
|
|
472
751
|
constructor(opts: KafkaPublisherOptions);
|
|
473
752
|
connect(): Promise<void>;
|
|
753
|
+
/**
|
|
754
|
+
* Borrow a new admin client from the driver. The returned admin is
|
|
755
|
+
* connected and ready to use; the CALLER must `close()` it. Throws if the
|
|
756
|
+
* driver does not implement admin (custom driver lacking the capability).
|
|
757
|
+
*/
|
|
758
|
+
admin(): Promise<KafkaAdmin>;
|
|
759
|
+
/**
|
|
760
|
+
* Idempotently provision topics. Each spec creates the topic if absent;
|
|
761
|
+
* existing topics are skipped without error. If `growPartitions: true`
|
|
762
|
+
* (default false), topics whose current partition count is below the
|
|
763
|
+
* requested `numPartitions` are grown via `createPartitions`.
|
|
764
|
+
*
|
|
765
|
+
* Replication factor and config entries on EXISTING topics are NOT
|
|
766
|
+
* reconciled — Kafka does not provide a safe in-place alter for those
|
|
767
|
+
* (changing replication requires reassignment; configs use alterConfigs).
|
|
768
|
+
* Reach for the raw admin if you need that.
|
|
769
|
+
*/
|
|
770
|
+
ensureTopics(specs: TopicCreateSpec[], opts?: {
|
|
771
|
+
growPartitions?: boolean;
|
|
772
|
+
}): Promise<void>;
|
|
773
|
+
/**
|
|
774
|
+
* Borrow a fresh admin from the driver and connect it. Throws when the
|
|
775
|
+
* driver does not implement admin (custom drivers without that capability).
|
|
776
|
+
*/
|
|
777
|
+
private openDriverAdmin;
|
|
778
|
+
/**
|
|
779
|
+
* Open an admin, list topics, throw if any required topic is missing.
|
|
780
|
+
* Always closes the admin (success or failure).
|
|
781
|
+
*/
|
|
782
|
+
private assertTopicsExist;
|
|
474
783
|
disconnect(): Promise<void>;
|
|
475
784
|
publish(messages: PublishableMessage[]): Promise<PublishResult[]>;
|
|
476
785
|
/**
|
|
@@ -480,6 +789,43 @@ declare class KafkaPublisher implements Publisher {
|
|
|
480
789
|
publishToDlq(message: PublishableMessage, error: Error): Promise<void>;
|
|
481
790
|
/** Whether the configured driver provides atomic (EOS) batch sends. */
|
|
482
791
|
get transactional(): boolean;
|
|
792
|
+
/**
|
|
793
|
+
* Cheap reachability probe. Borrows a fresh admin client, calls
|
|
794
|
+
* `listTopics`, and returns timing + outcome. Useful as the body of a
|
|
795
|
+
* `/healthz` or `/readyz` endpoint — proves the broker is reachable
|
|
796
|
+
* AND that the configured credentials still authenticate against it,
|
|
797
|
+
* without writing a record.
|
|
798
|
+
*
|
|
799
|
+
* Does NOT exercise the producer's send path — a healthy admin
|
|
800
|
+
* connection doesn't guarantee `publish()` will succeed (a fenced
|
|
801
|
+
* transactional producer would still answer healthy here). Treat this
|
|
802
|
+
* as "broker reachable + auth still good", not "publisher is fully
|
|
803
|
+
* operational".
|
|
804
|
+
*
|
|
805
|
+
* Default timeout 5_000 ms — long enough to ride out a single broker
|
|
806
|
+
* leader election, short enough to fail a liveness probe meaningfully.
|
|
807
|
+
* Set `timeoutMs: 0` to disable the timer entirely.
|
|
808
|
+
*
|
|
809
|
+
* The driver must implement `admin()` (the built-ins do); custom
|
|
810
|
+
* drivers without admin get `{ ok: false, error: ... }` instead of
|
|
811
|
+
* the throw `publisher.admin()` would surface — health checks are
|
|
812
|
+
* not the place to crash.
|
|
813
|
+
*/
|
|
814
|
+
healthCheck(opts?: {
|
|
815
|
+
timeoutMs?: number;
|
|
816
|
+
}): Promise<HealthStatus>;
|
|
817
|
+
/**
|
|
818
|
+
* Disconnect + re-connect the driver and re-send the batch ONCE. Used
|
|
819
|
+
* by the fence-recovery path. Concurrent fence recoveries dedupe on a
|
|
820
|
+
* shared in-flight promise (`fenceRecovery`) so we don't tear the
|
|
821
|
+
* producer down while another batch is mid-restart.
|
|
822
|
+
*
|
|
823
|
+
* If the second send STILL reports any fenced records, those failures
|
|
824
|
+
* are returned unchanged — another instance has almost certainly taken
|
|
825
|
+
* the same `transactionalId` and silently retrying again would mask
|
|
826
|
+
* the misconfiguration.
|
|
827
|
+
*/
|
|
828
|
+
private recoverAndRetry;
|
|
483
829
|
/**
|
|
484
830
|
* Start a span for the batch following the OTel messaging conventions.
|
|
485
831
|
*
|
|
@@ -490,5 +836,20 @@ declare class KafkaPublisher implements Publisher {
|
|
|
490
836
|
*/
|
|
491
837
|
private startBatchSpan;
|
|
492
838
|
}
|
|
839
|
+
/**
|
|
840
|
+
* Outcome of a {@link KafkaPublisher.healthCheck} call. Shape is stable
|
|
841
|
+
* and small so consumers (HTTP /healthz, k8s probes, Datadog) can
|
|
842
|
+
* marshal it without a translation layer.
|
|
843
|
+
*/
|
|
844
|
+
interface HealthStatus {
|
|
845
|
+
/** True when the broker answered within the timeout window. */
|
|
846
|
+
ok: boolean;
|
|
847
|
+
/** Wall-clock milliseconds spent on the probe (admin connect + listTopics). */
|
|
848
|
+
latencyMs: number;
|
|
849
|
+
/** Epoch ms when the probe started — handy for log correlation. */
|
|
850
|
+
timestamp: number;
|
|
851
|
+
/** Present only when `ok === false`. The classified error, untouched. */
|
|
852
|
+
error?: Error;
|
|
853
|
+
}
|
|
493
854
|
|
|
494
|
-
export { type ConfluentClientConfig, ConfluentDriver, type ConfluentDriverOptions, type DriverKind, type KafkaConnectionConfig, type KafkaDriver, KafkaJsDriver, type KafkaJsDriverOptions, type KafkaJsPartitionerChoice, KafkaPublisher, type KafkaPublisherHooks, type KafkaPublisherOptions, type KafkaTracer, NoopKafkaTracer, type OauthBearerToken, type ProducerBehaviorConfig, type SaslConfig, type SaslOauthbearerConfig, type SaslPasswordConfig, type SpanAttributeValue, type SpanLike, type TlsConfig, _resetKafkajsWarnDedup, buildConfluentClientConfig, classifyConfluentError, classifyKafkajsError, safeHook };
|
|
855
|
+
export { type ConfluentClientConfig, ConfluentDriver, type ConfluentDriverOptions, type DriverKind, type HealthStatus, type KafkaAdmin, type KafkaConnectionConfig, type KafkaDriver, type KafkaDriverAdmin, KafkaJsDriver, type KafkaJsDriverOptions, type KafkaJsPartitionerChoice, KafkaPublisher, type KafkaPublisherHooks, type KafkaPublisherOptions, type KafkaTracer, type LibrdkafkaStats, NoopKafkaTracer, type OauthBearerToken, type PartitionGrowSpec, type PartitionMetadata, type ProducerBehaviorConfig, type SaslConfig, type SaslOauthbearerConfig, type SaslPasswordConfig, type SpanAttributeValue, type SpanLike, type TlsConfig, type TopicCreateSpec, type TopicMetadata, _resetKafkajsWarnDedup, buildConfluentClientConfig, classifyConfluentError, classifyKafkajsError, safeHook };
|