@1upmonster/duel 0.1.8 → 0.2.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (186) hide show
  1. package/README.md +110 -59
  2. package/dist/admin.d.ts +25 -40
  3. package/dist/admin.js +93 -114
  4. package/dist/encryption.js +1 -2
  5. package/dist/generated/duel/accounts/index.d.ts +10 -0
  6. package/dist/generated/duel/accounts/index.js +11 -0
  7. package/dist/generated/duel/accounts/matchTicket.d.ts +38 -0
  8. package/dist/generated/duel/accounts/matchTicket.js +45 -0
  9. package/dist/generated/duel/accounts/queue.d.ts +42 -0
  10. package/dist/generated/duel/accounts/queue.js +45 -0
  11. package/dist/generated/duel/accounts/tenant.d.ts +41 -0
  12. package/dist/generated/duel/accounts/tenant.js +44 -0
  13. package/dist/generated/duel/errors/duel.d.ts +31 -0
  14. package/dist/generated/duel/errors/duel.js +35 -0
  15. package/dist/generated/duel/errors/index.d.ts +8 -0
  16. package/dist/generated/duel/errors/index.js +9 -0
  17. package/dist/generated/duel/index.d.ts +12 -0
  18. package/dist/generated/duel/index.js +13 -0
  19. package/dist/generated/duel/instructions/cancelTicket.d.ts +45 -0
  20. package/dist/generated/duel/instructions/cancelTicket.js +56 -0
  21. package/dist/generated/duel/instructions/closeTicket.d.ts +45 -0
  22. package/dist/generated/duel/instructions/closeTicket.js +56 -0
  23. package/dist/generated/duel/instructions/commitTickets.d.ts +39 -0
  24. package/dist/generated/duel/instructions/commitTickets.js +50 -0
  25. package/dist/generated/duel/instructions/createTicket.d.ts +48 -0
  26. package/dist/generated/duel/instructions/createTicket.js +63 -0
  27. package/dist/generated/duel/instructions/delegateQueue.d.ts +69 -0
  28. package/dist/generated/duel/instructions/delegateQueue.js +90 -0
  29. package/dist/generated/duel/instructions/delegateTicket.d.ts +69 -0
  30. package/dist/generated/duel/instructions/delegateTicket.js +90 -0
  31. package/dist/generated/duel/instructions/flushMatches.d.ts +37 -0
  32. package/dist/generated/duel/instructions/flushMatches.js +43 -0
  33. package/dist/generated/duel/instructions/index.d.ts +19 -0
  34. package/dist/generated/duel/instructions/index.js +20 -0
  35. package/dist/generated/duel/instructions/initializeQueue.d.ts +48 -0
  36. package/dist/generated/duel/instructions/initializeQueue.js +63 -0
  37. package/dist/generated/duel/instructions/initializeTenant.d.ts +70 -0
  38. package/dist/generated/duel/instructions/initializeTenant.js +67 -0
  39. package/dist/generated/duel/instructions/joinQueue.d.ts +51 -0
  40. package/dist/generated/duel/instructions/joinQueue.js +56 -0
  41. package/dist/generated/duel/instructions/processUndelegation.d.ts +43 -0
  42. package/dist/generated/duel/instructions/processUndelegation.js +49 -0
  43. package/dist/generated/duel/instructions/setupTicketPermission.d.ts +54 -0
  44. package/dist/generated/duel/instructions/setupTicketPermission.js +63 -0
  45. package/dist/generated/duel/programs/duel.d.ts +92 -0
  46. package/dist/generated/duel/programs/duel.js +146 -0
  47. package/dist/generated/duel/programs/index.d.ts +8 -0
  48. package/dist/generated/duel/programs/index.js +9 -0
  49. package/dist/generated/duel/types/accountType.d.ts +25 -0
  50. package/dist/generated/duel/types/accountType.js +25 -0
  51. package/dist/generated/duel/types/index.d.ts +13 -0
  52. package/dist/generated/duel/types/index.js +14 -0
  53. package/dist/generated/duel/types/matchEntry.d.ts +23 -0
  54. package/dist/generated/duel/types/matchEntry.js +18 -0
  55. package/dist/generated/duel/types/matchFound.d.ts +23 -0
  56. package/dist/generated/duel/types/matchFound.js +18 -0
  57. package/dist/generated/duel/types/pendingMatch.d.ts +21 -0
  58. package/dist/generated/duel/types/pendingMatch.js +18 -0
  59. package/dist/generated/duel/types/queueEntry.d.ts +19 -0
  60. package/dist/generated/duel/types/queueEntry.js +18 -0
  61. package/dist/generated/duel/types/ticketStatus.d.ts +40 -0
  62. package/dist/generated/duel/types/ticketStatus.js +25 -0
  63. package/dist/generated/rps-game/accounts/gameSession.d.ts +40 -0
  64. package/dist/generated/rps-game/accounts/gameSession.js +45 -0
  65. package/dist/generated/rps-game/accounts/index.d.ts +9 -0
  66. package/dist/generated/rps-game/accounts/index.js +10 -0
  67. package/dist/generated/rps-game/accounts/playerProfile.d.ts +36 -0
  68. package/dist/generated/rps-game/accounts/playerProfile.js +47 -0
  69. package/dist/generated/rps-game/errors/index.d.ts +8 -0
  70. package/dist/generated/rps-game/errors/index.js +9 -0
  71. package/dist/generated/rps-game/errors/rpsGame.d.ts +25 -0
  72. package/dist/generated/rps-game/errors/rpsGame.js +29 -0
  73. package/dist/generated/rps-game/index.d.ts +12 -0
  74. package/dist/generated/rps-game/index.js +13 -0
  75. package/dist/generated/rps-game/instructions/closePlayer.d.ts +45 -0
  76. package/dist/generated/rps-game/instructions/closePlayer.js +56 -0
  77. package/dist/generated/rps-game/instructions/delegatePda.d.ts +69 -0
  78. package/dist/generated/rps-game/instructions/delegatePda.js +90 -0
  79. package/dist/generated/rps-game/instructions/index.d.ts +16 -0
  80. package/dist/generated/rps-game/instructions/index.js +17 -0
  81. package/dist/generated/rps-game/instructions/initializePlayer.d.ts +48 -0
  82. package/dist/generated/rps-game/instructions/initializePlayer.js +63 -0
  83. package/dist/generated/rps-game/instructions/makeChoice.d.ts +44 -0
  84. package/dist/generated/rps-game/instructions/makeChoice.js +46 -0
  85. package/dist/generated/rps-game/instructions/onMatchFound.d.ts +43 -0
  86. package/dist/generated/rps-game/instructions/onMatchFound.js +45 -0
  87. package/dist/generated/rps-game/instructions/persistResults.d.ts +43 -0
  88. package/dist/generated/rps-game/instructions/persistResults.js +50 -0
  89. package/dist/generated/rps-game/instructions/processUndelegation.d.ts +43 -0
  90. package/dist/generated/rps-game/instructions/processUndelegation.js +49 -0
  91. package/dist/generated/rps-game/instructions/startGame.d.ts +54 -0
  92. package/dist/generated/rps-game/instructions/startGame.js +67 -0
  93. package/dist/generated/rps-game/instructions/startGameWithTicket.d.ts +57 -0
  94. package/dist/generated/rps-game/instructions/startGameWithTicket.js +67 -0
  95. package/dist/generated/rps-game/programs/index.d.ts +8 -0
  96. package/dist/generated/rps-game/programs/index.js +9 -0
  97. package/dist/generated/rps-game/programs/rpsGame.d.ts +78 -0
  98. package/dist/generated/rps-game/programs/rpsGame.js +118 -0
  99. package/dist/generated/rps-game/types/accountType.d.ts +34 -0
  100. package/dist/generated/rps-game/types/accountType.js +25 -0
  101. package/dist/generated/rps-game/types/choice.d.ts +17 -0
  102. package/dist/generated/rps-game/types/choice.js +25 -0
  103. package/dist/generated/rps-game/types/gameResult.d.ts +26 -0
  104. package/dist/generated/rps-game/types/gameResult.js +25 -0
  105. package/dist/generated/rps-game/types/index.d.ts +10 -0
  106. package/dist/generated/rps-game/types/index.js +11 -0
  107. package/dist/index.d.ts +2 -1
  108. package/dist/index.js +3 -2
  109. package/dist/player.d.ts +25 -46
  110. package/dist/player.js +81 -140
  111. package/dist/tee.d.ts +14 -0
  112. package/dist/tee.js +62 -0
  113. package/dist/transaction.d.ts +11 -0
  114. package/dist/transaction.js +50 -0
  115. package/dist/utils.d.ts +4 -4
  116. package/dist/utils.js +23 -8
  117. package/package.json +3 -6
  118. package/src/admin.ts +151 -161
  119. package/src/duel.json +66 -7
  120. package/src/encryption.ts +0 -3
  121. package/src/generated/duel/accounts/index.ts +11 -0
  122. package/src/generated/duel/accounts/matchTicket.ts +77 -0
  123. package/src/generated/duel/accounts/queue.ts +77 -0
  124. package/src/generated/duel/accounts/tenant.ts +76 -0
  125. package/src/generated/duel/errors/duel.ts +46 -0
  126. package/src/generated/duel/errors/index.ts +9 -0
  127. package/src/generated/duel/index.ts +13 -0
  128. package/src/generated/duel/instructions/cancelTicket.ts +100 -0
  129. package/src/generated/duel/instructions/closeTicket.ts +100 -0
  130. package/src/generated/duel/instructions/commitTickets.ts +84 -0
  131. package/src/generated/duel/instructions/createTicket.ts +109 -0
  132. package/src/generated/duel/instructions/delegateQueue.ts +157 -0
  133. package/src/generated/duel/instructions/delegateTicket.ts +157 -0
  134. package/src/generated/duel/instructions/flushMatches.ts +76 -0
  135. package/src/generated/duel/instructions/index.ts +20 -0
  136. package/src/generated/duel/instructions/initializeQueue.ts +109 -0
  137. package/src/generated/duel/instructions/initializeTenant.ts +126 -0
  138. package/src/generated/duel/instructions/joinQueue.ts +106 -0
  139. package/src/generated/duel/instructions/processUndelegation.ts +86 -0
  140. package/src/generated/duel/instructions/setupTicketPermission.ts +115 -0
  141. package/src/generated/duel/programs/duel.ts +108 -0
  142. package/src/generated/duel/programs/index.ts +9 -0
  143. package/src/generated/duel/types/accountType.ts +36 -0
  144. package/src/generated/duel/types/index.ts +14 -0
  145. package/src/generated/duel/types/matchEntry.ts +25 -0
  146. package/src/generated/duel/types/matchFound.ts +25 -0
  147. package/src/generated/duel/types/pendingMatch.ts +25 -0
  148. package/src/generated/duel/types/queueEntry.ts +25 -0
  149. package/src/generated/duel/types/ticketStatus.ts +38 -0
  150. package/src/generated/rps-game/accounts/gameSession.ts +77 -0
  151. package/src/generated/rps-game/accounts/index.ts +10 -0
  152. package/src/generated/rps-game/accounts/playerProfile.ts +80 -0
  153. package/src/generated/rps-game/errors/index.ts +9 -0
  154. package/src/generated/rps-game/errors/rpsGame.ts +40 -0
  155. package/src/generated/rps-game/index.ts +13 -0
  156. package/src/generated/rps-game/instructions/closePlayer.ts +100 -0
  157. package/src/generated/rps-game/instructions/delegatePda.ts +157 -0
  158. package/src/generated/rps-game/instructions/index.ts +17 -0
  159. package/src/generated/rps-game/instructions/initializePlayer.ts +109 -0
  160. package/src/generated/rps-game/instructions/makeChoice.ts +84 -0
  161. package/src/generated/rps-game/instructions/onMatchFound.ts +79 -0
  162. package/src/generated/rps-game/instructions/persistResults.ts +88 -0
  163. package/src/generated/rps-game/instructions/processUndelegation.ts +86 -0
  164. package/src/generated/rps-game/instructions/startGame.ts +118 -0
  165. package/src/generated/rps-game/instructions/startGameWithTicket.ts +121 -0
  166. package/src/generated/rps-game/programs/index.ts +9 -0
  167. package/src/generated/rps-game/programs/rpsGame.ts +95 -0
  168. package/src/generated/rps-game/types/accountType.ts +36 -0
  169. package/src/generated/rps-game/types/choice.ts +25 -0
  170. package/src/generated/rps-game/types/gameResult.ts +37 -0
  171. package/src/generated/rps-game/types/index.ts +11 -0
  172. package/src/index.ts +2 -1
  173. package/src/player.ts +129 -192
  174. package/src/{idl/private_matchmaking.json → rps_game.json} +547 -583
  175. package/src/tee.ts +79 -0
  176. package/src/transaction.ts +90 -0
  177. package/src/utils.ts +35 -20
  178. package/tsconfig.json +2 -2
  179. package/dist/client.d.ts +0 -54
  180. package/dist/client.js +0 -265
  181. package/dist/duel.json +0 -1207
  182. package/dist/private_matchmaking.json +0 -534
  183. package/dist/types.d.ts +0 -635
  184. package/dist/types.js +0 -2
  185. package/src/idl/private_matchmaking.ts +0 -1033
  186. package/src/types.ts +0 -300
