@drarzter/kafka-client 0.9.4 → 0.11.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.
Files changed (180) hide show
  1. package/README.md +693 -8
  2. package/dist/chunk-OR7TPAAE.mjs +4760 -0
  3. package/dist/chunk-OR7TPAAE.mjs.map +1 -0
  4. package/dist/chunk-PQVBRDNV.mjs +149 -0
  5. package/dist/chunk-PQVBRDNV.mjs.map +1 -0
  6. package/dist/cli/dlq.d.ts +119 -0
  7. package/dist/cli/dlq.d.ts.map +1 -0
  8. package/dist/cli/index.d.ts +3 -0
  9. package/dist/cli/index.d.ts.map +1 -0
  10. package/dist/{chunk-SM4FZKAZ.mjs → cli/index.js} +1073 -309
  11. package/dist/cli/index.js.map +1 -0
  12. package/dist/cli/index.mjs +356 -0
  13. package/dist/cli/index.mjs.map +1 -0
  14. package/dist/client/config/from-env.d.ts +188 -0
  15. package/dist/client/config/from-env.d.ts.map +1 -0
  16. package/dist/client/config/index.d.ts +2 -0
  17. package/dist/client/config/index.d.ts.map +1 -0
  18. package/dist/client/errors.d.ts +67 -0
  19. package/dist/client/errors.d.ts.map +1 -0
  20. package/dist/client/kafka.client/admin/ops.d.ts +114 -0
  21. package/dist/client/kafka.client/admin/ops.d.ts.map +1 -0
  22. package/dist/client/kafka.client/consumer/features/delayed.d.ts +24 -0
  23. package/dist/client/kafka.client/consumer/features/delayed.d.ts.map +1 -0
  24. package/dist/client/kafka.client/consumer/features/dlq-replay.d.ts +52 -0
  25. package/dist/client/kafka.client/consumer/features/dlq-replay.d.ts.map +1 -0
  26. package/dist/client/kafka.client/consumer/features/routed.d.ts +4 -0
  27. package/dist/client/kafka.client/consumer/features/routed.d.ts.map +1 -0
  28. package/dist/client/kafka.client/consumer/features/snapshot.d.ts +10 -0
  29. package/dist/client/kafka.client/consumer/features/snapshot.d.ts.map +1 -0
  30. package/dist/client/kafka.client/consumer/features/window.d.ts +5 -0
  31. package/dist/client/kafka.client/consumer/features/window.d.ts.map +1 -0
  32. package/dist/client/kafka.client/consumer/handler.d.ts +163 -0
  33. package/dist/client/kafka.client/consumer/handler.d.ts.map +1 -0
  34. package/dist/client/kafka.client/consumer/ops.d.ts +64 -0
  35. package/dist/client/kafka.client/consumer/ops.d.ts.map +1 -0
  36. package/dist/client/kafka.client/consumer/pipeline.d.ts +168 -0
  37. package/dist/client/kafka.client/consumer/pipeline.d.ts.map +1 -0
  38. package/dist/client/kafka.client/consumer/queue.d.ts +37 -0
  39. package/dist/client/kafka.client/consumer/queue.d.ts.map +1 -0
  40. package/dist/client/kafka.client/consumer/retry-topic.d.ts +68 -0
  41. package/dist/client/kafka.client/consumer/retry-topic.d.ts.map +1 -0
  42. package/dist/client/kafka.client/consumer/setup.d.ts +66 -0
  43. package/dist/client/kafka.client/consumer/setup.d.ts.map +1 -0
  44. package/dist/client/kafka.client/consumer/start.d.ts +7 -0
  45. package/dist/client/kafka.client/consumer/start.d.ts.map +1 -0
  46. package/dist/client/kafka.client/consumer/stop.d.ts +19 -0
  47. package/dist/client/kafka.client/consumer/stop.d.ts.map +1 -0
  48. package/dist/client/kafka.client/consumer/subscribe-retry.d.ts +4 -0
  49. package/dist/client/kafka.client/consumer/subscribe-retry.d.ts.map +1 -0
  50. package/dist/client/kafka.client/context.d.ts +75 -0
  51. package/dist/client/kafka.client/context.d.ts.map +1 -0
  52. package/dist/client/kafka.client/index.d.ts +155 -0
  53. package/dist/client/kafka.client/index.d.ts.map +1 -0
  54. package/dist/client/kafka.client/infra/circuit-breaker.manager.d.ts +61 -0
  55. package/dist/client/kafka.client/infra/circuit-breaker.manager.d.ts.map +1 -0
  56. package/dist/client/kafka.client/infra/dedup.store.d.ts +28 -0
  57. package/dist/client/kafka.client/infra/dedup.store.d.ts.map +1 -0
  58. package/dist/client/kafka.client/infra/inflight.tracker.d.ts +22 -0
  59. package/dist/client/kafka.client/infra/inflight.tracker.d.ts.map +1 -0
  60. package/dist/client/kafka.client/infra/metrics.manager.d.ts +67 -0
  61. package/dist/client/kafka.client/infra/metrics.manager.d.ts.map +1 -0
  62. package/dist/client/kafka.client/producer/lifecycle.d.ts +41 -0
  63. package/dist/client/kafka.client/producer/lifecycle.d.ts.map +1 -0
  64. package/dist/client/kafka.client/producer/ops.d.ts +79 -0
  65. package/dist/client/kafka.client/producer/ops.d.ts.map +1 -0
  66. package/dist/client/kafka.client/producer/send.d.ts +21 -0
  67. package/dist/client/kafka.client/producer/send.d.ts.map +1 -0
  68. package/dist/client/kafka.client/validate-options.d.ts +11 -0
  69. package/dist/client/kafka.client/validate-options.d.ts.map +1 -0
  70. package/dist/client/message/envelope.d.ts +105 -0
  71. package/dist/client/message/envelope.d.ts.map +1 -0
  72. package/dist/client/message/schema-registry.d.ts +124 -0
  73. package/dist/client/message/schema-registry.d.ts.map +1 -0
  74. package/dist/client/message/serde.d.ts +68 -0
  75. package/dist/client/message/serde.d.ts.map +1 -0
  76. package/dist/client/message/topic.d.ts +159 -0
  77. package/dist/client/message/topic.d.ts.map +1 -0
  78. package/dist/client/message/versioned-schema.d.ts +53 -0
  79. package/dist/client/message/versioned-schema.d.ts.map +1 -0
  80. package/dist/client/outbox/index.d.ts +4 -0
  81. package/dist/client/outbox/index.d.ts.map +1 -0
  82. package/dist/client/outbox/outbox.relay.d.ts +90 -0
  83. package/dist/client/outbox/outbox.relay.d.ts.map +1 -0
  84. package/dist/client/outbox/outbox.store.d.ts +42 -0
  85. package/dist/client/outbox/outbox.store.d.ts.map +1 -0
  86. package/dist/client/outbox/outbox.types.d.ts +144 -0
  87. package/dist/client/outbox/outbox.types.d.ts.map +1 -0
  88. package/dist/client/security/acl.d.ts +108 -0
  89. package/dist/client/security/acl.d.ts.map +1 -0
  90. package/dist/client/security/index.d.ts +5 -0
  91. package/dist/client/security/index.d.ts.map +1 -0
  92. package/dist/client/security/providers.d.ts +88 -0
  93. package/dist/client/security/providers.d.ts.map +1 -0
  94. package/dist/client/security/resolve-security.d.ts +19 -0
  95. package/dist/client/security/resolve-security.d.ts.map +1 -0
  96. package/dist/client/security/security.types.d.ts +76 -0
  97. package/dist/client/security/security.types.d.ts.map +1 -0
  98. package/dist/client/transport/confluent.transport.d.ts +32 -0
  99. package/dist/client/transport/confluent.transport.d.ts.map +1 -0
  100. package/dist/client/transport/transport.interface.d.ts +221 -0
  101. package/dist/client/transport/transport.interface.d.ts.map +1 -0
  102. package/dist/client/types/admin.interface.d.ts +174 -0
  103. package/dist/client/types/admin.interface.d.ts.map +1 -0
  104. package/dist/client/types/admin.types.d.ts +140 -0
  105. package/dist/client/types/admin.types.d.ts.map +1 -0
  106. package/dist/client/types/client.d.ts +21 -0
  107. package/dist/client/types/client.d.ts.map +1 -0
  108. package/dist/client/types/common.d.ts +84 -0
  109. package/dist/client/types/common.d.ts.map +1 -0
  110. package/dist/client/types/config.types.d.ts +167 -0
  111. package/dist/client/types/config.types.d.ts.map +1 -0
  112. package/dist/client/types/consumer.interface.d.ts +115 -0
  113. package/dist/client/types/consumer.interface.d.ts.map +1 -0
  114. package/dist/{consumer.types-fFCag3VJ.d.mts → client/types/consumer.types.d.ts} +62 -383
  115. package/dist/client/types/consumer.types.d.ts.map +1 -0
  116. package/dist/client/types/dedup.types.d.ts +50 -0
  117. package/dist/client/types/dedup.types.d.ts.map +1 -0
  118. package/dist/client/types/lifecycle.interface.d.ts +72 -0
  119. package/dist/client/types/lifecycle.interface.d.ts.map +1 -0
  120. package/dist/client/types/producer.interface.d.ts +52 -0
  121. package/dist/client/types/producer.interface.d.ts.map +1 -0
  122. package/dist/client/types/producer.types.d.ts +90 -0
  123. package/dist/client/types/producer.types.d.ts.map +1 -0
  124. package/dist/client/types.d.ts +8 -0
  125. package/dist/client/types.d.ts.map +1 -0
  126. package/dist/core.d.ts +13 -314
  127. package/dist/core.d.ts.map +1 -0
  128. package/dist/core.js +1466 -123
  129. package/dist/core.js.map +1 -1
  130. package/dist/core.mjs +45 -3
  131. package/dist/index.d.ts +7 -128
  132. package/dist/index.d.ts.map +1 -0
  133. package/dist/index.js +1483 -123
  134. package/dist/index.js.map +1 -1
  135. package/dist/index.mjs +62 -3
  136. package/dist/index.mjs.map +1 -1
  137. package/dist/nest/kafka.constants.d.ts +5 -0
  138. package/dist/nest/kafka.constants.d.ts.map +1 -0
  139. package/dist/nest/kafka.decorator.d.ts +49 -0
  140. package/dist/nest/kafka.decorator.d.ts.map +1 -0
  141. package/dist/nest/kafka.explorer.d.ts +17 -0
  142. package/dist/nest/kafka.explorer.d.ts.map +1 -0
  143. package/dist/nest/kafka.health.d.ts +7 -0
  144. package/dist/nest/kafka.health.d.ts.map +1 -0
  145. package/dist/nest/kafka.module.d.ts +61 -0
  146. package/dist/nest/kafka.module.d.ts.map +1 -0
  147. package/dist/otel.d.ts +83 -5
  148. package/dist/otel.d.ts.map +1 -0
  149. package/dist/otel.js +100 -6
  150. package/dist/otel.js.map +1 -1
  151. package/dist/otel.mjs +98 -5
  152. package/dist/otel.mjs.map +1 -1
  153. package/dist/serde.d.ts +157 -0
  154. package/dist/serde.d.ts.map +1 -0
  155. package/dist/serde.js +308 -0
  156. package/dist/serde.js.map +1 -0
  157. package/dist/serde.mjs +158 -0
  158. package/dist/serde.mjs.map +1 -0
  159. package/dist/testing/client.mock.d.ts +47 -0
  160. package/dist/testing/client.mock.d.ts.map +1 -0
  161. package/dist/testing/index.d.ts +4 -0
  162. package/dist/testing/index.d.ts.map +1 -0
  163. package/dist/testing/test.container.d.ts +63 -0
  164. package/dist/testing/test.container.d.ts.map +1 -0
  165. package/dist/{testing.d.mts → testing/transport.fake.d.ts} +7 -111
  166. package/dist/testing/transport.fake.d.ts.map +1 -0
  167. package/dist/testing.d.ts +2 -318
  168. package/dist/testing.d.ts.map +1 -0
  169. package/dist/testing.js +26 -0
  170. package/dist/testing.js.map +1 -1
  171. package/dist/testing.mjs +26 -0
  172. package/dist/testing.mjs.map +1 -1
  173. package/package.json +40 -8
  174. package/dist/chunk-SM4FZKAZ.mjs.map +0 -1
  175. package/dist/client-1irhGEu0.d.mts +0 -751
  176. package/dist/client-BpFjkHhr.d.ts +0 -751
  177. package/dist/consumer.types-fFCag3VJ.d.ts +0 -958
  178. package/dist/core.d.mts +0 -314
  179. package/dist/index.d.mts +0 -128
  180. package/dist/otel.d.mts +0 -27
