@blokjs/trigger-pubsub 0.2.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/CHANGELOG.md ADDED
@@ -0,0 +1,22 @@
1
+ # @blokjs/trigger-pubsub
2
+
3
+ ## 0.2.0
4
+
5
+ ### Minor Changes
6
+
7
+ - Initial public release of Blok packages.
8
+
9
+ This release includes:
10
+
11
+ - Core packages: @blokjs/shared, @blokjs/helper, @blokjs/runner
12
+ - Node packages: @blokjs/api-call, @blokjs/if-else, @blokjs/react
13
+ - Trigger packages: pubsub, queue, webhook, websocket, worker, cron, grpc
14
+ - CLI tool: blokctl
15
+ - Editor support: @blokjs/lsp-server, @blokjs/syntax
16
+
17
+ ### Patch Changes
18
+
19
+ - Updated dependencies
20
+ - @blokjs/shared@0.2.0
21
+ - @blokjs/helper@0.2.0
22
+ - @blokjs/runner@0.2.0
@@ -0,0 +1,116 @@
1
+ /**
2
+ * PubSubTrigger - Base class for pub/sub-based workflow triggers
3
+ *
4
+ * Extends TriggerBase to support pub/sub triggers:
5
+ * - GCP Pub/Sub
6
+ * - AWS SNS (via SQS subscription)
7
+ * - Azure Service Bus
8
+ *
9
+ * Pattern:
10
+ * 1. loadNodes() - Load available nodes into NodeMap
11
+ * 2. loadWorkflows() - Load workflows with pubsub triggers
12
+ * 3. startSubscriber() - Connect to pub/sub and start receiving messages
13
+ * 4. For each message:
14
+ * - Match workflow by trigger config (topic/subscription)
15
+ * - Create context with this.createContext()
16
+ * - Populate ctx.request with message data
17
+ * - Execute workflow via this.run(ctx)
18
+ * - Ack/nack based on response
19
+ */
20
+ import type { HelperResponse, PubSubProvider, PubSubTriggerOpts } from "@blok/helper";
21
+ import { DefaultLogger, type GlobalOptions, type BlokService, TriggerBase } from "@blok/runner";
22
+ /**
23
+ * Message received from pub/sub
24
+ */
25
+ export interface PubSubMessage {
26
+ /** Unique message ID */
27
+ id: string;
28
+ /** Message body (parsed) */
29
+ body: unknown;
30
+ /** Message attributes/metadata */
31
+ attributes: Record<string, string>;
32
+ /** Original raw message from provider */
33
+ raw: unknown;
34
+ /** Topic name */
35
+ topic: string;
36
+ /** Subscription name */
37
+ subscription?: string;
38
+ /** Publish timestamp */
39
+ publishTime?: Date;
40
+ /** Acknowledge the message */
41
+ ack: () => Promise<void>;
42
+ /** Reject/nack the message */
43
+ nack: () => Promise<void>;
44
+ }
45
+ /**
46
+ * Pub/Sub adapter interface - implemented by each provider
47
+ */
48
+ export interface PubSubAdapter {
49
+ /** Provider name */
50
+ readonly provider: PubSubProvider;
51
+ /** Connect to the pub/sub system */
52
+ connect(): Promise<void>;
53
+ /** Disconnect from the pub/sub system */
54
+ disconnect(): Promise<void>;
55
+ /** Subscribe to a topic and receive messages */
56
+ subscribe(config: PubSubTriggerOpts, handler: (message: PubSubMessage) => Promise<void>): Promise<void>;
57
+ /** Unsubscribe from a topic */
58
+ unsubscribe(subscription: string): Promise<void>;
59
+ /** Check if connected */
60
+ isConnected(): boolean;
61
+ /** Health check */
62
+ healthCheck(): Promise<boolean>;
63
+ }
64
+ /**
65
+ * Workflow model with pub/sub trigger configuration
66
+ */
67
+ interface PubSubWorkflowModel {
68
+ path: string;
69
+ config: {
70
+ name: string;
71
+ version: string;
72
+ trigger?: {
73
+ pubsub?: PubSubTriggerOpts;
74
+ [key: string]: unknown;
75
+ };
76
+ [key: string]: unknown;
77
+ };
78
+ }
79
+ /**
80
+ * PubSubTrigger - Abstract base class for pub/sub-based triggers
81
+ */
82
+ export declare abstract class PubSubTrigger extends TriggerBase {
83
+ protected nodeMap: GlobalOptions;
84
+ protected readonly tracer: import("@opentelemetry/api").Tracer;
85
+ protected readonly logger: DefaultLogger;
86
+ protected abstract adapter: PubSubAdapter;
87
+ protected abstract nodes: Record<string, BlokService<unknown>>;
88
+ protected abstract workflows: Record<string, HelperResponse>;
89
+ constructor();
90
+ /**
91
+ * Load nodes into the node map
92
+ */
93
+ loadNodes(): void;
94
+ /**
95
+ * Load workflows into the workflow map
96
+ */
97
+ loadWorkflows(): void;
98
+ /**
99
+ * Start the pub/sub subscriber - main entry point
100
+ */
101
+ listen(): Promise<number>;
102
+ /**
103
+ * Stop the pub/sub subscriber
104
+ */
105
+ stop(): Promise<void>;
106
+ protected onHmrWorkflowChange(): Promise<void>;
107
+ /**
108
+ * Get all workflows that have pub/sub triggers
109
+ */
110
+ protected getPubSubWorkflows(): PubSubWorkflowModel[];
111
+ /**
112
+ * Handle an incoming message
113
+ */
114
+ protected handleMessage(message: PubSubMessage, workflow: PubSubWorkflowModel, config: PubSubTriggerOpts): Promise<void>;
115
+ }
116
+ export default PubSubTrigger;
@@ -0,0 +1,219 @@
1
+ "use strict";
2
+ /**
3
+ * PubSubTrigger - Base class for pub/sub-based workflow triggers
4
+ *
5
+ * Extends TriggerBase to support pub/sub triggers:
6
+ * - GCP Pub/Sub
7
+ * - AWS SNS (via SQS subscription)
8
+ * - Azure Service Bus
9
+ *
10
+ * Pattern:
11
+ * 1. loadNodes() - Load available nodes into NodeMap
12
+ * 2. loadWorkflows() - Load workflows with pubsub triggers
13
+ * 3. startSubscriber() - Connect to pub/sub and start receiving messages
14
+ * 4. For each message:
15
+ * - Match workflow by trigger config (topic/subscription)
16
+ * - Create context with this.createContext()
17
+ * - Populate ctx.request with message data
18
+ * - Execute workflow via this.run(ctx)
19
+ * - Ack/nack based on response
20
+ */
21
+ Object.defineProperty(exports, "__esModule", { value: true });
22
+ exports.PubSubTrigger = void 0;
23
+ const runner_1 = require("@blok/runner");
24
+ const api_1 = require("@opentelemetry/api");
25
+ const uuid_1 = require("uuid");
26
+ /**
27
+ * PubSubTrigger - Abstract base class for pub/sub-based triggers
28
+ */
29
+ class PubSubTrigger extends runner_1.TriggerBase {
30
+ nodeMap = {};
31
+ tracer = api_1.trace.getTracer(process.env.PROJECT_NAME || "trigger-pubsub-workflow", process.env.PROJECT_VERSION || "0.0.1");
32
+ logger = new runner_1.DefaultLogger();
33
+ constructor() {
34
+ super();
35
+ this.loadNodes();
36
+ this.loadWorkflows();
37
+ }
38
+ /**
39
+ * Load nodes into the node map
40
+ */
41
+ loadNodes() {
42
+ this.nodeMap.nodes = new runner_1.NodeMap();
43
+ const nodeKeys = Object.keys(this.nodes);
44
+ for (const key of nodeKeys) {
45
+ this.nodeMap.nodes.addNode(key, this.nodes[key]);
46
+ }
47
+ }
48
+ /**
49
+ * Load workflows into the workflow map
50
+ */
51
+ loadWorkflows() {
52
+ this.nodeMap.workflows = this.workflows;
53
+ }
54
+ /**
55
+ * Start the pub/sub subscriber - main entry point
56
+ */
57
+ async listen() {
58
+ const startTime = this.startCounter();
59
+ try {
60
+ // Connect to pub/sub system
61
+ await this.adapter.connect();
62
+ this.logger.log(`Connected to ${this.adapter.provider} pub/sub system`);
63
+ // Find all workflows with pub/sub triggers
64
+ const pubsubWorkflows = this.getPubSubWorkflows();
65
+ if (pubsubWorkflows.length === 0) {
66
+ this.logger.log("No workflows with pub/sub triggers found");
67
+ return this.endCounter(startTime);
68
+ }
69
+ // Subscribe to each topic/subscription
70
+ for (const workflow of pubsubWorkflows) {
71
+ const config = workflow.config.trigger?.pubsub;
72
+ this.logger.log(`Subscribing to topic: ${config.topic}, subscription: ${config.subscription} for workflow: ${workflow.path}`);
73
+ await this.adapter.subscribe(config, async (message) => {
74
+ await this.handleMessage(message, workflow, config);
75
+ });
76
+ }
77
+ this.logger.log(`Pub/Sub trigger started. Listening to ${pubsubWorkflows.length} subscription(s)`);
78
+ // Enable HMR in development mode
79
+ if (process.env.BLOK_HMR === "true" || process.env.NODE_ENV === "development") {
80
+ await this.enableHotReload();
81
+ }
82
+ return this.endCounter(startTime);
83
+ }
84
+ catch (error) {
85
+ this.logger.error(`Failed to start pub/sub trigger: ${error.message}`);
86
+ throw error;
87
+ }
88
+ }
89
+ /**
90
+ * Stop the pub/sub subscriber
91
+ */
92
+ async stop() {
93
+ await this.adapter.disconnect();
94
+ this.logger.log("Pub/Sub trigger stopped");
95
+ }
96
+ async onHmrWorkflowChange() {
97
+ this.logger.log("[HMR] Pub/Sub workflow changed, reloading...");
98
+ await this.waitForInFlightRequests();
99
+ await this.stop();
100
+ this.loadWorkflows();
101
+ await this.listen();
102
+ }
103
+ /**
104
+ * Get all workflows that have pub/sub triggers
105
+ */
106
+ getPubSubWorkflows() {
107
+ const workflows = [];
108
+ for (const [path, workflow] of Object.entries(this.nodeMap.workflows || {})) {
109
+ // HelperResponse has a protected _config property
110
+ const workflowConfig = workflow._config;
111
+ if (workflowConfig?.trigger) {
112
+ const triggerType = Object.keys(workflowConfig.trigger)[0];
113
+ if (triggerType === "pubsub" && workflowConfig.trigger.pubsub) {
114
+ workflows.push({
115
+ path,
116
+ config: workflowConfig,
117
+ });
118
+ }
119
+ }
120
+ }
121
+ return workflows;
122
+ }
123
+ /**
124
+ * Handle an incoming message
125
+ */
126
+ async handleMessage(message, workflow, config) {
127
+ const id = message.id || (0, uuid_1.v4)();
128
+ const defaultMeter = api_1.metrics.getMeter("default");
129
+ const pubsubMessages = defaultMeter.createCounter("pubsub_messages", {
130
+ description: "Pub/Sub messages processed",
131
+ });
132
+ const pubsubErrors = defaultMeter.createCounter("pubsub_errors", {
133
+ description: "Pub/Sub message processing errors",
134
+ });
135
+ await this.tracer.startActiveSpan(`pubsub:${config.topic}`, async (span) => {
136
+ try {
137
+ const start = performance.now();
138
+ // Initialize configuration for this workflow
139
+ await this.configuration.init(workflow.path, this.nodeMap);
140
+ // Create context
141
+ const ctx = this.createContext(undefined, workflow.path, id);
142
+ // Populate request with message data
143
+ ctx.request = {
144
+ body: message.body,
145
+ headers: message.attributes,
146
+ query: {},
147
+ params: {
148
+ topic: message.topic,
149
+ subscription: message.subscription || "",
150
+ messageId: message.id,
151
+ },
152
+ };
153
+ // Store message metadata in context
154
+ if (!ctx.vars)
155
+ ctx.vars = {};
156
+ ctx.vars["_pubsub_message"] = {
157
+ topic: message.topic,
158
+ subscription: message.subscription || "",
159
+ publishTime: message.publishTime?.toISOString() ?? "",
160
+ attributes: JSON.stringify(message.attributes),
161
+ };
162
+ ctx.logger.log(`Processing message from ${config.topic}: ${id}`);
163
+ // Execute workflow
164
+ const response = await this.run(ctx);
165
+ const end = performance.now();
166
+ // Set span attributes
167
+ span.setAttribute("success", true);
168
+ span.setAttribute("message_id", id);
169
+ span.setAttribute("topic", config.topic);
170
+ span.setAttribute("subscription", config.subscription);
171
+ span.setAttribute("provider", config.provider);
172
+ span.setAttribute("elapsed_ms", end - start);
173
+ span.setStatus({ code: api_1.SpanStatusCode.OK });
174
+ // Record metrics
175
+ pubsubMessages.add(1, {
176
+ env: process.env.NODE_ENV,
177
+ topic: config.topic,
178
+ subscription: config.subscription,
179
+ provider: config.provider,
180
+ workflow_name: this.configuration.name,
181
+ success: "true",
182
+ });
183
+ ctx.logger.log(`Message processed in ${(end - start).toFixed(2)}ms: ${id}`);
184
+ // Acknowledge message if configured
185
+ if (config.ack !== false) {
186
+ await message.ack();
187
+ ctx.logger.log(`Message acknowledged: ${id}`);
188
+ }
189
+ }
190
+ catch (error) {
191
+ const errorMessage = error.message;
192
+ // Set span error
193
+ span.setAttribute("success", false);
194
+ span.recordException(error);
195
+ span.setStatus({ code: api_1.SpanStatusCode.ERROR, message: errorMessage });
196
+ // Record error metrics
197
+ pubsubErrors.add(1, {
198
+ env: process.env.NODE_ENV,
199
+ topic: config.topic,
200
+ subscription: config.subscription,
201
+ provider: config.provider,
202
+ workflow_name: this.configuration?.name || "unknown",
203
+ });
204
+ this.logger.error(`Failed to process message ${id}: ${errorMessage}`, error.stack);
205
+ // Nack message
206
+ if (config.ack !== false) {
207
+ await message.nack();
208
+ this.logger.log(`Message nacked: ${id}`);
209
+ }
210
+ }
211
+ finally {
212
+ span.end();
213
+ }
214
+ });
215
+ }
216
+ }
217
+ exports.PubSubTrigger = PubSubTrigger;
218
+ exports.default = PubSubTrigger;
219
+ //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiUHViU3ViVHJpZ2dlci5qcyIsInNvdXJjZVJvb3QiOiIiLCJzb3VyY2VzIjpbIi4uL3NyYy9QdWJTdWJUcmlnZ2VyLnRzIl0sIm5hbWVzIjpbXSwibWFwcGluZ3MiOiI7QUFBQTs7Ozs7Ozs7Ozs7Ozs7Ozs7O0dBa0JHOzs7QUFHSCx5Q0FPc0I7QUFFdEIsNENBQStFO0FBQy9FLCtCQUFrQztBQW9FbEM7O0dBRUc7QUFDSCxNQUFzQixhQUFjLFNBQVEsb0JBQVc7SUFDNUMsT0FBTyxHQUFrQixFQUFtQixDQUFDO0lBQ3BDLE1BQU0sR0FBRyxXQUFLLENBQUMsU0FBUyxDQUMxQyxPQUFPLENBQUMsR0FBRyxDQUFDLFlBQVksSUFBSSx5QkFBeUIsRUFDckQsT0FBTyxDQUFDLEdBQUcsQ0FBQyxlQUFlLElBQUksT0FBTyxDQUN0QyxDQUFDO0lBQ2lCLE1BQU0sR0FBRyxJQUFJLHNCQUFhLEVBQUUsQ0FBQztJQU9oRDtRQUNDLEtBQUssRUFBRSxDQUFDO1FBQ1IsSUFBSSxDQUFDLFNBQVMsRUFBRSxDQUFDO1FBQ2pCLElBQUksQ0FBQyxhQUFhLEVBQUUsQ0FBQztJQUN0QixDQUFDO0lBRUQ7O09BRUc7SUFDSCxTQUFTO1FBQ1IsSUFBSSxDQUFDLE9BQU8sQ0FBQyxLQUFLLEdBQUcsSUFBSSxnQkFBTyxFQUFFLENBQUM7UUFDbkMsTUFBTSxRQUFRLEdBQUcsTUFBTSxDQUFDLElBQUksQ0FBQyxJQUFJLENBQUMsS0FBSyxDQUFDLENBQUM7UUFDekMsS0FBSyxNQUFNLEdBQUcsSUFBSSxRQUFRLEVBQUUsQ0FBQztZQUM1QixJQUFJLENBQUMsT0FBTyxDQUFDLEtBQUssQ0FBQyxPQUFPLENBQUMsR0FBRyxFQUFFLElBQUksQ0FBQyxLQUFLLENBQUMsR0FBRyxDQUFDLENBQUMsQ0FBQztRQUNsRCxDQUFDO0lBQ0YsQ0FBQztJQUVEOztPQUVHO0lBQ0gsYUFBYTtRQUNaLElBQUksQ0FBQyxPQUFPLENBQUMsU0FBUyxHQUFHLElBQUksQ0FBQyxTQUFTLENBQUM7SUFDekMsQ0FB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QUFLLEtBQUssRUFBRSxDQUFDO29CQUMxQixNQUFNLE9BQU8sQ0FBQyxJQUFJLEVBQUUsQ0FBQztvQkFDckIsSUFBSSxDQUFDLE1BQU0sQ0FBQyxHQUFHLENBQUMsbUJBQW1CLEVBQUUsRUFBRSxDQUFDLENBQUM7Z0JBQzFDLENBQUM7WUFDRixDQUFDO29CQUFTLENBQUM7Z0JBQ1YsSUFBSSxDQUFDLEdBQUcsRUFBRSxDQUFDO1lBQ1osQ0FBQztRQUNGLENBQUMsQ0FBQyxDQUFDO0lBQ0osQ0FBQztDQUNEO0FBeE9ELHNDQXdPQztBQUVELGtCQUFlLGFBQWEsQ0FBQyIsInNvdXJjZXNDb250ZW50IjpbIi8qKlxuICogUHViU3ViVHJpZ2dlciAtIEJhc2UgY2xhc3MgZm9yIHB1Yi9zdWItYmFzZWQgd29ya2Zsb3cgdHJpZ2dlcnNcbiAqXG4gKiBFeHRlbmRzIFRyaWdnZXJCYXNlIHRvIHN1cHBvcnQgcHViL3N1YiB0cmlnZ2VyczpcbiAqIC0gR0NQIFB1Yi9TdWJcbiAqIC0gQVdTIFNOUyAodmlhIFNRUyBzdWJzY3JpcHRpb24pXG4gKiAtIEF6dXJlIFNlcnZpY2UgQnVzXG4gKlxuICogUGF0dGVybjpcbiAqIDEuIGxvYWROb2RlcygpIC0gTG9hZCBhdmFpbGFibGUgbm9kZXMgaW50byBOb2RlTWFwXG4gKiAyLiBsb2FkV29ya2Zsb3dzKCkgLSBMb2FkIHdvcmtmbG93cyB3aXRoIHB1YnN1YiB0cmlnZ2Vyc1xuICogMy4gc3RhcnRTdWJzY3JpYmVyKCkgLSBDb25uZWN0IHRvIHB1Yi9zdWIgYW5kIHN0YXJ0IHJlY2VpdmluZyBtZXNzYWdlc1xuICogNC4gRm9yIGVhY2ggbWVzc2FnZTpcbiAqICAgIC0gTWF0Y2ggd29ya2Zsb3cgYnkgdHJpZ2dlciBjb25maWcgKHRvcGljL3N1YnNjcmlwdGlvbilcbiAqICAgIC0gQ3JlYXRlIGNvbnRleHQgd2l0aCB0aGlzLmNyZWF0ZUNvbnRleHQoKVxuICogICAgLSBQb3B1bGF0ZSBjdHgucmVxdWVzdCB3aXRoIG1lc3NhZ2UgZGF0YVxuICogICAgLSBFeGVjdXRlIHdvcmtmbG93IHZpYSB0aGlzLnJ1bihjdHgpXG4gKiAgICAtIEFjay9uYWNrIGJhc2VkIG9uIHJlc3BvbnNlXG4gKi9cblxuaW1wb3J0IHR5cGUgeyBIZWxwZXJSZXNwb25zZSwgUHViU3ViUHJvdmlkZXIsIFB1YlN1YlRyaWdnZXJPcHRzIH0gZnJvbSBcIkBibG9rL2hlbHBlclwiO1xuaW1wb3J0IHtcblx0RGVmYXVsdExvZ2dlcixcblx0dHlwZSBHbG9iYWxPcHRpb25zLFxuXHR0eXBlIEJsb2tTZXJ2aWNlLFxuXHROb2RlTWFwLFxuXHRUcmlnZ2VyQmFzZSxcblx0dHlwZSBUcmlnZ2VyUmVzcG9uc2UsXG59IGZyb20gXCJAYmxvay9ydW5uZXJcIjtcbmltcG9ydCB0eXBlIHsgQ29udGV4dCwgUmVxdWVzdENvbnRleHQgfSBmcm9tIFwiQGJsb2svc2hhcmVkXCI7XG5pbXBvcnQgeyB0eXBlIFNwYW4sIFNwYW5TdGF0dXNDb2RlLCBtZXRyaWNzLCB0cmFjZSB9IGZyb20gXCJAb3BlbnRlbGVtZXRyeS9hcGlcIjtcbmltcG9ydCB7IHY0IGFzIHV1aWQgfSBmcm9tIFwidXVpZFwiO1xuXG4vKipcbiAqIE1lc3NhZ2UgcmVjZWl2ZWQgZnJvbSBwdWIvc3ViXG4gKi9cbmV4cG9ydCBpbnRlcmZhY2UgUHViU3ViTWVzc2FnZSB7XG5cdC8qKiBVbmlxdWUgbWVzc2FnZSBJRCAqL1xuXHRpZDogc3RyaW5nO1xuXHQvKiogTWVzc2FnZSBib2R5IChwYXJzZWQpICovXG5cdGJvZHk6IHVua25vd247XG5cdC8qKiBNZXNzYWdlIGF0dHJpYnV0ZXMvbWV0YWRhdGEgKi9cblx0YXR0cmlidXRlczogUmVjb3JkPHN0cmluZywgc3RyaW5nPjtcblx0LyoqIE9yaWdpbmFsIHJhdyBtZXNzYWdlIGZyb20gcHJvdmlkZXIgKi9cblx0cmF3OiB1bmtub3duO1xuXHQvKiogVG9waWMgbmFtZSAqL1xuXHR0b3BpYzogc3RyaW5nO1xuXHQvKiogU3Vic2NyaXB0aW9uIG5hbWUgKi9cblx0c3Vic2NyaXB0aW9uPzogc3RyaW5nO1xuXHQvKiogUHVibGlzaCB0aW1lc3RhbXAgKi9cblx0cHVibGlzaFRpbWU/OiBEYXRlO1xuXHQvKiogQWNrbm93bGVkZ2UgdGhlIG1lc3NhZ2UgKi9cblx0YWNrOiAoKSA9PiBQcm9taXNlPHZvaWQ+O1xuXHQvKiogUmVqZWN0L25hY2sgdGhlIG1lc3NhZ2UgKi9cblx0bmFjazogKCkgPT4gUHJvbWlzZTx2b2lkPjtcbn1cblxuLyoqXG4gKiBQdWIvU3ViIGFkYXB0ZXIgaW50ZXJmYWNlIC0gaW1wbGVtZW50ZWQgYnkgZWFjaCBwcm92aWRlclxuICovXG5leHBvcnQgaW50ZXJmYWNlIFB1YlN1YkFkYXB0ZXIge1xuXHQvKiogUHJvdmlkZXIgbmFtZSAqL1xuXHRyZWFkb25seSBwcm92aWRlcjogUHViU3ViUHJvdmlkZXI7XG5cblx0LyoqIENvbm5lY3QgdG8gdGhlIHB1Yi9zdWIgc3lzdGVtICovXG5cdGNvbm5lY3QoKTogUHJvbWlzZTx2b2lkPjtcblxuXHQvKiogRGlzY29ubmVjdCBmcm9tIHRoZSBwdWIvc3ViIHN5c3RlbSAqL1xuXHRkaXNjb25uZWN0KCk6IFByb21pc2U8dm9pZD47XG5cblx0LyoqIFN1YnNjcmliZSB0byBhIHRvcGljIGFuZCByZWNlaXZlIG1lc3NhZ2VzICovXG5cdHN1YnNjcmliZShjb25maWc6IFB1YlN1YlRyaWdnZXJPcHRzLCBoYW5kbGVyOiAobWVzc2FnZTogUHViU3ViTWVzc2FnZSkgPT4gUHJvbWlzZTx2b2lkPik6IFByb21pc2U8dm9pZD47XG5cblx0LyoqIFVuc3Vic2NyaWJlIGZyb20gYSB0b3BpYyAqL1xuXHR1bnN1YnNjcmliZShzdWJzY3JpcHRpb246IHN0cmluZyk6IFByb21pc2U8dm9pZD47XG5cblx0LyoqIENoZWNrIGlmIGNvbm5lY3RlZCAqL1xuXHRpc0Nvbm5lY3RlZCgpOiBib29sZWFuO1xuXG5cdC8qKiBIZWFsdGggY2hlY2sgKi9cblx0aGVhbHRoQ2hlY2soKTogUHJvbWlzZTxib29sZWFuPjtcbn1cblxuLyoqXG4gKiBXb3JrZmxvdyBtb2RlbCB3aXRoIHB1Yi9zdWIgdHJpZ2dlciBjb25maWd1cmF0aW9uXG4gKi9cbmludGVyZmFjZSBQdWJTdWJXb3JrZmxvd01vZGVsIHtcblx0cGF0aDogc3RyaW5nO1xuXHRjb25maWc6IHtcblx0XHRuYW1lOiBzdHJpbmc7XG5cdFx0dmVyc2lvbjogc3RyaW5nO1xuXHRcdHRyaWdnZXI/OiB7XG5cdFx0XHRwdWJzdWI/OiBQdWJTdWJUcmlnZ2VyT3B0cztcblx0XHRcdFtrZXk6IHN0cmluZ106IHVua25vd247XG5cdFx0fTtcblx0XHRba2V5OiBzdHJpbmddOiB1bmtub3duO1xuXHR9O1xufVxuXG4vKipcbiAqIFB1YlN1YlRyaWdnZXIgLSBBYnN0cmFjdCBiYXNlIGNsYXNzIGZvciBwdWIvc3ViLWJhc2VkIHRyaWdnZXJzXG4gKi9cbmV4cG9ydCBhYnN0cmFjdCBjbGFzcyBQdWJTdWJUcmlnZ2VyIGV4dGVuZHMgVHJpZ2dlckJhc2Uge1xuXHRwcm90ZWN0ZWQgbm9kZU1hcDogR2xvYmFsT3B0aW9ucyA9IHt9IGFzIEdsb2JhbE9wdGlvbnM7XG5cdHByb3RlY3RlZCByZWFkb25seSB0cmFjZXIgPSB0cmFjZS5nZXRUcmFjZXIoXG5cdFx0cHJvY2Vzcy5lbnYuUFJPSkVDVF9OQU1FIHx8IFwidHJpZ2dlci1wdWJzdWItd29ya2Zsb3dcIixcblx0XHRwcm9jZXNzLmVudi5QUk9KRUNUX1ZFUlNJT04gfHwgXCIwLjAuMVwiLFxuXHQpO1xuXHRwcm90ZWN0ZWQgcmVhZG9ubHkgbG9nZ2VyID0gbmV3IERlZmF1bHRMb2dnZXIoKTtcblx0cHJvdGVjdGVkIGFic3RyYWN0IGFkYXB0ZXI6IFB1YlN1YkFkYXB0ZXI7XG5cblx0Ly8gU3ViY2xhc3NlcyBwcm92aWRlIHRoZXNlXG5cdHByb3RlY3RlZCBhYnN0cmFjdCBub2RlczogUmVjb3JkPHN0cmluZywgQmxva1NlcnZpY2U8dW5rbm93bj4+O1xuXHRwcm90ZWN0ZWQgYWJzdHJhY3Qgd29ya2Zsb3dzOiBSZWNvcmQ8c3RyaW5nLCBIZWxwZXJSZXNwb25zZT47XG5cblx0Y29uc3RydWN0b3IoKSB7XG5cdFx0c3VwZXIoKTtcblx0XHR0aGlzLmxvYWROb2RlcygpO1xuXHRcdHRoaXMubG9hZFdvcmtmbG93cygpO1xuXHR9XG5cblx0LyoqXG5cdCAqIExvYWQgbm9kZXMgaW50byB0aGUgbm9kZSBtYXBcblx0ICovXG5cdGxvYWROb2RlcygpOiB2b2lkIHtcblx0XHR0aGlzLm5vZGVNYXAubm9kZXMgPSBuZXcgTm9kZU1hcCgpO1xuXHRcdGNvbnN0IG5vZGVLZXlzID0gT2JqZWN0LmtleXModGhpcy5ub2Rlcyk7XG5cdFx0Zm9yIChjb25zdCBrZXkgb2Ygbm9kZUtleXMpIHtcblx0XHRcdHRoaXMubm9kZU1hcC5ub2Rlcy5hZGROb2RlKGtleSwgdGhpcy5ub2Rlc1trZXldKTtcblx0XHR9XG5cdH1cblxuXHQvKipcblx0ICogTG9hZCB3b3JrZmxvd3MgaW50byB0aGUgd29ya2Zsb3cgbWFwXG5cdCAqL1xuXHRsb2FkV29ya2Zsb3dzKCk6IHZvaWQge1xuXHRcdHRoaXMubm9kZU1hcC53b3JrZmxvd3MgPSB0aGlzLndvcmtmbG93cztcblx0fVxuXG5cdC8qKlxuXHQgKiBTdGFydCB0aGUgcHViL3N1YiBzdWJzY3JpYmVyIC0gbWFpbiBlbnRyeSBwb2ludFxuXHQgKi9cblx0YXN5bmMgbGlzdGVuKCk6IFByb21pc2U8bnVtYmVyPiB7XG5cdFx0Y29uc3Qgc3RhcnRUaW1lID0gdGhpcy5zdGFydENvdW50ZXIoKTtcblxuXHRcdHRyeSB7XG5cdFx0XHQvLyBDb25uZWN0IHRvIHB1Yi9zdWIgc3lzdGVtXG5cdFx0XHRhd2FpdCB0aGlzLmFkYXB0ZXIuY29ubmVjdCgpO1xuXHRcdFx0dGhpcy5sb2dnZXIubG9nKGBDb25uZWN0ZWQgdG8gJHt0aGlzLmFkYXB0ZXIucHJvdmlkZXJ9IHB1Yi9zdWIgc3lzdGVtYCk7XG5cblx0XHRcdC8vIEZpbmQgYWxsIHdvcmtmbG93cyB3aXRoIHB1Yi9zdWIgdHJpZ2dlcnNcblx0XHRcdGNvbnN0IHB1YnN1YldvcmtmbG93cyA9IHRoaXMuZ2V0UHViU3ViV29ya2Zsb3dzKCk7XG5cblx0XHRcdGlmIChwdWJzdWJXb3JrZmxvd3MubGVuZ3RoID09PSAwKSB7XG5cdFx0XHRcdHRoaXMubG9nZ2VyLmxvZyhcIk5vIHdvcmtmbG93cyB3aXRoIHB1Yi9zdWIgdHJpZ2dlcnMgZm91bmRcIik7XG5cdFx0XHRcdHJldHVybiB0aGlzLmVuZENvdW50ZXIoc3RhcnRUaW1lKTtcblx0XHRcdH1cblxuXHRcdFx0Ly8gU3Vic2NyaWJlIHRvIGVhY2ggdG9waWMvc3Vic2NyaXB0aW9uXG5cdFx0XHRmb3IgKGNvbnN0IHdvcmtmbG93IG9mIHB1YnN1YldvcmtmbG93cykge1xuXHRcdFx0XHRjb25zdCBjb25maWcgPSB3b3JrZmxvdy5jb25maWcudHJpZ2dlcj8ucHVic3ViIGFzIFB1YlN1YlRyaWdnZXJPcHRzO1xuXHRcdFx0XHR0aGlzLmxvZ2dlci5sb2coXG5cdFx0XHRcdFx0YFN1YnNjcmliaW5nIHRvIHRvcGljOiAke2NvbmZpZy50b3BpY30sIHN1YnNjcmlwdGlvbjogJHtjb25maWcuc3Vic2NyaXB0aW9ufSBmb3Igd29ya2Zsb3c6ICR7d29ya2Zsb3cucGF0aH1gLFxuXHRcdFx0XHQpO1xuXG5cdFx0XHRcdGF3YWl0IHRoaXMuYWRhcHRlci5zdWJzY3JpYmUoY29uZmlnLCBhc3luYyAobWVzc2FnZSkgPT4ge1xuXHRcdFx0XHRcdGF3YWl0IHRoaXMuaGFuZGxlTWVzc2FnZShtZXNzYWdlLCB3b3JrZmxvdywgY29uZmlnKTtcblx0XHRcdFx0fSk7XG5cdFx0XHR9XG5cblx0XHRcdHRoaXMubG9nZ2VyLmxvZyhgUHViL1N1YiB0cmlnZ2VyIHN0YXJ0ZWQuIExpc3RlbmluZyB0byAke3B1YnN1YldvcmtmbG93cy5sZW5ndGh9IHN1YnNjcmlwdGlvbihzKWApO1xuXG5cdFx0XHQvLyBFbmFibGUgSE1SIGluIGRldmVsb3BtZW50IG1vZGVcblx0XHRcdGlmIChwcm9jZXNzLmVudi5CTE9LX0hNUiA9PT0gXCJ0cnVlXCIgfHwgcHJvY2Vzcy5lbnYuTk9ERV9FTlYgPT09IFwiZGV2ZWxvcG1lbnRcIikge1xuXHRcdFx0XHRhd2FpdCB0aGlzLmVuYWJsZUhvdFJlbG9hZCgpO1xuXHRcdFx0fVxuXG5cdFx0XHRyZXR1cm4gdGhpcy5lbmRDb3VudGVyKHN0YXJ0VGltZSk7XG5cdFx0fSBjYXRjaCAoZXJyb3IpIHtcblx0XHRcdHRoaXMubG9nZ2VyLmVycm9yKGBGYWlsZWQgdG8gc3RhcnQgcHViL3N1YiB0cmlnZ2VyOiAkeyhlcnJvciBhcyBFcnJvcikubWVzc2FnZX1gKTtcblx0XHRcdHRocm93IGVycm9yO1xuXHRcdH1cblx0fVxuXG5cdC8qKlxuXHQgKiBTdG9wIHRoZSBwdWIvc3ViIHN1YnNjcmliZXJcblx0ICovXG5cdGFzeW5jIHN0b3AoKTogUHJvbWlzZTx2b2lkPiB7XG5cdFx0YXdhaXQgdGhpcy5hZGFwdGVyLmRpc2Nvbm5lY3QoKTtcblx0XHR0aGlzLmxvZ2dlci5sb2coXCJQdWIvU3ViIHRyaWdnZXIgc3RvcHBlZFwiKTtcblx0fVxuXG5cdHByb3RlY3RlZCBvdmVycmlkZSBhc3luYyBvbkhtcldvcmtmbG93Q2hhbmdlKCk6IFByb21pc2U8dm9pZD4ge1xuXHRcdHRoaXMubG9nZ2VyLmxvZyhcIltITVJdIFB1Yi9TdWIgd29ya2Zsb3cgY2hhbmdlZCwgcmVsb2FkaW5nLi4uXCIpO1xuXHRcdGF3YWl0IHRoaXMud2FpdEZvckluRmxpZ2h0UmVxdWVzdHMoKTtcblx0XHRhd2FpdCB0aGlzLnN0b3AoKTtcblx0XHR0aGlzLmxvYWRXb3JrZmxvd3MoKTtcblx0XHRhd2FpdCB0aGlzLmxpc3RlbigpO1xuXHR9XG5cblx0LyoqXG5cdCAqIEdldCBhbGwgd29ya2Zsb3dzIHRoYXQgaGF2ZSBwdWIvc3ViIHRyaWdnZXJzXG5cdCAqL1xuXHRwcm90ZWN0ZWQgZ2V0UHViU3ViV29ya2Zsb3dzKCk6IFB1YlN1YldvcmtmbG93TW9kZWxbXSB7XG5cdFx0Y29uc3Qgd29ya2Zsb3dzOiBQdWJTdWJXb3JrZmxvd01vZGVsW10gPSBbXTtcblxuXHRcdGZvciAoY29uc3QgW3BhdGgsIHdvcmtmbG93XSBvZiBPYmplY3QuZW50cmllcyh0aGlzLm5vZGVNYXAud29ya2Zsb3dzIHx8IHt9KSkge1xuXHRcdFx0Ly8gSGVscGVyUmVzcG9uc2UgaGFzIGEgcHJvdGVjdGVkIF9jb25maWcgcHJvcGVydHlcblx0XHRcdGNvbnN0IHdvcmtmbG93Q29uZmlnID0gKHdvcmtmbG93IGFzIHVua25vd24gYXMgeyBfY29uZmlnOiBQdWJTdWJXb3JrZmxvd01vZGVsW1wiY29uZmlnXCJdIH0pLl9jb25maWc7XG5cblx0XHRcdGlmICh3b3JrZmxvd0NvbmZpZz8udHJpZ2dlcikge1xuXHRcdFx0XHRjb25zdCB0cmlnZ2VyVHlwZSA9IE9iamVjdC5rZXlzKHdvcmtmbG93Q29uZmlnLnRyaWdnZXIpWzBdO1xuXG5cdFx0XHRcdGlmICh0cmlnZ2VyVHlwZSA9PT0gXCJwdWJzdWJcIiAmJiB3b3JrZmxvd0NvbmZpZy50cmlnZ2VyLnB1YnN1Yikge1xuXHRcdFx0XHRcdHdvcmtmbG93cy5wdXNoKHtcblx0XHRcdFx0XHRcdHBhdGgsXG5cdFx0XHRcdFx0XHRjb25maWc6IHdvcmtmbG93Q29uZmlnLFxuXHRcdFx0XHRcdH0pO1xuXHRcdFx0XHR9XG5cdFx0XHR9XG5cdFx0fVxuXG5cdFx0cmV0dXJuIHdvcmtmbG93cztcblx0fVxuXG5cdC8qKlxuXHQgKiBIYW5kbGUgYW4gaW5jb21pbmcgbWVzc2FnZVxuXHQgKi9cblx0cHJvdGVjdGVkIGFzeW5jIGhhbmRsZU1lc3NhZ2UoXG5cdFx0bWVzc2FnZTogUHViU3ViTWVzc2FnZSxcblx0XHR3b3JrZmxvdzogUHViU3ViV29ya2Zsb3dNb2RlbCxcblx0XHRjb25maWc6IFB1YlN1YlRyaWdnZXJPcHRzLFxuXHQpOiBQcm9taXNlPHZvaWQ+IHtcblx0XHRjb25zdCBpZCA9IG1lc3NhZ2UuaWQgfHwgdXVpZCgpO1xuXHRcdGNvbnN0IGRlZmF1bHRNZXRlciA9IG1ldHJpY3MuZ2V0TWV0ZXIoXCJkZWZhdWx0XCIpO1xuXHRcdGNvbnN0IHB1YnN1Yk1lc3NhZ2VzID0gZGVmYXVsdE1ldGVyLmNyZWF0ZUNvdW50ZXIoXCJwdWJzdWJfbWVzc2FnZXNcIiwge1xuXHRcdFx0ZGVzY3JpcHRpb246IFwiUHViL1N1YiBtZXNzYWdlcyBwcm9jZXNzZWRcIixcblx0XHR9KTtcblx0XHRjb25zdCBwdWJzdWJFcnJvcnMgPSBkZWZhdWx0TWV0ZXIuY3JlYXRlQ291bnRlcihcInB1YnN1Yl9lcnJvcnNcIiwge1xuXHRcdFx0ZGVzY3JpcHRpb246IFwiUHViL1N1YiBtZXNzYWdlIHByb2Nlc3NpbmcgZXJyb3JzXCIsXG5cdFx0fSk7XG5cblx0XHRhd2FpdCB0aGlzLnRyYWNlci5zdGFydEFjdGl2ZVNwYW4oYHB1YnN1Yjoke2NvbmZpZy50b3BpY31gLCBhc3luYyAoc3BhbjogU3BhbikgPT4ge1xuXHRcdFx0dHJ5IHtcblx0XHRcdFx0Y29uc3Qgc3RhcnQgPSBwZXJmb3JtYW5jZS5ub3coKTtcblxuXHRcdFx0XHQvLyBJbml0aWFsaXplIGNvbmZpZ3VyYXRpb24gZm9yIHRoaXMgd29ya2Zsb3dcblx0XHRcdFx0YXdhaXQgdGhpcy5jb25maWd1cmF0aW9uLmluaXQod29ya2Zsb3cucGF0aCwgdGhpcy5ub2RlTWFwKTtcblxuXHRcdFx0XHQvLyBDcmVhdGUgY29udGV4dFxuXHRcdFx0XHRjb25zdCBjdHg6IENvbnRleHQgPSB0aGlzLmNyZWF0ZUNvbnRleHQodW5kZWZpbmVkLCB3b3JrZmxvdy5wYXRoLCBpZCk7XG5cblx0XHRcdFx0Ly8gUG9wdWxhdGUgcmVxdWVzdCB3aXRoIG1lc3NhZ2UgZGF0YVxuXHRcdFx0XHRjdHgucmVxdWVzdCA9IHtcblx0XHRcdFx0XHRib2R5OiBtZXNzYWdlLmJvZHksXG5cdFx0XHRcdFx0aGVhZGVyczogbWVzc2FnZS5hdHRyaWJ1dGVzLFxuXHRcdFx0XHRcdHF1ZXJ5OiB7fSxcblx0XHRcdFx0XHRwYXJhbXM6IHtcblx0XHRcdFx0XHRcdHRvcGljOiBtZXNzYWdlLnRvcGljLFxuXHRcdFx0XHRcdFx0c3Vic2NyaXB0aW9uOiBtZXNzYWdlLnN1YnNjcmlwdGlvbiB8fCBcIlwiLFxuXHRcdFx0XHRcdFx0bWVzc2FnZUlkOiBtZXNzYWdlLmlkLFxuXHRcdFx0XHRcdH0sXG5cdFx0XHRcdH0gYXMgdW5rbm93biBhcyBSZXF1ZXN0Q29udGV4dDtcblxuXHRcdFx0XHQvLyBTdG9yZSBtZXNzYWdlIG1ldGFkYXRhIGluIGNvbnRleHRcblx0XHRcdFx0aWYgKCFjdHgudmFycykgY3R4LnZhcnMgPSB7fTtcblx0XHRcdFx0Y3R4LnZhcnNbXCJfcHVic3ViX21lc3NhZ2VcIl0gPSB7XG5cdFx0XHRcdFx0dG9waWM6IG1lc3NhZ2UudG9waWMsXG5cdFx0XHRcdFx0c3Vic2NyaXB0aW9uOiBtZXNzYWdlLnN1YnNjcmlwdGlvbiB8fCBcIlwiLFxuXHRcdFx0XHRcdHB1Ymxpc2hUaW1lOiBtZXNzYWdlLnB1Ymxpc2hUaW1lPy50b0lTT1N0cmluZygpID8/IFwiXCIsXG5cdFx0XHRcdFx0YXR0cmlidXRlczogSlNPTi5zdHJpbmdpZnkobWVzc2FnZS5hdHRyaWJ1dGVzKSxcblx0XHRcdFx0fTtcblxuXHRcdFx0XHRjdHgubG9nZ2VyLmxvZyhgUHJvY2Vzc2luZyBtZXNzYWdlIGZyb20gJHtjb25maWcudG9waWN9OiAke2lkfWApO1xuXG5cdFx0XHRcdC8vIEV4ZWN1dGUgd29ya2Zsb3dcblx0XHRcdFx0Y29uc3QgcmVzcG9uc2U6IFRyaWdnZXJSZXNwb25zZSA9IGF3YWl0IHRoaXMucnVuKGN0eCk7XG5cdFx0XHRcdGNvbnN0IGVuZCA9IHBlcmZvcm1hbmNlLm5vdygpO1xuXG5cdFx0XHRcdC8vIFNldCBzcGFuIGF0dHJpYnV0ZXNcblx0XHRcdFx0c3Bhbi5zZXRBdHRyaWJ1dGUoXCJzdWNjZXNzXCIsIHRydWUpO1xuXHRcdFx0XHRzcGFuLnNldEF0dHJpYnV0ZShcIm1lc3NhZ2VfaWRcIiwgaWQpO1xuXHRcdFx0XHRzcGFuLnNldEF0dHJpYnV0ZShcInRvcGljXCIsIGNvbmZpZy50b3BpYyk7XG5cdFx0XHRcdHNwYW4uc2V0QXR0cmlidXRlKFwic3Vic2NyaXB0aW9uXCIsIGNvbmZpZy5zdWJzY3JpcHRpb24pO1xuXHRcdFx0XHRzcGFuLnNldEF0dHJpYnV0ZShcInByb3ZpZGVyXCIsIGNvbmZpZy5wcm92aWRlcik7XG5cdFx0XHRcdHNwYW4uc2V0QXR0cmlidXRlKFwiZWxhcHNlZF9tc1wiLCBlbmQgLSBzdGFydCk7XG5cdFx0XHRcdHNwYW4uc2V0U3RhdHVzKHsgY29kZTogU3BhblN0YXR1c0NvZGUuT0sgfSk7XG5cblx0XHRcdFx0Ly8gUmVjb3JkIG1ldHJpY3Ncblx0XHRcdFx0cHVic3ViTWVzc2FnZXMuYWRkKDEsIHtcblx0XHRcdFx0XHRlbnY6IHByb2Nlc3MuZW52Lk5PREVfRU5WLFxuXHRcdFx0XHRcdHRvcGljOiBjb25maWcudG9waWMsXG5cdFx0XHRcdFx0c3Vic2NyaXB0aW9uOiBjb25maWcuc3Vic2NyaXB0aW9uLFxuXHRcdFx0XHRcdHByb3ZpZGVyOiBjb25maWcucHJvdmlkZXIsXG5cdFx0XHRcdFx0d29ya2Zsb3dfbmFtZTogdGhpcy5jb25maWd1cmF0aW9uLm5hbWUsXG5cdFx0XHRcdFx0c3VjY2VzczogXCJ0cnVlXCIsXG5cdFx0XHRcdH0pO1xuXG5cdFx0XHRcdGN0eC5sb2dnZXIubG9nKGBNZXNzYWdlIHByb2Nlc3NlZCBpbiAkeyhlbmQgLSBzdGFydCkudG9GaXhlZCgyKX1tczogJHtpZH1gKTtcblxuXHRcdFx0XHQvLyBBY2tub3dsZWRnZSBtZXNzYWdlIGlmIGNvbmZpZ3VyZWRcblx0XHRcdFx0aWYgKGNvbmZpZy5hY2sgIT09IGZhbHNlKSB7XG5cdFx0XHRcdFx0YXdhaXQgbWVzc2FnZS5hY2soKTtcblx0XHRcdFx0XHRjdHgubG9nZ2VyLmxvZyhgTWVzc2FnZSBhY2tub3dsZWRnZWQ6ICR7aWR9YCk7XG5cdFx0XHRcdH1cblx0XHRcdH0gY2F0Y2ggKGVycm9yKSB7XG5cdFx0XHRcdGNvbnN0IGVycm9yTWVzc2FnZSA9IChlcnJvciBhcyBFcnJvcikubWVzc2FnZTtcblxuXHRcdFx0XHQvLyBTZXQgc3BhbiBlcnJvclxuXHRcdFx0XHRzcGFuLnNldEF0dHJpYnV0ZShcInN1Y2Nlc3NcIiwgZmFsc2UpO1xuXHRcdFx0XHRzcGFuLnJlY29yZEV4Y2VwdGlvbihlcnJvciBhcyBFcnJvcik7XG5cdFx0XHRcdHNwYW4uc2V0U3RhdHVzKHsgY29kZTogU3BhblN0YXR1c0NvZGUuRVJST1IsIG1lc3NhZ2U6IGVycm9yTWVzc2FnZSB9KTtcblxuXHRcdFx0XHQvLyBSZWNvcmQgZXJyb3IgbWV0cmljc1xuXHRcdFx0XHRwdWJzdWJFcnJvcnMuYWRkKDEsIHtcblx0XHRcdFx0XHRlbnY6IHByb2Nlc3MuZW52Lk5PREVfRU5WLFxuXHRcdFx0XHRcdHRvcGljOiBjb25maWcudG9waWMsXG5cdFx0XHRcdFx0c3Vic2NyaXB0aW9uOiBjb25maWcuc3Vic2NyaXB0aW9uLFxuXHRcdFx0XHRcdHByb3ZpZGVyOiBjb25maWcucHJvdmlkZXIsXG5cdFx0XHRcdFx0d29ya2Zsb3dfbmFtZTogdGhpcy5jb25maWd1cmF0aW9uPy5uYW1lIHx8IFwidW5rbm93blwiLFxuXHRcdFx0XHR9KTtcblxuXHRcdFx0XHR0aGlzLmxvZ2dlci5lcnJvcihgRmFpbGVkIHRvIHByb2Nlc3MgbWVzc2FnZSAke2lkfTogJHtlcnJvck1lc3NhZ2V9YCwgKGVycm9yIGFzIEVycm9yKS5zdGFjayk7XG5cblx0XHRcdFx0Ly8gTmFjayBtZXNzYWdlXG5cdFx0XHRcdGlmIChjb25maWcuYWNrICE9PSBmYWxzZSkge1xuXHRcdFx0XHRcdGF3YWl0IG1lc3NhZ2UubmFjaygpO1xuXHRcdFx0XHRcdHRoaXMubG9nZ2VyLmxvZyhgTWVzc2FnZSBuYWNrZWQ6ICR7aWR9YCk7XG5cdFx0XHRcdH1cblx0XHRcdH0gZmluYWxseSB7XG5cdFx0XHRcdHNwYW4uZW5kKCk7XG5cdFx0XHR9XG5cdFx0fSk7XG5cdH1cbn1cblxuZXhwb3J0IGRlZmF1bHQgUHViU3ViVHJpZ2dlcjtcbiJdfQ==
@@ -0,0 +1,67 @@
1
+ /**
2
+ * AWSSNSAdapter - AWS SNS/SQS adapter for PubSubTrigger
3
+ *
4
+ * Uses AWS SDK v3 for SNS/SQS connectivity.
5
+ * SNS topics deliver to SQS queues, which this adapter polls.
6
+ *
7
+ * Requires: npm install @aws-sdk/client-sns @aws-sdk/client-sqs
8
+ *
9
+ * Environment variables:
10
+ * - AWS_REGION: AWS region (default: us-east-1)
11
+ * - AWS_ACCESS_KEY_ID: AWS access key (optional if using IAM roles)
12
+ * - AWS_SECRET_ACCESS_KEY: AWS secret key (optional if using IAM roles)
13
+ * - SQS_WAIT_TIME_SECONDS: Long polling wait time (default: 20)
14
+ * - SQS_MAX_MESSAGES: Max messages per receive (default: 10)
15
+ */
16
+ import type { PubSubTriggerOpts } from "@blok/helper";
17
+ import type { PubSubAdapter, PubSubMessage } from "../PubSubTrigger";
18
+ /**
19
+ * AWS SNS/SQS configuration
20
+ */
21
+ export interface AWSSNSConfig {
22
+ region: string;
23
+ waitTimeSeconds?: number;
24
+ maxNumberOfMessages?: number;
25
+ }
26
+ /**
27
+ * AWSSNSAdapter - AWS SNS implementation using SQS subscriptions
28
+ */
29
+ export declare class AWSSNSAdapter implements PubSubAdapter {
30
+ readonly provider: "aws";
31
+ private sqsClient;
32
+ private connected;
33
+ private config;
34
+ private pollingIntervals;
35
+ private shouldStop;
36
+ constructor(config?: Partial<AWSSNSConfig>);
37
+ /**
38
+ * Connect to AWS
39
+ */
40
+ connect(): Promise<void>;
41
+ /**
42
+ * Disconnect from AWS
43
+ */
44
+ disconnect(): Promise<void>;
45
+ /**
46
+ * Subscribe to an SNS topic via SQS queue
47
+ * Note: The SQS queue should be pre-configured as an SNS subscription
48
+ */
49
+ subscribe(config: PubSubTriggerOpts, handler: (message: PubSubMessage) => Promise<void>): Promise<void>;
50
+ /**
51
+ * Poll SQS queue for messages (long polling)
52
+ */
53
+ private poll;
54
+ /**
55
+ * Unsubscribe from a queue (stops polling)
56
+ */
57
+ unsubscribe(queueUrl: string): Promise<void>;
58
+ /**
59
+ * Check if connected to AWS
60
+ */
61
+ isConnected(): boolean;
62
+ /**
63
+ * Health check - verify AWS connectivity
64
+ */
65
+ healthCheck(): Promise<boolean>;
66
+ }
67
+ export default AWSSNSAdapter;