package/src/tee.ts ADDED
@@ -0,0 +1,79 @@
1
+ import {
2
+ getBase58Decoder,
3
+ createSignableMessage,
4
+ type Address,
5
+ type MessagePartialSigner,
6
+ } from "@solana/kit";
7
+
8
+ export type { MessagePartialSigner as MessageSigner };
9
+
10
+ /**
11
+ * Authenticate with the MagicBlock TEE via challenge-sign flow.
12
+ */
13
+ export async function getAuthToken(
14
+ rpcUrl: string,
15
+ signer: MessagePartialSigner,
16
+ ): Promise<{ token: string; expiresAt: number }> {
17
+ const challengeRes = await fetch(
18
+ `${rpcUrl}/auth/challenge?pubkey=${signer.address}`
19
+ );
20
+ if (!challengeRes.ok) {
21
+ throw new Error(`TEE challenge failed: ${challengeRes.statusText}`);
22
+ }
23
+ const { challenge } = (await challengeRes.json()) as { challenge: string };
24
+
25
+ const challengeBytes = new TextEncoder().encode(challenge);
26
+ const [sigDict] = await signer.signMessages([createSignableMessage(challengeBytes)]);
27
+ const signature = sigDict[signer.address as Address];
28
+ const signatureString = getBase58Decoder().decode(signature);
29
+
30
+ const tokenRes = await fetch(`${rpcUrl}/auth/login`, {
31
+ method: "POST",
32
+ headers: { "Content-Type": "application/json" },
33
+ body: JSON.stringify({
34
+ pubkey: signer.address,
35
+ challenge,
36
+ signature: signatureString,
37
+ }),
38
+ });
39
+ const authJson = (await tokenRes.json()) as { token: string; expiresAt?: number; error?: string };
40
+ if (tokenRes.status !== 200) {
41
+ throw new Error(`Failed to authenticate: ${authJson.error}`);
42
+ }
43
+ const expiresAt = authJson.expiresAt ?? Date.now() + 1000 * 60 * 60 * 24 * 30;
44
+ return { token: authJson.token, expiresAt };
45
+ }
46
+
47
+ /**
48
+ * Poll the TEE /permission endpoint until the given PDA has authorized users,
49
+ * indicating delegation is active. Returns false on timeout (does not throw).
50
+ */
51
+ export async function waitUntilPermissionActive(
52
+ teeUrlWithToken: string,
53
+ pda: Address,
54
+ timeoutMs = 30000,
55
+ ): Promise<boolean> {
56
+ // Parse URL: "https://host/path?token=xxx" -> baseUrl="https://host/path", tokenParam="token=xxx"
57
+ const [baseUrl, tokenParam] = teeUrlWithToken.replace("/?", "?").split("?");
58
+ let permissionUrl: string;
59
+ if (tokenParam) {
60
+ permissionUrl = `${baseUrl}/permission?${tokenParam}&pubkey=${pda}`;
61
+ } else {
62
+ permissionUrl = `${baseUrl}/permission?pubkey=${pda}`;
63
+ }
64
+
65
+ const start = Date.now();
66
+ while (Date.now() - start < timeoutMs) {
67
+ try {
68
+ const res = await fetch(permissionUrl);
69
+ if (res.ok) {
70
+ const { authorizedUsers } = (await res.json()) as { authorizedUsers?: unknown[] };
71
+ if (authorizedUsers && authorizedUsers.length > 0) return true;
72
+ }
73
+ } catch {
74
+ // ignore transient errors, keep polling
75
+ }
76
+ await new Promise((r) => setTimeout(r, 400));
77
+ }
78
+ return false;
79
+ }
@@ -0,0 +1,90 @@
1
+ import {
2
+ createTransactionMessage,
3
+ setTransactionMessageFeePayerSigner,
4
+ setTransactionMessageLifetimeUsingBlockhash,
5
+ appendTransactionMessageInstruction,
6
+ appendTransactionMessageInstructions,
7
+ signTransactionMessageWithSigners,
8
+ getBase64EncodedWireTransaction,
9
+ pipe,
10
+ type Instruction,
11
+ type SolanaRpcApi,
12
+ type Rpc,
13
+ type TransactionSigner,
14
+ } from "@solana/kit";
15
+
16
+ type SolanaRpc = Rpc<SolanaRpcApi>;
17
+
18
+ /**
19
+ * Build, sign with a Kit keypair signer, and send a single instruction.
20
+ */
21
+ export async function sendInstruction(
22
+ rpc: SolanaRpc,
23
+ instruction: Instruction,
24
+ signer: TransactionSigner,
25
+ ): Promise<string> {
26
+ const { value: latestBlockhash } = await rpc.getLatestBlockhash().send();
27
+
28
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
29
+ const tx: any = pipe(
30
+ createTransactionMessage({ version: 0 as const }),
31
+ (m) => setTransactionMessageFeePayerSigner(signer, m),
32
+ (m) => setTransactionMessageLifetimeUsingBlockhash(latestBlockhash, m),
33
+ (m) => appendTransactionMessageInstruction(instruction, m),
34
+ );
35
+
36
+ const signedTx = await signTransactionMessageWithSigners(tx);
37
+ const encoded = getBase64EncodedWireTransaction(signedTx);
38
+
39
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
40
+ const sig = await (rpc.sendTransaction(encoded as any, {
41
+ encoding: "base64",
42
+ skipPreflight: true,
43
+ }).send() as Promise<string>);
44
+
45
+ // Poll for status to detect runtime errors
46
+ for (let i = 0; i < 8; i++) {
47
+ await new Promise(r => setTimeout(r, 1000));
48
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
49
+ const statuses = await (rpc as any).getSignatureStatuses([sig], { searchTransactionHistory: false }).send().catch(() => null);
50
+ const status = statuses?.value?.[0];
51
+ if (status) {
52
+ if (status.err) {
53
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
54
+ console.error(`[TX] ${sig.slice(0, 16)}... FAILED:`, JSON.stringify(status.err, (_, v) => typeof v === 'bigint' ? v.toString() : v));
55
+ } else if (status.confirmationStatus) {
56
+ console.log(`[TX] ${sig.slice(0, 16)}... ${status.confirmationStatus}`);
57
+ }
58
+ break;
59
+ }
60
+ }
61
+ return sig;
62
+ }
63
+
64
+ /**
65
+ * Build, sign, and send multiple instructions in a single transaction.
66
+ */
67
+ export async function sendInstructions(
68
+ rpc: SolanaRpc,
69
+ instructions: Instruction[],
70
+ signer: TransactionSigner,
71
+ ): Promise<string> {
72
+ const { value: latestBlockhash } = await rpc.getLatestBlockhash().send();
73
+
74
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
75
+ const tx: any = pipe(
76
+ createTransactionMessage({ version: 0 as const }),
77
+ (m) => setTransactionMessageFeePayerSigner(signer, m),
78
+ (m) => setTransactionMessageLifetimeUsingBlockhash(latestBlockhash, m),
79
+ (m) => appendTransactionMessageInstructions(instructions, m),
80
+ );
81
+
82
+ const signedTx = await signTransactionMessageWithSigners(tx);
83
+ const encoded = getBase64EncodedWireTransaction(signedTx);
84
+
85
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
86
+ return rpc.sendTransaction(encoded as any, {
87
+ encoding: "base64",
88
+ skipPreflight: true,
89
+ }).send() as Promise<string>;
90
+ }
package/src/utils.ts CHANGED
@@ -1,30 +1,45 @@
1
- import * as web3 from "@solana/web3.js";
2
- import * as anchor from "@coral-xyz/anchor";
1
+ import {
2
+ getProgramDerivedAddress,
3
+ getAddressEncoder,
4
+ getUtf8Encoder,
5
+ type Address,
6
+ } from "@solana/kit";
7
+
8
+ const addressEncoder = getAddressEncoder();
9
+ const utf8Encoder = getUtf8Encoder();
3
10
 