@@ -0,0 +1,105 @@
1
+ import type { MessageHeaders } from "../types";
2
+ export declare const HEADER_EVENT_ID = "x-event-id";
3
+ export declare const HEADER_CORRELATION_ID = "x-correlation-id";
4
+ export declare const HEADER_TIMESTAMP = "x-timestamp";
5
+ export declare const HEADER_SCHEMA_VERSION = "x-schema-version";
6
+ export declare const HEADER_TRACEPARENT = "traceparent";
7
+ /** Monotonically increasing logical clock stamped by the producer for deduplication. */
8
+ export declare const HEADER_LAMPORT_CLOCK = "x-lamport-clock";
9
+ /** Absolute epoch-ms deadline before which a delayed message must not be delivered. */
10
+ export declare const HEADER_DELAYED_UNTIL = "x-delayed-until";
11
+ /** Target topic a delayed message is forwarded to once its deadline passes. */
12
+ export declare const HEADER_DELAYED_TARGET = "x-delayed-target";
13
+ /**
14
+ * Typed wrapper combining a parsed message payload with Kafka metadata
15
+ * and envelope headers.
16
+ *
17
+ * On **send**, the library auto-generates envelope headers
18
+ * (`x-event-id`, `x-correlation-id`, `x-timestamp`, `x-schema-version`).
19
+ *
20
+ * On **consume**, the library extracts those headers and assembles
21
+ * an `EventEnvelope` that is passed to the handler.
22
+ *
23
+ * @example
24
+ * ```ts
25
+ * await kafka.startConsumer(['orders'], async (envelope: EventEnvelope<Order>) => {
26
+ * console.log(envelope.payload.orderId); // typed payload
27
+ * console.log(envelope.correlationId); // auto-propagated
28
+ * console.log(envelope.eventId); // unique message ID
29
+ * });
30
+ * ```
31
+ */
32
+ export interface EventEnvelope<T> {
33
+ /** Deserialized + validated message body. */
34
+ payload: T;
35
+ /** Topic the message was produced to / consumed from. */
36
+ topic: string;
37
+ /** Kafka partition (consume-side only, `-1` on send). */
38
+ partition: number;
39
+ /** Kafka offset (consume-side only, empty string on send). */
40
+ offset: string;
41
+ /** ISO-8601 timestamp set by the producer. */
42
+ timestamp: string;
43
+ /** Unique ID for this event (UUID v4). */
44
+ eventId: string;
45
+ /** Correlation ID — auto-propagated via AsyncLocalStorage. */
46
+ correlationId: string;
47
+ /** Schema version of the payload. */
48
+ schemaVersion: number;
49
+ /** W3C Trace Context `traceparent` header (set by OTel instrumentation). */
50
+ traceparent?: string;
51
+ /** All decoded Kafka headers for extensibility. */
52
+ headers: MessageHeaders;
53
+ }
54
+ interface EnvelopeCtx {
55
+ correlationId: string;
56
+ traceparent?: string;
57
+ }
58
+ /**
59
+ * Read the current envelope context (correlationId / traceparent) from ALS.
60
+ * Returns `undefined` outside of a Kafka consumer handler.
61
+ * @example
62
+ * ```ts
63
+ * const ctx = getEnvelopeContext();
64
+ * if (ctx) console.log('correlationId:', ctx.correlationId);
65
+ * ```
66
+ */
67
+ export declare function getEnvelopeContext(): EnvelopeCtx | undefined;
68
+ /**
69
+ * Execute `fn` inside an envelope context so nested sends inherit correlationId.
70
+ * Automatically called by the consumer pipeline — use this in tests or manual flows.
71
+ * @example
72
+ * ```ts
73
+ * await runWithEnvelopeContext({ correlationId: 'abc-123' }, async () => {
74
+ * await kafka.sendMessage('orders.created', payload); // inherits correlationId
75
+ * });
76
+ * ```
77
+ */
78
+ export declare function runWithEnvelopeContext<R>(ctx: EnvelopeCtx, fn: () => R): R;
79
+ /** Options accepted by `buildEnvelopeHeaders`. */
80
+ export interface EnvelopeHeaderOptions {
81
+ correlationId?: string;
82
+ schemaVersion?: number;
83
+ eventId?: string;
84
+ headers?: MessageHeaders;
85
+ }
86
+ /**
87
+ * Generate envelope headers for the send path.
88
+ *
89
+ * Priority for `correlationId`:
90
+ * explicit option → ALS context → new UUID.
91
+ */
92
+ export declare function buildEnvelopeHeaders(options?: EnvelopeHeaderOptions): MessageHeaders;
93
+ /**
94
+ * Decode kafkajs headers (`Record<string, Buffer | string | undefined>`)
95
+ * into plain `Record<string, string>`.
96
+ */
97
+ export declare function decodeHeaders(raw: Record<string, Buffer | string | (Buffer | string)[] | undefined> | undefined): MessageHeaders;
98
+ /**
99
+ * Build an `EventEnvelope` from a consumed kafkajs message.
100
+ * Tolerates missing envelope headers — generates defaults so messages
101
+ * from non-envelope producers still work.
102
+ */
103
+ export declare function extractEnvelope<T>(payload: T, headers: MessageHeaders, topic: string, partition: number, offset: string): EventEnvelope<T>;
104
+ export {};
105
+ //# sourceMappingURL=envelope.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"envelope.d.ts","sourceRoot":"","sources":["../../../src/client/message/envelope.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,UAAU,CAAC;AAI/C,eAAO,MAAM,eAAe,eAAe,CAAC;AAC5C,eAAO,MAAM,qBAAqB,qBAAqB,CAAC;AACxD,eAAO,MAAM,gBAAgB,gBAAgB,CAAC;AAC9C,eAAO,MAAM,qBAAqB,qBAAqB,CAAC;AACxD,eAAO,MAAM,kBAAkB,gBAAgB,CAAC;AAChD,wFAAwF;AACxF,eAAO,MAAM,oBAAoB,oBAAoB,CAAC;AACtD,uFAAuF;AACvF,eAAO,MAAM,oBAAoB,oBAAoB,CAAC;AACtD,+EAA+E;AAC/E,eAAO,MAAM,qBAAqB,qBAAqB,CAAC;AAIxD;;;;;;;;;;;;;;;;;;GAkBG;AACH,MAAM,WAAW,aAAa,CAAC,CAAC;IAC9B,6CAA6C;IAC7C,OAAO,EAAE,CAAC,CAAC;IACX,yDAAyD;IACzD,KAAK,EAAE,MAAM,CAAC;IACd,yDAAyD;IACzD,SAAS,EAAE,MAAM,CAAC;IAClB,8DAA8D;IAC9D,MAAM,EAAE,MAAM,CAAC;IACf,8CAA8C;IAC9C,SAAS,EAAE,MAAM,CAAC;IAClB,0CAA0C;IAC1C,OAAO,EAAE,MAAM,CAAC;IAChB,8DAA8D;IAC9D,aAAa,EAAE,MAAM,CAAC;IACtB,qCAAqC;IACrC,aAAa,EAAE,MAAM,CAAC;IACtB,4EAA4E;IAC5E,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,mDAAmD;IACnD,OAAO,EAAE,cAAc,CAAC;CACzB;AAID,UAAU,WAAW;IACnB,aAAa,EAAE,MAAM,CAAC;IACtB,WAAW,CAAC,EAAE,MAAM,CAAC;CACtB;AAID;;;;;;;;GAQG;AACH,wBAAgB,kBAAkB,IAAI,WAAW,GAAG,SAAS,CAE5D;AAED;;;;;;;;;GASG;AACH,wBAAgB,sBAAsB,CAAC,CAAC,EAAE,GAAG,EAAE,WAAW,EAAE,EAAE,EAAE,MAAM,CAAC,GAAG,CAAC,CAE1E;AAID,kDAAkD;AAClD,MAAM,WAAW,qBAAqB;IACpC,aAAa,CAAC,EAAE,MAAM,CAAC;IACvB,aAAa,CAAC,EAAE,MAAM,CAAC;IACvB,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,OAAO,CAAC,EAAE,cAAc,CAAC;CAC1B;AAED;;;;;GAKG;AACH,wBAAgB,oBAAoB,CAClC,OAAO,GAAE,qBAA0B,GAClC,cAAc,CAuBhB;AAED;;;GAGG;AACH,wBAAgB,aAAa,CAC3B,GAAG,EACC,MAAM,CAAC,MAAM,EAAE,MAAM,GAAG,MAAM,GAAG,CAAC,MAAM,GAAG,MAAM,CAAC,EAAE,GAAG,SAAS,CAAC,GACjE,SAAS,GACZ,cAAc,CAehB;AAED;;;;GAIG;AACH,wBAAgB,eAAe,CAAC,CAAC,EAC/B,OAAO,EAAE,CAAC,EACV,OAAO,EAAE,cAAc,EACvB,KAAK,EAAE,MAAM,EACb,SAAS,EAAE,MAAM,EACjB,MAAM,EAAE,MAAM,GACb,aAAa,CAAC,CAAC,CAAC,CAalB"}
@@ -0,0 +1,124 @@
1
+ import type { SchemaLike } from "./topic";
2
+ /** A schema registered in a Confluent-compatible Schema Registry. */
3
+ export interface RegisteredSchema {
4
+ /** Globally unique schema id assigned by the registry. */
5
+ id: number;
6
+ /** Version of the schema within its subject. */
7
+ version: number;
8
+ /** The schema definition string (JSON Schema / Avro / Protobuf source). */
9
+ schema: string;
10
+ }
11
+ /** Options for `SchemaRegistryClient`. */
12
+ export interface SchemaRegistryClientOptions {
13
+ /** Registry base URL, e.g. `http://localhost:8081` or a Confluent Cloud SR endpoint. */
14
+ baseUrl: string;
15
+ /** HTTP Basic credentials (Confluent Cloud SR API key/secret). */
16
+ auth?: {
17
+ username: string;
18
+ password: string;
19
+ };
20
+ /** Cache TTL for subject lookups in ms. Default: `300_000` (5 min). */
21
+ cacheTtlMs?: number;
22
+ /** Injectable fetch implementation (tests). Default: global `fetch`. */
23
+ fetchFn?: typeof fetch;
24
+ }
25
+ /** Schema type accepted by Confluent-compatible registries. */
26
+ export type RegistrySchemaType = "JSON" | "AVRO" | "PROTOBUF";
27
+ /**
28
+ * Minimal, dependency-free client for the Confluent Schema Registry REST API
29
+ * (works with Confluent Platform/Cloud, Redpanda, Karapace, AWS Glue SR proxy).
30
+ *
31
+ * Scope: subject/version management, compatibility checks, and id->schema
32
+ * lookups. Used to keep locally-defined schemas in lockstep with a central
33
+ * registry, and as the backing lookup for the Avro/Protobuf serdes in
34
+ * `@drarzter/kafka-client/serde` (which handle the wire-format framing).
35
+ *
36
+ * @example
37
+ * ```ts
38
+ * const registry = new SchemaRegistryClient({ baseUrl: 'http://localhost:8081' });
39
+ * const { id } = await registry.registerSchema(
40
+ * 'order.created-value',
41
+ * JSON.stringify(orderJsonSchema),
42
+ * 'JSON',
43
+ * );
44
+ * ```
45
+ */
46
+ export declare class SchemaRegistryClient {
47
+ private readonly options;
48
+ private readonly fetchFn;
49
+ private readonly cacheTtlMs;
50
+ private readonly latestCache;
51
+ /**
52
+ * `id → schema` cache. Schema ids are immutable in a Confluent-compatible
53
+ * registry (a given id always maps to the same schema string), so entries
54
+ * are cached for the lifetime of the client with no TTL.
55
+ */
56
+ private readonly byIdCache;
57
+ constructor(options: SchemaRegistryClientOptions);
58
+ private headers;
59
+ private request;
60
+ /** Fetch the latest schema registered under `subject`. Cached for `cacheTtlMs`. */
61
+ getLatestSchema(subject: string): Promise<RegisteredSchema>;
62
+ /**
63
+ * Fetch a schema by its globally unique registry id (`GET /schemas/ids/{id}`).
64
+ *
65
+ * Used by the Avro/Protobuf serdes on the deserialize path: the writer schema
66
+ * id is read from the Confluent wire-format prefix, then resolved here. Results
67
+ * are cached forever (schema ids are immutable), so a given id triggers exactly
68
+ * one registry round-trip regardless of how many messages reference it.
69
+ */
70
+ getSchemaById(id: number): Promise<{
71
+ id: number;
72
+ schema: string;
73
+ schemaType?: string;
74
+ }>;
75
+ /** Fetch a specific schema version of a subject. */
76
+ getSchemaVersion(subject: string, version: number): Promise<RegisteredSchema>;
77
+ /**
78
+ * Register a schema under `subject` (idempotent — re-registering the same
79
+ * schema returns the existing id). Returns the registry-assigned schema id.
80
+ */
81
+ registerSchema(subject: string, schema: string, schemaType?: RegistrySchemaType): Promise<{
82
+ id: number;
83
+ }>;
84
+ /**
85
+ * Test `schema` against the subject's compatibility policy without registering.
86
+ * Returns `true` when the registry reports the schema as compatible.
87
+ */
88
+ checkCompatibility(subject: string, schema: string, schemaType?: RegistrySchemaType): Promise<boolean>;
89
+ }
90
+ /** Options for `registrySchema()`. */
91
+ export interface RegistrySchemaOptions<T> {
92
+ /**
93
+ * Local structural validator (Zod/Valibot/…) applied to every message.
94
+ * The registry governs schema *evolution*; this governs runtime *shape*.
95
+ */
96
+ validator?: SchemaLike<T>;
97
+ /**
98
+ * When `true` (default), the message's `x-schema-version` must not be newer
99
+ * than the latest version registered for the subject — a producer publishing
100
+ * an unregistered version fails loudly instead of drifting silently.
101
+ */
102
+ enforceVersion?: boolean;
103
+ }
104
+ /**
105
+ * Bridge a Schema Registry subject to this library's `SchemaLike` seam.
106
+ *
107
+ * On each `parse` the adapter resolves the subject's latest registered version
108
+ * (cached), optionally verifies the message's schema version does not exceed
109
+ * it, and delegates structural validation to the provided local validator.
110
+ * Attach the result to a `TopicDescriptor` like any other schema:
111
+ *
112
+ * @example
113
+ * ```ts
114
+ * const registry = new SchemaRegistryClient({ baseUrl: 'http://localhost:8081' });
115
+ *
116
+ * const OrderCreated = topic('order.created').schema(
117
+ * registrySchema(registry, 'order.created-value', {
118
+ * validator: z.object({ orderId: z.string() }),
119
+ * }),
120
+ * );
121
+ * ```
122
+ */
123
+ export declare function registrySchema<T = any>(client: SchemaRegistryClient, subject: string, options?: RegistrySchemaOptions<T>): SchemaLike<T>;
124
+ //# sourceMappingURL=schema-registry.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"schema-registry.d.ts","sourceRoot":"","sources":["../../../src/client/message/schema-registry.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,UAAU,EAAsB,MAAM,SAAS,CAAC;AAE9D,qEAAqE;AACrE,MAAM,WAAW,gBAAgB;IAC/B,0DAA0D;IAC1D,EAAE,EAAE,MAAM,CAAC;IACX,gDAAgD;IAChD,OAAO,EAAE,MAAM,CAAC;IAChB,2EAA2E;IAC3E,MAAM,EAAE,MAAM,CAAC;CAChB;AAED,0CAA0C;AAC1C,MAAM,WAAW,2BAA2B;IAC1C,wFAAwF;IACxF,OAAO,EAAE,MAAM,CAAC;IAChB,kEAAkE;IAClE,IAAI,CAAC,EAAE;QAAE,QAAQ,EAAE,MAAM,CAAC;QAAC,QAAQ,EAAE,MAAM,CAAA;KAAE,CAAC;IAC9C,uEAAuE;IACvE,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,wEAAwE;IACxE,OAAO,CAAC,EAAE,OAAO,KAAK,CAAC;CACxB;AAED,+DAA+D;AAC/D,MAAM,MAAM,kBAAkB,GAAG,MAAM,GAAG,MAAM,GAAG,UAAU,CAAC;AAE9D;;;;;;;;;;;;;;;;;;GAkBG;AACH,qBAAa,oBAAoB;IAiBnB,OAAO,CAAC,QAAQ,CAAC,OAAO;IAhBpC,OAAO,CAAC,QAAQ,CAAC,OAAO,CAAe;IACvC,OAAO,CAAC,QAAQ,CAAC,UAAU,CAAS;IACpC,OAAO,CAAC,QAAQ,CAAC,WAAW,CAGxB;IACJ;;;;OAIG;IACH,OAAO,CAAC,QAAQ,CAAC,SAAS,CAGtB;gBAEyB,OAAO,EAAE,2BAA2B;IAQjE,OAAO,CAAC,OAAO;YAYD,OAAO;IAoBrB,mFAAmF;IAC7E,eAAe,CAAC,OAAO,EAAE,MAAM,GAAG,OAAO,CAAC,gBAAgB,CAAC;IAoBjE;;;;;;;OAOG;IACG,aAAa,CACjB,EAAE,EAAE,MAAM,GACT,OAAO,CAAC;QAAE,EAAE,EAAE,MAAM,CAAC;QAAC,MAAM,EAAE,MAAM,CAAC;QAAC,UAAU,CAAC,EAAE,MAAM,CAAA;KAAE,CAAC;IAY/D,oDAAoD;IAC9C,gBAAgB,CACpB,OAAO,EAAE,MAAM,EACf,OAAO,EAAE,MAAM,GACd,OAAO,CAAC,gBAAgB,CAAC;IAY5B;;;OAGG;IACG,cAAc,CAClB,OAAO,EAAE,MAAM,EACf,MAAM,EAAE,MAAM,EACd,UAAU,GAAE,kBAA2B,GACtC,OAAO,CAAC;QAAE,EAAE,EAAE,MAAM,CAAA;KAAE,CAAC;IAS1B;;;OAGG;IACG,kBAAkB,CACtB,OAAO,EAAE,MAAM,EACf,MAAM,EAAE,MAAM,EACd,UAAU,GAAE,kBAA2B,GACtC,OAAO,CAAC,OAAO,CAAC;CAQpB;AAED,sCAAsC;AACtC,MAAM,WAAW,qBAAqB,CAAC,CAAC;IACtC;;;OAGG;IACH,SAAS,CAAC,EAAE,UAAU,CAAC,CAAC,CAAC,CAAC;IAC1B;;;;OAIG;IACH,cAAc,CAAC,EAAE,OAAO,CAAC;CAC1B;AAED;;;;;;;;;;;;;;;;;;GAkBG;AACH,wBAAgB,cAAc,CAAC,CAAC,GAAG,GAAG,EACpC,MAAM,EAAE,oBAAoB,EAC5B,OAAO,EAAE,MAAM,EACf,OAAO,CAAC,EAAE,qBAAqB,CAAC,CAAC,CAAC,GACjC,UAAU,CAAC,CAAC,CAAC,CAkBf"}
@@ -0,0 +1,68 @@
1
+ import type { MessageHeaders } from "../types";
2
+ /**
3
+ * Context passed to `MessageSerde.serialize` / `deserialize`.
4
+ *
5
+ * Carries the topic name, decoded message headers, and which side of the
6
+ * record is being (de)serialized. A Confluent Schema Registry serde uses
7
+ * `topic` + `isKey` to derive the subject name (`<topic>-value` / `<topic>-key`)
8
+ * and reads the schema id from the header/magic-byte prefix on `data`.
9
+ */
10
+ export interface SerdeContext {
11
+ /** Topic the message is produced to / consumed from. */
12
+ topic: string;
13
+ /** Decoded message headers (envelope headers included). */
14
+ headers: MessageHeaders;
15
+ /**
16
+ * Which side of the Kafka record this call is (de)serializing.
17
+ * `false` / omitted → the value (default); `true` → the key.
18
+ * Used by schema-registry serdes to pick the `value` vs `key` subject.
19
+ */
20
+ isKey?: boolean;
21
+ }
22
+ /**
23
+ * Pluggable serialization layer for message payloads.
24
+ *
25
+ * A `MessageSerde` converts a validated payload object to the wire form
26
+ * (`Buffer` or `string`) on produce, and back to an object on consume.
27
+ * The default is {@link JsonSerde}, which reproduces the client's historical
28
+ * `JSON.stringify` / `JSON.parse` behaviour exactly.
29
+ *
30
+ * Serde only touches the message VALUE. Envelope metadata
31
+ * (`x-event-id`, `x-correlation-id`, `x-lamport-clock`, `traceparent`, …)
32
+ * always travels in headers and is never serialized through this layer.
33
+ *
34
+ * Set a client-wide serde via `KafkaClientOptions.serde`, or a per-topic
35
+ * override via `topic(...).serde(mySerde)` — the per-topic serde wins for
36
+ * that topic.
37
+ *
38
+ * @example
39
+ * ```ts
40
+ * const kafka = new KafkaClient(id, group, brokers, { serde: new JsonSerde() });
41
+ * ```
42
+ */
43
+ export interface MessageSerde {
44
+ /**
45
+ * Serialize a validated payload object to wire bytes (`Buffer`) or a
46
+ * `string`. Validation has already run on `value` before this is called.
47
+ */
48
+ serialize(value: unknown, ctx: SerdeContext): Buffer | string | Promise<Buffer | string>;
49
+ /**
50
+ * Deserialize raw wire bytes into a payload object. Schema validation
51
+ * (if any) runs on the returned object afterwards.
52
+ */
53
+ deserialize(data: Buffer, ctx: SerdeContext): unknown | Promise<unknown>;
54
+ }
55
+ /**
56
+ * Default {@link MessageSerde}: JSON via `JSON.stringify` / `JSON.parse`.
57
+ *
58
+ * Byte-for-byte identical to the client's historical serialization, so it is
59
+ * a zero-behaviour-change default. Produces a UTF-8 `string` on serialize and
60
+ * decodes UTF-8 bytes on deserialize.
61
+ */
62
+ export declare class JsonSerde implements MessageSerde {
63
+ /** JSON-stringify the validated payload. Returns a UTF-8 string. */
64
+ serialize(value: unknown): string;
65
+ /** JSON-parse UTF-8 wire bytes into an object. */
66
+ deserialize(data: Buffer): unknown;
67
+ }
68
+ //# sourceMappingURL=serde.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"serde.d.ts","sourceRoot":"","sources":["../../../src/client/message/serde.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,UAAU,CAAC;AAE/C;;;;;;;GAOG;AACH,MAAM,WAAW,YAAY;IAC3B,wDAAwD;IACxD,KAAK,EAAE,MAAM,CAAC;IACd,2DAA2D;IAC3D,OAAO,EAAE,cAAc,CAAC;IACxB;;;;OAIG;IACH,KAAK,CAAC,EAAE,OAAO,CAAC;CACjB;AAED;;;;;;;;;;;;;;;;;;;;GAoBG;AACH,MAAM,WAAW,YAAY;IAC3B;;;OAGG;IACH,SAAS,CACP,KAAK,EAAE,OAAO,EACd,GAAG,EAAE,YAAY,GAChB,MAAM,GAAG,MAAM,GAAG,OAAO,CAAC,MAAM,GAAG,MAAM,CAAC,CAAC;IAC9C;;;OAGG;IACH,WAAW,CAAC,IAAI,EAAE,MAAM,EAAE,GAAG,EAAE,YAAY,GAAG,OAAO,GAAG,OAAO,CAAC,OAAO,CAAC,CAAC;CAC1E;AAED;;;;;;GAMG;AACH,qBAAa,SAAU,YAAW,YAAY;IAC5C,oEAAoE;IACpE,SAAS,CAAC,KAAK,EAAE,OAAO,GAAG,MAAM;IAIjC,kDAAkD;IAClD,WAAW,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO;CAGnC"}
@@ -0,0 +1,159 @@
1
+ import type { MessageHeaders } from "../types";
2
+ import type { MessageSerde } from "./serde";
3
+ /**
4
+ * Context passed as the second argument to `SchemaLike.parse()`.
5
+ * Enables schema-registry adapters, version-aware migration, and
6
+ * header-driven parsing without coupling validators to Kafka internals.
7
+ *
8
+ * All fields are optional-friendly — validators that don't need the context
9
+ * can simply ignore the second argument.
10
+ */
11
+ export interface SchemaParseContext {
12
+ /** Topic the message was produced to / consumed from. */
13
+ topic: string;
14
+ /** Decoded message headers (envelope headers included). */
15
+ headers: MessageHeaders;
16
+ /** Value of the `x-schema-version` header, defaults to `1`. */
17
+ version: number;
18
+ }
19
+ /**
20
+ * Any validation library with a `.parse()` method.
21
+ * Works with Zod, Valibot, ArkType, or any custom validator.
22
+ *
23
+ * The optional `ctx` argument carries topic/header/version metadata so
24
+ * validators can perform schema-registry lookups or version-aware migrations.
25
+ * Existing validators that only use the first argument continue to work
26
+ * unchanged — the second argument is silently ignored.
27
+ *
28
+ * @example
29
+ * ```ts
30
+ * import { z } from 'zod';
31
+ * const schema: SchemaLike<{ id: string }> = z.object({ id: z.string() });
32
+ *
33
+ * // Context-aware validator:
34
+ * const schema: SchemaLike<MyType> = {
35
+ * parse(data, ctx) {
36
+ * const version = ctx?.version ?? 1;
37
+ * return version >= 2 ? migrateV1toV2(data) : validateV1(data);
38
+ * }
39
+ * };
40
+ * ```
41
+ */
42
+ export interface SchemaLike<T = any> {
43
+ parse(data: unknown, ctx?: SchemaParseContext): T | Promise<T>;
44
+ }
45
+ /** Infer the output type from a SchemaLike. */
46
+ export type InferSchema<S extends SchemaLike> = S extends SchemaLike<infer T> ? T : never;
47
+ /**
48
+ * A typed topic descriptor that pairs a topic name with its message type.
49
+ * Created via the `topic()` factory function.
50
+ *
51
+ * @typeParam N - The literal topic name string.
52
+ * @typeParam M - The message payload type for this topic.
53
+ */
54
+ export interface TopicDescriptor<N extends string = string, M extends Record<string, any> = Record<string, any>> {
55
+ readonly __topic: N;
56
+ /** @internal Phantom type — never has a real value at runtime. */
57
+ readonly __type: M;
58
+ /** Runtime schema validator. Present only when created via `topic().schema()`. */
59
+ readonly __schema?: SchemaLike<M>;
60
+ /**
61
+ * Per-topic serialization override. Present only when created via `.serde()`.
62
+ * When set, this serde is used for produce/consume on this topic instead of
63
+ * the client-wide `KafkaClientOptions.serde`.
64
+ */
65
+ readonly __serde?: MessageSerde;
66
+ /**
67
+ * Partition-key extractor. Present only when created via `.key()`.
68
+ * Applied on every send through this descriptor unless an explicit
69
+ * `key` is passed in `SendOptions` / the batch item.
70
+ *
71
+ * Declared with method syntax (not a function property) so `M` stays
72
+ * bivariant — otherwise narrow descriptors would stop being assignable
73
+ * to `TopicDescriptor<string, Record<string, any>>` parameters.
74
+ */
75
+ __key?(message: M): string;
76
+ }
77
+ /**
78
+ * A `TopicDescriptor` that can still be extended with a `.key()` extractor or
79
+ * a `.serde()` override. Returned by `topic().type()` and `topic().schema()` —
80
+ * usable directly as a descriptor, or chained further to declare partition
81
+ * affinity and/or a custom serializer.
82
+ */
83
+ export type KeyableTopicDescriptor<N extends string, M extends Record<string, any>> = TopicDescriptor<N, M> & {
84
+ /**
85
+ * Declare a partition-key extractor for this topic. The extractor runs on
86
+ * the ORIGINAL (pre-validation) payload of every message sent through this
87
+ * descriptor, so messages with the same logical key always land on the same
88
+ * partition without passing `key` at each call site.
89
+ *
90
+ * An explicit `SendOptions.key` / batch-item `key` always wins.
91
+ *
92
+ * @example
93
+ * ```ts
94
+ * const OrderCreated = topic('order.created')
95
+ * .type<{ orderId: string; amount: number }>()
96
+ * .key((m) => m.orderId);
97
+ *
98
+ * await kafka.sendMessage(OrderCreated, { orderId: '42', amount: 100 });
99
+ * // → produced with key '42'
100
+ * ```
101
+ */
102
+ key(extractor: (message: M) => string): KeyableTopicDescriptor<N, M>;
103
+ /**
104
+ * Declare a per-topic serialization override. The given {@link MessageSerde}
105
+ * is used for produce/consume on this topic instead of the client-wide
106
+ * `KafkaClientOptions.serde`. Chainable with `.key()`.
107
+ *
108
+ * @example
109
+ * ```ts
110
+ * const OrderCreated = topic('order.created')
111
+ * .schema(OrderSchema)
112
+ * .serde(new AvroSerde(OrderAvroSchema));
113
+ * ```
114
+ */
115
+ serde(serde: MessageSerde): KeyableTopicDescriptor<N, M>;
116
+ };
117
+ /**
118
+ * Define a typed topic descriptor.
119
+ *
120
+ * @example
121
+ * ```ts
122
+ * // Without schema — explicit type via .type<T>():
123
+ * const OrderCreated = topic('order.created').type<{ orderId: string; amount: number }>();
124
+ *
125
+ * // With schema — type inferred from schema:
126
+ * const OrderCreated = topic('order.created').schema(z.object({
127
+ * orderId: z.string(),
128
+ * amount: z.number(),
129
+ * }));
130
+ *
131
+ * // Use with KafkaClient:
132
+ * await kafka.sendMessage(OrderCreated, { orderId: '123', amount: 100 });
133
+ *
134
+ * // Use with @SubscribeTo:
135
+ * @SubscribeTo(OrderCreated)
136
+ * async handleOrder(msg) { ... }
137
+ * ```
138
+ */
139
+ export declare function topic<N extends string>(name: N): {
140
+ /** Provide an explicit message type without a runtime schema. */
141
+ type: <M extends Record<string, any>>() => KeyableTopicDescriptor<N, M>;
142
+ schema: <S extends SchemaLike<Record<string, any>>>(schema: S) => KeyableTopicDescriptor<N, InferSchema<S>>;
143
+ };
144
+ /**
145
+ * Build a topic-message map type from a union of TopicDescriptors.
146
+ *
147
+ * @example
148
+ * ```ts
149
+ * const OrderCreated = topic('order.created').type<{ orderId: string }>();
150
+ * const OrderCompleted = topic('order.completed').type<{ completedAt: string }>();
151
+ *
152
+ * type MyTopics = TopicsFrom<typeof OrderCreated | typeof OrderCompleted>;
153
+ * // { 'order.created': { orderId: string }; 'order.completed': { completedAt: string } }
154
+ * ```
155
+ */
156
+ export type TopicsFrom<D extends TopicDescriptor<any, any>> = {
157
+ [K in D as K["__topic"]]: K["__type"];
158
+ };
159
+ //# sourceMappingURL=topic.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"topic.d.ts","sourceRoot":"","sources":["../../../src/client/message/topic.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,UAAU,CAAC;AAC/C,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,SAAS,CAAC;AAE5C;;;;;;;GAOG;AACH,MAAM,WAAW,kBAAkB;IACjC,yDAAyD;IACzD,KAAK,EAAE,MAAM,CAAC;IACd,2DAA2D;IAC3D,OAAO,EAAE,cAAc,CAAC;IACxB,+DAA+D;IAC/D,OAAO,EAAE,MAAM,CAAC;CACjB;AAED;;;;;;;;;;;;;;;;;;;;;;GAsBG;AACH,MAAM,WAAW,UAAU,CAAC,CAAC,GAAG,GAAG;IACjC,KAAK,CAAC,IAAI,EAAE,OAAO,EAAE,GAAG,CAAC,EAAE,kBAAkB,GAAG,CAAC,GAAG,OAAO,CAAC,CAAC,CAAC,CAAC;CAChE;AAED,+CAA+C;AAC/C,MAAM,MAAM,WAAW,CAAC,CAAC,SAAS,UAAU,IAC1C,CAAC,SAAS,UAAU,CAAC,MAAM,CAAC,CAAC,GAAG,CAAC,GAAG,KAAK,CAAC;AAE5C;;;;;;GAMG;AACH,MAAM,WAAW,eAAe,CAC9B,CAAC,SAAS,MAAM,GAAG,MAAM,EACzB,CAAC,SAAS,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,GAAG,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC;IAEnD,QAAQ,CAAC,OAAO,EAAE,CAAC,CAAC;IACpB,kEAAkE;IAClE,QAAQ,CAAC,MAAM,EAAE,CAAC,CAAC;IACnB,kFAAkF;IAClF,QAAQ,CAAC,QAAQ,CAAC,EAAE,UAAU,CAAC,CAAC,CAAC,CAAC;IAClC;;;;OAIG;IACH,QAAQ,CAAC,OAAO,CAAC,EAAE,YAAY,CAAC;IAChC;;;;;;;;OAQG;IACH,KAAK,CAAC,CAAC,OAAO,EAAE,CAAC,GAAG,MAAM,CAAC;CAC5B;AAED;;;;;GAKG;AACH,MAAM,MAAM,sBAAsB,CAChC,CAAC,SAAS,MAAM,EAChB,CAAC,SAAS,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,IAC3B,eAAe,CAAC,CAAC,EAAE,CAAC,CAAC,GAAG;IAC1B;;;;;;;;;;;;;;;;;OAiBG;IACH,GAAG,CAAC,SAAS,EAAE,CAAC,OAAO,EAAE,CAAC,KAAK,MAAM,GAAG,sBAAsB,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC;IACrE;;;;;;;;;;;OAWG;IACH,KAAK,CAAC,KAAK,EAAE,YAAY,GAAG,sBAAsB,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC;CAC1D,CAAC;AAEF;;;;;;;;;;;;;;;;;;;;;GAqBG;AACH,wBAAgB,KAAK,CAAC,CAAC,SAAS,MAAM,EAAE,IAAI,EAAE,CAAC;IAE3C,iEAAiE;WAC1D,CAAC,SAAS,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,OAAK,sBAAsB,CAAC,CAAC,EAAE,CAAC,CAAC;aAM5D,CAAC,SAAS,UAAU,CAAC,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,CAAC,UACxC,CAAC,KACR,sBAAsB,CAAC,CAAC,EAAE,WAAW,CAAC,CAAC,CAAC,CAAC;EAO/C;AAeD;;;;;;;;;;;GAWG;AACH,MAAM,MAAM,UAAU,CAAC,CAAC,SAAS,eAAe,CAAC,GAAG,EAAE,GAAG,CAAC,IAAI;KAC3D,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC,SAAS,CAAC,GAAG,CAAC,CAAC,QAAQ,CAAC;CACtC,CAAC"}
@@ -0,0 +1,53 @@
1
+ import type { SchemaLike } from "./topic";
2
+ /**
3
+ * Options for `versionedSchema()`.
4
+ * @typeParam T - The (latest) output type produced after parsing and migration.
5
+ */
6
+ export interface VersionedSchemaOptions<T> {
7
+ /**
8
+ * Called after a message parsed with a non-latest schema version.
9
+ * Receives the parsed data, the version it was parsed with, and the latest
10
+ * registered version. Must return the data in its latest shape.
11
+ *
12
+ * When omitted, older versions are returned as parsed — callers must handle
13
+ * shape differences themselves.
14
+ */
15
+ migrate?: (data: any, fromVersion: number, latestVersion: number) => T | Promise<T>;
16
+ }
17
+ /**
18
+ * Compose per-version validators into a single `SchemaLike` that dispatches on
19
+ * the message's `x-schema-version` header (via `SchemaParseContext.version`).
20
+ *
21
+ * - Consume path: the version comes from the `x-schema-version` header
22
+ * (defaults to `1` when absent).
23
+ * - Send path: the version comes from `SendOptions.schemaVersion`
24
+ * (defaults to `1`).
25
+ * - No parse context at all (direct `.parse(data)` call): the latest
26
+ * registered version is assumed.
27
+ *
28
+ * Throws when a message carries a version with no registered schema — a
29
+ * misconfigured producer fails loudly instead of validating against the
30
+ * wrong shape.
31
+ *
32
+ * @typeParam T - The latest message shape (post-migration).
33
+ * @param versions Map of version number → validator for that version.
34
+ * @param options Optional migration hook to upgrade old shapes to the latest.
35
+ *
36
+ * @example
37
+ * ```ts
38
+ * const OrderSchema = versionedSchema<{ orderId: string; amountMinor: number }>(
39
+ * {
40
+ * 1: z.object({ orderId: z.string(), amount: z.number() }),
41
+ * 2: z.object({ orderId: z.string(), amountMinor: z.number().int() }),
42
+ * },
43
+ * {
44
+ * migrate: (data, from) =>
45
+ * from === 1 ? { orderId: data.orderId, amountMinor: Math.round(data.amount * 100) } : data,
46
+ * },
47
+ * );
48
+ *
49
+ * const OrderCreated = topic('order.created').schema(OrderSchema);
50
+ * ```
51
+ */
52
+ export declare function versionedSchema<T = any>(versions: Record<number, SchemaLike<any>>, options?: VersionedSchemaOptions<T>): SchemaLike<T>;
53
+ //# sourceMappingURL=versioned-schema.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"versioned-schema.d.ts","sourceRoot":"","sources":["../../../src/client/message/versioned-schema.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,UAAU,EAAsB,MAAM,SAAS,CAAC;AAE9D;;;GAGG;AACH,MAAM,WAAW,sBAAsB,CAAC,CAAC;IACvC;;;;;;;OAOG;IACH,OAAO,CAAC,EAAE,CACR,IAAI,EAAE,GAAG,EACT,WAAW,EAAE,MAAM,EACnB,aAAa,EAAE,MAAM,KAClB,CAAC,GAAG,OAAO,CAAC,CAAC,CAAC,CAAC;CACrB;AAED;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAkCG;AACH,wBAAgB,eAAe,CAAC,CAAC,GAAG,GAAG,EACrC,QAAQ,EAAE,MAAM,CAAC,MAAM,EAAE,UAAU,CAAC,GAAG,CAAC,CAAC,EACzC,OAAO,CAAC,EAAE,sBAAsB,CAAC,CAAC,CAAC,GAClC,UAAU,CAAC,CAAC,CAAC,CA6Bf"}
@@ -0,0 +1,4 @@
1
+ export * from "./outbox.types";
2
+ export * from "./outbox.store";
3
+ export * from "./outbox.relay";
4
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/client/outbox/index.ts"],"names":[],"mappings":"AAAA,cAAc,gBAAgB,CAAC;AAC/B,cAAc,gBAAgB,CAAC;AAC/B,cAAc,gBAAgB,CAAC"}
@@ -0,0 +1,90 @@
1
+ import type { TopicMapConstraint } from "../types";
2
+ import type { OutboxProducer, OutboxRelayHandle, OutboxRelayOptions, OutboxStore } from "./outbox.types";
3
+ /**
4
+ * Start a transactional-outbox relay.
5
+ *
6
+ * The outbox pattern decouples "write my business state" from "publish an event"
7
+ * so the two can never diverge: application code writes an event row into an
8
+ * outbox table **in the same DB transaction** as its business writes; this relay
9
+ * polls that table and publishes the rows to Kafka, marking them published only
10
+ * after Kafka has acked them.
11
+ *
12
+ * ## Poll loop
13
+ * Every `pollIntervalMs` the relay calls {@link OutboxStore.fetchUnpublished}
14
+ * with `batchSize`. An empty result means there is nothing to do — it waits for
15
+ * the next tick. A non-empty batch is published **entirely inside one Kafka
16
+ * transaction** (`kafka.transaction`, one `tx.send` per message), and only after
17
+ * that transaction commits does it call {@link OutboxStore.markPublished} with
18
+ * the row ids.
19
+ *
20
+ * ## Delivery guarantee: at-least-once
21
+ * If the process crashes **after** the Kafka transaction commits but **before**
22
+ * `markPublished` runs, those rows are still unpublished in the store and will be
23
+ * re-published on the next poll. This produces **duplicates**, but each duplicate
24
+ * carries the **same** `x-event-id` (when {@link OutboxMessage.eventId} is set on
25
+ * the row), so consumers can deduplicate — either via this library's Lamport-clock
26
+ * deduplication or an application-level idempotency check keyed on the event id.
27
+ * Persist a stable `eventId` on each outbox row to make this work.
28
+ *
29
+ * ## Failure handling
30
+ * The loop never dies and never leaks an unhandled rejection:
31
+ * - If the publishing transaction throws, `onError` fires, the batch is **not**
32
+ * marked published, and it is retried on the next tick.
33
+ * - If the store throws (either `fetchUnpublished` or `markPublished`), same
34
+ * behaviour: `onError`, no marking, retried next tick.
35
+ *
36
+ * Note the one asymmetry: if `markPublished` throws *after* a successful Kafka
37
+ * commit, the batch is re-published next tick — an intentional consequence of
38
+ * the at-least-once guarantee above.
39
+ *
40
+ * ## Concurrency
41
+ * Iterations never overlap. If an iteration runs longer than `pollIntervalMs`,
42
+ * intervening ticks are skipped (a guard flag) rather than stacked.
43
+ *
44
+ * ## Shutdown
45
+ * `handle.stop()` clears the timer and awaits any in-flight iteration before
46
+ * resolving, so no publish is left half-done. It is idempotent.
47
+ *
48
+ * @param kafka Any producer exposing `transaction()` — a `KafkaClient<T>` or
49
+ * `IKafkaProducer<T>`. Its producer must already be connected.
50
+ * @param store Your DB-backed {@link OutboxStore} implementation.
51
+ * @param options Tuning and observability — see {@link OutboxRelayOptions}.
52
+ *
53
+ * @example
54
+ * ```ts
55
+ * // Pseudo-Postgres outbox store.
56
+ * const store: OutboxStore = {
57
+ * async fetchUnpublished(limit) {
58
+ * const { rows } = await pool.query(
59
+ * `SELECT id, topic, payload, key, correlation_id AS "correlationId",
60
+ * event_id AS "eventId", headers
61
+ * FROM outbox
62
+ * WHERE published_at IS NULL
63
+ * ORDER BY created_at ASC
64
+ * LIMIT $1`,
65
+ * [limit],
66
+ * );
67
+ * return rows;
68
+ * },
69
+ * async markPublished(ids) {
70
+ * await pool.query(
71
+ * `UPDATE outbox SET published_at = now() WHERE id = ANY($1)`,
72
+ * [ids],
73
+ * );
74
+ * },
75
+ * };
76
+ *
77
+ * await kafka.connectProducer();
78
+ * const relay = startOutboxRelay(kafka, store, {
79
+ * pollIntervalMs: 500,
80
+ * batchSize: 200,
81
+ * onPublished: (n) => metrics.increment('outbox.published', n),
82
+ * });
83
+ *
84
+ * // On shutdown:
85
+ * await relay.stop();
86
+ * await kafka.disconnect();
87
+ * ```
88
+ */
89
+ export declare function startOutboxRelay<T extends TopicMapConstraint<T>>(kafka: OutboxProducer<T>, store: OutboxStore, options?: OutboxRelayOptions): OutboxRelayHandle;
90
+ //# sourceMappingURL=outbox.relay.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"outbox.relay.d.ts","sourceRoot":"","sources":["../../../src/client/outbox/outbox.relay.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,kBAAkB,EAAE,MAAM,UAAU,CAAC;AACnD,OAAO,KAAK,EAEV,cAAc,EACd,iBAAiB,EACjB,kBAAkB,EAClB,WAAW,EACZ,MAAM,gBAAgB,CAAC;AAOxB;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAqFG;AACH,wBAAgB,gBAAgB,CAAC,CAAC,SAAS,kBAAkB,CAAC,CAAC,CAAC,EAC9D,KAAK,EAAE,cAAc,CAAC,CAAC,CAAC,EACxB,KAAK,EAAE,WAAW,EAClB,OAAO,GAAE,kBAAuB,GAC/B,iBAAiB,CAwEnB"}