@dbx-tools/appkit-lakebox 0.0.0-stage → 0.9.132

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 (55) hide show
  1. package/LICENSE +202 -0
  2. package/README.md +197 -2
  3. package/index.ts +32 -0
  4. package/lib/index.d.ts +28 -0
  5. package/lib/index.js +26 -0
  6. package/lib/src/_package.d.ts +2 -0
  7. package/lib/src/_package.js +11 -0
  8. package/lib/src/_resource-registry.d.ts +41 -0
  9. package/lib/src/_resource-registry.js +128 -0
  10. package/lib/src/_service-snapshots.d.ts +55 -0
  11. package/lib/src/_service-snapshots.js +65 -0
  12. package/lib/src/config.d.ts +40 -0
  13. package/lib/src/config.js +86 -0
  14. package/lib/src/desktop.d.ts +6 -0
  15. package/lib/src/desktop.js +88 -0
  16. package/lib/src/metrics.d.ts +59 -0
  17. package/lib/src/metrics.js +151 -0
  18. package/lib/src/plugin.d.ts +259 -0
  19. package/lib/src/plugin.js +329 -0
  20. package/lib/src/routes.d.ts +75 -0
  21. package/lib/src/routes.js +441 -0
  22. package/lib/src/runtime.d.ts +154 -0
  23. package/lib/src/runtime.js +760 -0
  24. package/lib/src/services.d.ts +101 -0
  25. package/lib/src/services.js +188 -0
  26. package/lib/src/skills.d.ts +5 -0
  27. package/lib/src/skills.js +21 -0
  28. package/lib/src/user.d.ts +46 -0
  29. package/lib/src/user.js +76 -0
  30. package/lib/src/workspace.d.ts +43 -0
  31. package/lib/src/workspace.js +138 -0
  32. package/lib/tsconfig.tsbuildinfo +1 -0
  33. package/package.json +167 -4
  34. package/scripts/desktop-prerequisites.sh +66 -0
  35. package/scripts/desktop-session.sh +307 -0
  36. package/scripts/lakebox-admin.sh +234 -0
  37. package/scripts/lakebox-service.py +449 -0
  38. package/skills/html-page/SKILL.md +40 -0
  39. package/skills/jupyter-lab/SKILL.md +32 -0
  40. package/skills/lakebox-showcase/SKILL.md +66 -0
  41. package/skills/network-speed-test/SKILL.md +29 -0
  42. package/skills/realtime-whiteboard/SKILL.md +28 -0
  43. package/src/_package.ts +11 -0
  44. package/src/_resource-registry.ts +160 -0
  45. package/src/_service-snapshots.ts +84 -0
  46. package/src/config.ts +128 -0
  47. package/src/desktop.ts +97 -0
  48. package/src/metrics.ts +178 -0
  49. package/src/plugin.ts +446 -0
  50. package/src/routes.ts +524 -0
  51. package/src/runtime.ts +952 -0
  52. package/src/services.ts +239 -0
  53. package/src/skills.ts +23 -0
  54. package/src/user.ts +88 -0
  55. package/src/workspace.ts +185 -0