4
11
  export const QUEUE_SEED = "queue";
5
12
  export const TENANT_SEED = "tenant";
6
13
  export const TICKET_SEED = "ticket";
7
14
 
8
- export function deriveQueuePda(programId: web3.PublicKey, authority: web3.PublicKey): web3.PublicKey {
9
- const [pda] = web3.PublicKey.findProgramAddressSync(
10
- [Buffer.from(QUEUE_SEED), authority.toBuffer()],
11
- programId
12
- );
13
- return pda;
15
+ export async function deriveQueuePda(programId: Address, authority: Address): Promise<Address> {
16
+ const [pda] = await getProgramDerivedAddress({
17
+ programAddress: programId,
18
+ seeds: [utf8Encoder.encode(QUEUE_SEED), addressEncoder.encode(authority)],
19
+ });
20
+ return pda;
14
21
  }
15
22
 
16
- export function deriveTenantPda(programId: web3.PublicKey, authority: web3.PublicKey): web3.PublicKey {
17
- const [pda] = web3.PublicKey.findProgramAddressSync(
18
- [Buffer.from(TENANT_SEED), authority.toBuffer()],
19
- programId
20
- );
21
- return pda;
23
+ export async function deriveTenantPda(programId: Address, authority: Address): Promise<Address> {
24
+ const [pda] = await getProgramDerivedAddress({
25
+ programAddress: programId,
26
+ seeds: [utf8Encoder.encode(TENANT_SEED), addressEncoder.encode(authority)],
27
+ });
28
+ return pda;
22
29
  }
23
30
 
24
- export function deriveTicketPda(programId: web3.PublicKey, player: web3.PublicKey, tenant: web3.PublicKey): web3.PublicKey {
25
- const [pda] = web3.PublicKey.findProgramAddressSync(
26
- [Buffer.from(TICKET_SEED), player.toBuffer(), tenant.toBuffer()],
27
- programId
28
- );
29
- return pda;
31
+ export async function deriveTicketPda(
32
+ programId: Address,
33
+ player: Address,
34
+ tenant: Address
35
+ ): Promise<Address> {
36
+ const [pda] = await getProgramDerivedAddress({
37
+ programAddress: programId,
38
+ seeds: [
39
+ utf8Encoder.encode(TICKET_SEED),
40
+ addressEncoder.encode(player),
41
+ addressEncoder.encode(tenant),
42
+ ],
43
+ });
44
+ return pda;
30
45
  }
package/tsconfig.json CHANGED
@@ -1,8 +1,8 @@
1
1
  {
2
2
  "compilerOptions": {
3
3
  "target": "es2020",
4
- "module": "nodenext",
5
- "moduleResolution": "nodenext",
4
+ "module": "esnext",
5
+ "moduleResolution": "bundler",
6
6
  "noEmit": false,
7
7
  "lib": ["es2020", "dom"],
8
8
  "declaration": true,
package/dist/client.d.ts DELETED
@@ -1,54 +0,0 @@
1
- import { Program, AnchorProvider } from "@coral-xyz/anchor";
2
- import { PublicKey, Keypair, TransactionSignature } from "@solana/web3.js";
3
- import { PrivateMatchmaking } from "./idl/private_matchmaking";
4
- import { MatchmakingClientConfig, QueueHead, QueuePage, PlayerStatus, JoinQueueResult } from "./types";
5
- import { EncryptionProvider } from "./encryption";
6
- export declare class MatchmakingClient {
7
- program: Program<PrivateMatchmaking>;
8
- provider: AnchorProvider;
9
- config: MatchmakingClientConfig;
10
- encryption: EncryptionProvider;
11
- constructor(provider: AnchorProvider, programId: PublicKey, config?: MatchmakingClientConfig);
12
- /**
13
- * Initialize a new matchmaking queue.
14
- */
15
- initializeQueue(queueId: string, config: any, capacity: number, pageSize?: number): Promise<PublicKey>;
16
- /**
17
- * Initialize a specific page for the queue.
18
- */
19
- initializePage(queue: PublicKey, index: number): Promise<PublicKey>;
20
- /**
21
- * Delegate the queue to the Privacy Layer (Ephemeral Rollup).
22
- */
23
- delegateQueue(queueId: string, validatorOverride?: PublicKey): Promise<TransactionSignature>;
24
- /**
25
- * Join the queue.
26
- */
27
- joinQueue(queue: PublicKey, playerGameAccount: PublicKey, tenantProgramId: PublicKey): Promise<JoinQueueResult>;
28
- /**
29
- * Unlock a player manually (refund rent).
30
- */
31
- unlockPlayer(playerGameAccount: PublicKey, playerWallet: PublicKey): Promise<TransactionSignature>;
32
- /**
33
- * Process matches on a specific page.
34
- * Usually called by the off-chain worker or manually for testing.
35
- */
36
- processMatch(queue: PublicKey, pageIndex: number): Promise<TransactionSignature>;
37
- /**
38
- * Resize the queue capacity.
39
- */
40
- resizeQueue(queue: PublicKey, newCapacity: number): Promise<TransactionSignature>;
41
- getQueue(queuePda: PublicKey): Promise<QueueHead>;
42
- getPage(pagePda: PublicKey): Promise<QueuePage>;
43
- getPlayerStatus(statusPda: PublicKey): Promise<PlayerStatus>;
44
- getPlayerStatusForGameAccount(playerGameAccount: PublicKey): Promise<PlayerStatus>;
45
- createMockPlayer(playerAccount: Keypair, elo: number): Promise<TransactionSignature>;
46
- /**
47
- * Close a queue page to reclaim rent.
48
- */
49
- closePage(queue: PublicKey, index: number): Promise<TransactionSignature>;
50
- /**
51
- * Close a queue head to reclaim rent.
52
- */
53
- closeQueue(queueId: string): Promise<TransactionSignature>;
54
- }
package/dist/client.js DELETED
@@ -1,265 +0,0 @@
1
- "use strict";
2
- var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
3
- if (k2 === undefined) k2 = k;
4
- var desc = Object.getOwnPropertyDescriptor(m, k);
5
- if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
6
- desc = { enumerable: true, get: function() { return m[k]; } };
7
- }
8
- Object.defineProperty(o, k2, desc);
9
- }) : (function(o, m, k, k2) {
10
- if (k2 === undefined) k2 = k;
11
- o[k2] = m[k];
12
- }));
13
- var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
14
- Object.defineProperty(o, "default", { enumerable: true, value: v });
15
- }) : function(o, v) {
16
- o["default"] = v;
17
- });
18
- var __importStar = (this && this.__importStar) || function (mod) {
19
- if (mod && mod.__esModule) return mod;
20
- var result = {};
21
- if (mod != null) for (var k in mod) if (k !== "default" && Object.prototype.hasOwnProperty.call(mod, k)) __createBinding(result, mod, k);
22
- __setModuleDefault(result, mod);
23
- return result;
24
- };
25
- Object.defineProperty(exports, "__esModule", { value: true });
26
- exports.MatchmakingClient = void 0;
27
- const anchor = __importStar(require("@coral-xyz/anchor"));
28
- const anchor_1 = require("@coral-xyz/anchor");
29
- const web3_js_1 = require("@solana/web3.js");
30
- const utils_1 = require("./utils");
31
- const encryption_1 = require("./encryption");
32
- // Default Validator for Devnet (MagicBlock)
33
- const DEFAULT_VALIDATOR = "FnE6VJT5QNZdedZPnCoLsARgBwoE6DeJNjBs2H1gySXA";
34
- class MatchmakingClient {
35
- constructor(provider, programId, config = {}) {
36
- this.provider = provider;
37
- this.config = config;
38
- this.encryption = new encryption_1.EncryptionProvider();
39
- // Load the IDL (we assume it's bundled or we can require it if running in node)
40
- // For SDK purity, we should import the JSON.
41
- const idl = require("./idl/private_matchmaking.json");
42
- idl.address = programId.toBase58();
43
- this.program = new anchor_1.Program(idl, provider);
44
- }
45
- /**
46
- * Initialize a new matchmaking queue.
47
- */
48
- async initializeQueue(queueId, config, capacity, pageSize = 50) {
49
- const queuePda = (0, utils_1.deriveQueuePda)(this.program.programId, this.provider.wallet.publicKey, queueId);
50
- // Ensure tenant ID is set
51
- const tenantProgramId = config.tenantProgramId || this.provider.wallet.publicKey;
52
- const tx = await this.program.methods
53
- .initializeQueue(queueId, config, capacity, pageSize)
54
- .accounts({
55
- // queue: queuePda,
56
- // authority: this.provider.wallet.publicKey,
57
- tenantProgramId: tenantProgramId,
58
- // systemProgram: SystemProgram.programId,
59
- })
60
- .rpc(this.config.confirmOptions);
61
- console.log(`Initialized Queue: ${queuePda.toBase58()} in tx: ${tx}`);
62
- // Initialize Pages (Ring Buffer)
63
- for (let i = 0; i < capacity; i++) {
64
- await this.initializePage(queuePda, i);
65
- }
66
- return queuePda;
67
- }
68
- /**
69
- * Initialize a specific page for the queue.
70
- */
71
- async initializePage(queue, index) {
72
- const pagePda = (0, utils_1.derivePagePda)(this.program.programId, queue, index);
73
- const tx = await this.program.methods
74
- .initializePage(new anchor.BN(index))
75
- .accounts({
76
- queue: queue,
77
- // page: pagePda,
78
- // authority: this.provider.wallet.publicKey,
79
- // systemProgram: SystemProgram.programId,
80
- })
81
- .rpc(this.config.confirmOptions);
82
- console.log(`Initialized Page ${index}: ${pagePda.toBase58()}`);
83
- return pagePda;
84
- }
85
- /**
86
- * Delegate the queue to the Privacy Layer (Ephemeral Rollup).
87
- */
88
- async delegateQueue(queueId, validatorOverride) {
89
- const validator = validatorOverride || new web3_js_1.PublicKey(DEFAULT_VALIDATOR);
90
- const queuePda = (0, utils_1.deriveQueuePda)(this.program.programId, this.provider.wallet.publicKey, queueId);
91
- const tx = await this.program.methods
92
- .delegateQueue(queueId)
93
- .preInstructions([
94
- web3_js_1.ComputeBudgetProgram.requestHeapFrame({ bytes: 256 * 1024 })
95
- ])
96
- .accounts({
97
- // pda: queuePda,
98
- // authority: this.provider.wallet.publicKey,
99
- // payer: this.provider.wallet.publicKey,
100
- validator: validator,
101
- })
102
- .rpc(this.config.confirmOptions);
103
- console.log(`Delegated Queue ${queueId}: ${tx}`);
104
- return tx;
105
- }
106
- /**
107
- * Join the queue.
108
- */
109
- async joinQueue(queue, playerGameAccount, tenantProgramId) {
110
- // Fetch queue to determine current WRITE index
111
- const queueAccount = await this.getQueue(queue);
112
- const capacity = queueAccount.capacity;
113
- const writeIndex = queueAccount.writePageIndex.toNumber();
114
- const currentIndex = writeIndex % capacity;
115
- const pagePda = (0, utils_1.derivePagePda)(this.program.programId, queue, currentIndex);
116
- const statusPda = (0, utils_1.derivePlayerStatusPda)(this.program.programId, playerGameAccount);
117
- // Encryption Handshake Integration
118
- let instructionData;
119
- if (this.config.encrypted) {
120
- console.log("🔒 Encrypted Queue Join Init...");
121
- // TODO: Fetch Validator Key from on-chain Registry or Delegate Account
122
- // For now, using a mock dummy key for demonstration of the flow
123
- // In PROD: const validatorKey = await this.getValidatorKey(queue);
124
- const mockValidatorKey = await this.encryption.createMockValidatorKey(); // Valid P-256 Public Key
125
- // Perform Encryption
126
- // Real payload would vary (e.g. Rock/Paper/Scissor choice)
127
- const payload = Buffer.from("PLAYER_CHOICE_ROCK");
128
- const { encrypted, clientPublicKey } = await this.encryption.encryptPayload(payload, mockValidatorKey);
129
- console.log("🔒 Payload Encrypted. Client PubKey:", Buffer.from(clientPublicKey).toString('hex'));
130
- // In a real implementation, 'encrypted' and 'clientPublicKey' would be passed
131
- // as arguments to the 'joinQueue' instruction modification.
132
- // Since the IDL isn't updated for arguments yet, we just log it.
133
- }
134
- const tx = await this.program.methods
135
- .joinQueue()
136
- .accounts({
137
- queue: queue,
138
- // page: pagePda,
139
- page: pagePda,
140
- // playerStatus: statusPda,
141
- // playerAuthority: this.provider.wallet.publicKey,
142
- playerGameAccount: playerGameAccount,
143
- tenantProgram: tenantProgramId, // Added for Matchable Interface CPI
144
- // systemProgram: SystemProgram.programId,
145
- })
146
- .rpc(this.config.confirmOptions);
147
- console.log(`Joined Queue at Page ${currentIndex}: ${tx} (Lock: ${statusPda.toBase58()})`);
148
- // Future Logic: If SDK receives a return log/event indicating "Instant Match", parse it.
149
- // For now, default to "Queued".
150
- return {
151
- status: "Queued",
152
- tx,
153
- statusPda
154
- };
155
- }
156
- /**
157
- * Unlock a player manually (refund rent).
158
- */
159
- async unlockPlayer(playerGameAccount, playerWallet) {
160
- const statusPda = (0, utils_1.derivePlayerStatusPda)(this.program.programId, playerGameAccount);
161
- const statusAccount = await this.getPlayerStatus(statusPda);
162
- const queueAddress = statusAccount.queue;
163
- const tx = await this.program.methods
164
- .unlockPlayer()
165
- .accounts({
166
- queue: queueAddress,
167
- // authority: this.provider.wallet.publicKey,
168
- // playerStatus: statusPda,
169
- player: playerWallet,
170
- playerGameAccount: playerGameAccount
171
- })
172
- .rpc(this.config.confirmOptions);
173
- console.log(`Unlocked Player: ${tx}`);
174
- return tx;
175
- }
176
- /**
177
- * Process matches on a specific page.
178
- * Usually called by the off-chain worker or manually for testing.
179
- */
180
- async processMatch(queue, pageIndex) {
181
- const pagePda = (0, utils_1.derivePagePda)(this.program.programId, queue, pageIndex);
182
- const tx = await this.program.methods
183
- .processMatch(new anchor.BN(pageIndex))
184
- .accounts({
185
- queueAccount: queue,
186
- // page: pagePda,
187
- })
188
- .rpc(this.config.confirmOptions);
189
- return tx;
190
- }
191
- /**
192
- * Resize the queue capacity.
193
- */
194
- async resizeQueue(queue, newCapacity) {
195
- const tx = await this.program.methods
196
- .resizeQueue(newCapacity)
197
- .accounts({
198
- queue: queue,
199
- // authority: this.provider.wallet.publicKey,
200
- })
201
- .rpc(this.config.confirmOptions);
202
- console.log(`Resized Queue to ${newCapacity}: ${tx}`);
203
- return tx;
204
- }
205
- // --- State Fetchers ---
206
- async getQueue(queuePda) {
207
- return await this.program.account.queueHead.fetch(queuePda);
208
- }
209
- async getPage(pagePda) {
210
- return await this.program.account.queuePage.fetch(pagePda);
211
- }
212
- async getPlayerStatus(statusPda) {
213
- return await this.program.account.playerStatus.fetch(statusPda);
214
- }
215
- async getPlayerStatusForGameAccount(playerGameAccount) {
216
- const statusPda = (0, utils_1.derivePlayerStatusPda)(this.program.programId, playerGameAccount);
217
- return await this.getPlayerStatus(statusPda);
218
- }
219
- // --- Dev Helpers ---
220
- async createMockPlayer(playerAccount, elo) {
221
- const tx = await this.program.methods
222
- .createMockPlayer(new anchor.BN(elo))
223
- .accounts({
224
- playerAccount: playerAccount.publicKey,
225
- authority: this.provider.wallet.publicKey,
226
- // systemProgram: SystemProgram.programId,
227
- })
228
- .signers([playerAccount])
229
- .rpc(this.config.confirmOptions);
230
- return tx;
231
- }
232
- /**
233
- * Close a queue page to reclaim rent.
234
- */
235
- async closePage(queue, index) {
236
- const pagePda = (0, utils_1.derivePagePda)(this.program.programId, queue, index);
237
- const tx = await this.program.methods
238
- .closePage(new anchor.BN(index))
239
- .accounts({
240
- queue: queue,
241
- // page: pagePda, // Auto-resolved
242
- authority: this.provider.wallet.publicKey,
243
- })
244
- .rpc(this.config.confirmOptions);
245
- console.log(`Closed Page ${index}: ${pagePda.toBase58()}`);
246
- return tx;
247
- }
248
- /**
249
- * Close a queue head to reclaim rent.
250
- */
251
- async closeQueue(queueId) {
252
- const queuePda = (0, utils_1.deriveQueuePda)(this.program.programId, this.provider.wallet.publicKey, queueId);
253
- const tx = await this.program.methods
254
- .closeQueue(queueId)
255
- .accounts({
256
- // queue: queuePda, // Auto-resolved
257
- authority: this.provider.wallet.publicKey,
258
- })
259
- .rpc(this.config.confirmOptions);
260
- console.log(`Closed Queue: ${queuePda.toBase58()}`);
261
- return tx;
262
- }
263
- }
264
- exports.MatchmakingClient = MatchmakingClient;
265
- //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiY2xpZW50LmpzIiwic291cmNlUm9vdCI6IiIsInNvdXJjZXMiOlsiLi4vc3JjL2NsaWVudC50cyJdLCJuYW1lcyI6W10sIm1hcHBpbmdzIjoiOzs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7OztBQUFBLDBEQUE0QztBQUM1Qyw4Q0FBaUU7QUFDakUsNkNBTXlCO0FBR3pCLG1DQUErRTtBQUMvRSw2Q0FBa0Q7QUFFbEQsNENBQTRDO0FBQzVDLE1BQU0saUJBQWlCLEdBQUcsOENBQThDLENBQUM7QUFFekUsTUFBYSxpQkFBaUI7SUFNMUIsWUFDSSxRQUF3QixFQUN4QixTQUFvQixFQUNwQixTQUFrQyxFQUFFO1FBRXBDLElBQUksQ0FBQyxRQUFRLEdBQUcsUUFBUSxDQUFDO1FBQ3pCLElBQUksQ0FBQyxNQUFNLEdBQUcsTUFBTSxDQUFDO1FBQ3JCLElBQUksQ0FBQyxVQUFVLEdBQUcsSUFBSSwrQkFBa0IsRUFBRSxDQUFDO1FBRTNDLGdGQUFnRjtRQUNoRiw2Q0FBNkM7UUFDN0MsTUFBTSxHQUFHLEdBQUcsT0FBTyxDQUFDLGdDQUFnQyxDQUFDLENBQUM7UUFDdEQsR0FBRyxDQUFDLE9BQU8sR0FBRyxTQUFTLENBQUMsUUFBUSxFQUFFLENBQUM7UUFDbkMsSUFBSSxDQUFDLE9BQU8sR0FBRyxJQUFJLGdCQUFPLENBQUMsR0FBRyxFQUFFLFFBQVEsQ0FBQyxDQUFDO0lBQzlDLENBQUM7SUFFRDs7T0FFRztJQUNILEtBQUssQ0FBQyxlQUFlLENBQ2pCLE9BQWUsRUFDZixNQUFXLEVBQ1gsUUFBZ0IsRUFDaEIsV0FBbUIsRUFBRTtRQUVyQixNQUFNLFFBQVEsR0FBRyxJQUFBLHNCQUFjLEVBQUMsSUFBSSxDQUFDLE9BQU8sQ0FBQyxTQUFTLEVBQUUsSUFBSSxDQUFDLFFBQVEsQ0FBQyxNQUFNLENBQUMsU0FBUyxFQUFFLE9BQU8sQ0FBQyxDQUFDO1FBRWpHLDBCQUEwQjtRQUMxQixNQUFNLGVBQWUsR0FBRyxNQUFNLENBQUMsZUFBZSxJQUFJLElBQUksQ0FBQyxRQUFRLENB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QyxjQUFjLENBQUMsQ0FBQztRQUNyQyxPQUFPLENBQUMsR0FBRyxDQUFDLGlCQUFpQixRQUFRLENBQUMsUUFBUSxFQUFFLEVBQUUsQ0FBQyxDQUFDO1FBQ3BELE9BQU8sRUFBRSxDQUFDO0lBQ2QsQ0FBQztDQUNKO0FBclNELDhDQXFTQyIsInNvdXJjZXNDb250ZW50IjpbImltcG9ydCAqIGFzIGFuY2hvciBmcm9tIFwiQGNvcmFsLXh5ei9hbmNob3JcIjtcbmltcG9ydCB7IFByb2dyYW0sIElkbCwgQW5jaG9yUHJvdmlkZXIgfSBmcm9tIFwiQGNvcmFsLXh5ei9hbmNob3JcIjtcbmltcG9ydCB7IFxuICAgIFB1YmxpY0tleSwgXG4gICAgU3lzdGVtUHJvZ3JhbSwgXG4gICAgS2V5cGFpciwgXG4gICAgVHJhbnNhY3Rpb25TaWduYXR1cmUsXG4gICAgQ29tcHV0ZUJ1ZGdldFByb2dyYW1cbn0gZnJvbSBcIkBzb2xhbmEvd2ViMy5qc1wiO1xuaW1wb3J0IHsgUHJpdmF0ZU1hdGNobWFraW5nIH0gZnJvbSBcIi4vaWRsL3ByaXZhdGVfbWF0Y2htYWtpbmdcIjtcbmltcG9ydCB7IE1hdGNobWFraW5nQ2xpZW50Q29uZmlnLCBRdWV1ZUhlYWQsIFF1ZXVlUGFnZSwgUGxheWVyU3RhdHVzLCBKb2luUXVldWVSZXN1bHQgfSBmcm9tIFwiLi90eXBlc1wiO1xuaW1wb3J0IHsgZGVyaXZlUGFnZVBkYSwgZGVyaXZlUGxheWVyU3RhdHVzUGRhLCBkZXJpdmVRdWV1ZVBkYSB9IGZyb20gXCIuL3V0aWxzXCI7XG5pbXBvcnQgeyBFbmNyeXB0aW9uUHJvdmlkZXIgfSBmcm9tIFwiLi9lbmNyeXB0aW9uXCI7XG5cbi8vIERlZmF1bHQgVmFsaWRhdG9yIGZvciBEZXZuZXQgKE1hZ2ljQmxvY2spXG5jb25zdCBERUZBVUxUX1ZBTElEQVRPUiA9IFwiRm5FNlZKVDVRTlpkZWRaUG5Db0xzQVJnQndvRTZEZUpOakJzMkgxZ3lTWEFcIjtcblxuZXhwb3J0IGNsYXNzIE1hdGNobWFraW5nQ2xpZW50IHtcbiAgICBwcm9ncmFtOiBQcm9ncmFtPFByaXZhdGVNYXRjaG1ha2luZz47XG4gICAgcHJvdmlkZXI6IEFuY2hvclByb3ZpZGVyO1xuICAgIGNvbmZpZzogTWF0Y2htYWtpbmdDbGllbnRDb25maWc7XG4gICAgZW5jcnlwdGlvbjogRW5jcnlwdGlvblByb3ZpZGVyO1xuXG4gICAgY29uc3RydWN0b3IoXG4gICAgICAgIHByb3ZpZGVyOiBBbmNob3JQcm92aWRlciwgXG4gICAgICAgIHByb2dyYW1JZDogUHVibGljS2V5LFxuICAgICAgICBjb25maWc6IE1hdGNobWFraW5nQ2xpZW50Q29uZmlnID0ge31cbiAgICApIHtcbiAgICAgICAgdGhpcy5wcm92aWRlciA9IHByb3ZpZGVyO1xuICAgICAgICB0aGlzLmNvbmZpZyA9IGNvbmZpZztcbiAgICAgICAgdGhpcy5lbmNyeXB0aW9uID0gbmV3IEVuY3J5cHRpb25Qcm92aWRlcigpO1xuICAgICAgICBcbiAgICAgICAgLy8gTG9hZCB0aGUgSURMICh3ZSBhc3N1bWUgaXQncyBidW5kbGVkIG9yIHdlIGNhbiByZXF1aXJlIGl0IGlmIHJ1bm5pbmcgaW4gbm9kZSlcbiAgICAgICAgLy8gRm9yIFNESyBwdXJpdHksIHdlIHNob3VsZCBpbXBvcnQgdGhlIEpTT04uXG4gICAgICAgIGNvbnN0IGlkbCA9IHJlcXVpcmUoXCIuL2lkbC9wcml2YXRlX21hdGNobWFraW5nLmpzb25cIik7XG4gICAgICAgIGlkbC5hZGRyZXNzID0gcHJvZ3JhbUlkLnRvQmFzZTU4KCk7XG4gICAgICAgIHRoaXMucHJvZ3JhbSA9IG5ldyBQcm9ncmFtKGlkbCwgcHJvdmlkZXIpO1xuICAgIH1cblxuICAgIC8qKlxuICAgICAqIEluaXRpYWxpemUgYSBuZXcgbWF0Y2htYWtpbmcgcXVldWUuXG4gICAgICovXG4gICAgYXN5bmMgaW5pdGlhbGl6ZVF1ZXVlKFxuICAgICAgICBxdWV1ZUlkOiBzdHJpbmcsIFxuICAgICAgICBjb25maWc6IGFueSwgXG4gICAgICAgIGNhcGFjaXR5OiBudW1iZXIsXG4gICAgICAgIHBhZ2VTaXplOiBudW1iZXIgPSA1MFxuICAgICk6IFByb21pc2U8UHVibGljS2V5PiB7XG4gICAgICAgIGNvbnN0IHF1ZXVlUGRhID0gZGVyaXZlUXVldWVQZGEodGhpcy5wcm9ncmFtLnByb2dyYW1JZCwgdGhpcy5wcm92aWRlci53YWxsZXQucHVibGljS2V5LCBxdWV1ZUlkKTtcblxuICAgICAgICAvLyBFbnN1cmUgdGVuYW50IElEIGlzIHNldFxuICAgICAgICBjb25zdCB0ZW5hbnRQcm9ncmFtSWQgPSBjb25maWcudGVuYW50UHJvZ3JhbUlkIHx8IHRoaXMucHJvdmlkZXIud2FsbGV0LnB1YmxpY0tleTtcblxuICAgICAgICBjb25zdCB0eCA9IGF3YWl0IHRoaXMucHJvZ3JhbS5tZXRob2RzXG4gICAgICAgICAgICAuaW5pdGlhbGl6ZVF1ZXVlKHF1ZXVlSWQsIGNvbmZpZywgY2FwYWNpdHksIHBhZ2VTaXplKVxuICAgICAgICAgICAgLmFjY291bnRzKHtcbiAgICAgICAgICAgICAgICAvLyBxdWV1ZTogcXVldWVQZGEsXG4gICAgICAgICAgICAgICAgLy8gYXV0aG9yaXR5OiB0aGlzLnByb3ZpZGVyLndhbGxldC5wdWJsaWNLZXksXG4gICAgICAgICAgICAgICAgdGVuYW50UHJvZ3JhbUlkOiB0ZW5hbnRQcm9ncmFtSWQsXG4gICAgICAgICAgICAgICAgLy8gc3lzdGVtUHJvZ3JhbTogU3lzdGVtUHJvZ3JhbS5wcm9ncmFtSWQsXG4gICAgICAgICAgICB9KVxuICAgICAgICAgICAgLnJwYyh0aGlzLmNvbmZpZy5jb25maXJtT3B0aW9ucyk7XG4gICAgICAgIFxuICAgICAgICBjb25zb2xlLmxvZyhgSW5pdGlhbGl6ZWQgUXVldWU6ICR7cXVldWVQZGEudG9CYXNlNTgoKX0gaW4gdHg6ICR7dHh9YCk7XG4gICAgICAgIFxuICAgICAgICAvLyBJbml0aWFsaXplIFBhZ2VzIChSaW5nIEJ1ZmZlcilcbiAgICAgICAgZm9yIChsZXQgaSA9IDA7IGkgPCBjYXBhY2l0eTsgaSsrKSB7XG4gICAgICAgICAgICBhd2FpdCB0aGlzLmluaXRpYWxpemVQYWdlKHF1ZXVlUGRhLCBpKTtcbiAgICAgICAgfVxuICAgICAgICBcbiAgICAgICAgcmV0dXJuIHF1ZXVlUGRhO1xuICAgIH1cblxuICAgIC8qKlxuICAgICAqIEluaXRpYWxpemUgYSBzcGVjaWZpYyBwYWdlIGZvciB0aGUgcXVldWUuXG4gICAgICovXG4gICAgYXN5bmMgaW5pdGlhbGl6ZVBhZ2UocXVldWU6IFB1YmxpY0tleSwgaW5kZXg6IG51bWJlcik6IFByb21pc2U8UHVibGljS2V5PiB7XG4gICAgICAgIGNvbnN0IHBhZ2VQZGEgPSBkZXJpdmVQYWdlUGRhKHRoaXMucHJvZ3JhbS5wcm9ncmFtSWQsIHF1ZXVlLCBpbmRleCk7XG4gICAgICAgIFxuICAgICAgICBjb25zdCB0eCA9IGF3YWl0IHRoaXMucHJvZ3JhbS5tZXRob2RzXG4gICAgICAgICAgICAuaW5pdGlhbGl6ZVBhZ2UobmV3IGFuY2hvci5CTihpbmRleCkpXG4gICAgICAgICAgICAuYWNjb3VudHMoe1xuICAgICAgICAgICAgICAgIHF1ZXVlOiBxdWV1ZSxcbiAgICAgICAgICAgICAgICAvLyBwYWdlOiBwYWdlUGRhLFxuICAgICAgICAgICAgICAgIC8vIGF1dGhvcml0eTogdGhpcy5wcm92aWRlci53YWxsZXQucHVibGljS2V5LFxuICAgICAgICAgICAgICAgIC8vIHN5c3RlbVByb2dyYW06IFN5c3RlbVByb2dyYW0ucHJvZ3JhbUlkLFxuICAgICAgICAgICAgfSlcbiAgICAgICAgICAgIC5ycGModGhpcy5jb25maWcuY29uZmlybU9wdGlvbnMpO1xuICAgICAgICAgICAgXG4gICAgICAgIGNvbnNvbGUubG9nKGBJbml0aWFsaXplZCBQYWdlICR7aW5kZXh9OiAke3BhZ2VQZGEudG9CYXNlNTgoKX1gKTtcbiAgICAgICAgcmV0dXJuIHBhZ2VQZGE7XG4gICAgfVxuXG4gICAgLyoqXG4gICAgICogRGVsZWdhdGUgdGhlIHF1ZXVlIHRvIHRoZSBQcml2YWN5IExheWVyIChFcGhlbWVyYWwgUm9sbHVwKS5cbiAgICAgKi9cbiAgICBhc3luYyBkZWxlZ2F0ZVF1ZXVlKHF1ZXVlSWQ6IHN0cmluZywgdmFsaWRhdG9yT3ZlcnJpZGU/OiBQdWJsaWNLZXkpOiBQcm9taXNlPFRyYW5zYWN0aW9uU2lnbmF0dXJlPiB7XG4gICAgICAgIGNvbnN0IHZhbGlkYXRvciA9IHZhbGlkYXRvck92ZXJyaWRlIHx8IG5ldyBQdWJsaWNLZXkoREVGQVVMVF9WQUxJREFUT1IpO1xuICAgICAgICBjb25zdCBxdWV1ZVBkYSA9IGRlcml2ZVF1ZXVlUGRhKHRoaXMucHJvZ3JhbS5wcm9ncmFtSWQsIHRoaXMucHJvdmlkZXIud2FsbGV0LnB1YmxpY0tleSwgcXVldWVJZCk7XG5cbiAgICAgICAgY29uc3QgdHggPSBhd2FpdCB0aGlzLnByb2dyYW0ubWV0aG9kc1xuICAgICAgICAgICAgLmRlbGVnYXRlUXVldWUocXVldWVJZClcbiAgICAgICAgICAgIC5wcmVJbnN0cnVjdGlvbnMoW1xuICAgICAgICAgICAgICAgIENvbXB1dGVCdWRnZXRQcm9ncmFtLnJlcXVlc3RIZWFwRnJhbWUoeyBieXRlczogMjU2ICogMTAyNCB9KVxuICAgICAgICAgICAgXSlcbiAgICAgICAgICAgIC5hY2NvdW50cyh7XG4gICAgICAgICAgICAgICAgLy8gcGRhOiBxdWV1ZVBkYSxcbiAgICAgICAgICAgICAgICAvLyBhdXRob3JpdHk6IHRoaXMucHJvdmlkZXIud2FsbGV0LnB1YmxpY0tleSxcbiAgICAgICAgICAgICAgICAvLyBwYXllcjogdGhpcy5wcm92aWRlci53YWxsZXQucHVibGljS2V5LFxuICAgICAgICAgICAgICAgIHZhbGlkYXRvcjogdmFsaWRhdG9yLFxuICAgICAgICAgICAgfSlcbiAgICAgICAgICAgIC5ycGModGhpcy5jb25maWcuY29uZmlybU9wdGlvbnMpO1xuICAgICAgICAgICAgXG4gICAgICAgIGNvbnNvbGUubG9nKGBEZWxlZ2F0ZWQgUXVldWUgJHtxdWV1ZUlkfTogJHt0eH1gKTtcbiAgICAgICAgcmV0dXJuIHR4O1xuICAgIH1cblxuICAgIC8qKlxuICAgICAqIEpvaW4gdGhlIHF1ZXVlLlxuICAgICAqL1xuICAgIGFzeW5jIGpvaW5RdWV1ZShcbiAgICAgICAgcXVldWU6IFB1YmxpY0tleSwgXG4gICAgICAgIHBsYXllckdhbWVBY2NvdW50OiBQdWJsaWNLZXksIFxuICAgICAgICB0ZW5hbnRQcm9ncmFtSWQ6IFB1YmxpY0tleVxuICAgICk6IFByb21pc2U8Sm9pblF1ZXVlUmVzdWx0PiB7XG4gICAgICAgIC8vIEZldGNoIHF1ZXVlIHRvIGRldGVybWluZSBjdXJyZW50IFdSSVRFIGluZGV4XG4gICAgICAgIGNvbnN0IHF1ZXVlQWNjb3VudCA9IGF3YWl0IHRoaXMuZ2V0UXVldWUocXVldWUpO1xuICAgICAgICBjb25zdCBjYXBhY2l0eSA9IHF1ZXVlQWNjb3VudC5jYXBhY2l0eTtcbiAgICAgICAgY29uc3Qgd3JpdGVJbmRleCA9IHF1ZXVlQWNjb3VudC53cml0ZVBhZ2VJbmRleC50b051bWJlcigpO1xuICAgICAgICBjb25zdCBjdXJyZW50SW5kZXggPSB3cml0ZUluZGV4ICUgY2FwYWNpdHk7XG5cbiAgICAgICAgY29uc3QgcGFnZVBkYSA9IGRlcml2ZVBhZ2VQZGEodGhpcy5wcm9ncmFtLnByb2dyYW1JZCwgcXVldWUsIGN1cnJlbnRJbmRleCk7XG4gICAgICAgIGNvbnN0IHN0YXR1c1BkYSA9IGRlcml2ZVBsYXllclN0YXR1c1BkYSh0aGlzLnByb2dyYW0ucHJvZ3JhbUlkLCBwbGF5ZXJHYW1lQWNjb3VudCk7XG5cbiAgICAgICAgLy8gRW5jcnlwdGlvbiBIYW5kc2hha2UgSW50ZWdyYXRpb25cbiAgICAgICAgbGV0IGluc3RydWN0aW9uRGF0YTogQnVmZmVyIHwgdW5kZWZpbmVkO1xuICAgICAgICBpZiAodGhpcy5jb25maWcuZW5jcnlwdGVkKSB7XG4gICAgICAgICAgICBjb25zb2xlLmxvZyhcIvCflJIgRW5jcnlwdGVkIFF1ZXVlIEpvaW4gSW5pdC4uLlwiKTtcbiAgICAgICAgICAgIC8vIFRPRE86IEZldGNoIFZhbGlkYXRvciBLZXkgZnJvbSBvbi1jaGFpbiBSZWdpc3RyeSBvciBEZWxlZ2F0ZSBBY2NvdW50XG4gICAgICAgICAgICAvLyBGb3Igbm93LCB1c2luZyBhIG1vY2sgZHVtbXkga2V5IGZvciBkZW1vbnN0cmF0aW9uIG9mIHRoZSBmbG93XG4gICAgICAgICAgICAvLyBJbiBQUk9EOiBjb25zdCB2YWxpZGF0b3JLZXkgPSBhd2FpdCB0aGlzLmdldFZhbGlkYXRvcktleShxdWV1ZSk7XG4gICAgICAgICAgICBjb25zdCBtb2NrVmFsaWRhdG9yS2V5ID0gYXdhaXQgdGhpcy5lbmNyeXB0aW9uLmNyZWF0ZU1vY2tWYWxpZGF0b3JLZXkoKTsgLy8gVmFsaWQgUC0yNTYgUHVibGljIEtleVxuICAgICAgICAgICAgXG4gICAgICAgICAgICAvLyBQZXJmb3JtIEVuY3J5cHRpb25cbiAgICAgICAgICAgIC8vIFJlYWwgcGF5bG9hZCB3b3VsZCB2YXJ5IChlLmcuIFJvY2svUGFwZXIvU2Npc3NvciBjaG9pY2UpXG4gICAgICAgICAgICBjb25zdCBwYXlsb2FkID0gQnVmZmVyLmZyb20oXCJQTEFZRVJfQ0hPSUNFX1JPQ0tcIik7XG4gICAgICAgICAgICBjb25zdCB7IGVuY3J5cHRlZCwgY2xpZW50UHVibGljS2V5IH0gPSBhd2FpdCB0aGlzLmVuY3J5cHRpb24uZW5jcnlwdFBheWxvYWQocGF5bG9hZCwgbW9ja1ZhbGlkYXRvcktleSk7XG4gICAgICAgICAgICBjb25zb2xlLmxvZyhcIvCflJIgUGF5bG9hZCBFbmNyeXB0ZWQuIENsaWVudCBQdWJLZXk6XCIsIEJ1ZmZlci5mcm9tKGNsaWVudFB1YmxpY0tleSkudG9TdHJpbmcoJ2hleCcpKTtcbiAgICAgICAgICAgIFxuICAgICAgICAgICAgLy8gSW4gYSByZWFsIGltcGxlbWVudGF0aW9uLCAnZW5jcnlwdGVkJyBhbmQgJ2NsaWVudFB1YmxpY0tleScgd291bGQgYmUgcGFzc2VkIFxuICAgICAgICAgICAgLy8gYXMgYXJndW1lbnRzIHRvIHRoZSAnam9pblF1ZXVlJyBpbnN0cnVjdGlvbiBtb2RpZmljYXRpb24uXG4gICAgICAgICAgICAvLyBTaW5jZSB0aGUgSURMIGlzbid0IHVwZGF0ZWQgZm9yIGFyZ3VtZW50cyB5ZXQsIHdlIGp1c3QgbG9nIGl0LlxuICAgICAgICB9XG5cbiAgICAgICAgY29uc3QgdHggPSBhd2FpdCB0aGlzLnByb2dyYW0ubWV0aG9kc1xuICAgICAgICAgICAgLmpvaW5RdWV1ZSgpXG4gICAgICAgICAgICAuYWNjb3VudHMoe1xuICAgICAgICAgICAgICAgIHF1ZXVlOiBxdWV1ZSxcbiAgICAgICAgICAgICAgICAvLyBwYWdlOiBwYWdlUGRhLFxuICAgICAgICAgICAgICAgIHBhZ2U6IHBhZ2VQZGEsIFxuICAgICAgICAgICAgICAgIC8vIHBsYXllclN0YXR1czogc3RhdHVzUGRhLFxuICAgICAgICAgICAgICAgIC8vIHBsYXllckF1dGhvcml0eTogdGhpcy5wcm92aWRlci53YWxsZXQucHVibGljS2V5LFxuICAgICAgICAgICAgICAgIHBsYXllckdhbWVBY2NvdW50OiBwbGF5ZXJHYW1lQWNjb3VudCxcbiAgICAgICAgICAgICAgICB0ZW5hbnRQcm9ncmFtOiB0ZW5hbnRQcm9ncmFtSWQsIC8vIEFkZGVkIGZvciBNYXRjaGFibGUgSW50ZXJmYWNlIENQSVxuICAgICAgICAgICAgICAgIC8vIHN5c3RlbVByb2dyYW06IFN5c3RlbVByb2dyYW0ucHJvZ3JhbUlkLFxuICAgICAgICAgICAgfSlcbiAgICAgICAgICAgIC5ycGModGhpcy5jb25maWcuY29uZmlybU9wdGlvbnMpO1xuXG4gICAgICAgIGNvbnNvbGUubG9nKGBKb2luZWQgUXVldWUgYXQgUGFnZSAke2N1cnJlbnRJbmRleH06ICR7dHh9IChMb2NrOiAke3N0YXR1c1BkYS50b0Jhc2U1OCgpfSlgKTtcbiAgICAgICAgXG4gICAgICAgIC8vIEZ1dHVyZSBMb2dpYzogSWYgU0RLIHJlY2VpdmVzIGEgcmV0dXJuIGxvZy9ldmVudCBpbmRpY2F0aW5nIFwiSW5zdGFudCBNYXRjaFwiLCBwYXJzZSBpdC5cbiAgICAgICAgLy8gRm9yIG5vdywgZGVmYXVsdCB0byBcIlF1ZXVlZFwiLlxuICAgICAgICByZXR1cm4geyBcbiAgICAgICAgICAgIHN0YXR1czogXCJRdWV1ZWRcIiwgXG4gICAgICAgICAgICB0eCwgXG4gICAgICAgICAgICBzdGF0dXNQZGEgXG4gICAgICAgIH07XG4gICAgfVxuXG4gICAgLyoqXG4gICAgICogVW5sb2NrIGEgcGxheWVyIG1hbnVhbGx5IChyZWZ1bmQgcmVudCkuXG4gICAgICovXG4gICAgYXN5bmMgdW5sb2NrUGxheWVyKFxuICAgICAgICBwbGF5ZXJHYW1lQWNjb3VudDogUHVibGljS2V5LCBcbiAgICAgICAgcGxheWVyV2FsbGV0OiBQdWJsaWNLZXlcbiAgICApOiBQcm9taXNlPFRyYW5zYWN0aW9uU2lnbmF0dXJlPiB7XG4gICAgICAgIGNvbnN0IHN0YXR1c1BkYSA9IGRlcml2ZVBsYXllclN0YXR1c1BkYSh0aGlzLnByb2dyYW0ucHJvZ3JhbUlkLCBwbGF5ZXJHYW1lQWNjb3VudCk7XG4gICAgICAgIGNvbnN0IHN0YXR1c0FjY291bnQgPSBhd2FpdCB0aGlzLmdldFBsYXllclN0YXR1cyhzdGF0dXNQZGEpO1xuICAgICAgICBjb25zdCBxdWV1ZUFkZHJlc3MgPSBzdGF0dXNBY2NvdW50LnF1ZXVlO1xuXG4gICAgICAgIGNvbnN0IHR4ID0gYXdhaXQgdGhpcy5wcm9ncmFtLm1ldGhvZHNcbiAgICAgICAgICAgIC51bmxvY2tQbGF5ZXIoKVxuICAgICAgICAgICAgLmFjY291bnRzKHtcbiAgICAgICAgICAgICAgICBxdWV1ZTogcXVldWVBZGRyZXNzLFxuICAgICAgICAgICAgICAgIC8vIGF1dGhvcml0eTogdGhpcy5wcm92aWRlci53YWxsZXQucHVibGljS2V5LFxuICAgICAgICAgICAgICAgIC8vIHBsYXllclN0YXR1czogc3RhdHVzUGRhLFxuICAgICAgICAgICAgICAgIHBsYXllcjogcGxheWVyV2FsbGV0LFxuICAgICAgICAgICAgICAgIHBsYXllckdhbWVBY2NvdW50OiBwbGF5ZXJHYW1lQWNjb3VudFxuICAgICAgICAgICAgfSlcbiAgICAgICAgICAgIC5ycGModGhpcy5jb25maWcuY29uZmlybU9wdGlvbnMpO1xuICAgICAgICBcbiAgICAgICAgY29uc29sZS5sb2coYFVubG9ja2VkIFBsYXllcjogJHt0eH1gKTtcbiAgICAgICAgcmV0dXJuIHR4O1xuICAgIH1cblxuICAgIC8qKlxuICAgICAqIFByb2Nlc3MgbWF0Y2hlcyBvbiBhIHNwZWNpZmljIHBhZ2UuXG4gICAgICogVXN1YWxseSBjYWxsZWQgYnkgdGhlIG9mZi1jaGFpbiB3b3JrZXIgb3IgbWFudWFsbHkgZm9yIHRlc3RpbmcuXG4gICAgICovXG4gICAgYXN5bmMgcHJvY2Vzc01hdGNoKFxuICAgICAgICBxdWV1ZTogUHVibGljS2V5LFxuICAgICAgICBwYWdlSW5kZXg6IG51bWJlclxuICAgICk6IFByb21pc2U8VHJhbnNhY3Rpb25TaWduYXR1cmU+IHtcbiAgICAgICAgY29uc3QgcGFnZVBkYSA9IGRlcml2ZVBhZ2VQZGEodGhpcy5wcm9ncmFtLnByb2dyYW1JZCwgcXVldWUsIHBhZ2VJbmRleCk7XG5cbiAgICAgICAgY29uc3QgdHggPSBhd2FpdCB0aGlzLnByb2dyYW0ubWV0aG9kc1xuICAgICAgICAgICAgLnByb2Nlc3NNYXRjaChuZXcgYW5jaG9yLkJOKHBhZ2VJbmRleCkpXG4gICAgICAgICAgICAuYWNjb3VudHMoe1xuICAgICAgICAgICAgICAgIHF1ZXVlQWNjb3VudDogcXVldWUsXG4gICAgICAgICAgICAgICAgLy8gcGFnZTogcGFnZVBkYSxcbiAgICAgICAgICAgIH0pXG4gICAgICAgICAgICAucnBjKHRoaXMuY29uZmlnLmNvbmZpcm1PcHRpb25zKTtcblxuICAgICAgICByZXR1cm4gdHg7XG4gICAgfVxuICAgIFxuICAgIC8qKlxuICAgICAqIFJlc2l6ZSB0aGUgcXVldWUgY2FwYWNpdHkuXG4gICAgICovXG4gICAgYXN5bmMgcmVzaXplUXVldWUocXVldWU6IFB1YmxpY0tleSwgbmV3Q2FwYWNpdHk6IG51bWJlcik6IFByb21pc2U8VHJhbnNhY3Rpb25TaWduYXR1cmU+IHtcbiAgICAgICAgY29uc3QgdHggPSBhd2FpdCB0aGlzLnByb2dyYW0ubWV0aG9kc1xuICAgICAgICAgICAgLnJlc2l6ZVF1ZXVlKG5ld0NhcGFjaXR5KVxuICAgICAgICAgICAgLmFjY291bnRzKHtcbiAgICAgICAgICAgICAgICBxdWV1ZTogcXVldWUsXG4gICAgICAgICAgICAgICAgLy8gYXV0aG9yaXR5OiB0aGlzLnByb3ZpZGVyLndhbGxldC5wdWJsaWNLZXksXG4gICAgICAgICAgICB9KVxuICAgICAgICAgICAgLnJwYyh0aGlzLmNvbmZpZy5jb25maXJtT3B0aW9ucyk7XG4gICAgICAgIGNvbnNvbGUubG9nKGBSZXNpemVkIFF1ZXVlIHRvICR7bmV3Q2FwYWNpdHl9OiAke3R4fWApO1xuICAgICAgICByZXR1cm4gdHg7XG4gICAgfVxuXG4gICAgLy8gLS0tIFN0YXRlIEZldGNoZXJzIC0tLVxuXG4gICAgYXN5bmMgZ2V0UXVldWUocXVldWVQZGE6IFB1YmxpY0tleSk6IFByb21pc2U8UXVldWVIZWFkPiB7XG4gICAgICAgIHJldHVybiBhd2FpdCB0aGlzLnByb2dyYW0uYWNjb3VudC5xdWV1ZUhlYWQuZmV0Y2gocXVldWVQZGEpO1xuICAgIH1cblxuICAgIGFzeW5jIGdldFBhZ2UocGFnZVBkYTogUHVibGljS2V5KTogUHJvbWlzZTxRdWV1ZVBhZ2U+IHtcbiAgICAgICAgcmV0dXJuIGF3YWl0IHRoaXMucHJvZ3JhbS5hY2NvdW50LnF1ZXVlUGFnZS5mZXRjaChwYWdlUGRhKTtcbiAgICB9XG5cbiAgICBhc3luYyBnZXRQbGF5ZXJTdGF0dXMoc3RhdHVzUGRhOiBQdWJsaWNLZXkpOiBQcm9taXNlPFBsYXllclN0YXR1cz4ge1xuICAgICAgICByZXR1cm4gYXdhaXQgdGhpcy5wcm9ncmFtLmFjY291bnQucGxheWVyU3RhdHVzLmZldGNoKHN0YXR1c1BkYSk7XG4gICAgfVxuICAgIFxuICAgIGFzeW5jIGdldFBsYXllclN0YXR1c0ZvckdhbWVBY2NvdW50KHBsYXllckdhbWVBY2NvdW50OiBQdWJsaWNLZXkpOiBQcm9taXNlPFBsYXllclN0YXR1cz4ge1xuICAgICAgICAgY29uc3Qgc3RhdHVzUGRhID0gZGVyaXZlUGxheWVyU3RhdHVzUGRhKHRoaXMucHJvZ3JhbS5wcm9ncmFtSWQsIHBsYXllckdhbWVBY2NvdW50KTtcbiAgICAgICAgIHJldHVybiBhd2FpdCB0aGlzLmdldFBsYXllclN0YXR1cyhzdGF0dXNQZGEpO1xuICAgIH1cblxuICAgIC8vIC0tLSBEZXYgSGVscGVycyAtLS1cblxuICAgIGFzeW5jIGNyZWF0ZU1vY2tQbGF5ZXIoXG4gICAgICAgIHBsYXllckFjY291bnQ6IEtleXBhaXIsIFxuICAgICAgICBlbG86IG51bWJlclxuICAgICk6IFByb21pc2U8VHJhbnNhY3Rpb25TaWduYXR1cmU+IHtcbiAgICAgICAgY29uc3QgdHggPSBhd2FpdCB0aGlzLnByb2dyYW0ubWV0aG9kc1xuICAgICAgICAgICAgLmNyZWF0ZU1vY2tQbGF5ZXIobmV3IGFuY2hvci5CTihlbG8pKVxuICAgICAgICAgICAgLmFjY291bnRzKHtcbiAgICAgICAgICAgICAgICBwbGF5ZXJBY2NvdW50OiBwbGF5ZXJBY2NvdW50LnB1YmxpY0tleSxcbiAgICAgICAgICAgICAgICBhdXRob3JpdHk6IHRoaXMucHJvdmlkZXIud2FsbGV0LnB1YmxpY0tleSxcbiAgICAgICAgICAgICAgICAvLyBzeXN0ZW1Qcm9ncmFtOiBTeXN0ZW1Qcm9ncmFtLnByb2dyYW1JZCxcbiAgICAgICAgICAgIH0pXG4gICAgICAgICAgICAuc2lnbmVycyhbcGxheWVyQWNjb3VudF0pXG4gICAgICAgICAgICAucnBjKHRoaXMuY29uZmlnLmNvbmZpcm1PcHRpb25zKTtcbiAgICAgICAgcmV0dXJuIHR4O1xuICAgIH1cblxuICAgIC8qKlxuICAgICAqIENsb3NlIGEgcXVldWUgcGFnZSB0byByZWNsYWltIHJlbnQuXG4gICAgICovXG4gICAgYXN5bmMgY2xvc2VQYWdlKHF1ZXVlOiBQdWJsaWNLZXksIGluZGV4OiBudW1iZXIpOiBQcm9taXNlPFRyYW5zYWN0aW9uU2lnbmF0dXJlPiB7XG4gICAgICAgIGNvbnN0IHBhZ2VQZGEgPSBkZXJpdmVQYWdlUGRhKHRoaXMucHJvZ3JhbS5wcm9ncmFtSWQsIHF1ZXVlLCBpbmRleCk7XG4gICAgICAgIGNvbnN0IHR4ID0gYXdhaXQgdGhpcy5wcm9ncmFtLm1ldGhvZHNcbiAgICAgICAgICAgIC5jbG9zZVBhZ2UobmV3IGFuY2hvci5CTihpbmRleCkpXG4gICAgICAgICAgICAuYWNjb3VudHMoe1xuICAgICAgICAgICAgICAgIHF1ZXVlOiBxdWV1ZSxcbiAgICAgICAgICAgICAgICAvLyBwYWdlOiBwYWdlUGRhLCAvLyBBdXRvLXJlc29sdmVkXG4gICAgICAgICAgICAgICAgYXV0aG9yaXR5OiB0aGlzLnByb3ZpZGVyLndhbGxldC5wdWJsaWNLZXksXG4gICAgICAgICAgICB9KVxuICAgICAgICAgICAgLnJwYyh0aGlzLmNvbmZpZy5jb25maXJtT3B0aW9ucyk7XG4gICAgICAgIGNvbnNvbGUubG9nKGBDbG9zZWQgUGFnZSAke2luZGV4fTogJHtwYWdlUGRhLnRvQmFzZTU4KCl9YCk7XG4gICAgICAgIHJldHVybiB0eDtcbiAgICB9XG5cbiAgICAvKipcbiAgICAgKiBDbG9zZSBhIHF1ZXVlIGhlYWQgdG8gcmVjbGFpbSByZW50LlxuICAgICAqL1xuICAgIGFzeW5jIGNsb3NlUXVldWUocXVldWVJZDogc3RyaW5nKTogUHJvbWlzZTxUcmFuc2FjdGlvblNpZ25hdHVyZT4ge1xuICAgICAgICBjb25zdCBxdWV1ZVBkYSA9IGRlcml2ZVF1ZXVlUGRhKHRoaXMucHJvZ3JhbS5wcm9ncmFtSWQsIHRoaXMucHJvdmlkZXIud2FsbGV0LnB1YmxpY0tleSwgcXVldWVJZCk7XG4gICAgICAgIGNvbnN0IHR4ID0gYXdhaXQgdGhpcy5wcm9ncmFtLm1ldGhvZHNcbiAgICAgICAgICAgIC5jbG9zZVF1ZXVlKHF1ZXVlSWQpXG4gICAgICAgICAgICAuYWNjb3VudHMoe1xuICAgICAgICAgICAgICAgIC8vIHF1ZXVlOiBxdWV1ZVBkYSwgLy8gQXV0by1yZXNvbHZlZFxuICAgICAgICAgICAgICAgIGF1dGhvcml0eTogdGhpcy5wcm92aWRlci53YWxsZXQucHVibGljS2V5LFxuICAgICAgICAgICAgfSlcbiAgICAgICAgICAgIC5ycGModGhpcy5jb25maWcuY29uZmlybU9wdGlvbnMpO1xuICAgICAgICBjb25zb2xlLmxvZyhgQ2xvc2VkIFF1ZXVlOiAke3F1ZXVlUGRhLnRvQmFzZTU4KCl9YCk7XG4gICAgICAgIHJldHVybiB0eDtcbiAgICB9XG59XG4iXX0=