@blokjs/trigger-pubsub 0.2.3 → 0.6.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (36) hide show
  1. package/__tests__/integration/gcp-pubsub.real-emulator.test.ts +235 -0
  2. package/__tests__/integration/kafka-pubsub.real-kafka.test.ts +269 -0
  3. package/__tests__/integration/nats-pubsub.real-nats.test.ts +138 -0
  4. package/dist/PubSubTrigger.d.ts +43 -3
  5. package/dist/PubSubTrigger.js +70 -16
  6. package/dist/adapters/AWSSNSAdapter.d.ts +16 -0
  7. package/dist/adapters/AWSSNSAdapter.js +52 -9
  8. package/dist/adapters/AzureServiceBusAdapter.d.ts +15 -0
  9. package/dist/adapters/AzureServiceBusAdapter.js +44 -11
  10. package/dist/adapters/GCPPubSubAdapter.d.ts +16 -0
  11. package/dist/adapters/GCPPubSubAdapter.js +42 -8
  12. package/dist/adapters/KafkaPubSubAdapter.d.ts +53 -0
  13. package/dist/adapters/KafkaPubSubAdapter.js +168 -0
  14. package/dist/adapters/NATSPubSubAdapter.d.ts +52 -0
  15. package/dist/adapters/NATSPubSubAdapter.js +260 -0
  16. package/dist/adapters/RedisStreamsPubSubAdapter.d.ts +49 -0
  17. package/dist/adapters/RedisStreamsPubSubAdapter.js +193 -0
  18. package/dist/adapters/factory.d.ts +22 -0
  19. package/dist/adapters/factory.js +80 -0
  20. package/dist/index.d.ts +36 -45
  21. package/dist/index.js +39 -46
  22. package/package.json +22 -10
  23. package/src/PubSubTrigger.ts +84 -18
  24. package/src/adapters/AWSSNSAdapter.ts +76 -12
  25. package/src/adapters/AzureServiceBusAdapter.ts +57 -14
  26. package/src/adapters/GCPPubSubAdapter.ts +50 -10
  27. package/src/adapters/KafkaPubSubAdapter.ts +194 -0
  28. package/src/adapters/NATSPubSubAdapter.ts +326 -0
  29. package/src/adapters/RedisStreamsPubSubAdapter.ts +225 -0
  30. package/src/adapters/factory.test.ts +87 -0
  31. package/src/adapters/factory.ts +88 -0
  32. package/src/adapters/new-adapters.test.ts +108 -0
  33. package/src/index.ts +40 -41
  34. package/template/package.json +6 -6
  35. package/template/src/runner/PubSubServer.ts +2 -2
  36. package/template/src/workflows/messages/on-message.ts +38 -34
