youeduc-sdk-messaging 0.0.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/LICENSE +21 -0
- package/README.md +218 -0
- package/dist/admin.d.ts +61 -0
- package/dist/admin.js +143 -0
- package/dist/admin.js.map +1 -0
- package/dist/config.d.ts +74 -0
- package/dist/config.js +128 -0
- package/dist/config.js.map +1 -0
- package/dist/consumer-client.d.ts +155 -0
- package/dist/consumer-client.js +299 -0
- package/dist/consumer-client.js.map +1 -0
- package/dist/consumer.d.ts +156 -0
- package/dist/consumer.js +1228 -0
- package/dist/consumer.js.map +1 -0
- package/dist/contracts/index.d.ts +16 -0
- package/dist/contracts/index.js +42 -0
- package/dist/contracts/index.js.map +1 -0
- package/dist/contracts/schemas.d.ts +570 -0
- package/dist/contracts/schemas.js +902 -0
- package/dist/contracts/schemas.js.map +1 -0
- package/dist/contracts/usuarios.d.ts +163 -0
- package/dist/contracts/usuarios.js +5 -0
- package/dist/contracts/usuarios.js.map +1 -0
- package/dist/dedup.d.ts +29 -0
- package/dist/dedup.js +65 -0
- package/dist/dedup.js.map +1 -0
- package/dist/dlq.d.ts +161 -0
- package/dist/dlq.js +341 -0
- package/dist/dlq.js.map +1 -0
- package/dist/errors.d.ts +72 -0
- package/dist/errors.js +95 -0
- package/dist/errors.js.map +1 -0
- package/dist/headers.d.ts +92 -0
- package/dist/headers.js +144 -0
- package/dist/headers.js.map +1 -0
- package/dist/index.d.ts +30 -0
- package/dist/index.js +32 -0
- package/dist/index.js.map +1 -0
- package/dist/integrations/redis.d.ts +118 -0
- package/dist/integrations/redis.js +272 -0
- package/dist/integrations/redis.js.map +1 -0
- package/dist/integrations/runtime.d.ts +232 -0
- package/dist/integrations/runtime.js +554 -0
- package/dist/integrations/runtime.js.map +1 -0
- package/dist/logger.d.ts +37 -0
- package/dist/logger.js +49 -0
- package/dist/logger.js.map +1 -0
- package/dist/messaging.d.ts +145 -0
- package/dist/messaging.js +164 -0
- package/dist/messaging.js.map +1 -0
- package/dist/naming.d.ts +64 -0
- package/dist/naming.js +167 -0
- package/dist/naming.js.map +1 -0
- package/dist/payload.d.ts +8 -0
- package/dist/payload.js +32 -0
- package/dist/payload.js.map +1 -0
- package/dist/publisher.d.ts +50 -0
- package/dist/publisher.js +199 -0
- package/dist/publisher.js.map +1 -0
- package/dist/retry.d.ts +24 -0
- package/dist/retry.js +42 -0
- package/dist/retry.js.map +1 -0
- package/dist/schema.d.ts +83 -0
- package/dist/schema.js +676 -0
- package/dist/schema.js.map +1 -0
- package/dist/telemetry.d.ts +52 -0
- package/dist/telemetry.js +221 -0
- package/dist/telemetry.js.map +1 -0
- package/dist/transport.d.ts +130 -0
- package/dist/transport.js +283 -0
- package/dist/transport.js.map +1 -0
- package/dist/types.d.ts +148 -0
- package/dist/types.js +35 -0
- package/dist/types.js.map +1 -0
- package/dist/util.d.ts +38 -0
- package/dist/util.js +112 -0
- package/dist/util.js.map +1 -0
- package/dist/w3c.d.ts +16 -0
- package/dist/w3c.js +119 -0
- package/dist/w3c.js.map +1 -0
- package/package.json +74 -1
|
@@ -0,0 +1,283 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Send transport over the `@confluentinc/kafka-javascript` producer (KafkaJS-compatible API).
|
|
3
|
+
*/
|
|
4
|
+
import { NotStartedError, PublishError } from './errors.js';
|
|
5
|
+
import { defaultLogger } from './logger.js';
|
|
6
|
+
import { errorFields, errorMessage, formatSeconds, isPositiveFinite } from './util.js';
|
|
7
|
+
/**
|
|
8
|
+
* Default factory: `new KafkaJS.Kafka().producer(config)` from `@confluentinc/kafka-javascript`.
|
|
9
|
+
* The native module is loaded on first use, so importing the SDK does not load librdkafka.
|
|
10
|
+
*/
|
|
11
|
+
export const createConfluentProducer = async (config) => {
|
|
12
|
+
const { KafkaJS } = (await import('@confluentinc/kafka-javascript')).default;
|
|
13
|
+
return new KafkaJS.Kafka({}).producer(config);
|
|
14
|
+
};
|
|
15
|
+
// ------------------------------------------------------------------------------ error classification
|
|
16
|
+
/** librdkafka error codes (`ErrorCodes` of the client) used by the classification. */
|
|
17
|
+
export const KafkaErrorCodes = Object.freeze({
|
|
18
|
+
ERR__MSG_TIMED_OUT: -192,
|
|
19
|
+
ERR__TIMED_OUT: -185,
|
|
20
|
+
ERR__QUEUE_FULL: -184,
|
|
21
|
+
ERR__TIMED_OUT_QUEUE: -166,
|
|
22
|
+
ERR__TRANSPORT: -195,
|
|
23
|
+
ERR__ALL_BROKERS_DOWN: -187,
|
|
24
|
+
ERR__UNKNOWN_TOPIC: -188,
|
|
25
|
+
ERR__RESOLVE: -193,
|
|
26
|
+
ERR_UNKNOWN_TOPIC_OR_PART: 3,
|
|
27
|
+
ERR_LEADER_NOT_AVAILABLE: 5,
|
|
28
|
+
ERR_NOT_LEADER_FOR_PARTITION: 6,
|
|
29
|
+
ERR_REQUEST_TIMED_OUT: 7,
|
|
30
|
+
ERR_MSG_SIZE_TOO_LARGE: 10,
|
|
31
|
+
ERR_NETWORK_EXCEPTION: 13,
|
|
32
|
+
ERR_TOPIC_EXCEPTION: 17,
|
|
33
|
+
ERR_RECORD_LIST_TOO_LARGE: 18,
|
|
34
|
+
ERR_NOT_ENOUGH_REPLICAS: 19,
|
|
35
|
+
ERR_NOT_ENOUGH_REPLICAS_AFTER_APPEND: 20,
|
|
36
|
+
ERR_TOPIC_AUTHORIZATION_FAILED: 29,
|
|
37
|
+
ERR_CLUSTER_AUTHORIZATION_FAILED: 31,
|
|
38
|
+
ERR_TOPIC_ALREADY_EXISTS: 36,
|
|
39
|
+
ERR_KAFKA_STORAGE_ERROR: 56,
|
|
40
|
+
ERR_FENCED_LEADER_EPOCH: 74,
|
|
41
|
+
ERR_UNKNOWN_LEADER_EPOCH: 75,
|
|
42
|
+
});
|
|
43
|
+
const C = KafkaErrorCodes;
|
|
44
|
+
// Deterministic errors: repeating the same send does not help (Python: MessageSizeTooLargeError,
|
|
45
|
+
// RecordTooLargeError, TopicAuthorizationFailedError, ClusterAuthorizationFailedError,
|
|
46
|
+
// InvalidTopicError).
|
|
47
|
+
const NON_RETRIABLE_CODES = new Set([
|
|
48
|
+
C.ERR_MSG_SIZE_TOO_LARGE,
|
|
49
|
+
C.ERR_RECORD_LIST_TOO_LARGE,
|
|
50
|
+
C.ERR_TOPIC_AUTHORIZATION_FAILED,
|
|
51
|
+
C.ERR_CLUSTER_AUTHORIZATION_FAILED,
|
|
52
|
+
C.ERR_TOPIC_EXCEPTION,
|
|
53
|
+
]);
|
|
54
|
+
// Timeouts are transient (Python: KafkaTimeoutError counts as retriable).
|
|
55
|
+
const TIMEOUT_CODES = new Set([
|
|
56
|
+
C.ERR__MSG_TIMED_OUT,
|
|
57
|
+
C.ERR__TIMED_OUT,
|
|
58
|
+
C.ERR__TIMED_OUT_QUEUE,
|
|
59
|
+
C.ERR_REQUEST_TIMED_OUT,
|
|
60
|
+
C.ERR__QUEUE_FULL,
|
|
61
|
+
]);
|
|
62
|
+
// Transient broker/network conditions (Kafka's RetriableException family; aiokafka marks the
|
|
63
|
+
// equivalent errors `retriable = True`). Used when the client does not flag retriability.
|
|
64
|
+
const TRANSIENT_CODES = new Set([
|
|
65
|
+
C.ERR__TRANSPORT,
|
|
66
|
+
C.ERR__ALL_BROKERS_DOWN,
|
|
67
|
+
C.ERR__UNKNOWN_TOPIC,
|
|
68
|
+
C.ERR__RESOLVE,
|
|
69
|
+
C.ERR_UNKNOWN_TOPIC_OR_PART,
|
|
70
|
+
C.ERR_LEADER_NOT_AVAILABLE,
|
|
71
|
+
C.ERR_NOT_LEADER_FOR_PARTITION,
|
|
72
|
+
C.ERR_NETWORK_EXCEPTION,
|
|
73
|
+
C.ERR_NOT_ENOUGH_REPLICAS,
|
|
74
|
+
C.ERR_NOT_ENOUGH_REPLICAS_AFTER_APPEND,
|
|
75
|
+
C.ERR_KAFKA_STORAGE_ERROR,
|
|
76
|
+
C.ERR_FENCED_LEADER_EPOCH,
|
|
77
|
+
C.ERR_UNKNOWN_LEADER_EPOCH,
|
|
78
|
+
]);
|
|
79
|
+
/**
|
|
80
|
+
* Whether the value is an error raised by the Kafka client: `KafkaJSError` family (`name`
|
|
81
|
+
* starting with `KafkaJS`) or the raw `LibrdKafkaError` (`origin` `local`/`kafka`), both with
|
|
82
|
+
* a numeric librdkafka `code`.
|
|
83
|
+
*/
|
|
84
|
+
export function isKafkaClientError(error) {
|
|
85
|
+
if (typeof error !== 'object' || error === null)
|
|
86
|
+
return false;
|
|
87
|
+
const shape = error;
|
|
88
|
+
if (typeof shape.code !== 'number')
|
|
89
|
+
return false;
|
|
90
|
+
return ((typeof shape.name === 'string' && shape.name.startsWith('KafkaJS')) ||
|
|
91
|
+
shape.origin === 'local' ||
|
|
92
|
+
shape.origin === 'kafka' ||
|
|
93
|
+
typeof shape.isRetriable === 'boolean');
|
|
94
|
+
}
|
|
95
|
+
/**
|
|
96
|
+
* Whether repeating the send may work (Python `is_retriable`): deterministic errors (message
|
|
97
|
+
* too large, authorization, invalid topic) → `false`; timeouts → `true`; otherwise the
|
|
98
|
+
* client's own flag, then the known transient broker/network codes; anything else → `false`.
|
|
99
|
+
*/
|
|
100
|
+
export function isRetriable(error) {
|
|
101
|
+
if (!isKafkaClientError(error))
|
|
102
|
+
return false;
|
|
103
|
+
if (NON_RETRIABLE_CODES.has(error.code))
|
|
104
|
+
return false;
|
|
105
|
+
if (TIMEOUT_CODES.has(error.code))
|
|
106
|
+
return true;
|
|
107
|
+
if (error.isRetriable === true || error.retriable === true)
|
|
108
|
+
return true;
|
|
109
|
+
return TRANSIENT_CODES.has(error.code);
|
|
110
|
+
}
|
|
111
|
+
/** Converts the ordered header list to the KafkaJS object form (repeated names → arrays). */
|
|
112
|
+
/**
|
|
113
|
+
* Largest record the SDK producers accept, in bytes (§11): the same limit as the Python SDK
|
|
114
|
+
* (aiokafka's `max_request_size`). librdkafka's own default (`message.max.bytes` = 1000000) would
|
|
115
|
+
* reject records that Python publishes, and a consumer routing one of them to retry/DLQ would stop.
|
|
116
|
+
*/
|
|
117
|
+
export const MAX_MESSAGE_BYTES = 1_048_576;
|
|
118
|
+
/**
|
|
119
|
+
* Producer settings shared by {@link KafkaProducerSender} and `replayDlq` (§11): idempotent,
|
|
120
|
+
* `acks=all`, Java-compatible murmur2 partitioner and the {@link MAX_MESSAGE_BYTES} limit.
|
|
121
|
+
*/
|
|
122
|
+
export function producerClientConfig(config) {
|
|
123
|
+
return {
|
|
124
|
+
...config.clientConfig(),
|
|
125
|
+
'enable.idempotence': true,
|
|
126
|
+
acks: -1,
|
|
127
|
+
// Java-compatible murmur2 (NULL key → random), the same partitioner as aiokafka (§11): a keyed
|
|
128
|
+
// message lands on the same partition whichever SDK publishes it. Without a `kafkaJS` block
|
|
129
|
+
// the confluent client keeps librdkafka's default (`consistent_random`, CRC32): set it here.
|
|
130
|
+
partitioner: 'murmur2_random',
|
|
131
|
+
'message.max.bytes': MAX_MESSAGE_BYTES,
|
|
132
|
+
};
|
|
133
|
+
}
|
|
134
|
+
export function toProducerHeaders(headers) {
|
|
135
|
+
const result = Object.create(null);
|
|
136
|
+
for (const [name, value] of headers) {
|
|
137
|
+
const existing = result[name];
|
|
138
|
+
if (existing === undefined)
|
|
139
|
+
result[name] = value;
|
|
140
|
+
else if (Array.isArray(existing))
|
|
141
|
+
existing.push(value);
|
|
142
|
+
else
|
|
143
|
+
result[name] = [existing, value];
|
|
144
|
+
}
|
|
145
|
+
return result;
|
|
146
|
+
}
|
|
147
|
+
function parseOffset(metadata) {
|
|
148
|
+
const raw = metadata.baseOffset ?? metadata.offset;
|
|
149
|
+
const value = raw === undefined ? Number.NaN : Number(raw);
|
|
150
|
+
return Number.isFinite(value) ? value : -1;
|
|
151
|
+
}
|
|
152
|
+
/**
|
|
153
|
+
* {@link MessageSender} with an idempotent producer (`acks=all`, `enable.idempotence=true`,
|
|
154
|
+
* `partitioner=murmur2_random`: keyed messages are partitioned like the Python SDK and Java).
|
|
155
|
+
*
|
|
156
|
+
* A single producer is shared by every send (no connection per message). No manual retry: the
|
|
157
|
+
* idempotent producer retries internally without duplicating. A timeout in `send` does not
|
|
158
|
+
* guarantee the message was not written (it may still be in the producer buffer):
|
|
159
|
+
* at-least-once semantics.
|
|
160
|
+
*/
|
|
161
|
+
export class KafkaProducerSender {
|
|
162
|
+
#config;
|
|
163
|
+
#sendTimeoutMs;
|
|
164
|
+
#lingerMs;
|
|
165
|
+
#compressionType;
|
|
166
|
+
#factory;
|
|
167
|
+
#logger;
|
|
168
|
+
#producer = null;
|
|
169
|
+
#lock = Promise.resolve();
|
|
170
|
+
constructor(config, options = {}) {
|
|
171
|
+
const sendTimeoutMs = options.sendTimeoutMs ?? 30_000;
|
|
172
|
+
const lingerMs = options.lingerMs ?? 5;
|
|
173
|
+
if (!isPositiveFinite(sendTimeoutMs)) {
|
|
174
|
+
throw new RangeError(`'send_timeout_ms' must be a finite number > 0; got ${String(sendTimeoutMs)}`);
|
|
175
|
+
}
|
|
176
|
+
if (!Number.isInteger(lingerMs) || lingerMs < 0) {
|
|
177
|
+
throw new RangeError(`'linger_ms' must be >= 0; got ${String(lingerMs)}`);
|
|
178
|
+
}
|
|
179
|
+
this.#config = config;
|
|
180
|
+
this.#sendTimeoutMs = sendTimeoutMs;
|
|
181
|
+
this.#lingerMs = lingerMs;
|
|
182
|
+
this.#compressionType = options.compressionType ?? 'none';
|
|
183
|
+
this.#factory = options.producerFactory ?? createConfluentProducer;
|
|
184
|
+
this.#logger = options.logger;
|
|
185
|
+
}
|
|
186
|
+
get started() {
|
|
187
|
+
return this.#producer !== null;
|
|
188
|
+
}
|
|
189
|
+
/** Final librdkafka configuration handed to the producer factory. */
|
|
190
|
+
producerConfig() {
|
|
191
|
+
return {
|
|
192
|
+
...producerClientConfig(this.#config),
|
|
193
|
+
'linger.ms': this.#lingerMs,
|
|
194
|
+
'compression.type': this.#compressionType,
|
|
195
|
+
};
|
|
196
|
+
}
|
|
197
|
+
#exclusive(fn) {
|
|
198
|
+
const run = this.#lock.then(fn);
|
|
199
|
+
this.#lock = run.then(() => undefined, () => undefined);
|
|
200
|
+
return run;
|
|
201
|
+
}
|
|
202
|
+
/** Creates and connects the producer. Idempotent; after `stop()` creates a new one. */
|
|
203
|
+
start() {
|
|
204
|
+
return this.#exclusive(async () => {
|
|
205
|
+
if (this.#producer !== null)
|
|
206
|
+
return;
|
|
207
|
+
const producer = await this.#factory(this.producerConfig());
|
|
208
|
+
try {
|
|
209
|
+
await producer.connect();
|
|
210
|
+
}
|
|
211
|
+
catch (error) {
|
|
212
|
+
// Release what a failed connect may have allocated.
|
|
213
|
+
await this.#disconnectQuietly(producer);
|
|
214
|
+
throw error;
|
|
215
|
+
}
|
|
216
|
+
this.#producer = producer;
|
|
217
|
+
});
|
|
218
|
+
}
|
|
219
|
+
/** Flushes the buffer and disconnects the producer. Idempotent. */
|
|
220
|
+
stop() {
|
|
221
|
+
return this.#exclusive(async () => {
|
|
222
|
+
const producer = this.#producer;
|
|
223
|
+
this.#producer = null;
|
|
224
|
+
if (producer !== null)
|
|
225
|
+
await producer.disconnect();
|
|
226
|
+
});
|
|
227
|
+
}
|
|
228
|
+
async send(topic, value, options) {
|
|
229
|
+
const producer = this.#producer;
|
|
230
|
+
if (producer === null)
|
|
231
|
+
throw new NotStartedError('KafkaProducerSender is not started');
|
|
232
|
+
let timer;
|
|
233
|
+
let pending;
|
|
234
|
+
try {
|
|
235
|
+
// Everything inside the same try: the client may fail before queueing (sync throw).
|
|
236
|
+
pending = producer.send({
|
|
237
|
+
topic,
|
|
238
|
+
messages: [{ key: options.key, value, headers: toProducerHeaders(options.headers) }],
|
|
239
|
+
});
|
|
240
|
+
}
|
|
241
|
+
catch (error) {
|
|
242
|
+
pending = Promise.reject(error);
|
|
243
|
+
}
|
|
244
|
+
const timeout = new Promise((_resolve, reject) => {
|
|
245
|
+
timer = setTimeout(() => {
|
|
246
|
+
reject(new PublishError(topic, `no broker acknowledgement within ${formatSeconds(this.#sendTimeoutMs)}s`, {
|
|
247
|
+
retriable: true,
|
|
248
|
+
}));
|
|
249
|
+
}, this.#sendTimeoutMs);
|
|
250
|
+
});
|
|
251
|
+
// After a timeout the delivery may still settle: never leave it unhandled.
|
|
252
|
+
pending.catch(() => undefined);
|
|
253
|
+
let metadata;
|
|
254
|
+
try {
|
|
255
|
+
metadata = await Promise.race([pending, timeout]);
|
|
256
|
+
}
|
|
257
|
+
catch (error) {
|
|
258
|
+
if (error instanceof PublishError)
|
|
259
|
+
throw error;
|
|
260
|
+
if (isKafkaClientError(error)) {
|
|
261
|
+
throw new PublishError(topic, errorMessage(error), { retriable: isRetriable(error), cause: error });
|
|
262
|
+
}
|
|
263
|
+
throw error;
|
|
264
|
+
}
|
|
265
|
+
finally {
|
|
266
|
+
clearTimeout(timer);
|
|
267
|
+
}
|
|
268
|
+
const record = metadata[0];
|
|
269
|
+
if (record === undefined) {
|
|
270
|
+
throw new PublishError(topic, 'broker returned no delivery report', { retriable: true });
|
|
271
|
+
}
|
|
272
|
+
return { topic: record.topicName, partition: record.partition, offset: parseOffset(record) };
|
|
273
|
+
}
|
|
274
|
+
async #disconnectQuietly(producer) {
|
|
275
|
+
try {
|
|
276
|
+
await producer.disconnect();
|
|
277
|
+
}
|
|
278
|
+
catch (error) {
|
|
279
|
+
(this.#logger ?? defaultLogger()).warn('kafka_producer_stop_failed', { error: errorFields(error) });
|
|
280
|
+
}
|
|
281
|
+
}
|
|
282
|
+
}
|
|
283
|
+
//# sourceMappingURL=transport.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"transport.js","sourceRoot":"","sources":["../src/transport.ts"],"names":[],"mappings":"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"}
|
package/dist/types.d.ts
ADDED
|
@@ -0,0 +1,148 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Shared contracts (data types and interfaces), mirroring Python's `types.py`. No Kafka client
|
|
3
|
+
* dependency: implementations (and test fakes) are interchangeable through these interfaces.
|
|
4
|
+
*/
|
|
5
|
+
import type { KafkaHeaders, MessageHeaders } from './headers.js';
|
|
6
|
+
export type ConsumeOutcome = 'success' | 'retry' | 'dlq' | 'deduplicated' | 'skipped';
|
|
7
|
+
export type DlqReason = 'permanent_error' | 'max_retries' | 'invalid_payload' | 'schema_error' | 'routing_failed';
|
|
8
|
+
export type PublishStatus = 'success' | 'error';
|
|
9
|
+
export declare const CONSUME_OUTCOMES: readonly ConsumeOutcome[];
|
|
10
|
+
export declare const DLQ_REASONS: readonly DlqReason[];
|
|
11
|
+
/** A JSON object (schemas and payloads). */
|
|
12
|
+
export type JsonObject = Record<string, unknown>;
|
|
13
|
+
export interface PublishResult {
|
|
14
|
+
readonly messageId: string;
|
|
15
|
+
readonly topic: string;
|
|
16
|
+
readonly partition: number;
|
|
17
|
+
readonly offset: number;
|
|
18
|
+
}
|
|
19
|
+
export interface ConsumedMessage {
|
|
20
|
+
readonly messageId: string;
|
|
21
|
+
readonly eventType: string;
|
|
22
|
+
readonly schemaVersion: string;
|
|
23
|
+
readonly payload: JsonObject;
|
|
24
|
+
readonly key: string | null;
|
|
25
|
+
readonly correlationId: string;
|
|
26
|
+
readonly idempotencyKey: string | null;
|
|
27
|
+
readonly tenantId: string | null;
|
|
28
|
+
readonly producer: string;
|
|
29
|
+
readonly publishedAt: string;
|
|
30
|
+
/** Number of previous retries (0 on the first delivery). */
|
|
31
|
+
readonly retryCount: number;
|
|
32
|
+
readonly topic: string;
|
|
33
|
+
readonly partition: number;
|
|
34
|
+
readonly offset: number;
|
|
35
|
+
readonly headers: Readonly<MessageHeaders>;
|
|
36
|
+
}
|
|
37
|
+
/**
|
|
38
|
+
* Resolving = success. Rejecting with `PermanentProcessingError` = DLQ. Any other rejection =
|
|
39
|
+
* retry. The `signal` is aborted when the consumer gives up on the handler (processing timeout
|
|
40
|
+
* or shutdown deadline); handlers should stop their work when it fires.
|
|
41
|
+
*/
|
|
42
|
+
export type MessageHandler = (message: ConsumedMessage, signal: AbortSignal) => Promise<void>;
|
|
43
|
+
export interface SentRecord {
|
|
44
|
+
readonly topic: string;
|
|
45
|
+
readonly partition: number;
|
|
46
|
+
readonly offset: number;
|
|
47
|
+
}
|
|
48
|
+
export interface SendOptions {
|
|
49
|
+
key: Buffer | null;
|
|
50
|
+
headers: KafkaHeaders;
|
|
51
|
+
}
|
|
52
|
+
export interface MessageSender {
|
|
53
|
+
/** Sends and waits for the broker ack. Failure → `PublishError`. */
|
|
54
|
+
send(topic: string, value: Buffer, options: SendOptions): Promise<SentRecord>;
|
|
55
|
+
}
|
|
56
|
+
export interface SchemaLoader {
|
|
57
|
+
/** `event` is the slug (`student-enrolled`). Missing → `SchemaNotFoundError`. */
|
|
58
|
+
load(domain: string, event: string, version: string): JsonObject;
|
|
59
|
+
}
|
|
60
|
+
export interface SchemaValidator {
|
|
61
|
+
/** Invalid → `SchemaValidationError` (no payload values in the message). */
|
|
62
|
+
validate(payload: unknown): void;
|
|
63
|
+
}
|
|
64
|
+
export type SchemaValidatorFactory = (schema: JsonObject) => SchemaValidator;
|
|
65
|
+
export interface PayloadValidationRequest {
|
|
66
|
+
domain: string;
|
|
67
|
+
eventType: string;
|
|
68
|
+
version: string;
|
|
69
|
+
payload: unknown;
|
|
70
|
+
}
|
|
71
|
+
/** Validates an event payload; implemented by `SchemaRegistry`. */
|
|
72
|
+
export interface PayloadValidator {
|
|
73
|
+
/** `SchemaNotFoundError` / `SchemaValidationError` when invalid. */
|
|
74
|
+
validate(request: PayloadValidationRequest): void;
|
|
75
|
+
}
|
|
76
|
+
/**
|
|
77
|
+
* Two-phase deduplication: query before, mark only after the handler succeeded. Retry and DLQ
|
|
78
|
+
* never mark: retries come back and the DLQ may be reprocessed.
|
|
79
|
+
*/
|
|
80
|
+
export interface DuplicateChecker {
|
|
81
|
+
isProcessed(key: string): Promise<boolean>;
|
|
82
|
+
markProcessed(key: string): Promise<void>;
|
|
83
|
+
}
|
|
84
|
+
/** Common attributes of spans and metrics. Never contains the payload (PII). */
|
|
85
|
+
export interface MessageInfo {
|
|
86
|
+
readonly topic: string;
|
|
87
|
+
readonly eventType: string;
|
|
88
|
+
readonly messageId: string;
|
|
89
|
+
readonly schemaVersion: string;
|
|
90
|
+
/** Kept for the SDK's own use (`has_key` in logs); never emitted in spans/metrics/logs. */
|
|
91
|
+
readonly key: string | null;
|
|
92
|
+
readonly correlationId: string | null;
|
|
93
|
+
readonly tenantId: string | null;
|
|
94
|
+
readonly partition: number | null;
|
|
95
|
+
readonly offset: number | null;
|
|
96
|
+
readonly group: string | null;
|
|
97
|
+
readonly retryCount: number;
|
|
98
|
+
}
|
|
99
|
+
export interface MessageInfoInit {
|
|
100
|
+
topic: string;
|
|
101
|
+
eventType: string;
|
|
102
|
+
messageId: string;
|
|
103
|
+
schemaVersion?: string | undefined;
|
|
104
|
+
key?: string | null | undefined;
|
|
105
|
+
correlationId?: string | null | undefined;
|
|
106
|
+
tenantId?: string | null | undefined;
|
|
107
|
+
partition?: number | null | undefined;
|
|
108
|
+
offset?: number | null | undefined;
|
|
109
|
+
group?: string | null | undefined;
|
|
110
|
+
retryCount?: number | undefined;
|
|
111
|
+
}
|
|
112
|
+
/** Builds a frozen {@link MessageInfo} applying the same defaults as Python's dataclass. */
|
|
113
|
+
export declare function createMessageInfo(init: MessageInfoInit): MessageInfo;
|
|
114
|
+
export interface ConsumeSpan {
|
|
115
|
+
setOutcome(outcome: ConsumeOutcome): void;
|
|
116
|
+
recordError(error: unknown): void;
|
|
117
|
+
}
|
|
118
|
+
export interface PublishRecord {
|
|
119
|
+
status: PublishStatus;
|
|
120
|
+
durationMs: number;
|
|
121
|
+
}
|
|
122
|
+
export interface ConsumeRecord {
|
|
123
|
+
outcome: ConsumeOutcome;
|
|
124
|
+
durationMs: number;
|
|
125
|
+
}
|
|
126
|
+
export interface RetryRecord {
|
|
127
|
+
attempt: number;
|
|
128
|
+
retryTopic: string;
|
|
129
|
+
}
|
|
130
|
+
export interface DlqRecord {
|
|
131
|
+
reason: DlqReason;
|
|
132
|
+
dlqTopic: string;
|
|
133
|
+
}
|
|
134
|
+
/**
|
|
135
|
+
* Spans and metrics. The span methods replace Python's context managers: they run `fn` inside
|
|
136
|
+
* the span (active context), record a thrown error on it, end it and return/rethrow.
|
|
137
|
+
*/
|
|
138
|
+
export interface Telemetry {
|
|
139
|
+
/** PRODUCER span `send {topic}`; injects the W3C context into `headers` (mutates) before `fn`. */
|
|
140
|
+
publishSpan<T>(info: MessageInfo, headers: MessageHeaders, fn: () => Promise<T>): Promise<T>;
|
|
141
|
+
/** CONSUMER span `process {topic}` whose parent is extracted from `headers`. */
|
|
142
|
+
consumeSpan<T>(info: MessageInfo, headers: Readonly<MessageHeaders>, fn: (span: ConsumeSpan) => Promise<T>): Promise<T>;
|
|
143
|
+
recordPublish(info: MessageInfo, record: PublishRecord): void;
|
|
144
|
+
recordSchemaError(info: MessageInfo): void;
|
|
145
|
+
recordConsume(info: MessageInfo, record: ConsumeRecord): void;
|
|
146
|
+
recordRetry(info: MessageInfo, record: RetryRecord): void;
|
|
147
|
+
recordDlq(info: MessageInfo, record: DlqRecord): void;
|
|
148
|
+
}
|
package/dist/types.js
ADDED
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Shared contracts (data types and interfaces), mirroring Python's `types.py`. No Kafka client
|
|
3
|
+
* dependency: implementations (and test fakes) are interchangeable through these interfaces.
|
|
4
|
+
*/
|
|
5
|
+
export const CONSUME_OUTCOMES = [
|
|
6
|
+
'success',
|
|
7
|
+
'retry',
|
|
8
|
+
'dlq',
|
|
9
|
+
'deduplicated',
|
|
10
|
+
'skipped',
|
|
11
|
+
];
|
|
12
|
+
export const DLQ_REASONS = [
|
|
13
|
+
'permanent_error',
|
|
14
|
+
'max_retries',
|
|
15
|
+
'invalid_payload',
|
|
16
|
+
'schema_error',
|
|
17
|
+
'routing_failed',
|
|
18
|
+
];
|
|
19
|
+
/** Builds a frozen {@link MessageInfo} applying the same defaults as Python's dataclass. */
|
|
20
|
+
export function createMessageInfo(init) {
|
|
21
|
+
return Object.freeze({
|
|
22
|
+
topic: init.topic,
|
|
23
|
+
eventType: init.eventType,
|
|
24
|
+
messageId: init.messageId,
|
|
25
|
+
schemaVersion: init.schemaVersion ?? '',
|
|
26
|
+
key: init.key ?? null,
|
|
27
|
+
correlationId: init.correlationId ?? null,
|
|
28
|
+
tenantId: init.tenantId ?? null,
|
|
29
|
+
partition: init.partition ?? null,
|
|
30
|
+
offset: init.offset ?? null,
|
|
31
|
+
group: init.group ?? null,
|
|
32
|
+
retryCount: init.retryCount ?? 0,
|
|
33
|
+
});
|
|
34
|
+
}
|
|
35
|
+
//# sourceMappingURL=types.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"types.js","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAaH,MAAM,CAAC,MAAM,gBAAgB,GAA8B;IACzD,SAAS;IACT,OAAO;IACP,KAAK;IACL,cAAc;IACd,SAAS;CACV,CAAC;AACF,MAAM,CAAC,MAAM,WAAW,GAAyB;IAC/C,iBAAiB;IACjB,aAAa;IACb,iBAAiB;IACjB,cAAc;IACd,gBAAgB;CACjB,CAAC;AAgIF,4FAA4F;AAC5F,MAAM,UAAU,iBAAiB,CAAC,IAAqB;IACrD,OAAO,MAAM,CAAC,MAAM,CAAC;QACnB,KAAK,EAAE,IAAI,CAAC,KAAK;QACjB,SAAS,EAAE,IAAI,CAAC,SAAS;QACzB,SAAS,EAAE,IAAI,CAAC,SAAS;QACzB,aAAa,EAAE,IAAI,CAAC,aAAa,IAAI,EAAE;QACvC,GAAG,EAAE,IAAI,CAAC,GAAG,IAAI,IAAI;QACrB,aAAa,EAAE,IAAI,CAAC,aAAa,IAAI,IAAI;QACzC,QAAQ,EAAE,IAAI,CAAC,QAAQ,IAAI,IAAI;QAC/B,SAAS,EAAE,IAAI,CAAC,SAAS,IAAI,IAAI;QACjC,MAAM,EAAE,IAAI,CAAC,MAAM,IAAI,IAAI;QAC3B,KAAK,EAAE,IAAI,CAAC,KAAK,IAAI,IAAI;QACzB,UAAU,EAAE,IAAI,CAAC,UAAU,IAAI,CAAC;KACjC,CAAC,CAAC;AACL,CAAC"}
|
package/dist/util.d.ts
ADDED
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Internal helpers shared by the modules (not part of the public API).
|
|
3
|
+
*
|
|
4
|
+
* Several of them exist only to keep texts and formats byte-compatible with the Python SDK
|
|
5
|
+
* (`repr()` quoting in error messages, character counting by code point, ISO 8601 timestamps
|
|
6
|
+
* in the shape produced by `datetime.isoformat()`).
|
|
7
|
+
*/
|
|
8
|
+
/**
|
|
9
|
+
* Python `repr()` of a string: single quotes unless the text contains `'` and no `"`; escapes
|
|
10
|
+
* backslash, the chosen quote, `\n`, `\r`, `\t` and non-printable control characters.
|
|
11
|
+
*/
|
|
12
|
+
export declare function pyRepr(value: string): string;
|
|
13
|
+
/**
|
|
14
|
+
* A duration in milliseconds written in seconds as it appears in error texts (§15), the same in
|
|
15
|
+
* both SDKs: the shortest positional decimal of the value, no exponent (`20000` → `"20"`,
|
|
16
|
+
* `500` → `"0.5"`, `0.05` → `"0.00005"`).
|
|
17
|
+
*/
|
|
18
|
+
export declare function formatSeconds(ms: number): string;
|
|
19
|
+
/** Truncates to `max` characters counted by Unicode code point (like Python `str[:max]`). */
|
|
20
|
+
export declare function truncateChars(value: string, max: number): string;
|
|
21
|
+
/**
|
|
22
|
+
* Name of the error "class" (spec §4.3 `x-last-error-type`): `error.name` (the SDK errors set
|
|
23
|
+
* it to their class name); a thrown value that is not an `Error` → `'Error'`.
|
|
24
|
+
*/
|
|
25
|
+
export declare function errorTypeName(error: unknown): string;
|
|
26
|
+
/** Text of the error, the equivalent of Python `str(exc)`. */
|
|
27
|
+
export declare function errorMessage(error: unknown): string;
|
|
28
|
+
/** `{type, message}` grouping used in the `error` field of the logs. */
|
|
29
|
+
export declare function errorFields(error: unknown, maxLength?: number): {
|
|
30
|
+
type: string;
|
|
31
|
+
message: string;
|
|
32
|
+
};
|
|
33
|
+
/** RFC 3339 UTC as written by the Node SDK (spec §4.1): `2026-10-06T12:34:56.789Z`. */
|
|
34
|
+
export declare function formatIsoUtc(epochMs: number): string;
|
|
35
|
+
/** Plain JSON object (prototype `Object.prototype` or `null`). */
|
|
36
|
+
export declare function isPlainObject(value: unknown): value is Record<string, unknown>;
|
|
37
|
+
/** Finite number strictly greater than zero. */
|
|
38
|
+
export declare function isPositiveFinite(value: unknown): value is number;
|
package/dist/util.js
ADDED
|
@@ -0,0 +1,112 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Internal helpers shared by the modules (not part of the public API).
|
|
3
|
+
*
|
|
4
|
+
* Several of them exist only to keep texts and formats byte-compatible with the Python SDK
|
|
5
|
+
* (`repr()` quoting in error messages, character counting by code point, ISO 8601 timestamps
|
|
6
|
+
* in the shape produced by `datetime.isoformat()`).
|
|
7
|
+
*/
|
|
8
|
+
/**
|
|
9
|
+
* Python `repr()` of a string: single quotes unless the text contains `'` and no `"`; escapes
|
|
10
|
+
* backslash, the chosen quote, `\n`, `\r`, `\t` and non-printable control characters.
|
|
11
|
+
*/
|
|
12
|
+
export function pyRepr(value) {
|
|
13
|
+
const quote = value.includes("'") && !value.includes('"') ? '"' : "'";
|
|
14
|
+
let out = quote;
|
|
15
|
+
for (const char of value) {
|
|
16
|
+
const code = char.codePointAt(0) ?? 0;
|
|
17
|
+
if (char === '\\')
|
|
18
|
+
out += '\\\\';
|
|
19
|
+
else if (char === quote)
|
|
20
|
+
out += `\\${quote}`;
|
|
21
|
+
else if (char === '\n')
|
|
22
|
+
out += '\\n';
|
|
23
|
+
else if (char === '\r')
|
|
24
|
+
out += '\\r';
|
|
25
|
+
else if (char === '\t')
|
|
26
|
+
out += '\\t';
|
|
27
|
+
else if (code < 0x20 || (code >= 0x7f && code <= 0xa0))
|
|
28
|
+
out += `\\x${code.toString(16).padStart(2, '0')}`;
|
|
29
|
+
else
|
|
30
|
+
out += char;
|
|
31
|
+
}
|
|
32
|
+
return out + quote;
|
|
33
|
+
}
|
|
34
|
+
/**
|
|
35
|
+
* A duration in milliseconds written in seconds as it appears in error texts (§15), the same in
|
|
36
|
+
* both SDKs: the shortest positional decimal of the value, no exponent (`20000` → `"20"`,
|
|
37
|
+
* `500` → `"0.5"`, `0.05` → `"0.00005"`).
|
|
38
|
+
*/
|
|
39
|
+
export function formatSeconds(ms) {
|
|
40
|
+
const text = String(ms / 1000);
|
|
41
|
+
const match = /^(-?)(\d+)(?:\.(\d+))?e([+-]\d+)$/.exec(text);
|
|
42
|
+
if (match === null)
|
|
43
|
+
return text;
|
|
44
|
+
const [, sign = '', int = '', frac = '', exponent = '0'] = match;
|
|
45
|
+
const digits = int + frac;
|
|
46
|
+
const point = int.length + Number(exponent);
|
|
47
|
+
if (point <= 0)
|
|
48
|
+
return `${sign}0.${'0'.repeat(-point)}${digits}`;
|
|
49
|
+
if (point >= digits.length)
|
|
50
|
+
return sign + digits + '0'.repeat(point - digits.length);
|
|
51
|
+
return `${sign}${digits.slice(0, point)}.${digits.slice(point)}`;
|
|
52
|
+
}
|
|
53
|
+
/** Truncates to `max` characters counted by Unicode code point (like Python `str[:max]`). */
|
|
54
|
+
export function truncateChars(value, max) {
|
|
55
|
+
if (value.length <= max)
|
|
56
|
+
return value; // UTF-16 length >= code point count
|
|
57
|
+
let count = 0;
|
|
58
|
+
let end = 0;
|
|
59
|
+
for (const char of value) {
|
|
60
|
+
if (count === max)
|
|
61
|
+
break;
|
|
62
|
+
count += 1;
|
|
63
|
+
end += char.length;
|
|
64
|
+
}
|
|
65
|
+
return value.slice(0, end);
|
|
66
|
+
}
|
|
67
|
+
/**
|
|
68
|
+
* Name of the error "class" (spec §4.3 `x-last-error-type`): `error.name` (the SDK errors set
|
|
69
|
+
* it to their class name); a thrown value that is not an `Error` → `'Error'`.
|
|
70
|
+
*/
|
|
71
|
+
export function errorTypeName(error) {
|
|
72
|
+
if (error instanceof Error)
|
|
73
|
+
return error.name || 'Error';
|
|
74
|
+
return 'Error';
|
|
75
|
+
}
|
|
76
|
+
/** Text of the error, the equivalent of Python `str(exc)`. */
|
|
77
|
+
export function errorMessage(error) {
|
|
78
|
+
if (error instanceof Error)
|
|
79
|
+
return error.message;
|
|
80
|
+
if (typeof error === 'string')
|
|
81
|
+
return error;
|
|
82
|
+
try {
|
|
83
|
+
return String(error);
|
|
84
|
+
}
|
|
85
|
+
catch {
|
|
86
|
+
return '';
|
|
87
|
+
}
|
|
88
|
+
}
|
|
89
|
+
/** `{type, message}` grouping used in the `error` field of the logs. */
|
|
90
|
+
export function errorFields(error, maxLength) {
|
|
91
|
+
const message = errorMessage(error);
|
|
92
|
+
return {
|
|
93
|
+
type: errorTypeName(error),
|
|
94
|
+
message: maxLength === undefined ? message : truncateChars(message, maxLength),
|
|
95
|
+
};
|
|
96
|
+
}
|
|
97
|
+
/** RFC 3339 UTC as written by the Node SDK (spec §4.1): `2026-10-06T12:34:56.789Z`. */
|
|
98
|
+
export function formatIsoUtc(epochMs) {
|
|
99
|
+
return new Date(epochMs).toISOString();
|
|
100
|
+
}
|
|
101
|
+
/** Plain JSON object (prototype `Object.prototype` or `null`). */
|
|
102
|
+
export function isPlainObject(value) {
|
|
103
|
+
if (typeof value !== 'object' || value === null || Array.isArray(value))
|
|
104
|
+
return false;
|
|
105
|
+
const proto = Object.getPrototypeOf(value);
|
|
106
|
+
return proto === Object.prototype || proto === null;
|
|
107
|
+
}
|
|
108
|
+
/** Finite number strictly greater than zero. */
|
|
109
|
+
export function isPositiveFinite(value) {
|
|
110
|
+
return typeof value === 'number' && Number.isFinite(value) && value > 0;
|
|
111
|
+
}
|
|
112
|
+
//# sourceMappingURL=util.js.map
|
package/dist/util.js.map
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"util.js","sourceRoot":"","sources":["../src/util.ts"],"names":[],"mappings":"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"}
|
package/dist/w3c.d.ts
ADDED
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Minimal W3C Trace Context (`traceparent`/`tracestate`) + W3C Baggage propagator, built only
|
|
3
|
+
* on `@opentelemetry/api`.
|
|
4
|
+
*
|
|
5
|
+
* Python's OpenTelemetry API defaults the global propagator to `tracecontext,baggage`; the JS
|
|
6
|
+
* API defaults to a no-op propagator until an SDK registers one. `OtelTelemetry` falls back to
|
|
7
|
+
* this propagator while the global one is the no-op, so a Node service propagates trace context
|
|
8
|
+
* exactly like a Python one even when the application only set a tracer provider.
|
|
9
|
+
*/
|
|
10
|
+
import { type Context, type TextMapGetter, type TextMapPropagator, type TextMapSetter } from '@opentelemetry/api';
|
|
11
|
+
/** W3C `traceparent`, `tracestate` and `baggage`. */
|
|
12
|
+
export declare class W3CPropagator implements TextMapPropagator {
|
|
13
|
+
inject(context: Context, carrier: unknown, setter: TextMapSetter): void;
|
|
14
|
+
extract(context: Context, carrier: unknown, getter: TextMapGetter): Context;
|
|
15
|
+
fields(): string[];
|
|
16
|
+
}
|