@@ -0,0 +1,329 @@
1
+ import { createWorkspaceClient, lakebase, toPlugin, } from "@databricks/appkit";
2
+ import { executeFromRegistry, } from "@databricks/appkit/beta";
3
+ import { USER_EMAIL_HEADER, USER_ID_HEADER, appkit, lakebaseResolver, pluginRegistry, toolkitEntries, toolOperations, ToolRegistryPlugin, } from "@dbx-tools/appkit";
4
+ import { createLakeboxServiceOperationSchema, executeLakeboxCommandOperationSchema, getLakeboxOperationSchema, getLakeboxServiceLogsOperationSchema, LAKEBOX_PLUGIN_NAME, LAKEBOX_ROUTES, lakeboxCommandRequestSchema, lakeboxServiceActionSchema, lakeboxServiceLogsRequestSchema, listLakeboxServicesOperationSchema, removeLakeboxServiceOperationSchema, startLakeboxServiceOperationSchema, stopLakeboxServiceOperationSchema, } from "@dbx-tools/shared-lakebox";
5
+ import { z } from "zod";
6
+ import { LakeboxResourceRegistry } from "./_resource-registry.js";
7
+ import { LakeboxServiceSnapshots } from "./_service-snapshots.js";
8
+ import { LakeboxPluginConfigSchema, resolveLakeboxPluginConfig, } from "./config.js";
9
+ import { LakeboxRuntime } from "./runtime.js";
10
+ import { withServiceOrigin } from "./services.js";
11
+ const CONFIG_SCHEMA = z.toJSONSchema(LakeboxPluginConfigSchema);
12
+ /**
13
+ * AppKit plugin for the single app Lakebox.
14
+ *
15
+ * Registers user-scoped REST routes, agent tools, the deterministic service
16
+ * proxy, and WebSocket upgrade handling. Every Lakebox lifecycle call uses the
17
+ * app service principal; the authenticated request identity only selects the
18
+ * user's Lakebox account and authorizes that user's routes.
19
+ */
20
+ export class AppKitLakeboxPlugin extends ToolRegistryPlugin {
21
+ static manifest = {
22
+ name: LAKEBOX_PLUGIN_NAME,
23
+ displayName: "Lakebox",
24
+ description: "Runs per-user services, terminals, desktops, and commands in one lazily created Databricks Lakebox.",
25
+ stability: "beta",
26
+ resources: { required: [], optional: [] },
27
+ config: { schema: CONFIG_SCHEMA },
28
+ };
29
+ runtime;
30
+ mounted = false;
31
+ httpServer;
32
+ upgradeAttachTimer;
33
+ upgradeIdentityGate;
34
+ upgradeHandler = (request, socket, head) => {
35
+ void this.handleUpgrade(request, socket, head);
36
+ };
37
+ /** Creates a plugin with a supplied shared runtime or one from resolved config. */
38
+ constructor(config = {}) {
39
+ super(config);
40
+ this.runtime = config.runtime ?? new LakeboxRuntime(resolveLakeboxPluginConfig(config));
41
+ }
42
+ get toolRegistry() {
43
+ return this.createToolRegistry();
44
+ }
45
+ async setup() {
46
+ this.context?.onLifecycle("setup:complete", async () => {
47
+ const pool = lakebaseResolver.captureServicePrincipalPool(pluginRegistry.require(this.context, lakebase, "appkit-lakebox").exports().pool);
48
+ this.runtime.initialize(createWorkspaceClient().config, (headers) => this.requestIdentity(headers), new LakeboxServiceSnapshots(), new LakeboxResourceRegistry(this.runtime.config.application, pool));
49
+ void this.runtime.cleanupLegacy(pool);
50
+ const server = this.context?.getPlugins().get("server");
51
+ if (!server || this.mounted)
52
+ return;
53
+ this.upgradeIdentityGate = resolveUpgradeIdentityGate(this.context?.getPlugins().get("authGate"));
54
+ server.addExtension((application) => application.use(this.runtime.routes.middleware()));
55
+ this.mounted = true;
56
+ this.attachUpgradeHandler(server);
57
+ });
58
+ }
59
+ injectRoutes(router) {
60
+ const json = (name, method, path, handler) => this.route(router, {
61
+ name,
62
+ method,
63
+ path,
64
+ handler: async (req, res) => {
65
+ res.json(await handler(this.resolveUserId(req), req));
66
+ },
67
+ });
68
+ json("status", "get", LAKEBOX_ROUTES.status, (identity) => this.runtime.summary(identity));
69
+ json("start", "post", LAKEBOX_ROUTES.start, async (identity) => {
70
+ await this.runtime.start();
71
+ return this.runtime.summary(identity);
72
+ });
73
+ json("stop", "post", LAKEBOX_ROUTES.stop, async (identity) => {
74
+ await this.runtime.stop();
75
+ return this.runtime.summary(identity);
76
+ });
77
+ json("executeCommand", "post", LAKEBOX_ROUTES.commands, (identity, req) => this.runtime.execute(identity, lakeboxCommandRequestSchema.parse(req.body ?? {})).result);
78
+ json("services", "get", LAKEBOX_ROUTES.services, async (identity) => ({
79
+ services: await this.runtime.listServices(identity),
80
+ }));
81
+ json("createService", "post", LAKEBOX_ROUTES.services, async (identity, req) => ({
82
+ service: await this.runtime.createService(identity, req.body ?? {}),
83
+ }));
84
+ json("startService", "post", LAKEBOX_ROUTES.serviceStart, async (identity, req) => ({
85
+ service: await this.runtime.startService(identity, serviceName(req)),
86
+ }));
87
+ json("stopService", "post", LAKEBOX_ROUTES.serviceStop, async (identity, req) => ({
88
+ service: await this.runtime.stopService(identity, serviceName(req)),
89
+ }));
90
+ json("removeService", "delete", LAKEBOX_ROUTES.service, async (identity, req) => ({
91
+ removed: await this.runtime.removeService(identity, serviceName(req)),
92
+ }));
93
+ json("serviceLogs", "get", LAKEBOX_ROUTES.serviceLogs, (identity, req) => this.runtime.serviceLogs(identity, lakeboxServiceLogsRequestSchema.parse({
94
+ name: req.params.name,
95
+ maxLines: req.query.maxLines,
96
+ maxChars: req.query.maxChars,
97
+ })));
98
+ json("metrics", "get", LAKEBOX_ROUTES.metrics, () => this.runtime.metricsResponse());
99
+ this.route(router, {
100
+ name: "metricsStream",
101
+ method: "get",
102
+ path: LAKEBOX_ROUTES.metricsStream,
103
+ handler: async (req, res) => this.streamMetrics(req, res),
104
+ });
105
+ }
106
+ /** Returns browser-safe route defaults published through AppKit client config. */
107
+ clientConfig() {
108
+ return { routePrefix: this.runtime.config.routePrefix };
109
+ }
110
+ /** Returns selected AppKit toolkit entries backed by this plugin's agent tools. */
111
+ toolkit(options) {
112
+ return toolkitEntries.entries(LAKEBOX_PLUGIN_NAME, this.getAgentTools(), {
113
+ ...options,
114
+ prefix: options?.prefix ?? "",
115
+ });
116
+ }
117
+ /** Executes one registered agent tool for the current execution identity. */
118
+ executeAgentTool(name, args, signal, context) {
119
+ return executeFromRegistry(this.createToolRegistry(context?.resourceId, context?.origin), name, args, signal);
120
+ }
121
+ /** Returns the typed sibling-plugin API. */
122
+ exports() {
123
+ return {
124
+ summary: (identity) => this.runtime.summary(identity),
125
+ start: () => this.runtime.start(),
126
+ stop: () => this.runtime.stop(),
127
+ delete: () => this.runtime.delete(),
128
+ executeCommand: (identity, input) => this.runtime.execute(identity, input).result,
129
+ listServices: (identity) => this.runtime.listServices(identity),
130
+ createService: (identity, input) => this.runtime.createService(identity, input),
131
+ startService: (identity, name) => this.runtime.startService(identity, name),
132
+ stopService: (identity, name) => this.runtime.stopService(identity, name),
133
+ removeService: (identity, name) => this.runtime.removeService(identity, name),
134
+ serviceLogs: (identity, input) => this.runtime.serviceLogs(identity, input),
135
+ operations: (identity) => this.operations(identity),
136
+ toolkit: (options) => this.toolkit(options),
137
+ };
138
+ }
139
+ /** Cancels in-flight work before shutdown tears down resources. */
140
+ abortActiveOperations() {
141
+ this.runtime.abortActiveOperations();
142
+ }
143
+ /** Detaches WebSocket handling and closes the shared runtime. */
144
+ async shutdown() {
145
+ if (this.upgradeAttachTimer)
146
+ clearTimeout(this.upgradeAttachTimer);
147
+ this.upgradeAttachTimer = undefined;
148
+ this.httpServer?.off("upgrade", this.upgradeHandler);
149
+ this.httpServer = undefined;
150
+ await this.runtime.close();
151
+ }
152
+ /** Returns the Zod-owned operation contracts used by agent tools. */
153
+ operations(identity, origin) {
154
+ const user = () => identity ?? this.executionIdentity();
155
+ const presentService = (value) => omitLog(withServiceOrigin(value, origin));
156
+ return toolOperations.defineOperations({
157
+ get_lakebox: {
158
+ schema: getLakeboxOperationSchema,
159
+ execute: async (_input, signal) => {
160
+ const summary = await this.runtime.summary(user(), signal);
161
+ return { ...summary, services: summary.services.map(presentService) };
162
+ },
163
+ },
164
+ execute_lakebox_command: {
165
+ schema: executeLakeboxCommandOperationSchema,
166
+ execute: (input, signal) => this.runtime.execute(user(), input, signal).result,
167
+ },
168
+ list_lakebox_services: {
169
+ schema: listLakeboxServicesOperationSchema,
170
+ execute: async (_input, signal) => ({
171
+ services: (await this.runtime.listServices(user(), signal)).map(presentService),
172
+ }),
173
+ },
174
+ create_lakebox_service: {
175
+ schema: createLakeboxServiceOperationSchema,
176
+ execute: async (input, signal) => ({
177
+ service: presentService(await this.runtime.createService(user(), input, signal)),
178
+ }),
179
+ },
180
+ start_lakebox_service: {
181
+ schema: startLakeboxServiceOperationSchema,
182
+ execute: async (input, signal) => ({
183
+ service: presentService(await this.runtime.startService(user(), input.name, signal)),
184
+ }),
185
+ },
186
+ stop_lakebox_service: {
187
+ schema: stopLakeboxServiceOperationSchema,
188
+ execute: async (input, signal) => ({
189
+ service: presentService(await this.runtime.stopService(user(), input.name, signal)),
190
+ }),
191
+ },
192
+ remove_lakebox_service: {
193
+ schema: removeLakeboxServiceOperationSchema,
194
+ execute: async (input, signal) => ({
195
+ removed: await this.runtime.removeService(user(), input.name, signal),
196
+ }),
197
+ },
198
+ get_lakebox_service_logs: {
199
+ schema: getLakeboxServiceLogsOperationSchema,
200
+ execute: (input, signal) => this.runtime.serviceLogs(user(), input, signal),
201
+ },
202
+ });
203
+ }
204
+ createToolRegistry(identity, origin) {
205
+ return toolOperations.toolRegistryFromOperations(this.operations(identity, origin));
206
+ }
207
+ executionIdentity() {
208
+ const context = appkit.tryGetExecutionContext();
209
+ if (!context)
210
+ throw new Error("Lakebox tools require an AppKit execution context");
211
+ return "userId" in context ? context.userId : context.serviceUserId;
212
+ }
213
+ /**
214
+ * Resolves route identity with the same rules as REST: the forwarded user,
215
+ * or AppKit's development user for direct loopback navigation outside
216
+ * production. Everything else is anonymous and fails closed.
217
+ */
218
+ requestIdentity(headers) {
219
+ const forwarded = headerValue(headers[USER_ID_HEADER])?.trim();
220
+ if (forwarded)
221
+ return forwarded;
222
+ if (process.env.NODE_ENV !== "development" || !isLoopbackHost(headerValue(headers.host))) {
223
+ return undefined;
224
+ }
225
+ try {
226
+ return this.resolveUserId({
227
+ header: (name) => headerValue(headers[name.toLowerCase()]),
228
+ });
229
+ }
230
+ catch {
231
+ return undefined;
232
+ }
233
+ }
234
+ async streamMetrics(req, res) {
235
+ const initial = await this.runtime.metricsResponse();
236
+ res.statusCode = 200;
237
+ res.setHeader("content-type", "text/event-stream; charset=utf-8");
238
+ res.setHeader("cache-control", "no-cache, no-transform");
239
+ res.setHeader("connection", "keep-alive");
240
+ res.setHeader("x-accel-buffering", "no");
241
+ res.flushHeaders?.();
242
+ res.write(`data: ${JSON.stringify(initial)}\n\n`);
243
+ const unsubscribe = this.runtime.subscribeMetrics((snapshot) => {
244
+ res.write(`data: ${JSON.stringify({ status: "running", series: [snapshot] })}\n\n`);
245
+ });
246
+ req.once("close", unsubscribe);
247
+ }
248
+ attachUpgradeHandler(server) {
249
+ if (this.httpServer || this.upgradeAttachTimer)
250
+ return;
251
+ try {
252
+ this.httpServer = server.getServer();
253
+ this.httpServer.on("upgrade", this.upgradeHandler);
254
+ }
255
+ catch {
256
+ this.upgradeAttachTimer = setTimeout(() => {
257
+ this.upgradeAttachTimer = undefined;
258
+ this.attachUpgradeHandler(server);
259
+ }, 10);
260
+ this.upgradeAttachTimer.unref?.();
261
+ }
262
+ }
263
+ async handleUpgrade(request, socket, head) {
264
+ const path = new URL(request.url ?? "/", "http://localhost").pathname;
265
+ if (!this.runtime.routes.canHandleUpgrade(path))
266
+ return;
267
+ const gate = this.upgradeIdentityGate;
268
+ if (gate) {
269
+ const headers = requestHeaders(request);
270
+ if (gate.isProtectedHost(headers)) {
271
+ delete request.headers[USER_ID_HEADER];
272
+ delete request.headers[USER_EMAIL_HEADER];
273
+ const userId = await gate.session(headers);
274
+ if (!userId) {
275
+ socket.destroy();
276
+ return;
277
+ }
278
+ request.headers[USER_ID_HEADER] = userId;
279
+ request.headers[USER_EMAIL_HEADER] = userId;
280
+ }
281
+ }
282
+ if (!(await this.runtime.routes.handleUpgrade(request, socket, head)))
283
+ socket.destroy();
284
+ }
285
+ }
286
+ function serviceName(req) {
287
+ return lakeboxServiceActionSchema.parse({ name: req.params.name }).name;
288
+ }
289
+ function omitLog({ log: _log, ...value }) {
290
+ return value;
291
+ }
292
+ function headerValue(value) {
293
+ return Array.isArray(value) ? value[0] : value;
294
+ }
295
+ function isLoopbackHost(host) {
296
+ if (!host)
297
+ return false;
298
+ try {
299
+ const hostname = new URL(`http://${host}`).hostname.toLowerCase();
300
+ return hostname === "localhost" || hostname === "127.0.0.1" || hostname === "[::1]";
301
+ }
302
+ catch {
303
+ return false;
304
+ }
305
+ }
306
+ function resolveUpgradeIdentityGate(plugin) {
307
+ const exports = plugin?.exports?.();
308
+ if (typeof exports?.isProtectedHost !== "function" ||
309
+ typeof exports?.session !== "function") {
310
+ return undefined;
311
+ }
312
+ return exports;
313
+ }
314
+ function requestHeaders(request) {
315
+ const headers = new Headers();
316
+ for (const [name, value] of Object.entries(request.headers)) {
317
+ if (Array.isArray(value)) {
318
+ for (const item of value)
319
+ headers.append(name, item);
320
+ }
321
+ else if (value !== undefined) {
322
+ headers.set(name, value);
323
+ }
324
+ }
325
+ return headers;
326
+ }
327
+ /** AppKit plugin factory for {@link AppKitLakeboxPlugin}. */
328
+ export const lakebox = toPlugin(AppKitLakeboxPlugin);
329
+ //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoicGx1Z2luLmpzIiwic291cmNlUm9vdCI6IiIsInNvdXJjZXMiOlsiLi4vLi4vc3JjL3BsdWdpbi50cyJdLCJuYW1lcyI6W10sIm1hcHBpbmdzIjoiQUFFQSxPQUFPLEVBQ0wscUJBQXFCLEVBQ3JCLFFBQVEsRUFDUixRQUFRLEdBR1QsTUFBTSxvQkFBb0IsQ0FBQztBQUM1QixPQUFPLEVBQ0wsbUJBQW1CLEdBSXBCLE1BQU0seUJBQXlCLENBQUM7QUFDakMsT0FBTyxFQUNMLGlCQUFpQixFQUNqQixjQUFjLEVBQ2QsTUFBTSxFQUNOLGdCQUFnQixFQUNoQixjQUFjLEVBQ2QsY0FBYyxFQUNkLGNBQWMsRUFDZCxrQkFBa0IsR0FFbkIsTUFBTSxtQkFBbUIsQ0FBQztBQUMzQixPQUFPLEVBQ0wsbUNBQW1DLEVBQ25DLG9DQUFvQyxFQUNwQyx5QkFBeUIsRUFDekIsb0NBQW9DLEVBQ3BDLG1CQUFtQixFQUNuQixjQUFjLEVBQ2QsMkJBQTJCLEVBQzNCLDBCQUEwQixFQUMxQiwrQkFBK0IsRUFDL0Isa0NBQWtDLEVBQ2xDLG1DQUFtQyxFQUNuQyxrQ0FBa0MsRUFDbEMsaUNBQWlDLEdBTWxDLE1BQU0sMkJBQTJCLENBQUM7QUFHbkMsT0FBTyxFQUFFLENBQUMsRUFBRSxNQUFNLEtBQUssQ0FBQztBQUN4QixPQUFPLEVBQUUsdUJBQXVCLEVBQUUsTUFBTSx5QkFBeUIsQ0FBQztBQUNsRSxPQUFPLEVBQUUsdUJBQXVCLEVBQUUsTUFBTSx5QkFBeUIsQ0FBQztBQUNsRSxPQUFPLEVBQ0wseUJBQXlCLEVBQ3pCLDBCQUEwQixHQUUzQixNQUFNLGFBQWEsQ0FBQztBQUNyQixPQUFPLEVBQUUsY0FBYyxFQUEwQixNQUFNLGNBQWMsQ0FBQztBQUN0RSxPQUFPLEVBQUUsaUJBQWlCLEVBQUUsTUFBTSxlQUFlLENBQUM7QUFZbEQsTUFBTSxhQUFhLEdBQUcsQ0FBQyxDQUFDLFlBQVksQ0FBQyx5QkFBeUIsQ0FBZ0IsQ0FBQztBQUUvRTs7Ozs7OztHQU9HO0FBQ0gsTUFBTSxPQUFP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Q0FBQztZQUN6QixLQUFLLE1BQU0sSUFBSSxJQUFJLEtBQUs7Z0JBQUUsT0FBTyxDQUFDLE1BQU0sQ0FBQyxJQUFJLEVBQUUsSUFBSSxDQUFDLENBQUM7UUFDdkQsQ0FBQzthQUFNLElBQUksS0FBSyxLQUFLLFNBQVMsRUFBRSxDQUFDO1lBQy9CLE9BQU8sQ0FBQyxHQUFHLENBQUMsSUFBSSxFQUFFLEtBQUssQ0FBQyxDQUFDO1FBQzNCLENBQUM7SUFDSCxDQUFDO0lBQ0QsT0FBTyxPQUFPLENBQUM7QUFDakIsQ0FBQztBQUVELDZEQUE2RDtBQUM3RCxNQUFNLENBQUMsTUFBTSxPQUFPLEdBQUcsUUFBUSxDQUFDLG1CQUFtQixDQUFDLENBQUMiLCJzb3VyY2VzQ29udGVudCI6WyJpbXBvcnQgdHlwZSB7IEluY29taW5nSHR0cEhlYWRlcnMsIEluY29taW5nTWVzc2FnZSwgU2VydmVyIGFzIEh0dHBTZXJ2ZXIgfSBmcm9tIFwibm9kZTpodHRwXCI7XG5pbXBvcnQgdHlwZSB7IFNvY2tldCB9IGZyb20gXCJub2RlOm5ldFwiO1xuaW1wb3J0IHtcbiAgY3JlYXRlV29ya3NwYWNlQ2xpZW50LFxuICBsYWtlYmFzZSxcbiAgdG9QbHVnaW4sXG4gIHR5cGUgSUFwcFJvdXRlcixcbiAgdHlwZSBQbHVnaW5NYW5pZmVzdCxcbn0gZnJvbSBcIkBkYXRhYnJpY2tzL2FwcGtpdFwiO1xuaW1wb3J0IHtcbiAgZXhlY3V0ZUZyb21SZWdpc3RyeSxcbiAgdHlwZSBUb29sa2l0RW50cnksXG4gIHR5cGUgVG9vbGtpdE9wdGlvbnMsXG4gIHR5cGUgVG9vbFJlZ2lzdHJ5LFxufSBmcm9tIFwiQGRhdGFicmlja3MvYXBwa2l0L2JldGFcIjtcbmltcG9ydCB7XG4gIFVTRVJfRU1BSUxfSEVBREVSLFxuICBVU0VSX0lEX0hFQURFUixcbiAgYXBwa2l0LFxuICBsYWtlYmFzZVJlc29sdmVyLFxuICBwbHVnaW5SZWdpc3RyeSxcbiAgdG9vbGtpdEVudHJpZXMsXG4gIHRvb2xPcGVyYXRpb25zLFxuICBUb29sUmVnaXN0cnlQbHVnaW4sXG4gIHR5cGUgQWdlbnRUb29sRXhlY3V0aW9uQ29udGV4dCxcbn0gZnJvbSBcIkBkYngtdG9vbHMvYXBwa2l0XCI7XG5pbXBvcnQge1xuICBjcmVhdGVMYWtlYm94U2VydmljZU9wZXJhdGlvblNjaGVtYSxcbiAgZXhlY3V0ZUxha2Vib3hDb21tYW5kT3BlcmF0aW9uU2NoZW1hLFxuICBnZXRMYWtlYm94T3BlcmF0aW9uU2NoZW1hLFxuICBnZXRMYWtlYm94U2VydmljZUxvZ3NPcGVyYXRpb25TY2hlbWEsXG4gIExBS0VCT1hfUExVR0lOX05BTUUsXG4gIExBS0VCT1hfUk9VVEVTLFxuICBsYWtlYm94Q29tbWFuZFJlcXVlc3RTY2hlbWEsXG4gIGxha2Vib3hTZXJ2aWNlQWN0aW9uU2NoZW1hLFxuICBsYWtlYm94U2VydmljZUxvZ3NSZXF1ZXN0U2NoZW1hLFxuICBsaXN0TGFrZWJveFNlcnZpY2VzT3BlcmF0aW9uU2NoZW1hLFxuICByZW1vdmVMYWtlYm94U2VydmljZU9wZXJhdGlvblNjaGVtYSxcbiAgc3RhcnRMYWtlYm94U2VydmljZU9wZXJhdGlvblNjaGVtYSxcbiAgc3RvcExha2Vib3hTZXJ2aWNlT3BlcmF0aW9uU2NoZW1hLFxuICB0eXBlIENyZWF0ZUxha2Vib3hTZXJ2aWNlUmVxdWVzdCxcbiAgdHlwZSBMYWtlYm94Q2xpZW50Q29uZmlnSW5wdXQsXG4gIHR5cGUgTGFrZWJveENvbW1hbmRSZXF1ZXN0LFxuICB0eXBlIExha2Vib3hTZXJ2aWNlLFxuICB0eXBlIExha2Vib3hTZXJ2aWNlTG9nc1JlcXVlc3QsXG59IGZyb20gXCJAZGJ4LXRvb2xzL3NoYXJlZC1sYWtlYm94XCI7XG5pbXBvcnQgdHlwZSB7IEFwcGxpY2F0aW9uLCBSZXF1ZXN0LCBSZXNwb25zZSB9IGZyb20gXCJleHByZXNzXCI7XG5pbXBvcnQgdHlwZSB7IEpTT05TY2hlbWE3IH0gZnJvbSBcImpzb24tc2NoZW1hXCI7XG5pbXBvcnQgeyB6IH0gZnJvbSBcInpvZFwiO1xuaW1wb3J0IHsgTGFrZWJveFJlc291cmNlUmVnaXN0cnkgfSBmcm9tIFwiLi9fcmVzb3VyY2UtcmVnaXN0cnkudHNcIjtcbmltcG9ydCB7IExha2Vib3hTZXJ2aWNlU25hcHNob3RzIH0gZnJvbSBcIi4vX3NlcnZpY2Utc25hcHNob3RzLnRzXCI7XG5pbXBvcnQge1xuICBMYWtlYm94UGx1Z2luQ29uZmlnU2NoZW1hLFxuICByZXNvbHZlTGFrZWJveFBsdWdpbkNvbmZpZyxcbiAgdHlwZSBMYWtlYm94UGx1Z2luQ29uZmlnLFxufSBmcm9tIFwiLi9jb25maWcudHNcIjtcbmltcG9ydCB7IExha2Vib3hSdW50aW1lLCB0eXBlIExha2Vib3hMZWdhY3lQb29sIH0gZnJvbSBcIi4vcnVudGltZS50c1wiO1xuaW1wb3J0IHsgd2l0aFNlcnZpY2VPcmlnaW4gfSBmcm9tIFwiLi9zZXJ2aWNlcy50c1wiO1xuXG5pbnRlcmZhY2UgU2VydmVyRXh0ZW5zaW9uIHtcbiAgYWRkRXh0ZW5zaW9uKGZuOiAoYXBwbGljYXRpb246IEFwcGxpY2F0aW9uKSA9PiB2b2lkKTogdm9pZDtcbiAgZ2V0U2VydmVyKCk6IEh0dHBTZXJ2ZXI7XG59XG5cbmludGVyZmFjZSBVcGdyYWRlSWRlbnRpdHlHYXRlIHtcbiAgaXNQcm90ZWN0ZWRIb3N0KGhlYWRlcnM6IEhlYWRlcnMpOiBib29sZWFuO1xuICBzZXNzaW9uKGhlYWRlcnM6IEhlYWRlcnMpOiBQcm9taXNlPHN0cmluZyB8IHVuZGVmaW5lZD47XG59XG5cbmNvbnN0IENPTkZJR19TQ0hFTUEgPSB6LnRvSlNPTlNjaGVtYShMYWtlYm94UGx1Z2luQ29uZmlnU2NoZW1hKSBhcyBKU09OU2NoZW1hNztcblxuLyoqXG4gKiBBcHBLaXQgcGx1Z2luIGZvciB0aGUgc2luZ2xlIGFwcCBMYWtlYm94LlxuICpcbiAqIFJlZ2lzdGVycyB1c2VyLXNjb3BlZCBSRVNUIHJvdXRlcywgYWdlbnQgdG9vbHMsIHRoZSBkZXRlcm1pbmlzdGljIHNlcnZpY2VcbiAqIHByb3h5LCBhbmQgV2ViU29ja2V0IHVwZ3JhZGUgaGFuZGxpbmcuIEV2ZXJ5IExha2Vib3ggbGlmZWN5Y2xlIGNhbGwgdXNlcyB0aGVcbiAqIGFwcCBzZXJ2aWNlIHByaW5jaXBhbDsgdGhlIGF1dGhlbnRpY2F0ZWQgcmVxdWVzdCBpZGVudGl0eSBvbmx5IHNlbGVjdHMgdGhlXG4gKiB1c2VyJ3MgTGFrZWJveCBhY2NvdW50IGFuZCBhdXRob3JpemVzIHRoYXQgdXNlcidzIHJvdXRlcy5cbiAqL1xuZXhwb3J0IGNsYXNzIEFwcEtpdExha2Vib3hQbHVnaW4gZXh0ZW5kcyBUb29sUmVnaXN0cnlQbHVnaW48TGFrZWJveFBsdWdpbkNvbmZpZz4ge1xuICBzdGF0aWMgbWFuaWZlc3QgPSB7XG4gICAgbmFtZTogTEFLRUJPWF9QTFVHSU5fTkFNRSxcbiAgICBkaXNwbGF5TmFtZTogXCJMYWtlYm94XCIsXG4gICAgZGVzY3JpcHRpb246XG4gICAgICBcIlJ1bnMgcGVyLXVzZXIgc2VydmljZXMsIHRlcm1pbmFscywgZGVza3RvcHMsIGFuZCBjb21tYW5kcyBpbiBvbmUgbGF6aWx5IGNyZWF0ZWQgRGF0YWJyaWNrcyBMYWtlYm94LlwiLFxuICAgIHN0YWJpbGl0eTogXCJiZXRhXCIsXG4gICAgcmVzb3VyY2VzOiB7IHJlcXVpcmVkOiBbXSwgb3B0aW9uYWw6IFtdIH0sXG4gICAgY29uZmlnOiB7IHNjaGVtYTogQ09ORklHX1NDSEVNQSB9LFxuICB9IHNhdGlzZmllcyBQbHVnaW5NYW5pZmVzdDx0eXBlb2YgTEFLRUJPWF9QTFVHSU5fTkFNRT47XG5cbiAgcmVhZG9ubHkgcnVudGltZTogTGFrZWJveFJ1bnRpbWU7XG4gIHByaXZhdGUgbW91bnRlZCA9IGZhbHNlO1xuICBwcml2YXRlIGh0dHBTZXJ2ZXI/OiBIdHRwU2VydmVyO1xuICBwcml2YXRlIHVwZ3JhZGVBdHRhY2hUaW1lcj86IFJldHVyblR5cGU8dHlwZW9mIHNldFRpbWVvdXQ+O1xuICBwcml2YXRlIHVwZ3JhZGVJZGVudGl0eUdhdGU/OiBVcGdyYWRlSWRlbnRpdHlHYXRlO1xuICBwcml2YXRlIHJlYWRvbmx5IHVwZ3JhZGVIYW5kbGVyID0gKHJlcXVlc3Q6IEluY29taW5nTWVzc2FnZSwgc29ja2V0OiBTb2NrZXQsIGhlYWQ6IEJ1ZmZlcikgPT4ge1xuICAgIHZvaWQgdGhpcy5oYW5kbGVVcGdyYWRlKHJlcXVlc3QsIHNvY2tldCwgaGVhZCk7XG4gIH07XG5cbiAgLyoqIENyZWF0ZXMgYSBwbHVnaW4gd2l0aCBhIHN1cHBsaWVkIHNoYXJlZCBydW50aW1lIG9yIG9uZSBmcm9tIHJlc29sdmVkIGNvbmZpZy4gKi9cbiAgY29uc3RydWN0b3IoY29uZmlnOiBMYWtlYm94UGx1Z2luQ29uZmlnID0ge30pIHtcbiAgICBzdXBlcihjb25maWcpO1xuICAgIHRoaXMucnVudGltZSA9IGNvbmZpZy5ydW50aW1lID8/IG5ldyBMYWtlYm94UnVudGltZShyZXNvbHZlTGFrZWJveFBsdWdpbkNvbmZpZyhjb25maWcpKTtcbiAgfVxuXG4gIHByb3RlY3RlZCBnZXQgdG9vbFJlZ2lzdHJ5KCk6IFRvb2xSZWdpc3RyeSB7XG4gICAgcmV0dXJuIHRoaXMuY3JlYXRlVG9vbFJlZ2lzdHJ5KCk7XG4gIH1cblxuICBvdmVycmlkZSBhc3luYyBzZXR1cCgpOiBQcm9taXNlPHZvaWQ+IHtcbiAgICB0aGlzLmNvbnRleHQ/Lm9uTGlmZWN5Y2xlKFwic2V0dXA6Y29tcGxldGVcIiwgYXN5bmMgKCkgPT4ge1xuICAgICAgY29uc3QgcG9vbCA9IGxha2ViYXNlUmVzb2x2ZXIuY2FwdHVyZVNlcnZpY2VQcmluY2lwYWxQb29sKFxuICAgICAgICBwbHVnaW5SZWdpc3RyeS5yZXF1aXJlKHRoaXMuY29udGV4dCwgbGFrZWJhc2UsIFwiYXBwa2l0LWxha2Vib3hcIikuZXhwb3J0cygpLnBvb2wsXG4gICAgICApO1xuICAgICAgdGhpcy5ydW50aW1lLmluaXRpYWxpemUoXG4gICAgICAgIGNyZWF0ZVdvcmtzcGFjZUNsaWVudCgpLmNvbmZpZyxcbiAgICAgICAgKGhlYWRlcnMpID0+IHRoaXMucmVxdWVzdElkZW50aXR5KGhlYWRlcnMpLFxuICAgICAgICBuZXcgTGFrZWJveFNlcnZpY2VTbmFwc2hvdHMoKSxcbiAgICAgICAgbmV3IExha2Vib3hSZXNvdXJjZVJlZ2lzdHJ5KHRoaXMucnVudGltZS5jb25maWcuYXBwbGljYXRpb24sIHBvb2wpLFxuICAgICAgKTtcbiAgICAgIHZvaWQgdGhpcy5ydW50aW1lLmNsZWFudXBMZWdhY3kocG9vbCBhcyBMYWtlYm94TGVnYWN5UG9vbCk7XG4gICAgICBjb25zdCBzZXJ2ZXIgPSB0aGlzLmNvbnRleHQ/LmdldFBsdWdpbnMoKS5nZXQoXCJzZXJ2ZXJcIikgYXMgU2VydmVyRXh0ZW5zaW9uIHwgdW5kZWZpbmVkO1xuICAgICAgaWYgKCFzZXJ2ZXIgfHwgdGhpcy5tb3VudGVkKSByZXR1cm47XG4gICAgICB0aGlzLnVwZ3JhZGVJZGVudGl0eUdhdGUgPSByZXNvbHZlVXBncmFkZUlkZW50aXR5R2F0ZShcbiAgICAgICAgdGhpcy5jb250ZXh0Py5nZXRQbHVnaW5zKCkuZ2V0KFwiYXV0aEdhdGVcIiksXG4gICAgICApO1xuICAgICAgc2VydmVyLmFkZEV4dGVuc2lvbigoYXBwbGljYXRpb24pID0+IGFwcGxpY2F0aW9uLnVzZSh0aGlzLnJ1bnRpbWUucm91dGVzLm1pZGRsZXdhcmUoKSkpO1xuICAgICAgdGhpcy5tb3VudGVkID0gdHJ1ZTtcbiAgICAgIHRoaXMuYXR0YWNoVXBncmFkZUhhbmRsZXIoc2VydmVyKTtcbiAgICB9KTtcbiAgfVxuXG4gIG92ZXJyaWRlIGluamVjdFJvdXRlcyhyb3V0ZXI6IElBcHBSb3V0ZXIpOiB2b2lkIHtcbiAgICBjb25zdCBqc29uID0gKFxuICAgICAgbmFtZTogc3RyaW5nLFxuICAgICAgbWV0aG9kOiBcImdldFwiIHwgXCJwb3N0XCIgfCBcImRlbGV0ZVwiLFxuICAgICAgcGF0aDogc3RyaW5nLFxuICAgICAgaGFuZGxlcjogKGlkZW50aXR5OiBzdHJpbmcsIHJlcTogUmVxdWVzdCkgPT4gUHJvbWlzZTx1bmtub3duPixcbiAgICApID0+XG4gICAgICB0aGlzLnJvdXRlKHJvdXRlciwge1xuICAgICAgICBuYW1lLFxuICAgICAgICBtZXRob2QsXG4gICAgICAgIHBhdGgsXG4gICAgICAgIGhhbmRsZXI6IGFzeW5jIChyZXEsIHJlcykgPT4ge1xuICAgICAgICAgIHJlcy5qc29uKGF3YWl0IGhhbmRsZXIodGhpcy5yZXNvbHZlVXNlcklkKHJlcSksIHJlcSkpO1xuICAgICAgICB9LFxuICAgICAgfSk7XG4gICAganNvbihcInN0YXR1c1wiLCBcImdldFwiLCBMQUtFQk9YX1JPVVRFUy5zdGF0dXMsIChpZGVudGl0eSkgPT4gdGhpcy5ydW50aW1lLnN1bW1hcnkoaWRlbnRpdHkpKTtcbiAgICBqc29uKFwic3RhcnRcIiwgXCJwb3N0XCIsIExBS0VCT1hfUk9VVEVTLnN0YXJ0LCBhc3luYyAoaWRlbnRpdHkpID0+IHtcbiAgICAgIGF3YWl0IHRoaXMucnVudGltZS5zdGFydCgpO1xuICAgICAgcmV0dXJuIHRoaXMucnVudGltZS5zdW1tYXJ5KGlkZW50aXR5KTtcbiAgICB9KTtcbiAgICBqc29uKFwic3RvcFwiLCBcInBvc3RcIiwgTEFLRUJPWF9ST1VURVMuc3RvcCwgYXN5bmMgKGlkZW50aXR5KSA9PiB7XG4gICAgICBhd2FpdCB0aGlzLnJ1bnRpbWUuc3RvcCgpO1xuICAgICAgcmV0dXJuIHRoaXMucnVudGltZS5zdW1tYXJ5KGlkZW50aXR5KTtcbiAgICB9KTtcbiAgICBqc29uKFxuICAgICAgXCJleGVjdXRlQ29tbWFuZFwiLFxuICAgICAgXCJwb3N0XCIsXG4gICAgICBMQUtFQk9YX1JPVVRFUy5jb21tYW5kcyxcbiAgICAgIChpZGVudGl0eSwgcmVxKSA9PlxuICAgICAgICB0aGlzLnJ1bnRpbWUuZXhlY3V0ZShpZGVudGl0eSwgbGFrZWJveENvbW1hbmRSZXF1ZXN0U2NoZW1hLnBhcnNlKHJlcS5ib2R5ID8/IHt9KSkucmVzdWx0LFxuICAgICk7XG4gICAganNvbihcInNlcnZpY2VzXCIsIFwiZ2V0XCIsIExBS0VCT1hfUk9VVEVTLnNlcnZpY2VzLCBhc3luYyAoaWRlbnRpdHkpID0+ICh7XG4gICAgICBzZXJ2aWNlczogYXdhaXQgdGhpcy5ydW50aW1lLmxpc3RTZXJ2aWNlcyhpZGVudGl0eSksXG4gICAgfSkpO1xuICAgIGpzb24oXCJjcmVhdGVTZXJ2aWNlXCIsIFwicG9zdFwiLCBMQUtFQk9YX1JPVVRFUy5zZXJ2aWNlcywgYXN5bmMgKGlkZW50aXR5LCByZXEpID0+ICh7XG4gICAgICBzZXJ2aWNlOiBhd2FpdCB0aGlzLnJ1bnRpbWUuY3JlYXRlU2VydmljZShpZGVudGl0eSwgcmVxLmJvZHkgPz8ge30pLFxuICAgIH0pKTtcbiAgICBqc29uKFwic3RhcnRTZXJ2aWNlXCIsIFwicG9zdFwiLCBMQUtFQk9YX1JPVVRFUy5zZXJ2aWNlU3RhcnQsIGFzeW5jIChpZGVudGl0eSwgcmVxKSA9PiAoe1xuICAgICAgc2VydmljZTogYXdhaXQgdGhpcy5ydW50aW1lLnN0YXJ0U2VydmljZShpZGVudGl0eSwgc2VydmljZU5hbWUocmVxKSksXG4gICAgfSkpO1xuICAgIGpzb24oXCJzdG9wU2VydmljZVwiLCBcInBvc3RcIiwgTEFLRUJPWF9ST1VURVMuc2VydmljZVN0b3AsIGFzeW5jIChpZGVudGl0eSwgcmVxKSA9PiAoe1xuICAgICAgc2VydmljZTogYXdhaXQgdGhpcy5ydW50aW1lLnN0b3BTZXJ2aWNlKGlkZW50aXR5LCBzZXJ2aWNlTmFtZShyZXEpKSxcbiAgICB9KSk7XG4gICAganNvbihcInJlbW92ZVNlcnZpY2VcIiwgXCJkZWxldGVcIiwgTEFLRUJPWF9ST1VURVMuc2VydmljZSwgYXN5bmMgKGlkZW50aXR5LCByZXEpID0+ICh7XG4gICAgICByZW1vdmVkOiBhd2FpdCB0aGlzLnJ1bnRpbWUucmVtb3ZlU2VydmljZShpZGVudGl0eSwgc2VydmljZU5hbWUocmVxKSksXG4gICAgfSkpO1xuICAgIGpzb24oXCJzZXJ2aWNlTG9nc1wiLCBcImdldFwiLCBMQUtFQk9YX1JPVVRFUy5zZXJ2aWNlTG9ncywgKGlkZW50aXR5LCByZXEpID0+XG4gICAgICB0aGlzLnJ1bnRpbWUuc2VydmljZUxvZ3MoXG4gICAgICAgIGlkZW50aXR5LFxuICAgICAgICBsYWtlYm94U2VydmljZUxvZ3NSZXF1ZXN0U2NoZW1hLnBhcnNlKHtcbiAgICAgICAgICBuYW1lOiByZXEucGFyYW1zLm5hbWUsXG4gICAgICAgICAgbWF4TGluZXM6IHJlcS5xdWVyeS5tYXhMaW5lcyxcbiAgICAgICAgICBtYXhDaGFyczogcmVxLnF1ZXJ5Lm1heENoYXJzLFxuICAgICAgICB9KSxcbiAgICAgICksXG4gICAgKTtcbiAgICBqc29uKFwibWV0cmljc1wiLCBcImdldFwiLCBMQUtFQk9YX1JPVVRFUy5tZXRyaWNzLCAoKSA9PiB0aGlzLnJ1bnRpbWUubWV0cmljc1Jlc3BvbnNlKCkpO1xuICAgIHRoaXMucm91dGUocm91dGVyLCB7XG4gICAgICBuYW1lOiBcIm1ldHJpY3NTdHJlYW1cIixcbiAgICAgIG1ldGhvZDogXCJnZXRcIixcbiAgICAgIHBhdGg6IExBS0VCT1hfUk9VVEVTLm1ldHJpY3NTdHJlYW0sXG4gICAgICBoYW5kbGVyOiBhc3luYyAocmVxLCByZXMpID0+IHRoaXMuc3RyZWFtTWV0cmljcyhyZXEsIHJlcyksXG4gICAgfSk7XG4gIH1cblxuICAvKiogUmV0dXJucyBicm93c2VyLXNhZmUgcm91dGUgZGVmYXVsdHMgcHVibGlzaGVkIHRocm91Z2ggQXBwS2l0IGNsaWVudCBjb25maWcuICovXG4gIGNsaWVudENvbmZpZygpOiBMYWtlYm94Q2xpZW50Q29uZmlnSW5wdXQge1xuICAgIHJldHVybiB7IHJvdXRlUHJlZml4OiB0aGlzLnJ1bnRpbWUuY29uZmlnLnJvdXRlUHJlZml4IH07XG4gIH1cblxuICAvKiogUmV0dXJucyBzZWxlY3RlZCBBcHBLaXQgdG9vbGtpdCBlbnRyaWVzIGJhY2tlZCBieSB0aGlzIHBsdWdpbidzIGFnZW50IHRvb2xzLiAqL1xuICB0b29sa2l0KG9wdGlvbnM/OiBUb29sa2l0T3B0aW9ucyk6IFJlY29yZDxzdHJpbmcsIFRvb2xraXRFbnRyeT4ge1xuICAgIHJldHVybiB0b29sa2l0RW50cmllcy5lbnRyaWVzKExBS0VCT1hfUExVR0lOX05BTUUsIHRoaXMuZ2V0QWdlbnRUb29scygpLCB7XG4gICAgICAuLi5vcHRpb25zLFxuICAgICAgcHJlZml4OiBvcHRpb25zPy5wcmVmaXggPz8gXCJcIixcbiAgICB9KTtcbiAgfVxuXG4gIC8qKiBFeGVjdXRlcyBvbmUgcmVnaXN0ZXJlZCBhZ2VudCB0b29sIGZvciB0aGUgY3VycmVudCBleGVjdXRpb24gaWRlbnRpdHkuICovXG4gIG92ZXJyaWRlIGV4ZWN1dGVBZ2VudFRvb2woXG4gICAgbmFtZTogc3RyaW5nLFxuICAgIGFyZ3M6IHVua25vd24sXG4gICAgc2lnbmFsPzogQWJvcnRTaWduYWwsXG4gICAgY29udGV4dD86IEFnZW50VG9vbEV4ZWN1dGlvbkNvbnRleHQsXG4gICk6IFByb21pc2U8dW5rbm93bj4ge1xuICAgIHJldHVybiBleGVjdXRlRnJvbVJlZ2lzdHJ5KFxuICAgICAgdGhpcy5jcmVhdGVUb29sUmVnaXN0cnkoY29udGV4dD8ucmVzb3VyY2VJZCwgY29udGV4dD8ub3JpZ2luKSxcbiAgICAgIG5hbWUsXG4gICAgICBhcmdzLFxuICAgICAgc2lnbmFsLFxuICAgICk7XG4gIH1cblxuICAvKiogUmV0dXJucyB0aGUgdHlwZWQgc2libGluZy1wbHVnaW4gQVBJLiAqL1xuICBleHBvcnRzKCkge1xuICAgIHJldHVybiB7XG4gICAgICBzdW1tYXJ5OiAoaWRlbnRpdHk6IHN0cmluZykgPT4gdGhpcy5ydW50aW1lLnN1bW1hcnkoaWRlbnRpdHkpLFxuICAgICAgc3RhcnQ6ICgpID0+IHRoaXMucnVudGltZS5zdGFydCgpLFxuICAgICAgc3RvcDogKCkgPT4gdGhpcy5ydW50aW1lLnN0b3AoKSxcbiAgICAgIGRlbGV0ZTogKCkgPT4gdGhpcy5ydW50aW1lLmRlbGV0ZSgpLFxuICAgICAgZXhlY3V0ZUNvbW1hbmQ6IChpZGVudGl0eTogc3RyaW5nLCBpbnB1dDogTGFrZWJveENvbW1hbmRSZXF1ZXN0KSA9PlxuICAgICAgICB0aGlzLnJ1bnRpbWUuZXhlY3V0ZShpZGVudGl0eSwgaW5wdXQpLnJlc3VsdCxcbiAgICAgIGxpc3RTZXJ2aWNlczogKGlkZW50aXR5OiBzdHJpbmcpID0+IHRoaXMucnVudGltZS5saXN0U2VydmljZXMoaWRlbnRpdHkpLFxuICAgICAgY3JlYXRlU2VydmljZTogKGlkZW50aXR5OiBzdHJpbmcsIGlucHV0OiBDcmVhdGVMYWtlYm94U2VydmljZVJlcXVlc3QpID0+XG4gICAgICAgIHRoaXMucnVudGltZS5jcmVhdGVTZXJ2aWNlKGlkZW50aXR5LCBpbnB1dCksXG4gICAgICBzdGFydFNlcnZpY2U6IChpZGVudGl0eTogc3RyaW5nLCBuYW1lOiBzdHJpbmcpID0+IHRoaXMucnVudGltZS5zdGFydFNlcnZpY2UoaWRlbnRpdHksIG5hbWUpLFxuICAgICAgc3RvcFNlcnZpY2U6IChpZGVudGl0eTogc3RyaW5nLCBuYW1lOiBzdHJpbmcpID0+IHRoaXMucnVudGltZS5zdG9wU2VydmljZShpZGVudGl0eSwgbmFtZSksXG4gICAgICByZW1vdmVTZXJ2aWNlOiAoaWRlbnRpdHk6IHN0cmluZywgbmFtZTogc3RyaW5nKSA9PiB0aGlzLnJ1bnRpbWUucmVtb3ZlU2VydmljZShpZGVudGl0eSwgbmFtZSksXG4gICAgICBzZXJ2aWNlTG9nczogKGlkZW50aXR5OiBzdHJpbmcsIGlucHV0OiBMYWtlYm94U2VydmljZUxvZ3NSZXF1ZXN0KSA9PlxuICAgICAgICB0aGlzLnJ1bnRpbWUuc2VydmljZUxvZ3MoaWRlbnRpdHksIGlucHV0KSxcbiAgICAgIG9wZXJhdGlvbnM6IChpZGVudGl0eT86IHN0cmluZykgPT4gdGhpcy5vcGVyYXRpb25zKGlkZW50aXR5KSxcbiAgICAgIHRvb2xraXQ6IChvcHRpb25zPzogVG9vbGtpdE9wdGlvbnMpID0+IHRoaXMudG9vbGtpdChvcHRpb25zKSxcbiAgICB9O1xuICB9XG5cbiAgLyoqIENhbmNlbHMgaW4tZmxpZ2h0IHdvcmsgYmVmb3JlIHNodXRkb3duIHRlYXJzIGRvd24gcmVzb3VyY2VzLiAqL1xuICBvdmVycmlkZSBhYm9ydEFjdGl2ZU9wZXJhdGlvbnMoKTogdm9pZCB7XG4gICAgdGhpcy5ydW50aW1lLmFib3J0QWN0aXZlT3BlcmF0aW9ucygpO1xuICB9XG5cbiAgLyoqIERldGFjaGVzIFdlYlNvY2tldCBoYW5kbGluZyBhbmQgY2xvc2VzIHRoZSBzaGFyZWQgcnVudGltZS4gKi9cbiAgYXN5bmMgc2h1dGRvd24oKTogUHJvbWlzZTx2b2lkPiB7XG4gICAgaWYgKHRoaXMudXBncmFkZUF0dGFjaFRpbWVyKSBjbGVhclRpbWVvdXQodGhpcy51cGdyYWRlQXR0YWNoVGltZXIpO1xuICAgIHRoaXMudXBncmFkZUF0dGFjaFRpbWVyID0gdW5kZWZpbmVkO1xuICAgIHRoaXMuaHR0cFNlcnZlcj8ub2ZmKFwidXBncmFkZVwiLCB0aGlzLnVwZ3JhZGVIYW5kbGVyKTtcbiAgICB0aGlzLmh0dHBTZXJ2ZXIgPSB1bmRlZmluZWQ7XG4gICAgYXdhaXQgdGhpcy5ydW50aW1lLmNsb3NlKCk7XG4gIH1cblxuICAvKiogUmV0dXJucyB0aGUgWm9kLW93bmVkIG9wZXJhdGlvbiBjb250cmFjdHMgdXNlZCBieSBhZ2VudCB0b29scy4gKi9cbiAgb3BlcmF0aW9ucyhpZGVudGl0eT86IHN0cmluZywgb3JpZ2luPzogc3RyaW5nKSB7XG4gICAgY29uc3QgdXNlciA9ICgpID0+IGlkZW50aXR5ID8/IHRoaXMuZXhlY3V0aW9uSWRlbnRpdHkoKTtcbiAgICBjb25zdCBwcmVzZW50U2VydmljZSA9ICh2YWx1ZTogTGFrZWJveFNlcnZpY2UpID0+IG9taXRMb2cod2l0aFNlcnZpY2VPcmlnaW4odmFsdWUsIG9yaWdpbikpO1xuICAgIHJldHVybiB0b29sT3BlcmF0aW9ucy5kZWZpbmVPcGVyYXRpb25zKHtcbiAgICAgIGdldF9sYWtlYm94OiB7XG4gICAgICAgIHNjaGVtYTogZ2V0TGFrZWJveE9wZXJhdGlvblNjaGVtYSxcbiAgICAgICAgZXhlY3V0ZTogYXN5bmMgKF9pbnB1dCwgc2lnbmFsKSA9PiB7XG4gICAgICAgICAgY29uc3Qgc3VtbWFyeSA9IGF3YWl0IHRoaXMucnVudGltZS5zdW1tYXJ5KHVzZXIoKSwgc2lnbmFsKTtcbiAgICAgICAgICByZXR1cm4geyAuLi5zdW1tYXJ5LCBzZXJ2aWNlczogc3VtbWFyeS5zZXJ2aWNlcy5tYXAocHJlc2VudFNlcnZpY2UpIH07XG4gICAgICAgIH0sXG4gICAgICB9LFxuICAgICAgZXhlY3V0ZV9sYWtlYm94X2NvbW1hbmQ6IHtcbiAgICAgICAgc2NoZW1hOiBleGVjdXRlTGFrZWJveENvbW1hbmRPcGVyYXRpb25TY2hlbWEsXG4gICAgICAgIGV4ZWN1dGU6IChpbnB1dCwgc2lnbmFsKSA9PiB0aGlzLnJ1bnRpbWUuZXhlY3V0ZSh1c2VyKCksIGlucHV0LCBzaWduYWwpLnJlc3VsdCxcbiAgICAgIH0sXG4gICAgICBsaXN0X2xha2Vib3hfc2VydmljZXM6IHtcbiAgICAgICAgc2NoZW1hOiBsaXN0TGFrZWJveFNlcnZpY2VzT3BlcmF0aW9uU2NoZW1hLFxuICAgICAgICBleGVjdXRlOiBhc3luYyAoX2lucHV0LCBzaWduYWwpID0+ICh7XG4gICAgICAgICAgc2VydmljZXM6IChhd2FpdCB0aGlzLnJ1bnRpbWUubGlzdFNlcnZpY2VzKHVzZXIoKSwgc2lnbmFsKSkubWFwKHByZXNlbnRTZXJ2aWNlKSxcbiAgICAgICAgfSksXG4gICAgICB9LFxuICAgICAgY3JlYXRlX2xha2Vib3hfc2VydmljZToge1xuICAgICAgICBzY2hlbWE6IGNyZWF0ZUxha2Vib3hTZXJ2aWNlT3BlcmF0aW9uU2NoZW1hLFxuICAgICAgICBleGVjdXRlOiBhc3luYyAoaW5wdXQsIHNpZ25hbCkgPT4gKHtcbiAgICAgICAgICBzZXJ2aWNlOiBwcmVzZW50U2VydmljZShhd2FpdCB0aGlzLnJ1bnRpbWUuY3JlYXRlU2VydmljZSh1c2VyKCksIGlucHV0LCBzaWduYWwpKSxcbiAgICAgICAgfSksXG4gICAgICB9LFxuICAgICAgc3RhcnRfbGFrZWJveF9zZXJ2aWNlOiB7XG4gICAgICAgIHNjaGVtYTogc3RhcnRMYWtlYm94U2VydmljZU9wZXJhdGlvblNjaGVtYSxcbiAgICAgICAgZXhlY3V0ZTogYXN5bmMgKGlucHV0LCBzaWduYWwpID0+ICh7XG4gICAgICAgICAgc2VydmljZTogcHJlc2VudFNlcnZpY2UoYXdhaXQgdGhpcy5ydW50aW1lLnN0YXJ0U2VydmljZSh1c2VyKCksIGlucHV0Lm5hbWUsIHNpZ25hbCkpLFxuICAgICAgICB9KSxcbiAgICAgIH0sXG4gICAgICBzdG9wX2xha2Vib3hfc2VydmljZToge1xuICAgICAgICBzY2hlbWE6IHN0b3BMYWtlYm94U2VydmljZU9wZXJhdGlvblNjaGVtYSxcbiAgICAgICAgZXhlY3V0ZTogYXN5bmMgKGlucHV0LCBzaWduYWwpID0+ICh7XG4gICAgICAgICAgc2VydmljZTogcHJlc2VudFNlcnZpY2UoYXdhaXQgdGhpcy5ydW50aW1lLnN0b3BTZXJ2aWNlKHVzZXIoKSwgaW5wdXQubmFtZSwgc2lnbmFsKSksXG4gICAgICAgIH0pLFxuICAgICAgfSxcbiAgICAgIHJlbW92ZV9sYWtlYm94X3NlcnZpY2U6IHtcbiAgICAgICAgc2NoZW1hOiByZW1vdmVMYWtlYm94U2VydmljZU9wZXJhdGlvblNjaGVtYSxcbiAgICAgICAgZXhlY3V0ZTogYXN5bmMgKGlucHV0LCBzaWduYWwpID0+ICh7XG4gICAgICAgICAgcmVtb3ZlZDogYXdhaXQgdGhpcy5ydW50aW1lLnJlbW92ZVNlcnZpY2UodXNlcigpLCBpbnB1dC5uYW1lLCBzaWduYWwpLFxuICAgICAgICB9KSxcbiAgICAgIH0sXG4gICAgICBnZXRfbGFrZWJveF9zZXJ2aWNlX2xvZ3M6IHtcbiAgICAgICAgc2NoZW1hOiBnZXRMYWtlYm94U2VydmljZUxvZ3NPcGVyYXRpb25TY2hlbWEsXG4gICAgICAgIGV4ZWN1dGU6IChpbnB1dCwgc2lnbmFsKSA9PiB0aGlzLnJ1bnRpbWUuc2VydmljZUxvZ3ModXNlcigpLCBpbnB1dCwgc2lnbmFsKSxcbiAgICAgIH0sXG4gICAgfSk7XG4gIH1cblxuICBwcml2YXRlIGNyZWF0ZVRvb2xSZWdpc3RyeShpZGVudGl0eT86IHN0cmluZywgb3JpZ2luPzogc3RyaW5nKTogVG9vbFJlZ2lzdHJ5IHtcbiAgICByZXR1cm4gdG9vbE9wZXJhdGlvbnMudG9vbFJlZ2lzdHJ5RnJvbU9wZXJhdGlvbnModGhpcy5vcGVyYXRpb25zKGlkZW50aXR5LCBvcmlnaW4pKTtcbiAgfVxuXG4gIHByaXZhdGUgZXhlY3V0aW9uSWRlbnRpdHkoKTogc3RyaW5nIHtcbiAgICBjb25zdCBjb250ZXh0ID0gYXBwa2l0LnRyeUdldEV4ZWN1dGlvbkNvbnRleHQoKTtcbiAgICBpZiAoIWNvbnRleHQpIHRocm93IG5ldyBFcnJvcihcIkxha2Vib3ggdG9vbHMgcmVxdWlyZSBhbiBBcHBLaXQgZXhlY3V0aW9uIGNvbnRleHRcIik7XG4gICAgcmV0dXJuIFwidXNlcklkXCIgaW4gY29udGV4dCA/IGNvbnRleHQudXNlcklkIDogY29udGV4dC5zZXJ2aWNlVXNlcklkO1xuICB9XG5cbiAgLyoqXG4gICAqIFJlc29sdmVzIHJvdXRlIGlkZW50aXR5IHdpdGggdGhlIHNhbWUgcnVsZXMgYXMgUkVTVDogdGhlIGZvcndhcmRlZCB1c2VyLFxuICAgKiBvciBBcHBLaXQncyBkZXZlbG9wbWVudCB1c2VyIGZvciBkaXJlY3QgbG9vcGJhY2sgbmF2aWdhdGlvbiBvdXRzaWRlXG4gICAqIHByb2R1Y3Rpb24uIEV2ZXJ5dGhpbmcgZWxzZSBpcyBhbm9ueW1vdXMgYW5kIGZhaWxzIGNsb3NlZC5cbiAgICovXG4gIHByaXZhdGUgcmVxdWVzdElkZW50aXR5KGhlYWRlcnM6IEluY29taW5nSHR0cEhlYWRlcnMpOiBzdHJpbmcgfCB1bmRlZmluZWQge1xuICAgIGNvbnN0IGZvcndhcmRlZCA9IGhlYWRlclZhbHVlKGhlYWRlcnNbVVNFUl9JRF9IRUFERVJdKT8udHJpbSgpO1xuICAgIGlmIChmb3J3YXJkZWQpIHJldHVybiBmb3J3YXJkZWQ7XG4gICAgaWYgKHByb2Nlc3MuZW52Lk5PREVfRU5WICE9PSBcImRldmVsb3BtZW50XCIgfHwgIWlzTG9vcGJhY2tIb3N0KGhlYWRlclZhbHVlKGhlYWRlcnMuaG9zdCkpKSB7XG4gICAgICByZXR1cm4gdW5kZWZpbmVkO1xuICAgIH1cbiAgICB0cnkge1xuICAgICAgcmV0dXJuIHRoaXMucmVzb2x2ZVVzZXJJZCh7XG4gICAgICAgIGhlYWRlcjogKG5hbWU6IHN0cmluZykgPT4gaGVhZGVyVmFsdWUoaGVhZGVyc1tuYW1lLnRvTG93ZXJDYXNlKCldKSxcbiAgICAgIH0gYXMgdW5rbm93biBhcyBSZXF1ZXN0KTtcbiAgICB9IGNhdGNoIHtcbiAgICAgIHJldHVybiB1bmRlZmluZWQ7XG4gICAgfVxuICB9XG5cbiAgcHJpdmF0ZSBhc3luYyBzdHJlYW1NZXRyaWNzKHJlcTogUmVxdWVzdCwgcmVzOiBSZXNwb25zZSk6IFByb21pc2U8dm9pZD4ge1xuICAgIGNvbnN0IGluaXRpYWwgPSBhd2FpdCB0aGlzLnJ1bnRpbWUubWV0cmljc1Jlc3BvbnNlKCk7XG4gICAgcmVzLnN0YXR1c0NvZGUgPSAyMDA7XG4gICAgcmVzLnNldEhlYWRlcihcImNvbnRlbnQtdHlwZVwiLCBcInRleHQvZXZlbnQtc3RyZWFtOyBjaGFyc2V0PXV0Zi04XCIpO1xuICAgIHJlcy5zZXRIZWFkZXIoXCJjYWNoZS1jb250cm9sXCIsIFwibm8tY2FjaGUsIG5vLXRyYW5zZm9ybVwiKTtcbiAgICByZXMuc2V0SGVhZGVyKFwiY29ubmVjdGlvblwiLCBcImtlZXAtYWxpdmVcIik7XG4gICAgcmVzLnNldEhlYWRlcihcIngtYWNjZWwtYnVmZmVyaW5nXCIsIFwibm9cIik7XG4gICAgcmVzLmZsdXNoSGVhZGVycz8uKCk7XG4gICAgcmVzLndyaXRlKGBkYXRhOiAke0pTT04uc3RyaW5naWZ5KGluaXRpYWwpfVxcblxcbmApO1xuICAgIGNvbnN0IHVuc3Vic2NyaWJlID0gdGhpcy5ydW50aW1lLnN1YnNjcmliZU1ldHJpY3MoKHNuYXBzaG90KSA9PiB7XG4gICAgICByZXMud3JpdGUoYGRhdGE6ICR7SlNPTi5zdHJpbmdpZnkoeyBzdGF0dXM6IFwicnVubmluZ1wiLCBzZXJpZXM6IFtzbmFwc2hvdF0gfSl9XFxuXFxuYCk7XG4gICAgfSk7XG4gICAgcmVxLm9uY2UoXCJjbG9zZVwiLCB1bnN1YnNjcmliZSk7XG4gIH1cblxuICBwcml2YXRlIGF0dGFjaFVwZ3JhZGVIYW5kbGVyKHNlcnZlcjogU2VydmVyRXh0ZW5zaW9uKTogdm9pZCB7XG4gICAgaWYgKHRoaXMuaHR0cFNlcnZlciB8fCB0aGlzLnVwZ3JhZGVBdHRhY2hUaW1lcikgcmV0dXJuO1xuICAgIHRyeSB7XG4gICAgICB0aGlzLmh0dHBTZXJ2ZXIgPSBzZXJ2ZXIuZ2V0U2VydmVyKCk7XG4gICAgICB0aGlzLmh0dHBTZXJ2ZXIub24oXCJ1cGdyYWRlXCIsIHRoaXMudXBncmFkZUhhbmRsZXIpO1xuICAgIH0gY2F0Y2gge1xuICAgICAgdGhpcy51cGdyYWRlQXR0YWNoVGltZXIgPSBzZXRUaW1lb3V0KCgpID0+IHtcbiAgICAgICAgdGhpcy51cGdyYWRlQXR0YWNoVGltZXIgPSB1bmRlZmluZWQ7XG4gICAgICAgIHRoaXMuYXR0YWNoVXBncmFkZUhhbmRsZXIoc2VydmVyKTtcbiAgICAgIH0sIDEwKTtcbiAgICAgIHRoaXMudXBncmFkZUF0dGFjaFRpbWVyLnVucmVmPy4oKTtcbiAgICB9XG4gIH1cblxuICBwcml2YXRlIGFzeW5jIGhhbmRsZVVwZ3JhZGUoXG4gICAgcmVxdWVzdDogSW5jb21pbmdNZXNzYWdlLFxuICAgIHNvY2tldDogU29ja2V0LFxuICAgIGhlYWQ6IEJ1ZmZlcixcbiAgKTogUHJvbWlzZTx2b2lkPiB7XG4gICAgY29uc3QgcGF0aCA9IG5ldyBVUkwocmVxdWVzdC51cmwgPz8gXCIvXCIsIFwiaHR0cDovL2xvY2FsaG9zdFwiKS5wYXRobmFtZTtcbiAgICBpZiAoIXRoaXMucnVudGltZS5yb3V0ZXMuY2FuSGFuZGxlVXBncmFkZShwYXRoKSkgcmV0dXJuO1xuICAgIGNvbnN0IGdhdGUgPSB0aGlzLnVwZ3JhZGVJZGVudGl0eUdhdGU7XG4gICAgaWYgKGdhdGUpIHtcbiAgICAgIGNvbnN0IGhlYWRlcnMgPSByZXF1ZXN0SGVhZGVycyhyZXF1ZXN0KTtcbiAgICAgIGlmIChnYXRlLmlzUHJvdGVjdGVkSG9zdChoZWFkZXJzKSkge1xuICAgICAgICBkZWxldGUgcmVxdWVzdC5oZWFkZXJzW1VTRVJfSURfSEVBREVSXTtcbiAgICAgICAgZGVsZXRlIHJlcXVlc3QuaGVhZGVyc1tVU0VSX0VNQUlMX0hFQURFUl07XG4gICAgICAgIGNvbnN0IHVzZXJJZCA9IGF3YWl0IGdhdGUuc2Vzc2lvbihoZWFkZXJzKTtcbiAgICAgICAgaWYgKCF1c2VySWQpIHtcbiAgICAgICAgICBzb2NrZXQuZGVzdHJveSgpO1xuICAgICAgICAgIHJldHVybjtcbiAgICAgICAgfVxuICAgICAgICByZXF1ZXN0LmhlYWRlcnNbVVNFUl9JRF9IRUFERVJdID0gdXNlcklkO1xuICAgICAgICByZXF1ZXN0LmhlYWRlcnNbVVNFUl9FTUFJTF9IRUFERVJdID0gdXNlcklkO1xuICAgICAgfVxuICAgIH1cbiAgICBpZiAoIShhd2FpdCB0aGlzLnJ1bnRpbWUucm91dGVzLmhhbmRsZVVwZ3JhZGUocmVxdWVzdCwgc29ja2V0LCBoZWFkKSkpIHNvY2tldC5kZXN0cm95KCk7XG4gIH1cbn1cblxuZnVuY3Rpb24gc2VydmljZU5hbWUocmVxOiBSZXF1ZXN0KTogc3RyaW5nIHtcbiAgcmV0dXJuIGxha2Vib3hTZXJ2aWNlQWN0aW9uU2NoZW1hLnBhcnNlKHsgbmFtZTogcmVxLnBhcmFtcy5uYW1lIH0pLm5hbWU7XG59XG5cbmZ1bmN0aW9uIG9taXRMb2c8VCBleHRlbmRzIHsgbG9nPzogc3RyaW5nIH0+KHsgbG9nOiBfbG9nLCAuLi52YWx1ZSB9OiBUKTogT21pdDxULCBcImxvZ1wiPiB7XG4gIHJldHVybiB2YWx1ZTtcbn1cblxuZnVuY3Rpb24gaGVhZGVyVmFsdWUodmFsdWU6IHN0cmluZyB8IHN0cmluZ1tdIHwgdW5kZWZpbmVkKTogc3RyaW5nIHwgdW5kZWZpbmVkIHtcbiAgcmV0dXJuIEFycmF5LmlzQXJyYXkodmFsdWUpID8gdmFsdWVbMF0gOiB2YWx1ZTtcbn1cblxuZnVuY3Rpb24gaXNMb29wYmFja0hvc3QoaG9zdDogc3RyaW5nIHwgdW5kZWZpbmVkKTogYm9vbGVhbiB7XG4gIGlmICghaG9zdCkgcmV0dXJuIGZhbHNlO1xuICB0cnkge1xuICAgIGNvbnN0IGhvc3RuYW1lID0gbmV3IFVSTChgaHR0cDovLyR7aG9zdH1gKS5ob3N0bmFtZS50b0xvd2VyQ2FzZSgpO1xuICAgIHJldHVybiBob3N0bmFtZSA9PT0gXCJsb2NhbGhvc3RcIiB8fCBob3N0bmFtZSA9PT0gXCIxMjcuMC4wLjFcIiB8fCBob3N0bmFtZSA9PT0gXCJbOjoxXVwiO1xuICB9IGNhdGNoIHtcbiAgICByZXR1cm4gZmFsc2U7XG4gIH1cbn1cblxuZnVuY3Rpb24gcmVzb2x2ZVVwZ3JhZGVJZGVudGl0eUdhdGUocGx1Z2luOiB1bmtub3duKTogVXBncmFkZUlkZW50aXR5R2F0ZSB8IHVuZGVmaW5lZCB7XG4gIGNvbnN0IGV4cG9ydHMgPSAocGx1Z2luIGFzIHsgZXhwb3J0cz86ICgpID0+IHVua25vd24gfSB8IHVuZGVmaW5lZCk/LmV4cG9ydHM/LigpO1xuICBpZiAoXG4gICAgdHlwZW9mIChleHBvcnRzIGFzIFVwZ3JhZGVJZGVudGl0eUdhdGUgfCB1bmRlZmluZWQpPy5pc1Byb3RlY3RlZEhvc3QgIT09IFwiZnVuY3Rpb25cIiB8fFxuICAgIHR5cGVvZiAoZXhwb3J0cyBhcyBVcGdyYWRlSWRlbnRpdHlHYXRlIHwgdW5kZWZpbmVkKT8uc2Vzc2lvbiAhPT0gXCJmdW5jdGlvblwiXG4gICkge1xuICAgIHJldHVybiB1bmRlZmluZWQ7XG4gIH1cbiAgcmV0dXJuIGV4cG9ydHMgYXMgVXBncmFkZUlkZW50aXR5R2F0ZTtcbn1cblxuZnVuY3Rpb24gcmVxdWVzdEhlYWRlcnMocmVxdWVzdDogSW5jb21pbmdNZXNzYWdlKTogSGVhZGVycyB7XG4gIGNvbnN0IGhlYWRlcnMgPSBuZXcgSGVhZGVycygpO1xuICBmb3IgKGNvbnN0IFtuYW1lLCB2YWx1ZV0gb2YgT2JqZWN0LmVudHJpZXMocmVxdWVzdC5oZWFkZXJzKSkge1xuICAgIGlmIChBcnJheS5pc0FycmF5KHZhbHVlKSkge1xuICAgICAgZm9yIChjb25zdCBpdGVtIG9mIHZhbHVlKSBoZWFkZXJzLmFwcGVuZChuYW1lLCBpdGVtKTtcbiAgICB9IGVsc2UgaWYgKHZhbHVlICE9PSB1bmRlZmluZWQpIHtcbiAgICAgIGhlYWRlcnMuc2V0KG5hbWUsIHZhbHVlKTtcbiAgICB9XG4gIH1cbiAgcmV0dXJuIGhlYWRlcnM7XG59XG5cbi8qKiBBcHBLaXQgcGx1Z2luIGZhY3RvcnkgZm9yIHtAbGluayBBcHBLaXRMYWtlYm94UGx1Z2lufS4gKi9cbmV4cG9ydCBjb25zdCBsYWtlYm94ID0gdG9QbHVnaW4oQXBwS2l0TGFrZWJveFBsdWdpbik7XG4iXX0=
@@ -0,0 +1,75 @@
1
+ import type { IncomingHttpHeaders, IncomingMessage } from "node:http";
2
+ import { type Socket } from "node:net";
3
+ import type { SshSession } from "@dbx-tools/lakebox/session";
4
+ import type { LakeboxUser } from "@dbx-tools/shared-lakebox";
5
+ import type { NextFunction, Request, Response } from "express";
6
+ import type { LakeboxServiceRoute } from "./services.ts";
7
+ /** Result of resolving one user's service for a routed request. */
8
+ export type LakeboxRouteResolution = {
9
+ kind: "ready";
10
+ session: SshSession;
11
+ route: LakeboxServiceRoute;
12
+ } | {
13
+ kind: "loading";
14
+ } | {
15
+ kind: "failed";
16
+ message: string;
17
+ } | {
18
+ kind: "missing";
19
+ };
20
+ /** Runtime callbacks that authorize and resolve deterministic routes. */
21
+ export interface LakeboxRouteResolver {
22
+ /** Returns the authenticated Lakebox user for request headers, or `undefined`. */
23
+ user(headers: IncomingHttpHeaders): LakeboxUser | undefined;
24
+ /**
25
+ * Resolves a running service. Returns `loading` after starting the Lakebox
26
+ * or service in the background, and `missing` for an unknown service.
27
+ */
28
+ resolve(user: LakeboxUser, service: string): Promise<LakeboxRouteResolution>;
29
+ /** Reports that a ready service refused a forwarded connection. */
30
+ unavailable(user: LakeboxUser, service: string): void;
31
+ }
32
+ interface RouteMatch {
33
+ userHash: string;
34
+ service: string;
35
+ mount: string;
36
+ }
37
+ /**
38
+ * Proxies `<prefix>/<user-hash>/<service>` to that user's service port.
39
+ *
40
+ * The user hash is the only routing key. A request is forwarded only when its
41
+ * authenticated identity hashes to the same value, and the port is derived from
42
+ * the hash and service name, so there is no stored route table. Requests
43
+ * without an identity are rejected with 404, including WebSocket upgrades.
44
+ */
45
+ export declare class LakeboxRouteRegistry {
46
+ private readonly prefix;
47
+ private readonly resolver;
48
+ private readonly forwardingReadyTimeoutMs;
49
+ private readonly bridges;
50
+ private readonly upgrades;
51
+ private readonly pending;
52
+ private readonly proxy;
53
+ private shuttingDown;
54
+ /** Creates a registry beneath one AppKit route prefix. */
55
+ constructor(prefix: string, resolver: LakeboxRouteResolver, forwardingReadyTimeoutMs?: number);
56
+ /** Returns the route path segment match for a request path. */
57
+ match(path: string): RouteMatch | undefined;
58
+ /** Express middleware that authorizes, rewrites, and proxies matching requests. */
59
+ middleware(): (request: Request, response: Response, next: NextFunction) => Promise<void>;
60
+ /** Number of open forwarded HTTP and WebSocket connections. */
61
+ activeConnections(): number;
62
+ /** Returns whether an upgrade path belongs to the Lakebox route prefix. */
63
+ canHandleUpgrade(path: string): boolean;
64
+ /** Proxies an authorized WebSocket upgrade. Returns false when it was not handled. */
65
+ handleUpgrade(request: IncomingMessage, socket: Socket, head: Buffer): Promise<boolean>;
66
+ /** Closes every forwarding bridge, for example after the Lakebox stops. */
67
+ closeBridges(): Promise<void>;
68
+ /** Suppresses expected proxy errors once shutdown begins. */
69
+ beginShutdown(): void;
70
+ /** Closes bridges and the shared proxy. */
71
+ close(): Promise<void>;
72
+ private authorize;
73
+ private bridge;
74
+ }
75
+ export {};