sonamu 0.11.16 → 0.13.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.
Files changed (322) hide show
  1. package/dist/_virtual/_rolldown/runtime.js +1 -2
  2. package/dist/ai/agents/agent.js +2 -3
  3. package/dist/ai/agents/index.js +1 -2
  4. package/dist/ai/index.js +1 -2
  5. package/dist/ai/providers/rtzr/api.js +1 -2
  6. package/dist/ai/providers/rtzr/error.js +2 -3
  7. package/dist/ai/providers/rtzr/model.js +1 -2
  8. package/dist/ai/providers/rtzr/options.js +1 -2
  9. package/dist/ai/providers/rtzr/provider.js +1 -2
  10. package/dist/ai/providers/rtzr/utils.js +1 -2
  11. package/dist/api/base-frame.js +4 -4
  12. package/dist/api/caster.d.ts +1 -1
  13. package/dist/api/caster.d.ts.map +1 -1
  14. package/dist/api/caster.js +9 -9
  15. package/dist/api/code-converters.js +6 -5
  16. package/dist/api/config.d.ts +1 -1
  17. package/dist/api/config.d.ts.map +1 -1
  18. package/dist/api/config.js +2 -3
  19. package/dist/api/context.d.ts +2 -3
  20. package/dist/api/context.d.ts.map +1 -1
  21. package/dist/api/context.js +2 -3
  22. package/dist/api/decorators.js +2 -3
  23. package/dist/api/http-validator.js +2 -3
  24. package/dist/api/index.js +8 -3
  25. package/dist/api/secret.js +1 -2
  26. package/dist/api/sonamu.d.ts +3 -5
  27. package/dist/api/sonamu.d.ts.map +1 -1
  28. package/dist/api/sonamu.js +30 -24
  29. package/dist/api/validator.js +1 -2
  30. package/dist/api/websocket-helpers.js +2 -3
  31. package/dist/auth/audit-log/builders.js +1 -2
  32. package/dist/auth/audit-log/client-ip.js +2 -3
  33. package/dist/auth/audit-log/events.js +1 -2
  34. package/dist/auth/audit-log/plugin.d.ts.map +1 -1
  35. package/dist/auth/audit-log/plugin.js +2 -3
  36. package/dist/auth/audit-log-ingestor.js +2 -3
  37. package/dist/auth/auth-generator.js +2 -3
  38. package/dist/auth/better-auth-entities.js +1 -2
  39. package/dist/auth/better-auth-request.js +2 -3
  40. package/dist/auth/index.js +2 -3
  41. package/dist/auth/knex-adapter.js +2 -3
  42. package/dist/auth/plugins/entity-definitions/admin.js +1 -2
  43. package/dist/auth/plugins/entity-definitions/anonymous.js +1 -2
  44. package/dist/auth/plugins/entity-definitions/api-key.js +1 -2
  45. package/dist/auth/plugins/entity-definitions/audit-log.js +1 -2
  46. package/dist/auth/plugins/entity-definitions/index.js +1 -2
  47. package/dist/auth/plugins/entity-definitions/jwt.js +1 -2
  48. package/dist/auth/plugins/entity-definitions/organization.js +1 -2
  49. package/dist/auth/plugins/entity-definitions/passkey.js +1 -2
  50. package/dist/auth/plugins/entity-definitions/phone-number.js +1 -2
  51. package/dist/auth/plugins/entity-definitions/sso.js +1 -2
  52. package/dist/auth/plugins/entity-definitions/two-factor.js +1 -2
  53. package/dist/auth/plugins/entity-definitions/username.js +1 -2
  54. package/dist/auth/plugins/wrappers/admin.d.ts +33 -32
  55. package/dist/auth/plugins/wrappers/admin.d.ts.map +1 -1
  56. package/dist/auth/plugins/wrappers/admin.js +2 -3
  57. package/dist/auth/plugins/wrappers/anonymous.js +2 -3
  58. package/dist/auth/plugins/wrappers/api-key.js +2 -3
  59. package/dist/auth/plugins/wrappers/jwt.js +2 -3
  60. package/dist/auth/plugins/wrappers/organization.js +2 -3
  61. package/dist/auth/plugins/wrappers/passkey.js +2 -3
  62. package/dist/auth/plugins/wrappers/phone-number.d.ts +43 -1
  63. package/dist/auth/plugins/wrappers/phone-number.d.ts.map +1 -1
  64. package/dist/auth/plugins/wrappers/phone-number.js +2 -3
  65. package/dist/auth/plugins/wrappers/sso.d.ts +138 -187
  66. package/dist/auth/plugins/wrappers/sso.d.ts.map +1 -1
  67. package/dist/auth/plugins/wrappers/sso.js +1 -2
  68. package/dist/auth/plugins/wrappers/two-factor.d.ts +51 -10
  69. package/dist/auth/plugins/wrappers/two-factor.d.ts.map +1 -1
  70. package/dist/auth/plugins/wrappers/two-factor.js +2 -3
  71. package/dist/auth/plugins/wrappers/username.d.ts +39 -5
  72. package/dist/auth/plugins/wrappers/username.d.ts.map +1 -1
  73. package/dist/auth/plugins/wrappers/username.js +2 -3
  74. package/dist/bin/build-config.js +5 -5
  75. package/dist/bin/compiler-policy.js +2 -3
  76. package/dist/bin/fixture.js +7 -5
  77. package/dist/bin/hmr-hook-register.js +1 -2
  78. package/dist/bin/migrate-targets.js +2 -3
  79. package/dist/bin/test-command.js +2 -3
  80. package/dist/bin/ts-loader-register.js +2 -3
  81. package/dist/bin/ts-loader-registration.js +2 -3
  82. package/dist/bin/typecheck-web.js +2 -3
  83. package/dist/cache/cache-manager.js +1 -2
  84. package/dist/cache/decorator.js +2 -3
  85. package/dist/cache/drivers.js +1 -2
  86. package/dist/cache/index.js +2 -3
  87. package/dist/cache-control/cache-control.js +1 -2
  88. package/dist/code-formatting/index.js +2 -3
  89. package/dist/code-formatting/oxfmt.js +2 -3
  90. package/dist/code-formatting/oxlint.js +2 -3
  91. package/dist/compress/compress.js +1 -2
  92. package/dist/compress/index.js +2 -2
  93. package/dist/compress/types.d.ts +1 -1
  94. package/dist/compress/types.d.ts.map +1 -1
  95. package/dist/cone/cone-generator.js +2 -3
  96. package/dist/database/_batch_update.js +1 -2
  97. package/dist/database/base-model.js +4 -4
  98. package/dist/database/db.js +4 -4
  99. package/dist/database/knex.js +2 -3
  100. package/dist/database/puri-wrapper.js +2 -3
  101. package/dist/database/puri.js +23 -22
  102. package/dist/database/puri.types.js +1 -2
  103. package/dist/database/transaction-context.js +1 -2
  104. package/dist/database/upsert-builder.js +2 -3
  105. package/dist/dict/en.js +2 -3
  106. package/dist/dict/index.js +3 -3
  107. package/dist/dict/ko.js +2 -3
  108. package/dist/dict/rc-keys.js +1 -2
  109. package/dist/dict/sd.js +2 -3
  110. package/dist/dict/sonamu-dictionary.js +7 -6
  111. package/dist/dict/utils.js +2 -3
  112. package/dist/entity/entity-manager.d.ts +66 -66
  113. package/dist/entity/entity-manager.js +4 -4
  114. package/dist/entity/entity-template-cone.js +2 -3
  115. package/dist/entity/entity.js +21 -18
  116. package/dist/env.js +1 -2
  117. package/dist/exceptions/error-handler.d.ts.map +1 -1
  118. package/dist/exceptions/error-handler.js +2 -3
  119. package/dist/exceptions/so-exceptions.js +2 -3
  120. package/dist/filter/index.js +3 -2
  121. package/dist/filter/types.js +1 -2
  122. package/dist/filter/utils.js +7 -6
  123. package/dist/index.d.ts +1 -0
  124. package/dist/index.d.ts.map +1 -1
  125. package/dist/index.js +42 -6
  126. package/dist/logger/category.js +2 -3
  127. package/dist/logger/configure.js +5 -5
  128. package/dist/migration/code-generation.js +23 -16
  129. package/dist/migration/index-where-predicate.js +2 -3
  130. package/dist/migration/migration-set.js +2 -3
  131. package/dist/migration/migrator.js +2 -3
  132. package/dist/migration/postgresql-schema-reader.js +6 -9
  133. package/dist/migration/slack-confirm.js +2 -3
  134. package/dist/naite/naite-reporter.js +2 -3
  135. package/dist/naite/naite.js +10 -8
  136. package/dist/runtime-type.js +2 -3
  137. package/dist/ssr/hmr-opt-out.js +1 -2
  138. package/dist/ssr/index.js +7 -4
  139. package/dist/ssr/registry.js +1 -2
  140. package/dist/ssr/renderer.js +2 -3
  141. package/dist/storage/base-file.js +1 -2
  142. package/dist/storage/buffered-file.js +2 -3
  143. package/dist/storage/drivers.js +1 -2
  144. package/dist/storage/index.js +2 -3
  145. package/dist/storage/s3-driver.js +1 -2
  146. package/dist/storage/storage-manager.js +2 -3
  147. package/dist/storage/uploaded-file.js +2 -3
  148. package/dist/stream/__tests__/ws-test-helpers.d.ts +108 -0
  149. package/dist/stream/__tests__/ws-test-helpers.d.ts.map +1 -0
  150. package/dist/stream/__tests__/ws-test-helpers.js +259 -0
  151. package/dist/stream/index.d.ts +1 -0
  152. package/dist/stream/index.d.ts.map +1 -1
  153. package/dist/stream/index.js +15 -4
  154. package/dist/stream/sse.js +1 -2
  155. package/dist/stream/ws-audience-resolver.d.ts +8 -5
  156. package/dist/stream/ws-audience-resolver.d.ts.map +1 -1
  157. package/dist/stream/ws-audience-resolver.js +17 -15
  158. package/dist/stream/ws-audience.js +1 -2
  159. package/dist/stream/ws-cluster-bus.d.ts +9 -4
  160. package/dist/stream/ws-cluster-bus.d.ts.map +1 -1
  161. package/dist/stream/ws-cluster-bus.js +2 -3
  162. package/dist/stream/ws-core.d.ts +10 -0
  163. package/dist/stream/ws-core.d.ts.map +1 -1
  164. package/dist/stream/ws-delivery.d.ts +30 -4
  165. package/dist/stream/ws-delivery.d.ts.map +1 -1
  166. package/dist/stream/ws-delivery.js +200 -54
  167. package/dist/stream/ws-frame.d.ts +15 -0
  168. package/dist/stream/ws-frame.d.ts.map +1 -0
  169. package/dist/stream/ws-frame.js +28 -0
  170. package/dist/stream/ws-local-connection-store.d.ts +4 -4
  171. package/dist/stream/ws-local-connection-store.d.ts.map +1 -1
  172. package/dist/stream/ws-local-connection-store.js +3 -4
  173. package/dist/stream/ws-presence-index.d.ts +36 -0
  174. package/dist/stream/ws-presence-index.d.ts.map +1 -0
  175. package/dist/stream/ws-presence-index.js +176 -0
  176. package/dist/stream/ws-presence-store.d.ts +39 -39
  177. package/dist/stream/ws-presence-store.d.ts.map +1 -1
  178. package/dist/stream/ws-presence-store.js +23 -154
  179. package/dist/stream/ws-registry.d.ts +45 -17
  180. package/dist/stream/ws-registry.d.ts.map +1 -1
  181. package/dist/stream/ws-registry.js +170 -62
  182. package/dist/stream/ws-session-id.d.ts +14 -0
  183. package/dist/stream/ws-session-id.d.ts.map +1 -0
  184. package/dist/stream/ws-session-id.js +36 -0
  185. package/dist/stream/ws-telemetry-memory.js +3 -4
  186. package/dist/stream/ws-telemetry-trace.js +1 -2
  187. package/dist/stream/ws-telemetry.js +2 -3
  188. package/dist/stream/ws.d.ts +20 -10
  189. package/dist/stream/ws.d.ts.map +1 -1
  190. package/dist/stream/ws.js +60 -38
  191. package/dist/syncer/api-parser.js +2 -3
  192. package/dist/syncer/checksum.js +4 -4
  193. package/dist/syncer/code-generator.js +4 -4
  194. package/dist/syncer/entity-operations.js +2 -3
  195. package/dist/syncer/event-batcher.js +1 -2
  196. package/dist/syncer/file-copy.js +2 -3
  197. package/dist/syncer/file-patterns.js +2 -3
  198. package/dist/syncer/file-tracking.js +2 -3
  199. package/dist/syncer/filesystem-dependencies.js +1 -2
  200. package/dist/syncer/index.js +8 -2
  201. package/dist/syncer/module-loader.js +8 -6
  202. package/dist/syncer/syncer-actions.js +8 -7
  203. package/dist/syncer/syncer.js +13 -8
  204. package/dist/syncer/watcher.js +2 -3
  205. package/dist/tasks/decorator.js +1 -2
  206. package/dist/tasks/step-wrapper.js +2 -3
  207. package/dist/tasks/workflow-manager.js +2 -3
  208. package/dist/template/entity-converter.js +17 -16
  209. package/dist/template/helpers.js +2 -3
  210. package/dist/template/implementations/entity.template.js +2 -3
  211. package/dist/template/implementations/entry-server.template.js +2 -3
  212. package/dist/template/implementations/generated.template.js +2 -3
  213. package/dist/template/implementations/generated_http.template.js +6 -6
  214. package/dist/template/implementations/generated_sso.template.js +2 -3
  215. package/dist/template/implementations/http_validators.template.js +2 -3
  216. package/dist/template/implementations/init_types.template.js +2 -3
  217. package/dist/template/implementations/model.template.js +4 -4
  218. package/dist/template/implementations/model_test.template.js +2 -3
  219. package/dist/template/implementations/queries.template.js +2 -3
  220. package/dist/template/implementations/sd.template.js +2 -3
  221. package/dist/template/implementations/services.template.js +2 -3
  222. package/dist/template/implementations/view_form.template.js +12 -9
  223. package/dist/template/implementations/view_id_all_select.template.js +2 -3
  224. package/dist/template/implementations/view_list.template.d.ts.map +1 -1
  225. package/dist/template/implementations/view_list.template.js +19 -17
  226. package/dist/template/implementations/view_search_input.template.js +2 -3
  227. package/dist/template/index.js +9 -3
  228. package/dist/template/template-manager.js +2 -3
  229. package/dist/template/template-types.js +1 -2
  230. package/dist/template/template.js +2 -3
  231. package/dist/template/zod-converter.js +54 -47
  232. package/dist/testing/_relation-graph.js +2 -3
  233. package/dist/testing/bootstrap.js +2 -3
  234. package/dist/testing/data-explorer.js +2 -3
  235. package/dist/testing/dev-test-routes.js +2 -3
  236. package/dist/testing/dev-vitest-manager.js +15 -12
  237. package/dist/testing/faker-mappings.js +1 -2
  238. package/dist/testing/filesystem.js +2 -3
  239. package/dist/testing/fixture-generator.js +2 -3
  240. package/dist/testing/fixture-loader.js +1 -2
  241. package/dist/testing/fixture-manager.js +12 -9
  242. package/dist/testing/global-setup.js +5 -5
  243. package/dist/testing/index.js +6 -3
  244. package/dist/testing/naite-vitest-reporter.js +1 -2
  245. package/dist/testing/parallel-db-manager.js +2 -3
  246. package/dist/testing/vitest-helpers.js +2 -3
  247. package/dist/tooling/__tests__/helpers/sonamu-test-root.js +2 -3
  248. package/dist/tooling/cdd-service.js +1 -2
  249. package/dist/tooling/cli-tooling.js +1 -2
  250. package/dist/tooling/fixture-service.js +1 -2
  251. package/dist/tooling/index.js +1 -2
  252. package/dist/tooling/test-run-client.js +2 -3
  253. package/dist/tsdown.api.config.js +1 -2
  254. package/dist/types/types.js +2 -3
  255. package/dist/ui/ai-api.d.ts.map +1 -1
  256. package/dist/ui/ai-api.js +4 -5
  257. package/dist/ui/ai-client.d.ts +2 -2
  258. package/dist/ui/ai-client.d.ts.map +1 -1
  259. package/dist/ui/ai-client.js +9 -8
  260. package/dist/ui/api.d.ts.map +1 -1
  261. package/dist/ui/api.js +11 -8
  262. package/dist/ui/async-route.js +1 -2
  263. package/dist/ui/cdd-service.js +22 -21
  264. package/dist/ui/migrations-api.js +6 -5
  265. package/dist/ui-web/assets/index-QDMyy31K.js +180 -0
  266. package/dist/ui-web/assets/index-dV7akMFB.css +1 -0
  267. package/dist/ui-web/index.html +2 -2
  268. package/dist/utils/async-utils.js +1 -2
  269. package/dist/utils/class-name.js +1 -2
  270. package/dist/utils/console-util.js +1 -2
  271. package/dist/utils/controller.js +2 -3
  272. package/dist/utils/esm-utils.js +1 -2
  273. package/dist/utils/fs-utils.js +2 -3
  274. package/dist/utils/lodash-able.js +1 -2
  275. package/dist/utils/model.js +1 -2
  276. package/dist/utils/object-utils.js +2 -3
  277. package/dist/utils/path-utils.js +2 -3
  278. package/dist/utils/process-utils.js +2 -3
  279. package/dist/utils/runtime-value.js +1 -2
  280. package/dist/utils/sql-parser.js +2 -3
  281. package/dist/utils/type-utils.js +8 -8
  282. package/dist/utils/utils.js +2 -3
  283. package/dist/utils/zod-error.js +2 -3
  284. package/dist/vector/chunking.js +1 -2
  285. package/dist/vector/config.js +1 -2
  286. package/dist/vector/embedding.js +4 -4
  287. package/package.json +63 -63
  288. package/src/ai/agents/agent.ts +1 -1
  289. package/src/api/__tests__/sonamu.plugins.test.ts +11 -1
  290. package/src/api/__tests__/sonamu.websocket-routes.test.ts +234 -0
  291. package/src/api/__tests__/sonamu.websocket.test.ts +241 -24
  292. package/src/api/config.ts +1 -1
  293. package/src/api/context.ts +2 -8
  294. package/src/api/sonamu.ts +46 -28
  295. package/src/auth/audit-log/plugin.ts +2 -0
  296. package/src/compress/types.ts +1 -1
  297. package/src/exceptions/error-handler.ts +3 -2
  298. package/src/index.ts +1 -0
  299. package/src/stream/__tests__/ws-contracts.test.ts +483 -265
  300. package/src/stream/__tests__/ws-multinode.test.ts +326 -0
  301. package/src/stream/__tests__/ws-registry-async.test.ts +855 -0
  302. package/src/stream/__tests__/ws-test-helpers.ts +360 -0
  303. package/src/stream/__tests__/ws.test.ts +342 -145
  304. package/src/stream/index.ts +1 -0
  305. package/src/stream/ws-audience-resolver.ts +21 -19
  306. package/src/stream/ws-cluster-bus.ts +12 -4
  307. package/src/stream/ws-core.ts +14 -0
  308. package/src/stream/ws-delivery.ts +321 -97
  309. package/src/stream/ws-frame.ts +39 -0
  310. package/src/stream/ws-local-connection-store.ts +10 -9
  311. package/src/stream/ws-presence-index.ts +273 -0
  312. package/src/stream/ws-presence-store.ts +64 -284
  313. package/src/stream/ws-registry.ts +272 -85
  314. package/src/stream/ws-session-id.ts +39 -0
  315. package/src/stream/ws.ts +91 -50
  316. package/src/template/implementations/model.template.ts +3 -3
  317. package/src/template/implementations/view_list.template.ts +12 -13
  318. package/src/ui/ai-api.ts +4 -2
  319. package/src/ui/ai-client.ts +2 -2
  320. package/src/ui/api.ts +58 -62
  321. package/dist/ui-web/assets/index-BMdIyKXS.css +0 -1
  322. package/dist/ui-web/assets/index-LpE8u19O.js +0 -178
