@blokjs/trigger-pubsub 0.6.18 → 0.6.20
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/dist/PubSubTrigger.js +20 -1
- package/package.json +5 -4
- package/CHANGELOG.md +0 -22
- package/__tests__/integration/gcp-pubsub.real-emulator.test.ts +0 -235
- package/__tests__/integration/kafka-pubsub.real-kafka.test.ts +0 -269
- package/__tests__/integration/nats-pubsub.real-nats.test.ts +0 -138
- package/src/PubSubTrigger.test.ts +0 -151
- package/src/PubSubTrigger.ts +0 -402
- package/src/adapters/AWSSNSAdapter.ts +0 -322
- package/src/adapters/AzureServiceBusAdapter.ts +0 -263
- package/src/adapters/GCPPubSubAdapter.ts +0 -236
- package/src/adapters/KafkaPubSubAdapter.ts +0 -194
- package/src/adapters/NATSPubSubAdapter.ts +0 -326
- package/src/adapters/RedisStreamsPubSubAdapter.ts +0 -225
- package/src/adapters/factory.test.ts +0 -87
- package/src/adapters/factory.ts +0 -88
- package/src/adapters/new-adapters.test.ts +0 -108
- package/src/index.ts +0 -67
- package/template/.env.example +0 -8
- package/template/package.json +0 -44
- package/template/src/Nodes.ts +0 -10
- package/template/src/Workflows.ts +0 -8
- package/template/src/index.ts +0 -41
- package/template/src/runner/PubSubServer.ts +0 -39
- package/template/src/runner/types/Workflows.ts +0 -7
- package/template/src/workflows/messages/on-message.ts +0 -48
- package/template/tsconfig.json +0 -31
- package/template/vitest.config.ts +0 -39
- package/tsconfig.json +0 -32
|
@@ -1,236 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* GCPPubSubAdapter - Google Cloud Pub/Sub adapter for PubSubTrigger
|
|
3
|
-
*
|
|
4
|
-
* Uses @google-cloud/pubsub for GCP Pub/Sub connectivity.
|
|
5
|
-
* Requires: npm install @google-cloud/pubsub
|
|
6
|
-
*
|
|
7
|
-
* Environment variables:
|
|
8
|
-
* - GOOGLE_CLOUD_PROJECT: GCP project ID
|
|
9
|
-
* - GOOGLE_APPLICATION_CREDENTIALS: Path to service account key file (optional if using default credentials)
|
|
10
|
-
* - PUBSUB_EMULATOR_HOST: Pub/Sub emulator host for local development (optional)
|
|
11
|
-
*/
|
|
12
|
-
|
|
13
|
-
import type { PubSubTriggerOpts } from "@blokjs/helper";
|
|
14
|
-
import type { Message, PubSub, Subscription } from "@google-cloud/pubsub";
|
|
15
|
-
import { v4 as uuid } from "uuid";
|
|
16
|
-
import type { PubSubAdapter, PubSubMessage } from "../PubSubTrigger";
|
|
17
|
-
|
|
18
|
-
/**
|
|
19
|
-
* GCP Pub/Sub configuration
|
|
20
|
-
*/
|
|
21
|
-
export interface GCPPubSubConfig {
|
|
22
|
-
projectId?: string;
|
|
23
|
-
credentials?: {
|
|
24
|
-
client_email: string;
|
|
25
|
-
private_key: string;
|
|
26
|
-
};
|
|
27
|
-
}
|
|
28
|
-
|
|
29
|
-
/**
|
|
30
|
-
* GCPPubSubAdapter - Google Cloud Pub/Sub implementation
|
|
31
|
-
*/
|
|
32
|
-
export class GCPPubSubAdapter implements PubSubAdapter {
|
|
33
|
-
readonly provider = "gcp" as const;
|
|
34
|
-
|
|
35
|
-
private client: PubSub | undefined;
|
|
36
|
-
private subscriptions: Map<string, Subscription> = new Map();
|
|
37
|
-
private connected = false;
|
|
38
|
-
private config: GCPPubSubConfig;
|
|
39
|
-
|
|
40
|
-
/**
|
|
41
|
-
* Type-narrowing accessor for `this.client`. Field is undefined until
|
|
42
|
-
* `connect()` runs.
|
|
43
|
-
*/
|
|
44
|
-
private requireClient(): PubSub {
|
|
45
|
-
if (!this.client) {
|
|
46
|
-
throw new Error("[GCPPubSubAdapter] client is not initialised — call connect() first");
|
|
47
|
-
}
|
|
48
|
-
return this.client;
|
|
49
|
-
}
|
|
50
|
-
|
|
51
|
-
constructor(config?: GCPPubSubConfig) {
|
|
52
|
-
this.config = {
|
|
53
|
-
projectId: config?.projectId || process.env.GOOGLE_CLOUD_PROJECT,
|
|
54
|
-
credentials: config?.credentials,
|
|
55
|
-
};
|
|
56
|
-
}
|
|
57
|
-
|
|
58
|
-
/**
|
|
59
|
-
* Connect to GCP Pub/Sub
|
|
60
|
-
*/
|
|
61
|
-
async connect(): Promise<void> {
|
|
62
|
-
if (this.connected) return;
|
|
63
|
-
|
|
64
|
-
try {
|
|
65
|
-
// Dynamic import of @google-cloud/pubsub
|
|
66
|
-
const { PubSub } = await import("@google-cloud/pubsub");
|
|
67
|
-
|
|
68
|
-
this.client = new PubSub({
|
|
69
|
-
projectId: this.config.projectId,
|
|
70
|
-
credentials: this.config.credentials,
|
|
71
|
-
});
|
|
72
|
-
|
|
73
|
-
this.connected = true;
|
|
74
|
-
console.log(`[GCPPubSubAdapter] Connected to GCP Pub/Sub: ${this.config.projectId}`);
|
|
75
|
-
} catch (error) {
|
|
76
|
-
throw new Error(
|
|
77
|
-
`Failed to connect to GCP Pub/Sub: ${(error as Error).message}. Make sure @google-cloud/pubsub is installed: npm install @google-cloud/pubsub`,
|
|
78
|
-
);
|
|
79
|
-
}
|
|
80
|
-
}
|
|
81
|
-
|
|
82
|
-
/**
|
|
83
|
-
* Disconnect from GCP Pub/Sub
|
|
84
|
-
*/
|
|
85
|
-
async disconnect(): Promise<void> {
|
|
86
|
-
if (!this.connected) return;
|
|
87
|
-
|
|
88
|
-
try {
|
|
89
|
-
// Close all subscriptions
|
|
90
|
-
for (const [name, subscription] of this.subscriptions) {
|
|
91
|
-
await subscription.close();
|
|
92
|
-
}
|
|
93
|
-
this.subscriptions.clear();
|
|
94
|
-
|
|
95
|
-
await this.requireClient().close();
|
|
96
|
-
this.connected = false;
|
|
97
|
-
console.log("[GCPPubSubAdapter] Disconnected from GCP Pub/Sub");
|
|
98
|
-
} catch (error) {
|
|
99
|
-
console.error(`[GCPPubSubAdapter] Error disconnecting: ${(error as Error).message}`);
|
|
100
|
-
}
|
|
101
|
-
}
|
|
102
|
-
|
|
103
|
-
/**
|
|
104
|
-
* Subscribe to a GCP Pub/Sub topic
|
|
105
|
-
*/
|
|
106
|
-
async subscribe(config: PubSubTriggerOpts, handler: (message: PubSubMessage) => Promise<void>): Promise<void> {
|
|
107
|
-
if (!this.connected) {
|
|
108
|
-
throw new Error("Not connected to GCP Pub/Sub. Call connect() first.");
|
|
109
|
-
}
|
|
110
|
-
|
|
111
|
-
if (!config.subscription) {
|
|
112
|
-
throw new Error("[GCPPubSubAdapter] `subscription` is required — must be the GCP subscription name.");
|
|
113
|
-
}
|
|
114
|
-
const subscriptionName = config.subscription;
|
|
115
|
-
|
|
116
|
-
// Get the subscription. The GCP SDK calls the per-subscription
|
|
117
|
-
// deadline `maxAckDeadline` (a `Duration`); our author-facing
|
|
118
|
-
// field is `ackDeadline` (seconds) for parity with other
|
|
119
|
-
// providers. `Duration` is the GCP SDK's tc39-Temporal-shaped
|
|
120
|
-
// shim — construct via `Duration.from({seconds})`.
|
|
121
|
-
const { Duration } = await import("@google-cloud/pubsub");
|
|
122
|
-
const subscription = this.requireClient().subscription(subscriptionName, {
|
|
123
|
-
flowControl: {
|
|
124
|
-
maxMessages: config.maxMessages || 10,
|
|
125
|
-
},
|
|
126
|
-
maxAckDeadline: Duration.from({ seconds: config.ackDeadline || 30 }),
|
|
127
|
-
});
|
|
128
|
-
|
|
129
|
-
// Message handler
|
|
130
|
-
const messageHandler = async (gcpMessage: Message) => {
|
|
131
|
-
// Parse message data
|
|
132
|
-
let body: unknown;
|
|
133
|
-
try {
|
|
134
|
-
const data = gcpMessage.data.toString();
|
|
135
|
-
body = JSON.parse(data);
|
|
136
|
-
} catch {
|
|
137
|
-
body = gcpMessage.data.toString();
|
|
138
|
-
}
|
|
139
|
-
|
|
140
|
-
// Create pub/sub message
|
|
141
|
-
const pubsubMessage: PubSubMessage = {
|
|
142
|
-
id: gcpMessage.id || uuid(),
|
|
143
|
-
body,
|
|
144
|
-
attributes: gcpMessage.attributes || {},
|
|
145
|
-
raw: gcpMessage,
|
|
146
|
-
topic: config.topic,
|
|
147
|
-
subscription: subscriptionName,
|
|
148
|
-
publishTime: gcpMessage.publishTime ? new Date(gcpMessage.publishTime) : new Date(),
|
|
149
|
-
ack: async () => {
|
|
150
|
-
gcpMessage.ack();
|
|
151
|
-
},
|
|
152
|
-
nack: async () => {
|
|
153
|
-
gcpMessage.nack();
|
|
154
|
-
},
|
|
155
|
-
};
|
|
156
|
-
|
|
157
|
-
// Process message
|
|
158
|
-
try {
|
|
159
|
-
await handler(pubsubMessage);
|
|
160
|
-
} catch (error) {
|
|
161
|
-
console.error(`[GCPPubSubAdapter] Error processing message: ${(error as Error).message}`);
|
|
162
|
-
}
|
|
163
|
-
};
|
|
164
|
-
|
|
165
|
-
// Error handler
|
|
166
|
-
const errorHandler = (error: Error) => {
|
|
167
|
-
console.error(`[GCPPubSubAdapter] Subscription error: ${error.message}`);
|
|
168
|
-
};
|
|
169
|
-
|
|
170
|
-
// Attach listeners
|
|
171
|
-
subscription.on("message", messageHandler);
|
|
172
|
-
subscription.on("error", errorHandler);
|
|
173
|
-
|
|
174
|
-
// Store subscription reference
|
|
175
|
-
this.subscriptions.set(subscriptionName, subscription);
|
|
176
|
-
|
|
177
|
-
console.log(`[GCPPubSubAdapter] Subscribed to: ${subscriptionName}`);
|
|
178
|
-
}
|
|
179
|
-
|
|
180
|
-
/**
|
|
181
|
-
* Unsubscribe from a GCP Pub/Sub subscription
|
|
182
|
-
*/
|
|
183
|
-
async unsubscribe(subscriptionName: string): Promise<void> {
|
|
184
|
-
const subscription = this.subscriptions.get(subscriptionName);
|
|
185
|
-
if (subscription) {
|
|
186
|
-
await subscription.close();
|
|
187
|
-
this.subscriptions.delete(subscriptionName);
|
|
188
|
-
console.log(`[GCPPubSubAdapter] Unsubscribed from: ${subscriptionName}`);
|
|
189
|
-
}
|
|
190
|
-
}
|
|
191
|
-
|
|
192
|
-
/**
|
|
193
|
-
* Check if connected to GCP Pub/Sub
|
|
194
|
-
*/
|
|
195
|
-
isConnected(): boolean {
|
|
196
|
-
return this.connected;
|
|
197
|
-
}
|
|
198
|
-
|
|
199
|
-
/**
|
|
200
|
-
* v0.7 PR 6 — publish to a GCP Pub/Sub topic.
|
|
201
|
-
*
|
|
202
|
-
* `partitionKey` / `orderingKey` map to GCP's `orderingKey` (the
|
|
203
|
-
* topic must have message ordering enabled — otherwise the field
|
|
204
|
-
* is ignored by the broker).
|
|
205
|
-
*/
|
|
206
|
-
async publish(
|
|
207
|
-
topic: string,
|
|
208
|
-
payload: unknown,
|
|
209
|
-
opts?: { partitionKey?: string; orderingKey?: string },
|
|
210
|
-
): Promise<void> {
|
|
211
|
-
if (!this.connected) throw new Error("[blok][pubsub-gcp] not connected. Call connect() first.");
|
|
212
|
-
const body = typeof payload === "string" ? payload : JSON.stringify(payload);
|
|
213
|
-
const t = this.requireClient().topic(topic);
|
|
214
|
-
await t.publishMessage({
|
|
215
|
-
data: Buffer.from(body),
|
|
216
|
-
orderingKey: opts?.orderingKey ?? opts?.partitionKey,
|
|
217
|
-
});
|
|
218
|
-
}
|
|
219
|
-
|
|
220
|
-
/**
|
|
221
|
-
* Health check - verify GCP Pub/Sub connectivity
|
|
222
|
-
*/
|
|
223
|
-
async healthCheck(): Promise<boolean> {
|
|
224
|
-
if (!this.connected) return false;
|
|
225
|
-
|
|
226
|
-
try {
|
|
227
|
-
// List subscriptions as a health check
|
|
228
|
-
const [subscriptions] = await this.requireClient().getSubscriptions({ pageSize: 1 });
|
|
229
|
-
return true;
|
|
230
|
-
} catch {
|
|
231
|
-
return false;
|
|
232
|
-
}
|
|
233
|
-
}
|
|
234
|
-
}
|
|
235
|
-
|
|
236
|
-
export default GCPPubSubAdapter;
|
|
@@ -1,194 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* KafkaPubSubAdapter — v0.7 PR 6 — Pub/Sub adapter backed by Apache
|
|
3
|
-
* Kafka via `kafkajs`.
|
|
4
|
-
*
|
|
5
|
-
* Pub/Sub vs Worker semantics: this adapter uses **per-subscriber
|
|
6
|
-
* consumer groups** so multiple subscribers each receive every message
|
|
7
|
-
* (fan-out). When `consumerGroup` is explicitly set, all subscribers
|
|
8
|
-
* share that group and compete (1 of N gets each message).
|
|
9
|
-
*
|
|
10
|
-
* Replay cursors via `startFrom`:
|
|
11
|
-
* - `"earliest"` → `fromBeginning: true` on first subscribe.
|
|
12
|
-
* - `"latest"` (default) → only new messages.
|
|
13
|
-
* - `{seq: N}` / `{timestamp: ms}` — provider-specific. Cleanest
|
|
14
|
-
* long-term path is Kafka's `admin.seek({offset|timestamp})` post-
|
|
15
|
-
* subscribe; v1 honors `earliest` / `latest` and `{seq}` via
|
|
16
|
-
* `auto.offset.reset` only.
|
|
17
|
-
*
|
|
18
|
-
* Requires `kafkajs` as a peer dependency.
|
|
19
|
-
*
|
|
20
|
-
* Environment variables:
|
|
21
|
-
* - `KAFKA_BROKERS` — comma-separated (default `localhost:9092`).
|
|
22
|
-
* - `KAFKA_CLIENT_ID` — default `"blok-pubsub"`.
|
|
23
|
-
* - `KAFKA_SASL_USERNAME` / `KAFKA_SASL_PASSWORD` — SASL/PLAIN.
|
|
24
|
-
* - `KAFKA_SSL` — `"true"` enables TLS.
|
|
25
|
-
*/
|
|
26
|
-
|
|
27
|
-
import type { PubSubTriggerOpts } from "@blokjs/helper";
|
|
28
|
-
import { v4 as uuid } from "uuid";
|
|
29
|
-
import type { PubSubAdapter, PubSubMessage } from "../PubSubTrigger";
|
|
30
|
-
|
|
31
|
-
export interface KafkaPubSubConfig {
|
|
32
|
-
brokers: string[];
|
|
33
|
-
clientId: string;
|
|
34
|
-
saslUsername?: string;
|
|
35
|
-
saslPassword?: string;
|
|
36
|
-
ssl: boolean;
|
|
37
|
-
}
|
|
38
|
-
|
|
39
|
-
interface KafkaConsumerHandle {
|
|
40
|
-
disconnect: () => Promise<void>;
|
|
41
|
-
stop: () => Promise<void>;
|
|
42
|
-
}
|
|
43
|
-
|
|
44
|
-
export class KafkaPubSubAdapter implements PubSubAdapter {
|
|
45
|
-
readonly provider = "kafka" as const;
|
|
46
|
-
private readonly config: KafkaPubSubConfig;
|
|
47
|
-
// biome-ignore lint/suspicious/noExplicitAny: kafkajs's exported `Kafka` constructor is loosely typed.
|
|
48
|
-
private kafka: any = null;
|
|
49
|
-
// biome-ignore lint/suspicious/noExplicitAny: producer is created lazily and typed loosely.
|
|
50
|
-
private producer: any = null;
|
|
51
|
-
private consumers: Map<string, KafkaConsumerHandle> = new Map();
|
|
52
|
-
private connected = false;
|
|
53
|
-
|
|
54
|
-
constructor(config?: Partial<KafkaPubSubConfig>) {
|
|
55
|
-
this.config = {
|
|
56
|
-
brokers: config?.brokers ?? (process.env.KAFKA_BROKERS ?? "localhost:9092").split(",").map((s) => s.trim()),
|
|
57
|
-
clientId: config?.clientId ?? process.env.KAFKA_CLIENT_ID ?? "blok-pubsub",
|
|
58
|
-
saslUsername: config?.saslUsername ?? process.env.KAFKA_SASL_USERNAME,
|
|
59
|
-
saslPassword: config?.saslPassword ?? process.env.KAFKA_SASL_PASSWORD,
|
|
60
|
-
ssl: config?.ssl ?? process.env.KAFKA_SSL === "true",
|
|
61
|
-
};
|
|
62
|
-
}
|
|
63
|
-
|
|
64
|
-
async connect(): Promise<void> {
|
|
65
|
-
if (this.connected) return;
|
|
66
|
-
try {
|
|
67
|
-
// biome-ignore lint/suspicious/noExplicitAny: kafkajs is a runtime peer dep.
|
|
68
|
-
const kafkajs: any = await import("kafkajs");
|
|
69
|
-
const sasl =
|
|
70
|
-
this.config.saslUsername && this.config.saslPassword
|
|
71
|
-
? { mechanism: "plain", username: this.config.saslUsername, password: this.config.saslPassword }
|
|
72
|
-
: undefined;
|
|
73
|
-
this.kafka = new kafkajs.Kafka({
|
|
74
|
-
clientId: this.config.clientId,
|
|
75
|
-
brokers: this.config.brokers,
|
|
76
|
-
ssl: this.config.ssl,
|
|
77
|
-
sasl,
|
|
78
|
-
});
|
|
79
|
-
this.producer = this.kafka.producer();
|
|
80
|
-
await this.producer.connect();
|
|
81
|
-
this.connected = true;
|
|
82
|
-
} catch (err) {
|
|
83
|
-
throw new Error(
|
|
84
|
-
`[blok][pubsub-kafka] connect failed: ${(err as Error).message}. Install kafkajs as a peer dependency: bun add kafkajs`,
|
|
85
|
-
);
|
|
86
|
-
}
|
|
87
|
-
}
|
|
88
|
-
|
|
89
|
-
async disconnect(): Promise<void> {
|
|
90
|
-
if (!this.connected) return;
|
|
91
|
-
for (const consumer of this.consumers.values()) {
|
|
92
|
-
try {
|
|
93
|
-
await consumer.disconnect();
|
|
94
|
-
} catch {
|
|
95
|
-
/* ignore */
|
|
96
|
-
}
|
|
97
|
-
}
|
|
98
|
-
this.consumers.clear();
|
|
99
|
-
try {
|
|
100
|
-
await this.producer?.disconnect();
|
|
101
|
-
} catch {
|
|
102
|
-
/* ignore */
|
|
103
|
-
}
|
|
104
|
-
this.producer = null;
|
|
105
|
-
this.connected = false;
|
|
106
|
-
}
|
|
107
|
-
|
|
108
|
-
async subscribe(config: PubSubTriggerOpts, handler: (message: PubSubMessage) => Promise<void>): Promise<void> {
|
|
109
|
-
if (!this.connected) throw new Error("[blok][pubsub-kafka] not connected. Call connect() first.");
|
|
110
|
-
// Fan-out: distinct group id per subscriber instance. Competing-
|
|
111
|
-
// consumer: explicit consumerGroup shared across all subscribers.
|
|
112
|
-
const groupId =
|
|
113
|
-
config.consumerGroup ?? `blok-fanout-${uuid().slice(0, 8)}-${config.topic.replace(/[^a-zA-Z0-9_]/g, "_")}`;
|
|
114
|
-
const consumer = this.kafka.consumer({ groupId });
|
|
115
|
-
await consumer.connect();
|
|
116
|
-
const fromBeginning = config.startFrom === "earliest";
|
|
117
|
-
await consumer.subscribe({ topic: config.topic, fromBeginning });
|
|
118
|
-
this.consumers.set(`${config.topic}#${groupId}`, consumer);
|
|
119
|
-
|
|
120
|
-
await consumer.run({
|
|
121
|
-
autoCommit: config.ack !== false,
|
|
122
|
-
eachMessage: async ({
|
|
123
|
-
message,
|
|
124
|
-
}: {
|
|
125
|
-
message: { key?: Buffer; value?: Buffer; offset: string; timestamp: string; headers?: Record<string, Buffer> };
|
|
126
|
-
}) => {
|
|
127
|
-
const text = message.value?.toString("utf8") ?? "";
|
|
128
|
-
let body: unknown = text;
|
|
129
|
-
try {
|
|
130
|
-
body = text.length > 0 ? JSON.parse(text) : null;
|
|
131
|
-
} catch {
|
|
132
|
-
/* leave as text */
|
|
133
|
-
}
|
|
134
|
-
const attributes: Record<string, string> = {};
|
|
135
|
-
if (message.headers) {
|
|
136
|
-
for (const [k, v] of Object.entries(message.headers)) attributes[k] = v?.toString("utf8") ?? "";
|
|
137
|
-
}
|
|
138
|
-
const msg: PubSubMessage = {
|
|
139
|
-
id: `${config.topic}:${message.offset}`,
|
|
140
|
-
body,
|
|
141
|
-
attributes,
|
|
142
|
-
raw: message,
|
|
143
|
-
topic: config.topic,
|
|
144
|
-
subscription: groupId,
|
|
145
|
-
publishTime: new Date(Number.parseInt(message.timestamp, 10)),
|
|
146
|
-
ack: async () => {
|
|
147
|
-
/* autoCommit handles ack */
|
|
148
|
-
},
|
|
149
|
-
nack: async () => {
|
|
150
|
-
/* kafkajs has no per-message nack; the offset commit is suppressed by throwing */
|
|
151
|
-
},
|
|
152
|
-
};
|
|
153
|
-
// On handler failure: re-throw so kafkajs suppresses the
|
|
154
|
-
// offset commit; the consumer will re-poll the message
|
|
155
|
-
// on the next cycle. The throw is intentional.
|
|
156
|
-
await handler(msg);
|
|
157
|
-
},
|
|
158
|
-
});
|
|
159
|
-
}
|
|
160
|
-
|
|
161
|
-
async unsubscribe(subscription: string): Promise<void> {
|
|
162
|
-
const consumer = this.consumers.get(subscription);
|
|
163
|
-
if (!consumer) return;
|
|
164
|
-
try {
|
|
165
|
-
await consumer.stop();
|
|
166
|
-
await consumer.disconnect();
|
|
167
|
-
} catch {
|
|
168
|
-
/* ignore */
|
|
169
|
-
}
|
|
170
|
-
this.consumers.delete(subscription);
|
|
171
|
-
}
|
|
172
|
-
|
|
173
|
-
async publish(
|
|
174
|
-
topic: string,
|
|
175
|
-
payload: unknown,
|
|
176
|
-
opts?: { partitionKey?: string; orderingKey?: string },
|
|
177
|
-
): Promise<void> {
|
|
178
|
-
if (!this.connected || !this.producer) throw new Error("[blok][pubsub-kafka] not connected. Call connect() first.");
|
|
179
|
-
const body = typeof payload === "string" ? payload : JSON.stringify(payload);
|
|
180
|
-
const key = opts?.partitionKey ?? opts?.orderingKey;
|
|
181
|
-
await this.producer.send({
|
|
182
|
-
topic,
|
|
183
|
-
messages: [{ key, value: body }],
|
|
184
|
-
});
|
|
185
|
-
}
|
|
186
|
-
|
|
187
|
-
isConnected(): boolean {
|
|
188
|
-
return this.connected;
|
|
189
|
-
}
|
|
190
|
-
|
|
191
|
-
async healthCheck(): Promise<boolean> {
|
|
192
|
-
return this.connected;
|
|
193
|
-
}
|
|
194
|
-
}
|