@babelqueue/pulsar 1.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +81 -0
- package/dist/index.cjs +154 -0
- package/dist/index.cjs.map +1 -0
- package/dist/index.d.cts +130 -0
- package/dist/index.d.ts +130 -0
- package/dist/index.js +126 -0
- package/dist/index.js.map +1 -0
- package/package.json +57 -0
package/README.md
ADDED
|
@@ -0,0 +1,81 @@
|
|
|
1
|
+
# @babelqueue/pulsar
|
|
2
|
+
|
|
3
|
+
Apache Pulsar adapter for [BabelQueue](https://babelqueue.com) — a canonical-envelope
|
|
4
|
+
**publisher** and a URN-routed **consumer** over `pulsar-client`, on `@babelqueue/core`.
|
|
5
|
+
Implements
|
|
6
|
+
[§5 of the broker-bindings contract](https://babelqueue.com/docs/spec/1.x/broker-bindings#apache-pulsar),
|
|
7
|
+
so a Pulsar-based Node service speaks the same wire contract as the .NET, Java, Python and Go
|
|
8
|
+
SDKs.
|
|
9
|
+
|
|
10
|
+
## Install
|
|
11
|
+
|
|
12
|
+
```bash
|
|
13
|
+
npm i @babelqueue/pulsar pulsar-client
|
|
14
|
+
```
|
|
15
|
+
|
|
16
|
+
`pulsar-client` is an optional peer — **you provide the producer/consumer** (a `Producer` /
|
|
17
|
+
`Consumer` from `pulsar-client` satisfies the adapter structurally).
|
|
18
|
+
|
|
19
|
+
## Use
|
|
20
|
+
|
|
21
|
+
```ts
|
|
22
|
+
import Pulsar from "pulsar-client";
|
|
23
|
+
import { PulsarPublisher, PulsarConsumer } from "@babelqueue/pulsar";
|
|
24
|
+
|
|
25
|
+
const client = new Pulsar.Client({ serviceUrl: "pulsar://localhost:6650" });
|
|
26
|
+
|
|
27
|
+
// produce
|
|
28
|
+
const producer = await client.createProducer({ topic: "orders" });
|
|
29
|
+
const id = await new PulsarPublisher(producer)
|
|
30
|
+
.publish("urn:babel:orders:created", { order_id: 1042 });
|
|
31
|
+
|
|
32
|
+
// consume (Shared subscription)
|
|
33
|
+
const sub = await client.subscribe({
|
|
34
|
+
topic: "orders",
|
|
35
|
+
subscription: "babelqueue",
|
|
36
|
+
subscriptionType: "Shared",
|
|
37
|
+
});
|
|
38
|
+
const consumer = new PulsarConsumer(
|
|
39
|
+
sub,
|
|
40
|
+
{
|
|
41
|
+
"urn:babel:orders:created": async (env, message) => {
|
|
42
|
+
console.log(env.data.order_id, env.trace_id, env.attempts);
|
|
43
|
+
},
|
|
44
|
+
},
|
|
45
|
+
{ onError: (err) => console.error(err) },
|
|
46
|
+
);
|
|
47
|
+
await consumer.run();
|
|
48
|
+
```
|
|
49
|
+
|
|
50
|
+
Delayed delivery: `publish(urn, data, { delayMs: 300000 })` → native `deliverAfter`. The
|
|
51
|
+
consumer routes purely on the `bq-job` property.
|
|
52
|
+
|
|
53
|
+
## Contract mapping (§5)
|
|
54
|
+
|
|
55
|
+
| Envelope | Apache Pulsar |
|
|
56
|
+
| :--- | :--- |
|
|
57
|
+
| body | message payload (byte-identical across SDKs) |
|
|
58
|
+
| `job` (URN) | property `bq-job` (consumer routes on this) |
|
|
59
|
+
| `trace_id` | property `bq-trace-id` |
|
|
60
|
+
| `meta.id` | property `bq-message-id` |
|
|
61
|
+
| `meta.schema_version` | property `bq-schema-version` |
|
|
62
|
+
| `meta.lang` | property `bq-source-lang` |
|
|
63
|
+
| `meta.created_at` | publish time (mirror; body authoritative) |
|
|
64
|
+
| `attempts` | property `bq-attempts` (authoritative), cross-checked against `getRedeliveryCount()` |
|
|
65
|
+
| reserve / ack / retry | `acknowledge` / `negativeAcknowledge` |
|
|
66
|
+
|
|
67
|
+
Pulsar properties are string→string, so `bq-attempts` carries the contract `attempts` and is
|
|
68
|
+
**authoritative**. The consumer reconciles to `max(bq-attempts, getRedeliveryCount())`:
|
|
69
|
+
`getRedeliveryCount()` is 0-based (0 on first delivery) so it maps directly with **no −1**,
|
|
70
|
+
and the `max` never lowers a higher body count — so a republish-driven retry and a native
|
|
71
|
+
redelivery both converge on the same number. A throwing handler `negativeAcknowledge`s, so
|
|
72
|
+
the broker redelivers (at-least-once). The poll loop never stops on a bad message — observe
|
|
73
|
+
via `onError` / `onUnknownUrn`. The envelope is unchanged (`schema_version` stays `1`);
|
|
74
|
+
Pulsar is purely additive.
|
|
75
|
+
|
|
76
|
+
The producer/consumer are injected, so the unit tests use fakes — no Pulsar, no broker. Dual
|
|
77
|
+
ESM + CJS.
|
|
78
|
+
|
|
79
|
+
## License
|
|
80
|
+
|
|
81
|
+
MIT
|
package/dist/index.cjs
ADDED
|
@@ -0,0 +1,154 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __defProp = Object.defineProperty;
|
|
3
|
+
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
|
|
4
|
+
var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
5
|
+
var __hasOwnProp = Object.prototype.hasOwnProperty;
|
|
6
|
+
var __export = (target, all) => {
|
|
7
|
+
for (var name in all)
|
|
8
|
+
__defProp(target, name, { get: all[name], enumerable: true });
|
|
9
|
+
};
|
|
10
|
+
var __copyProps = (to, from, except, desc) => {
|
|
11
|
+
if (from && typeof from === "object" || typeof from === "function") {
|
|
12
|
+
for (let key of __getOwnPropNames(from))
|
|
13
|
+
if (!__hasOwnProp.call(to, key) && key !== except)
|
|
14
|
+
__defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
|
|
15
|
+
}
|
|
16
|
+
return to;
|
|
17
|
+
};
|
|
18
|
+
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
|
|
19
|
+
|
|
20
|
+
// src/index.ts
|
|
21
|
+
var index_exports = {};
|
|
22
|
+
__export(index_exports, {
|
|
23
|
+
PulsarConsumer: () => PulsarConsumer,
|
|
24
|
+
PulsarPublisher: () => PulsarPublisher,
|
|
25
|
+
pulsarProperties: () => pulsarProperties,
|
|
26
|
+
toPulsarMessage: () => toPulsarMessage
|
|
27
|
+
});
|
|
28
|
+
module.exports = __toCommonJS(index_exports);
|
|
29
|
+
var import_core = require("@babelqueue/core");
|
|
30
|
+
function topicName(topic) {
|
|
31
|
+
const slash = topic.lastIndexOf("/");
|
|
32
|
+
return slash >= 0 ? topic.slice(slash + 1) : topic;
|
|
33
|
+
}
|
|
34
|
+
function pulsarProperties(envelope) {
|
|
35
|
+
const properties = {};
|
|
36
|
+
if (envelope.job) properties["bq-job"] = envelope.job;
|
|
37
|
+
if (envelope.trace_id) properties["bq-trace-id"] = envelope.trace_id;
|
|
38
|
+
if (envelope.meta.id) properties["bq-message-id"] = envelope.meta.id;
|
|
39
|
+
if (envelope.meta.schema_version != null) {
|
|
40
|
+
properties["bq-schema-version"] = String(envelope.meta.schema_version);
|
|
41
|
+
}
|
|
42
|
+
if (envelope.meta.lang) properties["bq-source-lang"] = envelope.meta.lang;
|
|
43
|
+
properties["bq-attempts"] = String(envelope.attempts ?? 0);
|
|
44
|
+
return properties;
|
|
45
|
+
}
|
|
46
|
+
function toPulsarMessage(envelope) {
|
|
47
|
+
return {
|
|
48
|
+
data: Buffer.from(import_core.EnvelopeCodec.encode(envelope), "utf8"),
|
|
49
|
+
properties: pulsarProperties(envelope)
|
|
50
|
+
};
|
|
51
|
+
}
|
|
52
|
+
var PulsarPublisher = class {
|
|
53
|
+
constructor(producer) {
|
|
54
|
+
this.producer = producer;
|
|
55
|
+
}
|
|
56
|
+
producer;
|
|
57
|
+
/**
|
|
58
|
+
* Build the canonical envelope for `(urn, data)`, send it with the §5 property projection,
|
|
59
|
+
* and return the message id (`meta.id`). A positive `delayMs` schedules native delayed
|
|
60
|
+
* delivery via `deliverAfter` and mirrors `bq-delay`.
|
|
61
|
+
*/
|
|
62
|
+
async publish(urn, data, options = {}) {
|
|
63
|
+
const envelope = import_core.EnvelopeCodec.make(urn, data, {
|
|
64
|
+
queue: topicName(this.producer.getTopic()),
|
|
65
|
+
traceId: options.traceId
|
|
66
|
+
});
|
|
67
|
+
const message = toPulsarMessage(envelope);
|
|
68
|
+
if (options.delayMs != null && options.delayMs > 0) {
|
|
69
|
+
message.properties = { ...message.properties, "bq-delay": String(options.delayMs) };
|
|
70
|
+
message.deliverAfter = options.delayMs;
|
|
71
|
+
}
|
|
72
|
+
await this.producer.send(message);
|
|
73
|
+
return envelope.meta.id;
|
|
74
|
+
}
|
|
75
|
+
};
|
|
76
|
+
function isReceiveTimeout(error) {
|
|
77
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
78
|
+
return /timeout/i.test(message);
|
|
79
|
+
}
|
|
80
|
+
function decodePayload(data) {
|
|
81
|
+
return typeof data === "string" ? data : data.toString("utf8");
|
|
82
|
+
}
|
|
83
|
+
var PulsarConsumer = class {
|
|
84
|
+
constructor(consumer, handlers, options = {}) {
|
|
85
|
+
this.consumer = consumer;
|
|
86
|
+
this.handlers = handlers;
|
|
87
|
+
this.options = options;
|
|
88
|
+
}
|
|
89
|
+
consumer;
|
|
90
|
+
handlers;
|
|
91
|
+
options;
|
|
92
|
+
/** Receive one message (up to the receive timeout), route + settle it. Returns 1, or 0 on timeout. */
|
|
93
|
+
async poll() {
|
|
94
|
+
let message;
|
|
95
|
+
try {
|
|
96
|
+
message = await this.consumer.receive(this.options.receiveTimeoutMs ?? 1e3);
|
|
97
|
+
} catch (error) {
|
|
98
|
+
if (isReceiveTimeout(error)) return 0;
|
|
99
|
+
throw error;
|
|
100
|
+
}
|
|
101
|
+
await this.handle(message);
|
|
102
|
+
return 1;
|
|
103
|
+
}
|
|
104
|
+
/** Poll until `signal` aborts. */
|
|
105
|
+
async run(signal) {
|
|
106
|
+
while (signal?.aborted !== true) {
|
|
107
|
+
await this.poll();
|
|
108
|
+
}
|
|
109
|
+
}
|
|
110
|
+
async handle(message) {
|
|
111
|
+
const envelope = import_core.EnvelopeCodec.decode(decodePayload(message.getData()));
|
|
112
|
+
const redeliveryCount = typeof message.getRedeliveryCount === "function" ? message.getRedeliveryCount() : 0;
|
|
113
|
+
const current = typeof envelope.attempts === "number" ? envelope.attempts : 0;
|
|
114
|
+
if (redeliveryCount > current) {
|
|
115
|
+
envelope.attempts = redeliveryCount;
|
|
116
|
+
}
|
|
117
|
+
if (!import_core.EnvelopeCodec.accepts(envelope)) {
|
|
118
|
+
this.options.onError?.(
|
|
119
|
+
new import_core.BabelQueueError("Rejected a non-conformant BabelQueue envelope from Apache Pulsar."),
|
|
120
|
+
envelope,
|
|
121
|
+
message
|
|
122
|
+
);
|
|
123
|
+
await this.consumer.negativeAcknowledge(message);
|
|
124
|
+
return;
|
|
125
|
+
}
|
|
126
|
+
const urn = import_core.EnvelopeCodec.urn(envelope);
|
|
127
|
+
const handler = this.handlers[urn];
|
|
128
|
+
if (!handler) {
|
|
129
|
+
if (this.options.onUnknownUrn) {
|
|
130
|
+
await this.options.onUnknownUrn(envelope, message);
|
|
131
|
+
await this.consumer.acknowledge(message);
|
|
132
|
+
} else {
|
|
133
|
+
this.options.onError?.(new import_core.UnknownUrnError(urn), envelope, message);
|
|
134
|
+
await this.consumer.negativeAcknowledge(message);
|
|
135
|
+
}
|
|
136
|
+
return;
|
|
137
|
+
}
|
|
138
|
+
try {
|
|
139
|
+
await handler(envelope, message);
|
|
140
|
+
await this.consumer.acknowledge(message);
|
|
141
|
+
} catch (error) {
|
|
142
|
+
this.options.onError?.(error, envelope, message);
|
|
143
|
+
await this.consumer.negativeAcknowledge(message);
|
|
144
|
+
}
|
|
145
|
+
}
|
|
146
|
+
};
|
|
147
|
+
// Annotate the CommonJS export names for ESM import in node:
|
|
148
|
+
0 && (module.exports = {
|
|
149
|
+
PulsarConsumer,
|
|
150
|
+
PulsarPublisher,
|
|
151
|
+
pulsarProperties,
|
|
152
|
+
toPulsarMessage
|
|
153
|
+
});
|
|
154
|
+
//# sourceMappingURL=index.cjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/index.ts"],"sourcesContent":["/**\n * Apache Pulsar adapter for BabelQueue.\n *\n * A canonical-envelope **publisher** and a URN-routed **consumer** over Apache Pulsar, so a\n * Pulsar-based Node service speaks the same contract (envelope shape, URN identity, trace\n * propagation) as the .NET, Java, Python and Go SDKs.\n *\n * import Pulsar from \"pulsar-client\";\n * import { PulsarPublisher, PulsarConsumer } from \"@babelqueue/pulsar\";\n *\n * const client = new Pulsar.Client({ serviceUrl: \"pulsar://localhost:6650\" });\n *\n * const producer = await client.createProducer({ topic: \"orders\" });\n * await new PulsarPublisher(producer).publish(\"urn:babel:orders:created\", { order_id: 1042 });\n *\n * const sub = await client.subscribe({\n * topic: \"orders\", subscription: \"babelqueue\", subscriptionType: \"Shared\",\n * });\n * const consumer = new PulsarConsumer(sub, {\n * \"urn:babel:orders:created\": async (env) => { ... },\n * });\n * await consumer.run();\n *\n * This implements §5 of the broker-bindings contract: the canonical envelope is the message\n * payload, projected onto native Pulsar message properties (string→string) — `bq-job` = URN,\n * `bq-trace-id` = trace_id, `bq-message-id` = meta.id, plus `bq-schema-version` /\n * `bq-source-lang` / `bq-attempts`. The envelope is unchanged (`schema_version` stays 1);\n * Pulsar is purely additive. A failed handler `negativeAcknowledge`s the message, so it is\n * redelivered (at-least-once) and `getRedeliveryCount()` is incremented; the authoritative\n * attempt count is the body's `bq-attempts`, reconciled to `max(bq-attempts,\n * getRedeliveryCount())` — no −1, because Pulsar's redelivery count is 0-based.\n */\n\nimport { BabelQueueError, EnvelopeCodec, UnknownUrnError } from \"@babelqueue/core\";\nimport type { Envelope, IncomingEnvelope } from \"@babelqueue/core\";\n\n// --- Minimal Pulsar shapes (a structural subset of pulsar-client) --------------\n\n/** A message to send (structural subset of pulsar-client `ProducerMessage`). */\nexport interface PulsarProducerMessage {\n data: Buffer;\n properties?: { [key: string]: string };\n /** Relative delay in milliseconds before the message becomes available (native `deliverAfter`). */\n deliverAfter?: number;\n}\n\n/**\n * The subset of the Pulsar producer this adapter calls. A `Producer` from `pulsar-client`\n * satisfies it structurally; a fake satisfies it in tests.\n */\nexport interface PulsarProducer {\n getTopic(): string;\n send(message: PulsarProducerMessage): Promise<unknown>;\n}\n\n/** A received message (structural subset of pulsar-client `Message`). */\nexport interface PulsarReceivedMessage {\n getData(): Buffer | string;\n getProperties(): { [key: string]: string };\n getRedeliveryCount(): number;\n}\n\n/**\n * The subset of the Pulsar consumer this adapter calls. A `Consumer` from `pulsar-client`\n * satisfies it structurally; a fake satisfies it in tests.\n */\nexport interface PulsarConsumerClient {\n receive(timeoutMs?: number): Promise<PulsarReceivedMessage>;\n acknowledge(message: PulsarReceivedMessage): Promise<void> | void;\n negativeAcknowledge(message: PulsarReceivedMessage): Promise<void> | void;\n}\n\n// --- Property projection (contract §5.2) ---------------------------------------\n\n/** Bare topic name (the part after the last `/`), used as `meta.queue`. */\nfunction topicName(topic: string): string {\n const slash = topic.lastIndexOf(\"/\");\n return slash >= 0 ? topic.slice(slash + 1) : topic;\n}\n\n/**\n * Project the envelope's contract fields onto native Pulsar message properties\n * (string→string): `bq-job` = URN, `bq-trace-id` = trace_id, `bq-message-id` = meta.id, plus\n * `bq-schema-version` / `bq-source-lang` / `bq-attempts`. The body stays authoritative.\n */\nexport function pulsarProperties(envelope: Envelope): { [key: string]: string } {\n const properties: { [key: string]: string } = {};\n if (envelope.job) properties[\"bq-job\"] = envelope.job;\n if (envelope.trace_id) properties[\"bq-trace-id\"] = envelope.trace_id;\n if (envelope.meta.id) properties[\"bq-message-id\"] = envelope.meta.id;\n if (envelope.meta.schema_version != null) {\n properties[\"bq-schema-version\"] = String(envelope.meta.schema_version);\n }\n if (envelope.meta.lang) properties[\"bq-source-lang\"] = envelope.meta.lang;\n properties[\"bq-attempts\"] = String(envelope.attempts ?? 0);\n return properties;\n}\n\n/** Project the envelope onto a native Pulsar producer message (payload + properties). */\nexport function toPulsarMessage(envelope: Envelope): PulsarProducerMessage {\n return {\n data: Buffer.from(EnvelopeCodec.encode(envelope), \"utf8\"),\n properties: pulsarProperties(envelope),\n };\n}\n\n// --- Publisher -----------------------------------------------------------------\n\n/** Options for {@link PulsarPublisher.publish}. */\nexport interface PublishOptions {\n /** Reuse an existing trace id (trace continuation). */\n traceId?: string;\n /** Schedule native delayed delivery this many milliseconds from now (`deliverAfter`). */\n delayMs?: number;\n}\n\n/** Sends canonical-envelope messages to one Pulsar topic with the §5 property projection. */\nexport class PulsarPublisher {\n constructor(private readonly producer: PulsarProducer) {}\n\n /**\n * Build the canonical envelope for `(urn, data)`, send it with the §5 property projection,\n * and return the message id (`meta.id`). A positive `delayMs` schedules native delayed\n * delivery via `deliverAfter` and mirrors `bq-delay`.\n */\n async publish(\n urn: string,\n data: Record<string, unknown>,\n options: PublishOptions = {},\n ): Promise<string> {\n const envelope = EnvelopeCodec.make(urn, data, {\n queue: topicName(this.producer.getTopic()),\n traceId: options.traceId,\n });\n const message = toPulsarMessage(envelope);\n\n if (options.delayMs != null && options.delayMs > 0) {\n message.properties = { ...message.properties, \"bq-delay\": String(options.delayMs) };\n message.deliverAfter = options.delayMs;\n }\n\n await this.producer.send(message);\n return envelope.meta.id;\n }\n}\n\n// --- Consumer ------------------------------------------------------------------\n\n/** A URN handler. Receives the validated envelope and the raw Pulsar message. */\nexport type BabelHandler = (envelope: Envelope, message: PulsarReceivedMessage) => unknown | Promise<unknown>;\n\n/** A map of URN → handler. */\nexport type BabelHandlers = Record<string, BabelHandler>;\n\n/** Options for {@link PulsarConsumer}. */\nexport interface PulsarConsumerOptions {\n /** Called instead of erroring when a message's URN has no handler (then the message is acknowledged). */\n onUnknownUrn?: (envelope: IncomingEnvelope, message: PulsarReceivedMessage) => unknown | Promise<unknown>;\n /** Called for a non-conformant envelope, an unmapped URN (no `onUnknownUrn`), or a throwing handler. The loop never stops. */\n onError?: (error: unknown, envelope: IncomingEnvelope, message: PulsarReceivedMessage) => void;\n /** Per-receive timeout in ms (default 1000). A timeout yields an empty poll, not an error. */\n receiveTimeoutMs?: number;\n}\n\nfunction isReceiveTimeout(error: unknown): boolean {\n const message = error instanceof Error ? error.message : String(error);\n return /timeout/i.test(message);\n}\n\nfunction decodePayload(data: Buffer | string): string {\n return typeof data === \"string\" ? data : data.toString(\"utf8\");\n}\n\n/**\n * Receives from a Pulsar subscription, decodes + validates each message, routes it to the\n * handler for its URN, and acknowledges it on success. A throwing handler\n * `negativeAcknowledge`s the message — the broker redelivers it and increments\n * `getRedeliveryCount()` (at-least-once); `attempts` is reconciled to\n * `max(bq-attempts, getRedeliveryCount())` for the handler.\n */\nexport class PulsarConsumer {\n constructor(\n private readonly consumer: PulsarConsumerClient,\n private readonly handlers: BabelHandlers,\n private readonly options: PulsarConsumerOptions = {},\n ) {}\n\n /** Receive one message (up to the receive timeout), route + settle it. Returns 1, or 0 on timeout. */\n async poll(): Promise<number> {\n let message: PulsarReceivedMessage;\n try {\n message = await this.consumer.receive(this.options.receiveTimeoutMs ?? 1000);\n } catch (error) {\n if (isReceiveTimeout(error)) return 0;\n throw error;\n }\n await this.handle(message);\n return 1;\n }\n\n /** Poll until `signal` aborts. */\n async run(signal?: AbortSignal): Promise<void> {\n while (signal?.aborted !== true) {\n await this.poll();\n }\n }\n\n private async handle(message: PulsarReceivedMessage): Promise<void> {\n const envelope = EnvelopeCodec.decode(decodePayload(message.getData()));\n\n // attempts = max(current, getRedeliveryCount()): the redelivery count is 0-based, so it\n // maps directly with no −1; the max never lowers a higher body count republished from\n // another SDK (the Go/Python transports retry by re-sending with attempts+1).\n const redeliveryCount = typeof message.getRedeliveryCount === \"function\" ? message.getRedeliveryCount() : 0;\n const current = typeof envelope.attempts === \"number\" ? envelope.attempts : 0;\n if (redeliveryCount > current) {\n envelope.attempts = redeliveryCount;\n }\n\n if (!EnvelopeCodec.accepts(envelope)) {\n this.options.onError?.(\n new BabelQueueError(\"Rejected a non-conformant BabelQueue envelope from Apache Pulsar.\"),\n envelope,\n message,\n );\n await this.consumer.negativeAcknowledge(message);\n return;\n }\n\n const urn = EnvelopeCodec.urn(envelope);\n const handler = this.handlers[urn];\n if (!handler) {\n if (this.options.onUnknownUrn) {\n await this.options.onUnknownUrn(envelope, message);\n await this.consumer.acknowledge(message);\n } else {\n this.options.onError?.(new UnknownUrnError(urn), envelope, message);\n await this.consumer.negativeAcknowledge(message);\n }\n return;\n }\n\n try {\n await handler(envelope as Envelope, message);\n await this.consumer.acknowledge(message);\n } catch (error) {\n // Negative-ack releases the message — the broker redelivers and increments the count.\n this.options.onError?.(error, envelope, message);\n await this.consumer.negativeAcknowledge(message);\n }\n }\n}\n"],"mappings":"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","names":[]}
|
package/dist/index.d.cts
ADDED
|
@@ -0,0 +1,130 @@
|
|
|
1
|
+
import { Envelope, IncomingEnvelope } from '@babelqueue/core';
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Apache Pulsar adapter for BabelQueue.
|
|
5
|
+
*
|
|
6
|
+
* A canonical-envelope **publisher** and a URN-routed **consumer** over Apache Pulsar, so a
|
|
7
|
+
* Pulsar-based Node service speaks the same contract (envelope shape, URN identity, trace
|
|
8
|
+
* propagation) as the .NET, Java, Python and Go SDKs.
|
|
9
|
+
*
|
|
10
|
+
* import Pulsar from "pulsar-client";
|
|
11
|
+
* import { PulsarPublisher, PulsarConsumer } from "@babelqueue/pulsar";
|
|
12
|
+
*
|
|
13
|
+
* const client = new Pulsar.Client({ serviceUrl: "pulsar://localhost:6650" });
|
|
14
|
+
*
|
|
15
|
+
* const producer = await client.createProducer({ topic: "orders" });
|
|
16
|
+
* await new PulsarPublisher(producer).publish("urn:babel:orders:created", { order_id: 1042 });
|
|
17
|
+
*
|
|
18
|
+
* const sub = await client.subscribe({
|
|
19
|
+
* topic: "orders", subscription: "babelqueue", subscriptionType: "Shared",
|
|
20
|
+
* });
|
|
21
|
+
* const consumer = new PulsarConsumer(sub, {
|
|
22
|
+
* "urn:babel:orders:created": async (env) => { ... },
|
|
23
|
+
* });
|
|
24
|
+
* await consumer.run();
|
|
25
|
+
*
|
|
26
|
+
* This implements §5 of the broker-bindings contract: the canonical envelope is the message
|
|
27
|
+
* payload, projected onto native Pulsar message properties (string→string) — `bq-job` = URN,
|
|
28
|
+
* `bq-trace-id` = trace_id, `bq-message-id` = meta.id, plus `bq-schema-version` /
|
|
29
|
+
* `bq-source-lang` / `bq-attempts`. The envelope is unchanged (`schema_version` stays 1);
|
|
30
|
+
* Pulsar is purely additive. A failed handler `negativeAcknowledge`s the message, so it is
|
|
31
|
+
* redelivered (at-least-once) and `getRedeliveryCount()` is incremented; the authoritative
|
|
32
|
+
* attempt count is the body's `bq-attempts`, reconciled to `max(bq-attempts,
|
|
33
|
+
* getRedeliveryCount())` — no −1, because Pulsar's redelivery count is 0-based.
|
|
34
|
+
*/
|
|
35
|
+
|
|
36
|
+
/** A message to send (structural subset of pulsar-client `ProducerMessage`). */
|
|
37
|
+
interface PulsarProducerMessage {
|
|
38
|
+
data: Buffer;
|
|
39
|
+
properties?: {
|
|
40
|
+
[key: string]: string;
|
|
41
|
+
};
|
|
42
|
+
/** Relative delay in milliseconds before the message becomes available (native `deliverAfter`). */
|
|
43
|
+
deliverAfter?: number;
|
|
44
|
+
}
|
|
45
|
+
/**
|
|
46
|
+
* The subset of the Pulsar producer this adapter calls. A `Producer` from `pulsar-client`
|
|
47
|
+
* satisfies it structurally; a fake satisfies it in tests.
|
|
48
|
+
*/
|
|
49
|
+
interface PulsarProducer {
|
|
50
|
+
getTopic(): string;
|
|
51
|
+
send(message: PulsarProducerMessage): Promise<unknown>;
|
|
52
|
+
}
|
|
53
|
+
/** A received message (structural subset of pulsar-client `Message`). */
|
|
54
|
+
interface PulsarReceivedMessage {
|
|
55
|
+
getData(): Buffer | string;
|
|
56
|
+
getProperties(): {
|
|
57
|
+
[key: string]: string;
|
|
58
|
+
};
|
|
59
|
+
getRedeliveryCount(): number;
|
|
60
|
+
}
|
|
61
|
+
/**
|
|
62
|
+
* The subset of the Pulsar consumer this adapter calls. A `Consumer` from `pulsar-client`
|
|
63
|
+
* satisfies it structurally; a fake satisfies it in tests.
|
|
64
|
+
*/
|
|
65
|
+
interface PulsarConsumerClient {
|
|
66
|
+
receive(timeoutMs?: number): Promise<PulsarReceivedMessage>;
|
|
67
|
+
acknowledge(message: PulsarReceivedMessage): Promise<void> | void;
|
|
68
|
+
negativeAcknowledge(message: PulsarReceivedMessage): Promise<void> | void;
|
|
69
|
+
}
|
|
70
|
+
/**
|
|
71
|
+
* Project the envelope's contract fields onto native Pulsar message properties
|
|
72
|
+
* (string→string): `bq-job` = URN, `bq-trace-id` = trace_id, `bq-message-id` = meta.id, plus
|
|
73
|
+
* `bq-schema-version` / `bq-source-lang` / `bq-attempts`. The body stays authoritative.
|
|
74
|
+
*/
|
|
75
|
+
declare function pulsarProperties(envelope: Envelope): {
|
|
76
|
+
[key: string]: string;
|
|
77
|
+
};
|
|
78
|
+
/** Project the envelope onto a native Pulsar producer message (payload + properties). */
|
|
79
|
+
declare function toPulsarMessage(envelope: Envelope): PulsarProducerMessage;
|
|
80
|
+
/** Options for {@link PulsarPublisher.publish}. */
|
|
81
|
+
interface PublishOptions {
|
|
82
|
+
/** Reuse an existing trace id (trace continuation). */
|
|
83
|
+
traceId?: string;
|
|
84
|
+
/** Schedule native delayed delivery this many milliseconds from now (`deliverAfter`). */
|
|
85
|
+
delayMs?: number;
|
|
86
|
+
}
|
|
87
|
+
/** Sends canonical-envelope messages to one Pulsar topic with the §5 property projection. */
|
|
88
|
+
declare class PulsarPublisher {
|
|
89
|
+
private readonly producer;
|
|
90
|
+
constructor(producer: PulsarProducer);
|
|
91
|
+
/**
|
|
92
|
+
* Build the canonical envelope for `(urn, data)`, send it with the §5 property projection,
|
|
93
|
+
* and return the message id (`meta.id`). A positive `delayMs` schedules native delayed
|
|
94
|
+
* delivery via `deliverAfter` and mirrors `bq-delay`.
|
|
95
|
+
*/
|
|
96
|
+
publish(urn: string, data: Record<string, unknown>, options?: PublishOptions): Promise<string>;
|
|
97
|
+
}
|
|
98
|
+
/** A URN handler. Receives the validated envelope and the raw Pulsar message. */
|
|
99
|
+
type BabelHandler = (envelope: Envelope, message: PulsarReceivedMessage) => unknown | Promise<unknown>;
|
|
100
|
+
/** A map of URN → handler. */
|
|
101
|
+
type BabelHandlers = Record<string, BabelHandler>;
|
|
102
|
+
/** Options for {@link PulsarConsumer}. */
|
|
103
|
+
interface PulsarConsumerOptions {
|
|
104
|
+
/** Called instead of erroring when a message's URN has no handler (then the message is acknowledged). */
|
|
105
|
+
onUnknownUrn?: (envelope: IncomingEnvelope, message: PulsarReceivedMessage) => unknown | Promise<unknown>;
|
|
106
|
+
/** Called for a non-conformant envelope, an unmapped URN (no `onUnknownUrn`), or a throwing handler. The loop never stops. */
|
|
107
|
+
onError?: (error: unknown, envelope: IncomingEnvelope, message: PulsarReceivedMessage) => void;
|
|
108
|
+
/** Per-receive timeout in ms (default 1000). A timeout yields an empty poll, not an error. */
|
|
109
|
+
receiveTimeoutMs?: number;
|
|
110
|
+
}
|
|
111
|
+
/**
|
|
112
|
+
* Receives from a Pulsar subscription, decodes + validates each message, routes it to the
|
|
113
|
+
* handler for its URN, and acknowledges it on success. A throwing handler
|
|
114
|
+
* `negativeAcknowledge`s the message — the broker redelivers it and increments
|
|
115
|
+
* `getRedeliveryCount()` (at-least-once); `attempts` is reconciled to
|
|
116
|
+
* `max(bq-attempts, getRedeliveryCount())` for the handler.
|
|
117
|
+
*/
|
|
118
|
+
declare class PulsarConsumer {
|
|
119
|
+
private readonly consumer;
|
|
120
|
+
private readonly handlers;
|
|
121
|
+
private readonly options;
|
|
122
|
+
constructor(consumer: PulsarConsumerClient, handlers: BabelHandlers, options?: PulsarConsumerOptions);
|
|
123
|
+
/** Receive one message (up to the receive timeout), route + settle it. Returns 1, or 0 on timeout. */
|
|
124
|
+
poll(): Promise<number>;
|
|
125
|
+
/** Poll until `signal` aborts. */
|
|
126
|
+
run(signal?: AbortSignal): Promise<void>;
|
|
127
|
+
private handle;
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
export { type BabelHandler, type BabelHandlers, type PublishOptions, PulsarConsumer, type PulsarConsumerClient, type PulsarConsumerOptions, type PulsarProducer, type PulsarProducerMessage, PulsarPublisher, type PulsarReceivedMessage, pulsarProperties, toPulsarMessage };
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,130 @@
|
|
|
1
|
+
import { Envelope, IncomingEnvelope } from '@babelqueue/core';
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Apache Pulsar adapter for BabelQueue.
|
|
5
|
+
*
|
|
6
|
+
* A canonical-envelope **publisher** and a URN-routed **consumer** over Apache Pulsar, so a
|
|
7
|
+
* Pulsar-based Node service speaks the same contract (envelope shape, URN identity, trace
|
|
8
|
+
* propagation) as the .NET, Java, Python and Go SDKs.
|
|
9
|
+
*
|
|
10
|
+
* import Pulsar from "pulsar-client";
|
|
11
|
+
* import { PulsarPublisher, PulsarConsumer } from "@babelqueue/pulsar";
|
|
12
|
+
*
|
|
13
|
+
* const client = new Pulsar.Client({ serviceUrl: "pulsar://localhost:6650" });
|
|
14
|
+
*
|
|
15
|
+
* const producer = await client.createProducer({ topic: "orders" });
|
|
16
|
+
* await new PulsarPublisher(producer).publish("urn:babel:orders:created", { order_id: 1042 });
|
|
17
|
+
*
|
|
18
|
+
* const sub = await client.subscribe({
|
|
19
|
+
* topic: "orders", subscription: "babelqueue", subscriptionType: "Shared",
|
|
20
|
+
* });
|
|
21
|
+
* const consumer = new PulsarConsumer(sub, {
|
|
22
|
+
* "urn:babel:orders:created": async (env) => { ... },
|
|
23
|
+
* });
|
|
24
|
+
* await consumer.run();
|
|
25
|
+
*
|
|
26
|
+
* This implements §5 of the broker-bindings contract: the canonical envelope is the message
|
|
27
|
+
* payload, projected onto native Pulsar message properties (string→string) — `bq-job` = URN,
|
|
28
|
+
* `bq-trace-id` = trace_id, `bq-message-id` = meta.id, plus `bq-schema-version` /
|
|
29
|
+
* `bq-source-lang` / `bq-attempts`. The envelope is unchanged (`schema_version` stays 1);
|
|
30
|
+
* Pulsar is purely additive. A failed handler `negativeAcknowledge`s the message, so it is
|
|
31
|
+
* redelivered (at-least-once) and `getRedeliveryCount()` is incremented; the authoritative
|
|
32
|
+
* attempt count is the body's `bq-attempts`, reconciled to `max(bq-attempts,
|
|
33
|
+
* getRedeliveryCount())` — no −1, because Pulsar's redelivery count is 0-based.
|
|
34
|
+
*/
|
|
35
|
+
|
|
36
|
+
/** A message to send (structural subset of pulsar-client `ProducerMessage`). */
|
|
37
|
+
interface PulsarProducerMessage {
|
|
38
|
+
data: Buffer;
|
|
39
|
+
properties?: {
|
|
40
|
+
[key: string]: string;
|
|
41
|
+
};
|
|
42
|
+
/** Relative delay in milliseconds before the message becomes available (native `deliverAfter`). */
|
|
43
|
+
deliverAfter?: number;
|
|
44
|
+
}
|
|
45
|
+
/**
|
|
46
|
+
* The subset of the Pulsar producer this adapter calls. A `Producer` from `pulsar-client`
|
|
47
|
+
* satisfies it structurally; a fake satisfies it in tests.
|
|
48
|
+
*/
|
|
49
|
+
interface PulsarProducer {
|
|
50
|
+
getTopic(): string;
|
|
51
|
+
send(message: PulsarProducerMessage): Promise<unknown>;
|
|
52
|
+
}
|
|
53
|
+
/** A received message (structural subset of pulsar-client `Message`). */
|
|
54
|
+
interface PulsarReceivedMessage {
|
|
55
|
+
getData(): Buffer | string;
|
|
56
|
+
getProperties(): {
|
|
57
|
+
[key: string]: string;
|
|
58
|
+
};
|
|
59
|
+
getRedeliveryCount(): number;
|
|
60
|
+
}
|
|
61
|
+
/**
|
|
62
|
+
* The subset of the Pulsar consumer this adapter calls. A `Consumer` from `pulsar-client`
|
|
63
|
+
* satisfies it structurally; a fake satisfies it in tests.
|
|
64
|
+
*/
|
|
65
|
+
interface PulsarConsumerClient {
|
|
66
|
+
receive(timeoutMs?: number): Promise<PulsarReceivedMessage>;
|
|
67
|
+
acknowledge(message: PulsarReceivedMessage): Promise<void> | void;
|
|
68
|
+
negativeAcknowledge(message: PulsarReceivedMessage): Promise<void> | void;
|
|
69
|
+
}
|
|
70
|
+
/**
|
|
71
|
+
* Project the envelope's contract fields onto native Pulsar message properties
|
|
72
|
+
* (string→string): `bq-job` = URN, `bq-trace-id` = trace_id, `bq-message-id` = meta.id, plus
|
|
73
|
+
* `bq-schema-version` / `bq-source-lang` / `bq-attempts`. The body stays authoritative.
|
|
74
|
+
*/
|
|
75
|
+
declare function pulsarProperties(envelope: Envelope): {
|
|
76
|
+
[key: string]: string;
|
|
77
|
+
};
|
|
78
|
+
/** Project the envelope onto a native Pulsar producer message (payload + properties). */
|
|
79
|
+
declare function toPulsarMessage(envelope: Envelope): PulsarProducerMessage;
|
|
80
|
+
/** Options for {@link PulsarPublisher.publish}. */
|
|
81
|
+
interface PublishOptions {
|
|
82
|
+
/** Reuse an existing trace id (trace continuation). */
|
|
83
|
+
traceId?: string;
|
|
84
|
+
/** Schedule native delayed delivery this many milliseconds from now (`deliverAfter`). */
|
|
85
|
+
delayMs?: number;
|
|
86
|
+
}
|
|
87
|
+
/** Sends canonical-envelope messages to one Pulsar topic with the §5 property projection. */
|
|
88
|
+
declare class PulsarPublisher {
|
|
89
|
+
private readonly producer;
|
|
90
|
+
constructor(producer: PulsarProducer);
|
|
91
|
+
/**
|
|
92
|
+
* Build the canonical envelope for `(urn, data)`, send it with the §5 property projection,
|
|
93
|
+
* and return the message id (`meta.id`). A positive `delayMs` schedules native delayed
|
|
94
|
+
* delivery via `deliverAfter` and mirrors `bq-delay`.
|
|
95
|
+
*/
|
|
96
|
+
publish(urn: string, data: Record<string, unknown>, options?: PublishOptions): Promise<string>;
|
|
97
|
+
}
|
|
98
|
+
/** A URN handler. Receives the validated envelope and the raw Pulsar message. */
|
|
99
|
+
type BabelHandler = (envelope: Envelope, message: PulsarReceivedMessage) => unknown | Promise<unknown>;
|
|
100
|
+
/** A map of URN → handler. */
|
|
101
|
+
type BabelHandlers = Record<string, BabelHandler>;
|
|
102
|
+
/** Options for {@link PulsarConsumer}. */
|
|
103
|
+
interface PulsarConsumerOptions {
|
|
104
|
+
/** Called instead of erroring when a message's URN has no handler (then the message is acknowledged). */
|
|
105
|
+
onUnknownUrn?: (envelope: IncomingEnvelope, message: PulsarReceivedMessage) => unknown | Promise<unknown>;
|
|
106
|
+
/** Called for a non-conformant envelope, an unmapped URN (no `onUnknownUrn`), or a throwing handler. The loop never stops. */
|
|
107
|
+
onError?: (error: unknown, envelope: IncomingEnvelope, message: PulsarReceivedMessage) => void;
|
|
108
|
+
/** Per-receive timeout in ms (default 1000). A timeout yields an empty poll, not an error. */
|
|
109
|
+
receiveTimeoutMs?: number;
|
|
110
|
+
}
|
|
111
|
+
/**
|
|
112
|
+
* Receives from a Pulsar subscription, decodes + validates each message, routes it to the
|
|
113
|
+
* handler for its URN, and acknowledges it on success. A throwing handler
|
|
114
|
+
* `negativeAcknowledge`s the message — the broker redelivers it and increments
|
|
115
|
+
* `getRedeliveryCount()` (at-least-once); `attempts` is reconciled to
|
|
116
|
+
* `max(bq-attempts, getRedeliveryCount())` for the handler.
|
|
117
|
+
*/
|
|
118
|
+
declare class PulsarConsumer {
|
|
119
|
+
private readonly consumer;
|
|
120
|
+
private readonly handlers;
|
|
121
|
+
private readonly options;
|
|
122
|
+
constructor(consumer: PulsarConsumerClient, handlers: BabelHandlers, options?: PulsarConsumerOptions);
|
|
123
|
+
/** Receive one message (up to the receive timeout), route + settle it. Returns 1, or 0 on timeout. */
|
|
124
|
+
poll(): Promise<number>;
|
|
125
|
+
/** Poll until `signal` aborts. */
|
|
126
|
+
run(signal?: AbortSignal): Promise<void>;
|
|
127
|
+
private handle;
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
export { type BabelHandler, type BabelHandlers, type PublishOptions, PulsarConsumer, type PulsarConsumerClient, type PulsarConsumerOptions, type PulsarProducer, type PulsarProducerMessage, PulsarPublisher, type PulsarReceivedMessage, pulsarProperties, toPulsarMessage };
|
package/dist/index.js
ADDED
|
@@ -0,0 +1,126 @@
|
|
|
1
|
+
// src/index.ts
|
|
2
|
+
import { BabelQueueError, EnvelopeCodec, UnknownUrnError } from "@babelqueue/core";
|
|
3
|
+
function topicName(topic) {
|
|
4
|
+
const slash = topic.lastIndexOf("/");
|
|
5
|
+
return slash >= 0 ? topic.slice(slash + 1) : topic;
|
|
6
|
+
}
|
|
7
|
+
function pulsarProperties(envelope) {
|
|
8
|
+
const properties = {};
|
|
9
|
+
if (envelope.job) properties["bq-job"] = envelope.job;
|
|
10
|
+
if (envelope.trace_id) properties["bq-trace-id"] = envelope.trace_id;
|
|
11
|
+
if (envelope.meta.id) properties["bq-message-id"] = envelope.meta.id;
|
|
12
|
+
if (envelope.meta.schema_version != null) {
|
|
13
|
+
properties["bq-schema-version"] = String(envelope.meta.schema_version);
|
|
14
|
+
}
|
|
15
|
+
if (envelope.meta.lang) properties["bq-source-lang"] = envelope.meta.lang;
|
|
16
|
+
properties["bq-attempts"] = String(envelope.attempts ?? 0);
|
|
17
|
+
return properties;
|
|
18
|
+
}
|
|
19
|
+
function toPulsarMessage(envelope) {
|
|
20
|
+
return {
|
|
21
|
+
data: Buffer.from(EnvelopeCodec.encode(envelope), "utf8"),
|
|
22
|
+
properties: pulsarProperties(envelope)
|
|
23
|
+
};
|
|
24
|
+
}
|
|
25
|
+
var PulsarPublisher = class {
|
|
26
|
+
constructor(producer) {
|
|
27
|
+
this.producer = producer;
|
|
28
|
+
}
|
|
29
|
+
producer;
|
|
30
|
+
/**
|
|
31
|
+
* Build the canonical envelope for `(urn, data)`, send it with the §5 property projection,
|
|
32
|
+
* and return the message id (`meta.id`). A positive `delayMs` schedules native delayed
|
|
33
|
+
* delivery via `deliverAfter` and mirrors `bq-delay`.
|
|
34
|
+
*/
|
|
35
|
+
async publish(urn, data, options = {}) {
|
|
36
|
+
const envelope = EnvelopeCodec.make(urn, data, {
|
|
37
|
+
queue: topicName(this.producer.getTopic()),
|
|
38
|
+
traceId: options.traceId
|
|
39
|
+
});
|
|
40
|
+
const message = toPulsarMessage(envelope);
|
|
41
|
+
if (options.delayMs != null && options.delayMs > 0) {
|
|
42
|
+
message.properties = { ...message.properties, "bq-delay": String(options.delayMs) };
|
|
43
|
+
message.deliverAfter = options.delayMs;
|
|
44
|
+
}
|
|
45
|
+
await this.producer.send(message);
|
|
46
|
+
return envelope.meta.id;
|
|
47
|
+
}
|
|
48
|
+
};
|
|
49
|
+
function isReceiveTimeout(error) {
|
|
50
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
51
|
+
return /timeout/i.test(message);
|
|
52
|
+
}
|
|
53
|
+
function decodePayload(data) {
|
|
54
|
+
return typeof data === "string" ? data : data.toString("utf8");
|
|
55
|
+
}
|
|
56
|
+
var PulsarConsumer = class {
|
|
57
|
+
constructor(consumer, handlers, options = {}) {
|
|
58
|
+
this.consumer = consumer;
|
|
59
|
+
this.handlers = handlers;
|
|
60
|
+
this.options = options;
|
|
61
|
+
}
|
|
62
|
+
consumer;
|
|
63
|
+
handlers;
|
|
64
|
+
options;
|
|
65
|
+
/** Receive one message (up to the receive timeout), route + settle it. Returns 1, or 0 on timeout. */
|
|
66
|
+
async poll() {
|
|
67
|
+
let message;
|
|
68
|
+
try {
|
|
69
|
+
message = await this.consumer.receive(this.options.receiveTimeoutMs ?? 1e3);
|
|
70
|
+
} catch (error) {
|
|
71
|
+
if (isReceiveTimeout(error)) return 0;
|
|
72
|
+
throw error;
|
|
73
|
+
}
|
|
74
|
+
await this.handle(message);
|
|
75
|
+
return 1;
|
|
76
|
+
}
|
|
77
|
+
/** Poll until `signal` aborts. */
|
|
78
|
+
async run(signal) {
|
|
79
|
+
while (signal?.aborted !== true) {
|
|
80
|
+
await this.poll();
|
|
81
|
+
}
|
|
82
|
+
}
|
|
83
|
+
async handle(message) {
|
|
84
|
+
const envelope = EnvelopeCodec.decode(decodePayload(message.getData()));
|
|
85
|
+
const redeliveryCount = typeof message.getRedeliveryCount === "function" ? message.getRedeliveryCount() : 0;
|
|
86
|
+
const current = typeof envelope.attempts === "number" ? envelope.attempts : 0;
|
|
87
|
+
if (redeliveryCount > current) {
|
|
88
|
+
envelope.attempts = redeliveryCount;
|
|
89
|
+
}
|
|
90
|
+
if (!EnvelopeCodec.accepts(envelope)) {
|
|
91
|
+
this.options.onError?.(
|
|
92
|
+
new BabelQueueError("Rejected a non-conformant BabelQueue envelope from Apache Pulsar."),
|
|
93
|
+
envelope,
|
|
94
|
+
message
|
|
95
|
+
);
|
|
96
|
+
await this.consumer.negativeAcknowledge(message);
|
|
97
|
+
return;
|
|
98
|
+
}
|
|
99
|
+
const urn = EnvelopeCodec.urn(envelope);
|
|
100
|
+
const handler = this.handlers[urn];
|
|
101
|
+
if (!handler) {
|
|
102
|
+
if (this.options.onUnknownUrn) {
|
|
103
|
+
await this.options.onUnknownUrn(envelope, message);
|
|
104
|
+
await this.consumer.acknowledge(message);
|
|
105
|
+
} else {
|
|
106
|
+
this.options.onError?.(new UnknownUrnError(urn), envelope, message);
|
|
107
|
+
await this.consumer.negativeAcknowledge(message);
|
|
108
|
+
}
|
|
109
|
+
return;
|
|
110
|
+
}
|
|
111
|
+
try {
|
|
112
|
+
await handler(envelope, message);
|
|
113
|
+
await this.consumer.acknowledge(message);
|
|
114
|
+
} catch (error) {
|
|
115
|
+
this.options.onError?.(error, envelope, message);
|
|
116
|
+
await this.consumer.negativeAcknowledge(message);
|
|
117
|
+
}
|
|
118
|
+
}
|
|
119
|
+
};
|
|
120
|
+
export {
|
|
121
|
+
PulsarConsumer,
|
|
122
|
+
PulsarPublisher,
|
|
123
|
+
pulsarProperties,
|
|
124
|
+
toPulsarMessage
|
|
125
|
+
};
|
|
126
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/index.ts"],"sourcesContent":["/**\n * Apache Pulsar adapter for BabelQueue.\n *\n * A canonical-envelope **publisher** and a URN-routed **consumer** over Apache Pulsar, so a\n * Pulsar-based Node service speaks the same contract (envelope shape, URN identity, trace\n * propagation) as the .NET, Java, Python and Go SDKs.\n *\n * import Pulsar from \"pulsar-client\";\n * import { PulsarPublisher, PulsarConsumer } from \"@babelqueue/pulsar\";\n *\n * const client = new Pulsar.Client({ serviceUrl: \"pulsar://localhost:6650\" });\n *\n * const producer = await client.createProducer({ topic: \"orders\" });\n * await new PulsarPublisher(producer).publish(\"urn:babel:orders:created\", { order_id: 1042 });\n *\n * const sub = await client.subscribe({\n * topic: \"orders\", subscription: \"babelqueue\", subscriptionType: \"Shared\",\n * });\n * const consumer = new PulsarConsumer(sub, {\n * \"urn:babel:orders:created\": async (env) => { ... },\n * });\n * await consumer.run();\n *\n * This implements §5 of the broker-bindings contract: the canonical envelope is the message\n * payload, projected onto native Pulsar message properties (string→string) — `bq-job` = URN,\n * `bq-trace-id` = trace_id, `bq-message-id` = meta.id, plus `bq-schema-version` /\n * `bq-source-lang` / `bq-attempts`. The envelope is unchanged (`schema_version` stays 1);\n * Pulsar is purely additive. A failed handler `negativeAcknowledge`s the message, so it is\n * redelivered (at-least-once) and `getRedeliveryCount()` is incremented; the authoritative\n * attempt count is the body's `bq-attempts`, reconciled to `max(bq-attempts,\n * getRedeliveryCount())` — no −1, because Pulsar's redelivery count is 0-based.\n */\n\nimport { BabelQueueError, EnvelopeCodec, UnknownUrnError } from \"@babelqueue/core\";\nimport type { Envelope, IncomingEnvelope } from \"@babelqueue/core\";\n\n// --- Minimal Pulsar shapes (a structural subset of pulsar-client) --------------\n\n/** A message to send (structural subset of pulsar-client `ProducerMessage`). */\nexport interface PulsarProducerMessage {\n data: Buffer;\n properties?: { [key: string]: string };\n /** Relative delay in milliseconds before the message becomes available (native `deliverAfter`). */\n deliverAfter?: number;\n}\n\n/**\n * The subset of the Pulsar producer this adapter calls. A `Producer` from `pulsar-client`\n * satisfies it structurally; a fake satisfies it in tests.\n */\nexport interface PulsarProducer {\n getTopic(): string;\n send(message: PulsarProducerMessage): Promise<unknown>;\n}\n\n/** A received message (structural subset of pulsar-client `Message`). */\nexport interface PulsarReceivedMessage {\n getData(): Buffer | string;\n getProperties(): { [key: string]: string };\n getRedeliveryCount(): number;\n}\n\n/**\n * The subset of the Pulsar consumer this adapter calls. A `Consumer` from `pulsar-client`\n * satisfies it structurally; a fake satisfies it in tests.\n */\nexport interface PulsarConsumerClient {\n receive(timeoutMs?: number): Promise<PulsarReceivedMessage>;\n acknowledge(message: PulsarReceivedMessage): Promise<void> | void;\n negativeAcknowledge(message: PulsarReceivedMessage): Promise<void> | void;\n}\n\n// --- Property projection (contract §5.2) ---------------------------------------\n\n/** Bare topic name (the part after the last `/`), used as `meta.queue`. */\nfunction topicName(topic: string): string {\n const slash = topic.lastIndexOf(\"/\");\n return slash >= 0 ? topic.slice(slash + 1) : topic;\n}\n\n/**\n * Project the envelope's contract fields onto native Pulsar message properties\n * (string→string): `bq-job` = URN, `bq-trace-id` = trace_id, `bq-message-id` = meta.id, plus\n * `bq-schema-version` / `bq-source-lang` / `bq-attempts`. The body stays authoritative.\n */\nexport function pulsarProperties(envelope: Envelope): { [key: string]: string } {\n const properties: { [key: string]: string } = {};\n if (envelope.job) properties[\"bq-job\"] = envelope.job;\n if (envelope.trace_id) properties[\"bq-trace-id\"] = envelope.trace_id;\n if (envelope.meta.id) properties[\"bq-message-id\"] = envelope.meta.id;\n if (envelope.meta.schema_version != null) {\n properties[\"bq-schema-version\"] = String(envelope.meta.schema_version);\n }\n if (envelope.meta.lang) properties[\"bq-source-lang\"] = envelope.meta.lang;\n properties[\"bq-attempts\"] = String(envelope.attempts ?? 0);\n return properties;\n}\n\n/** Project the envelope onto a native Pulsar producer message (payload + properties). */\nexport function toPulsarMessage(envelope: Envelope): PulsarProducerMessage {\n return {\n data: Buffer.from(EnvelopeCodec.encode(envelope), \"utf8\"),\n properties: pulsarProperties(envelope),\n };\n}\n\n// --- Publisher -----------------------------------------------------------------\n\n/** Options for {@link PulsarPublisher.publish}. */\nexport interface PublishOptions {\n /** Reuse an existing trace id (trace continuation). */\n traceId?: string;\n /** Schedule native delayed delivery this many milliseconds from now (`deliverAfter`). */\n delayMs?: number;\n}\n\n/** Sends canonical-envelope messages to one Pulsar topic with the §5 property projection. */\nexport class PulsarPublisher {\n constructor(private readonly producer: PulsarProducer) {}\n\n /**\n * Build the canonical envelope for `(urn, data)`, send it with the §5 property projection,\n * and return the message id (`meta.id`). A positive `delayMs` schedules native delayed\n * delivery via `deliverAfter` and mirrors `bq-delay`.\n */\n async publish(\n urn: string,\n data: Record<string, unknown>,\n options: PublishOptions = {},\n ): Promise<string> {\n const envelope = EnvelopeCodec.make(urn, data, {\n queue: topicName(this.producer.getTopic()),\n traceId: options.traceId,\n });\n const message = toPulsarMessage(envelope);\n\n if (options.delayMs != null && options.delayMs > 0) {\n message.properties = { ...message.properties, \"bq-delay\": String(options.delayMs) };\n message.deliverAfter = options.delayMs;\n }\n\n await this.producer.send(message);\n return envelope.meta.id;\n }\n}\n\n// --- Consumer ------------------------------------------------------------------\n\n/** A URN handler. Receives the validated envelope and the raw Pulsar message. */\nexport type BabelHandler = (envelope: Envelope, message: PulsarReceivedMessage) => unknown | Promise<unknown>;\n\n/** A map of URN → handler. */\nexport type BabelHandlers = Record<string, BabelHandler>;\n\n/** Options for {@link PulsarConsumer}. */\nexport interface PulsarConsumerOptions {\n /** Called instead of erroring when a message's URN has no handler (then the message is acknowledged). */\n onUnknownUrn?: (envelope: IncomingEnvelope, message: PulsarReceivedMessage) => unknown | Promise<unknown>;\n /** Called for a non-conformant envelope, an unmapped URN (no `onUnknownUrn`), or a throwing handler. The loop never stops. */\n onError?: (error: unknown, envelope: IncomingEnvelope, message: PulsarReceivedMessage) => void;\n /** Per-receive timeout in ms (default 1000). A timeout yields an empty poll, not an error. */\n receiveTimeoutMs?: number;\n}\n\nfunction isReceiveTimeout(error: unknown): boolean {\n const message = error instanceof Error ? error.message : String(error);\n return /timeout/i.test(message);\n}\n\nfunction decodePayload(data: Buffer | string): string {\n return typeof data === \"string\" ? data : data.toString(\"utf8\");\n}\n\n/**\n * Receives from a Pulsar subscription, decodes + validates each message, routes it to the\n * handler for its URN, and acknowledges it on success. A throwing handler\n * `negativeAcknowledge`s the message — the broker redelivers it and increments\n * `getRedeliveryCount()` (at-least-once); `attempts` is reconciled to\n * `max(bq-attempts, getRedeliveryCount())` for the handler.\n */\nexport class PulsarConsumer {\n constructor(\n private readonly consumer: PulsarConsumerClient,\n private readonly handlers: BabelHandlers,\n private readonly options: PulsarConsumerOptions = {},\n ) {}\n\n /** Receive one message (up to the receive timeout), route + settle it. Returns 1, or 0 on timeout. */\n async poll(): Promise<number> {\n let message: PulsarReceivedMessage;\n try {\n message = await this.consumer.receive(this.options.receiveTimeoutMs ?? 1000);\n } catch (error) {\n if (isReceiveTimeout(error)) return 0;\n throw error;\n }\n await this.handle(message);\n return 1;\n }\n\n /** Poll until `signal` aborts. */\n async run(signal?: AbortSignal): Promise<void> {\n while (signal?.aborted !== true) {\n await this.poll();\n }\n }\n\n private async handle(message: PulsarReceivedMessage): Promise<void> {\n const envelope = EnvelopeCodec.decode(decodePayload(message.getData()));\n\n // attempts = max(current, getRedeliveryCount()): the redelivery count is 0-based, so it\n // maps directly with no −1; the max never lowers a higher body count republished from\n // another SDK (the Go/Python transports retry by re-sending with attempts+1).\n const redeliveryCount = typeof message.getRedeliveryCount === \"function\" ? message.getRedeliveryCount() : 0;\n const current = typeof envelope.attempts === \"number\" ? envelope.attempts : 0;\n if (redeliveryCount > current) {\n envelope.attempts = redeliveryCount;\n }\n\n if (!EnvelopeCodec.accepts(envelope)) {\n this.options.onError?.(\n new BabelQueueError(\"Rejected a non-conformant BabelQueue envelope from Apache Pulsar.\"),\n envelope,\n message,\n );\n await this.consumer.negativeAcknowledge(message);\n return;\n }\n\n const urn = EnvelopeCodec.urn(envelope);\n const handler = this.handlers[urn];\n if (!handler) {\n if (this.options.onUnknownUrn) {\n await this.options.onUnknownUrn(envelope, message);\n await this.consumer.acknowledge(message);\n } else {\n this.options.onError?.(new UnknownUrnError(urn), envelope, message);\n await this.consumer.negativeAcknowledge(message);\n }\n return;\n }\n\n try {\n await handler(envelope as Envelope, message);\n await this.consumer.acknowledge(message);\n } catch (error) {\n // Negative-ack releases the message — the broker redelivers and increments the count.\n this.options.onError?.(error, envelope, message);\n await this.consumer.negativeAcknowledge(message);\n }\n }\n}\n"],"mappings":"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","names":[]}
|
package/package.json
ADDED
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@babelqueue/pulsar",
|
|
3
|
+
"version": "1.0.0",
|
|
4
|
+
"description": "Apache Pulsar adapter for BabelQueue — canonical-envelope publisher and a URN-routed consumer.",
|
|
5
|
+
"keywords": ["babelqueue", "pulsar", "apache-pulsar", "queue", "polyglot", "messaging"],
|
|
6
|
+
"homepage": "https://babelqueue.com",
|
|
7
|
+
"bugs": {
|
|
8
|
+
"url": "https://github.com/BabelQueue/babelqueue-node-adapters/issues"
|
|
9
|
+
},
|
|
10
|
+
"repository": {
|
|
11
|
+
"type": "git",
|
|
12
|
+
"url": "git+https://github.com/BabelQueue/babelqueue-node-adapters.git",
|
|
13
|
+
"directory": "packages/pulsar"
|
|
14
|
+
},
|
|
15
|
+
"license": "MIT",
|
|
16
|
+
"author": "Muhammet Şafak <info@muhammetsafak.com.tr>",
|
|
17
|
+
"type": "module",
|
|
18
|
+
"exports": {
|
|
19
|
+
".": {
|
|
20
|
+
"import": {
|
|
21
|
+
"types": "./dist/index.d.ts",
|
|
22
|
+
"default": "./dist/index.js"
|
|
23
|
+
},
|
|
24
|
+
"require": {
|
|
25
|
+
"types": "./dist/index.d.cts",
|
|
26
|
+
"default": "./dist/index.cjs"
|
|
27
|
+
}
|
|
28
|
+
}
|
|
29
|
+
},
|
|
30
|
+
"main": "./dist/index.cjs",
|
|
31
|
+
"module": "./dist/index.js",
|
|
32
|
+
"types": "./dist/index.d.ts",
|
|
33
|
+
"files": ["dist"],
|
|
34
|
+
"sideEffects": false,
|
|
35
|
+
"engines": {
|
|
36
|
+
"node": ">=18"
|
|
37
|
+
},
|
|
38
|
+
"scripts": {
|
|
39
|
+
"build": "tsup",
|
|
40
|
+
"typecheck": "tsc --noEmit",
|
|
41
|
+
"test": "node --import tsx --test test/*.test.ts"
|
|
42
|
+
},
|
|
43
|
+
"dependencies": {
|
|
44
|
+
"@babelqueue/core": "^1.0.0"
|
|
45
|
+
},
|
|
46
|
+
"peerDependencies": {
|
|
47
|
+
"pulsar-client": "^1.8.0"
|
|
48
|
+
},
|
|
49
|
+
"peerDependenciesMeta": {
|
|
50
|
+
"pulsar-client": {
|
|
51
|
+
"optional": true
|
|
52
|
+
}
|
|
53
|
+
},
|
|
54
|
+
"publishConfig": {
|
|
55
|
+
"access": "public"
|
|
56
|
+
}
|
|
57
|
+
}
|