@@ -1,46 +1,74 @@
1
+ import { type z } from "zod";
1
2
  import { type WebSocketAudience as WebSocketAudienceSpec } from "./ws-audience";
2
- import { WebSocketAudienceResolver } from "./ws-audience-resolver";
3
3
  import { type WebSocketClusterBus } from "./ws-cluster-bus";
4
- import { type ManagedWebSocketConnection, type WebSocketRegistryStats, type WebSocketRoomId, type WebSocketUserId } from "./ws-core";
5
- import { WebSocketDeliveryEngine } from "./ws-delivery";
4
+ import { type ManagedWebSocketConnection, type WebSocketFrameConnection, type WebSocketRegistryStats, type WebSocketRoomId, type WebSocketUserId } from "./ws-core";
5
+ import { type WebSocketRouteDefinition } from "./ws-delivery";
6
6
  import { WebSocketLocalConnectionStore } from "./ws-local-connection-store";
7
7
  import { type WebSocketPresenceStore, type WebSocketSessionPresence } from "./ws-presence-store";
8
8
  import { type WebSocketTelemetryController } from "./ws-telemetry";
9
9
  export type { ManagedWebSocketConnection, WebSocketRegistryStats, WebSocketRoomId, WebSocketUserId, };
10
10
  export type WebSocketConnectionMeta = WebSocketSessionPresence;
11
+ export type WebSocketRoomMemberCount = {
12
+ count: number;
13
+ partial: boolean;
14
+ };
11
15
  export type WebSocketRegistryOptions = {
12
16
  nodeId: string;
13
17
  presenceStore?: WebSocketPresenceStore;
14
18
  clusterBus?: WebSocketClusterBus;
15
19
  telemetryController?: WebSocketTelemetryController;
20
+ drainTimeoutMs?: number;
16
21
  };
22
+ /**
23
+ * 노드 로컬 presence를 메모리에 소유하고, 설정된 store에는 세션별 순서대로 replica를 반영한다.
24
+ * mutation과 publish는 호출 시점에 메모리를 동기로 갱신하고, store 실패는 telemetry로만 기록하며 reject하지 않는다.
25
+ */
17
26
  export declare class WebSocketRegistry {
18
27
  readonly nodeId: string;
19
28
  readonly localConnections: WebSocketLocalConnectionStore;
20
29
  readonly presenceStore: WebSocketPresenceStore;
21
30
  readonly clusterBus: WebSocketClusterBus;
22
- readonly audienceResolver: WebSocketAudienceResolver;
23
- readonly deliveryEngine: WebSocketDeliveryEngine;
24
31
  readonly telemetryController: WebSocketTelemetryController;
32
+ private readonly memory;
33
+ private readonly deliveryEngine;
34
+ private readonly drainTimeoutMs;
35
+ private readonly mutationQueues;
25
36
  constructor(options: WebSocketRegistryOptions);
26
- register(connection: ManagedWebSocketConnection, active?: boolean): WebSocketConnectionMeta;
27
- activate(connectionId: string): void;
28
- unregister(connectionId: string): void;
37
+ registerRoute(routeKey: string, outEvents: z.ZodObject): void;
38
+ replaceRoutes(routes: WebSocketRouteDefinition[]): void;
39
+ /**
40
+ * 연결을 메모리에 즉시 등록하고 store.register가 끝나면 resolve한다.
41
+ * store가 실패해도 메모리 전용 등록으로 resolve한다.
42
+ */
43
+ register(connection: WebSocketFrameConnection, active?: boolean): Promise<WebSocketConnectionMeta>;
44
+ activate(connectionId: string): Promise<void>;
45
+ unregister(connectionId: string): Promise<void>;
29
46
  touch(connectionId: string): void;
30
- setUserId(connectionId: string, userId: WebSocketUserId): void;
31
- clearUserId(connectionId: string): void;
32
- join(connectionId: string, roomId: WebSocketRoomId): void;
33
- leave(connectionId: string, roomId: WebSocketRoomId): void;
34
- private emitMutationSkipped;
35
- broadcast<Data>(event: string, data: Data, namespace?: string): void;
36
- publishToRoom<Data>(roomId: WebSocketRoomId, event: string, data: Data, namespace?: string): void;
37
- publishToUser<Data>(userId: WebSocketUserId, event: string, data: Data, namespace?: string): void;
38
- publishToAudience<Data>(audience: WebSocketAudienceSpec, event: string, data: Data): void;
47
+ setUserId(connectionId: string, userId: WebSocketUserId): Promise<void>;
48
+ clearUserId(connectionId: string): Promise<void>;
49
+ join(connectionId: string, roomId: WebSocketRoomId): Promise<void>;
50
+ leave(connectionId: string, roomId: WebSocketRoomId): Promise<void>;
51
+ broadcast<Data>(event: string, data: Data, namespace?: string): Promise<void>;
52
+ publishToRoom<Data>(roomId: WebSocketRoomId, event: string, data: Data, namespace?: string): Promise<void>;
53
+ publishToUser<Data>(userId: WebSocketUserId, event: string, data: Data, namespace?: string): Promise<void>;
54
+ publishToAudience<Data>(audience: WebSocketAudienceSpec, event: string, data: Data): Promise<void>;
39
55
  getConnection(connectionId: string): WebSocketConnectionMeta | undefined;
40
56
  getConnectionCount(namespace?: string): number;
41
57
  getRoomMembers(roomId: WebSocketRoomId, namespace?: string): WebSocketConnectionMeta[];
42
58
  getStats(): WebSocketRegistryStats;
59
+ /**
60
+ * 클러스터 전체의 활성 방 인원수. store가 실패하면 노드 로컬 수에 partial: true를 붙여 반환하며 reject하지 않는다.
61
+ */
62
+ getRoomMemberCount(roomId: WebSocketRoomId, namespace?: string): Promise<WebSocketRoomMemberCount>;
43
63
  closeAll(code?: number, reason?: string): void;
64
+ /**
65
+ * 로컬 연결을 닫고, 진행 중인 store mutation과 publish를 drainTimeoutMs까지 기다린 뒤 store와 bus를 종료한다.
66
+ */
44
67
  shutdown(code?: number, reason?: string): Promise<void>;
68
+ /**
69
+ * 세션별 큐 뒤에 store 호출을 이어 붙인다. 실패는 telemetry로만 기록하고, 반환한 promise는 reject하지 않는다.
70
+ */
71
+ private enqueueStoreMutation;
72
+ private emitMutationSkipped;
45
73
  }
46
74
  //# sourceMappingURL=ws-registry.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"ws-registry.d.ts","sourceRoot":"","sources":["../../src/stream/ws-registry.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"ws-registry.d.ts","sourceRoot":"","sources":["../../src/stream/ws-registry.ts"],"names":[],"mappings":"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"}
@@ -1,13 +1,24 @@
1
1
  import { __esmMin } from "../_virtual/_rolldown/runtime.js";
2
2
  import { init_runtime_value, isFunctionValue } from "../utils/runtime-value.js";
3
+ import { init_runtime_type, runtimeTypeOf } from "../runtime-type.js";
3
4
  import { WebSocketAudience, init_ws_audience } from "./ws-audience.js";
4
5
  import { WebSocketAudienceResolver, init_ws_audience_resolver } from "./ws-audience-resolver.js";
5
6
  import { NoopWebSocketClusterBus, init_ws_cluster_bus } from "./ws-cluster-bus.js";
6
7
  import { NoopWebSocketTelemetryController, init_ws_telemetry } from "./ws-telemetry.js";
7
8
  import { WebSocketDeliveryEngine, init_ws_delivery } from "./ws-delivery.js";
8
9
  import { WebSocketLocalConnectionStore, init_ws_local_connection_store } from "./ws-local-connection-store.js";
10
+ import { WebSocketPresenceIndex, init_ws_presence_index } from "./ws-presence-index.js";
9
11
  import { InMemoryWebSocketPresenceStore, init_ws_presence_store } from "./ws-presence-store.js";