@@ -0,0 +1,52 @@
1
+ /**
2
+ * NATSPubSubAdapter — v0.7 PR 6 — Pub/Sub adapter backed by NATS.
3
+ *
4
+ * Two modes:
5
+ * - **Fan-out** (default, when `consumerGroup` is absent): every
6
+ * subscriber receives every message on the subject. NATS Core
7
+ * publish/subscribe semantics — cheapest path, no persistence.
8
+ * - **Competing-consumer** (when `consumerGroup` is set): NATS
9
+ * Queue Group — exactly one subscriber in the named group
10
+ * receives each message. Pure NATS Core.
11
+ * - **Durable** (when `durable: true`): subscribe via NATS
12
+ * JetStream consumer so the subscription survives restarts and
13
+ * replays missed messages from `startFrom`. Required for the
14
+ * `{seq}` / `{timestamp}` replay cursors.
15
+ *
16
+ * Subject wildcards (`orders.*.created`, `orders.>`) are honored by
17
+ * NATS natively in both modes.
18
+ *
19
+ * Requires `nats` as a peer dependency:
20
+ *
21
+ * bun add nats
22
+ *
23
+ * Environment variables:
24
+ * - `NATS_SERVERS` — comma-separated URLs (default `localhost:4222`).
25
+ * - `NATS_TOKEN` — bearer token authentication.
26
+ * - `NATS_USER` / `NATS_PASS` — userpass authentication.
27
+ */
28
+ import type { PubSubTriggerOpts } from "@blokjs/helper";
29
+ import type { PubSubAdapter, PubSubMessage } from "../PubSubTrigger";
30
+ export interface NATSPubSubConfig {
31
+ servers: string[];
32
+ token?: string;
33
+ user?: string;
34
+ pass?: string;
35
+ }
36
+ export declare class NATSPubSubAdapter implements PubSubAdapter {
37
+ readonly provider: "nats";
38
+ private readonly config;
39
+ private conn;
40
+ private subscriptions;
41
+ private connected;
42
+ constructor(config?: Partial<NATSPubSubConfig>);
43
+ connect(): Promise<void>;
44
+ disconnect(): Promise<void>;
45
+ subscribe(config: PubSubTriggerOpts, handler: (message: PubSubMessage) => Promise<void>): Promise<void>;
46
+ private dispatchJsMessage;
47
+ private dispatchCoreMessage;
48
+ unsubscribe(subscription: string): Promise<void>;
49
+ publish(topic: string, payload: unknown): Promise<void>;
50
+ isConnected(): boolean;
51
+ healthCheck(): Promise<boolean>;
52
+ }
@@ -0,0 +1,260 @@
1
+ /**
2
+ * NATSPubSubAdapter — v0.7 PR 6 — Pub/Sub adapter backed by NATS.
3
+ *
4
+ * Two modes:
5
+ * - **Fan-out** (default, when `consumerGroup` is absent): every
6
+ * subscriber receives every message on the subject. NATS Core
7
+ * publish/subscribe semantics — cheapest path, no persistence.
8
+ * - **Competing-consumer** (when `consumerGroup` is set): NATS
9
+ * Queue Group — exactly one subscriber in the named group
10
+ * receives each message. Pure NATS Core.
11
+ * - **Durable** (when `durable: true`): subscribe via NATS
12
+ * JetStream consumer so the subscription survives restarts and
13
+ * replays missed messages from `startFrom`. Required for the
14
+ * `{seq}` / `{timestamp}` replay cursors.
15
+ *
16
+ * Subject wildcards (`orders.*.created`, `orders.>`) are honored by
17
+ * NATS natively in both modes.
18
+ *
19
+ * Requires `nats` as a peer dependency:
20
+ *
21
+ * bun add nats
22
+ *
23
+ * Environment variables:
24
+ * - `NATS_SERVERS` — comma-separated URLs (default `localhost:4222`).
25
+ * - `NATS_TOKEN` — bearer token authentication.
26
+ * - `NATS_USER` / `NATS_PASS` — userpass authentication.
27
+ */
28
+ import { v4 as uuid } from "uuid";
29
+ const TEXT_DECODER = new TextDecoder();
30
+ const TEXT_ENCODER = new TextEncoder();
31
+ export class NATSPubSubAdapter {
32
+ provider = "nats";
33
+ config;
34
+ conn = null;
35
+ subscriptions = new Map();
36
+ connected = false;
37
+ constructor(config) {
38
+ this.config = {
39
+ servers: config?.servers ?? (process.env.NATS_SERVERS ?? "localhost:4222").split(",").map((s) => s.trim()),
40
+ token: config?.token ?? process.env.NATS_TOKEN,
41
+ user: config?.user ?? process.env.NATS_USER,
42
+ pass: config?.pass ?? process.env.NATS_PASS,
43
+ };
44
+ }
45
+ async connect() {
46
+ if (this.connected)
47
+ return;
48
+ try {
49
+ // biome-ignore lint/suspicious/noExplicitAny: nats is a runtime peer dep.
50
+ const nats = await import("nats");
51
+ this.conn = (await nats.connect({
52
+ servers: this.config.servers,
53
+ token: this.config.token,
54
+ user: this.config.user,
55
+ pass: this.config.pass,
56
+ }));
57
+ this.connected = true;
58
+ }
59
+ catch (err) {
60
+ throw new Error(`[blok][pubsub-nats] connect failed: ${err.message}. Install nats as a peer dependency: bun add nats`);
61
+ }
62
+ }
63
+ async disconnect() {
64
+ if (!this.connected)
65
+ return;
66
+ for (const sub of this.subscriptions.values()) {
67
+ try {
68
+ const result = sub.unsubscribe();
69
+ if (result instanceof Promise)
70
+ await result;
71
+ }
72
+ catch {
73
+ /* ignore */
74
+ }
75
+ }
76
+ this.subscriptions.clear();
77
+ try {
78
+ await this.conn?.drain();
79
+ }
80
+ catch {
81
+ /* ignore */
82
+ }
83
+ this.conn = null;
84
+ this.connected = false;
85
+ }
86
+ async subscribe(config, handler) {
87
+ if (!this.connected || !this.conn)
88
+ throw new Error("[blok][pubsub-nats] not connected. Call connect() first.");
89
+ const subKey = `${config.topic}#${config.consumerGroup ?? ""}`;
90
+ if (config.durable === true) {
91
+ // JetStream durable subscription — survives restarts.
92
+ if (!this.conn.jetstream) {
93
+ throw new Error("[blok][pubsub-nats] durable subscriptions require JetStream support in the nats client");
94
+ }
95
+ const jsm = await this.conn.jetstreamManager?.();
96
+ if (jsm) {
97
+ // Auto-create a stream covering the subject if one doesn't
98
+ // exist. Production deployments should pre-provision via
99
+ // `nats stream add` to control retention.
100
+ try {
101
+ await jsm.streams.add({
102
+ name: `blok-${(config.topic ?? "").replace(/[^a-zA-Z0-9_]/g, "_")}`,
103
+ subjects: [config.topic],
104
+ });
105
+ }
106
+ catch {
107
+ /* stream already exists — ignore */
108
+ }
109
+ }
110
+ const js = this.conn.jetstream();
111
+ const startSeq = typeof config.startFrom === "object" && config.startFrom && "seq" in config.startFrom
112
+ ? config.startFrom.seq
113
+ : undefined;
114
+ const deliverPolicy = config.startFrom === "earliest"
115
+ ? "all"
116
+ : config.startFrom === "latest"
117
+ ? "new"
118
+ : startSeq !== undefined
119
+ ? "by_start_sequence"
120
+ : "all";
121
+ const sub = await js.subscribe(config.topic, {
122
+ config: {
123
+ durable_name: config.consumerGroup ??
124
+ `blok-${(config.subscription ?? config.topic ?? "default").replace(/[^a-zA-Z0-9_]/g, "_")}`,
125
+ deliver_policy: deliverPolicy,
126
+ opt_start_seq: startSeq,
127
+ ack_policy: config.ack === false ? "none" : "explicit",
128
+ },
129
+ });
130
+ this.subscriptions.set(subKey, { unsubscribe: () => sub.unsubscribe() });
131
+ // Drive the async iterator in a background loop.
132
+ void (async () => {
133
+ try {
134
+ for await (const msg of sub) {
135
+ await this.dispatchJsMessage(msg, config, handler);
136
+ }
137
+ }
138
+ catch (err) {
139
+ // Subscription closed or connection lost — let the trigger
140
+ // re-listen via HMR/reconnect logic. Log for visibility.
141
+ console.error(`[blok][pubsub-nats] subscription error: ${err.message}`);
142
+ }
143
+ })();
144
+ return;
145
+ }
146
+ // Core NATS subscription — fire-and-forget, with optional queue
147
+ // group for competing-consumer semantics.
148
+ const sub = this.conn.subscribe(config.topic, {
149
+ queue: config.consumerGroup,
150
+ callback: (err, msg) => {
151
+ if (err) {
152
+ console.error(`[blok][pubsub-nats] subscribe error: ${err.message}`);
153
+ return;
154
+ }
155
+ void this.dispatchCoreMessage(msg, config, handler);
156
+ },
157
+ });
158
+ this.subscriptions.set(subKey, sub);
159
+ }
160
+ async dispatchJsMessage(msg, config, handler) {
161
+ const text = TEXT_DECODER.decode(msg.data);
162
+ let body = text;
163
+ try {
164
+ body = text.length > 0 ? JSON.parse(text) : null;
165
+ }
166
+ catch {
167
+ /* leave as text */
168
+ }
169
+ const message = {
170
+ id: `${msg.info.stream}:${msg.seq}`,
171
+ body,
172
+ attributes: { subject: msg.subject },
173
+ raw: msg,
174
+ topic: msg.subject,
175
+ subscription: msg.info.consumer,
176
+ publishTime: msg.info.timestampNanos ? new Date(msg.info.timestampNanos / 1e6) : undefined,
177
+ ack: async () => {
178
+ msg.ack();
179
+ },
180
+ nack: async () => {
181
+ msg.nak();
182
+ },
183
+ };
184
+ try {
185
+ await handler(message);
186
+ if (config.ack !== false)
187
+ msg.ack();
188
+ }
189
+ catch {
190
+ msg.nak();
191
+ }
192
+ }
193
+ async dispatchCoreMessage(msg, _config, handler) {
194
+ const text = TEXT_DECODER.decode(msg.data);
195
+ let body = text;
196
+ try {
197
+ body = text.length > 0 ? JSON.parse(text) : null;
198
+ }
199
+ catch {
200
+ /* leave as text */
201
+ }
202
+ const message = {
203
+ id: `${msg.subject}:${msg.sid}:${uuid()}`,
204
+ body,
205
+ attributes: { subject: msg.subject },
206
+ raw: msg,
207
+ topic: msg.subject,
208
+ ack: async () => {
209
+ /* core NATS has no explicit ack */
210
+ },
211
+ nack: async () => {
212
+ /* core NATS has no explicit nack */
213
+ },
214
+ };
215
+ try {
216
+ await handler(message);
217
+ }
218
+ catch (err) {
219
+ console.error(`[blok][pubsub-nats] handler error: ${err.message}`);
220
+ }
221
+ }
222
+ async unsubscribe(subscription) {
223
+ const sub = this.subscriptions.get(subscription);
224
+ if (!sub)
225
+ return;
226
+ try {
227
+ const result = sub.unsubscribe();
228
+ if (result instanceof Promise)
229
+ await result;
230
+ }
231
+ catch {
232
+ /* ignore */
233
+ }
234
+ this.subscriptions.delete(subscription);
235
+ }
236
+ async publish(topic, payload) {
237
+ if (!this.connected || !this.conn)
238
+ throw new Error("[blok][pubsub-nats] not connected. Call connect() first.");
239
+ const body = typeof payload === "string" ? payload : JSON.stringify(payload);
240
+ const data = TEXT_ENCODER.encode(body);
241
+ // Use core NATS publish. When a durable subscriber has been
242
+ // installed for this subject, the subscribe() path created a
243
+ // JetStream stream with a subject filter that captures any
244
+ // publish to `topic` — so durable consumers see the message via
245
+ // the stream while core subscribers see it directly. The earlier
246
+ // "try js.publish first, fall back to core" logic caused
247
+ // **double-delivery**: js.publish to a subject covered by a
248
+ // stream goes to BOTH the stream and core subscribers, then the
249
+ // fallback would publish AGAIN if js.publish timed out (vs
250
+ // returning 503 fast). Sticking to core publish avoids the race.
251
+ this.conn.publish(topic, data);
252
+ }
253
+ isConnected() {
254
+ return this.connected;
255
+ }
256
+ async healthCheck() {
257
+ return this.connected;
258
+ }
259
+ }
260
+ //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiTkFUU1B1YlN1YkFkYXB0ZXIuanMiLCJzb3VyY2VSb290IjoiIiwic291cmNlcyI6WyIuLi8uLi9zcmMvYWRhcHRlcnMvTkFUU1B1YlN1YkFkYXB0ZXIudHMiXSwibmFtZXMiOltdLCJtYXBwaW5ncyI6IkFBQUE7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7O0dBMEJHO0FBR0gsT0FBTyxFQUFFLEVBQUUsSUFBSSxJQUFJLEVBQUUsTUFBTSxNQUFNLENBQUM7QUEwRGxDLE1BQU0sWUFBWSxHQUFHLElBQUksV0FBVyxFQUFFLENBQUM7QUFDdkMsTUFBTSxZQUFZLEdBQUcsSUFBSSxXQUFXLEVBQUUsQ0FBQztBQUV2QyxNQUFNLE9BQU8saUJBQWlCO0lBQ3BCLFFBQVEsR0FBRyxNQUFlLENBQUM7SUFDbkIsTUFBTSxDQUFtQjtJQUNsQyxJQUFJLEdBQTBCLElBQUksQ0FBQztJQUNuQyxhQUFhLEdBQWdGLElBQUksR0FBRyxFQUFFLENBQUM7SUFDdkcsU0FBUyxHQUFHLEtBQUssQ0FBQztJQUUxQixZQUFZLE1BQWtDO1FBQzdDLElBQUksQ0FBQyxNQUFNLEdBQUc7WUFDYixPQUFPLEVBQUUsTUFBTSxFQUFFLE9BQU8sSUFBSSxDQUFDLE9BQU8sQ0FBQyxHQUFHLENBQUMsWUFBWSxJQUFJLGdCQUFnQixDQUFDLENBQUMsS0FBSyxDQUFDLEdBQUcsQ0FBQyxDQUFDLEdBQUcsQ0FBQyxDQUFDLENBQUMsRUFBRSxFQUFFLENBQUMsQ0FBQyxDQUFDLElBQUksRUFBRSxDQUFDO1lBQzFHLEtBQUssRUFBRSxNQUFNLEVBQUUsS0FBSyxJQUFJLE9BQU8sQ0FBQyxHQUFHLENBQUMsVUFBVTtZQUM5QyxJQUFJLEVBQUUsTUFBTSxFQUFFLElBQUksSUFBSSxPQUFPLENBQUMsR0FBRyxDQUFDLFNBQVM7WUFDM0MsSUFBSSxFQUFFLE1BQU0sRUFBRSxJQUFJLElBQUksT0FBTyxDQUFDLEdBQUcsQ0FBQyxTQUFTO1NBQzNDLENBQUM7SUFDSCxDQUFDO0lBRUQsS0FBSyxDQUFDLE9BQU87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QztJQUNoQyxDQUFDO0lBRUQsV0FBVztRQUNWLE9BQU8sSUFBSSxDQUFDLFNBQVMsQ0FBQztJQUN2QixDQUFDO0lBRUQsS0FBSyxDQUFDLFdBQVc7UUFDaEIsT0FBTyxJQUFJLENBQUMsU0FBUyxDQUFDO0lBQ3ZCLENBQUM7Q0FDRCIsInNvdXJjZXNDb250ZW50IjpbIi8qKlxuICogTkFUU1B1YlN1YkFkYXB0ZXIg4oCUIHYwLjcgUFIgNiDigJQgUHViL1N1YiBhZGFwdGVyIGJhY2tlZCBieSBOQVRTLlxuICpcbiAqIFR3byBtb2RlczpcbiAqICAgLSAqKkZhbi1vdXQqKiAoZGVmYXVsdCwgd2hlbiBgY29uc3VtZXJHcm91cGAgaXMgYWJzZW50KTogZXZlcnlcbiAqICAgICBzdWJzY3JpYmVyIHJlY2VpdmVzIGV2ZXJ5IG1lc3NhZ2Ugb24gdGhlIHN1YmplY3QuIE5BVFMgQ29yZVxuICogICAgIHB1Ymxpc2gvc3Vic2NyaWJlIHNlbWFudGljcyDigJQgY2hlYXBlc3QgcGF0aCwgbm8gcGVyc2lzdGVuY2UuXG4gKiAgIC0gKipDb21wZXRpbmctY29uc3VtZXIqKiAod2hlbiBgY29uc3VtZXJHcm91cGAgaXMgc2V0KTogTkFUU1xuICogICAgIFF1ZXVlIEdyb3VwIOKAlCBleGFjdGx5IG9uZSBzdWJzY3JpYmVyIGluIHRoZSBuYW1lZCBncm91cFxuICogICAgIHJlY2VpdmVzIGVhY2ggbWVzc2FnZS4gUHVyZSBOQVRTIENvcmUuXG4gKiAgIC0gKipEdXJhYmxlKiogKHdoZW4gYGR1cmFibGU6IHRydWVgKTogc3Vic2NyaWJlIHZpYSBOQVRTXG4gKiAgICAgSmV0U3RyZWFtIGNvbnN1bWVyIHNvIHRoZSBzdWJzY3JpcHRpb24gc3Vydml2ZXMgcmVzdGFydHMgYW5kXG4gKiAgICAgcmVwbGF5cyBtaXNzZWQgbWVzc2FnZXMgZnJvbSBgc3RhcnRGcm9tYC4gUmVxdWlyZWQgZm9yIHRoZVxuICogICAgIGB7c2VxfWAgLyBge3RpbWVzdGFtcH1gIHJlcGxheSBjdXJzb3JzLlxuICpcbiAqIFN1YmplY3Qgd2lsZGNhcmRzIChgb3JkZXJzLiouY3JlYXRlZGAsIGBvcmRlcnMuPmApIGFyZSBob25vcmVkIGJ5XG4gKiBOQVRTIG5hdGl2ZWx5IGluIGJvdGggbW9kZXMuXG4gKlxuICogUmVxdWlyZXMgYG5hdHNgIGFzIGEgcGVlciBkZXBlbmRlbmN5OlxuICpcbiAqICAgICBidW4gYWRkIG5hdHNcbiAqXG4gKiBFbnZpcm9ubWVudCB2YXJpYWJsZXM6XG4gKiAgIC0gYE5BVFNfU0VSVkVSU2AgICDigJQgY29tbWEtc2VwYXJhdGVkIFVSTHMgKGRlZmF1bHQgYGxvY2FsaG9zdDo0MjIyYCkuXG4gKiAgIC0gYE5BVFNfVE9LRU5gICAgICDigJQgYmVhcmVyIHRva2VuIGF1dGhlbnRpY2F0aW9uLlxuICogICAtIGBOQVRTX1VTRVJgIC8gYE5BVFNfUEFTU2AgIOKAlCB1c2VycGFzcyBhdXRoZW50aWNhdGlvbi5cbiAqL1xuXG5pbXBvcnQgdHlwZSB7IFB1YlN1YlRyaWdnZXJPcHRzIH0gZnJvbSBcIkBibG9ranMvaGVscGVyXCI7XG5pbXBvcnQgeyB2NCBhcyB1dWlkIH0gZnJvbSBcInV1aWRcIjtcbmltcG9ydCB0eXBlIHsgUHViU3ViQWRhcHRlciwgUHViU3ViTWVzc2FnZSB9IGZyb20gXCIuLi9QdWJTdWJUcmlnZ2VyXCI7XG5cbmV4cG9ydCBpbnRlcmZhY2UgTkFUU1B1YlN1YkNvbmZpZyB7XG5cdHNlcnZlcnM6IHN0cmluZ1tdO1xuXHR0b2tlbj86IHN0cmluZztcblx0dXNlcj86IHN0cmluZztcblx0cGFzcz86IHN0cmluZztcbn1cblxuaW50ZXJmYWNlIE5hdHNTdWJzY3JpcHRpb24ge1xuXHR1bnN1YnNjcmliZTogKCkgPT4gdm9pZCB8IFByb21pc2U8dm9pZD47XG59XG5cbmludGVyZmFjZSBOYXRzQ29ubmVjdGlvbiB7XG5cdGNsb3NlOiAoKSA9PiBQcm9taXNlPHZvaWQ+O1xuXHRkcmFpbjogKCkgPT4gUHJvbWlzZTx2b2lkPjtcblx0c3Vic2NyaWJlOiAoXG5cdFx0c3ViamVjdDogc3RyaW5nLFxuXHRcdG9wdHM/OiB7IHF1ZXVlPzogc3RyaW5nOyBjYWxsYmFjaz86IChlcnI6IEVycm9yIHwgbnVsbCwgbXNnOiBOYXRzTXNnKSA9PiB2b2lkIH0sXG5cdCkgPT4gTmF0c1N1YnNjcmlwdGlvbjtcblx0cHVibGlzaDogKHN1YmplY3Q6IHN0cmluZywgcGF5bG9hZDogVWludDhBcnJheSkgPT4gdm9pZDtcblx0amV0c3RyZWFtPzogKCkgPT4gTmF0c0pldFN0cmVhbTtcblx0amV0c3RyZWFtTWFuYWdlcj86ICgpID0+IFByb21pc2U8TmF0c0pldFN0cmVhbU1hbmFnZXI+O1xufVxuXG5pbnRlcmZhY2UgTmF0c0pldFN0cmVhbSB7XG5cdHN1YnNjcmliZTogKFxuXHRcdHN1YmplY3Q6IHN0cmluZyxcblx0XHRvcHRzPzogdW5rbm93bixcblx0KSA9PiBQcm9taXNlPHtcblx0XHRbU3ltYm9sLmFzeW5jSXRlcmF0b3JdOiAoKSA9PiBBc3luY0l0ZXJhdG9yPE5hdHNKc01zZz47XG5cdFx0dW5zdWJzY3JpYmU6ICgpID0+IFByb21pc2U8dm9pZD4gfCB2b2lkO1xuXHR9Pjtcblx0cHVibGlzaDogKHN1YmplY3Q6IHN0cmluZywgcGF5bG9hZDogVWludDhBcnJheSkgPT4gUHJvbWlzZTx1bmtub3duPjtcbn1cblxuaW50ZXJmYWNlIE5hdHNKZXRTdHJlYW1NYW5hZ2VyIHtcblx0c3RyZWFtczogeyBhZGQ6IChjb25maWc6IHVua25vd24pID0+IFByb21pc2U8dW5rbm93bj47IGluZm86IChuYW1lOiBzdHJpbmcpID0+IFByb21pc2U8dW5rbm93bj4gfTtcblx0Y29uc3VtZXJzOiB7IGFkZDogKHN0cmVhbTogc3RyaW5nLCBjb25maWc6IHVua25vd24pID0+IFByb21pc2U8dW5rbm93bj4gfTtcbn1cblxuaW50ZXJmYWNlIE5hdHNNc2cge1xuXHRzdWJqZWN0OiBzdHJpbmc7XG5cdGRhdGE6IFVpbnQ4QXJyYXk7XG5cdHNpZDogbnVtYmVyO1xuXHRyZXNwb25kPzogKGRhdGE/OiBVaW50OEFycmF5KSA9PiBib29sZWFuO1xufVxuXG5pbnRlcmZhY2UgTmF0c0pzTXNnIHtcblx0c3ViamVjdDogc3RyaW5nO1xuXHRkYXRhOiBVaW50OEFycmF5O1xuXHRzZXE6IG51bWJlcjtcblx0YWNrOiAoKSA9PiB2b2lkO1xuXHRuYWs6IChtaWxsaXM/OiBudW1iZXIpID0+IHZvaWQ7XG5cdGluZm86IHsgc3RyZWFtOiBzdHJpbmc7IGNvbnN1bWVyOiBzdHJpbmc7IHJlZGVsaXZlcnlDb3VudDogbnVtYmVyOyB0aW1lc3RhbXBOYW5vcz86IG51bWJlciB9O1xufVxuXG5jb25zdCBURVhUX0RFQ09ERVIgPSBuZXcgVGV4dERlY29kZXIoKTtcbmNvbnN0IFRFWFRfRU5DT0RFUiA9IG5ldyBUZXh0RW5jb2RlcigpO1xuXG5leHBvcnQgY2xhc3MgTkFUU1B1YlN1YkFkYXB0ZXIgaW1wbGVtZW50cyBQdWJTdWJBZGFwdGVyIHtcblx0cmVhZG9ubHkgcHJvdmlkZXIgPSBcIm5hdHNcIiBhcyBjb25zdDtcblx0cHJpdmF0ZSByZWFkb25seSBjb25maWc6IE5BVFNQdWJTdWJDb25maWc7XG5cdHByaXZhdGUgY29ubjogTmF0c0Nvbm5lY3Rpb24gfCBudWxsID0gbnVsbDtcblx0cHJpdmF0ZSBzdWJzY3JpcHRpb25zOiBNYXA8c3RyaW5nLCBOYXRzU3Vic2NyaXB0aW9uIHwgeyB1bnN1YnNjcmliZTogKCkgPT4gUHJvbWlzZTx2b2lkPiB8IHZvaWQgfT4gPSBuZXcgTWFwKCk7XG5cdHByaXZhdGUgY29ubmVjdGVkID0gZmFsc2U7XG5cblx0Y29uc3RydWN0b3IoY29uZmlnPzogUGFydGlhbDxOQVRTUHViU3ViQ29uZmlnPikge1xuXHRcdHRoaXMuY29uZmlnID0ge1xuXHRcdFx0c2VydmVyczogY29uZmlnPy5zZXJ2ZXJzID8/IChwcm9jZXNzLmVudi5OQVRTX1NFUlZFUlMgPz8gXCJsb2NhbGhvc3Q6NDIyMlwiKS5zcGxpdChcIixcIikubWFwKChzKSA9PiBzLnRyaW0oKSksXG5cdFx0XHR0b2tlbjogY29uZmlnPy50b2tlbiA/PyBwcm9jZXNzLmVudi5OQVRTX1RPS0VOLFxuXHRcdFx0dXNlcjogY29uZmlnPy51c2VyID8/IHByb2Nlc3MuZW52Lk5BVFNfVVNFUixcblx0XHRcdHBhc3M6IGNvbmZpZz8ucGFzcyA/PyBwcm9jZXNzLmVudi5OQVRTX1BBU1MsXG5cdFx0fTtcblx0fVxuXG5cdGFzeW5jIGNvbm5lY3QoKTogUHJvbWlzZTx2b2lkPiB7XG5cdFx0aWYgKHRoaXMuY29ubmVjdGVkKSByZXR1cm47XG5cdFx0dHJ5IHtcblx0XHRcdC8vIGJpb21lLWlnbm9yZSBsaW50L3N1c3BpY2lvdXMvbm9FeHBsaWNpdEFueTogbmF0cyBpcyBhIHJ1bnRpbWUgcGVlciBkZXAuXG5cdFx0XHRjb25zdCBuYXRzOiBhbnkgPSBhd2FpdCBpbXBvcnQoXCJuYXRzXCIpO1xuXHRcdFx0dGhpcy5jb25uID0gKGF3YWl0IG5hdHMuY29ubmVjdCh7XG5cdFx0XHRcdHNlcnZlcnM6IHRoaXMuY29uZmlnLnNlcnZlcnMsXG5cdFx0XHRcdHRva2VuOiB0aGlzLmNvbmZpZy50b2tlbixcblx0XHRcdFx0dXNlcjogdGhpcy5jb25maWcudXNlcixcblx0XHRcdFx0cGFzczogdGhpcy5jb25maWcucGFzcyxcblx0XHRcdH0pKSBhcyBOYXRzQ29ubmVjdGlvbjtcblx0XHRcdHRoaXMuY29ubmVjdGVkID0gdHJ1ZTtcblx0XHR9IGNhdGNoIChlcnIpIHtcblx0XHRcdHRocm93IG5ldyBFcnJvcihcblx0XHRcdFx0YFtibG9rXVtwdWJzdWItbmF0c10gY29ubmVjdCBmYWlsZWQ6ICR7KGVyciBhcyBFcnJvcikubWVzc2FnZX0uIEluc3RhbGwgbmF0cyBhcyBhIHBlZXIgZGVwZW5kZW5jeTogYnVuIGFkZCBuYXRzYCxcblx0XHRcdCk7XG5cdFx0fVxuXHR9XG5cblx0YXN5bmMgZGlzY29ubmVjdCgpOiBQcm9taXNlPHZvaWQ+IHtcblx0XHRpZiAoIXRoaXMuY29ubmVjdGVkKSByZXR1cm47XG5cdFx0Zm9yIChjb25zdCBzdWIgb2YgdGhpcy5zdWJzY3JpcHRpb25zLnZhbHVlcygpKSB7XG5cdFx0XHR0cnkge1xuXHRcdFx0XHRjb25zdCByZXN1bHQgPSBzdWIudW5zdWJzY3JpYmUoKTtcblx0XHRcdFx0aWYgKHJlc3VsdCBpbnN0YW5jZW9mIFByb21pc2UpIGF3YWl0IHJlc3VsdDtcblx0XHRcdH0gY2F0Y2gge1xuXHRcdFx0XHQvKiBpZ25vcmUgKi9cblx0XHRcdH1cblx0XHR9XG5cdFx0dGhpcy5zdWJzY3JpcHRpb25zLmNsZWFyKCk7XG5cdFx0dHJ5IHtcblx0XHRcdGF3YWl0IHRoaXMuY29ubj8uZHJhaW4oKTtcblx0XHR9IGNhdGNoIHtcblx0XHRcdC8qIGlnbm9yZSAqL1xuXHRcdH1cblx0XHR0aGlzLmNvbm4gPSBudWxsO1xuXHRcdHRoaXMuY29ubmVjdGVkID0gZmFsc2U7XG5cdH1cblxuXHRhc3luYyBzdWJzY3JpYmUoY29uZmlnOiBQdWJTdWJUcmlnZ2VyT3B0cywgaGFuZGxlcjogKG1lc3NhZ2U6IFB1YlN1Yk1lc3NhZ2UpID0+IFByb21pc2U8dm9pZD4pOiBQcm9taXNlPHZvaWQ+IHtcblx0XHRpZiAoIXRoaXMuY29ubmVjdGVkIHx8ICF0aGlzLmNvbm4pIHRocm93IG5ldyBFcnJvcihcIltibG9rXVtwdWJzdWItbmF0c10gbm90IGNvbm5lY3RlZC4gQ2FsbCBjb25uZWN0KCkgZmlyc3QuXCIpO1xuXHRcdGNvbnN0IHN1YktleSA9IGAke2NvbmZpZy50b3BpY30jJHtjb25maWcuY29uc3VtZXJHcm91cCA/PyBcIlwifWA7XG5cblx0XHRpZiAoY29uZmlnLmR1cmFibGUgPT09IHRydWUpIHtcblx0XHRcdC8vIEpldFN0cmVhbSBkdXJhYmxlIHN1YnNjcmlwdGlvbiDigJQgc3Vydml2ZXMgcmVzdGFydHMuXG5cdFx0XHRpZiAoIXRoaXMuY29ubi5qZXRzdHJlYW0pIHtcblx0XHRcdFx0dGhyb3cgbmV3IEVycm9yKFwiW2Jsb2tdW3B1YnN1Yi1uYXRzXSBkdXJhYmxlIHN1YnNjcmlwdGlvbnMgcmVxdWlyZSBKZXRTdHJlYW0gc3VwcG9ydCBpbiB0aGUgbmF0cyBjbGllbnRcIik7XG5cdFx0XHR9XG5cdFx0XHRjb25zdCBqc20gPSBhd2FpdCB0aGlzLmNvbm4uamV0c3RyZWFtTWFuYWdlcj8uKCk7XG5cdFx0XHRpZiAoanNtKSB7XG5cdFx0XHRcdC8vIEF1dG8tY3JlYXRlIGEgc3RyZWFtIGNvdmVyaW5nIHRoZSBzdWJqZWN0IGlmIG9uZSBkb2Vzbid0XG5cdFx0XHRcdC8vIGV4aXN0LiBQcm9kdWN0aW9uIGRlcGxveW1lbnRzIHNob3VsZCBwcmUtcHJvdmlzaW9uIHZpYVxuXHRcdFx0XHQvLyBgbmF0cyBzdHJlYW0gYWRkYCB0byBjb250cm9sIHJldGVudGlvbi5cblx0XHRcdFx0dHJ5IHtcblx0XHRcdFx0XHRhd2FpdCBqc20uc3RyZWFtcy5hZGQoe1xuXHRcdFx0XHRcdFx0bmFtZTogYGJsb2stJHsoY29uZmlnLnRvcGljID8/IFwiXCIpLnJlcGxhY2UoL1teYS16QS1aMC05X10vZywgXCJfXCIpfWAsXG5cdFx0XHRcdFx0XHRzdWJqZWN0czogW2NvbmZpZy50b3BpY10sXG5cdFx0XHRcdFx0fSk7XG5cdFx0XHRcdH0gY2F0Y2gge1xuXHRcdFx0XHRcdC8qIHN0cmVhbSBhbHJlYWR5IGV4aXN0cyDigJQgaWdub3JlICovXG5cdFx0XHRcdH1cblx0XHRcdH1cblx0XHRcdGNvbnN0IGpzID0gdGhpcy5jb25uLmpldHN0cmVhbSgpO1xuXHRcdFx0Y29uc3Qgc3RhcnRTZXEgPVxuXHRcdFx0XHR0eXBlb2YgY29uZmlnLnN0YXJ0RnJvbSA9PT0gXCJvYmplY3RcIiAmJiBjb25maWcuc3RhcnRGcm9tICYmIFwic2VxXCIgaW4gY29uZmlnLnN0YXJ0RnJvbVxuXHRcdFx0XHRcdD8gY29uZmlnLnN0YXJ0RnJvbS5zZXFcblx0XHRcdFx0XHQ6IHVuZGVmaW5lZDtcblx0XHRcdGNvbnN0IGRlbGl2ZXJQb2xpY3kgPVxuXHRcdFx0XHRjb25maWcuc3RhcnRGcm9tID09PSBcImVhcmxpZXN0XCJcblx0XHRcdFx0XHQ/IFwiYWxsXCJcblx0XHRcdFx0XHQ6IGNvbmZpZy5zdGFydEZyb20gPT09IFwibGF0ZXN0XCJcblx0XHRcdFx0XHRcdD8gXCJuZXdcIlxuXHRcdFx0XHRcdFx0OiBzdGFydFNlcSAhPT0gdW5kZWZpbmVkXG5cdFx0XHRcdFx0XHRcdD8gXCJieV9zdGFydF9zZXF1ZW5jZVwiXG5cdFx0XHRcdFx0XHRcdDogXCJhbGxcIjtcblx0XHRcdGNvbnN0IHN1YiA9IGF3YWl0IGpzLnN1YnNjcmliZShjb25maWcudG9waWMsIHtcblx0XHRcdFx0Y29uZmlnOiB7XG5cdFx0XHRcdFx0ZHVyYWJsZV9uYW1lOlxuXHRcdFx0XHRcdFx0Y29uZmlnLmNvbnN1bWVyR3JvdXAgPz9cblx0XHRcdFx0XHRcdGBibG9rLSR7KGNvbmZpZy5zdWJzY3JpcHRpb24gPz8gY29uZmlnLnRvcGljID8/IFwiZGVmYXVsdFwiKS5yZXBsYWNlKC9bXmEtekEtWjAtOV9dL2csIFwiX1wiKX1gLFxuXHRcdFx0XHRcdGRlbGl2ZXJfcG9saWN5OiBkZWxpdmVyUG9saWN5LFxuXHRcdFx0XHRcdG9wdF9zdGFydF9zZXE6IHN0YXJ0U2VxLFxuXHRcdFx0XHRcdGFja19wb2xpY3k6IGNvbmZpZy5hY2sgPT09IGZhbHNlID8gXCJub25lXCIgOiBcImV4cGxpY2l0XCIsXG5cdFx0XHRcdH0sXG5cdFx0XHR9KTtcblx0XHRcdHRoaXMuc3Vic2NyaXB0aW9ucy5zZXQoc3ViS2V5LCB7IHVuc3Vic2NyaWJlOiAoKSA9PiBzdWIudW5zdWJzY3JpYmUoKSB9KTtcblx0XHRcdC8vIERyaXZlIHRoZSBhc3luYyBpdGVyYXRvciBpbiBhIGJhY2tncm91bmQgbG9vcC5cblx0XHRcdHZvaWQgKGFzeW5jICgpID0+IHtcblx0XHRcdFx0dHJ5IHtcblx0XHRcdFx0XHRmb3IgYXdhaXQgKGNvbnN0IG1zZyBvZiBzdWIgYXMgdW5rbm93biBhcyBBc3luY0l0ZXJhYmxlPE5hdHNKc01zZz4pIHtcblx0XHRcdFx0XHRcdGF3YWl0IHRoaXMuZGlzcGF0Y2hKc01lc3NhZ2UobXNnLCBjb25maWcsIGhhbmRsZXIpO1xuXHRcdFx0XHRcdH1cblx0XHRcdFx0fSBjYXRjaCAoZXJyKSB7XG5cdFx0XHRcdFx0Ly8gU3Vic2NyaXB0aW9uIGNsb3NlZCBvciBjb25uZWN0aW9uIGxvc3Qg4oCUIGxldCB0aGUgdHJpZ2dlclxuXHRcdFx0XHRcdC8vIHJlLWxpc3RlbiB2aWEgSE1SL3JlY29ubmVjdCBsb2dpYy4gTG9nIGZvciB2aXNpYmlsaXR5LlxuXHRcdFx0XHRcdGNvbnNvbGUuZXJyb3IoYFtibG9rXVtwdWJzdWItbmF0c10gc3Vic2NyaXB0aW9uIGVycm9yOiAkeyhlcnIgYXMgRXJyb3IpLm1lc3NhZ2V9YCk7XG5cdFx0XHRcdH1cblx0XHRcdH0pKCk7XG5cdFx0XHRyZXR1cm47XG5cdFx0fVxuXG5cdFx0Ly8gQ29yZSBOQVRTIHN1YnNjcmlwdGlvbiDigJQgZmlyZS1hbmQtZm9yZ2V0LCB3aXRoIG9wdGlvbmFsIHF1ZXVlXG5cdFx0Ly8gZ3JvdXAgZm9yIGNvbXBldGluZy1jb25zdW1lciBzZW1hbnRpY3MuXG5cdFx0Y29uc3Qgc3ViID0gdGhpcy5jb25uLnN1YnNjcmliZShjb25maWcudG9waWMsIHtcblx0XHRcdHF1ZXVlOiBjb25maWcuY29uc3VtZXJHcm91cCxcblx0XHRcdGNhbGxiYWNrOiAoZXJyLCBtc2cpID0+IHtcblx0XHRcdFx0aWYgKGVycikge1xuXHRcdFx0XHRcdGNvbnNvbGUuZXJyb3IoYFtibG9rXVtwdWJzdWItbmF0c10gc3Vic2NyaWJlIGVycm9yOiAke2Vyci5tZXNzYWdlfWApO1xuXHRcdFx0XHRcdHJldHVybjtcblx0XHRcdFx0fVxuXHRcdFx0XHR2b2lkIHRoaXMuZGlzcGF0Y2hDb3JlTWVzc2FnZShtc2csIGNvbmZpZywgaGFuZGxlcik7XG5cdFx0XHR9LFxuXHRcdH0pO1xuXHRcdHRoaXMuc3Vic2NyaXB0aW9ucy5zZXQoc3ViS2V5LCBzdWIpO1xuXHR9XG5cblx0cHJpdmF0ZSBhc3luYyBkaXNwYXRjaEpzTWVzc2FnZShcblx0XHRtc2c6IE5hdHNKc01zZyxcblx0XHRjb25maWc6IFB1YlN1YlRyaWdnZXJPcHRzLFxuXHRcdGhhbmRsZXI6IChtZXNzYWdlOiBQdWJTdWJNZXNzYWdlKSA9PiBQcm9taXNlPHZvaWQ+LFxuXHQpOiBQcm9taXNlPHZvaWQ+IHtcblx0XHRjb25zdCB0ZXh0ID0gVEVYVF9ERUNPREVSLmRlY29kZShtc2cuZGF0YSk7XG5cdFx0bGV0IGJvZHk6IHVua25vd24gPSB0ZXh0O1xuXHRcdHRyeSB7XG5cdFx0XHRib2R5ID0gdGV4dC5sZW5ndGggPiAwID8gSlNPTi5wYXJzZSh0ZXh0KSA6IG51bGw7XG5cdFx0fSBjYXRjaCB7XG5cdFx0XHQvKiBsZWF2ZSBhcyB0ZXh0ICovXG5cdFx0fVxuXHRcdGNvbnN0IG1lc3NhZ2U6IFB1YlN1Yk1lc3NhZ2UgPSB7XG5cdFx0XHRpZDogYCR7bXNnLmluZm8uc3RyZWFtfToke21zZy5zZXF9YCxcblx0XHRcdGJvZHksXG5cdFx0XHRhdHRyaWJ1dGVzOiB7IHN1YmplY3Q6IG1zZy5zdWJqZWN0IH0sXG5cdFx0XHRyYXc6IG1zZyxcblx0XHRcdHRvcGljOiBtc2cuc3ViamVjdCxcblx0XHRcdHN1YnNjcmlwdGlvbjogbXNnLmluZm8uY29uc3VtZXIsXG5cdFx0XHRwdWJsaXNoVGltZTogbXNnLmluZm8udGltZXN0YW1wTmFub3MgPyBuZXcgRGF0ZShtc2cuaW5mby50aW1lc3RhbXBOYW5vcyAvIDFlNikgOiB1bmRlZmluZWQsXG5cdFx0XHRhY2s6IGFzeW5jICgpID0+IHtcblx0XHRcdFx0bXNnLmFjaygpO1xuXHRcdFx0fSxcblx0XHRcdG5hY2s6IGFzeW5jICgpID0+IHtcblx0XHRcdFx0bXNnLm5haygpO1xuXHRcdFx0fSxcblx0XHR9O1xuXHRcdHRyeSB7XG5cdFx0XHRhd2FpdCBoYW5kbGVyKG1lc3NhZ2UpO1xuXHRcdFx0aWYgKGNvbmZpZy5hY2sgIT09IGZhbHNlKSBtc2cuYWNrKCk7XG5cdFx0fSBjYXRjaCB7XG5cdFx0XHRtc2cubmFrKCk7XG5cdFx0fVxuXHR9XG5cblx0cHJpdmF0ZSBhc3luYyBkaXNwYXRjaENvcmVNZXNzYWdlKFxuXHRcdG1zZzogTmF0c01zZyxcblx0XHRfY29uZmlnOiBQdWJTdWJUcmlnZ2VyT3B0cyxcblx0XHRoYW5kbGVyOiAobWVzc2FnZTogUHViU3ViTWVzc2FnZSkgPT4gUHJvbWlzZTx2b2lkPixcblx0KTogUHJvbWlzZTx2b2lkPiB7XG5cdFx0Y29uc3QgdGV4dCA9IFRFWFRfREVDT0RFUi5kZWNvZGUobXNnLmRhdGEpO1xuXHRcdGxldCBib2R5OiB1bmtub3duID0gdGV4dDtcblx0XHR0cnkge1xuXHRcdFx0Ym9keSA9IHRleHQubGVuZ3RoID4gMCA/IEpTT04ucGFyc2UodGV4dCkgOiBudWxsO1xuXHRcdH0gY2F0Y2gge1xuXHRcdFx0LyogbGVhdmUgYXMgdGV4dCAqL1xuXHRcdH1cblx0XHRjb25zdCBtZXNzYWdlOiBQdWJTdWJNZXNzYWdlID0ge1xuXHRcdFx0aWQ6IGAke21zZy5zdWJqZWN0fToke21zZy5zaWR9OiR7dXVpZCgpfWAsXG5cdFx0XHRib2R5LFxuXHRcdFx0YXR0cmlidXRlczogeyBzdWJqZWN0OiBtc2cuc3ViamVjdCB9LFxuXHRcdFx0cmF3OiBtc2csXG5cdFx0XHR0b3BpYzogbXNnLnN1YmplY3QsXG5cdFx0XHRhY2s6IGFzeW5jICgpID0+IHtcblx0XHRcdFx0LyogY29yZSBOQVRTIGhhcyBubyBleHBsaWNpdCBhY2sgKi9cblx0XHRcdH0sXG5cdFx0XHRuYWNrOiBhc3luYyAoKSA9PiB7XG5cdFx0XHRcdC8qIGNvcmUgTkFUUyBoYXMgbm8gZXhwbGljaXQgbmFjayAqL1xuXHRcdFx0fSxcblx0XHR9O1xuXHRcdHRyeSB7XG5cdFx0XHRhd2FpdCBoYW5kbGVyKG1lc3NhZ2UpO1xuXHRcdH0gY2F0Y2ggKGVycikge1xuXHRcdFx0Y29uc29sZS5lcnJvcihgW2Jsb2tdW3B1YnN1Yi1uYXRzXSBoYW5kbGVyIGVycm9yOiAkeyhlcnIgYXMgRXJyb3IpLm1lc3NhZ2V9YCk7XG5cdFx0fVxuXHR9XG5cblx0YXN5bmMgdW5zdWJzY3JpYmUoc3Vic2NyaXB0aW9uOiBzdHJpbmcpOiBQcm9taXNlPHZvaWQ+IHtcblx0XHRjb25zdCBzdWIgPSB0aGlzLnN1YnNjcmlwdGlvbnMuZ2V0KHN1YnNjcmlwdGlvbik7XG5cdFx0aWYgKCFzdWIpIHJldHVybjtcblx0XHR0cnkge1xuXHRcdFx0Y29uc3QgcmVzdWx0ID0gc3ViLnVuc3Vic2NyaWJlKCk7XG5cdFx0XHRpZiAocmVzdWx0IGluc3RhbmNlb2YgUHJvbWlzZSkgYXdhaXQgcmVzdWx0O1xuXHRcdH0gY2F0Y2gge1xuXHRcdFx0LyogaWdub3JlICovXG5cdFx0fVxuXHRcdHRoaXMuc3Vic2NyaXB0aW9ucy5kZWxldGUoc3Vic2NyaXB0aW9uKTtcblx0fVxuXG5cdGFzeW5jIHB1Ymxpc2godG9waWM6IHN0cmluZywgcGF5bG9hZDogdW5rbm93bik6IFByb21pc2U8dm9pZD4ge1xuXHRcdGlmICghdGhpcy5jb25uZWN0ZWQgfHwgIXRoaXMuY29ubikgdGhyb3cgbmV3IEVycm9yKFwiW2Jsb2tdW3B1YnN1Yi1uYXRzXSBub3QgY29ubmVjdGVkLiBDYWxsIGNvbm5lY3QoKSBmaXJzdC5cIik7XG5cdFx0Y29uc3QgYm9keSA9IHR5cGVvZiBwYXlsb2FkID09PSBcInN0cmluZ1wiID8gcGF5bG9hZCA6IEpTT04uc3RyaW5naWZ5KHBheWxvYWQpO1xuXHRcdGNvbnN0IGRhdGEgPSBURVhUX0VOQ09ERVIuZW5jb2RlKGJvZHkpO1xuXHRcdC8vIFVzZSBjb3JlIE5BVFMgcHVibGlzaC4gV2hlbiBhIGR1cmFibGUgc3Vic2NyaWJlciBoYXMgYmVlblxuXHRcdC8vIGluc3RhbGxlZCBmb3IgdGhpcyBzdWJqZWN0LCB0aGUgc3Vic2NyaWJlKCkgcGF0aCBjcmVhdGVkIGFcblx0XHQvLyBKZXRTdHJlYW0gc3RyZWFtIHdpdGggYSBzdWJqZWN0IGZpbHRlciB0aGF0IGNhcHR1cmVzIGFueVxuXHRcdC8vIHB1Ymxpc2ggdG8gYHRvcGljYCDigJQgc28gZHVyYWJsZSBjb25zdW1lcnMgc2VlIHRoZSBtZXNzYWdlIHZpYVxuXHRcdC8vIHRoZSBzdHJlYW0gd2hpbGUgY29yZSBzdWJzY3JpYmVycyBzZWUgaXQgZGlyZWN0bHkuIFRoZSBlYXJsaWVyXG5cdFx0Ly8gXCJ0cnkganMucHVibGlzaCBmaXJzdCwgZmFsbCBiYWNrIHRvIGNvcmVcIiBsb2dpYyBjYXVzZWRcblx0XHQvLyAqKmRvdWJsZS1kZWxpdmVyeSoqOiBqcy5wdWJsaXNoIHRvIGEgc3ViamVjdCBjb3ZlcmVkIGJ5IGFcblx0XHQvLyBzdHJlYW0gZ29lcyB0byBCT1RIIHRoZSBzdHJlYW0gYW5kIGNvcmUgc3Vic2NyaWJlcnMsIHRoZW4gdGhlXG5cdFx0Ly8gZmFsbGJhY2sgd291bGQgcHVibGlzaCBBR0FJTiBpZiBqcy5wdWJsaXNoIHRpbWVkIG91dCAodnNcblx0XHQvLyByZXR1cm5pbmcgNTAzIGZhc3QpLiBTdGlja2luZyB0byBjb3JlIHB1Ymxpc2ggYXZvaWRzIHRoZSByYWNlLlxuXHRcdHRoaXMuY29ubi5wdWJsaXNoKHRvcGljLCBkYXRhKTtcblx0fVxuXG5cdGlzQ29ubmVjdGVkKCk6IGJvb2xlYW4ge1xuXHRcdHJldHVybiB0aGlzLmNvbm5lY3RlZDtcblx0fVxuXG5cdGFzeW5jIGhlYWx0aENoZWNrKCk6IFByb21pc2U8Ym9vbGVhbj4ge1xuXHRcdHJldHVybiB0aGlzLmNvbm5lY3RlZDtcblx0fVxufVxuIl19
@@ -0,0 +1,49 @@
1
+ /**
2
+ * RedisStreamsPubSubAdapter — v0.7 PR 6 — Pub/Sub adapter backed by
3
+ * Redis Streams via `ioredis`.
4
+ *
5
+ * Pub/Sub vs Worker semantics: this adapter uses **distinct consumer
6
+ * groups per subscriber** so multiple subscribers each receive every
7
+ * message (fan-out). When `consumerGroup` is explicitly set on the
8
+ * workflow, all subscribers in the group compete (1 of N gets each).
9
+ *
10
+ * Replay cursors:
11
+ * - `"earliest"` / unset → `$` (only new messages by default).
12
+ * - `"earliest"` with explicit intent → `0` (replay full stream).
13
+ * - `{seq: N}` → resume from stream id `N-0`.
14
+ *
15
+ * Requires `ioredis` as a peer dependency.
16
+ *
17
+ * Environment variables:
18
+ * - `REDIS_HOST` (default `localhost`).
19
+ * - `REDIS_PORT` (default `6379`).
20
+ * - `REDIS_PASSWORD`.
21
+ * - `REDIS_DB`.
22
+ */
23
+ import type { PubSubTriggerOpts } from "@blokjs/helper";
24
+ import type { PubSubAdapter, PubSubMessage } from "../PubSubTrigger";
25
+ export interface RedisStreamsPubSubConfig {
26
+ host: string;
27
+ port: number;
28
+ password?: string;
29
+ db?: number;
30
+ blockMs: number;
31
+ count: number;
32
+ }
33
+ export declare class RedisStreamsPubSubAdapter implements PubSubAdapter {
34
+ readonly provider: "redis-streams";
35
+ private readonly config;
36
+ private client;
37
+ private subscriptions;
38
+ private connected;
39
+ private consumerName;
40
+ constructor(config?: Partial<RedisStreamsPubSubConfig>);
41
+ connect(): Promise<void>;
42
+ disconnect(): Promise<void>;
43
+ subscribe(config: PubSubTriggerOpts, handler: (message: PubSubMessage) => Promise<void>): Promise<void>;
44
+ private fieldsToObject;
45
+ unsubscribe(subscription: string): Promise<void>;
46
+ publish(topic: string, payload: unknown): Promise<void>;
47
+ isConnected(): boolean;
48
+ healthCheck(): Promise<boolean>;
49
+ }
@@ -0,0 +1,193 @@
1
+ /**
2
+ * RedisStreamsPubSubAdapter — v0.7 PR 6 — Pub/Sub adapter backed by
3
+ * Redis Streams via `ioredis`.
4
+ *
5
+ * Pub/Sub vs Worker semantics: this adapter uses **distinct consumer
6
+ * groups per subscriber** so multiple subscribers each receive every
7
+ * message (fan-out). When `consumerGroup` is explicitly set on the
8
+ * workflow, all subscribers in the group compete (1 of N gets each).
9
+ *
10
+ * Replay cursors:
11
+ * - `"earliest"` / unset → `$` (only new messages by default).
12
+ * - `"earliest"` with explicit intent → `0` (replay full stream).
13
+ * - `{seq: N}` → resume from stream id `N-0`.
14
+ *
15
+ * Requires `ioredis` as a peer dependency.
16
+ *
17
+ * Environment variables:
18
+ * - `REDIS_HOST` (default `localhost`).
19
+ * - `REDIS_PORT` (default `6379`).
20
+ * - `REDIS_PASSWORD`.
21
+ * - `REDIS_DB`.
22
+ */
23
+ import { v4 as uuid } from "uuid";
24
+ export class RedisStreamsPubSubAdapter {
25
+ provider = "redis-streams";
26
+ config;
27
+ client = null;
28
+ subscriptions = new Map();
29
+ connected = false;
30
+ consumerName = `blok-pubsub-${uuid().slice(0, 8)}`;
31
+ constructor(config) {
32
+ this.config = {
33
+ host: config?.host ?? process.env.REDIS_HOST ?? "localhost",
34
+ port: config?.port ?? Number.parseInt(process.env.REDIS_PORT ?? "6379", 10),
35
+ password: config?.password ?? process.env.REDIS_PASSWORD,
36
+ db: config?.db ?? Number.parseInt(process.env.REDIS_DB ?? "0", 10),
37
+ blockMs: config?.blockMs ?? 5000,
38
+ count: config?.count ?? 10,
39
+ };
40
+ }
41
+ async connect() {
42
+ if (this.connected)
43
+ return;
44
+ try {
45
+ // biome-ignore lint/suspicious/noExplicitAny: ioredis is a runtime peer dep.
46
+ const ioredis = await import("ioredis");
47
+ const IORedis = ioredis.default ?? ioredis.Redis ?? ioredis;
48
+ this.client = new IORedis({
49
+ host: this.config.host,
50
+ port: this.config.port,
51
+ password: this.config.password,
52
+ db: this.config.db,
53
+ });
54
+ await this.client.ping();
55
+ this.connected = true;
56
+ }
57
+ catch (err) {
58
+ throw new Error(`[blok][pubsub-redis] connect failed: ${err.message}. Install ioredis as a peer dependency: bun add ioredis`);
59
+ }
60
+ }
61
+ async disconnect() {
62
+ if (!this.connected)
63
+ return;
64
+ for (const sub of this.subscriptions.values())
65
+ sub.stop();
66
+ this.subscriptions.clear();
67
+ try {
68
+ await this.client?.quit();
69
+ }
70
+ catch {
71
+ /* ignore */
72
+ }
73
+ this.client = null;
74
+ this.connected = false;
75
+ }
76
+ async subscribe(config, handler) {
77
+ if (!this.connected || !this.client)
78
+ throw new Error("[blok][pubsub-redis] not connected. Call connect() first.");
79
+ const client = this.client;
80
+ const stream = config.topic;
81
+ // Fan-out: each subscriber gets its own group (unique per instance).
82
+ // Competing-consumer: explicit `consumerGroup` makes all subscribers
83
+ // share work.
84
+ const group = config.consumerGroup ?? `blok-fanout-${this.consumerName}-${stream.replace(/[^a-zA-Z0-9_]/g, "_")}`;
85
+ const startId = config.startFrom === "earliest"
86
+ ? "0"
87
+ : config.startFrom === "latest" || config.startFrom === undefined
88
+ ? "$"
89
+ : typeof config.startFrom === "object" && "seq" in config.startFrom
90
+ ? `${config.startFrom.seq}-0`
91
+ : "$";
92
+ try {
93
+ await client.xgroup("CREATE", stream, group, startId, "MKSTREAM");
94
+ }
95
+ catch (err) {
96
+ if (!/BUSYGROUP/i.test(err.message))
97
+ throw err;
98
+ }
99
+ let stopped = false;
100
+ const sub = {
101
+ stop: () => {
102
+ stopped = true;
103
+ },
104
+ };
105
+ this.subscriptions.set(`${stream}#${group}`, sub);
106
+ void (async () => {
107
+ while (!stopped) {
108
+ let entries = null;
109
+ try {
110
+ entries = await client.xreadgroup("GROUP", group, this.consumerName, "COUNT", String(this.config.count), "BLOCK", String(this.config.blockMs), "STREAMS", stream, ">");
111
+ }
112
+ catch {
113
+ await new Promise((r) => setTimeout(r, 1000));
114
+ continue;
115
+ }
116
+ if (!entries)
117
+ continue;
118
+ for (const [, msgs] of entries) {
119
+ for (const [id, fields] of msgs) {
120
+ if (stopped)
121
+ break;
122
+ const payload = this.fieldsToObject(fields);
123
+ const dataString = typeof payload.data === "string" ? payload.data : "";
124
+ let body;
125
+ try {
126
+ body = dataString.length > 0 ? JSON.parse(dataString) : null;
127
+ }
128
+ catch {
129
+ body = dataString;
130
+ }
131
+ const message = {
132
+ id,
133
+ body,
134
+ attributes: payload,
135
+ raw: { id, fields },
136
+ topic: stream,
137
+ subscription: group,
138
+ publishTime: new Date(Number.parseInt(id.split("-")[0] ?? String(Date.now()), 10)),
139
+ ack: async () => {
140
+ await client.xack(stream, group, id);
141
+ },
142
+ nack: async () => {
143
+ /* leave unacked — picked up by XAUTOCLAIM */
144
+ },
145
+ };
146
+ try {
147
+ await handler(message);
148
+ if (config.ack !== false)
149
+ await client.xack(stream, group, id);
150
+ }
151
+ catch {
152
+ // Leave unacked — pending entries are visible in XPENDING.
153
+ }
154
+ }
155
+ }
156
+ }
157
+ })();
158
+ }
159
+ fieldsToObject(fields) {
160
+ const out = {};
161
+ for (let i = 0; i < fields.length; i += 2)
162
+ out[fields[i]] = fields[i + 1];
163
+ return out;
164
+ }
165
+ async unsubscribe(subscription) {
166
+ const sub = this.subscriptions.get(subscription);
167
+ if (!sub)
168
+ return;
169
+ sub.stop();
170
+ this.subscriptions.delete(subscription);
171
+ }
172
+ async publish(topic, payload) {
173
+ if (!this.connected || !this.client)
174
+ throw new Error("[blok][pubsub-redis] not connected. Call connect() first.");
175
+ const body = typeof payload === "string" ? payload : JSON.stringify(payload);
176
+ await this.client.xadd(topic, "*", "data", body);
177
+ }
178
+ isConnected() {
179
+ return this.connected;
180
+ }
181
+ async healthCheck() {
182
+ if (!this.connected || !this.client)
183
+ return false;
184
+ try {
185
+ const pong = await this.client.ping();
186
+ return pong === "PONG";
187
+ }
188
+ catch {
189
+ return false;
190
+ }
191
+ }
192
+ }
193
+ //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiUmVkaXNTdHJlYW1zUHViU3ViQWRhcHRlci5qcyIsInNvdXJjZVJvb3QiOiIiLCJzb3VyY2VzIjpbIi4uLy4uL3NyYy9hZGFwdGVycy9SZWRpc1N0cmVhbXNQdWJTdWJBZGFwdGVyLnRzIl0sIm5hbWVzIjpbXSwibWFwcGluZ3MiOiJBQUFBOzs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7R0FxQkc7QUFHSCxPQUFPLEVBQUUsRUFBRSxJQUFJLElBQUksRUFBRSxNQUFNLE1BQU0sQ0FBQztBQXlCbEMsTUFBTSxPQUFPLHlCQUF5QjtJQUM1QixRQUFRLEdBQUcsZUFBd0IsQ0FBQztJQUM1QixNQUFNLENBQTJCO0lBQzFDLE1BQU0sR0FBdUIsSUFBSSxDQUFDO0lBQ2xDLGFBQWEsR0FBb0MsSUFBSSxHQUFHLEVBQUUsQ0FBQztJQUMzRCxTQUFTLEdBQUcsS0FBSyxDQUFDO0lBQ2xCLFlBQVksR0FBRyxlQUFlLElBQUksRUFBRSxDQUFDLEtBQUssQ0FBQyxDQUFDLEVBQUUsQ0FBQyxDQUFDLEVBQUUsQ0FBQztJQUUzRCxZQUFZLE1BQTBDO1FBQ3JELElBQUksQ0FBQyxNQUFNLEdBQUc7WUFDYixJQUFJLEVBQUUsTUFBTSxFQUFFLElBQUksSUFBSSxPQUFPLENBQUMsR0FBRyxDQUFDLFVBQVUsSUFBSSxXQUFXO1lBQzNELElBQUksRUFBRSxNQUFNLEVBQUUsSUFBSSxJQUFJLE1BQU0sQ0FBQyxRQUFRLENBQUMsT0FBTyxDQUFDLEdBQUcsQ0FBQyxVQUFVLElBQUksTUFBTSxFQUFFLEVBQUUsQ0FBQztZQUMzRSxRQUFRLEVBQUUsTUFBTSxFQUFFLFFBQVEsSUFBSSxPQUFPLENBQUMsR0FBRyxDQUFDLGNBQWM7WUFDeEQsRUFBRSxFQUFFLE1BQU0sRUFBRSxFQUFFLElBQUksTUFBTSxDQUFDLFFBQVEsQ0FBQyxPQUFPLENBQUMsR0FBRyxDQUFDLFFBQVEsSUFBSSxHQUFHLEVBQUUsRUFBRSxDQUFDO1lBQ2xFLE9BQU8sRUFBRSxNQUFNLEVBQUUsT0FBTyxJQUFJLElBQUk7WUFDaEMs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QUFJLENBQUMsSUFBSSxDQUFDLFNBQVMsSUFBSSxDQUFDLElBQUksQ0FBQyxNQUFNO1lBQUUsTUFBTSxJQUFJLEtBQUssQ0FBQywyREFBMkQsQ0FBQyxDQUFDO1FBQ2xILE1BQU0sSUFBSSxHQUFHLE9BQU8sT0FBTyxLQUFLLFFBQVEsQ0FBQyxDQUFDLENBQUMsT0FBTyxDQUFDLENBQUMsQ0FBQyxJQUFJLENBQUMsU0FBUyxDQUFDLE9BQU8sQ0FBQyxDQUFDO1FBQzdFLE1BQU0sSUFBSSxDQUFDLE1BQU0sQ0FBQyxJQUFJLENBQUMsS0FBSyxFQUFFLEdBQUcsRUFBRSxNQUFNLEVBQUUsSUFBSSxDQUFDLENBQUM7SUFDbEQsQ0FBQztJQUVELFdBQVc7UUFDVixPQUFPLElBQUksQ0FBQyxTQUFTLENBQUM7SUFDdkIsQ0FBQztJQUVELEtBQUssQ0FBQyxXQUFXO1FBQ2hCLElBQUksQ0FBQyxJQUFJLENBQUMsU0FBUyxJQUFJLENBQUMsSUFBSSxDQUFDLE1BQU07WUFBRSxPQUFPLEtBQUssQ0FBQztRQUNsRCxJQUFJLENBQUM7WUFDSixNQUFNLElBQUksR0FBRyxNQUFNLElBQUksQ0FBQyxNQUFNLENBQUMsSUFBSSxFQUFFLENBQUM7WUFDdEMsT0FBTyxJQUFJLEtBQUssTUFBTSxDQUFDO1FBQ3hCLENBQUM7UUFBQyxNQUFNLENBQUM7WUFDUixPQUFPLEtBQUssQ0FBQztRQUNkLENBQUM7SUFDRixDQUFDO0NBQ0QiLCJzb3VyY2VzQ29udGVudCI6WyIvKipcbiAqIFJlZGlzU3RyZWFtc1B1YlN1YkFkYXB0ZXIg4oCUIHYwLjcgUFIgNiDigJQgUHViL1N1YiBhZGFwdGVyIGJhY2tlZCBieVxuICogUmVkaXMgU3RyZWFtcyB2aWEgYGlvcmVkaXNgLlxuICpcbiAqIFB1Yi9TdWIgdnMgV29ya2VyIHNlbWFudGljczogdGhpcyBhZGFwdGVyIHVzZXMgKipkaXN0aW5jdCBjb25zdW1lclxuICogZ3JvdXBzIHBlciBzdWJzY3JpYmVyKiogc28gbXVsdGlwbGUgc3Vic2NyaWJlcnMgZWFjaCByZWNlaXZlIGV2ZXJ5XG4gKiBtZXNzYWdlIChmYW4tb3V0KS4gV2hlbiBgY29uc3VtZXJHcm91cGAgaXMgZXhwbGljaXRseSBzZXQgb24gdGhlXG4gKiB3b3JrZmxvdywgYWxsIHN1YnNjcmliZXJzIGluIHRoZSBncm91cCBjb21wZXRlICgxIG9mIE4gZ2V0cyBlYWNoKS5cbiAqXG4gKiBSZXBsYXkgY3Vyc29yczpcbiAqICAgLSBgXCJlYXJsaWVzdFwiYCAvIHVuc2V0IOKGkiBgJGAgKG9ubHkgbmV3IG1lc3NhZ2VzIGJ5IGRlZmF1bHQpLlxuICogICAtIGBcImVhcmxpZXN0XCJgIHdpdGggZXhwbGljaXQgaW50ZW50IOKGkiBgMGAgKHJlcGxheSBmdWxsIHN0cmVhbSkuXG4gKiAgIC0gYHtzZXE6IE59YCDihpIgcmVzdW1lIGZyb20gc3RyZWFtIGlkIGBOLTBgLlxuICpcbiAqIFJlcXVpcmVzIGBpb3JlZGlzYCBhcyBhIHBlZXIgZGVwZW5kZW5jeS5cbiAqXG4gKiBFbnZpcm9ubWVudCB2YXJpYWJsZXM6XG4gKiAgIC0gYFJFRElTX0hPU1RgIChkZWZhdWx0IGBsb2NhbGhvc3RgKS5cbiAqICAgLSBgUkVESVNfUE9SVGAgKGRlZmF1bHQgYDYzNzlgKS5cbiAqICAgLSBgUkVESVNfUEFTU1dPUkRgLlxuICogICAtIGBSRURJU19EQmAuXG4gKi9cblxuaW1wb3J0IHR5cGUgeyBQdWJTdWJUcmlnZ2VyT3B0cyB9IGZyb20gXCJAYmxva2pzL2hlbHBlclwiO1xuaW1wb3J0IHsgdjQgYXMgdXVpZCB9IGZyb20gXCJ1dWlkXCI7XG5pbXBvcnQgdHlwZSB7IFB1YlN1YkFkYXB0ZXIsIFB1YlN1Yk1lc3NhZ2UgfSBmcm9tIFwiLi4vUHViU3ViVHJpZ2dlclwiO1xuXG5leHBvcnQgaW50ZXJmYWNlIFJlZGlzU3RyZWFtc1B1YlN1YkNvbmZpZyB7XG5cdGhvc3Q6IHN0cmluZztcblx0cG9ydDogbnVtYmVyO1xuXHRwYXNzd29yZD86IHN0cmluZztcblx0ZGI/OiBudW1iZXI7XG5cdGJsb2NrTXM6IG51bWJlcjtcblx0Y291bnQ6IG51bWJlcjtcbn1cblxuaW50ZXJmYWNlIFJlZGlzQ2xpZW50IHtcblx0eGFkZChzdHJlYW06IHN0cmluZywgLi4uYXJnczogc3RyaW5nW10pOiBQcm9taXNlPHN0cmluZz47XG5cdHhyZWFkZ3JvdXAoLi4uYXJnczogc3RyaW5nW10pOiBQcm9taXNlPEFycmF5PFtzdHJpbmcsIEFycmF5PFtzdHJpbmcsIHN0cmluZ1tdXT5dPiB8IG51bGw+O1xuXHR4Z3JvdXAoLi4uYXJnczogc3RyaW5nW10pOiBQcm9taXNlPHN0cmluZz47XG5cdHhhY2soc3RyZWFtOiBzdHJpbmcsIGdyb3VwOiBzdHJpbmcsIC4uLmlkczogc3RyaW5nW10pOiBQcm9taXNlPG51bWJlcj47XG5cdHBpbmcoKTogUHJvbWlzZTxzdHJpbmc+O1xuXHRxdWl0KCk6IFByb21pc2U8c3RyaW5nPjtcbn1cblxuaW50ZXJmYWNlIEFjdGl2ZVN1YnNjcmlwdGlvbiB7XG5cdHN0b3A6ICgpID0+IHZvaWQ7XG59XG5cbmV4cG9ydCBjbGFzcyBSZWRpc1N0cmVhbXNQdWJTdWJBZGFwdGVyIGltcGxlbWVudHMgUHViU3ViQWRhcHRlciB7XG5cdHJlYWRvbmx5IHByb3ZpZGVyID0gXCJyZWRpcy1zdHJlYW1zXCIgYXMgY29uc3Q7XG5cdHByaXZhdGUgcmVhZG9ubHkgY29uZmlnOiBSZWRpc1N0cmVhbXNQdWJTdWJDb25maWc7XG5cdHByaXZhdGUgY2xpZW50OiBSZWRpc0NsaWVudCB8IG51bGwgPSBudWxsO1xuXHRwcml2YXRlIHN1YnNjcmlwdGlvbnM6IE1hcDxzdHJpbmcsIEFjdGl2ZVN1YnNjcmlwdGlvbj4gPSBuZXcgTWFwKCk7XG5cdHByaXZhdGUgY29ubmVjdGVkID0gZmFsc2U7XG5cdHByaXZhdGUgY29uc3VtZXJOYW1lID0gYGJsb2stcHVic3ViLSR7dXVpZCgpLnNsaWNlKDAsIDgpfWA7XG5cblx0Y29uc3RydWN0b3IoY29uZmlnPzogUGFydGlhbDxSZWRpc1N0cmVhbXNQdWJTdWJDb25maWc+KSB7XG5cdFx0dGhpcy5jb25maWcgPSB7XG5cdFx0XHRob3N0OiBjb25maWc/Lmhvc3QgPz8gcHJvY2Vzcy5lbnYuUkVESVNfSE9TVCA/PyBcImxvY2FsaG9zdFwiLFxuXHRcdFx0cG9ydDogY29uZmlnPy5wb3J0ID8/IE51bWJlci5wYXJzZUludChwcm9jZXNzLmVudi5SRURJU19QT1JUID8/IFwiNjM3OVwiLCAxMCksXG5cdFx0XHRwYXNzd29yZDogY29uZmlnPy5wYXNzd29yZCA/PyBwcm9jZXNzLmVudi5SRURJU19QQVNTV09SRCxcblx0XHRcdGRiOiBjb25maWc/LmRiID8/IE51bWJlci5wYXJzZUludChwcm9jZXNzLmVudi5SRURJU19EQiA/PyBcIjBcIiwgMTApLFxuXHRcdFx0YmxvY2tNczogY29uZmlnPy5ibG9ja01zID8/IDUwMDAsXG5cdFx0XHRjb3VudDogY29uZmlnPy5jb3VudCA/PyAxMCxcblx0XHR9O1xuXHR9XG5cblx0YXN5bmMgY29ubmVjdCgpOiBQcm9taXNlPHZvaWQ+IHtcblx0XHRpZiAodGhpcy5jb25uZWN0ZWQpIHJldHVybjtcblx0XHR0cnkge1xuXHRcdFx0Ly8gYmlvbWUtaWdub3JlIGxpbnQvc3VzcGljaW91cy9ub0V4cGxpY2l0QW55OiBpb3JlZGlzIGlzIGEgcnVudGltZSBwZWVyIGRlcC5cblx0XHRcdGNvbnN0IGlvcmVkaXM6IGFueSA9IGF3YWl0IGltcG9ydChcImlvcmVkaXNcIik7XG5cdFx0XHRjb25zdCBJT1JlZGlzID0gaW9yZWRpcy5kZWZhdWx0ID8/IGlvcmVkaXMuUmVkaXMgPz8gaW9yZWRpcztcblx0XHRcdHRoaXMuY2xpZW50ID0gbmV3IElPUmVkaXMoe1xuXHRcdFx0XHRob3N0OiB0aGlzLmNvbmZpZy5ob3N0LFxuXHRcdFx0XHRwb3J0OiB0aGlzLmNvbmZpZy5wb3J0LFxuXHRcdFx0XHRwYXNzd29yZDogdGhpcy5jb25maWcucGFzc3dvcmQsXG5cdFx0XHRcdGRiOiB0aGlzLmNvbmZpZy5kYixcblx0XHRcdH0pIGFzIFJlZGlzQ2xpZW50O1xuXHRcdFx0YXdhaXQgdGhpcy5jbGllbnQucGluZygpO1xuXHRcdFx0dGhpcy5jb25uZWN0ZWQgPSB0cnVlO1xuXHRcdH0gY2F0Y2ggKGVycikge1xuXHRcdFx0dGhyb3cgbmV3IEVycm9yKFxuXHRcdFx0XHRgW2Jsb2tdW3B1YnN1Yi1yZWRpc10gY29ubmVjdCBmYWlsZWQ6ICR7KGVyciBhcyBFcnJvcikubWVzc2FnZX0uIEluc3RhbGwgaW9yZWRpcyBhcyBhIHBlZXIgZGVwZW5kZW5jeTogYnVuIGFkZCBpb3JlZGlzYCxcblx0XHRcdCk7XG5cdFx0fVxuXHR9XG5cblx0YXN5bmMgZGlzY29ubmVjdCgpOiBQcm9taXNlPHZvaWQ+IHtcblx0XHRpZiAoIXRoaXMuY29ubmVjdGVkKSByZXR1cm47XG5cdFx0Zm9yIChjb25zdCBzdWIgb2YgdGhpcy5zdWJzY3JpcHRpb25zLnZhbHVlcygpKSBzdWIuc3RvcCgpO1xuXHRcdHRoaXMuc3Vic2NyaXB0aW9ucy5jbGVhcigpO1xuXHRcdHRyeSB7XG5cdFx0XHRhd2FpdCB0aGlzLmNsaWVudD8ucXVpdCgpO1xuXHRcdH0gY2F0Y2gge1xuXHRcdFx0LyogaWdub3JlICovXG5cdFx0fVxuXHRcdHRoaXMuY2xpZW50ID0gbnVsbDtcblx0XHR0aGlzLmNvbm5lY3RlZCA9IGZhbHNlO1xuXHR9XG5cblx0YXN5bmMgc3Vic2NyaWJlKGNvbmZpZzogUHViU3ViVHJpZ2dlck9wdHMsIGhhbmRsZXI6IChtZXNzYWdlOiBQdWJTdWJNZXNzYWdlKSA9PiBQcm9taXNlPHZvaWQ+KTogUHJvbWlzZTx2b2lkPiB7XG5cdFx0aWYgKCF0aGlzLmNvbm5lY3RlZCB8fCAhdGhpcy5jbGllbnQpIHRocm93IG5ldyBFcnJvcihcIltibG9rXVtwdWJzdWItcmVkaXNdIG5vdCBjb25uZWN0ZWQuIENhbGwgY29ubmVjdCgpIGZpcnN0LlwiKTtcblx0XHRjb25zdCBjbGllbnQgPSB0aGlzLmNsaWVudDtcblx0XHRjb25zdCBzdHJlYW0gPSBjb25maWcudG9waWM7XG5cdFx0Ly8gRmFuLW91dDogZWFjaCBzdWJzY3JpYmVyIGdldHMgaXRzIG93biBncm91cCAodW5pcXVlIHBlciBpbnN0YW5jZSkuXG5cdFx0Ly8gQ29tcGV0aW5nLWNvbnN1bWVyOiBleHBsaWNpdCBgY29uc3VtZXJHcm91cGAgbWFrZXMgYWxsIHN1YnNjcmliZXJzXG5cdFx0Ly8gc2hhcmUgd29yay5cblx0XHRjb25zdCBncm91cCA9IGNvbmZpZy5jb25zdW1lckdyb3VwID8/IGBibG9rLWZhbm91dC0ke3RoaXMuY29uc3VtZXJOYW1lfS0ke3N0cmVhbS5yZXBsYWNlKC9bXmEtekEtWjAtOV9dL2csIFwiX1wiKX1gO1xuXHRcdGNvbnN0IHN0YXJ0SWQgPVxuXHRcdFx0Y29uZmlnLnN0YXJ0RnJvbSA9PT0gXCJlYXJsaWVzdFwiXG5cdFx0XHRcdD8gXCIwXCJcblx0XHRcdFx0OiBjb25maWcuc3RhcnRGcm9tID09PSBcImxhdGVzdFwiIHx8IGNvbmZpZy5zdGFydEZyb20gPT09IHVuZGVmaW5lZFxuXHRcdFx0XHRcdD8gXCIkXCJcblx0XHRcdFx0XHQ6IHR5cGVvZiBjb25maWcuc3RhcnRGcm9tID09PSBcIm9iamVjdFwiICYmIFwic2VxXCIgaW4gY29uZmlnLnN0YXJ0RnJvbVxuXHRcdFx0XHRcdFx0PyBgJHtjb25maWcuc3RhcnRGcm9tLnNlcX0tMGBcblx0XHRcdFx0XHRcdDogXCIkXCI7XG5cblx0XHR0cnkge1xuXHRcdFx0YXdhaXQgY2xpZW50Lnhncm91cChcIkNSRUFURVwiLCBzdHJlYW0sIGdyb3VwLCBzdGFydElkLCBcIk1LU1RSRUFNXCIpO1xuXHRcdH0gY2F0Y2ggKGVycikge1xuXHRcdFx0aWYgKCEvQlVTWUdST1VQL2kudGVzdCgoZXJyIGFzIEVycm9yKS5tZXNzYWdlKSkgdGhyb3cgZXJyO1xuXHRcdH1cblxuXHRcdGxldCBzdG9wcGVkID0gZmFsc2U7XG5cdFx0Y29uc3Qgc3ViOiBBY3RpdmVTdWJzY3JpcHRpb24gPSB7XG5cdFx0XHRzdG9wOiAoKSA9PiB7XG5cdFx0XHRcdHN0b3BwZWQgPSB0cnVlO1xuXHRcdFx0fSxcblx0XHR9O1xuXHRcdHRoaXMuc3Vic2NyaXB0aW9ucy5zZXQoYCR7c3RyZWFtfSMke2dyb3VwfWAsIHN1Yik7XG5cblx0XHR2b2lkIChhc3luYyAoKSA9PiB7XG5cdFx0XHR3aGlsZSAoIXN0b3BwZWQpIHtcblx0XHRcdFx0bGV0IGVudHJpZXM6IEFycmF5PFtzdHJpbmcsIEFycmF5PFtzdHJpbmcsIHN0cmluZ1tdXT5dPiB8IG51bGwgPSBudWxsO1xuXHRcdFx0XHR0cnkge1xuXHRcdFx0XHRcdGVudHJpZXMgPSBhd2FpdCBjbGllbnQueHJlYWRncm91cChcblx0XHRcdFx0XHRcdFwiR1JPVVBcIixcblx0XHRcdFx0XHRcdGdyb3VwLFxuXHRcdFx0XHRcdFx0dGhpcy5jb25zdW1lck5hbWUsXG5cdFx0XHRcdFx0XHRcIkNPVU5UXCIsXG5cdFx0XHRcdFx0XHRTdHJpbmcodGhpcy5jb25maWcuY291bnQpLFxuXHRcdFx0XHRcdFx0XCJCTE9DS1wiLFxuXHRcdFx0XHRcdFx0U3RyaW5nKHRoaXMuY29uZmlnLmJsb2NrTXMpLFxuXHRcdFx0XHRcdFx0XCJTVFJFQU1TXCIsXG5cdFx0XHRcdFx0XHRzdHJlYW0sXG5cdFx0XHRcdFx0XHRcIj5cIixcblx0XHRcdFx0XHQpO1xuXHRcdFx0XHR9IGNhdGNoIHtcblx0XHRcdFx0XHRhd2FpdCBuZXcgUHJvbWlzZSgocikgPT4gc2V0VGltZW91dChyLCAxMDAwKSk7XG5cdFx0XHRcdFx0Y29udGludWU7XG5cdFx0XHRcdH1cblx0XHRcdFx0aWYgKCFlbnRyaWVzKSBjb250aW51ZTtcblx0XHRcdFx0Zm9yIChjb25zdCBbLCBtc2dzXSBvZiBlbnRyaWVzKSB7XG5cdFx0XHRcdFx0Zm9yIChjb25zdCBbaWQsIGZpZWxkc10gb2YgbXNncykge1xuXHRcdFx0XHRcdFx0aWYgKHN0b3BwZWQpIGJyZWFrO1xuXHRcdFx0XHRcdFx0Y29uc3QgcGF5bG9hZCA9IHRoaXMuZmllbGRzVG9PYmplY3QoZmllbGRzKTtcblx0XHRcdFx0XHRcdGNvbnN0IGRhdGFTdHJpbmcgPSB0eXBlb2YgcGF5bG9hZC5kYXRhID09PSBcInN0cmluZ1wiID8gcGF5bG9hZC5kYXRhIDogXCJcIjtcblx0XHRcdFx0XHRcdGxldCBib2R5OiB1bmtub3duO1xuXHRcdFx0XHRcdFx0dHJ5IHtcblx0XHRcdFx0XHRcdFx0Ym9keSA9IGRhdGFTdHJpbmcubGVuZ3RoID4gMCA/IEpTT04ucGFyc2UoZGF0YVN0cmluZykgOiBudWxsO1xuXHRcdFx0XHRcdFx0fSBjYXRjaCB7XG5cdFx0XHRcdFx0XHRcdGJvZHkgPSBkYXRhU3RyaW5nO1xuXHRcdFx0XHRcdFx0fVxuXHRcdFx0XHRcdFx0Y29uc3QgbWVzc2FnZTogUHViU3ViTWVzc2FnZSA9IHtcblx0XHRcdFx0XHRcdFx0aWQsXG5cdFx0XHRcdFx0XHRcdGJvZHksXG5cdFx0XHRcdFx0XHRcdGF0dHJpYnV0ZXM6IHBheWxvYWQsXG5cdFx0XHRcdFx0XHRcdHJhdzogeyBpZCwgZmllbGRzIH0sXG5cdFx0XHRcdFx0XHRcdHRvcGljOiBzdHJlYW0sXG5cdFx0XHRcdFx0XHRcdHN1YnNjcmlwdGlvbjogZ3JvdXAsXG5cdFx0XHRcdFx0XHRcdHB1Ymxpc2hUaW1lOiBuZXcgRGF0ZShOdW1iZXIucGFyc2VJbnQoaWQuc3BsaXQoXCItXCIpWzBdID8/IFN0cmluZyhEYXRlLm5vdygpKSwgMTApKSxcblx0XHRcdFx0XHRcdFx0YWNrOiBhc3luYyAoKSA9PiB7XG5cdFx0XHRcdFx0XHRcdFx0YXdhaXQgY2xpZW50LnhhY2soc3RyZWFtLCBncm91cCwgaWQpO1xuXHRcdFx0XHRcdFx0XHR9LFxuXHRcdFx0XHRcdFx0XHRuYWNrOiBhc3luYyAoKSA9PiB7XG5cdFx0XHRcdFx0XHRcdFx0LyogbGVhdmUgdW5hY2tlZCDigJQgcGlja2VkIHVwIGJ5IFhBVVRPQ0xBSU0gKi9cblx0XHRcdFx0XHRcdFx0fSxcblx0XHRcdFx0XHRcdH07XG5cdFx0XHRcdFx0XHR0cnkge1xuXHRcdFx0XHRcdFx0XHRhd2FpdCBoYW5kbGVyKG1lc3NhZ2UpO1xuXHRcdFx0XHRcdFx0XHRpZiAoY29uZmlnLmFjayAhPT0gZmFsc2UpIGF3YWl0IGNsaWVudC54YWNrKHN0cmVhbSwgZ3JvdXAsIGlkKTtcblx0XHRcdFx0XHRcdH0gY2F0Y2gge1xuXHRcdFx0XHRcdFx0XHQvLyBMZWF2ZSB1bmFja2VkIOKAlCBwZW5kaW5nIGVudHJpZXMgYXJlIHZpc2libGUgaW4gWFBFTkRJTkcuXG5cdFx0XHRcdFx0XHR9XG5cdFx0XHRcdFx0fVxuXHRcdFx0XHR9XG5cdFx0XHR9XG5cdFx0fSkoKTtcblx0fVxuXG5cdHByaXZhdGUgZmllbGRzVG9PYmplY3QoZmllbGRzOiBzdHJpbmdbXSk6IFJlY29yZDxzdHJpbmcsIHN0cmluZz4ge1xuXHRcdGNvbnN0IG91dDogUmVjb3JkPHN0cmluZywgc3RyaW5nPiA9IHt9O1xuXHRcdGZvciAobGV0IGkgPSAwOyBpIDwgZmllbGRzLmxlbmd0aDsgaSArPSAyKSBvdXRbZmllbGRzW2ldXSA9IGZpZWxkc1tpICsgMV07XG5cdFx0cmV0dXJuIG91dDtcblx0fVxuXG5cdGFzeW5jIHVuc3Vic2NyaWJlKHN1YnNjcmlwdGlvbjogc3RyaW5nKTogUHJvbWlzZTx2b2lkPiB7XG5cdFx0Y29uc3Qgc3ViID0gdGhpcy5zdWJzY3JpcHRpb25zLmdldChzdWJzY3JpcHRpb24pO1xuXHRcdGlmICghc3ViKSByZXR1cm47XG5cdFx0c3ViLnN0b3AoKTtcblx0XHR0aGlzLnN1YnNjcmlwdGlvbnMuZGVsZXRlKHN1YnNjcmlwdGlvbik7XG5cdH1cblxuXHRhc3luYyBwdWJsaXNoKHRvcGljOiBzdHJpbmcsIHBheWxvYWQ6IHVua25vd24pOiBQcm9taXNlPHZvaWQ+IHtcblx0XHRpZiAoIXRoaXMuY29ubmVjdGVkIHx8ICF0aGlzLmNsaWVudCkgdGhyb3cgbmV3IEVycm9yKFwiW2Jsb2tdW3B1YnN1Yi1yZWRpc10gbm90IGNvbm5lY3RlZC4gQ2FsbCBjb25uZWN0KCkgZmlyc3QuXCIpO1xuXHRcdGNvbnN0IGJvZHkgPSB0eXBlb2YgcGF5bG9hZCA9PT0gXCJzdHJpbmdcIiA/IHBheWxvYWQgOiBKU09OLnN0cmluZ2lmeShwYXlsb2FkKTtcblx0XHRhd2FpdCB0aGlzLmNsaWVudC54YWRkKHRvcGljLCBcIipcIiwgXCJkYXRhXCIsIGJvZHkpO1xuXHR9XG5cblx0aXNDb25uZWN0ZWQoKTogYm9vbGVhbiB7XG5cdFx0cmV0dXJuIHRoaXMuY29ubmVjdGVkO1xuXHR9XG5cblx0YXN5bmMgaGVhbHRoQ2hlY2soKTogUHJvbWlzZTxib29sZWFuPiB7XG5cdFx0aWYgKCF0aGlzLmNvbm5lY3RlZCB8fCAhdGhpcy5jbGllbnQpIHJldHVybiBmYWxzZTtcblx0XHR0cnkge1xuXHRcdFx0Y29uc3QgcG9uZyA9IGF3YWl0IHRoaXMuY2xpZW50LnBpbmcoKTtcblx0XHRcdHJldHVybiBwb25nID09PSBcIlBPTkdcIjtcblx0XHR9IGNhdGNoIHtcblx0XHRcdHJldHVybiBmYWxzZTtcblx0XHR9XG5cdH1cbn1cbiJdfQ==
@@ -0,0 +1,22 @@
1
+ /**
2
+ * v0.7 PR 6 — pub/sub adapter factory.
3
+ *
4
+ * Resolves a `provider` string to a concrete `PubSubAdapter` instance.
5
+ * Used by `PubSubTrigger` (per-workflow provider dispatch) and by the
6
+ * `@blokjs/pubsub-publish` helper.
7
+ *
8
+ * Provider resolution order:
9
+ * 1. Explicit `provider` field on the workflow.
10
+ * 2. `BLOK_PUBSUB_ADAPTER` env var.
11
+ * 3. `"nats"` fallback (cheapest infra; matches the v0.7 plan's
12
+ * "default for pub/sub" recommendation).
13
+ *
14
+ * Each adapter lazy-imports its broker SDK on first use; workflows
15
+ * that don't use a given provider don't pay the install cost.
16
+ */
17
+ import type { PubSubProvider } from "@blokjs/helper";
18
+ import type { PubSubAdapter } from "../PubSubTrigger";
19
+ export declare function resolveProvider(provider?: PubSubProvider): PubSubProvider;
20
+ export declare function createPubSubAdapter(provider: PubSubProvider): PubSubAdapter;
21
+ export declare function getOrCreateAdapter(provider: PubSubProvider): PubSubAdapter;
22
+ export declare function _resetAdapterPoolForTests(): void;