cdk-local-lambda 0.0.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 (53) hide show
  1. package/LICENSE +202 -0
  2. package/README.md +94 -0
  3. package/lib/aspect/docker-function-hook.d.ts +18 -0
  4. package/lib/aspect/docker-function-hook.js +31 -0
  5. package/lib/aspect/live-lambda-aspect.d.ts +85 -0
  6. package/lib/aspect/live-lambda-aspect.js +277 -0
  7. package/lib/aspect/live-lambda-bootstrap.d.ts +17 -0
  8. package/lib/aspect/live-lambda-bootstrap.js +260 -0
  9. package/lib/aspect/nodejs-function-hook.d.ts +20 -0
  10. package/lib/aspect/nodejs-function-hook.js +27 -0
  11. package/lib/bootstrap-stack/bootstrap-stack.d.ts +60 -0
  12. package/lib/bootstrap-stack/bootstrap-stack.js +338 -0
  13. package/lib/cli/appsync/client.d.ts +30 -0
  14. package/lib/cli/appsync/client.js +227 -0
  15. package/lib/cli/cdk-app.d.ts +7 -0
  16. package/lib/cli/cdk-app.js +25 -0
  17. package/lib/cli/commands/bootstrap.d.ts +9 -0
  18. package/lib/cli/commands/bootstrap.js +50 -0
  19. package/lib/cli/commands/local.d.ts +40 -0
  20. package/lib/cli/commands/local.js +1172 -0
  21. package/lib/cli/daemon.d.ts +22 -0
  22. package/lib/cli/daemon.js +18 -0
  23. package/lib/cli/docker/container.d.ts +116 -0
  24. package/lib/cli/docker/container.js +414 -0
  25. package/lib/cli/docker/types.d.ts +71 -0
  26. package/lib/cli/docker/types.js +5 -0
  27. package/lib/cli/docker/watcher.d.ts +44 -0
  28. package/lib/cli/docker/watcher.js +115 -0
  29. package/lib/cli/index.d.ts +9 -0
  30. package/lib/cli/index.js +26 -0
  31. package/lib/cli/runtime-api/server.d.ts +102 -0
  32. package/lib/cli/runtime-api/server.js +396 -0
  33. package/lib/cli/runtime-api/types.d.ts +149 -0
  34. package/lib/cli/runtime-api/types.js +10 -0
  35. package/lib/cli/runtime-wrapper/nodejs-runtime.d.ts +16 -0
  36. package/lib/cli/runtime-wrapper/nodejs-runtime.js +248 -0
  37. package/lib/cli/watcher/file-watcher.d.ts +32 -0
  38. package/lib/cli/watcher/file-watcher.js +57 -0
  39. package/lib/functions/bridge/appsync-client.d.ts +73 -0
  40. package/lib/functions/bridge/appsync-client.js +345 -0
  41. package/lib/functions/bridge/handler.d.ts +17 -0
  42. package/lib/functions/bridge/handler.js +79 -0
  43. package/lib/functions/bridge/ssm-config.d.ts +19 -0
  44. package/lib/functions/bridge/ssm-config.js +45 -0
  45. package/lib/functions/bridge-builder/handler.d.ts +12 -0
  46. package/lib/functions/bridge-builder/handler.js +181 -0
  47. package/lib/functions/bridge-docker/runtime.d.ts +9 -0
  48. package/lib/functions/bridge-docker/runtime.js +127 -0
  49. package/lib/index.d.ts +24 -0
  50. package/lib/index.js +28 -0
  51. package/lib/shared/types.d.ts +102 -0
  52. package/lib/shared/types.js +125 -0
  53. package/package.json +111 -0