10
- //#region src/stream/ws-registry.ts
12
+ // src/stream/ws-registry.ts
13
+ function callStore(call) {
14
+ return Promise.resolve().then(call);
15
+ }
16
+ function assertMatchingNodeId(source, sourceNodeId, nodeId) {
17
+ if (sourceNodeId !== void 0 && sourceNodeId !== nodeId) throw new Error(`WebSocket ${source}.nodeId (${sourceNodeId}) must match the runtime nodeId (${nodeId}).`);
18
+ }
19
+ function describeError(cause) {
20
+ return cause instanceof Error ? cause.name : runtimeTypeOf(cause);
21
+ }
11
22
  function getConnectionTelemetryContext(connection) {
12
23
  if (!connection || !isTelemetryContextProvider(connection)) return {};
13
24
  return connection.getTelemetryContext();
@@ -18,42 +29,50 @@ function formatUserId(userId) {
18
29
  function isTelemetryContextProvider(connection) {
19
30
  return "getTelemetryContext" in connection && isFunctionValue(connection.getTelemetryContext);
20
31
  }
21
- var WebSocketRegistry;
32
+ var DEFAULT_DRAIN_TIMEOUT_MS, WebSocketRegistry;
22
33
  var init_ws_registry = __esmMin((() => {
34
+ init_runtime_type();
23
35
  init_runtime_value();
24
36
  init_ws_audience();
25
37
  init_ws_audience_resolver();
26
38
  init_ws_cluster_bus();
27
39
  init_ws_delivery();
28
40
  init_ws_local_connection_store();
41
+ init_ws_presence_index();
29
42
  init_ws_presence_store();
30
43
  init_ws_telemetry();
44
+ DEFAULT_DRAIN_TIMEOUT_MS = 5e3;
31
45
  WebSocketRegistry = class {
32
46
  nodeId;
33
47
  localConnections = new WebSocketLocalConnectionStore();
34
48
  presenceStore;
35
49
  clusterBus;
36
- audienceResolver;
37
- deliveryEngine;
38
50
  telemetryController;
51
+ memory = new WebSocketPresenceIndex();
52
+ deliveryEngine;
53
+ drainTimeoutMs;
54
+ mutationQueues = /* @__PURE__ */ new Map();
39
55
  constructor(options) {
40
56
  this.nodeId = options.nodeId;
41
57
  this.presenceStore = options.presenceStore ?? new InMemoryWebSocketPresenceStore();
42
58
  this.clusterBus = options.clusterBus ?? new NoopWebSocketClusterBus();
59
+ this.drainTimeoutMs = options.drainTimeoutMs ?? DEFAULT_DRAIN_TIMEOUT_MS;
60
+ assertMatchingNodeId("presenceStore", this.presenceStore.nodeId, this.nodeId);
61
+ assertMatchingNodeId("clusterBus", this.clusterBus.nodeId, this.nodeId);
43
62
  this.telemetryController = options.telemetryController ?? new NoopWebSocketTelemetryController();
44
- this.audienceResolver = new WebSocketAudienceResolver({
45
- nodeId: this.nodeId,
46
- presenceStore: this.presenceStore
47
- });
48
63
  this.deliveryEngine = new WebSocketDeliveryEngine({
49
64
  nodeId: this.nodeId,
50
65
  localConnections: this.localConnections,
51
- audienceResolver: this.audienceResolver,
66
+ resolveLocalSessionIds: (audience) => this.memory.queryAudience(audience).map((session) => session.sessionId),
67
+ audienceResolver: new WebSocketAudienceResolver({
68
+ nodeId: this.nodeId,
69
+ presenceStore: this.presenceStore
70
+ }),
52
71
  clusterBus: this.clusterBus,
53
72
  telemetryController: this.telemetryController
54
73
  });
55
74
  this.telemetryController.registerMetricSource({ collect: (_now) => {
56
- const stats = this.presenceStore.getStats();
75
+ const stats = this.memory.getStats();
57
76
  const localSnapshot = this.localConnections.getTelemetrySnapshot();
58
77
  return {
59
78
  activeConnections: stats.totalConnections,
@@ -65,14 +84,26 @@ var init_ws_registry = __esmMin((() => {
65
84
  };
66
85
  } });
67
86
  }
68
- register(connection, active = true) {
69
- this.localConnections.register(connection);
70
- const meta = this.presenceStore.register({
87
+ registerRoute(routeKey, outEvents) {
88
+ this.deliveryEngine.registerRoute(routeKey, outEvents);
89
+ }
90
+ replaceRoutes(routes) {
91
+ this.deliveryEngine.replaceRoutes(routes);
92
+ }
93
+ /**
94
+ * 연결을 메모리에 즉시 등록하고 store.register가 끝나면 resolve한다.
95
+ * store가 실패해도 메모리 전용 등록으로 resolve한다.
96
+ */
97
+ async register(connection, active = true) {
98
+ const registration = {
71
99
  sessionId: connection.id,
72
100
  nodeId: this.nodeId,
73
101
  namespace: connection.namespace,
74
- active
75
- });
102
+ active,
103
+ connectedAt: /* @__PURE__ */ new Date()
104
+ };
105
+ this.localConnections.register(connection);
106
+ this.memory.register(registration);
76
107
  this.telemetryController.emit({
77
108
  name: "ws.connection.registered",
78
109
  level: "info",
@@ -92,14 +123,19 @@ var init_ws_registry = __esmMin((() => {
92
123
  },
93
124
  userId: connection.userId
94
125
  });
95
- return meta;
126
+ await this.enqueueStoreMutation(connection.id, "register", () => this.presenceStore.register(registration));
127
+ return this.memory.getConnection(connection.id) ?? {
128
+ ...registration,
129
+ rooms: /* @__PURE__ */ new Set(),
130
+ lastSeenAt: registration.connectedAt
131
+ };
96
132
  }
97
133
  activate(connectionId) {
98
- if (!this.presenceStore.getConnection(connectionId)) {
134
+ if (!this.memory.has(connectionId)) {
99
135
  this.emitMutationSkipped(connectionId, { operation: "activate" });
100
- return;
136
+ return Promise.resolve();
101
137
  }
102
- this.presenceStore.activate(connectionId);
138
+ this.memory.activate(connectionId);
103
139
  const connection = this.localConnections.getConnection(connectionId);
104
140
  this.telemetryController.emit({
105
141
  name: "ws.connection.activated",
@@ -108,12 +144,13 @@ var init_ws_registry = __esmMin((() => {
108
144
  userId: connection?.userId,
109
145
  ...getConnectionTelemetryContext(connection)
110
146
  });
147
+ return this.enqueueStoreMutation(connectionId, "activate", () => this.presenceStore.activate(connectionId));
111
148
  }
112
149
  unregister(connectionId) {
113
150
  const connection = this.localConnections.getConnection(connectionId);
114
151
  const telemetryContext = getConnectionTelemetryContext(connection);
115
- const userId = connection?.userId ?? formatUserId(this.presenceStore.getConnection(connectionId)?.userId);
116
- const meta = this.presenceStore.unregister(connectionId);
152
+ const meta = this.memory.unregister(connectionId);
153
+ const userId = connection?.userId ?? formatUserId(meta?.userId);
117
154
  this.localConnections.unregister(connectionId);
118
155
  this.telemetryController.emit({
119
156
  name: "ws.connection.unregistered",
@@ -123,7 +160,8 @@ var init_ws_registry = __esmMin((() => {
123
160
  userId,
124
161
  ...telemetryContext
125
162
  });
126
- if (meta) this.telemetryController.recordMetric({
163
+ if (!meta) return Promise.resolve();
164
+ this.telemetryController.recordMetric({
127
165
  name: "sonamu.ws.connections",
128
166
  kind: "counter",
129
167
  value: 1,
@@ -134,38 +172,40 @@ var init_ws_registry = __esmMin((() => {
134
172
  },
135
173
  userId
136
174
  });
175
+ return this.enqueueStoreMutation(connectionId, "unregister", () => this.presenceStore.unregister(connectionId));
137
176
  }
138
177
  touch(connectionId) {
139
- this.presenceStore.touch(connectionId);
178
+ this.memory.touch(connectionId);
140
179
  }
141
180
  setUserId(connectionId, userId) {
142
- if (!this.presenceStore.getConnection(connectionId)) {
181
+ const meta = this.memory.getConnection(connectionId);
182
+ if (!meta) {
143
183
  this.emitMutationSkipped(connectionId, {
144
184
  operation: "setUserId",
145
185
  userId
146
186
  });
147
- return;
187
+ return Promise.resolve();
148
188
  }
149
- this.presenceStore.setUserId(connectionId, userId);
150
- const meta = this.presenceStore.getConnection(connectionId);
189
+ this.memory.setUserId(connectionId, userId);
151
190
  this.telemetryController.emit({
152
191
  name: "ws.user.bound",
153
192
  level: "debug",
154
193
  connectionId,
155
- namespace: meta?.namespace,
194
+ namespace: meta.namespace,
156
195
  userId: String(userId),
157
196
  detail: { userId },
158
197
  ...getConnectionTelemetryContext(this.localConnections.getConnection(connectionId))
159
198
  });
199
+ return this.enqueueStoreMutation(connectionId, "setUserId", () => this.presenceStore.setUserId(connectionId, userId));
160
200
  }
161
201
  clearUserId(connectionId) {
162
- const meta = this.presenceStore.getConnection(connectionId);
202
+ const meta = this.memory.getConnection(connectionId);
163
203
  if (!meta) {
164
204
  this.emitMutationSkipped(connectionId, { operation: "clearUserId" });
165
- return;
205
+ return Promise.resolve();
166
206
  }
167
207
  const userId = meta.userId;
168
- this.presenceStore.clearUserId(connectionId);
208
+ this.memory.clearUserId(connectionId);
169
209
  this.telemetryController.emit({
170
210
  name: "ws.user.cleared",
171
211
  level: "debug",
@@ -175,38 +215,40 @@ var init_ws_registry = __esmMin((() => {
175
215
  detail: userId !== void 0 ? { userId } : void 0,
176
216
  ...getConnectionTelemetryContext(this.localConnections.getConnection(connectionId))
177
217
  });
218
+ return this.enqueueStoreMutation(connectionId, "clearUserId", () => this.presenceStore.clearUserId(connectionId));
178
219
  }
179
220
  join(connectionId, roomId) {
180
- if (!this.presenceStore.getConnection(connectionId)) {
221
+ const meta = this.memory.getConnection(connectionId);
222
+ if (!meta) {
181
223
  this.emitMutationSkipped(connectionId, {
182
224
  operation: "join",
183
225
  roomId
184
226
  });
185
- return;
227
+ return Promise.resolve();
186
228
  }
187
- this.presenceStore.join(connectionId, roomId);
188
- const meta = this.presenceStore.getConnection(connectionId);
229
+ this.memory.join(connectionId, roomId);
189
230
  const connection = this.localConnections.getConnection(connectionId);
190
231
  this.telemetryController.emit({
191
232
  name: "ws.room.joined",
192
233
  level: "debug",
193
234
  connectionId,
194
- namespace: meta?.namespace,
195
- userId: connection?.userId ?? formatUserId(meta?.userId),
235
+ namespace: meta.namespace,
236
+ userId: connection?.userId ?? formatUserId(meta.userId),
196
237
  detail: { roomId },
197
238
  ...getConnectionTelemetryContext(connection)
198
239
  });
240
+ return this.enqueueStoreMutation(connectionId, "join", () => this.presenceStore.join(connectionId, roomId));
199
241
  }
200
242
  leave(connectionId, roomId) {
201
- const meta = this.presenceStore.getConnection(connectionId);
243
+ const meta = this.memory.getConnection(connectionId);
202
244
  if (!meta) {
203
245
  this.emitMutationSkipped(connectionId, {
204
246
  operation: "leave",
205
247
  roomId
206
248
  });
207
- return;
249
+ return Promise.resolve();
208
250
  }
209
- this.presenceStore.leave(connectionId, roomId);
251
+ this.memory.leave(connectionId, roomId);
210
252
  const connection = this.localConnections.getConnection(connectionId);
211
253
  this.telemetryController.emit({
212
254
  name: "ws.room.left",
@@ -217,53 +259,119 @@ var init_ws_registry = __esmMin((() => {
217
259
  detail: { roomId },
218
260
  ...getConnectionTelemetryContext(connection)
219
261
  });
220
- }
221
- emitMutationSkipped(connectionId, detail) {
222
- this.telemetryController.emit({
223
- name: "ws.registry.mutation.skipped",
224
- level: "debug",
225
- connectionId,
226
- detail: {
227
- ...detail,
228
- reason: "connectionMissing"
229
- }
230
- });
262
+ return this.enqueueStoreMutation(connectionId, "leave", () => this.presenceStore.leave(connectionId, roomId));
231
263
  }
232
264
  broadcast(event, data, namespace) {
233
- this.publishToAudience(WebSocketAudience.all(namespace), event, data);
265
+ return this.publishToAudience(WebSocketAudience.all(namespace), event, data);
234
266
  }
235
267
  publishToRoom(roomId, event, data, namespace) {
236
- this.publishToAudience(WebSocketAudience.room(roomId, namespace), event, data);
268
+ return this.publishToAudience(WebSocketAudience.room(roomId, namespace), event, data);
237
269
  }
238
270
  publishToUser(userId, event, data, namespace) {
239
- this.publishToAudience(WebSocketAudience.user(userId, namespace), event, data);
271
+ return this.publishToAudience(WebSocketAudience.user(userId, namespace), event, data);
240
272
  }
241
273
  publishToAudience(audience, event, data) {
242
- this.deliveryEngine.publishToAudience(audience, event, data);
274
+ return this.deliveryEngine.publishToAudience(audience, event, data);
243
275
  }
244
276
  getConnection(connectionId) {
245
- return this.presenceStore.getConnection(connectionId);
277
+ return this.memory.getConnection(connectionId);
246
278
  }
247
279
  getConnectionCount(namespace) {
248
- return this.presenceStore.getConnectionCount(namespace);
280
+ return this.memory.getConnectionCount(namespace);
249
281
  }
250
282
  getRoomMembers(roomId, namespace) {
251
- return this.presenceStore.getRoomMembers(roomId, namespace);
283
+ return this.memory.getRoomMembers(roomId, namespace);
252
284
  }
253
285
  getStats() {
254
- return this.presenceStore.getStats();
286
+ return this.memory.getStats();
287
+ }
288
+ /**
289
+ * 클러스터 전체의 활성 방 인원수. store가 실패하면 노드 로컬 수에 partial: true를 붙여 반환하며 reject하지 않는다.
290
+ */
291
+ async getRoomMemberCount(roomId, namespace) {
292
+ try {
293
+ return {
294
+ count: await callStore(() => this.presenceStore.getRoomMemberCount(roomId, namespace)),
295
+ partial: false
296
+ };
297
+ } catch (cause) {
298
+ this.telemetryController.emit({
299
+ name: "ws.registry.read.failed",
300
+ level: "warn",
301
+ detail: {
302
+ operation: "getRoomMemberCount",
303
+ roomId,
304
+ errorType: describeError(cause)
305
+ }
306
+ });
307
+ return {
308
+ count: this.memory.getRoomMemberCount(roomId, namespace),
309
+ partial: true
310
+ };
311
+ }
255
312
  }
256
313
  closeAll(code, reason) {
257
314
  this.localConnections.closeAll(code, reason);
258
315
  }
316
+ /**
317
+ * 로컬 연결을 닫고, 진행 중인 store mutation과 publish를 drainTimeoutMs까지 기다린 뒤 store와 bus를 종료한다.
318
+ */
259
319
  async shutdown(code, reason) {
260
320
  this.closeAll(code, reason);
261
- await this.deliveryEngine.shutdown();
321
+ let timer;
322
+ const deadline = new Promise((resolve) => {
323
+ timer = setTimeout(resolve, this.drainTimeoutMs);
324
+ });
325
+ try {
326
+ await Promise.race([Promise.all([...this.mutationQueues.values(), this.deliveryEngine.drainPublishes()]), deadline]);
327
+ } finally {
328
+ clearTimeout(timer);
329
+ }
330
+ try {
331
+ await callStore(() => this.presenceStore.shutdown?.());
332
+ } catch (cause) {
333
+ this.telemetryController.emit({
334
+ name: "ws.registry.shutdown.failed",
335
+ level: "error",
336
+ detail: { errorType: describeError(cause) }
337
+ });
338
+ }
339
+ await this.deliveryEngine.shutdown(Promise.resolve());
340
+ }
341
+ /**
342
+ * 세션별 큐 뒤에 store 호출을 이어 붙인다. 실패는 telemetry로만 기록하고, 반환한 promise는 reject하지 않는다.
343
+ */
344
+ enqueueStoreMutation(sessionId, operation, call) {
345
+ const next = (this.mutationQueues.get(sessionId) ?? Promise.resolve()).then(call).then(() => void 0, (cause) => {
346
+ this.telemetryController.emit({
347
+ name: "ws.registry.mutation.failed",
348
+ level: "warn",
349
+ connectionId: sessionId,
350
+ detail: {
351
+ operation,
352
+ errorType: describeError(cause)
353
+ }
354
+ });
355
+ }).then(() => {
356
+ if (this.mutationQueues.get(sessionId) === next) this.mutationQueues.delete(sessionId);
357
+ });
358
+ this.mutationQueues.set(sessionId, next);
359
+ return next;
360
+ }
361
+ emitMutationSkipped(connectionId, detail) {
362
+ this.telemetryController.emit({
363
+ name: "ws.registry.mutation.skipped",
364
+ level: "debug",
365
+ connectionId,
366
+ detail: {
367
+ ...detail,
368
+ reason: "connectionMissing"
369
+ }
370
+ });
262
371
  }
263
372
  };
264
373
  }));
265
- //#endregion
266
374
  init_ws_registry();
267
375
  export { WebSocketRegistry, init_ws_registry };
268
376
 
269
- //# sourceMappingURL=data:application/json;charset=utf-8;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoid3MtcmVnaXN0cnkuanMiLCJuYW1lcyI6W10sInNvdXJjZXMiOlsiLi4vLi4vc3JjL3N0cmVhbS93cy1yZWdpc3RyeS50cyJdLCJzb3VyY2VzQ29udGVudCI6WyJpbXBvcnQgeyBpc0Z1bmN0aW9uVmFsdWUgfSBmcm9tIFwiLi4vdXRpbHMvcnVudGltZS12YWx1ZVwiO1xuaW1wb3J0IHsgV2ViU29ja2V0QXVkaWVuY2UsIHR5cGUgV2ViU29ja2V0QXVkaWVuY2UgYXMgV2ViU29ja2V0QXVkaWVuY2VTcGVjIH0gZnJvbSBcIi4vd3MtYXVkaWVuY2VcIjtcbmltcG9ydCB7IFdlYlNvY2tldEF1ZGllbmNlUmVzb2x2ZXIgfSBmcm9tIFwiLi93cy1hdWRpZW5jZS1yZXNvbHZlclwiO1xuaW1wb3J0IHsgTm9vcFdlYlNvY2tldENsdXN0ZXJCdXMsIHR5cGUgV2ViU29ja2V0Q2x1c3RlckJ1cyB9IGZyb20gXCIuL3dzLWNsdXN0ZXItYnVzXCI7XG5pbXBvcnQge1xuICB0eXBlIE1hbmFnZWRXZWJTb2NrZXRDb25uZWN0aW9uLFxuICB0eXBlIFdlYlNvY2tldFJlZ2lzdHJ5U3RhdHMsXG4gIHR5cGUgV2ViU29ja2V0Um9vbUlkLFxuICB0eXBlIFdlYlNvY2tldFVzZXJJZCxcbn0gZnJvbSBcIi4vd3MtY29yZVwiO1xuaW1wb3J0IHsgV2ViU29ja2V0RGVsaXZlcnlFbmdpbmUgfSBmcm9tIFwiLi93cy1kZWxpdmVyeVwiO1xuaW1wb3J0IHsgV2ViU29ja2V0TG9jYWxDb25uZWN0aW9uU3RvcmUgfSBmcm9tIFwiLi93cy1sb2NhbC1jb25uZWN0aW9uLXN0b3JlXCI7XG5pbXBvcnQge1xuICBJbk1lbW9yeVdlYlNvY2tldFByZXNlbmNlU3RvcmUsXG4gIHR5cGUgV2ViU29ja2V0UHJlc2VuY2VTdG9yZSxcbiAgdHlwZSBXZWJTb2NrZXRTZXNzaW9uUHJlc2VuY2UsXG59IGZyb20gXCIuL3dzLXByZXNlbmNlLXN0b3JlXCI7XG5pbXBvcnQge1xuICB0eXBlIFRlbGVtZXRyeUNvbnRleHRQcm92aWRlcixcbiAgTm9vcFdlYlNvY2tldFRlbGVtZXRyeUNvbnRyb2xsZXIsXG4gIHR5cGUgV2ViU29ja2V0VGVsZW1ldHJ5Q29ubmVjdGlvbkNvbnRleHQsXG4gIHR5cGUgV2ViU29ja2V0VGVsZW1ldHJ5Q29udHJvbGxlcixcbn0gZnJvbSBcIi4vd3MtdGVsZW1ldHJ5XCI7XG5cbmV4cG9ydCB0eXBlIHtcbiAgTWFuYWdlZFdlYlNvY2tldENvbm5lY3Rpb24sXG4gIFdlYlNvY2tldFJlZ2lzdHJ5U3RhdHMsXG4gIFdlYlNvY2tldFJvb21JZCxcbiAgV2ViU29ja2V0VXNlcklkLFxufTtcbmV4cG9ydCB0eXBlIFdlYlNvY2tldENvbm5lY3Rpb25NZXRhID0gV2ViU29ja2V0U2Vzc2lvblByZXNlbmNlO1xuXG5leHBvcnQgdHlwZSBXZWJTb2NrZXRSZWdpc3RyeU9wdGlvbnMgPSB7XG4gIC8vIOu2hOyCsCDtmZjqsr3sl5DshJwg64W465OcIOqwhCDrnbzsmrDtjIUg7Iud67OE7J6Q66GcIOyTsOydtOuvgOuhnCDrsJjrk5zsi5wg7Zi47LacIOy4oShXZWJTb2NrZXRSdW50aW1lKeyXkOyEnCDqsrDsoJXtlbQg64SY6ri064ukXG4gIG5vZGVJZDogc3RyaW5nO1xuICBwcmVzZW5jZVN0b3JlPzogV2ViU29ja2V0UHJlc2VuY2VTdG9yZTtcbiAgY2x1c3RlckJ1cz86IFdlYlNvY2tldENsdXN0ZXJCdXM7XG4gIHRlbGVtZXRyeUNvbnRyb2xsZXI/OiBXZWJTb2NrZXRUZWxlbWV0cnlDb250cm9sbGVyO1xufTtcblxuZXhwb3J0IGNsYXNzIFdlYlNvY2tldFJlZ2lzdHJ5IHtcbiAgcmVhZG9ubHkgbm9kZUlkOiBzdHJpbmc7XG4gIHJlYWRvbmx5IGxvY2FsQ29ubmVjdGlvbnMgPSBuZXcgV2ViU29ja2V0TG9jYWxDb25uZWN0aW9uU3RvcmUoKTtcbiAgcmVhZG9ubHkgcHJlc2VuY2VTdG9yZTogV2ViU29ja2V0UHJlc2VuY2VTdG9yZTtcbiAgcmVhZG9ubHkgY2x1c3RlckJ1czogV2ViU29ja2V0Q2x1c3RlckJ1cztcbiAgcmVhZG9ubHkgYXVkaWVuY2VSZXNvbHZlcjogV2ViU29ja2V0QXVkaWVuY2VSZXNvbHZlcjtcbiAgcmVhZG9ubHkgZGVsaXZlcnlFbmdpbmU6IFdlYlNvY2tldERlbGl2ZXJ5RW5naW5lO1xuICByZWFkb25seSB0ZWxlbWV0cnlDb250cm9sbGVyOiBXZWJTb2NrZXRUZWxlbWV0cnlDb250cm9sbGVyO1xuXG4gIGNvbnN0cnVjdG9yKG9wdGlvbnM6IFdlYlNvY2tldFJlZ2lzdHJ5T3B0aW9ucykge1xuICAgIHRoaXMubm9kZUlkID0gb3B0aW9ucy5ub2RlSWQ7XG4gICAgdGhpcy5wcmVzZW5jZVN0b3JlID0gb3B0aW9ucy5wcmVzZW5jZVN0b3JlID8/IG5ldyBJbk1lbW9yeVdlYlNvY2tldFByZXNlbmNlU3RvcmUoKTtcbiAgICB0aGlzLmNsdXN0ZXJCdXMgPSBvcHRpb25zLmNsdXN0ZXJCdXMgPz8gbmV3IE5vb3BXZWJTb2NrZXRDbHVzdGVyQnVzKCk7XG4gICAgdGhpcy50ZWxlbWV0cnlDb250cm9sbGVyID1cbiAgICAgIG9wdGlvbnMudGVsZW1ldHJ5Q29udHJvbGxlciA/PyBuZXcgTm9vcFdlYlNvY2tldFRlbGVtZXRyeUNvbnRyb2xsZXIoKTtcbiAgICB0aGlzLmF1ZGllbmNlUmVzb2x2ZXIgPSBuZXcgV2ViU29ja2V0QXVkaWVuY2VSZXNvbHZlcih7XG4gICAgICBub2RlSWQ6IHRoaXMubm9kZUlkLFxuICAgICAgcHJlc2VuY2VTdG9yZTogdGhpcy5wcmVzZW5jZVN0b3JlLFxuICAgIH0pO1xuICAgIHRoaXMuZGVsaXZlcnlFbmdpbmUgPSBuZXcgV2ViU29ja2V0RGVsaXZlcnlFbmdpbmUoe1xuICAgICAgbm9kZUlkOiB0aGlzLm5vZGVJZCxcbiAgICAgIGxvY2FsQ29ubmVjdGlvbnM6IHRoaXMubG9jYWxDb25uZWN0aW9ucyxcbiAgICAgIGF1ZGllbmNlUmVzb2x2ZXI6IHRoaXMuYXVkaWVuY2VSZXNvbHZlcixcbiAgICAgIGNsdXN0ZXJCdXM6IHRoaXMuY2x1c3RlckJ1cyxcbiAgICAgIHRlbGVtZXRyeUNvbnRyb2xsZXI6IHRoaXMudGVsZW1ldHJ5Q29udHJvbGxlcixcbiAgICB9KTtcblxuICAgIHRoaXMudGVsZW1ldHJ5Q29udHJvbGxlci5yZWdpc3Rlck1ldHJpY1NvdXJjZSh7XG4gICAgICBjb2xsZWN0OiAoX25vdykgPT4ge1xuICAgICAgICBjb25zdCBzdGF0cyA9IHRoaXMucHJlc2VuY2VTdG9yZS5nZXRTdGF0cygpO1xuICAgICAgICBjb25zdCBsb2NhbFNuYXBzaG90ID0gdGhpcy5sb2NhbENvbm5lY3Rpb25zLmdldFRlbGVtZXRyeVNuYXBzaG90KCk7XG4gICAgICAgIHJldHVybiB7XG4gICAgICAgICAgYWN0aXZlQ29ubmVjdGlvbnM6IHN0YXRzLnRvdGFsQ29ubmVjdGlvbnMsXG4gICAgICAgICAgYWN0aXZlQ29ubmVjdGlvbnNCeU5hbWVzcGFjZTogc3RhdHMuYnlOYW1lc3BhY2UsXG4gICAgICAgICAgcm9vbUNvdW50OiBzdGF0cy50b3RhbFJvb21zLFxuICAgICAgICAgIHBlbmRpbmdJbmJvdW5kTWVzc2FnZXM6IGxvY2FsU25hcHNob3QucGVuZGluZ0luYm91bmRNZXNzYWdlcyxcbiAgICAgICAgICBwZW5kaW5nT3V0Ym91bmRNZXNzYWdlczogbG9jYWxTbmFwc2hvdC5wZW5kaW5nT3V0Ym91bmRNZXNzYWdlcyxcbiAgICAgICAgICBzb2NrZXRCdWZmZXJlZEJ5dGVzOiBsb2NhbFNuYXBzaG90LnNvY2tldEJ1ZmZlcmVkQnl0ZXMsXG4gICAgICAgIH07XG4gICAgICB9LFxuICAgIH0pO1xuICB9XG5cbiAgcmVnaXN0ZXIoXG4gICAgY29ubmVjdGlvbjogTWFuYWdlZFdlYlNvY2tldENvbm5lY3Rpb24sXG4gICAgYWN0aXZlOiBib29sZWFuID0gdHJ1ZSxcbiAgKTogV2ViU29ja2V0Q29ubmVjdGlvbk1ldGEge1xuICAgIHRoaXMubG9jYWxDb25uZWN0aW9ucy5yZWdpc3Rlcihjb25uZWN0aW9uKTtcbiAgICBjb25zdCBtZXRhID0gdGhpcy5wcmVzZW5jZVN0b3JlLnJlZ2lzdGVyKHtcbiAgICAgIHNlc3Npb25JZDogY29ubmVjdGlvbi5pZCxcbiAgICAgIG5vZGVJZDogdGhpcy5ub2RlSWQsXG4gICAgICBuYW1lc3BhY2U6IGNvbm5lY3Rpb24ubmFtZXNwYWNlLFxuICAgICAgYWN0aXZlLFxuICAgIH0pO1xuICAgIHRoaXMudGVsZW1ldHJ5Q29udHJvbGxlci5lbWl0KHtcbiAgICAgIG5hbWU6IFwid3MuY29ubmVjdGlvbi5yZWdpc3RlcmVkXCIsXG4gICAgICBsZXZlbDogXCJpbmZvXCIsXG4gICAgICBjb25uZWN0aW9uSWQ6IGNvbm5lY3Rpb24uaWQsXG4gICAgICBuYW1lc3BhY2U6IGNvbm5lY3Rpb24ubmFtZXNwYWNlLFxuICAgICAgdXNlcklkOiBjb25uZWN0aW9uLnVzZXJJZCxcbiAgICAgIC4uLmdldENvbm5lY3Rpb25UZWxlbWV0cnlDb250ZXh0KGNvbm5lY3Rpb24pLFxuICAgIH0pO1xuICAgIHRoaXMudGVsZW1ldHJ5Q29udHJvbGxlci5yZWNvcmRNZXRyaWMoe1xuICAgICAgbmFtZTogXCJzb25hbXUud3MuY29ubmVjdGlvbnNcIixcbiAgICAgIGtpbmQ6IFwiY291bnRlclwiLFxuICAgICAgdmFsdWU6IDEsXG4gICAgICB1bml0OiBcIjFcIixcbiAgICAgIHRhZ3M6IHsgb3V0Y29tZTogXCJyZWdpc3RlcmVkXCIsIG5hbWVzcGFjZTogY29ubmVjdGlvbi5uYW1lc3BhY2UgfSxcbiAgICAgIHVzZXJJZDogY29ubmVjdGlvbi51c2VySWQsXG4gICAgfSk7XG4gICAgcmV0dXJuIG1ldGE7XG4gIH1cblxuICBhY3RpdmF0ZShjb25uZWN0aW9uSWQ6IHN0cmluZyk6IHZvaWQge1xuICAgIGlmICghdGhpcy5wcmVzZW5jZVN0b3JlLmdldENvbm5lY3Rpb24oY29ubmVjdGlvbklkKSkge1xuICAgICAgdGhpcy5lbWl0TXV0YXRpb25Ta2lwcGVkKGNvbm5lY3Rpb25JZCwgeyBvcGVyYXRpb246IFwiYWN0aXZhdGVcIiB9KTtcbiAgICAgIHJldHVybjtcbiAgICB9XG5cbiAgICB0aGlzLnByZXNlbmNlU3RvcmUuYWN0aXZhdGUoY29ubmVjdGlvbklkKTtcbiAgICBjb25zdCBjb25uZWN0aW9uID0gdGhpcy5sb2NhbENvbm5lY3Rpb25zLmdldENvbm5lY3Rpb24oY29ubmVjdGlvbklkKTtcbiAgICB0aGlzLnRlbGVtZXRyeUNvbnRyb2xsZXIuZW1pdCh7XG4gICAgICBuYW1lOiBcIndzLmNvbm5lY3Rpb24uYWN0aXZhdGVkXCIsXG4gICAgICBsZXZlbDogXCJpbmZvXCIsXG4gICAgICBjb25uZWN0aW9uSWQsXG4gICAgICB1c2VySWQ6IGNvbm5lY3Rpb24/LnVzZXJJZCxcbiAgICAgIC4uLmdldENvbm5lY3Rpb25UZWxlbWV0cnlDb250ZXh0KGNvbm5lY3Rpb24pLFxuICAgIH0pO1xuICB9XG5cbiAgdW5yZWdpc3Rlcihjb25uZWN0aW9uSWQ6IHN0cmluZyk6IHZvaWQge1xuICAgIGNvbnN0IGNvbm5lY3Rpb24gPSB0aGlzLmxvY2FsQ29ubmVjdGlvbnMuZ2V0Q29ubmVjdGlvbihjb25uZWN0aW9uSWQpO1xuICAgIGNvbnN0IHRlbGVtZXRyeUNvbnRleHQgPSBnZXRDb25uZWN0aW9uVGVsZW1ldHJ5Q29udGV4dChjb25uZWN0aW9uKTtcbiAgICBjb25zdCB1c2VySWQgPVxuICAgICAgY29ubmVjdGlvbj8udXNlcklkID8/IGZvcm1hdFVzZXJJZCh0aGlzLnByZXNlbmNlU3RvcmUuZ2V0Q29ubmVjdGlvbihjb25uZWN0aW9uSWQpPy51c2VySWQpO1xuICAgIGNvbnN0IG1ldGEgPSB0aGlzLnByZXNlbmNlU3RvcmUudW5yZWdpc3Rlcihjb25uZWN0aW9uSWQpO1xuICAgIHRoaXMubG9jYWxDb25uZWN0aW9ucy51bnJlZ2lzdGVyKGNvbm5lY3Rpb25JZCk7XG4gICAgdGhpcy50ZWxlbWV0cnlDb250cm9sbGVyLmVtaXQoe1xuICAgICAgbmFtZTogXCJ3cy5jb25uZWN0aW9uLnVucmVnaXN0ZXJlZFwiLFxuICAgICAgbGV2ZWw6IFwiZGVidWdcIixcbiAgICAgIGNvbm5lY3Rpb25JZCxcbiAgICAgIG5hbWVzcGFjZTogbWV0YT8ubmFtZXNwYWNlLFxuICAgICAgdXNlcklkLFxuICAgICAgLi4udGVsZW1ldHJ5Q29udGV4dCxcbiAgICB9KTtcbiAgICBpZiAobWV0YSkge1xuICAgICAgdGhpcy50ZWxlbWV0cnlDb250cm9sbGVyLnJlY29yZE1ldHJpYyh7XG4gICAgICAgIG5hbWU6IFwic29uYW11LndzLmNvbm5lY3Rpb25zXCIsXG4gICAgICAgIGtpbmQ6IFwiY291bnRlclwiLFxuICAgICAgICB2YWx1ZTogMSxcbiAgICAgICAgdW5pdDogXCIxXCIsXG4gICAgICAgIHRhZ3M6IHsgb3V0Y29tZTogXCJ1bnJlZ2lzdGVyZWRcIiwgbmFtZXNwYWNlOiBtZXRhLm5hbWVzcGFjZSB9LFxuICAgICAgICB1c2VySWQsXG4gICAgICB9KTtcbiAgICB9XG4gIH1cblxuICB0b3VjaChjb25uZWN0aW9uSWQ6IHN0cmluZyk6IHZvaWQge1xuICAgIHRoaXMucHJlc2VuY2VTdG9yZS50b3VjaChjb25uZWN0aW9uSWQpO1xuICB9XG5cbiAgc2V0VXNlcklkKGNvbm5lY3Rpb25JZDogc3RyaW5nLCB1c2VySWQ6IFdlYlNvY2tldFVzZXJJZCk6IHZvaWQge1xuICAgIGlmICghdGhpcy5wcmVzZW5jZVN0b3JlLmdldENvbm5lY3Rpb24oY29ubmVjdGlvbklkKSkge1xuICAgICAgdGhpcy5lbWl0TXV0YXRpb25Ta2lwcGVkKGNvbm5lY3Rpb25JZCwgeyBvcGVyYXRpb246IFwic2V0VXNlcklkXCIsIHVzZXJJZCB9KTtcbiAgICAgIHJldHVybjtcbiAgICB9XG5cbiAgICB0aGlzLnByZXNlbmNlU3RvcmUuc2V0VXNlcklkKGNvbm5lY3Rpb25JZCwgdXNlcklkKTtcbiAgICBjb25zdCBtZXRhID0gdGhpcy5wcmVzZW5jZVN0b3JlLmdldENvbm5lY3Rpb24oY29ubmVjdGlvbklkKTtcbiAgICB0aGlzLnRlbGVtZXRyeUNvbnRyb2xsZXIuZW1pdCh7XG4gICAgICBuYW1lOiBcIndzLnVzZXIuYm91bmRcIixcbiAgICAgIGxldmVsOiBcImRlYnVnXCIsXG4gICAgICBjb25uZWN0aW9uSWQsXG4gICAgICBuYW1lc3BhY2U6IG1ldGE/Lm5hbWVzcGFjZSxcbiAgICAgIHVzZXJJZDogU3RyaW5nKHVzZXJJZCksXG4gICAgICBkZXRhaWw6IHsgdXNlcklkIH0sXG4gICAgICAuLi5nZXRDb25uZWN0aW9uVGVsZW1ldHJ5Q29udGV4dCh0aGlzLmxvY2FsQ29ubmVjdGlvbnMuZ2V0Q29ubmVjdGlvbihjb25uZWN0aW9uSWQpKSxcbiAgICB9KTtcbiAgfVxuXG4gIGNsZWFyVXNlcklkKGNvbm5lY3Rpb25JZDogc3RyaW5nKTogdm9pZCB7XG4gICAgY29uc3QgbWV0YSA9IHRoaXMucHJlc2VuY2VTdG9yZS5nZXRDb25uZWN0aW9uKGNvbm5lY3Rpb25JZCk7XG4gICAgaWYgKCFtZXRhKSB7XG4gICAgICB0aGlzLmVtaXRNdXRhdGlvblNraXBwZWQoY29ubmVjdGlvbklkLCB7IG9wZXJhdGlvbjogXCJjbGVhclVzZXJJZFwiIH0pO1xuICAgICAgcmV0dXJuO1xuICAgIH1cblxuICAgIGNvbnN0IHVzZXJJZCA9IG1ldGEudXNlcklkO1xuICAgIHRoaXMucHJlc2VuY2VTdG9yZS5jbGVhclVzZXJJZChjb25uZWN0aW9uSWQpO1xuICAgIHRoaXMudGVsZW1ldHJ5Q29udHJvbGxlci5lbWl0KHtcbiAgICAgIG5hbWU6IFwid3MudXNlci5jbGVhcmVkXCIsXG4gICAgICBsZXZlbDogXCJkZWJ1Z1wiLFxuICAgICAgY29ubmVjdGlvbklkLFxuICAgICAgbmFtZXNwYWNlOiBtZXRhLm5hbWVzcGFjZSxcbiAgICAgIHVzZXJJZDogZm9ybWF0VXNlcklkKHVzZXJJZCksXG4gICAgICBkZXRhaWw6IHVzZXJJZCAhPT0gdW5kZWZpbmVkID8geyB1c2VySWQgfSA6IHVuZGVmaW5lZCxcbiAgICAgIC4uLmdldENvbm5lY3Rpb25UZWxlbWV0cnlDb250ZXh0KHRoaXMubG9jYWxDb25uZWN0aW9ucy5nZXRDb25uZWN0aW9uKGNvbm5lY3Rpb25JZCkpLFxuICAgIH0pO1xuICB9XG5cbiAgam9pbihjb25uZWN0aW9uSWQ6IHN0cmluZywgcm9vbUlkOiBXZWJTb2NrZXRSb29tSWQpOiB2b2lkIHtcbiAgICBpZiAoIXRoaXMucHJlc2VuY2VTdG9yZS5nZXRDb25uZWN0aW9uKGNvbm5lY3Rpb25JZCkpIHtcbiAgICAgIHRoaXMuZW1pdE11dGF0aW9uU2tpcHBlZChjb25uZWN0aW9uSWQsIHsgb3BlcmF0aW9uOiBcImpvaW5cIiwgcm9vbUlkIH0pO1xuICAgICAgcmV0dXJuO1xuICAgIH1cblxuICAgIHRoaXMucHJlc2VuY2VTdG9yZS5qb2luKGNvbm5lY3Rpb25JZCwgcm9vbUlkKTtcbiAgICBjb25zdCBtZXRhID0gdGhpcy5wcmVzZW5jZVN0b3JlLmdldENvbm5lY3Rpb24oY29ubmVjdGlvbklkKTtcbiAgICBjb25zdCBjb25uZWN0aW9uID0gdGhpcy5sb2NhbENvbm5lY3Rpb25zLmdldENvbm5lY3Rpb24oY29ubmVjdGlvbklkKTtcbiAgICB0aGlzLnRlbGVtZXRyeUNvbnRyb2xsZXIuZW1pdCh7XG4gICAgICBuYW1lOiBcIndzLnJvb20uam9pbmVkXCIsXG4gICAgICBsZXZlbDogXCJkZWJ1Z1wiLFxuICAgICAgY29ubmVjdGlvbklkLFxuICAgICAgbmFtZXNwYWNlOiBtZXRhPy5uYW1lc3BhY2UsXG4gICAgICB1c2VySWQ6IGNvbm5lY3Rpb24/LnVzZXJJZCA/PyBmb3JtYXRVc2VySWQobWV0YT8udXNlcklkKSxcbiAgICAgIGRldGFpbDogeyByb29tSWQgfSxcbiAgICAgIC4uLmdldENvbm5lY3Rpb25UZWxlbWV0cnlDb250ZXh0KGNvbm5lY3Rpb24pLFxuICAgIH0pO1xuICB9XG5cbiAgbGVhdmUoY29ubmVjdGlvbklkOiBzdHJpbmcsIHJvb21JZDogV2ViU29ja2V0Um9vbUlkKTogdm9pZCB7XG4gICAgY29uc3QgbWV0YSA9IHRoaXMucHJlc2VuY2VTdG9yZS5nZXRDb25uZWN0aW9uKGNvbm5lY3Rpb25JZCk7XG4gICAgaWYgKCFtZXRhKSB7XG4gICAgICB0aGlzLmVtaXRNdXRhdGlvblNraXBwZWQoY29ubmVjdGlvbklkLCB7IG9wZXJhdGlvbjogXCJsZWF2ZVwiLCByb29tSWQgfSk7XG4gICAgICByZXR1cm47XG4gICAgfVxuXG4gICAgdGhpcy5wcmVzZW5jZVN0b3JlLmxlYXZlKGNvbm5lY3Rpb25JZCwgcm9vbUlkKTtcbiAgICBjb25zdCBjb25uZWN0aW9uID0gdGhpcy5sb2NhbENvbm5lY3Rpb25zLmdldENvbm5lY3Rpb24oY29ubmVjdGlvbklkKTtcbiAgICB0aGlzLnRlbGVtZXRyeUNvbnRyb2xsZXIuZW1pdCh7XG4gICAgICBuYW1lOiBcIndzLnJvb20ubGVmdFwiLFxuICAgICAgbGV2ZWw6IFwiZGVidWdcIixcbiAgICAgIGNvbm5lY3Rpb25JZCxcbiAgICAgIG5hbWVzcGFjZTogbWV0YS5uYW1lc3BhY2UsXG4gICAgICB1c2VySWQ6IGNvbm5lY3Rpb24/LnVzZXJJZCA/PyBmb3JtYXRVc2VySWQobWV0YS51c2VySWQpLFxuICAgICAgZGV0YWlsOiB7IHJvb21JZCB9LFxuICAgICAgLi4uZ2V0Q29ubmVjdGlvblRlbGVtZXRyeUNvbnRleHQoY29ubmVjdGlvbiksXG4gICAgfSk7XG4gIH1cblxuICBwcml2YXRlIGVtaXRNdXRhdGlvblNraXBwZWQoXG4gICAgY29ubmVjdGlvbklkOiBzdHJpbmcsXG4gICAgZGV0YWlsOiB7XG4gICAgICBvcGVyYXRpb246IFwiYWN0aXZhdGVcIiB8IFwic2V0VXNlcklkXCIgfCBcImNsZWFyVXNlcklkXCIgfCBcImpvaW5cIiB8IFwibGVhdmVcIjtcbiAgICAgIHJvb21JZD86IFdlYlNvY2tldFJvb21JZDtcbiAgICAgIHVzZXJJZD86IFdlYlNvY2tldFVzZXJJZDtcbiAgICB9LFxuICApOiB2b2lkIHtcbiAgICB0aGlzLnRlbGVtZXRyeUNvbnRyb2xsZXIuZW1pdCh7XG4gICAgICBuYW1lOiBcIndzLnJlZ2lzdHJ5Lm11dGF0aW9uLnNraXBwZWRcIixcbiAgICAgIGxldmVsOiBcImRlYnVnXCIsXG4gICAgICBjb25uZWN0aW9uSWQsXG4gICAgICBkZXRhaWw6IHsgLi4uZGV0YWlsLCByZWFzb246IFwiY29ubmVjdGlvbk1pc3NpbmdcIiB9LFxuICAgIH0pO1xuICB9XG5cbiAgYnJvYWRjYXN0PERhdGE+KGV2ZW50OiBzdHJpbmcsIGRhdGE6IERhdGEsIG5hbWVzcGFjZT86IHN0cmluZyk6IHZvaWQge1xuICAgIHRoaXMucHVibGlzaFRvQXVkaWVuY2UoV2ViU29ja2V0QXVkaWVuY2UuYWxsKG5hbWVzcGFjZSksIGV2ZW50LCBkYXRhKTtcbiAgfVxuXG4gIHB1Ymxpc2hUb1Jvb208RGF0YT4oXG4gICAgcm9vbUlkOiBXZWJTb2NrZXRSb29tSWQsXG4gICAgZXZlbnQ6IHN0cmluZyxcbiAgICBkYXRhOiBEYXRhLFxuICAgIG5hbWVzcGFjZT86IHN0cmluZyxcbiAgKTogdm9pZCB7XG4gICAgdGhpcy5wdWJsaXNoVG9BdWRpZW5jZShXZWJTb2NrZXRBdWRpZW5jZS5yb29tKHJvb21JZCwgbmFtZXNwYWNlKSwgZXZlbnQsIGRhdGEpO1xuICB9XG5cbiAgcHVibGlzaFRvVXNlcjxEYXRhPihcbiAgICB1c2VySWQ6IFdlYlNvY2tldFVzZXJJZCxcbiAgICBldmVudDogc3RyaW5nLFxuICAgIGRhdGE6IERhdGEsXG4gICAgbmFtZXNwYWNlPzogc3RyaW5nLFxuICApOiB2b2lkIHtcbiAgICB0aGlzLnB1Ymxpc2hUb0F1ZGllbmNlKFdlYlNvY2tldEF1ZGllbmNlLnVzZXIodXNlcklkLCBuYW1lc3BhY2UpLCBldmVudCwgZGF0YSk7XG4gIH1cblxuICBwdWJsaXNoVG9BdWRpZW5jZTxEYXRhPihhdWRpZW5jZTogV2ViU29ja2V0QXVkaWVuY2VTcGVjLCBldmVudDogc3RyaW5nLCBkYXRhOiBEYXRhKTogdm9pZCB7XG4gICAgdGhpcy5kZWxpdmVyeUVuZ2luZS5wdWJsaXNoVG9BdWRpZW5jZShhdWRpZW5jZSwgZXZlbnQsIGRhdGEpO1xuICB9XG5cbiAgZ2V0Q29ubmVjdGlvbihjb25uZWN0aW9uSWQ6IHN0cmluZyk6IFdlYlNvY2tldENvbm5lY3Rpb25NZXRhIHwgdW5kZWZpbmVkIHtcbiAgICByZXR1cm4gdGhpcy5wcmVzZW5jZVN0b3JlLmdldENvbm5lY3Rpb24oY29ubmVjdGlvbklkKTtcbiAgfVxuXG4gIGdldENvbm5lY3Rpb25Db3VudChuYW1lc3BhY2U/OiBzdHJpbmcpOiBudW1iZXIge1xuICAgIHJldHVybiB0aGlzLnByZXNlbmNlU3RvcmUuZ2V0Q29ubmVjdGlvbkNvdW50KG5hbWVzcGFjZSk7XG4gIH1cblxuICBnZXRSb29tTWVtYmVycyhyb29tSWQ6IFdlYlNvY2tldFJvb21JZCwgbmFtZXNwYWNlPzogc3RyaW5nKTogV2ViU29ja2V0Q29ubmVjdGlvbk1ldGFbXSB7XG4gICAgcmV0dXJuIHRoaXMucHJlc2VuY2VTdG9yZS5nZXRSb29tTWVtYmVycyhyb29tSWQsIG5hbWVzcGFjZSk7XG4gIH1cblxuICBnZXRTdGF0cygpOiBXZWJTb2NrZXRSZWdpc3RyeVN0YXRzIHtcbiAgICByZXR1cm4gdGhpcy5wcmVzZW5jZVN0b3JlLmdldFN0YXRzKCk7XG4gIH1cblxuICBjbG9zZUFsbChjb2RlPzogbnVtYmVyLCByZWFzb24/OiBzdHJpbmcpOiB2b2lkIHtcbiAgICB0aGlzLmxvY2FsQ29ubmVjdGlvbnMuY2xvc2VBbGwoY29kZSwgcmVhc29uKTtcbiAgfVxuXG4gIGFzeW5jIHNodXRkb3duKGNvZGU/OiBudW1iZXIsIHJlYXNvbj86IHN0cmluZyk6IFByb21pc2U8dm9pZD4ge1xuICAgIHRoaXMuY2xvc2VBbGwoY29kZSwgcmVhc29uKTtcbiAgICBhd2FpdCB0aGlzLmRlbGl2ZXJ5RW5naW5lLnNodXRkb3duKCk7XG4gIH1cbn1cblxuZnVuY3Rpb24gZ2V0Q29ubmVjdGlvblRlbGVtZXRyeUNvbnRleHQoXG4gIGNvbm5lY3Rpb246IE1hbmFnZWRXZWJTb2NrZXRDb25uZWN0aW9uIHwgdW5kZWZpbmVkLFxuKTogV2ViU29ja2V0VGVsZW1ldHJ5Q29ubmVjdGlvbkNvbnRleHQge1xuICBpZiAoIWNvbm5lY3Rpb24gfHwgIWlzVGVsZW1ldHJ5Q29udGV4dFByb3ZpZGVyKGNvbm5lY3Rpb24pKSB7XG4gICAgcmV0dXJuIHt9O1xuICB9XG5cbiAgcmV0dXJuIGNvbm5lY3Rpb24uZ2V0VGVsZW1ldHJ5Q29udGV4dCgpO1xufVxuXG4vLyB0ZWxlbWV0cnkgcmVjb3Jk7J2YIHVzZXJJZOuKlCBzdHJpbmcg7KCV6rec7ZmUIOKAlCBwcmVzZW5jZSBzdG9yZeuKlCBudW1iZXIgfCBzdHJpbmcg6re464yA66GcIOuztOq0gO2VqFxuZnVuY3Rpb24gZm9ybWF0VXNlcklkKHVzZXJJZDogV2ViU29ja2V0VXNlcklkIHwgdW5kZWZpbmVkKTogc3RyaW5nIHwgdW5kZWZpbmVkIHtcbiAgcmV0dXJuIHVzZXJJZCA9PT0gdW5kZWZpbmVkID8gdW5kZWZpbmVkIDogU3RyaW5nKHVzZXJJZCk7XG59XG5cbmZ1bmN0aW9uIGlzVGVsZW1ldHJ5Q29udGV4dFByb3ZpZGVyKFxuICBjb25uZWN0aW9uOiBNYW5hZ2VkV2ViU29ja2V0Q29ubmVjdGlvbixcbik6IGNvbm5lY3Rpb24gaXMgTWFuYWdlZFdlYlNvY2tldENvbm5lY3Rpb24gJiBUZWxlbWV0cnlDb250ZXh0UHJvdmlkZXIge1xuICByZXR1cm4gXCJnZXRUZWxlbWV0cnlDb250ZXh0XCIgaW4gY29ubmVjdGlvbiAmJiBpc0Z1bmN0aW9uVmFsdWUoY29ubmVjdGlvbi5nZXRUZWxlbWV0cnlDb250ZXh0KTtcbn1cbiJdLCJtYXBwaW5ncyI6Ijs7Ozs7Ozs7OztBQW9UQSxTQUFTLDhCQUNQLFlBQ3FDO0NBQ3JDLElBQUksQ0FBQyxjQUFjLENBQUMsMkJBQTJCLFVBQVUsR0FDdkQsT0FBTyxDQUFDO0NBR1YsT0FBTyxXQUFXLG9CQUFvQjtBQUN4QztBQUdBLFNBQVMsYUFBYSxRQUF5RDtDQUM3RSxPQUFPLFdBQVcsU0FBWSxTQUFZLE9BQU8sTUFBTTtBQUN6RDtBQUVBLFNBQVMsMkJBQ1AsWUFDcUU7Q0FDckUsT0FBTyx5QkFBeUIs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
377
+ //# sourceMappingURL=data:application/json;charset=utf-8;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoid3MtcmVnaXN0cnkuanMiLCJuYW1lcyI6W10sInNvdXJjZXMiOlsiLi4vLi4vc3JjL3N0cmVhbS93cy1yZWdpc3RyeS50cyJdLCJzb3VyY2VzQ29udGVudCI6WyJpbXBvcnQgeyB0eXBlIHogfSBmcm9tIFwiem9kXCI7XG5cbmltcG9ydCB7IHJ1bnRpbWVUeXBlT2YgfSBmcm9tIFwiLi4vcnVudGltZS10eXBlXCI7XG5pbXBvcnQgeyBpc0Z1bmN0aW9uVmFsdWUgfSBmcm9tIFwiLi4vdXRpbHMvcnVudGltZS12YWx1ZVwiO1xuaW1wb3J0IHsgV2ViU29ja2V0QXVkaWVuY2UsIHR5cGUgV2ViU29ja2V0QXVkaWVuY2UgYXMgV2ViU29ja2V0QXVkaWVuY2VTcGVjIH0gZnJvbSBcIi4vd3MtYXVkaWVuY2VcIjtcbmltcG9ydCB7IFdlYlNvY2tldEF1ZGllbmNlUmVzb2x2ZXIgfSBmcm9tIFwiLi93cy1hdWRpZW5jZS1yZXNvbHZlclwiO1xuaW1wb3J0IHsgTm9vcFdlYlNvY2tldENsdXN0ZXJCdXMsIHR5cGUgV2ViU29ja2V0Q2x1c3RlckJ1cyB9IGZyb20gXCIuL3dzLWNsdXN0ZXItYnVzXCI7XG5pbXBvcnQge1xuICB0eXBlIE1hbmFnZWRXZWJTb2NrZXRDb25uZWN0aW9uLFxuICB0eXBlIE1heWJlUHJvbWlzZSxcbiAgdHlwZSBXZWJTb2NrZXRGcmFtZUNvbm5lY3Rpb24sXG4gIHR5cGUgV2ViU29ja2V0UmVnaXN0cnlTdGF0cyxcbiAgdHlwZSBXZWJTb2NrZXRSb29tSWQsXG4gIHR5cGUgV2ViU29ja2V0VXNlcklkLFxufSBmcm9tIFwiLi93cy1jb3JlXCI7XG5pbXBvcnQgeyBXZWJTb2NrZXREZWxpdmVyeUVuZ2luZSwgdHlwZSBXZWJTb2NrZXRSb3V0ZURlZmluaXRpb24gfSBmcm9tIFwiLi93cy1kZWxpdmVyeVwiO1xuaW1wb3J0IHsgV2ViU29ja2V0TG9jYWxDb25uZWN0aW9uU3RvcmUgfSBmcm9tIFwiLi93cy1sb2NhbC1jb25uZWN0aW9uLXN0b3JlXCI7XG5pbXBvcnQgeyBXZWJTb2NrZXRQcmVzZW5jZUluZGV4IH0gZnJvbSBcIi4vd3MtcHJlc2VuY2UtaW5kZXhcIjtcbmltcG9ydCB7XG4gIEluTWVtb3J5V2ViU29ja2V0UHJlc2VuY2VTdG9yZSxcbiAgdHlwZSBXZWJTb2NrZXRQcmVzZW5jZVN0b3JlLFxuICB0eXBlIFdlYlNvY2tldFNlc3Npb25QcmVzZW5jZSxcbn0gZnJvbSBcIi4vd3MtcHJlc2VuY2Utc3RvcmVcIjtcbmltcG9ydCB7XG4gIHR5cGUgVGVsZW1ldHJ5Q29udGV4dFByb3ZpZGVyLFxuICBOb29wV2ViU29ja2V0VGVsZW1ldHJ5Q29udHJvbGxlcixcbiAgdHlwZSBXZWJTb2NrZXRUZWxlbWV0cnlDb25uZWN0aW9uQ29udGV4dCxcbiAgdHlwZSBXZWJTb2NrZXRUZWxlbWV0cnlDb250cm9sbGVyLFxufSBmcm9tIFwiLi93cy10ZWxlbWV0cnlcIjtcblxuZXhwb3J0IHR5cGUge1xuICBNYW5hZ2VkV2ViU29ja2V0Q29ubmVjdGlvbixcbiAgV2ViU29ja2V0UmVnaXN0cnlTdGF0cyxcbiAgV2ViU29ja2V0Um9vbUlkLFxuICBXZWJTb2NrZXRVc2VySWQsXG59O1xuZXhwb3J0IHR5cGUgV2ViU29ja2V0Q29ubmVjdGlvbk1ldGEgPSBXZWJTb2NrZXRTZXNzaW9uUHJlc2VuY2U7XG5leHBvcnQgdHlwZSBXZWJTb2NrZXRSb29tTWVtYmVyQ291bnQgPSB7XG4gIGNvdW50OiBudW1iZXI7XG4gIC8vIHN0b3JlIOyhsO2ajOyXkCDsi6TtjKjtlbQg64W465OcIOuhnOy7rCDsiJjroZwg64yA7LK07ZaI7Jy866m0IHRydWVcbiAgcGFydGlhbDogYm9vbGVhbjtcbn07XG5cbmNvbnN0IERFRkFVTFRfRFJBSU5fVElNRU9VVF9NUyA9IDUwMDA7XG5cbnR5cGUgU3RvcmVNdXRhdGlvbk9wZXJhdGlvbiA9XG4gIHwgXCJyZWdpc3RlclwiXG4gIHwgXCJhY3RpdmF0ZVwiXG4gIHwgXCJ1bnJlZ2lzdGVyXCJcbiAgfCBcInNldFVzZXJJZFwiXG4gIHwgXCJjbGVhclVzZXJJZFwiXG4gIHwgXCJqb2luXCJcbiAgfCBcImxlYXZlXCI7XG5cbmV4cG9ydCB0eXBlIFdlYlNvY2tldFJlZ2lzdHJ5T3B0aW9ucyA9IHtcbiAgLy8g67aE7IKwIO2ZmOqyveyXkOyEnCDrhbjrk5wg6rCEIOudvOyasO2MhSDsi53rs4TsnpDroZwg7JOw7J2066+A66GcIOuwmOuTnOyLnCDtmLjstpwg7LihKFdlYlNvY2tldFJ1bnRpbWUp7JeQ7IScIOqysOygle2VtCDrhJjquLTri6RcbiAgbm9kZUlkOiBzdHJpbmc7XG4gIHByZXNlbmNlU3RvcmU/OiBXZWJTb2NrZXRQcmVzZW5jZVN0b3JlO1xuICBjbHVzdGVyQnVzPzogV2ViU29ja2V0Q2x1c3RlckJ1cztcbiAgdGVsZW1ldHJ5Q29udHJvbGxlcj86IFdlYlNvY2tldFRlbGVtZXRyeUNvbnRyb2xsZXI7XG4gIC8vIHNodXRkb3duIOyLnCDsp4Ttlokg7KSR7J24IHN0b3JlIG11dGF0aW9u6rO8IHB1Ymxpc2jrpbwg6riw64uk66as64qUIOyDge2VnFxuICBkcmFpblRpbWVvdXRNcz86IG51bWJlcjtcbn07XG5cbi8qKlxuICog64W465OcIOuhnOy7rCBwcmVzZW5jZeulvCDrqZTrqqjrpqzsl5Ag7IaM7Jyg7ZWY6rOgLCDshKTsoJXrkJwgc3RvcmXsl5DripQg7IS47IWY67OEIOyInOyEnOuMgOuhnCByZXBsaWNh66W8IOuwmOyYge2VnOuLpC5cbiAqIG11dGF0aW9u6rO8IHB1Ymxpc2jripQg7Zi47LacIOyLnOygkOyXkCDrqZTrqqjrpqzrpbwg64+Z6riw66GcIOqwseyLoO2VmOqzoCwgc3RvcmUg7Iuk7Yyo64qUIHRlbGVtZXRyeeuhnOunjCDquLDroZ3tlZjrqbAgcmVqZWN07ZWY7KeAIOyViuuKlOuLpC5cbiAqL1xuZXhwb3J0IGNsYXNzIFdlYlNvY2tldFJlZ2lzdHJ5IHtcbiAgcmVhZG9ubHkgbm9kZUlkOiBzdHJpbmc7XG4gIHJlYWRvbmx5IGxvY2FsQ29ubmVjdGlvbnMgPSBuZXcgV2ViU29ja2V0TG9jYWxDb25uZWN0aW9uU3RvcmUoKTtcbiAgcmVhZG9ubHkgcHJlc2VuY2VTdG9yZTogV2ViU29ja2V0UHJlc2VuY2VTdG9yZTtcbiAgcmVhZG9ubHkgY2x1c3RlckJ1czogV2ViU29ja2V0Q2x1c3RlckJ1cztcbiAgcmVhZG9ubHkgdGVsZW1ldHJ5Q29udHJvbGxlcjogV2ViU29ja2V0VGVsZW1ldHJ5Q29udHJvbGxlcjtcbiAgcHJpdmF0ZSByZWFkb25seSBtZW1vcnkgPSBuZXcgV2ViU29ja2V0UHJlc2VuY2VJbmRleCgpO1xuICBwcml2YXRlIHJlYWRvbmx5IGRlbGl2ZXJ5RW5naW5lOiBXZWJTb2NrZXREZWxpdmVyeUVuZ2luZTtcbiAgcHJpdmF0ZSByZWFkb25seSBkcmFpblRpbWVvdXRNczogbnVtYmVyO1xuICAvLyDshLjshZjrs4Qgc3RvcmUgbXV0YXRpb24g7YGQ7J2YIOq8rOumrC4g6rCZ7J2AIOyEuOyFmOydmCDtmLjstpzsnYAg7J20IHByb21pc2Ug65Kk7JeQIOydtOyWtCDrtpnslrQg7Zi47LacIOyInOyEnOuMgOuhnCBzdG9yZeyXkCDrj4Tri6ztlZzri6RcbiAgcHJpdmF0ZSByZWFkb25seSBtdXRhdGlvblF1ZXVlcyA9IG5ldyBNYXA8c3RyaW5nLCBQcm9taXNlPHZvaWQ+PigpO1xuXG4gIGNvbnN0cnVjdG9yKG9wdGlvbnM6IFdlYlNvY2tldFJlZ2lzdHJ5T3B0aW9ucykge1xuICAgIHRoaXMubm9kZUlkID0gb3B0aW9ucy5ub2RlSWQ7XG4gICAgdGhpcy5wcmVzZW5jZVN0b3JlID0gb3B0aW9ucy5wcmVzZW5jZVN0b3JlID8/IG5ldyBJbk1lbW9yeVdlYlNvY2tldFByZXNlbmNlU3RvcmUoKTtcbiAgICB0aGlzLmNsdXN0ZXJCdXMgPSBvcHRpb25zLmNsdXN0ZXJCdXMgPz8gbmV3IE5vb3BXZWJTb2NrZXRDbHVzdGVyQnVzKCk7XG4gICAgdGhpcy5kcmFpblRpbWVvdXRNcyA9IG9wdGlvbnMuZHJhaW5UaW1lb3V0TXMgPz8gREVGQVVMVF9EUkFJTl9USU1FT1VUX01TO1xuICAgIGFzc2VydE1hdGNoaW5nTm9kZUlkKFwicHJlc2VuY2VTdG9yZVwiLCB0aGlzLnByZXNlbmNlU3RvcmUubm9kZUlkLCB0aGlzLm5vZGVJZCk7XG4gICAgYXNzZXJ0TWF0Y2hpbmdOb2RlSWQoXCJjbHVzdGVyQnVzXCIsIHRoaXMuY2x1c3RlckJ1cy5ub2RlSWQsIHRoaXMubm9kZUlkKTtcbiAgICB0aGlzLnRlbGVtZXRyeUNvbnRyb2xsZXIgPVxuICAgICAgb3B0aW9ucy50ZWxlbWV0cnlDb250cm9sbGVyID8/IG5ldyBOb29wV2ViU29ja2V0VGVsZW1ldHJ5Q29udHJvbGxlcigpO1xuICAgIHRoaXMuZGVsaXZlcnlFbmdpbmUgPSBuZXcgV2ViU29ja2V0RGVsaXZlcnlFbmdpbmUoe1xuICAgICAgbm9kZUlkOiB0aGlzLm5vZGVJZCxcbiAgICAgIGxvY2FsQ29ubmVjdGlvbnM6IHRoaXMubG9jYWxDb25uZWN0aW9ucyxcbiAgICAgIHJlc29sdmVMb2NhbFNlc3Npb25JZHM6IChhdWRpZW5jZSkgPT5cbiAgICAgICAgdGhpcy5tZW1vcnkucXVlcnlBdWRpZW5jZShhdWRpZW5jZSkubWFwKChzZXNzaW9uKSA9PiBzZXNzaW9uLnNlc3Npb25JZCksXG4gICAgICBhdWRpZW5jZVJlc29sdmVyOiBuZXcgV2ViU29ja2V0QXVkaWVuY2VSZXNvbHZlcih7XG4gICAgICAgIG5vZGVJZDogdGhpcy5ub2RlSWQsXG4gICAgICAgIHByZXNlbmNlU3RvcmU6IHRoaXMucHJlc2VuY2VTdG9yZSxcbiAgICAgIH0pLFxuICAgICAgY2x1c3RlckJ1czogdGhpcy5jbHVzdGVyQnVzLFxuICAgICAgdGVsZW1ldHJ5Q29udHJvbGxlcjogdGhpcy50ZWxlbWV0cnlDb250cm9sbGVyLFxuICAgIH0pO1xuXG4gICAgLy8gdGVsZW1ldHJ564qUIOuFuOuTnCDroZzsu6wg66mU66qo66as66eMIOynkeqzhO2VnOuLpC4g7YG065+s7Iqk7YSwIO2VqeqzhOulvCDrqqjrk6Ag64W465Oc6rCAIOuztOqzoO2VmOuptCDsp5Hqs4Qg7IucIOykkeuzteuQmOq4sCDrlYzrrLjsnbTri6RcbiAgICB0aGlzLnRlbGVtZXRyeUNvbnRyb2xsZXIucmVnaXN0ZXJNZXRyaWNTb3VyY2Uoe1xuICAgICAgY29sbGVjdDogKF9ub3cpID0+IHtcbiAgICAgICAgY29uc3Qgc3RhdHMgPSB0aGlzLm1lbW9yeS5nZXRTdGF0cygpO1xuICAgICAgICBjb25zdCBsb2NhbFNuYXBzaG90ID0gdGhpcy5sb2NhbENvbm5lY3Rpb25zLmdldFRlbGVtZXRyeVNuYXBzaG90KCk7XG4gICAgICAgIHJldHVybiB7XG4gICAgICAgICAgYWN0aXZlQ29ubmVjdGlvbnM6IHN0YXRzLnRvdGFsQ29ubmVjdGlvbnMsXG4gICAgICAgICAgYWN0aXZlQ29ubmVjdGlvbnNCeU5hbWVzcGFjZTogc3RhdHMuYnlOYW1lc3BhY2UsXG4gICAgICAgICAgcm9vbUNvdW50OiBzdGF0cy50b3RhbFJvb21zLFxuICAgICAgICAgIHBlbmRpbmdJbmJvdW5kTWVzc2FnZXM6IGxvY2FsU25hcHNob3QucGVuZGluZ0luYm91bmRNZXNzYWdlcyxcbiAgICAgICAgICBwZW5kaW5nT3V0Ym91bmRNZXNzYWdlczogbG9jYWxTbmFwc2hvdC5wZW5kaW5nT3V0Ym91bmRNZXNzYWdlcyxcbiAgICAgICAgICBzb2NrZXRCdWZmZXJlZEJ5dGVzOiBsb2NhbFNuYXBzaG90LnNvY2tldEJ1ZmZlcmVkQnl0ZXMsXG4gICAgICAgIH07XG4gICAgICB9LFxuICAgIH0pO1xuICB9XG5cbiAgcmVnaXN0ZXJSb3V0ZShyb3V0ZUtleTogc3RyaW5nLCBvdXRFdmVudHM6IHouWm9kT2JqZWN0KTogdm9pZCB7XG4gICAgdGhpcy5kZWxpdmVyeUVuZ2luZS5yZWdpc3RlclJvdXRlKHJvdXRlS2V5LCBvdXRFdmVudHMpO1xuICB9XG5cbiAgcmVwbGFjZVJvdXRlcyhyb3V0ZXM6IFdlYlNvY2tldFJvdXRlRGVmaW5pdGlvbltdKTogdm9pZCB7XG4gICAgdGhpcy5kZWxpdmVyeUVuZ2luZS5yZXBsYWNlUm91dGVzKHJvdXRlcyk7XG4gIH1cblxuICAvKipcbiAgICog7Jew6rKw7J2EIOuplOuqqOumrOyXkCDsponsi5wg65Ox66Gd7ZWY6rOgIHN0b3JlLnJlZ2lzdGVy6rCAIOuBneuCmOuptCByZXNvbHZl7ZWc64ukLlxuICAgKiBzdG9yZeqwgCDsi6TtjKjtlbTrj4Qg66mU66qo66asIOyghOyaqSDrk7HroZ3snLzroZwgcmVzb2x2Ze2VnOuLpC5cbiAgICovXG4gIGFzeW5jIHJlZ2lzdGVyKFxuICAgIGNvbm5lY3Rpb246IFdlYlNvY2tldEZyYW1lQ29ubmVjdGlvbixcbiAgICBhY3RpdmU6IGJvb2xlYW4gPSB0cnVlLFxuICApOiBQcm9taXNlPFdlYlNvY2tldENvbm5lY3Rpb25NZXRhPiB7XG4gICAgY29uc3QgcmVnaXN0cmF0aW9uID0ge1xuICAgICAgc2Vzc2lvbklkOiBjb25uZWN0aW9uLmlkLFxuICAgICAgbm9kZUlkOiB0aGlzLm5vZGVJZCxcbiAgICAgIG5hbWVzcGFjZTogY29ubmVjdGlvbi5uYW1lc3BhY2UsXG4gICAgICBhY3RpdmUsXG4gICAgICBjb25uZWN0ZWRBdDogbmV3IERhdGUoKSxcbiAgICB9O1xuICAgIHRoaXMubG9jYWxDb25uZWN0aW9ucy5yZWdpc3Rlcihjb25uZWN0aW9uKTtcbiAgICB0aGlzLm1lbW9yeS5yZWdpc3RlcihyZWdpc3RyYXRpb24pO1xuICAgIHRoaXMudGVsZW1ldHJ5Q29udHJvbGxlci5lbWl0KHtcbiAgICAgIG5hbWU6IFwid3MuY29ubmVjdGlvbi5yZWdpc3RlcmVkXCIsXG4gICAgICBsZXZlbDogXCJpbmZvXCIsXG4gICAgICBjb25uZWN0aW9uSWQ6IGNvbm5lY3Rpb24uaWQsXG4gICAgICBuYW1lc3BhY2U6IGNvbm5lY3Rpb24ubmFtZXNwYWNlLFxuICAgICAgdXNlcklkOiBjb25uZWN0aW9uLnVzZXJJZCxcbiAgICAgIC4uLmdldENvbm5lY3Rpb25UZWxlbWV0cnlDb250ZXh0KGNvbm5lY3Rpb24pLFxuICAgIH0pO1xuICAgIHRoaXMudGVsZW1ldHJ5Q29udHJvbGxlci5yZWNvcmRNZXRyaWMoe1xuICAgICAgbmFtZTogXCJzb25hbXUud3MuY29ubmVjdGlvbnNcIixcbiAgICAgIGtpbmQ6IFwiY291bnRlclwiLFxuICAgICAgdmFsdWU6IDEsXG4gICAgICB1bml0OiBcIjFcIixcbiAgICAgIHRhZ3M6IHsgb3V0Y29tZTogXCJyZWdpc3RlcmVkXCIsIG5hbWVzcGFjZTogY29ubmVjdGlvbi5uYW1lc3BhY2UgfSxcbiAgICAgIHVzZXJJZDogY29ubmVjdGlvbi51c2VySWQsXG4gICAgfSk7XG5cbiAgICBhd2FpdCB0aGlzLmVucXVldWVTdG9yZU11dGF0aW9uKGNvbm5lY3Rpb24uaWQsIFwicmVnaXN0ZXJcIiwgKCkgPT5cbiAgICAgIHRoaXMucHJlc2VuY2VTdG9yZS5yZWdpc3RlcihyZWdpc3RyYXRpb24pLFxuICAgICk7XG4gICAgLy8g65Ox66GdIOuPhOykkSDri6vtmJTsnLzrqbQg66mU66qo66as7JeQ7IScIOydtOuvuCDruaDsoYzsnLzrr4DroZwg65Ox66GdIOyLnOygkCDqsJLsnYQg64+M66Ck7KSA64ukXG4gICAgcmV0dXJuIChcbiAgICAgIHRoaXMubWVtb3J5LmdldENvbm5lY3Rpb24oY29ubmVjdGlvbi5pZCkgPz8ge1xuICAgICAgICAuLi5yZWdpc3RyYXRpb24sXG4gICAgICAgIHJvb21zOiBuZXcgU2V0KCksXG4gICAgICAgIGxhc3RTZWVuQXQ6IHJlZ2lzdHJhdGlvbi5jb25uZWN0ZWRBdCxcbiAgICAgIH1cbiAgICApO1xuICB9XG5cbiAgYWN0aXZhdGUoY29ubmVjdGlvbklkOiBzdHJpbmcpOiBQcm9taXNlPHZvaWQ+IHtcbiAgICBpZiAoIXRoaXMubWVtb3J5Lmhhcyhjb25uZWN0aW9uSWQpKSB7XG4gICAgICB0aGlzLmVtaXRNdXRhdGlvblNraXBwZWQoY29ubmVjdGlvbklkLCB7IG9wZXJhdGlvbjogXCJhY3RpdmF0ZVwiIH0pO1xuICAgICAgcmV0dXJuIFByb21pc2UucmVzb2x2ZSgpO1xuICAgIH1cblxuICAgIHRoaXMubWVtb3J5LmFjdGl2YXRlKGNvbm5lY3Rpb25JZCk7XG4gICAgY29uc3QgY29ubmVjdGlvbiA9IHRoaXMubG9jYWxDb25uZWN0aW9ucy5nZXRDb25uZWN0aW9uKGNvbm5lY3Rpb25JZCk7XG4gICAgdGhpcy50ZWxlbWV0cnlDb250cm9sbGVyLmVtaXQoe1xuICAgICAgbmFtZTogXCJ3cy5jb25uZWN0aW9uLmFjdGl2YXRlZFwiLFxuICAgICAgbGV2ZWw6IFwiaW5mb1wiLFxuICAgICAgY29ubmVjdGlvbklkLFxuICAgICAgdXNlcklkOiBjb25uZWN0aW9uPy51c2VySWQsXG4gICAgICAuLi5nZXRDb25uZWN0aW9uVGVsZW1ldHJ5Q29udGV4dChjb25uZWN0aW9uKSxcbiAgICB9KTtcbiAgICByZXR1cm4gdGhpcy5lbnF1ZXVlU3RvcmVNdXRhdGlvbihjb25uZWN0aW9uSWQsIFwiYWN0aXZhdGVcIiwgKCkgPT5cbiAgICAgIHRoaXMucHJlc2VuY2VTdG9yZS5hY3RpdmF0ZShjb25uZWN0aW9uSWQpLFxuICAgICk7XG4gIH1cblxuICB1bnJlZ2lzdGVyKGNvbm5lY3Rpb25JZDogc3RyaW5nKTogUHJvbWlzZTx2b2lkPiB7XG4gICAgY29uc3QgY29ubmVjdGlvbiA9IHRoaXMubG9jYWxDb25uZWN0aW9ucy5nZXRDb25uZWN0aW9uKGNvbm5lY3Rpb25JZCk7XG4gICAgY29uc3QgdGVsZW1ldHJ5Q29udGV4dCA9IGdldENvbm5lY3Rpb25UZWxlbWV0cnlDb250ZXh0KGNvbm5lY3Rpb24pO1xuICAgIGNvbnN0IG1ldGEgPSB0aGlzLm1lbW9yeS51bnJlZ2lzdGVyKGNvbm5lY3Rpb25JZCk7XG4gICAgY29uc3QgdXNlcklkID0gY29ubmVjdGlvbj8udXNlcklkID8/IGZvcm1hdFVzZXJJZChtZXRhPy51c2VySWQpO1xuICAgIHRoaXMubG9jYWxDb25uZWN0aW9ucy51bnJlZ2lzdGVyKGNvbm5lY3Rpb25JZCk7XG4gICAgdGhpcy50ZWxlbWV0cnlDb250cm9sbGVyLmVtaXQoe1xuICAgICAgbmFtZTogXCJ3cy5jb25uZWN0aW9uLnVucmVnaXN0ZXJlZFwiLFxuICAgICAgbGV2ZWw6IFwiZGVidWdcIixcbiAgICAgIGNvbm5lY3Rpb25JZCxcbiAgICAgIG5hbWVzcGFjZTogbWV0YT8ubmFtZXNwYWNlLFxuICAgICAgdXNlcklkLFxuICAgICAgLi4udGVsZW1ldHJ5Q29udGV4dCxcbiAgICB9KTtcbiAgICBpZiAoIW1ldGEpIHtcbiAgICAgIHJldHVybiBQcm9taXNlLnJlc29sdmUoKTtcbiAgICB9XG5cbiAgICB0aGlzLnRlbGVtZXRyeUNvbnRyb2xsZXIucmVjb3JkTWV0cmljKHtcbiAgICAgIG5hbWU6IFwic29uYW11LndzLmNvbm5lY3Rpb25zXCIsXG4gICAgICBraW5kOiBcImNvdW50ZXJcIixcbiAgICAgIHZhbHVlOiAxLFxuICAgICAgdW5pdDogXCIxXCIsXG4gICAgICB0YWdzOiB7IG91dGNvbWU6IFwidW5yZWdpc3RlcmVkXCIsIG5hbWVzcGFjZTogbWV0YS5uYW1lc3BhY2UgfSxcbiAgICAgIHVzZXJJZCxcbiAgICB9KTtcbiAgICAvLyDqsJnsnYAg7IS47IWYIO2BkOyXkCDsnbTslrQg67aZ7Jy866+A66GcIOynhO2WiSDspJHsnbggcmVnaXN0ZXLrs7Tri6Qg66i87KCAIHN0b3Jl7JeQIOuPhOuLrO2VmOyngCDslYrripTri6RcbiAgICByZXR1cm4gdGhpcy5lbnF1ZXVlU3RvcmVNdXRhdGlvbihjb25uZWN0aW9uSWQsIFwidW5yZWdpc3RlclwiLCAoKSA9PlxuICAgICAgdGhpcy5wcmVzZW5jZVN0b3JlLnVucmVnaXN0ZXIoY29ubmVjdGlvbklkKSxcbiAgICApO1xuICB9XG5cbiAgLy8gbGFzdFNlZW5BdOydgCDrqZTrqqjrpqzrp4wg6rCx7Iug7ZWc64ukLiBzdG9yZeyXkOuKlCDrp4jsp4Drp4kgbXV0YXRpb24g7Iuc7KCQIOqwkuydtCDrgqjripTri6RcbiAgdG91Y2goY29ubmVjdGlvbklkOiBzdHJpbmcpOiB2b2lkIHtcbiAgICB0aGlzLm1lbW9yeS50b3VjaChjb25uZWN0aW9uSWQpO1xuICB9XG5cbiAgc2V0VXNlcklkKGNvbm5lY3Rpb25JZDogc3RyaW5nLCB1c2VySWQ6IFdlYlNvY2tldFVzZXJJZCk6IFByb21pc2U8dm9pZD4ge1xuICAgIGNvbnN0IG1ldGEgPSB0aGlzLm1lbW9yeS5nZXRDb25uZWN0aW9uKGNvbm5lY3Rpb25JZCk7XG4gICAgaWYgKCFtZXRhKSB7XG4gICAgICB0aGlzLmVtaXRNdXRhdGlvblNraXBwZWQoY29ubmVjdGlvbklkLCB7IG9wZXJhdGlvbjogXCJzZXRVc2VySWRcIiwgdXNlcklkIH0pO1xuICAgICAgcmV0dXJuIFByb21pc2UucmVzb2x2ZSgpO1xuICAgIH1cblxuICAgIHRoaXMubWVtb3J5LnNldFVzZXJJZChjb25uZWN0aW9uSWQsIHVzZXJJZCk7XG4gICAgdGhpcy50ZWxlbWV0cnlDb250cm9sbGVyLmVtaXQoe1xuICAgICAgbmFtZTogXCJ3cy51c2VyLmJvdW5kXCIsXG4gICAgICBsZXZlbDogXCJkZWJ1Z1wiLFxuICAgICAgY29ubmVjdGlvbklkLFxuICAgICAgbmFtZXNwYWNlOiBtZXRhLm5hbWVzcGFjZSxcbiAgICAgIHVzZXJJZDogU3RyaW5nKHVzZXJJZCksXG4gICAgICBkZXRhaWw6IHsgdXNlcklkIH0sXG4gICAgICAuLi5nZXRDb25uZWN0aW9uVGVsZW1ldHJ5Q29udGV4dCh0aGlzLmxvY2FsQ29ubmVjdGlvbnMuZ2V0Q29ubmVjdGlvbihjb25uZWN0aW9uSWQpKSxcbiAgICB9KTtcbiAgICByZXR1cm4gdGhpcy5lbnF1ZXVlU3RvcmVNdXRhdGlvbihjb25uZWN0aW9uSWQsIFwic2V0VXNlcklkXCIsICgpID0+XG4gICAgICB0aGlzLnByZXNlbmNlU3RvcmUuc2V0VXNlcklkKGNvbm5lY3Rpb25JZCwgdXNlcklkKSxcbiAgICApO1xuICB9XG5cbiAgY2xlYXJVc2VySWQoY29ubmVjdGlvbklkOiBzdHJpbmcpOiBQcm9taXNlPHZvaWQ+IHtcbiAgICBjb25zdCBtZXRhID0gdGhpcy5tZW1vcnkuZ2V0Q29ubmVjdGlvbihjb25uZWN0aW9uSWQpO1xuICAgIGlmICghbWV0YSkge1xuICAgICAgdGhpcy5lbWl0TXV0YXRpb25Ta2lwcGVkKGNvbm5lY3Rpb25JZCwgeyBvcGVyYXRpb246IFwiY2xlYXJVc2VySWRcIiB9KTtcbiAgICAgIHJldHVybiBQcm9taXNlLnJlc29sdmUoKTtcbiAgICB9XG5cbiAgICBjb25zdCB1c2VySWQgPSBtZXRhLnVzZXJJZDtcbiAgICB0aGlzLm1lbW9yeS5jbGVhclVzZXJJZChjb25uZWN0aW9uSWQpO1xuICAgIHRoaXMudGVsZW1ldHJ5Q29udHJvbGxlci5lbWl0KHtcbiAgICAgIG5hbWU6IFwid3MudXNlci5jbGVhcmVkXCIsXG4gICAgICBsZXZlbDogXCJkZWJ1Z1wiLFxuICAgICAgY29ubmVjdGlvbklkLFxuICAgICAgbmFtZXNwYWNlOiBtZXRhLm5hbWVzcGFjZSxcbiAgICAgIHVzZXJJZDogZm9ybWF0VXNlcklkKHVzZXJJZCksXG4gICAgICBkZXRhaWw6IHVzZXJJZCAhPT0gdW5kZWZpbmVkID8geyB1c2VySWQgfSA6IHVuZGVmaW5lZCxcbiAgICAgIC4uLmdldENvbm5lY3Rpb25UZWxlbWV0cnlDb250ZXh0KHRoaXMubG9jYWxDb25uZWN0aW9ucy5nZXRDb25uZWN0aW9uKGNvbm5lY3Rpb25JZCkpLFxuICAgIH0pO1xuICAgIHJldHVybiB0aGlzLmVucXVldWVTdG9yZU11dGF0aW9uKGNvbm5lY3Rpb25JZCwgXCJjbGVhclVzZXJJZFwiLCAoKSA9PlxuICAgICAgdGhpcy5wcmVzZW5jZVN0b3JlLmNsZWFyVXNlcklkKGNvbm5lY3Rpb25JZCksXG4gICAgKTtcbiAgfVxuXG4gIGpvaW4oY29ubmVjdGlvbklkOiBzdHJpbmcsIHJvb21JZDogV2ViU29ja2V0Um9vbUlkKTogUHJvbWlzZTx2b2lkPiB7XG4gICAgY29uc3QgbWV0YSA9IHRoaXMubWVtb3J5LmdldENvbm5lY3Rpb24oY29ubmVjdGlvbklkKTtcbiAgICBpZiAoIW1ldGEpIHtcbiAgICAgIHRoaXMuZW1pdE11dGF0aW9uU2tpcHBlZChjb25uZWN0aW9uSWQsIHsgb3BlcmF0aW9uOiBcImpvaW5cIiwgcm9vbUlkIH0pO1xuICAgICAgcmV0dXJuIFByb21pc2UucmVzb2x2ZSgpO1xuICAgIH1cblxuICAgIHRoaXMubWVtb3J5LmpvaW4oY29ubmVjdGlvbklkLCByb29tSWQpO1xuICAgIGNvbnN0IGNvbm5lY3Rpb24gPSB0aGlzLmxvY2FsQ29ubmVjdGlvbnMuZ2V0Q29ubmVjdGlvbihjb25uZWN0aW9uSWQpO1xuICAgIHRoaXMudGVsZW1ldHJ5Q29udHJvbGxlci5lbWl0KHtcbiAgICAgIG5hbWU6IFwid3Mucm9vbS5qb2luZWRcIixcbiAgICAgIGxldmVsOiBcImRlYnVnXCIsXG4gICAgICBjb25uZWN0aW9uSWQsXG4gICAgICBuYW1lc3BhY2U6IG1ldGEubmFtZXNwYWNlLFxuICAgICAgdXNlcklkOiBjb25uZWN0aW9uPy51c2VySWQgPz8gZm9ybWF0VXNlcklkKG1ldGEudXNlcklkKSxcbiAgICAgIGRldGFpbDogeyByb29tSWQgfSxcbiAgICAgIC4uLmdldENvbm5lY3Rpb25UZWxlbWV0cnlDb250ZXh0KGNvbm5lY3Rpb24pLFxuICAgIH0pO1xuICAgIHJldHVybiB0aGlzLmVucXVldWVTdG9yZU11dGF0aW9uKGNvbm5lY3Rpb25JZCwgXCJqb2luXCIsICgpID0+XG4gICAgICB0aGlzLnByZXNlbmNlU3RvcmUuam9pbihjb25uZWN0aW9uSWQsIHJvb21JZCksXG4gICAgKTtcbiAgfVxuXG4gIGxlYXZlKGNvbm5lY3Rpb25JZDogc3RyaW5nLCByb29tSWQ6IFdlYlNvY2tldFJvb21JZCk6IFByb21pc2U8dm9pZD4ge1xuICAgIGNvbnN0IG1ldGEgPSB0aGlzLm1lbW9yeS5nZXRDb25uZWN0aW9uKGNvbm5lY3Rpb25JZCk7XG4gICAgaWYgKCFtZXRhKSB7XG4gICAgICB0aGlzLmVtaXRNdXRhdGlvblNraXBwZWQoY29ubmVjdGlvbklkLCB7IG9wZXJhdGlvbjogXCJsZWF2ZVwiLCByb29tSWQgfSk7XG4gICAgICByZXR1cm4gUHJvbWlzZS5yZXNvbHZlKCk7XG4gICAgfVxuXG4gICAgdGhpcy5tZW1vcnkubGVhdmUoY29ubmVjdGlvbklkLCByb29tSWQpO1xuICAgIGNvbnN0IGNvbm5lY3Rpb24gPSB0aGlzLmxvY2FsQ29ubmVjdGlvbnMuZ2V0Q29ubmVjdGlvbihjb25uZWN0aW9uSWQpO1xuICAgIHRoaXMudGVsZW1ldHJ5Q29udHJvbGxlci5lbWl0KHtcbiAgICAgIG5hbWU6IFwid3Mucm9vbS5sZWZ0XCIsXG4gICAgICBsZXZlbDogXCJkZWJ1Z1wiLFxuICAgICAgY29ubmVjdGlvbklkLFxuICAgICAgbmFtZXNwYWNlOiBtZXRhLm5hbWVzcGFjZSxcbiAgICAgIHVzZXJJZDogY29ubmVjdGlvbj8udXNlcklkID8/IGZvcm1hdFVzZXJJZChtZXRhLnVzZXJJZCksXG4gICAgICBkZXRhaWw6IHsgcm9vbUlkIH0sXG4gICAgICAuLi5nZXRDb25uZWN0aW9uVGVsZW1ldHJ5Q29udGV4dChjb25uZWN0aW9uKSxcbiAgICB9KTtcbiAgICByZXR1cm4gdGhpcy5lbnF1ZXVlU3RvcmVNdXRhdGlvbihjb25uZWN0aW9uSWQsIFwibGVhdmVcIiwgKCkgPT5cbiAgICAgIHRoaXMucHJlc2VuY2VTdG9yZS5sZWF2ZShjb25uZWN0aW9uSWQsIHJvb21JZCksXG4gICAgKTtcbiAgfVxuXG4gIGJyb2FkY2FzdDxEYXRhPihldmVudDogc3RyaW5nLCBkYXRhOiBEYXRhLCBuYW1lc3BhY2U/OiBzdHJpbmcpOiBQcm9taXNlPHZvaWQ+IHtcbiAgICByZXR1cm4gdGhpcy5wdWJsaXNoVG9BdWRpZW5jZShXZWJTb2NrZXRBdWRpZW5jZS5hbGwobmFtZXNwYWNlKSwgZXZlbnQsIGRhdGEpO1xuICB9XG5cbiAgcHVibGlzaFRvUm9vbTxEYXRhPihcbiAgICByb29tSWQ6IFdlYlNvY2tldFJvb21JZCxcbiAgICBldmVudDogc3RyaW5nLFxuICAgIGRhdGE6IERhdGEsXG4gICAgbmFtZXNwYWNlPzogc3RyaW5nLFxuICApOiBQcm9taXNlPHZvaWQ+IHtcbiAgICByZXR1cm4gdGhpcy5wdWJsaXNoVG9BdWRpZW5jZShXZWJTb2NrZXRBdWRpZW5jZS5yb29tKHJvb21JZCwgbmFtZXNwYWNlKSwgZXZlbnQsIGRhdGEpO1xuICB9XG5cbiAgcHVibGlzaFRvVXNlcjxEYXRhPihcbiAgICB1c2VySWQ6IFdlYlNvY2tldFVzZXJJZCxcbiAgICBldmVudDogc3RyaW5nLFxuICAgIGRhdGE6IERhdGEsXG4gICAgbmFtZXNwYWNlPzogc3RyaW5nLFxuICApOiBQcm9taXNlPHZvaWQ+IHtcbiAgICByZXR1cm4gdGhpcy5wdWJsaXNoVG9BdWRpZW5jZShXZWJTb2NrZXRBdWRpZW5jZS51c2VyKHVzZXJJZCwgbmFtZXNwYWNlKSwgZXZlbnQsIGRhdGEpO1xuICB9XG5cbiAgcHVibGlzaFRvQXVkaWVuY2U8RGF0YT4oXG4gICAgYXVkaWVuY2U6IFdlYlNvY2tldEF1ZGllbmNlU3BlYyxcbiAgICBldmVudDogc3RyaW5nLFxuICAgIGRhdGE6IERhdGEsXG4gICk6IFByb21pc2U8dm9pZD4ge1xuICAgIHJldHVybiB0aGlzLmRlbGl2ZXJ5RW5naW5lLnB1Ymxpc2hUb0F1ZGllbmNlKGF1ZGllbmNlLCBldmVudCwgZGF0YSk7XG4gIH1cblxuICAvLyDslYTrnpgg7J296riwIEFQSeuKlCDrhbjrk5wg66Gc7LusIHNuYXBzaG90KOuzteyCrOuzuCnsnbTri6RcbiAgZ2V0Q29ubmVjdGlvbihjb25uZWN0aW9uSWQ6IHN0cmluZyk6IFdlYlNvY2tldENvbm5lY3Rpb25NZXRhIHwgdW5kZWZpbmVkIHtcbiAgICByZXR1cm4gdGhpcy5tZW1vcnkuZ2V0Q29ubmVjdGlvbihjb25uZWN0aW9uSWQpO1xuICB9XG5cbiAgZ2V0Q29ubmVjdGlvbkNvdW50KG5hbWVzcGFjZT86IHN0cmluZyk6IG51bWJlciB7XG4gICAgcmV0dXJuIHRoaXMubWVtb3J5LmdldENvbm5lY3Rpb25Db3VudChuYW1lc3BhY2UpO1xuICB9XG5cbiAgZ2V0Um9vbU1lbWJlcnMocm9vbUlkOiBXZWJTb2NrZXRSb29tSWQsIG5hbWVzcGFjZT86IHN0cmluZyk6IFdlYlNvY2tldENvbm5lY3Rpb25NZXRhW10ge1xuICAgIHJldHVybiB0aGlzLm1lbW9yeS5nZXRSb29tTWVtYmVycyhyb29tSWQsIG5hbWVzcGFjZSk7XG4gIH1cblxuICBnZXRTdGF0cygpOiBXZWJTb2NrZXRSZWdpc3RyeVN0YXRzIHtcbiAgICByZXR1cm4gdGhpcy5tZW1vcnkuZ2V0U3RhdHMoKTtcbiAgfVxuXG4gIC8qKlxuICAgKiDtgbTrn6zsiqTthLAg7KCE7LK07J2YIO2ZnOyEsSDrsKkg7J247JuQ7IiYLiBzdG9yZeqwgCDsi6TtjKjtlZjrqbQg64W465OcIOuhnOy7rCDsiJjsl5AgcGFydGlhbDogdHJ1ZeulvCDrtpnsl6wg67CY7ZmY7ZWY66mwIHJlamVjdO2VmOyngCDslYrripTri6QuXG4gICAqL1xuICBhc3luYyBnZXRSb29tTWVtYmVyQ291bnQoXG4gICAgcm9vbUlkOiBXZWJTb2NrZXRSb29tSWQsXG4gICAgbmFtZXNwYWNlPzogc3RyaW5nLFxuICApOiBQcm9taXNlPFdlYlNvY2tldFJvb21NZW1iZXJDb3VudD4ge1xuICAgIHRyeSB7XG4gICAgICBjb25zdCBjb3VudCA9IGF3YWl0IGNhbGxTdG9yZSgoKSA9PiB0aGlzLnByZXNlbmNlU3RvcmUuZ2V0Um9vbU1lbWJlckNvdW50KHJvb21JZCwgbmFtZXNwYWNlKSk7XG4gICAgICByZXR1cm4geyBjb3VudCwgcGFydGlhbDogZmFsc2UgfTtcbiAgICB9IGNhdGNoIChjYXVzZSkge1xuICAgICAgdGhpcy50ZWxlbWV0cnlDb250cm9sbGVyLmVtaXQoe1xuICAgICAgICBuYW1lOiBcIndzLnJlZ2lzdHJ5LnJlYWQuZmFpbGVkXCIsXG4gICAgICAgIGxldmVsOiBcIndhcm5cIixcbiAgICAgICAgZGV0YWlsOiB7IG9wZXJhdGlvbjogXCJnZXRSb29tTWVtYmVyQ291bnRcIiwgcm9vbUlkLCBlcnJvclR5cGU6IGRlc2NyaWJlRXJyb3IoY2F1c2UpIH0sXG4gICAgICB9KTtcbiAgICAgIHJldHVybiB7IGNvdW50OiB0aGlzLm1lbW9yeS5nZXRSb29tTWVtYmVyQ291bnQocm9vbUlkLCBuYW1lc3BhY2UpLCBwYXJ0aWFsOiB0cnVlIH07XG4gICAgfVxuICB9XG5cbiAgY2xvc2VBbGwoY29kZT86IG51bWJlciwgcmVhc29uPzogc3RyaW5nKTogdm9pZCB7XG4gICAgdGhpcy5sb2NhbENvbm5lY3Rpb25zLmNsb3NlQWxsKGNvZGUsIHJlYXNvbik7XG4gIH1cblxuICAvKipcbiAgICog66Gc7LusIOyXsOqysOydhCDri6vqs6AsIOynhO2WiSDspJHsnbggc3RvcmUgbXV0YXRpb27qs7wgcHVibGlzaOulvCBkcmFpblRpbWVvdXRNc+q5jOyngCDquLDri6TrprAg65KkIHN0b3Jl7JmAIGJ1c+ulvCDsooXro4ztlZzri6QuXG4gICAqL1xuICBhc3luYyBzaHV0ZG93bihjb2RlPzogbnVtYmVyLCByZWFzb24/OiBzdHJpbmcpOiBQcm9taXNlPHZvaWQ+IHtcbiAgICB0aGlzLmNsb3NlQWxsKGNvZGUsIHJlYXNvbik7XG5cbiAgICBsZXQgdGltZXI6IFJldHVyblR5cGU8dHlwZW9mIHNldFRpbWVvdXQ+IHwgdW5kZWZpbmVkO1xuICAgIGNvbnN0IGRlYWRsaW5lID0gbmV3IFByb21pc2U8dm9pZD4oKHJlc29sdmUpID0+IHtcbiAgICAgIHRpbWVyID0gc2V0VGltZW91dChyZXNvbHZlLCB0aGlzLmRyYWluVGltZW91dE1zKTtcbiAgICB9KTtcbiAgICB0cnkge1xuICAgICAgYXdhaXQgUHJvbWlzZS5yYWNlKFtcbiAgICAgICAgUHJvbWlzZS5hbGwoWy4uLnRoaXMubXV0YXRpb25RdWV1ZXMudmFsdWVzKCksIHRoaXMuZGVsaXZlcnlFbmdpbmUuZHJhaW5QdWJsaXNoZXMoKV0pLFxuICAgICAgICBkZWFkbGluZSxcbiAgICAgIF0pO1xuICAgIH0gZmluYWxseSB7XG4gICAgICBjbGVhclRpbWVvdXQodGltZXIpO1xuICAgIH1cblxuICAgIHRyeSB7XG4gICAgICBhd2FpdCBjYWxsU3RvcmUoKCkgPT4gdGhpcy5wcmVzZW5jZVN0b3JlLnNodXRkb3duPy4oKSk7XG4gICAgfSBjYXRjaCAoY2F1c2UpIHtcbiAgICAgIHRoaXMudGVsZW1ldHJ5Q29udHJvbGxlci5lbWl0KHtcbiAgICAgICAgbmFtZTogXCJ3cy5yZWdpc3RyeS5zaHV0ZG93bi5mYWlsZWRcIixcbiAgICAgICAgbGV2ZWw6IFwiZXJyb3JcIixcbiAgICAgICAgZGV0YWlsOiB7IGVycm9yVHlwZTogZGVzY3JpYmVFcnJvcihjYXVzZSkgfSxcbiAgICAgIH0pO1xuICAgIH1cbiAgICAvLyDqtazrj4Ug7ZW07KCc64qUIGRyYWlu7J20IOuBneuCrOycvOuvgOuhnCDsponsi5wg7Y+s6riw7ZWgIOyImCDsnojqsowg7J2066+4IOuBneuCnCBkZWFkbGluZeydhCDrhJjquLTri6RcbiAgICBhd2FpdCB0aGlzLmRlbGl2ZXJ5RW5naW5lLnNodXRkb3duKFByb21pc2UucmVzb2x2ZSgpKTtcbiAgfVxuXG4gIC8qKlxuICAgKiDshLjshZjrs4Qg7YGQIOuSpOyXkCBzdG9yZSDtmLjstpzsnYQg7J207Ja0IOu2meyduOuLpC4g7Iuk7Yyo64qUIHRlbGVtZXRyeeuhnOunjCDquLDroZ3tlZjqs6AsIOuwmO2ZmO2VnCBwcm9taXNl64qUIHJlamVjdO2VmOyngCDslYrripTri6QuXG4gICAqL1xuICBwcml2YXRlIGVucXVldWVTdG9yZU11dGF0aW9uKFxuICAgIHNlc3Npb25JZDogc3RyaW5nLFxuICAgIG9wZXJhdGlvbjogU3RvcmVNdXRhdGlvbk9wZXJhdGlvbixcbiAgICBjYWxsOiAoKSA9PiBNYXliZVByb21pc2U8dm9pZD4sXG4gICk6IFByb21pc2U8dm9pZD4ge1xuICAgIGNvbnN0IHByZXZpb3VzID0gdGhpcy5tdXRhdGlvblF1ZXVlcy5nZXQoc2Vzc2lvbklkKSA/PyBQcm9taXNlLnJlc29sdmUoKTtcbiAgICBjb25zdCBuZXh0OiBQcm9taXNlPHZvaWQ+ID0gcHJldmlvdXNcbiAgICAgIC50aGVuKGNhbGwpXG4gICAgICAudGhlbihcbiAgICAgICAgKCkgPT4gdW5kZWZpbmVkLFxuICAgICAgICAoY2F1c2U6IHVua25vd24pID0+IHtcbiAgICAgICAgICB0aGlzLnRlbGVtZXRyeUNvbnRyb2xsZXIuZW1pdCh7XG4gICAgICAgICAgICBuYW1lOiBcIndzLnJlZ2lzdHJ5Lm11dGF0aW9uLmZhaWxlZFwiLFxuICAgICAgICAgICAgbGV2ZWw6IFwid2FyblwiLFxuICAgICAgICAgICAgY29ubmVjdGlvbklkOiBzZXNzaW9uSWQsXG4gICAgICAgICAgICBkZXRhaWw6IHsgb3BlcmF0aW9uLCBlcnJvclR5cGU6IGRlc2NyaWJlRXJyb3IoY2F1c2UpIH0sXG4gICAgICAgICAgfSk7XG4gICAgICAgIH0sXG4gICAgICApXG4gICAgICAudGhlbigoKSA9PiB7XG4gICAgICAgIC8vIOuniOyngOuniSBtdXRhdGlvbuydtCDrgZ3rgpjrqbQg7YGQ66W8IOu5hOybjCDshLjshZgg7IiY66eM7YG8IHByb21pc2XqsIAg7IyT7J207KeAIOyViuqyjCDtlZzri6RcbiAgICAgICAgaWYgKHRoaXMubXV0YXRpb25RdWV1ZXMuZ2V0KHNlc3Npb25JZCkgPT09IG5leHQpIHtcbiAgICAgICAgICB0aGlzLm11dGF0aW9uUXVldWVzLmRlbGV0ZShzZXNzaW9uSWQpO1xuICAgICAgICB9XG4gICAgICB9KTtcbiAgICB0aGlzLm11dGF0aW9uUXVldWVzLnNldChzZXNzaW9uSWQsIG5leHQpO1xuICAgIHJldHVybiBuZXh0O1xuICB9XG5cbiAgcHJpdmF0ZSBlbWl0TXV0YXRpb25Ta2lwcGVkKFxuICAgIGNvbm5lY3Rpb25JZDogc3RyaW5nLFxuICAgIGRldGFpbDoge1xuICAgICAgb3BlcmF0aW9uOiBcImFjdGl2YXRlXCIgfCBcInNldFVzZXJJZFwiIHwgXCJjbGVhclVzZXJJZFwiIHwgXCJqb2luXCIgfCBcImxlYXZlXCI7XG4gICAgICByb29tSWQ/OiBXZWJTb2NrZXRSb29tSWQ7XG4gICAgICB1c2VySWQ/OiBXZWJTb2NrZXRVc2VySWQ7XG4gICAgfSxcbiAgKTogdm9pZCB7XG4gICAgdGhpcy50ZWxlbWV0cnlDb250cm9sbGVyLmVtaXQoe1xuICAgICAgbmFtZTogXCJ3cy5yZWdpc3RyeS5tdXRhdGlvbi5za2lwcGVkXCIsXG4gICAgICBsZXZlbDogXCJkZWJ1Z1wiLFxuICAgICAgY29ubmVjdGlvbklkLFxuICAgICAgZGV0YWlsOiB7IC4uLmRldGFpbCwgcmVhc29uOiBcImNvbm5lY3Rpb25NaXNzaW5nXCIgfSxcbiAgICB9KTtcbiAgfVxufVxuXG4vLyBzdG9yZSDtmLjstpzsnZgg64+Z6riwIHRocm9366W8IHJlamVjdGlvbuycvOuhnCDrsJTqvrzri6RcbmZ1bmN0aW9uIGNhbGxTdG9yZTxUPihjYWxsOiAoKSA9PiBNYXliZVByb21pc2U8VD4pOiBQcm9taXNlPFQ+IHtcbiAgcmV0dXJuIFByb21pc2UucmVzb2x2ZSgpLnRoZW4oY2FsbCk7XG59XG5cbmZ1bmN0aW9uIGFzc2VydE1hdGNoaW5nTm9kZUlkKFxuICBzb3VyY2U6IFwicHJlc2VuY2VTdG9yZVwiIHwgXCJjbHVzdGVyQnVzXCIsXG4gIHNvdXJjZU5vZGVJZDogc3RyaW5nIHwgdW5kZWZpbmVkLFxuICBub2RlSWQ6IHN0cmluZyxcbik6IHZvaWQge1xuICBpZiAoc291cmNlTm9kZUlkICE9PSB1bmRlZmluZWQgJiYgc291cmNlTm9kZUlkICE9PSBub2RlSWQpIHtcbiAgICB0aHJvdyBuZXcgRXJyb3IoXG4gICAgICBgV2ViU29ja2V0ICR7c291cmNlfS5ub2RlSWQgKCR7c291cmNlTm9kZUlkfSkgbXVzdCBtYXRjaCB0aGUgcnVudGltZSBub2RlSWQgKCR7bm9kZUlkfSkuYCxcbiAgICApO1xuICB9XG59XG5cbmZ1bmN0aW9uIGRlc2NyaWJlRXJyb3IoY2F1c2U6IHVua25vd24pOiBzdHJpbmcge1xuICByZXR1cm4gY2F1c2UgaW5zdGFuY2VvZiBFcnJvciA/IGNhdXNlLm5hbWUgOiBydW50aW1lVHlwZU9mKGNhdXNlKTtcbn1cblxuZnVuY3Rpb24gZ2V0Q29ubmVjdGlvblRlbGVtZXRyeUNvbnRleHQoXG4gIGNvbm5lY3Rpb246IE1hbmFnZWRXZWJTb2NrZXRDb25uZWN0aW9uIHwgdW5kZWZpbmVkLFxuKTogV2ViU29ja2V0VGVsZW1ldHJ5Q29ubmVjdGlvbkNvbnRleHQge1xuICBpZiAoIWNvbm5lY3Rpb24gfHwgIWlzVGVsZW1ldHJ5Q29udGV4dFByb3ZpZGVyKGNvbm5lY3Rpb24pKSB7XG4gICAgcmV0dXJuIHt9O1xuICB9XG5cbiAgcmV0dXJuIGNvbm5lY3Rpb24uZ2V0VGVsZW1ldHJ5Q29udGV4dCgpO1xufVxuXG4vLyB0ZWxlbWV0cnkgcmVjb3Jk7J2YIHVzZXJJZOuKlCBzdHJpbmcg7KCV6rec7ZmUIOKAlCBwcmVzZW5jZeuKlCBudW1iZXIgfCBzdHJpbmcg6re464yA66GcIOuztOq0gO2VqFxuZnVuY3Rpb24gZm9ybWF0VXNlcklkKHVzZXJJZDogV2ViU29ja2V0VXNlcklkIHwgdW5kZWZpbmVkKTogc3RyaW5nIHwgdW5kZWZpbmVkIHtcbiAgcmV0dXJuIHVzZXJJZCA9PT0gdW5kZWZpbmVkID8gdW5kZWZpbmVkIDogU3RyaW5nKHVzZXJJZCk7XG59XG5cbmZ1bmN0aW9uIGlzVGVsZW1ldHJ5Q29udGV4dFByb3ZpZGVyKFxuICBjb25uZWN0aW9uOiBNYW5hZ2VkV2ViU29ja2V0Q29ubmVjdGlvbixcbik6IGNvbm5lY3Rpb24gaXMgTWFuYWdlZFdlYlNvY2tldENvbm5lY3Rpb24gJiBUZWxlbWV0cnlDb250ZXh0UHJvdmlkZXIge1xuICByZXR1cm4gXCJnZXRUZWxlbWV0cnlDb250ZXh0XCIgaW4gY29ubmVjdGlvbiAmJiBpc0Z1bmN0aW9uVmFsdWUoY29ubmVjdGlvbi5nZXRUZWxlbWV0cnlDb250ZXh0KTtcbn1cbiJdLCJtYXBwaW5ncyI6Ijs7Ozs7Ozs7Ozs7O0FBMmRBLFNBQVMsVUFBYSxNQUF5QztDQUM3RCxPQUFPLFFBQVEsUUFBUSxDQUFDLENBQUMsS0FBSyxJQUFJO0FBQ3BDO0FBRUEsU0FBUyxxQkFDUCxRQUNBLGNBQ0EsUUFDTTtDQUNOLElBQUksaUJBQWlCLFVBQWEsaUJBQWlCLFFBQ2pELE1BQU0sSUFBSSxNQUNSLGFBQWEs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