@@ -0,0 +1,16 @@
1
+ /**
2
+ * Node.js runtime wrapper that emulates the Lambda Runtime environment.
3
+ *
4
+ * This script does what the Lambda runtime does inside a container:
5
+ * 1. Polls our local Runtime API for invocations (GET /invocation/next)
6
+ * 2. Loads and invokes the user's handler via import()
7
+ * 3. Posts responses back (POST /invocation/{requestId}/response)
8
+ *
9
+ * Environment variables:
10
+ * - AWS_LAMBDA_RUNTIME_API: The host:port of our local Runtime API server
11
+ * - _HANDLER: The handler path in format "path/to/file.handlerName"
12
+ * - LAMBDA_TASK_ROOT: The project root directory for resolving handler modules
13
+ *
14
+ * This script is spawned by the daemon for each Node.js Lambda function.
15
+ */
16
+ export {};
@@ -0,0 +1,248 @@
1
+ /**
2
+ * Node.js runtime wrapper that emulates the Lambda Runtime environment.
3
+ *
4
+ * This script does what the Lambda runtime does inside a container:
5
+ * 1. Polls our local Runtime API for invocations (GET /invocation/next)
6
+ * 2. Loads and invokes the user's handler via import()
7
+ * 3. Posts responses back (POST /invocation/{requestId}/response)
8
+ *
9
+ * Environment variables:
10
+ * - AWS_LAMBDA_RUNTIME_API: The host:port of our local Runtime API server
11
+ * - _HANDLER: The handler path in format "path/to/file.handlerName"
12
+ * - LAMBDA_TASK_ROOT: The project root directory for resolving handler modules
13
+ *
14
+ * This script is spawned by the daemon for each Node.js Lambda function.
15
+ */
16
+ import * as path from "node:path";
17
+ // Runtime API base URL from environment
18
+ const RUNTIME_API = process.env.AWS_LAMBDA_RUNTIME_API;
19
+ if (!RUNTIME_API) {
20
+ console.error("AWS_LAMBDA_RUNTIME_API not set");
21
+ process.exit(1);
22
+ }
23
+ // Handler path from environment (e.g., "functions/greeter/handler.handler")
24
+ const HANDLER = process.env._HANDLER;
25
+ if (!HANDLER) {
26
+ console.error("_HANDLER not set");
27
+ process.exit(1);
28
+ }
29
+ // Project root for resolving handler modules
30
+ const PROJECT_ROOT = process.env.LAMBDA_TASK_ROOT ?? process.cwd();
31
+ const RUNTIME_API_BASE = `http://${RUNTIME_API}`;
32
+ // Current request ID for logging context (set during invocation)
33
+ let currentRequestId = null;
34
+ /**
35
+ * Log an error in Lambda log format so it gets the proper invocation prefix.
36
+ * Format: TIMESTAMP\tREQUEST_ID\tLEVEL\tMESSAGE
37
+ */
38
+ function lambdaError(level, message) {
39
+ const timestamp = new Date().toISOString();
40
+ const requestId = currentRequestId ?? "00000000-0000-0000-0000-000000000000";
41
+ console.error(`${timestamp}\t${requestId}\t${level}\t${message}`);
42
+ }
43
+ /**
44
+ * Parse handler path into module path and export name.
45
+ * Handler format: "path/to/module.exportName"
46
+ * Example: "functions/greeter/handler.handler" -> { modulePath: "functions/greeter/handler", exportName: "handler" }
47
+ */
48
+ function parseHandler(handlerPath) {
49
+ const lastDot = handlerPath.lastIndexOf(".");
50
+ if (lastDot === -1) {
51
+ throw new Error(`Invalid handler format: "${handlerPath}". Expected "path/to/module.exportName"`);
52
+ }
53
+ return {
54
+ modulePath: handlerPath.substring(0, lastDot),
55
+ exportName: handlerPath.substring(lastDot + 1),
56
+ };
57
+ }
58
+ /**
59
+ * Resolve and load the handler function.
60
+ * Tries common extensions: .ts, .js, .mjs, .cjs (Bun handles all natively)
61
+ */
62
+ async function loadHandler(handlerPath, projectRoot) {
63
+ const { modulePath, exportName } = parseHandler(handlerPath);
64
+ // Extensions to try (Bun supports all natively)
65
+ const extensions = [".ts", ".js", ".mjs", ".cjs", ""];
66
+ let loadedModule;
67
+ let resolvedPath;
68
+ for (const ext of extensions) {
69
+ const fullPath = path.resolve(projectRoot, modulePath + ext);
70
+ try {
71
+ loadedModule = (await import(fullPath));
72
+ resolvedPath = fullPath;
73
+ break;
74
+ }
75
+ catch {
76
+ // Try next extension
77
+ }
78
+ }
79
+ if (!loadedModule || !resolvedPath) {
80
+ throw new Error(`Could not load handler module: "${modulePath}" (tried extensions: ${extensions.join(", ")})`);
81
+ }
82
+ const handler = loadedModule[exportName];
83
+ if (typeof handler !== "function") {
84
+ throw new Error(`Handler "${exportName}" is not a function (got ${typeof handler})`);
85
+ }
86
+ return handler;
87
+ }
88
+ /**
89
+ * Get next invocation from Runtime API.
90
+ * This blocks until an invocation is available.
91
+ *
92
+ * Returns null if the server returned 503 (poll timeout), signaling we should retry.
93
+ * This is expected behavior - the server times out idle connections to work within
94
+ * HTTP server limitations, but native workers can just reconnect and keep polling.
95
+ */
96
+ async function getNextInvocation() {
97
+ const response = await fetch(`${RUNTIME_API_BASE}/2018-06-01/runtime/invocation/next`);
98
+ // 503 means the server's poll timeout expired - this is expected behavior
99
+ // for idle connections. Native workers should just retry polling.
100
+ if (response.status === 503) {
101
+ return null;
102
+ }
103
+ if (!response.ok) {
104
+ throw new Error(`Failed to get next invocation: ${response.status}`);
105
+ }
106
+ const event = await response.json();
107
+ const requestId = response.headers.get("Lambda-Runtime-Aws-Request-Id") ?? "";
108
+ const deadlineMs = response.headers.get("Lambda-Runtime-Deadline-Ms") ?? "0";
109
+ const invokedFunctionArn = response.headers.get("Lambda-Runtime-Invoked-Function-Arn") ?? "";
110
+ const logGroupName = response.headers.get("Lambda-Runtime-Log-Group-Name") ?? "";
111
+ const logStreamName = response.headers.get("Lambda-Runtime-Log-Stream-Name") ?? "";
112
+ // Extract function name and version from ARN
113
+ // ARN format: arn:aws:lambda:region:account:function:name:version
114
+ const arnParts = invokedFunctionArn.split(":");
115
+ const functionName = arnParts[6] ?? "";
116
+ const functionVersion = arnParts[7] ?? "$LATEST";
117
+ // Build Lambda context object
118
+ const context = {
119
+ functionName,
120
+ functionVersion,
121
+ invokedFunctionArn,
122
+ memoryLimitInMB: process.env.AWS_LAMBDA_FUNCTION_MEMORY_SIZE ?? "128",
123
+ awsRequestId: requestId,
124
+ logGroupName,
125
+ logStreamName,
126
+ getRemainingTimeInMillis: () => Math.max(0, Number(deadlineMs) - Date.now()),
127
+ // These are not used in local execution but required by the type
128
+ callbackWaitsForEmptyEventLoop: true,
129
+ identity: undefined,
130
+ clientContext: undefined,
131
+ done: () => { },
132
+ fail: () => { },
133
+ succeed: () => { },
134
+ };
135
+ return { event, context, requestId };
136
+ }
137
+ /**
138
+ * Post successful response to Runtime API.
139
+ */
140
+ async function postResponse(requestId, result) {
141
+ const response = await fetch(`${RUNTIME_API_BASE}/2018-06-01/runtime/invocation/${requestId}/response`, {
142
+ method: "POST",
143
+ headers: { "Content-Type": "application/json" },
144
+ body: JSON.stringify(result),
145
+ });
146
+ if (!response.ok) {
147
+ lambdaError("ERROR", `Failed to post response: ${response.status}`);
148
+ }
149
+ }
150
+ /**
151
+ * Post error to Runtime API.
152
+ */
153
+ async function postError(requestId, error) {
154
+ const errorPayload = {
155
+ errorType: error.name ?? "Error",
156
+ errorMessage: error.message ?? String(error),
157
+ stackTrace: error.stack?.split("\n") ?? [],
158
+ };
159
+ const response = await fetch(`${RUNTIME_API_BASE}/2018-06-01/runtime/invocation/${requestId}/error`, {
160
+ method: "POST",
161
+ headers: {
162
+ "Content-Type": "application/json",
163
+ "Lambda-Runtime-Function-Error-Type": errorPayload.errorType,
164
+ },
165
+ body: JSON.stringify(errorPayload),
166
+ });
167
+ if (!response.ok) {
168
+ lambdaError("ERROR", `Failed to post error: ${response.status}`);
169
+ }
170
+ }
171
+ /**
172
+ * Post init error to Runtime API.
173
+ */
174
+ async function postInitError(error) {
175
+ const errorPayload = {
176
+ errorType: error.name ?? "InitError",
177
+ errorMessage: error.message ?? String(error),
178
+ stackTrace: error.stack?.split("\n") ?? [],
179
+ };
180
+ try {
181
+ await fetch(`${RUNTIME_API_BASE}/2018-06-01/runtime/init/error`, {
182
+ method: "POST",
183
+ headers: {
184
+ "Content-Type": "application/json",
185
+ "Lambda-Runtime-Function-Error-Type": errorPayload.errorType,
186
+ },
187
+ body: JSON.stringify(errorPayload),
188
+ });
189
+ }
190
+ catch {
191
+ // Ignore - we're already in an error state
192
+ }
193
+ }
194
+ /**
195
+ * Main runtime loop.
196
+ */
197
+ async function main() {
198
+ // Load handler once at startup
199
+ // Note: bun --watch automatically tracks dynamic imports and restarts when they change
200
+ let handler;
201
+ try {
202
+ handler = await loadHandler(HANDLER, PROJECT_ROOT);
203
+ }
204
+ catch (error) {
205
+ console.error(`Failed to load handler: ${error}`);
206
+ await postInitError(error instanceof Error ? error : new Error(String(error)));
207
+ process.exit(1);
208
+ }
209
+ // Main invocation loop
210
+ while (true) {
211
+ try {
212
+ // Get next invocation (blocks until available)
213
+ const invocation = await getNextInvocation();
214
+ // null means 503 timeout - server wants us to reconnect and keep polling
215
+ if (invocation === null) {
216
+ // Small delay before reconnecting to avoid tight loop
217
+ await new Promise((resolve) => setTimeout(resolve, 100));
218
+ continue;
219
+ }
220
+ const { event, context, requestId } = invocation;
221
+ currentRequestId = requestId;
222
+ try {
223
+ // Invoke handler
224
+ const result = await handler(event, context);
225
+ // Post response
226
+ await postResponse(requestId, result);
227
+ }
228
+ catch (error) {
229
+ lambdaError("ERROR", `Handler error: ${error}`);
230
+ await postError(requestId, error instanceof Error ? error : new Error(String(error)));
231
+ }
232
+ finally {
233
+ currentRequestId = null;
234
+ }
235
+ }
236
+ catch (error) {
237
+ // Error getting next invocation - this is fatal, exit and let daemon restart
238
+ console.error(`Fatal error in invocation loop: ${error}`);
239
+ process.exit(1);
240
+ }
241
+ }
242
+ }
243
+ // Run the main loop
244
+ main().catch((error) => {
245
+ console.error(`Unhandled error: ${error}`);
246
+ process.exit(1);
247
+ });
248
+ //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoibm9kZWpzLXJ1bnRpbWUuanMiLCJzb3VyY2VSb290IjoiIiwic291cmNlcyI6WyIuLi8uLi8uLi9zcmMvY2xpL3J1bnRpbWUtd3JhcHBlci9ub2RlanMtcnVudGltZS50cyJdLCJuYW1lcyI6W10sIm1hcHBpbmdzIjoiQUFBQTs7Ozs7Ozs7Ozs7Ozs7R0FjRztBQUVILE9BQU8sS0FBSyxJQUFJLE1BQU0sV0FBVyxDQUFBO0FBR2pDLHdDQUF3QztBQUN4QyxNQUFNLFdBQVcsR0FBRyxPQUFPLENBQUMsR0FBRyxDQUFDLHNCQUFzQixDQUFBO0FBQ3RELElBQUksQ0FBQyxXQUFXLEVBQUUsQ0FBQztJQUNqQixPQUFPLENBQUMsS0FBSyxDQUFDLGdDQUFnQyxDQUFDLENBQUE7SUFDL0MsT0FBTyxDQUFDLElBQUksQ0FBQyxDQUFDLENBQUMsQ0FBQTtBQUNqQixDQUFDO0FBRUQsNEVBQTRFO0FBQzVFLE1BQU0sT0FBTyxHQUFHLE9BQU8sQ0FBQyxHQUFHLENBQUMsUUFBUSxDQUFBO0FBQ3BDLElBQUksQ0FBQyxPQUFPLEVBQUUsQ0FBQztJQUNiLE9BQU8sQ0FBQyxLQUFLLENBQUMsa0JBQWtCLENBQUMsQ0FBQTtJQUNqQyxPQUFPLENBQUMsSUFBSSxDQUFDLENBQUMsQ0FBQyxDQUFBO0FBQ2pCLENBQUM7QUFFRCw2Q0FBNkM7QUFDN0MsTUFBTSxZQUFZLEdBQUcsT0FBTyxDQUFDLEdBQUcsQ0FBQyxnQkFBZ0IsSUFBSSxPQUFPLENBQUMsR0FBRyxFQUFFLENBQUE7QUFFbEUsTUFBTSxnQkFBZ0IsR0FBRyxVQUFVLFdBQVcsRUFBRSxDQUFBO0FBRWhELGlFQUFpRTtBQUNqRSxJQUFJLGdCQUFnQixHQUFrQixJQUFJLENBQUE7QUFFMUM7OztHQUdHO0FBQ0gsU0FBUyxXQUFXLENBQUMsS0FBYSxFQUFFLE9BQWU7SUFDakQsTUFBTSxTQUFTLEdBQUcsSUFBSSxJQUFJLEVBQUUsQ0FBQyxXQUFXLEVBQUUsQ0FBQTtJQUMxQyxNQUFNLFNBQVMsR0FBRyxnQkFBZ0IsSUFBSSxz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QUFLLENBQUMsQ0FBQyxDQUFDLElBQUksS0FBSyxDQUFDLE1BQU0sQ0FBQyxLQUFLLENBQUMsQ0FBQyxDQUMxRCxDQUFBO1lBQ0gsQ0FBQztvQkFBUyxDQUFDO2dCQUNULGdCQUFnQixHQUFHLElBQUksQ0FBQTtZQUN6QixDQUFDO1FBQ0gsQ0FBQztRQUFDLE9BQU8sS0FBSyxFQUFFLENBQUM7WUFDZiw2RUFBNkU7WUFDN0UsT0FBTyxDQUFDLEtBQUssQ0FBQyxtQ0FBbUMsS0FBSyxFQUFFLENBQUMsQ0FBQTtZQUN6RCxPQUFPLENBQUMsSUFBSSxDQUFDLENBQUMsQ0FBQyxDQUFBO1FBQ2pCLENBQUM7SUFDSCxDQUFDO0FBQ0gsQ0FBQztBQUVELG9CQUFvQjtBQUNwQixJQUFJLEVBQUUsQ0FBQyxLQUFLLENBQUMsQ0FBQyxLQUFLLEVBQUUsRUFBRTtJQUNyQixPQUFPLENBQUMsS0FBSyxDQUFDLG9CQUFvQixLQUFLLEVBQUUsQ0FBQyxDQUFBO0lBQzFDLE9BQU8sQ0FBQyxJQUFJLENBQUMsQ0FBQyxDQUFDLENBQUE7QUFDakIsQ0FBQyxDQUFDLENBQUEiLCJzb3VyY2VzQ29udGVudCI6WyIvKipcbiAqIE5vZGUuanMgcnVudGltZSB3cmFwcGVyIHRoYXQgZW11bGF0ZXMgdGhlIExhbWJkYSBSdW50aW1lIGVudmlyb25tZW50LlxuICpcbiAqIFRoaXMgc2NyaXB0IGRvZXMgd2hhdCB0aGUgTGFtYmRhIHJ1bnRpbWUgZG9lcyBpbnNpZGUgYSBjb250YWluZXI6XG4gKiAxLiBQb2xscyBvdXIgbG9jYWwgUnVudGltZSBBUEkgZm9yIGludm9jYXRpb25zIChHRVQgL2ludm9jYXRpb24vbmV4dClcbiAqIDIuIExvYWRzIGFuZCBpbnZva2VzIHRoZSB1c2VyJ3MgaGFuZGxlciB2aWEgaW1wb3J0KClcbiAqIDMuIFBvc3RzIHJlc3BvbnNlcyBiYWNrIChQT1NUIC9pbnZvY2F0aW9uL3tyZXF1ZXN0SWR9L3Jlc3BvbnNlKVxuICpcbiAqIEVudmlyb25tZW50IHZhcmlhYmxlczpcbiAqIC0gQVdTX0xBTUJEQV9SVU5USU1FX0FQSTogVGhlIGhvc3Q6cG9ydCBvZiBvdXIgbG9jYWwgUnVudGltZSBBUEkgc2VydmVyXG4gKiAtIF9IQU5ETEVSOiBUaGUgaGFuZGxlciBwYXRoIGluIGZvcm1hdCBcInBhdGgvdG8vZmlsZS5oYW5kbGVyTmFtZVwiXG4gKiAtIExBTUJEQV9UQVNLX1JPT1Q6IFRoZSBwcm9qZWN0IHJvb3QgZGlyZWN0b3J5IGZvciByZXNvbHZpbmcgaGFuZGxlciBtb2R1bGVzXG4gKlxuICogVGhpcyBzY3JpcHQgaXMgc3Bhd25lZCBieSB0aGUgZGFlbW9uIGZvciBlYWNoIE5vZGUuanMgTGFtYmRhIGZ1bmN0aW9uLlxuICovXG5cbmltcG9ydCAqIGFzIHBhdGggZnJvbSBcIm5vZGU6cGF0aFwiXG5pbXBvcnQgdHlwZSB7IENvbnRleHQgfSBmcm9tIFwiYXdzLWxhbWJkYVwiXG5cbi8vIFJ1bnRpbWUgQVBJIGJhc2UgVVJMIGZyb20gZW52aXJvbm1lbnRcbmNvbnN0IFJVTlRJTUVfQVBJID0gcHJvY2Vzcy5lbnYuQVdTX0xBTUJEQV9SVU5USU1FX0FQSVxuaWYgKCFSVU5USU1FX0FQSSkge1xuICBjb25zb2xlLmVycm9yKFwiQVdTX0xBTUJEQV9SVU5USU1FX0FQSSBub3Qgc2V0XCIpXG4gIHByb2Nlc3MuZXhpdCgxKVxufVxuXG4vLyBIYW5kbGVyIHBhdGggZnJvbSBlbnZpcm9ubWVudCAoZS5nLiwgXCJmdW5jdGlvbnMvZ3JlZXRlci9oYW5kbGVyLmhhbmRsZXJcIilcbmNvbnN0IEhBTkRMRVIgPSBwcm9jZXNzLmVudi5fSEFORExFUlxuaWYgKCFIQU5ETEVSKSB7XG4gIGNvbnNvbGUuZXJyb3IoXCJfSEFORExFUiBub3Qgc2V0XCIpXG4gIHByb2Nlc3MuZXhpdCgxKVxufVxuXG4vLyBQcm9qZWN0IHJvb3QgZm9yIHJlc29sdmluZyBoYW5kbGVyIG1vZHVsZXNcbmNvbnN0IFBST0pFQ1RfUk9PVCA9IHByb2Nlc3MuZW52LkxBTUJEQV9UQVNLX1JPT1QgPz8gcHJvY2Vzcy5jd2QoKVxuXG5jb25zdCBSVU5USU1FX0FQSV9CQVNFID0gYGh0dHA6Ly8ke1JVTlRJTUVfQVBJfWBcblxuLy8gQ3VycmVudCByZXF1ZXN0IElEIGZvciBsb2dnaW5nIGNvbnRleHQgKHNldCBkdXJpbmcgaW52b2NhdGlvbilcbmxldCBjdXJyZW50UmVxdWVzdElkOiBzdHJpbmcgfCBudWxsID0gbnVsbFxuXG4vKipcbiAqIExvZyBhbiBlcnJvciBpbiBMYW1iZGEgbG9nIGZvcm1hdCBzbyBpdCBnZXRzIHRoZSBwcm9wZXIgaW52b2NhdGlvbiBwcmVmaXguXG4gKiBGb3JtYXQ6IFRJTUVTVEFNUFxcdFJFUVVFU1RfSURcXHRMRVZFTFxcdE1FU1NBR0VcbiAqL1xuZnVuY3Rpb24gbGFtYmRhRXJyb3IobGV2ZWw6IHN0cmluZywgbWVzc2FnZTogc3RyaW5nKTogdm9pZCB7XG4gIGNvbnN0IHRpbWVzdGFtcCA9IG5ldyBEYXRlKCkudG9JU09TdHJpbmcoKVxuICBjb25zdCByZXF1ZXN0SWQgPSBjdXJyZW50UmVxdWVzdElkID8/IFwiMDAwMDAwMDAtMDAwMC0wMDAwLTAwMDAtMDAwMDAwMDAwMDAwXCJcbiAgY29uc29sZS5lcnJvcihgJHt0aW1lc3RhbXB9XFx0JHtyZXF1ZXN0SWR9XFx0JHtsZXZlbH1cXHQke21lc3NhZ2V9YClcbn1cblxuLyoqXG4gKiBQYXJzZSBoYW5kbGVyIHBhdGggaW50byBtb2R1bGUgcGF0aCBhbmQgZXhwb3J0IG5hbWUuXG4gKiBIYW5kbGVyIGZvcm1hdDogXCJwYXRoL3RvL21vZHVsZS5leHBvcnROYW1lXCJcbiAqIEV4YW1wbGU6IFwiZnVuY3Rpb25zL2dyZWV0ZXIvaGFuZGxlci5oYW5kbGVyXCIgLT4geyBtb2R1bGVQYXRoOiBcImZ1bmN0aW9ucy9ncmVldGVyL2hhbmRsZXJcIiwgZXhwb3J0TmFtZTogXCJoYW5kbGVyXCIgfVxuICovXG5mdW5jdGlvbiBwYXJzZUhhbmRsZXIoaGFuZGxlclBhdGg6IHN0cmluZyk6IHtcbiAgbW9kdWxlUGF0aDogc3RyaW5nXG4gIGV4cG9ydE5hbWU6IHN0cmluZ1xufSB7XG4gIGNvbnN0IGxhc3REb3QgPSBoYW5kbGVyUGF0aC5sYXN0SW5kZXhPZihcIi5cIilcbiAgaWYgKGxhc3REb3QgPT09IC0xKSB7XG4gICAgdGhyb3cgbmV3IEVycm9yKFxuICAgICAgYEludmFsaWQgaGFuZGxlciBmb3JtYXQ6IFwiJHtoYW5kbGVyUGF0aH1cIi4gRXhwZWN0ZWQgXCJwYXRoL3RvL21vZHVsZS5leHBvcnROYW1lXCJgLFxuICAgIClcbiAgfVxuICByZXR1cm4ge1xuICAgIG1vZHVsZVBhdGg6IGhhbmRsZXJQYXRoLnN1YnN0cmluZygwLCBsYXN0RG90KSxcbiAgICBleHBvcnROYW1lOiBoYW5kbGVyUGF0aC5zdWJzdHJpbmcobGFzdERvdCArIDEpLFxuICB9XG59XG5cbi8qKlxuICogUmVzb2x2ZSBhbmQgbG9hZCB0aGUgaGFuZGxlciBmdW5jdGlvbi5cbiAqIFRyaWVzIGNvbW1vbiBleHRlbnNpb25zOiAudHMsIC5qcywgLm1qcywgLmNqcyAoQnVuIGhhbmRsZXMgYWxsIG5hdGl2ZWx5KVxuICovXG5hc3luYyBmdW5jdGlvbiBsb2FkSGFuZGxlcihcbiAgaGFuZGxlclBhdGg6IHN0cmluZyxcbiAgcHJvamVjdFJvb3Q6IHN0cmluZyxcbik6IFByb21pc2U8KGV2ZW50OiB1bmtub3duLCBjb250ZXh0OiBDb250ZXh0KSA9PiBQcm9taXNlPHVua25vd24+PiB7XG4gIGNvbnN0IHsgbW9kdWxlUGF0aCwgZXhwb3J0TmFtZSB9ID0gcGFyc2VIYW5kbGVyKGhhbmRsZXJQYXRoKVxuXG4gIC8vIEV4dGVuc2lvbnMgdG8gdHJ5IChCdW4gc3VwcG9ydHMgYWxsIG5hdGl2ZWx5KVxuICBjb25zdCBleHRlbnNpb25zID0gW1wiLnRzXCIsIFwiLmpzXCIsIFwiLm1qc1wiLCBcIi5janNcIiwgXCJcIl1cblxuICBsZXQgbG9hZGVkTW9kdWxlOiBSZWNvcmQ8c3RyaW5nLCB1bmtub3duPiB8IHVuZGVmaW5lZFxuICBsZXQgcmVzb2x2ZWRQYXRoOiBzdHJpbmcgfCB1bmRlZmluZWRcblxuICBmb3IgKGNvbnN0IGV4dCBvZiBleHRlbnNpb25zKSB7XG4gICAgY29uc3QgZnVsbFBhdGggPSBwYXRoLnJlc29sdmUocHJvamVjdFJvb3QsIG1vZHVsZVBhdGggKyBleHQpXG4gICAgdHJ5IHtcbiAgICAgIGxvYWRlZE1vZHVsZSA9IChhd2FpdCBpbXBvcnQoZnVsbFBhdGgpKSBhcyBSZWNvcmQ8c3RyaW5nLCB1bmtub3duPlxuICAgICAgcmVzb2x2ZWRQYXRoID0gZnVsbFBhdGhcbiAgICAgIGJyZWFrXG4gICAgfSBjYXRjaCB7XG4gICAgICAvLyBUcnkgbmV4dCBleHRlbnNpb25cbiAgICB9XG4gIH1cblxuICBpZiAoIWxvYWRlZE1vZHVsZSB8fCAhcmVzb2x2ZWRQYXRoKSB7XG4gICAgdGhyb3cgbmV3IEVycm9yKFxuICAgICAgYENvdWxkIG5vdCBsb2FkIGhhbmRsZXIgbW9kdWxlOiBcIiR7bW9kdWxlUGF0aH1cIiAodHJpZWQgZXh0ZW5zaW9uczogJHtleHRlbnNpb25zLmpvaW4oXCIsIFwiKX0pYCxcbiAgICApXG4gIH1cblxuICBjb25zdCBoYW5kbGVyID0gbG9hZGVkTW9kdWxlW2V4cG9ydE5hbWVdXG4gIGlmICh0eXBlb2YgaGFuZGxlciAhPT0gXCJmdW5jdGlvblwiKSB7XG4gICAgdGhyb3cgbmV3IEVycm9yKFxuICAgICAgYEhhbmRsZXIgXCIke2V4cG9ydE5hbWV9XCIgaXMgbm90IGEgZnVuY3Rpb24gKGdvdCAke3R5cGVvZiBoYW5kbGVyfSlgLFxuICAgIClcbiAgfVxuXG4gIHJldHVybiBoYW5kbGVyIGFzIChldmVudDogdW5rbm93biwgY29udGV4dDogQ29udGV4dCkgPT4gUHJvbWlzZTx1bmtub3duPlxufVxuXG4vKipcbiAqIEdldCBuZXh0IGludm9jYXRpb24gZnJvbSBSdW50aW1lIEFQSS5cbiAqIFRoaXMgYmxvY2tzIHVudGlsIGFuIGludm9jYXRpb24gaXMgYXZhaWxhYmxlLlxuICpcbiAqIFJldHVybnMgbnVsbCBpZiB0aGUgc2VydmVyIHJldHVybmVkIDUwMyAocG9sbCB0aW1lb3V0KSwgc2lnbmFsaW5nIHdlIHNob3VsZCByZXRyeS5cbiAqIFRoaXMgaXMgZXhwZWN0ZWQgYmVoYXZpb3IgLSB0aGUgc2VydmVyIHRpbWVzIG91dCBpZGxlIGNvbm5lY3Rpb25zIHRvIHdvcmsgd2l0aGluXG4gKiBIVFRQIHNlcnZlciBsaW1pdGF0aW9ucywgYnV0IG5hdGl2ZSB3b3JrZXJzIGNhbiBqdXN0IHJlY29ubmVjdCBhbmQga2VlcCBwb2xsaW5nLlxuICovXG5hc3luYyBmdW5jdGlvbiBnZXROZXh0SW52b2NhdGlvbigpOiBQcm9taXNlPHtcbiAgZXZlbnQ6IHVua25vd25cbiAgY29udGV4dDogQ29udGV4dFxuICByZXF1ZXN0SWQ6IHN0cmluZ1xufSB8IG51bGw+IHtcbiAgY29uc3QgcmVzcG9uc2UgPSBhd2FpdCBmZXRjaChcbiAgICBgJHtSVU5USU1FX0FQSV9CQVNFfS8yMDE4LTA2LTAxL3J1bnRpbWUvaW52b2NhdGlvbi9uZXh0YCxcbiAgKVxuXG4gIC8vIDUwMyBtZWFucyB0aGUgc2VydmVyJ3MgcG9sbCB0aW1lb3V0IGV4cGlyZWQgLSB0aGlzIGlzIGV4cGVjdGVkIGJlaGF2aW9yXG4gIC8vIGZvciBpZGxlIGNvbm5lY3Rpb25zLiBOYXRpdmUgd29ya2VycyBzaG91bGQganVzdCByZXRyeSBwb2xsaW5nLlxuICBpZiAocmVzcG9uc2Uuc3RhdHVzID09PSA1MDMpIHtcbiAgICByZXR1cm4gbnVsbFxuICB9XG5cbiAgaWYgKCFyZXNwb25zZS5vaykge1xuICAgIHRocm93IG5ldyBFcnJvcihgRmFpbGVkIHRvIGdldCBuZXh0IGludm9jYXRpb246ICR7cmVzcG9uc2Uuc3RhdHVzfWApXG4gIH1cblxuICBjb25zdCBldmVudCA9IGF3YWl0IHJlc3BvbnNlLmpzb24oKVxuICBjb25zdCByZXF1ZXN0SWQgPSByZXNwb25zZS5oZWFkZXJzLmdldChcIkxhbWJkYS1SdW50aW1lLUF3cy1SZXF1ZXN0LUlkXCIpID8/IFwiXCJcbiAgY29uc3QgZGVhZGxpbmVNcyA9IHJlc3BvbnNlLmhlYWRlcnMuZ2V0KFwiTGFtYmRhLVJ1bnRpbWUtRGVhZGxpbmUtTXNcIikgPz8gXCIwXCJcbiAgY29uc3QgaW52b2tlZEZ1bmN0aW9uQXJuID1cbiAgICByZXNwb25zZS5oZWFkZXJzLmdldChcIkxhbWJkYS1SdW50aW1lLUludm9rZWQtRnVuY3Rpb24tQXJuXCIpID8/IFwiXCJcbiAgY29uc3QgbG9nR3JvdXBOYW1lID1cbiAgICByZXNwb25zZS5oZWFkZXJzLmdldChcIkxhbWJkYS1SdW50aW1lLUxvZy1Hcm91cC1OYW1lXCIpID8/IFwiXCJcbiAgY29uc3QgbG9nU3RyZWFtTmFtZSA9XG4gICAgcmVzcG9uc2UuaGVhZGVycy5nZXQoXCJMYW1iZGEtUnVudGltZS1Mb2ctU3RyZWFtLU5hbWVcIikgPz8gXCJcIlxuXG4gIC8vIEV4dHJhY3QgZnVuY3Rpb24gbmFtZSBhbmQgdmVyc2lvbiBmcm9tIEFSTlxuICAvLyBBUk4gZm9ybWF0OiBhcm46YXdzOmxhbWJkYTpyZWdpb246YWNjb3VudDpmdW5jdGlvbjpuYW1lOnZlcnNpb25cbiAgY29uc3QgYXJuUGFydHMgPSBpbnZva2VkRnVuY3Rpb25Bcm4uc3BsaXQoXCI6XCIpXG4gIGNvbnN0IGZ1bmN0aW9uTmFtZSA9IGFyblBhcnRzWzZdID8/IFwiXCJcbiAgY29uc3QgZnVuY3Rpb25WZXJzaW9uID0gYXJuUGFydHNbN10gPz8gXCIkTEFURVNUXCJcblxuICAvLyBCdWlsZCBMYW1iZGEgY29udGV4dCBvYmplY3RcbiAgY29uc3QgY29udGV4dDogQ29udGV4dCA9IHtcbiAgICBmdW5jdGlvbk5hbWUsXG4gICAgZnVuY3Rpb25WZXJzaW9uLFxuICAgIGludm9rZWRGdW5jdGlvbkFybixcbiAgICBtZW1vcnlMaW1pdEluTUI6IHByb2Nlc3MuZW52LkFXU19MQU1CREFfRlVOQ1RJT05fTUVNT1JZX1NJWkUgPz8gXCIxMjhcIixcbiAgICBhd3NSZXF1ZXN0SWQ6IHJlcXVlc3RJZCxcbiAgICBsb2dHcm91cE5hbWUsXG4gICAgbG9nU3RyZWFtTmFtZSxcbiAgICBnZXRSZW1haW5pbmdUaW1lSW5NaWxsaXM6ICgpID0+XG4gICAgICBNYXRoLm1heCgwLCBOdW1iZXIoZGVhZGxpbmVNcykgLSBEYXRlLm5vdygpKSxcbiAgICAvLyBUaGVzZSBhcmUgbm90IHVzZWQgaW4gbG9jYWwgZXhlY3V0aW9uIGJ1dCByZXF1aXJlZCBieSB0aGUgdHlwZVxuICAgIGNhbGxiYWNrV2FpdHNGb3JFbXB0eUV2ZW50TG9vcDogdHJ1ZSxcbiAgICBpZGVudGl0eTogdW5kZWZpbmVkLFxuICAgIGNsaWVudENvbnRleHQ6IHVuZGVmaW5lZCxcbiAgICBkb25lOiAoKSA9PiB7fSxcbiAgICBmYWlsOiAoKSA9PiB7fSxcbiAgICBzdWNjZWVkOiAoKSA9PiB7fSxcbiAgfVxuXG4gIHJldHVybiB7IGV2ZW50LCBjb250ZXh0LCByZXF1ZXN0SWQgfVxufVxuXG4vKipcbiAqIFBvc3Qgc3VjY2Vzc2Z1bCByZXNwb25zZSB0byBSdW50aW1lIEFQSS5cbiAqL1xuYXN5bmMgZnVuY3Rpb24gcG9zdFJlc3BvbnNlKHJlcXVlc3RJZDogc3RyaW5nLCByZXN1bHQ6IHVua25vd24pOiBQcm9taXNlPHZvaWQ+IHtcbiAgY29uc3QgcmVzcG9uc2UgPSBhd2FpdCBmZXRjaChcbiAgICBgJHtSVU5USU1FX0FQSV9CQVNFfS8yMDE4LTA2LTAxL3J1bnRpbWUvaW52b2NhdGlvbi8ke3JlcXVlc3RJZH0vcmVzcG9uc2VgLFxuICAgIHtcbiAgICAgIG1ldGhvZDogXCJQT1NUXCIsXG4gICAgICBoZWFkZXJzOiB7IFwiQ29udGVudC1UeXBlXCI6IFwiYXBwbGljYXRpb24vanNvblwiIH0sXG4gICAgICBib2R5OiBKU09OLnN0cmluZ2lmeShyZXN1bHQpLFxuICAgIH0sXG4gIClcblxuICBpZiAoIXJlc3BvbnNlLm9rKSB7XG4gICAgbGFtYmRhRXJyb3IoXCJFUlJPUlwiLCBgRmFpbGVkIHRvIHBvc3QgcmVzcG9uc2U6ICR7cmVzcG9uc2Uuc3RhdHVzfWApXG4gIH1cbn1cblxuLyoqXG4gKiBQb3N0IGVycm9yIHRvIFJ1bnRpbWUgQVBJLlxuICovXG5hc3luYyBmdW5jdGlvbiBwb3N0RXJyb3IocmVxdWVzdElkOiBzdHJpbmcsIGVycm9yOiBFcnJvcik6IFByb21pc2U8dm9pZD4ge1xuICBjb25zdCBlcnJvclBheWxvYWQgPSB7XG4gICAgZXJyb3JUeXBlOiBlcnJvci5uYW1lID8/IFwiRXJyb3JcIixcbiAgICBlcnJvck1lc3NhZ2U6IGVycm9yLm1lc3NhZ2UgPz8gU3RyaW5nKGVycm9yKSxcbiAgICBzdGFja1RyYWNlOiBlcnJvci5zdGFjaz8uc3BsaXQoXCJcXG5cIikgPz8gW10sXG4gIH1cblxuICBjb25zdCByZXNwb25zZSA9IGF3YWl0IGZldGNoKFxuICAgIGAke1JVTlRJTUVfQVBJX0JBU0V9LzIwMTgtMDYtMDEvcnVudGltZS9pbnZvY2F0aW9uLyR7cmVxdWVzdElkfS9lcnJvcmAsXG4gICAge1xuICAgICAgbWV0aG9kOiBcIlBPU1RcIixcbiAgICAgIGhlYWRlcnM6IHtcbiAgICAgICAgXCJDb250ZW50LVR5cGVcIjogXCJhcHBsaWNhdGlvbi9qc29uXCIsXG4gICAgICAgIFwiTGFtYmRhLVJ1bnRpbWUtRnVuY3Rpb24tRXJyb3ItVHlwZVwiOiBlcnJvclBheWxvYWQuZXJyb3JUeXBlLFxuICAgICAgfSxcbiAgICAgIGJvZHk6IEpTT04uc3RyaW5naWZ5KGVycm9yUGF5bG9hZCksXG4gICAgfSxcbiAgKVxuXG4gIGlmICghcmVzcG9uc2Uub2spIHtcbiAgICBsYW1iZGFFcnJvcihcIkVSUk9SXCIsIGBGYWlsZWQgdG8gcG9zdCBlcnJvcjogJHtyZXNwb25zZS5zdGF0dXN9YClcbiAgfVxufVxuXG4vKipcbiAqIFBvc3QgaW5pdCBlcnJvciB0byBSdW50aW1lIEFQSS5cbiAqL1xuYXN5bmMgZnVuY3Rpb24gcG9zdEluaXRFcnJvcihlcnJvcjogRXJyb3IpOiBQcm9taXNlPHZvaWQ+IHtcbiAgY29uc3QgZXJyb3JQYXlsb2FkID0ge1xuICAgIGVycm9yVHlwZTogZXJyb3IubmFtZSA/PyBcIkluaXRFcnJvclwiLFxuICAgIGVycm9yTWVzc2FnZTogZXJyb3IubWVzc2FnZSA/PyBTdHJpbmcoZXJyb3IpLFxuICAgIHN0YWNrVHJhY2U6IGVycm9yLnN0YWNrPy5zcGxpdChcIlxcblwiKSA/PyBbXSxcbiAgfVxuXG4gIHRyeSB7XG4gICAgYXdhaXQgZmV0Y2goYCR7UlVOVElNRV9BUElfQkFTRX0vMjAxOC0wNi0wMS9ydW50aW1lL2luaXQvZXJyb3JgLCB7XG4gICAgICBtZXRob2Q6IFwiUE9TVFwiLFxuICAgICAgaGVhZGVyczoge1xuICAgICAgICBcIkNvbnRlbnQtVHlwZVwiOiBcImFwcGxpY2F0aW9uL2pzb25cIixcbiAgICAgICAgXCJMYW1iZGEtUnVudGltZS1GdW5jdGlvbi1FcnJvci1UeXBlXCI6IGVycm9yUGF5bG9hZC5lcnJvclR5cGUsXG4gICAgICB9LFxuICAgICAgYm9keTogSlNPTi5zdHJpbmdpZnkoZXJyb3JQYXlsb2FkKSxcbiAgICB9KVxuICB9IGNhdGNoIHtcbiAgICAvLyBJZ25vcmUgLSB3ZSdyZSBhbHJlYWR5IGluIGFuIGVycm9yIHN0YXRlXG4gIH1cbn1cblxuLyoqXG4gKiBNYWluIHJ1bnRpbWUgbG9vcC5cbiAqL1xuYXN5bmMgZnVuY3Rpb24gbWFpbigpOiBQcm9taXNlPHZvaWQ+IHtcbiAgLy8gTG9hZCBoYW5kbGVyIG9uY2UgYXQgc3RhcnR1cFxuICAvLyBOb3RlOiBidW4gLS13YXRjaCBhdXRvbWF0aWNhbGx5IHRyYWNrcyBkeW5hbWljIGltcG9ydHMgYW5kIHJlc3RhcnRzIHdoZW4gdGhleSBjaGFuZ2VcbiAgbGV0IGhhbmRsZXI6IChldmVudDogdW5rbm93biwgY29udGV4dDogQ29udGV4dCkgPT4gUHJvbWlzZTx1bmtub3duPlxuICB0cnkge1xuICAgIGhhbmRsZXIgPSBhd2FpdCBsb2FkSGFuZGxlcihIQU5ETEVSISwgUFJPSkVDVF9ST09UKVxuICB9IGNhdGNoIChlcnJvcikge1xuICAgIGNvbnNvbGUuZXJyb3IoYEZhaWxlZCB0byBsb2FkIGhhbmRsZXI6ICR7ZXJyb3J9YClcbiAgICBhd2FpdCBwb3N0SW5pdEVycm9yKFxuICAgICAgZXJyb3IgaW5zdGFuY2VvZiBFcnJvciA/IGVycm9yIDogbmV3IEVycm9yKFN0cmluZyhlcnJvcikpLFxuICAgIClcbiAgICBwcm9jZXNzLmV4aXQoMSlcbiAgfVxuXG4gIC8vIE1haW4gaW52b2NhdGlvbiBsb29wXG4gIHdoaWxlICh0cnVlKSB7XG4gICAgdHJ5IHtcbiAgICAgIC8vIEdldCBuZXh0IGludm9jYXRpb24gKGJsb2NrcyB1bnRpbCBhdmFpbGFibGUpXG4gICAgICBjb25zdCBpbnZvY2F0aW9uID0gYXdhaXQgZ2V0TmV4dEludm9jYXRpb24oKVxuXG4gICAgICAvLyBudWxsIG1lYW5zIDUwMyB0aW1lb3V0IC0gc2VydmVyIHdhbnRzIHVzIHRvIHJlY29ubmVjdCBhbmQga2VlcCBwb2xsaW5nXG4gICAgICBpZiAoaW52b2NhdGlvbiA9PT0gbnVsbCkge1xuICAgICAgICAvLyBTbWFsbCBkZWxheSBiZWZvcmUgcmVjb25uZWN0aW5nIHRvIGF2b2lkIHRpZ2h0IGxvb3BcbiAgICAgICAgYXdhaXQgbmV3IFByb21pc2UoKHJlc29sdmUpID0+IHNldFRpbWVvdXQocmVzb2x2ZSwgMTAwKSlcbiAgICAgICAgY29udGludWVcbiAgICAgIH1cblxuICAgICAgY29uc3QgeyBldmVudCwgY29udGV4dCwgcmVxdWVzdElkIH0gPSBpbnZvY2F0aW9uXG4gICAgICBjdXJyZW50UmVxdWVzdElkID0gcmVxdWVzdElkXG5cbiAgICAgIHRyeSB7XG4gICAgICAgIC8vIEludm9rZSBoYW5kbGVyXG4gICAgICAgIGNvbnN0IHJlc3VsdCA9IGF3YWl0IGhhbmRsZXIoZXZlbnQsIGNvbnRleHQpXG5cbiAgICAgICAgLy8gUG9zdCByZXNwb25zZVxuICAgICAgICBhd2FpdCBwb3N0UmVzcG9uc2UocmVxdWVzdElkLCByZXN1bHQpXG4gICAgICB9IGNhdGNoIChlcnJvcikge1xuICAgICAgICBsYW1iZGFFcnJvcihcIkVSUk9SXCIsIGBIYW5kbGVyIGVycm9yOiAke2Vycm9yfWApXG4gICAgICAgIGF3YWl0IHBvc3RFcnJvcihcbiAgICAgICAgICByZXF1ZXN0SWQsXG4gICAgICAgICAgZXJyb3IgaW5zdGFuY2VvZiBFcnJvciA/IGVycm9yIDogbmV3IEVycm9yKFN0cmluZyhlcnJvcikpLFxuICAgICAgICApXG4gICAgICB9IGZpbmFsbHkge1xuICAgICAgICBjdXJyZW50UmVxdWVzdElkID0gbnVsbFxuICAgICAgfVxuICAgIH0gY2F0Y2ggKGVycm9yKSB7XG4gICAgICAvLyBFcnJvciBnZXR0aW5nIG5leHQgaW52b2NhdGlvbiAtIHRoaXMgaXMgZmF0YWwsIGV4aXQgYW5kIGxldCBkYWVtb24gcmVzdGFydFxuICAgICAgY29uc29sZS5lcnJvcihgRmF0YWwgZXJyb3IgaW4gaW52b2NhdGlvbiBsb29wOiAke2Vycm9yfWApXG4gICAgICBwcm9jZXNzLmV4aXQoMSlcbiAgICB9XG4gIH1cbn1cblxuLy8gUnVuIHRoZSBtYWluIGxvb3Bcbm1haW4oKS5jYXRjaCgoZXJyb3IpID0+IHtcbiAgY29uc29sZS5lcnJvcihgVW5oYW5kbGVkIGVycm9yOiAke2Vycm9yfWApXG4gIHByb2Nlc3MuZXhpdCgxKVxufSlcbiJdfQ==
@@ -0,0 +1,32 @@
1
+ /**
2
+ * File watching for hot-reload support.
3
+ *
4
+ * Watches handler files and triggers rebuilds when they change.
5
+ */
6
+ import chokidar from "chokidar";
7
+ import { Stream } from "effect";
8
+ /**
9
+ * File change event.
10
+ */
11
+ export interface FileChangeEvent {
12
+ type: "add" | "change" | "unlink";
13
+ path: string;
14
+ }
15
+ /**
16
+ * Create a file watcher stream that emits events when files change.
17
+ *
18
+ * @param patterns - Glob patterns to watch (e.g., ["src/**\/*.ts"])
19
+ * @param options - Chokidar options
20
+ */
21
+ export declare const watchFiles: (patterns: string | string[], options?: Parameters<typeof chokidar.watch>[1]) => Stream.Stream<FileChangeEvent, Error>;
22
+ /**
23
+ * Watch for changes to handler files and debounce notifications.
24
+ *
25
+ * @param patterns - Glob patterns to watch
26
+ * @param debounceMs - Debounce delay in milliseconds (default: 500)
27
+ */
28
+ export declare const watchHandlerFiles: (patterns: string | string[], debounceMs?: number) => Stream.Stream<FileChangeEvent[], Error>;
29
+ /**
30
+ * Watch a directory and accumulate changes.
31
+ */
32
+ export declare const watchDirectory: (directory: string, extensions?: string[]) => Stream.Stream<FileChangeEvent[], Error>;
@@ -0,0 +1,57 @@
1
+ /**
2
+ * File watching for hot-reload support.
3
+ *
4
+ * Watches handler files and triggers rebuilds when they change.
5
+ */
6
+ import chokidar from "chokidar";
7
+ import { Effect, Stream } from "effect";
8
+ /**
9
+ * Create a file watcher stream that emits events when files change.
10
+ *
11
+ * @param patterns - Glob patterns to watch (e.g., ["src/**\/*.ts"])
12
+ * @param options - Chokidar options
13
+ */
14
+ export const watchFiles = (patterns, options) => Stream.asyncScoped((emit) => Effect.gen(function* () {
15
+ const watcher = chokidar.watch(patterns, {
16
+ ignoreInitial: true,
17
+ ...options,
18
+ });
19
+ watcher.on("add", (path) => {
20
+ emit.single({ type: "add", path });
21
+ });
22
+ watcher.on("change", (path) => {
23
+ emit.single({ type: "change", path });
24
+ });
25
+ watcher.on("unlink", (path) => {
26
+ emit.single({ type: "unlink", path });
27
+ });
28
+ watcher.on("error", (err) => {
29
+ const error = err;
30
+ emit.fail(new Error(`Watcher error: ${error.message}`));
31
+ });
32
+ // Cleanup when scope closes
33
+ yield* Effect.addFinalizer(() => Effect.gen(function* () {
34
+ yield* Effect.promise(async () => {
35
+ await watcher.close();
36
+ });
37
+ yield* Effect.logInfo("File watcher closed");
38
+ }));
39
+ yield* Effect.logInfo(`Watching: ${Array.isArray(patterns) ? patterns.join(", ") : patterns}`);
40
+ }));
41
+ /**
42
+ * Watch for changes to handler files and debounce notifications.
43
+ *
44
+ * @param patterns - Glob patterns to watch
45
+ * @param debounceMs - Debounce delay in milliseconds (default: 500)
46
+ */
47
+ export const watchHandlerFiles = (patterns, debounceMs = 500) => watchFiles(patterns).pipe(
48
+ // Collect events within the debounce window
49
+ Stream.debounce(`${debounceMs} millis`), Stream.map((event) => [event]));
50
+ /**
51
+ * Watch a directory and accumulate changes.
52
+ */
53
+ export const watchDirectory = (directory, extensions = ["ts", "js", "mjs", "cjs", "json"]) => {
54
+ const pattern = `${directory}/**/*.{${extensions.join(",")}}`;
55
+ return watchHandlerFiles(pattern);
56
+ };
57
+ //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiZmlsZS13YXRjaGVyLmpzIiwic291cmNlUm9vdCI6IiIsInNvdXJjZXMiOlsiLi4vLi4vLi4vc3JjL2NsaS93YXRjaGVyL2ZpbGUtd2F0Y2hlci50cyJdLCJuYW1lcyI6W10sIm1hcHBpbmdzIjoiQUFBQTs7OztHQUlHO0FBRUgsT0FBTyxRQUFRLE1BQU0sVUFBVSxDQUFBO0FBQy9CLE9BQU8sRUFBRSxNQUFNLEVBQUUsTUFBTSxFQUFFLE1BQU0sUUFBUSxDQUFBO0FBVXZDOzs7OztHQUtHO0FBQ0gsTUFBTSxDQUFDLE1BQU0sVUFBVSxHQUFHLENBQ3hCLFFBQTJCLEVBQzNCLE9BQThDLEVBQ1AsRUFBRSxDQUN6QyxNQUFNLENBQUMsV0FBVyxDQUF5QixDQUFDLElBQUksRUFBRSxFQUFFLENBQ2xELE1BQU0sQ0FBQyxHQUFHLENBQUMsUUFBUSxDQUFDO0lBQ2xCLE1BQU0sT0FBTyxHQUFHLFFBQVEsQ0FBQyxLQUFLLENBQUMsUUFBUSxFQUFFO1FBQ3ZDLGFBQWEsRUFBRSxJQUFJO1FBQ25CLEdBQUcsT0FBTztLQUNYLENBQUMsQ0FBQTtJQUVGLE9BQU8sQ0FBQyxFQUFFLENBQUMsS0FBSyxFQUFFLENBQUMsSUFBSSxFQUFFLEVBQUU7UUFDekIsSUFBSSxDQUFDLE1BQU0sQ0FBQyxFQUFFLElBQUksRUFBRSxLQUFLLEVBQUUsSUFBSSxFQUFFLENBQUMsQ0FBQTtJQUNwQyxDQUFDLENBQUMsQ0FBQTtJQUVGLE9BQU8sQ0FBQyxFQUFFLENBQUMsUUFBUSxFQUFFLENBQUMsSUFBSSxFQUFFLEVBQUU7UUFDNUIsSUFBSSxDQUFDLE1BQU0sQ0FBQyxFQUFFLElBQUksRUFBRSxRQUFRLEVBQUUsSUFBSSxFQUFFLENBQUMsQ0FBQTtJQUN2QyxDQUFDLENBQUMsQ0FBQTtJQUVGLE9BQU8sQ0FBQyxFQUFFLENBQUMsUUFBUSxFQUFFLENBQUMsSUFBSSxFQUFFLEVBQUU7UUFDNUIsSUFBSSxDQUFDLE1BQU0sQ0FBQyxFQUFFLElBQUksRUFBRSxRQUFRLEVBQUUsSUFBSSxFQUFFLENBQUMsQ0FBQTtJQUN2QyxDQUFDLENBQUMsQ0FBQTtJQUVGLE9BQU8sQ0FBQyxFQUFFLENBQUMsT0FBTyxFQUFFLENBQUMsR0FBRyxFQUFFLEVBQUU7UUFDMUIsTUFBTSxLQUFLLEdBQUcsR0FBWSxDQUFBO1FBQzFCLElBQUksQ0FBQyxJQUFJLENBQUMsSUFBSSxLQUFLLENBQUMsa0JBQWtCLEtBQUssQ0FBQyxPQUFPLEVBQUUsQ0FBQyxDQUFDLENBQUE7SUFDekQsQ0FBQyxDQUFDLENBQUE7SUFFRiw0QkFBNEI7SUFDNUIsS0FBSyxDQUFDLENBQUMsTUFBTSxDQUFDLFlBQVksQ0FBQyxHQUFHLEVBQUUsQ0FDOUIsTUFBTSxDQUFDLEdBQUcsQ0FBQyxRQUFRLENBQUM7UUFDbEIsS0FBSyxDQUFDLENBQUMsTUFBTSxDQUFDLE9BQU8sQ0FBQyxLQUFLLElBQUksRUFBRTtZQUMvQixNQUFNLE9BQU8sQ0FBQyxLQUFLLEVBQUUsQ0FBQTtRQUN2QixDQUFDLENBQUMsQ0FBQTtRQUNGLEtBQUssQ0FBQyxDQUFDLE1BQU0sQ0FBQyxPQUFPLENBQUMscUJBQXFCLENBQUMsQ0FBQTtJQUM5QyxDQUFDLENBQUMsQ0FDSCxDQUFBO0lBRUQsS0FBSyxDQUFDLENBQUMsTUFBTSxDQUFDLE9BQU8sQ0FDbkIsYUFBYSxLQUFLLENBQUMsT0FBTyxDQUFDLFFBQVEsQ0FBQyxDQUFDLENBQUMsQ0FBQyxRQUFRLENBQUMsSUFBSSxDQUFDLElBQUksQ0FBQyxDQUFDLENBQUMsQ0FBQyxRQUFRLEVBQUUsQ0FDeEUsQ0FBQTtBQUNILENBQUMsQ0FBQyxDQUNILENBQUE7QUFFSDs7Ozs7R0FLRztBQUNILE1BQU0sQ0FBQyxNQUFNLGlCQUFpQixHQUFHLENBQy9CLFFBQTJCLEVBQzNCLFVBQVUsR0FBRyxHQUFHLEVBQ3lCLEVBQUUsQ0FDM0MsVUFBVSxDQUFDLFFBQVEsQ0FBQyxDQUFDLElBQUk7QUFDdkIsNENBQTRDO0FBQzVDLE1BQU0sQ0FBQyxRQUFRLENBQUMsR0FBRyxVQUFVLFNBQVMsQ0FBQyxFQUN2QyxNQUFNLENBQUMsR0FBRyxDQUFDLENBQUMsS0FBSyxFQUFFLEVBQUUsQ0FBQyxDQUFDLEtBQUssQ0FBQyxDQUFDLENBQy9CLENBQUE7QUFFSDs7R0FFRztBQUNILE1BQU0sQ0FBQyxNQUFNLGNBQWMsR0FBRyxDQUM1QixTQUFpQixFQUNqQixVQUFVLEdBQUcsQ0FBQyxJQUFJLEVBQUUsSUFBSSxFQUFFLEtBQUssRUFBRSxLQUFLLEVBQUUsTUFBTSxDQUFDLEVBQ04sRUFBRTtJQUMzQyxNQUFNLE9BQU8sR0FBRyxHQUFHLFNBQVMsVUFBVSxVQUFVLENBQUMsSUFBSSxDQUFDLEdBQUcsQ0FBQyxHQUFHLENBQUE7SUFDN0QsT0FBTyxpQkFBaUIsQ0FBQyxPQUFPLENBQUMsQ0FBQTtBQUNuQyxDQUFDLENBQUEiLCJzb3VyY2VzQ29udGVudCI6WyIvKipcbiAqIEZpbGUgd2F0Y2hpbmcgZm9yIGhvdC1yZWxvYWQgc3VwcG9ydC5cbiAqXG4gKiBXYXRjaGVzIGhhbmRsZXIgZmlsZXMgYW5kIHRyaWdnZXJzIHJlYnVpbGRzIHdoZW4gdGhleSBjaGFuZ2UuXG4gKi9cblxuaW1wb3J0IGNob2tpZGFyIGZyb20gXCJjaG9raWRhclwiXG5pbXBvcnQgeyBFZmZlY3QsIFN0cmVhbSB9IGZyb20gXCJlZmZlY3RcIlxuXG4vKipcbiAqIEZpbGUgY2hhbmdlIGV2ZW50LlxuICovXG5leHBvcnQgaW50ZXJmYWNlIEZpbGVDaGFuZ2VFdmVudCB7XG4gIHR5cGU6IFwiYWRkXCIgfCBcImNoYW5nZVwiIHwgXCJ1bmxpbmtcIlxuICBwYXRoOiBzdHJpbmdcbn1cblxuLyoqXG4gKiBDcmVhdGUgYSBmaWxlIHdhdGNoZXIgc3RyZWFtIHRoYXQgZW1pdHMgZXZlbnRzIHdoZW4gZmlsZXMgY2hhbmdlLlxuICpcbiAqIEBwYXJhbSBwYXR0ZXJucyAtIEdsb2IgcGF0dGVybnMgdG8gd2F0Y2ggKGUuZy4sIFtcInNyYy8qKlxcLyoudHNcIl0pXG4gKiBAcGFyYW0gb3B0aW9ucyAtIENob2tpZGFyIG9wdGlvbnNcbiAqL1xuZXhwb3J0IGNvbnN0IHdhdGNoRmlsZXMgPSAoXG4gIHBhdHRlcm5zOiBzdHJpbmcgfCBzdHJpbmdbXSxcbiAgb3B0aW9ucz86IFBhcmFtZXRlcnM8dHlwZW9mIGNob2tpZGFyLndhdGNoPlsxXSxcbik6IFN0cmVhbS5TdHJlYW08RmlsZUNoYW5nZUV2ZW50LCBFcnJvcj4gPT5cbiAgU3RyZWFtLmFzeW5jU2NvcGVkPEZpbGVDaGFuZ2VFdmVudCwgRXJyb3I+KChlbWl0KSA9PlxuICAgIEVmZmVjdC5nZW4oZnVuY3Rpb24qICgpIHtcbiAgICAgIGNvbnN0IHdhdGNoZXIgPSBjaG9raWRhci53YXRjaChwYXR0ZXJucywge1xuICAgICAgICBpZ25vcmVJbml0aWFsOiB0cnVlLFxuICAgICAgICAuLi5vcHRpb25zLFxuICAgICAgfSlcblxuICAgICAgd2F0Y2hlci5vbihcImFkZFwiLCAocGF0aCkgPT4ge1xuICAgICAgICBlbWl0LnNpbmdsZSh7IHR5cGU6IFwiYWRkXCIsIHBhdGggfSlcbiAgICAgIH0pXG5cbiAgICAgIHdhdGNoZXIub24oXCJjaGFuZ2VcIiwgKHBhdGgpID0+IHtcbiAgICAgICAgZW1pdC5zaW5nbGUoeyB0eXBlOiBcImNoYW5nZVwiLCBwYXRoIH0pXG4gICAgICB9KVxuXG4gICAgICB3YXRjaGVyLm9uKFwidW5saW5rXCIsIChwYXRoKSA9PiB7XG4gICAgICAgIGVtaXQuc2luZ2xlKHsgdHlwZTogXCJ1bmxpbmtcIiwgcGF0aCB9KVxuICAgICAgfSlcblxuICAgICAgd2F0Y2hlci5vbihcImVycm9yXCIsIChlcnIpID0+IHtcbiAgICAgICAgY29uc3QgZXJyb3IgPSBlcnIgYXMgRXJyb3JcbiAgICAgICAgZW1pdC5mYWlsKG5ldyBFcnJvcihgV2F0Y2hlciBlcnJvcjogJHtlcnJvci5tZXNzYWdlfWApKVxuICAgICAgfSlcblxuICAgICAgLy8gQ2xlYW51cCB3aGVuIHNjb3BlIGNsb3Nlc1xuICAgICAgeWllbGQqIEVmZmVjdC5hZGRGaW5hbGl6ZXIoKCkgPT5cbiAgICAgICAgRWZmZWN0LmdlbihmdW5jdGlvbiogKCkge1xuICAgICAgICAgIHlpZWxkKiBFZmZlY3QucHJvbWlzZShhc3luYyAoKSA9PiB7XG4gICAgICAgICAgICBhd2FpdCB3YXRjaGVyLmNsb3NlKClcbiAgICAgICAgICB9KVxuICAgICAgICAgIHlpZWxkKiBFZmZlY3QubG9nSW5mbyhcIkZpbGUgd2F0Y2hlciBjbG9zZWRcIilcbiAgICAgICAgfSksXG4gICAgICApXG5cbiAgICAgIHlpZWxkKiBFZmZlY3QubG9nSW5mbyhcbiAgICAgICAgYFdhdGNoaW5nOiAke0FycmF5LmlzQXJyYXkocGF0dGVybnMpID8gcGF0dGVybnMuam9pbihcIiwgXCIpIDogcGF0dGVybnN9YCxcbiAgICAgIClcbiAgICB9KSxcbiAgKVxuXG4vKipcbiAqIFdhdGNoIGZvciBjaGFuZ2VzIHRvIGhhbmRsZXIgZmlsZXMgYW5kIGRlYm91bmNlIG5vdGlmaWNhdGlvbnMuXG4gKlxuICogQHBhcmFtIHBhdHRlcm5zIC0gR2xvYiBwYXR0ZXJucyB0byB3YXRjaFxuICogQHBhcmFtIGRlYm91bmNlTXMgLSBEZWJvdW5jZSBkZWxheSBpbiBtaWxsaXNlY29uZHMgKGRlZmF1bHQ6IDUwMClcbiAqL1xuZXhwb3J0IGNvbnN0IHdhdGNoSGFuZGxlckZpbGVzID0gKFxuICBwYXR0ZXJuczogc3RyaW5nIHwgc3RyaW5nW10sXG4gIGRlYm91bmNlTXMgPSA1MDAsXG4pOiBTdHJlYW0uU3RyZWFtPEZpbGVDaGFuZ2VFdmVudFtdLCBFcnJvcj4gPT5cbiAgd2F0Y2hGaWxlcyhwYXR0ZXJucykucGlwZShcbiAgICAvLyBDb2xsZWN0IGV2ZW50cyB3aXRoaW4gdGhlIGRlYm91bmNlIHdpbmRvd1xuICAgIFN0cmVhbS5kZWJvdW5jZShgJHtkZWJvdW5jZU1zfSBtaWxsaXNgKSxcbiAgICBTdHJlYW0ubWFwKChldmVudCkgPT4gW2V2ZW50XSksXG4gIClcblxuLyoqXG4gKiBXYXRjaCBhIGRpcmVjdG9yeSBhbmQgYWNjdW11bGF0ZSBjaGFuZ2VzLlxuICovXG5leHBvcnQgY29uc3Qgd2F0Y2hEaXJlY3RvcnkgPSAoXG4gIGRpcmVjdG9yeTogc3RyaW5nLFxuICBleHRlbnNpb25zID0gW1widHNcIiwgXCJqc1wiLCBcIm1qc1wiLCBcImNqc1wiLCBcImpzb25cIl0sXG4pOiBTdHJlYW0uU3RyZWFtPEZpbGVDaGFuZ2VFdmVudFtdLCBFcnJvcj4gPT4ge1xuICBjb25zdCBwYXR0ZXJuID0gYCR7ZGlyZWN0b3J5fS8qKi8qLnske2V4dGVuc2lvbnMuam9pbihcIixcIil9fWBcbiAgcmV0dXJuIHdhdGNoSGFuZGxlckZpbGVzKHBhdHRlcm4pXG59XG4iXX0=
@@ -0,0 +1,73 @@
1
+ /**
2
+ * AppSync Events client with AWS SigV4 signing.
3
+ *
4
+ * This client handles:
5
+ * - HTTP requests to publish events (with IAM signing)
6
+ * - WebSocket connections for real-time subscriptions (with IAM signing)
7
+ */
8
+ export interface AppSyncEventsClientConfig {
9
+ httpEndpoint: string;
10
+ realtimeEndpoint: string;
11
+ region?: string;
12
+ }
13
+ export interface PublishAndWaitOptions<T> {
14
+ publishChannel: string;
15
+ subscribeChannel: string;
16
+ message: unknown;
17
+ timeoutMs: number;
18
+ matchResponse: (message: T) => boolean;
19
+ }
20
+ export interface SubscriptionOptions<T> {
21
+ channel: string;
22
+ onMessage: (message: T) => void | Promise<void>;
23
+ onError?: (error: Error) => void;
24
+ }
25
+ /**
26
+ * Client for AppSync Events API
27
+ */
28
+ export declare class AppSyncEventsClient {
29
+ private readonly httpEndpoint;
30
+ private readonly realtimeEndpoint;
31
+ private readonly region;
32
+ private ws;
33
+ constructor(config: AppSyncEventsClientConfig);
34
+ /**
35
+ * Publish a message to a channel and wait for a response on another channel.
36
+ */
37
+ publishAndWaitForResponse<T>(options: PublishAndWaitOptions<T>): Promise<T>;
38
+ /**
39
+ * Publish a message to a channel via HTTP
40
+ */
41
+ publish(channel: string, message: unknown): Promise<void>;
42
+ /**
43
+ * Subscribe to a channel and receive messages continuously.
44
+ * Returns an unsubscribe function.
45
+ */
46
+ subscribe<T>(options: SubscriptionOptions<T>): Promise<() => void>;
47
+ /**
48
+ * Check if WebSocket is connected
49
+ */
50
+ isConnected(): boolean;
51
+ /**
52
+ * Connect to WebSocket and subscribe to a channel
53
+ */
54
+ private connectAndSubscribe;
55
+ /**
56
+ * Connect to WebSocket and subscribe to a channel with continuous message handling.
57
+ * Unlike connectAndSubscribe, this doesn't close after receiving a message.
58
+ */
59
+ private connectAndSubscribeContinuous;
60
+ /**
61
+ * Create authorization headers for subscribe operation
62
+ */
63
+ private createSubscribeAuthorization;
64
+ /**
65
+ * Build signed WebSocket connection info for AppSync Events
66
+ * Returns [url, subprotocols] for WebSocket constructor
67
+ */
68
+ private buildSignedWebSocketConnection;
69
+ /**
70
+ * Close the WebSocket connection
71
+ */
72
+ close(): Promise<void>;
73
+ }