@microsoft/terraform-cdk-constructs 1.3.1 → 1.4.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 (72) hide show
  1. package/.jsii +10617 -7822
  2. package/API.md +25592 -20586
  3. package/lib/azure-actiongroup/lib/action-group.js +1 -1
  4. package/lib/azure-activitylogalert/lib/activity-log-alert.js +1 -1
  5. package/lib/azure-aks/lib/aks-cluster.js +1 -1
  6. package/lib/azure-diagnosticsettings/lib/diagnostic-settings.js +1 -1
  7. package/lib/azure-dnsforwardingruleset/lib/dns-forwarding-ruleset.js +1 -1
  8. package/lib/azure-dnsforwardingruleset/lib/forwarding-rule.js +1 -1
  9. package/lib/azure-dnsforwardingruleset/lib/virtual-network-link.js +1 -1
  10. package/lib/azure-dnsresolver/lib/dns-resolver.js +1 -1
  11. package/lib/azure-dnsresolver/lib/inbound-endpoint.js +1 -1
  12. package/lib/azure-dnsresolver/lib/outbound-endpoint.js +1 -1
  13. package/lib/azure-dnszone/lib/dns-zone.js +1 -1
  14. package/lib/azure-metricalert/lib/metric-alert.js +1 -1
  15. package/lib/azure-networkinterface/lib/network-interface.js +1 -1
  16. package/lib/azure-networksecuritygroup/lib/network-security-group.js +1 -1
  17. package/lib/azure-policyassignment/lib/policy-assignment.js +1 -1
  18. package/lib/azure-policydefinition/lib/policy-definition.js +1 -1
  19. package/lib/azure-privatednszone/lib/private-dns-zone.js +1 -1
  20. package/lib/azure-privatednszonelink/lib/private-dns-zone-link.js +1 -1
  21. package/lib/azure-publicipaddress/lib/public-ip-address.js +1 -1
  22. package/lib/azure-resourcegroup/lib/resource-group.js +1 -1
  23. package/lib/azure-roleassignment/lib/role-assignment.js +1 -1
  24. package/lib/azure-roledefinition/lib/role-definition.js +1 -1
  25. package/lib/azure-storageaccount/lib/storage-account.js +1 -1
  26. package/lib/azure-subnet/lib/subnet.js +1 -1
  27. package/lib/azure-virtualmachine/lib/virtual-machine.js +1 -1
  28. package/lib/azure-virtualnetwork/lib/virtual-network.js +1 -1
  29. package/lib/azure-virtualnetworkmanager/lib/connectivity-configuration.js +1 -1
  30. package/lib/azure-virtualnetworkmanager/lib/index.d.ts +5 -0
  31. package/lib/azure-virtualnetworkmanager/lib/index.js +6 -1
  32. package/lib/azure-virtualnetworkmanager/lib/ipam-pool-schemas.d.ts +24 -0
  33. package/lib/azure-virtualnetworkmanager/lib/ipam-pool-schemas.js +169 -0
  34. package/lib/azure-virtualnetworkmanager/lib/ipam-pool-static-cidr-schemas.d.ts +32 -0
  35. package/lib/azure-virtualnetworkmanager/lib/ipam-pool-static-cidr-schemas.js +206 -0
  36. package/lib/azure-virtualnetworkmanager/lib/ipam-pool-static-cidr.d.ts +170 -0
  37. package/lib/azure-virtualnetworkmanager/lib/ipam-pool-static-cidr.js +214 -0
  38. package/lib/azure-virtualnetworkmanager/lib/ipam-pool.d.ts +175 -0
  39. package/lib/azure-virtualnetworkmanager/lib/ipam-pool.js +206 -0
  40. package/lib/azure-virtualnetworkmanager/lib/network-group-static-member.js +1 -1
  41. package/lib/azure-virtualnetworkmanager/lib/network-group.js +1 -1
  42. package/lib/azure-virtualnetworkmanager/lib/security-admin-configuration.js +1 -1
  43. package/lib/azure-virtualnetworkmanager/lib/security-admin-rule-collection.js +1 -1
  44. package/lib/azure-virtualnetworkmanager/lib/security-admin-rule.js +1 -1
  45. package/lib/azure-virtualnetworkmanager/lib/utils/cidr-validator.d.ts +225 -0
  46. package/lib/azure-virtualnetworkmanager/lib/utils/cidr-validator.js +389 -0
  47. package/lib/azure-virtualnetworkmanager/lib/virtual-network-manager.d.ts +56 -0
  48. package/lib/azure-virtualnetworkmanager/lib/virtual-network-manager.js +29 -2
  49. package/lib/azure-virtualnetworkmanager/test/cidr-validator.spec.d.ts +6 -0
  50. package/lib/azure-virtualnetworkmanager/test/cidr-validator.spec.js +292 -0
  51. package/lib/azure-virtualnetworkmanager/test/ipam-pool-static-cidr.spec.d.ts +6 -0
  52. package/lib/azure-virtualnetworkmanager/test/ipam-pool-static-cidr.spec.js +430 -0
  53. package/lib/azure-virtualnetworkmanager/test/ipam-pool.spec.d.ts +6 -0
  54. package/lib/azure-virtualnetworkmanager/test/ipam-pool.spec.js +372 -0
  55. package/lib/azure-virtualnetworkmanager/test/virtual-network-manager.integ.d.ts +2 -1
  56. package/lib/azure-virtualnetworkmanager/test/virtual-network-manager.integ.js +30 -3
  57. package/lib/azure-virtualnetworkmanager/test/virtual-network-manager.spec.js +105 -1
  58. package/lib/azure-vmss/lib/virtual-machine-scale-set.js +1 -1
  59. package/lib/core-azure/lib/azapi/azapi-resource.js +2 -2
  60. package/lib/core-azure/lib/azapi/providers-azapi/data-azapi-client-config/index.js +2 -2
  61. package/lib/core-azure/lib/azapi/providers-azapi/data-azapi-resource/index.js +5 -5
  62. package/lib/core-azure/lib/azapi/providers-azapi/provider/index.js +1 -1
  63. package/lib/core-azure/lib/azapi/providers-azapi/resource/index.js +5 -5
  64. package/lib/core-azure/lib/azapi/providers-azapi/resource-action/index.js +3 -3
  65. package/lib/core-azure/lib/azapi/providers-azapi/update-resource/index.js +3 -3
  66. package/lib/core-azure/lib/azapi/schema-mapper/schema-mapper.js +1 -1
  67. package/lib/core-azure/lib/version-manager/api-version-manager.js +1 -1
  68. package/lib/core-azure/lib/version-manager/interfaces/version-interfaces.js +7 -7
  69. package/lib/testing/index.js +2 -2
  70. package/lib/testing/lib/cleanup.js +1 -1
  71. package/lib/testing/lib/metadata.js +1 -1
  72. package/package.json +1 -1
@@ -0,0 +1,225 @@
1
+ /**
2
+ * CIDR Validator Utility
3
+ *
4
+ * Provides comprehensive validation and parsing utilities for IPv4 CIDR notation.
5
+ * Used by IPAM constructs to ensure proper network address space management.
6
+ */
7
+ /**
8
+ * Result of CIDR validation operations
9
+ */
10
+ export interface CidrValidationResult {
11
+ /** Whether the validation passed */
12
+ readonly valid: boolean;
13
+ /** List of validation errors */
14
+ readonly errors: string[];
15
+ /** List of validation warnings */
16
+ readonly warnings: string[];
17
+ }
18
+ /**
19
+ * Parsed CIDR information
20
+ */
21
+ export interface ParsedCidr {
22
+ /** Original CIDR notation (e.g., "10.0.0.0/8") */
23
+ readonly cidr: string;
24
+ /** Network address (e.g., "10.0.0.0") */
25
+ readonly network: string;
26
+ /** Prefix length (e.g., 8) */
27
+ readonly prefix: number;
28
+ /** First usable IP address */
29
+ readonly firstIp: string;
30
+ /** Last usable IP address */
31
+ readonly lastIp: string;
32
+ /** Total number of addresses in the range */
33
+ readonly totalAddresses: number;
34
+ /** Network mask (e.g., "255.0.0.0") */
35
+ readonly netmask: string;
36
+ }
37
+ /**
38
+ * Validates if a string is a valid CIDR notation
39
+ *
40
+ * @param cidr - CIDR string (e.g., "10.0.0.0/16")
41
+ * @returns boolean - true if valid CIDR format
42
+ *
43
+ * @example
44
+ * const valid = isValidCidr("10.0.0.0/16");
45
+ * console.log(valid); // true
46
+ */
47
+ export declare function isValidCidr(cidr: string): boolean;
48
+ /**
49
+ * Validates if CIDR is within allowed private ranges (RFC 1918)
50
+ *
51
+ * @param cidr - CIDR string
52
+ * @returns boolean - true if within private IP range
53
+ *
54
+ * @example
55
+ * const isPrivate = isPrivateRange("10.0.0.0/16");
56
+ * console.log(isPrivate); // true
57
+ */
58
+ export declare function isPrivateRange(cidr: string): boolean;
59
+ /**
60
+ * Checks if two CIDR blocks overlap
61
+ *
62
+ * @param cidr1 - First CIDR
63
+ * @param cidr2 - Second CIDR
64
+ * @returns boolean - true if CIDRs overlap
65
+ *
66
+ * @example
67
+ * const overlap = cidrsOverlap("10.0.0.0/8", "10.1.0.0/16");
68
+ * console.log(overlap); // true
69
+ */
70
+ export declare function cidrsOverlap(cidr1: string, cidr2: string): boolean;
71
+ /**
72
+ * Validates if child CIDR is contained within parent CIDR
73
+ *
74
+ * @param childCidr - Child CIDR block
75
+ * @param parentCidr - Parent CIDR block
76
+ * @returns boolean - true if child is subnet of parent
77
+ *
78
+ * @example
79
+ * const isSubnet = isSubnet("10.1.0.0/16", "10.0.0.0/8");
80
+ * console.log(isSubnet); // true
81
+ */
82
+ export declare function isSubnet(childCidr: string, parentCidr: string): boolean;
83
+ /**
84
+ * Calculates number of IP addresses in a CIDR block
85
+ *
86
+ * @param cidr - CIDR string
87
+ * @returns number - Total IP addresses in the block
88
+ *
89
+ * @example
90
+ * const count = calculateAddressCount("10.0.0.0/24");
91
+ * console.log(count); // 256
92
+ */
93
+ export declare function calculateAddressCount(cidr: string): number;
94
+ /**
95
+ * Validates prefix length is within allowed range
96
+ *
97
+ * @param cidr - CIDR string
98
+ * @param minPrefix - Minimum allowed prefix (e.g., 8)
99
+ * @param maxPrefix - Maximum allowed prefix (e.g., 29)
100
+ * @returns boolean - true if prefix length is within range
101
+ *
102
+ * @example
103
+ * const valid = isValidPrefixLength("10.0.0.0/24", 8, 29);
104
+ * console.log(valid); // true
105
+ */
106
+ export declare function isValidPrefixLength(cidr: string, minPrefix: number, maxPrefix: number): boolean;
107
+ /**
108
+ * Validate CIDR format and structure
109
+ *
110
+ * @param cidr - CIDR notation string (e.g., "10.0.0.0/8")
111
+ * @returns Validation result with errors and warnings
112
+ *
113
+ * @example
114
+ * const result = validateCidr("10.0.0.0/8");
115
+ * if (!result.valid) {
116
+ * console.error("Invalid CIDR:", result.errors);
117
+ * }
118
+ */
119
+ export declare function validateCidr(cidr: string): CidrValidationResult;
120
+ /**
121
+ * Parse CIDR into structured information
122
+ *
123
+ * @param cidr - CIDR notation string
124
+ * @returns Parsed CIDR information
125
+ * @throws Error if CIDR format is invalid
126
+ *
127
+ * @example
128
+ * const parsed = parseCidr("10.0.0.0/8");
129
+ * console.log(`Network: ${parsed.network}, Prefix: ${parsed.prefix}`);
130
+ * console.log(`Range: ${parsed.firstIp} - ${parsed.lastIp}`);
131
+ * console.log(`Total addresses: ${parsed.totalAddresses}`);
132
+ */
133
+ export declare function parseCidr(cidr: string): ParsedCidr;
134
+ /**
135
+ * Check if two CIDRs overlap
136
+ *
137
+ * @param cidr1 - First CIDR block
138
+ * @param cidr2 - Second CIDR block
139
+ * @returns True if the CIDRs overlap
140
+ *
141
+ * @example
142
+ * const overlaps = checkOverlap("10.0.0.0/16", "10.0.1.0/24");
143
+ * console.log(overlaps); // true
144
+ */
145
+ export declare function checkOverlap(cidr1: string, cidr2: string): boolean;
146
+ /**
147
+ * Validate that multiple CIDRs don't overlap
148
+ *
149
+ * @param cidrs - Array of CIDR blocks to check
150
+ * @returns Validation result with details of any overlaps
151
+ *
152
+ * @example
153
+ * const result = validateNoOverlaps([
154
+ * "10.0.0.0/16",
155
+ * "10.1.0.0/16",
156
+ * "10.0.1.0/24"
157
+ * ]);
158
+ * if (!result.valid) {
159
+ * console.error("Overlapping CIDRs:", result.errors);
160
+ * }
161
+ */
162
+ export declare function validateNoOverlaps(cidrs: string[]): CidrValidationResult;
163
+ /**
164
+ * Check if a child CIDR is contained within a parent CIDR
165
+ *
166
+ * @param parentCidr - Parent CIDR block
167
+ * @param childCidr - Child CIDR block to check
168
+ * @returns True if child is fully contained in parent
169
+ *
170
+ * @example
171
+ * const contained = isContained("10.0.0.0/16", "10.0.1.0/24");
172
+ * console.log(contained); // true
173
+ */
174
+ export declare function isContained(parentCidr: string, childCidr: string): boolean;
175
+ /**
176
+ * Validate that multiple child CIDRs are all contained within a parent CIDR
177
+ *
178
+ * @param parentCidr - Parent CIDR block
179
+ * @param childCidrs - Array of child CIDR blocks
180
+ * @returns Validation result with details of any containment violations
181
+ *
182
+ * @example
183
+ * const result = validateContainment("10.0.0.0/16", [
184
+ * "10.0.1.0/24",
185
+ * "10.0.2.0/24"
186
+ * ]);
187
+ * if (!result.valid) {
188
+ * console.error("Containment violations:", result.errors);
189
+ * }
190
+ */
191
+ export declare function validateContainment(parentCidr: string, childCidrs: string[]): CidrValidationResult;
192
+ /**
193
+ * Convert an IP address string to a 32-bit number
194
+ *
195
+ * @param ip - IP address string (e.g., "10.0.0.1")
196
+ * @returns 32-bit number representation
197
+ *
198
+ * @example
199
+ * const num = ipToNumber("10.0.0.1");
200
+ * console.log(num); // 167772161
201
+ */
202
+ export declare function ipToNumber(ip: string): number;
203
+ /**
204
+ * Convert a 32-bit number to an IP address string
205
+ *
206
+ * @param num - 32-bit number representation
207
+ * @returns IP address string
208
+ *
209
+ * @example
210
+ * const ip = numberToIp(167772161);
211
+ * console.log(ip); // "10.0.0.1"
212
+ */
213
+ export declare function numberToIp(num: number): string;
214
+ /**
215
+ * Convert a prefix length to a netmask number
216
+ *
217
+ * @param prefix - Prefix length (0-32)
218
+ * @returns 32-bit netmask number
219
+ *
220
+ * @example
221
+ * const mask = prefixToMask(24);
222
+ * const maskIp = numberToIp(mask);
223
+ * console.log(maskIp); // "255.255.255.0"
224
+ */
225
+ export declare function prefixToMask(prefix: number): number;
@@ -0,0 +1,389 @@
1
+ "use strict";
2
+ /**
3
+ * CIDR Validator Utility
4
+ *
5
+ * Provides comprehensive validation and parsing utilities for IPv4 CIDR notation.
6
+ * Used by IPAM constructs to ensure proper network address space management.
7
+ */
8
+ Object.defineProperty(exports, "__esModule", { value: true });
9
+ exports.isValidCidr = isValidCidr;
10
+ exports.isPrivateRange = isPrivateRange;
11
+ exports.cidrsOverlap = cidrsOverlap;
12
+ exports.isSubnet = isSubnet;
13
+ exports.calculateAddressCount = calculateAddressCount;
14
+ exports.isValidPrefixLength = isValidPrefixLength;
15
+ exports.validateCidr = validateCidr;
16
+ exports.parseCidr = parseCidr;
17
+ exports.checkOverlap = checkOverlap;
18
+ exports.validateNoOverlaps = validateNoOverlaps;
19
+ exports.isContained = isContained;
20
+ exports.validateContainment = validateContainment;
21
+ exports.ipToNumber = ipToNumber;
22
+ exports.numberToIp = numberToIp;
23
+ exports.prefixToMask = prefixToMask;
24
+ const IPV4_REGEX = /^(\d{1,3})\.(\d{1,3})\.(\d{1,3})\.(\d{1,3})\/(\d{1,2})$/;
25
+ // RFC 1918 private IP ranges
26
+ const PRIVATE_RANGES = [
27
+ { start: "10.0.0.0", end: "10.255.255.255", cidr: "10.0.0.0/8" },
28
+ { start: "172.16.0.0", end: "172.31.255.255", cidr: "172.16.0.0/12" },
29
+ { start: "192.168.0.0", end: "192.168.255.255", cidr: "192.168.0.0/16" },
30
+ ];
31
+ /**
32
+ * Validates if a string is a valid CIDR notation
33
+ *
34
+ * @param cidr - CIDR string (e.g., "10.0.0.0/16")
35
+ * @returns boolean - true if valid CIDR format
36
+ *
37
+ * @example
38
+ * const valid = isValidCidr("10.0.0.0/16");
39
+ * console.log(valid); // true
40
+ */
41
+ function isValidCidr(cidr) {
42
+ const result = validateCidr(cidr);
43
+ return result.valid;
44
+ }
45
+ /**
46
+ * Validates if CIDR is within allowed private ranges (RFC 1918)
47
+ *
48
+ * @param cidr - CIDR string
49
+ * @returns boolean - true if within private IP range
50
+ *
51
+ * @example
52
+ * const isPrivate = isPrivateRange("10.0.0.0/16");
53
+ * console.log(isPrivate); // true
54
+ */
55
+ function isPrivateRange(cidr) {
56
+ if (!isValidCidr(cidr)) {
57
+ return false;
58
+ }
59
+ const parsed = parseCidr(cidr);
60
+ const startNum = ipToNumber(parsed.firstIp);
61
+ const endNum = ipToNumber(parsed.lastIp);
62
+ // Check if the entire CIDR range falls within any private range
63
+ return PRIVATE_RANGES.some((range) => {
64
+ const rangeStart = ipToNumber(range.start);
65
+ const rangeEnd = ipToNumber(range.end);
66
+ return startNum >= rangeStart && endNum <= rangeEnd;
67
+ });
68
+ }
69
+ /**
70
+ * Checks if two CIDR blocks overlap
71
+ *
72
+ * @param cidr1 - First CIDR
73
+ * @param cidr2 - Second CIDR
74
+ * @returns boolean - true if CIDRs overlap
75
+ *
76
+ * @example
77
+ * const overlap = cidrsOverlap("10.0.0.0/8", "10.1.0.0/16");
78
+ * console.log(overlap); // true
79
+ */
80
+ function cidrsOverlap(cidr1, cidr2) {
81
+ return checkOverlap(cidr1, cidr2);
82
+ }
83
+ /**
84
+ * Validates if child CIDR is contained within parent CIDR
85
+ *
86
+ * @param childCidr - Child CIDR block
87
+ * @param parentCidr - Parent CIDR block
88
+ * @returns boolean - true if child is subnet of parent
89
+ *
90
+ * @example
91
+ * const isSubnet = isSubnet("10.1.0.0/16", "10.0.0.0/8");
92
+ * console.log(isSubnet); // true
93
+ */
94
+ function isSubnet(childCidr, parentCidr) {
95
+ return isContained(parentCidr, childCidr);
96
+ }
97
+ /**
98
+ * Calculates number of IP addresses in a CIDR block
99
+ *
100
+ * @param cidr - CIDR string
101
+ * @returns number - Total IP addresses in the block
102
+ *
103
+ * @example
104
+ * const count = calculateAddressCount("10.0.0.0/24");
105
+ * console.log(count); // 256
106
+ */
107
+ function calculateAddressCount(cidr) {
108
+ const parsed = parseCidr(cidr);
109
+ return parsed.totalAddresses;
110
+ }
111
+ /**
112
+ * Validates prefix length is within allowed range
113
+ *
114
+ * @param cidr - CIDR string
115
+ * @param minPrefix - Minimum allowed prefix (e.g., 8)
116
+ * @param maxPrefix - Maximum allowed prefix (e.g., 29)
117
+ * @returns boolean - true if prefix length is within range
118
+ *
119
+ * @example
120
+ * const valid = isValidPrefixLength("10.0.0.0/24", 8, 29);
121
+ * console.log(valid); // true
122
+ */
123
+ function isValidPrefixLength(cidr, minPrefix, maxPrefix) {
124
+ if (!isValidCidr(cidr)) {
125
+ return false;
126
+ }
127
+ const parsed = parseCidr(cidr);
128
+ return parsed.prefix >= minPrefix && parsed.prefix <= maxPrefix;
129
+ }
130
+ /**
131
+ * Validate CIDR format and structure
132
+ *
133
+ * @param cidr - CIDR notation string (e.g., "10.0.0.0/8")
134
+ * @returns Validation result with errors and warnings
135
+ *
136
+ * @example
137
+ * const result = validateCidr("10.0.0.0/8");
138
+ * if (!result.valid) {
139
+ * console.error("Invalid CIDR:", result.errors);
140
+ * }
141
+ */
142
+ function validateCidr(cidr) {
143
+ const errors = [];
144
+ const warnings = [];
145
+ if (!cidr || typeof cidr !== "string") {
146
+ errors.push("CIDR must be a non-empty string");
147
+ return { valid: false, errors, warnings };
148
+ }
149
+ const match = cidr.match(IPV4_REGEX);
150
+ if (!match) {
151
+ errors.push(`Invalid CIDR format: ${cidr}. Expected format: x.x.x.x/y`);
152
+ return { valid: false, errors, warnings };
153
+ }
154
+ const [, oct1, oct2, oct3, oct4, prefix] = match;
155
+ const octets = [oct1, oct2, oct3, oct4].map(Number);
156
+ const prefixLength = Number(prefix);
157
+ // Validate octets (0-255)
158
+ for (let i = 0; i < octets.length; i++) {
159
+ if (octets[i] < 0 || octets[i] > 255) {
160
+ errors.push(`Invalid octet value: ${octets[i]}. Must be between 0 and 255`);
161
+ }
162
+ }
163
+ // Validate prefix length (0-32)
164
+ if (prefixLength < 0 || prefixLength > 32) {
165
+ errors.push(`Invalid prefix length: ${prefixLength}. Must be between 0 and 32`);
166
+ }
167
+ if (errors.length > 0) {
168
+ return { valid: false, errors, warnings };
169
+ }
170
+ // Verify network address alignment
171
+ const ipNum = ipToNumber(octets.join("."));
172
+ const mask = prefixToMask(prefixLength);
173
+ const networkNum = ipNum & mask;
174
+ if (ipNum !== networkNum) {
175
+ const correctNetwork = numberToIp(networkNum);
176
+ warnings.push(`IP address ${octets.join(".")} is not aligned to network boundary. ` +
177
+ `Network address should be ${correctNetwork}/${prefixLength}`);
178
+ }
179
+ return { valid: errors.length === 0, errors, warnings };
180
+ }
181
+ /**
182
+ * Parse CIDR into structured information
183
+ *
184
+ * @param cidr - CIDR notation string
185
+ * @returns Parsed CIDR information
186
+ * @throws Error if CIDR format is invalid
187
+ *
188
+ * @example
189
+ * const parsed = parseCidr("10.0.0.0/8");
190
+ * console.log(`Network: ${parsed.network}, Prefix: ${parsed.prefix}`);
191
+ * console.log(`Range: ${parsed.firstIp} - ${parsed.lastIp}`);
192
+ * console.log(`Total addresses: ${parsed.totalAddresses}`);
193
+ */
194
+ function parseCidr(cidr) {
195
+ const validation = validateCidr(cidr);
196
+ if (!validation.valid) {
197
+ throw new Error(`Invalid CIDR: ${validation.errors.join(", ")}`);
198
+ }
199
+ const match = cidr.match(IPV4_REGEX);
200
+ const [, oct1, oct2, oct3, oct4, prefix] = match;
201
+ const ipAddress = `${oct1}.${oct2}.${oct3}.${oct4}`;
202
+ const prefixLength = Number(prefix);
203
+ const ipNum = ipToNumber(ipAddress);
204
+ const mask = prefixToMask(prefixLength);
205
+ const networkNum = ipNum & mask;
206
+ const broadcastNum = networkNum | (~mask >>> 0);
207
+ const totalAddresses = Math.pow(2, 32 - prefixLength);
208
+ const firstIpNum = networkNum;
209
+ const lastIpNum = broadcastNum;
210
+ return {
211
+ cidr,
212
+ network: numberToIp(networkNum),
213
+ prefix: prefixLength,
214
+ firstIp: numberToIp(firstIpNum),
215
+ lastIp: numberToIp(lastIpNum),
216
+ totalAddresses,
217
+ netmask: numberToIp(mask),
218
+ };
219
+ }
220
+ /**
221
+ * Check if two CIDRs overlap
222
+ *
223
+ * @param cidr1 - First CIDR block
224
+ * @param cidr2 - Second CIDR block
225
+ * @returns True if the CIDRs overlap
226
+ *
227
+ * @example
228
+ * const overlaps = checkOverlap("10.0.0.0/16", "10.0.1.0/24");
229
+ * console.log(overlaps); // true
230
+ */
231
+ function checkOverlap(cidr1, cidr2) {
232
+ const parsed1 = parseCidr(cidr1);
233
+ const parsed2 = parseCidr(cidr2);
234
+ const start1 = ipToNumber(parsed1.firstIp);
235
+ const end1 = ipToNumber(parsed1.lastIp);
236
+ const start2 = ipToNumber(parsed2.firstIp);
237
+ const end2 = ipToNumber(parsed2.lastIp);
238
+ // Check if ranges overlap
239
+ return !(end1 < start2 || end2 < start1);
240
+ }
241
+ /**
242
+ * Validate that multiple CIDRs don't overlap
243
+ *
244
+ * @param cidrs - Array of CIDR blocks to check
245
+ * @returns Validation result with details of any overlaps
246
+ *
247
+ * @example
248
+ * const result = validateNoOverlaps([
249
+ * "10.0.0.0/16",
250
+ * "10.1.0.0/16",
251
+ * "10.0.1.0/24"
252
+ * ]);
253
+ * if (!result.valid) {
254
+ * console.error("Overlapping CIDRs:", result.errors);
255
+ * }
256
+ */
257
+ function validateNoOverlaps(cidrs) {
258
+ const errors = [];
259
+ const warnings = [];
260
+ // First validate each CIDR individually
261
+ for (const cidr of cidrs) {
262
+ const validation = validateCidr(cidr);
263
+ if (!validation.valid) {
264
+ errors.push(`Invalid CIDR ${cidr}: ${validation.errors.join(", ")}`);
265
+ }
266
+ }
267
+ if (errors.length > 0) {
268
+ return { valid: false, errors, warnings };
269
+ }
270
+ // Check for overlaps
271
+ for (let i = 0; i < cidrs.length; i++) {
272
+ for (let j = i + 1; j < cidrs.length; j++) {
273
+ if (checkOverlap(cidrs[i], cidrs[j])) {
274
+ errors.push(`CIDRs overlap: ${cidrs[i]} and ${cidrs[j]}`);
275
+ }
276
+ }
277
+ }
278
+ return { valid: errors.length === 0, errors, warnings };
279
+ }
280
+ /**
281
+ * Check if a child CIDR is contained within a parent CIDR
282
+ *
283
+ * @param parentCidr - Parent CIDR block
284
+ * @param childCidr - Child CIDR block to check
285
+ * @returns True if child is fully contained in parent
286
+ *
287
+ * @example
288
+ * const contained = isContained("10.0.0.0/16", "10.0.1.0/24");
289
+ * console.log(contained); // true
290
+ */
291
+ function isContained(parentCidr, childCidr) {
292
+ const parent = parseCidr(parentCidr);
293
+ const child = parseCidr(childCidr);
294
+ const parentStart = ipToNumber(parent.firstIp);
295
+ const parentEnd = ipToNumber(parent.lastIp);
296
+ const childStart = ipToNumber(child.firstIp);
297
+ const childEnd = ipToNumber(child.lastIp);
298
+ // Child must be fully within parent range
299
+ return childStart >= parentStart && childEnd <= parentEnd;
300
+ }
301
+ /**
302
+ * Validate that multiple child CIDRs are all contained within a parent CIDR
303
+ *
304
+ * @param parentCidr - Parent CIDR block
305
+ * @param childCidrs - Array of child CIDR blocks
306
+ * @returns Validation result with details of any containment violations
307
+ *
308
+ * @example
309
+ * const result = validateContainment("10.0.0.0/16", [
310
+ * "10.0.1.0/24",
311
+ * "10.0.2.0/24"
312
+ * ]);
313
+ * if (!result.valid) {
314
+ * console.error("Containment violations:", result.errors);
315
+ * }
316
+ */
317
+ function validateContainment(parentCidr, childCidrs) {
318
+ const errors = [];
319
+ const warnings = [];
320
+ // Validate parent CIDR
321
+ const parentValidation = validateCidr(parentCidr);
322
+ if (!parentValidation.valid) {
323
+ errors.push(`Invalid parent CIDR ${parentCidr}: ${parentValidation.errors.join(", ")}`);
324
+ return { valid: false, errors, warnings };
325
+ }
326
+ // Validate each child CIDR
327
+ for (const childCidr of childCidrs) {
328
+ const childValidation = validateCidr(childCidr);
329
+ if (!childValidation.valid) {
330
+ errors.push(`Invalid child CIDR ${childCidr}: ${childValidation.errors.join(", ")}`);
331
+ continue;
332
+ }
333
+ // Check containment
334
+ if (!isContained(parentCidr, childCidr)) {
335
+ errors.push(`Child CIDR ${childCidr} is not contained within parent CIDR ${parentCidr}`);
336
+ }
337
+ }
338
+ return { valid: errors.length === 0, errors, warnings };
339
+ }
340
+ /**
341
+ * Convert an IP address string to a 32-bit number
342
+ *
343
+ * @param ip - IP address string (e.g., "10.0.0.1")
344
+ * @returns 32-bit number representation
345
+ *
346
+ * @example
347
+ * const num = ipToNumber("10.0.0.1");
348
+ * console.log(num); // 167772161
349
+ */
350
+ function ipToNumber(ip) {
351
+ const octets = ip.split(".").map(Number);
352
+ return (((octets[0] << 24) >>> 0) +
353
+ ((octets[1] << 16) >>> 0) +
354
+ ((octets[2] << 8) >>> 0) +
355
+ octets[3]);
356
+ }
357
+ /**
358
+ * Convert a 32-bit number to an IP address string
359
+ *
360
+ * @param num - 32-bit number representation
361
+ * @returns IP address string
362
+ *
363
+ * @example
364
+ * const ip = numberToIp(167772161);
365
+ * console.log(ip); // "10.0.0.1"
366
+ */
367
+ function numberToIp(num) {
368
+ return [
369
+ (num >>> 24) & 0xff,
370
+ (num >>> 16) & 0xff,
371
+ (num >>> 8) & 0xff,
372
+ num & 0xff,
373
+ ].join(".");
374
+ }
375
+ /**
376
+ * Convert a prefix length to a netmask number
377
+ *
378
+ * @param prefix - Prefix length (0-32)
379
+ * @returns 32-bit netmask number
380
+ *
381
+ * @example
382
+ * const mask = prefixToMask(24);
383
+ * const maskIp = numberToIp(mask);
384
+ * console.log(maskIp); // "255.255.255.0"
385
+ */
386
+ function prefixToMask(prefix) {
387
+ return prefix === 0 ? 0 : (0xffffffff << (32 - prefix)) >>> 0;
388
+ }
389
+ //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiY2lkci12YWxpZGF0b3IuanMiLCJzb3VyY2VSb290IjoiIiwic291cmNlcyI6WyIuLi8uLi8uLi8uLi9zcmMvYXp1cmUtdmlydHVhbG5ldHdvcmttYW5hZ2VyL2xpYi91dGlscy9jaWRyLXZhbGlkYXRvci50cyJdLCJuYW1lcyI6W10sIm1hcHBpbmdzIjoiO0FBQUE7Ozs7O0dBS0c7O0FBdURILGtDQUdDO0FBWUQsd0NBZUM7QUFhRCxvQ0FFQztBQWFELDRCQUVDO0FBWUQsc0RBR0M7QUFjRCxrREFXQztBQWNELG9DQXFEQztBQWVELDhCQTZCQztBQWFELG9DQVdDO0FBa0JELGdEQTBCQztBQWFELGtDQVdDO0FBa0JELGtEQW1DQztBQVlELGdDQVFDO0FBWUQsZ0NBT0M7QUFhRCxvQ0FFQztBQTdhRCxNQUFNLFVBQVUsR0FBRyx5REFBeUQsQ0FBQztBQUU3RSw2QkFBNkI7QUFDN0IsTUFBTSxjQUFjLEdBQUc7SUFDckIsRUFBRSxLQUFLLEVBQUUsVUFBVSxFQUFFLEdBQUcsRUFBRSxnQkFBZ0IsRUFBRSxJQUFJLEVBQUUsWUFBWSxFQUFFO0lBQ2hFLEVBQUUsS0FBSyxFQUFFLFlBQVksRUFBRSxHQUFHLEVBQUUsZ0JBQWdCLEVBQUUsSUFBSSxFQUFFLGVBQWUsRUFBRTtJQUNyRSxFQUFFLEtBQUssRUFBRSxhQUFhLEVBQUUsR0FBRyxFQUFFLGlCQUFpQixFQUFFLElBQUksRUFBRSxnQkFBZ0IsRUFBRTtDQUN6RSxDQUFDO0FBRUY7Ozs7Ozs7OztHQVNHO0FBQ0gsU0FBZ0IsV0FBVyxDQUFDLElBQVk7SUFDdEMsTUFBTSxNQUFNLEdBQUcsWUFBWSxDQUFDLElBQUksQ0FBQyxDQUFDO0lBQ2xDLE9BQU8sTUFBTSxDQUFDLEtBQUssQ0FBQztBQUN0QixDQUFDO0FBRUQ7Ozs7Ozs7OztHQVNHO0FBQ0gsU0FBZ0IsY0FBYyxDQUFDLElBQVk7SUFDekMsSUFBSSxDQUFDLFdBQVcsQ0FBQyxJQUFJLENBQUMsRUFBRSxDQUFDO1FBQ3ZCLE9BQU8sS0FBSyxDQUFDO0lBQ2Ys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QUFDLEdBQUcsQ0FBQyxDQUFDO0FBQ2QsQ0FBQztBQUVEOzs7Ozs7Ozs7O0dBVUc7QUFDSCxTQUFnQixZQUFZLENBQUMsTUFBYztJQUN6QyxPQUFPLE1BQU0sS0FBSyxDQUFDLENBQUMsQ0FBQyxDQUFDLENBQUMsQ0FBQyxDQUFDLENBQUMsQ0FBQyxVQUFVLElBQUksQ0FBQyxFQUFFLEdBQUcsTUFBTSxDQUFDLENBQUMsS0FBSyxDQUFDLENBQUM7QUFDaEUsQ0FBQyIsInNvdXJjZXNDb250ZW50IjpbIi8qKlxuICogQ0lEUiBWYWxpZGF0b3IgVXRpbGl0eVxuICpcbiAqIFByb3ZpZGVzIGNvbXByZWhlbnNpdmUgdmFsaWRhdGlvbiBhbmQgcGFyc2luZyB1dGlsaXRpZXMgZm9yIElQdjQgQ0lEUiBub3RhdGlvbi5cbiAqIFVzZWQgYnkgSVBBTSBjb25zdHJ1Y3RzIHRvIGVuc3VyZSBwcm9wZXIgbmV0d29yayBhZGRyZXNzIHNwYWNlIG1hbmFnZW1lbnQuXG4gKi9cblxuLyogZXNsaW50LWRpc2FibGUgbm8tYml0d2lzZSAqL1xuXG4vKipcbiAqIFJlc3VsdCBvZiBDSURSIHZhbGlkYXRpb24gb3BlcmF0aW9uc1xuICovXG5leHBvcnQgaW50ZXJmYWNlIENpZHJWYWxpZGF0aW9uUmVzdWx0IHtcbiAgLyoqIFdoZXRoZXIgdGhlIHZhbGlkYXRpb24gcGFzc2VkICovXG4gIHJlYWRvbmx5IHZhbGlkOiBib29sZWFuO1xuICAvKiogTGlzdCBvZiB2YWxpZGF0aW9uIGVycm9ycyAqL1xuICByZWFkb25seSBlcnJvcnM6IHN0cmluZ1tdO1xuICAvKiogTGlzdCBvZiB2YWxpZGF0aW9uIHdhcm5pbmdzICovXG4gIHJlYWRvbmx5IHdhcm5pbmdzOiBzdHJpbmdbXTtcbn1cblxuLyoqXG4gKiBQYXJzZWQgQ0lEUiBpbmZvcm1hdGlvblxuICovXG5leHBvcnQgaW50ZXJmYWNlIFBhcnNlZENpZHIge1xuICAvKiogT3JpZ2luYWwgQ0lEUiBub3RhdGlvbiAoZS5nLiwgXCIxMC4wLjAuMC84XCIpICovXG4gIHJlYWRvbmx5IGNpZHI6IHN0cmluZztcbiAgLyoqIE5ldHdvcmsgYWRkcmVzcyAoZS5nLiwgXCIxMC4wLjAuMFwiKSAqL1xuICByZWFkb25seSBuZXR3b3JrOiBzdHJpbmc7XG4gIC8qKiBQcmVmaXggbGVuZ3RoIChlLmcuLCA4KSAqL1xuICByZWFkb25seSBwcmVmaXg6IG51bWJlcjtcbiAgLyoqIEZpcnN0IHVzYWJsZSBJUCBhZGRyZXNzICovXG4gIHJlYWRvbmx5IGZpcnN0SXA6IHN0cmluZztcbiAgLyoqIExhc3QgdXNhYmxlIElQIGFkZHJlc3MgKi9cbiAgcmVhZG9ubHkgbGFzdElwOiBzdHJpbmc7XG4gIC8qKiBUb3RhbCBudW1iZXIgb2YgYWRkcmVzc2VzIGluIHRoZSByYW5nZSAqL1xuICByZWFkb25seSB0b3RhbEFkZHJlc3NlczogbnVtYmVyO1xuICAvKiogTmV0d29yayBtYXNrIChlLmcuLCBcIjI1NS4wLjAuMFwiKSAqL1xuICByZWFkb25seSBuZXRtYXNrOiBzdHJpbmc7XG59XG5cbmNvbnN0IElQVjRfUkVHRVggPSAvXihcXGR7MSwzfSlcXC4oXFxkezEsM30pXFwuKFxcZHsxLDN9KVxcLihcXGR7MSwzfSlcXC8oXFxkezEsMn0pJC87XG5cbi8vIFJGQyAxOTE4IHByaXZhdGUgSVAgcmFuZ2VzXG5jb25zdCBQUklWQVRFX1JBTkdFUyA9IFtcbiAgeyBzdGFydDogXCIxMC4wLjAuMFwiLCBlbmQ6IFwiMTAuMjU1LjI1NS4yNTVcIiwgY2lkcjogXCIxMC4wLjAuMC84XCIgfSxcbiAgeyBzdGFydDogXCIxNzIuMTYuMC4wXCIsIGVuZDogXCIxNzIuMzEuMjU1LjI1NVwiLCBjaWRyOiBcIjE3Mi4xNi4wLjAvMTJcIiB9LFxuICB7IHN0YXJ0OiBcIjE5Mi4xNjguMC4wXCIsIGVuZDogXCIxOTIuMTY4LjI1NS4yNTVcIiwgY2lkcjogXCIxOTIuMTY4LjAuMC8xNlwiIH0sXG5dO1xuXG4vKipcbiAqIFZhbGlkYXRlcyBpZiBhIHN0cmluZyBpcyBhIHZhbGlkIENJRFIgbm90YXRpb25cbiAqXG4gKiBAcGFyYW0gY2lkciAtIENJRFIgc3RyaW5nIChlLmcuLCBcIjEwLjAuMC4wLzE2XCIpXG4gKiBAcmV0dXJucyBib29sZWFuIC0gdHJ1ZSBpZiB2YWxpZCBDSURSIGZvcm1hdFxuICpcbiAqIEBleGFtcGxlXG4gKiBjb25zdCB2YWxpZCA9IGlzVmFsaWRDaWRyKFwiMTAuMC4wLjAvMTZcIik7XG4gKiBjb25zb2xlLmxvZyh2YWxpZCk7IC8vIHRydWVcbiAqL1xuZXhwb3J0IGZ1bmN0aW9uIGlzVmFsaWRDaWRyKGNpZHI6IHN0cmluZyk6IGJvb2xlYW4ge1xuICBjb25zdCByZXN1bHQgPSB2YWxpZGF0ZUNpZHIoY2lkcik7XG4gIHJldHVybiByZXN1bHQudmFsaWQ7XG59XG5cbi8qKlxuICogVmFsaWRhdGVzIGlmIENJRFIgaXMgd2l0aGluIGFsbG93ZWQgcHJpdmF0ZSByYW5nZXMgKFJGQyAxOTE4KVxuICpcbiAqIEBwYXJhbSBjaWRyIC0gQ0lEUiBzdHJpbmdcbiAqIEByZXR1cm5zIGJvb2xlYW4gLSB0cnVlIGlmIHdpdGhpbiBwcml2YXRlIElQIHJhbmdlXG4gKlxuICogQGV4YW1wbGVcbiAqIGNvbnN0IGlzUHJpdmF0ZSA9IGlzUHJpdmF0ZVJhbmdlKFwiMTAuMC4wLjAvMTZcIik7XG4gKiBjb25zb2xlLmxvZyhpc1ByaXZhdGUpOyAvLyB0cnVlXG4gKi9cbmV4cG9ydCBmdW5jdGlvbiBpc1ByaXZhdGVSYW5nZShjaWRyOiBzdHJpbmcpOiBib29sZWFuIHtcbiAgaWYgKCFpc1ZhbGlkQ2lkcihjaWRyKSkge1xuICAgIHJldHVybiBmYWxzZTtcbiAgfVxuXG4gIGNvbnN0IHBhcnNlZCA9IHBhcnNlQ2lkcihjaWRyKTtcbiAgY29uc3Qgc3RhcnROdW0gPSBpcFRvTnVtYmVyKHBhcnNlZC5maXJzdElwKTtcbiAgY29uc3QgZW5kTnVtID0gaXBUb051bWJlcihwYXJzZWQubGFzdElwKTtcblxuICAvLyBDaGVjayBpZiB0aGUgZW50aXJlIENJRFIgcmFuZ2UgZmFsbHMgd2l0aGluIGFueSBwcml2YXRlIHJhbmdlXG4gIHJldHVybiBQUklWQVRFX1JBTkdFUy5zb21lKChyYW5nZSkgPT4ge1xuICAgIGNvbnN0IHJhbmdlU3RhcnQgPSBpcFRvTnVtYmVyKHJhbmdlLnN0YXJ0KTtcbiAgICBjb25zdCByYW5nZUVuZCA9IGlwVG9OdW1iZXIocmFuZ2UuZW5kKTtcbiAgICByZXR1cm4gc3RhcnROdW0gPj0gcmFuZ2VTdGFydCAmJiBlbmROdW0gPD0gcmFuZ2VFbmQ7XG4gIH0pO1xufVxuXG4vKipcbiAqIENoZWNrcyBpZiB0d28gQ0lEUiBibG9ja3Mgb3ZlcmxhcFxuICpcbiAqIEBwYXJhbSBjaWRyMSAtIEZpcnN0IENJRFJcbiAqIEBwYXJhbSBjaWRyMiAtIFNlY29uZCBDSURSXG4gKiBAcmV0dXJucyBib29sZWFuIC0gdHJ1ZSBpZiBDSURScyBvdmVybGFwXG4gKlxuICogQGV4YW1wbGVcbiAqIGNvbnN0IG92ZXJsYXAgPSBjaWRyc092ZXJsYXAoXCIxMC4wLjAuMC84XCIsIFwiMTAuMS4wLjAvMTZcIik7XG4gKiBjb25zb2xlLmxvZyhvdmVybGFwKTsgLy8gdHJ1ZVxuICovXG5leHBvcnQgZnVuY3Rpb24gY2lkcnNPdmVybGFwKGNpZHIxOiBzdHJpbmcsIGNpZHIyOiBzdHJpbmcpOiBib29sZWFuIHtcbiAgcmV0dXJuIGNoZWNrT3ZlcmxhcChjaWRyMSwgY2lkcjIpO1xufVxuXG4vKipcbiAqIFZhbGlkYXRlcyBpZiBjaGlsZCBDSURSIGlzIGNvbnRhaW5lZCB3aXRoaW4gcGFyZW50IENJRFJcbiAqXG4gKiBAcGFyYW0gY2hpbGRDaWRyIC0gQ2hpbGQgQ0lEUiBibG9ja1xuICogQHBhcmFtIHBhcmVudENpZHIgLSBQYXJlbnQgQ0lEUiBibG9ja1xuICogQHJldHVybnMgYm9vbGVhbiAtIHRydWUgaWYgY2hpbGQgaXMgc3VibmV0IG9mIHBhcmVudFxuICpcbiAqIEBleGFtcGxlXG4gKiBjb25zdCBpc1N1Ym5ldCA9IGlzU3VibmV0KFwiMTAuMS4wLjAvMTZcIiwgXCIxMC4wLjAuMC84XCIpO1xuICogY29uc29sZS5sb2coaXNTdWJuZXQpOyAvLyB0cnVlXG4gKi9cbmV4cG9ydCBmdW5jdGlvbiBpc1N1Ym5ldChjaGlsZENpZHI6IHN0cmluZywgcGFyZW50Q2lkcjogc3RyaW5nKTogYm9vbGVhbiB7XG4gIHJldHVybiBpc0NvbnRhaW5lZChwYXJlbnRDaWRyLCBjaGlsZENpZHIpO1xufVxuXG4vKipcbiAqIENhbGN1bGF0ZXMgbnVtYmVyIG9mIElQIGFkZHJlc3NlcyBpbiBhIENJRFIgYmxvY2tcbiAqXG4gKiBAcGFyYW0gY2lkciAtIENJRFIgc3RyaW5nXG4gKiBAcmV0dXJucyBudW1iZXIgLSBUb3RhbCBJUCBhZGRyZXNzZXMgaW4gdGhlIGJsb2NrXG4gKlxuICogQGV4YW1wbGVcbiAqIGNvbnN0IGNvdW50ID0gY2FsY3VsYXRlQWRkcmVzc0NvdW50KFwiMTAuMC4wLjAvMjRcIik7XG4gKiBjb25zb2xlLmxvZyhjb3VudCk7IC8vIDI1NlxuICovXG5leHBvcnQgZnVuY3Rpb24gY2FsY3VsYXRlQWRkcmVzc0NvdW50KGNpZHI6IHN0cmluZyk6IG51bWJlciB7XG4gIGNvbnN0IHBhcnNlZCA9IHBhcnNlQ2lkcihjaWRyKTtcbiAgcmV0dXJuIHBhcnNlZC50b3RhbEFkZHJlc3Nlcztcbn1cblxuLyoqXG4gKiBWYWxpZGF0ZXMgcHJlZml4IGxlbmd0aCBpcyB3aXRoaW4gYWxsb3dlZCByYW5nZVxuICpcbiAqIEBwYXJhbSBjaWRyIC0gQ0lEUiBzdHJpbmdcbiAqIEBwYXJhbSBtaW5QcmVmaXggLSBNaW5pbXVtIGFsbG93ZWQgcHJlZml4IChlLmcuLCA4KVxuICogQHBhcmFtIG1heFByZWZpeCAtIE1heGltdW0gYWxsb3dlZCBwcmVmaXggKGUuZy4sIDI5KVxuICogQHJldHVybnMgYm9vbGVhbiAtIHRydWUgaWYgcHJlZml4IGxlbmd0aCBpcyB3aXRoaW4gcmFuZ2VcbiAqXG4gKiBAZXhhbXBsZVxuICogY29uc3QgdmFsaWQgPSBpc1ZhbGlkUHJlZml4TGVuZ3RoKFwiMTAuMC4wLjAvMjRcIiwgOCwgMjkpO1xuICogY29uc29sZS5sb2codmFsaWQpOyAvLyB0cnVlXG4gKi9cbmV4cG9ydCBmdW5jdGlvbiBpc1ZhbGlkUHJlZml4TGVuZ3RoKFxuICBjaWRyOiBzdHJpbmcsXG4gIG1pblByZWZpeDogbnVtYmVyLFxuICBtYXhQcmVmaXg6IG51bWJlcixcbik6IGJvb2xlYW4ge1xuICBpZiAoIWlzVmFsaWRDaWRyKGNpZHIpKSB7XG4gICAgcmV0dXJuIGZhbHNlO1xuICB9XG5cbiAgY29uc3QgcGFyc2VkID0gcGFyc2VDaWRyKGNpZHIpO1xuICByZXR1cm4gcGFyc2VkLnByZWZpeCA+PSBtaW5QcmVmaXggJiYgcGFyc2VkLnByZWZpeCA8PSBtYXhQcmVmaXg7XG59XG5cbi8qKlxuICogVmFsaWRhdGUgQ0lEUiBmb3JtYXQgYW5kIHN0cnVjdHVyZVxuICpcbiAqIEBwYXJhbSBjaWRyIC0gQ0lEUiBub3RhdGlvbiBzdHJpbmcgKGUuZy4sIFwiMTAuMC4wLjAvOFwiKVxuICogQHJldHVybnMgVmFsaWRhdGlvbiByZXN1bHQgd2l0aCBlcnJvcnMgYW5kIHdhcm5pbmdzXG4gKlxuICogQGV4YW1wbGVcbiAqIGNvbnN0IHJlc3VsdCA9IHZhbGlkYXRlQ2lkcihcIjEwLjAuMC4wLzhcIik7XG4gKiBpZiAoIXJlc3VsdC52YWxpZCkge1xuICogICBjb25zb2xlLmVycm9yKFwiSW52YWxpZCBDSURSOlwiLCByZXN1bHQuZXJyb3JzKTtcbiAqIH1cbiAqL1xuZXhwb3J0IGZ1bmN0aW9uIHZhbGlkYXRlQ2lkcihjaWRyOiBzdHJpbmcpOiBDaWRyVmFsaWRhdGlvblJlc3VsdCB7XG4gIGNvbnN0IGVycm9yczogc3RyaW5nW10gPSBbXTtcbiAgY29uc3Qgd2FybmluZ3M6IHN0cmluZ1tdID0gW107XG5cbiAgaWYgKCFjaWRyIHx8IHR5cGVvZiBjaWRyICE9PSBcInN0cmluZ1wiKSB7XG4gICAgZXJyb3JzLnB1c2goXCJDSURSIG11c3QgYmUgYSBub24tZW1wdHkgc3RyaW5nXCIpO1xuICAgIHJldHVybiB7IHZhbGlkOiBmYWxzZSwgZXJyb3JzLCB3YXJuaW5ncyB9O1xuICB9XG5cbiAgY29uc3QgbWF0Y2ggPSBjaWRyLm1hdGNoKElQVjRfUkVHRVgpO1xuICBpZiAoIW1hdGNoKSB7XG4gICAgZXJyb3JzLnB1c2goYEludmFsaWQgQ0lEUiBmb3JtYXQ6ICR7Y2lkcn0uIEV4cGVjdGVkIGZvcm1hdDogeC54LngueC95YCk7XG4gICAgcmV0dXJuIHsgdmFsaWQ6IGZhbHNlLCBlcnJvcnMsIHdhcm5pbmdzIH07XG4gIH1cblxuICBjb25zdCBbLCBvY3QxLCBvY3QyLCBvY3QzLCBvY3Q0LCBwcmVmaXhdID0gbWF0Y2g7XG4gIGNvbnN0IG9jdGV0cyA9IFtvY3QxLCBvY3QyLCBvY3QzLCBvY3Q0XS5tYXAoTnVtYmVyKTtcbiAgY29uc3QgcHJlZml4TGVuZ3RoID0gTnVtYmVyKHByZWZpeCk7XG5cbiAgLy8gVmFsaWRhdGUgb2N0ZXRzICgwLTI1NSlcbiAgZm9yIChsZXQgaSA9IDA7IGkgPCBvY3RldHMubGVuZ3RoOyBpKyspIHtcbiAgICBpZiAob2N0ZXRzW2ldIDwgMCB8fCBvY3RldHNbaV0gPiAyNTUpIHtcbiAgICAgIGVycm9ycy5wdXNoKFxuICAgICAgICBgSW52YWxpZCBvY3RldCB2YWx1ZTogJHtvY3RldHNbaV19LiBNdXN0IGJlIGJldHdlZW4gMCBhbmQgMjU1YCxcbiAgICAgICk7XG4gICAgfVxuICB9XG5cbiAgLy8gVmFsaWRhdGUgcHJlZml4IGxlbmd0aCAoMC0zMilcbiAgaWYgKHByZWZpeExlbmd0aCA8IDAgfHwgcHJlZml4TGVuZ3RoID4gMzIpIHtcbiAgICBlcnJvcnMucHVzaChcbiAgICAgIGBJbnZhbGlkIHByZWZpeCBsZW5ndGg6ICR7cHJlZml4TGVuZ3RofS4gTXVzdCBiZSBiZXR3ZWVuIDAgYW5kIDMyYCxcbiAgICApO1xuICB9XG5cbiAgaWYgKGVycm9ycy5sZW5ndGggPiAwKSB7XG4gICAgcmV0dXJuIHsgdmFsaWQ6IGZhbHNlLCBlcnJvcnMsIHdhcm5pbmdzIH07XG4gIH1cblxuICAvLyBWZXJpZnkgbmV0d29yayBhZGRyZXNzIGFsaWdubWVudFxuICBjb25zdCBpcE51bSA9IGlwVG9OdW1iZXIob2N0ZXRzLmpvaW4oXCIuXCIpKTtcbiAgY29uc3QgbWFzayA9IHByZWZpeFRvTWFzayhwcmVmaXhMZW5ndGgpO1xuICBjb25zdCBuZXR3b3JrTnVtID0gaXBOdW0gJiBtYXNrO1xuXG4gIGlmIChpcE51bSAhPT0gbmV0d29ya051bSkge1xuICAgIGNvbnN0IGNvcnJlY3ROZXR3b3JrID0gbnVtYmVyVG9JcChuZXR3b3JrTnVtKTtcbiAgICB3YXJuaW5ncy5wdXNoKFxuICAgICAgYElQIGFkZHJlc3MgJHtvY3RldHMuam9pbihcIi5cIil9IGlzIG5vdCBhbGlnbmVkIHRvIG5ldHdvcmsgYm91bmRhcnkuIGAgK1xuICAgICAgICBgTmV0d29yayBhZGRyZXNzIHNob3VsZCBiZSAke2NvcnJlY3ROZXR3b3JrfS8ke3ByZWZpeExlbmd0aH1gLFxuICAgICk7XG4gIH1cblxuICByZXR1cm4geyB2YWxpZDogZXJyb3JzLmxlbmd0aCA9PT0gMCwgZXJyb3JzLCB3YXJuaW5ncyB9O1xufVxuXG4vKipcbiAqIFBhcnNlIENJRFIgaW50byBzdHJ1Y3R1cmVkIGluZm9ybWF0aW9uXG4gKlxuICogQHBhcmFtIGNpZHIgLSBDSURSIG5vdGF0aW9uIHN0cmluZ1xuICogQHJldHVybnMgUGFyc2VkIENJRFIgaW5mb3JtYXRpb25cbiAqIEB0aHJvd3MgRXJyb3IgaWYgQ0lEUiBmb3JtYXQgaXMgaW52YWxpZFxuICpcbiAqIEBleGFtcGxlXG4gKiBjb25zdCBwYXJzZWQgPSBwYXJzZUNpZHIoXCIxMC4wLjAuMC84XCIpO1xuICogY29uc29sZS5sb2coYE5ldHdvcms6ICR7cGFyc2VkLm5ldHdvcmt9LCBQcmVmaXg6ICR7cGFyc2VkLnByZWZpeH1gKTtcbiAqIGNvbnNvbGUubG9nKGBSYW5nZTogJHtwYXJzZWQuZmlyc3RJcH0gLSAke3BhcnNlZC5sYXN0SXB9YCk7XG4gKiBjb25zb2xlLmxvZyhgVG90YWwgYWRkcmVzc2VzOiAke3BhcnNlZC50b3RhbEFkZHJlc3Nlc31gKTtcbiAqL1xuZXhwb3J0IGZ1bmN0aW9uIHBhcnNlQ2lkcihjaWRyOiBzdHJpbmcpOiBQYXJzZWRDaWRyIHtcbiAgY29uc3QgdmFsaWRhdGlvbiA9IHZhbGlkYXRlQ2lkcihjaWRyKTtcbiAgaWYgKCF2YWxpZGF0aW9uLnZhbGlkKSB7XG4gICAgdGhyb3cgbmV3IEVycm9yKGBJbnZhbGlkIENJRFI6ICR7dmFsaWRhdGlvbi5lcnJvcnMuam9pbihcIiwgXCIpfWApO1xuICB9XG5cbiAgY29uc3QgbWF0Y2ggPSBjaWRyLm1hdGNoKElQVjRfUkVHRVgpITtcbiAgY29uc3QgWywgb2N0MSwgb2N0Miwgb2N0Mywgb2N0NCwgcHJlZml4XSA9IG1hdGNoO1xuICBjb25zdCBpcEFkZHJlc3MgPSBgJHtvY3QxfS4ke29jdDJ9LiR7b2N0M30uJHtvY3Q0fWA7XG4gIGNvbnN0IHByZWZpeExlbmd0aCA9IE51bWJlcihwcmVmaXgpO1xuXG4gIGNvbnN0IGlwTnVtID0gaXBUb051bWJlcihpcEFkZHJlc3MpO1xuICBjb25zdCBtYXNrID0gcHJlZml4VG9NYXNrKHByZWZpeExlbmd0aCk7XG4gIGNvbnN0IG5ldHdvcmtOdW0gPSBpcE51bSAmIG1hc2s7XG4gIGNvbnN0IGJyb2FkY2FzdE51bSA9IG5ldHdvcmtOdW0gfCAofm1hc2sgPj4+IDApO1xuXG4gIGNvbnN0IHRvdGFsQWRkcmVzc2VzID0gTWF0aC5wb3coMiwgMzIgLSBwcmVmaXhMZW5ndGgpO1xuICBjb25zdCBmaXJzdElwTnVtID0gbmV0d29ya051bTtcbiAgY29uc3QgbGFzdElwTnVtID0gYnJvYWRjYXN0TnVtO1xuXG4gIHJldHVybiB7XG4gICAgY2lkcixcbiAgICBuZXR3b3JrOiBudW1iZXJUb0lwKG5ldHdvcmtOdW0pLFxuICAgIHByZWZpeDogcHJlZml4TGVuZ3RoLFxuICAgIGZpcnN0SXA6IG51bWJlclRvSXAoZmlyc3RJcE51bSksXG4gICAgbGFzdElwOiBudW1iZXJUb0lwKGxhc3RJcE51bSksXG4gICAgdG90YWxBZGRyZXNzZXMsXG4gICAgbmV0bWFzazogbnVtYmVyVG9JcChtYXNrKSxcbiAgfTtcbn1cblxuLyoqXG4gKiBDaGVjayBpZiB0d28gQ0lEUnMgb3ZlcmxhcFxuICpcbiAqIEBwYXJhbSBjaWRyMSAtIEZpcnN0IENJRFIgYmxvY2tcbiAqIEBwYXJhbSBjaWRyMiAtIFNlY29uZCBDSURSIGJsb2NrXG4gKiBAcmV0dXJucyBUcnVlIGlmIHRoZSBDSURScyBvdmVybGFwXG4gKlxuICogQGV4YW1wbGVcbiAqIGNvbnN0IG92ZXJsYXBzID0gY2hlY2tPdmVybGFwKFwiMTAuMC4wLjAvMTZcIiwgXCIxMC4wLjEuMC8yNFwiKTtcbiAqIGNvbnNvbGUubG9nKG92ZXJsYXBzKTsgLy8gdHJ1ZVxuICovXG5leHBvcnQgZnVuY3Rpb24gY2hlY2tPdmVybGFwKGNpZHIxOiBzdHJpbmcsIGNpZHIyOiBzdHJpbmcpOiBib29sZWFuIHtcbiAgY29uc3QgcGFyc2VkMSA9IHBhcnNlQ2lkcihjaWRyMSk7XG4gIGNvbnN0IHBhcnNlZDIgPSBwYXJzZUNpZHIoY2lkcjIpO1xuXG4gIGNvbnN0IHN0YXJ0MSA9IGlwVG9OdW1iZXIocGFyc2VkMS5maXJzdElwKTtcbiAgY29uc3QgZW5kMSA9IGlwVG9OdW1iZXIocGFyc2VkMS5sYXN0SXApO1xuICBjb25zdCBzdGFydDIgPSBpcFRvTnVtYmVyKHBhcnNlZDIuZmlyc3RJcCk7XG4gIGNvbnN0IGVuZDIgPSBpcFRvTnVtYmVyKHBhcnNlZDIubGFzdElwKTtcblxuICAvLyBDaGVjayBpZiByYW5nZXMgb3ZlcmxhcFxuICByZXR1cm4gIShlbmQxIDwgc3RhcnQyIHx8IGVuZDIgPCBzdGFydDEpO1xufVxuXG4vKipcbiAqIFZhbGlkYXRlIHRoYXQgbXVsdGlwbGUgQ0lEUnMgZG9uJ3Qgb3ZlcmxhcFxuICpcbiAqIEBwYXJhbSBjaWRycyAtIEFycmF5IG9mIENJRFIgYmxvY2tzIHRvIGNoZWNrXG4gKiBAcmV0dXJucyBWYWxpZGF0aW9uIHJlc3VsdCB3aXRoIGRldGFpbHMgb2YgYW55IG92ZXJsYXBzXG4gKlxuICogQGV4YW1wbGVcbiAqIGNvbnN0IHJlc3VsdCA9IHZhbGlkYXRlTm9PdmVybGFwcyhbXG4gKiAgIFwiMTAuMC4wLjAvMTZcIixcbiAqICAgXCIxMC4xLjAuMC8xNlwiLFxuICogICBcIjEwLjAuMS4wLzI0XCJcbiAqIF0pO1xuICogaWYgKCFyZXN1bHQudmFsaWQpIHtcbiAqICAgY29uc29sZS5lcnJvcihcIk92ZXJsYXBwaW5nIENJRFJzOlwiLCByZXN1bHQuZXJyb3JzKTtcbiAqIH1cbiAqL1xuZXhwb3J0IGZ1bmN0aW9uIHZhbGlkYXRlTm9PdmVybGFwcyhjaWRyczogc3RyaW5nW10pOiBDaWRyVmFsaWRhdGlvblJlc3VsdCB7XG4gIGNvbnN0IGVycm9yczogc3RyaW5nW10gPSBbXTtcbiAgY29uc3Qgd2FybmluZ3M6IHN0cmluZ1tdID0gW107XG5cbiAgLy8gRmlyc3QgdmFsaWRhdGUgZWFjaCBDSURSIGluZGl2aWR1YWxseVxuICBmb3IgKGNvbnN0IGNpZHIgb2YgY2lkcnMpIHtcbiAgICBjb25zdCB2YWxpZGF0aW9uID0gdmFsaWRhdGVDaWRyKGNpZHIpO1xuICAgIGlmICghdmFsaWRhdGlvbi52YWxpZCkge1xuICAgICAgZXJyb3JzLnB1c2goYEludmFsaWQgQ0lEUiAke2NpZHJ9OiAke3ZhbGlkYXRpb24uZXJyb3JzLmpvaW4oXCIsIFwiKX1gKTtcbiAgICB9XG4gIH1cblxuICBpZiAoZXJyb3JzLmxlbmd0aCA+IDApIHtcbiAgICByZXR1cm4geyB2YWxpZDogZmFsc2UsIGVycm9ycywgd2FybmluZ3MgfTtcbiAgfVxuXG4gIC8vIENoZWNrIGZvciBvdmVybGFwc1xuICBmb3IgKGxldCBpID0gMDsgaSA8IGNpZHJzLmxlbmd0aDsgaSsrKSB7XG4gICAgZm9yIChsZXQgaiA9IGkgKyAxOyBqIDwgY2lkcnMubGVuZ3RoOyBqKyspIHtcbiAgICAgIGlmIChjaGVja092ZXJsYXAoY2lkcnNbaV0sIGNpZHJzW2pdKSkge1xuICAgICAgICBlcnJvcnMucHVzaChgQ0lEUnMgb3ZlcmxhcDogJHtjaWRyc1tpXX0gYW5kICR7Y2lkcnNbal19YCk7XG4gICAgICB9XG4gICAgfVxuICB9XG5cbiAgcmV0dXJuIHsgdmFsaWQ6IGVycm9ycy5sZW5ndGggPT09IDAsIGVycm9ycywgd2FybmluZ3MgfTtcbn1cblxuLyoqXG4gKiBDaGVjayBpZiBhIGNoaWxkIENJRFIgaXMgY29udGFpbmVkIHdpdGhpbiBhIHBhcmVudCBDSURSXG4gKlxuICogQHBhcmFtIHBhcmVudENpZHIgLSBQYXJlbnQgQ0lEUiBibG9ja1xuICogQHBhcmFtIGNoaWxkQ2lkciAtIENoaWxkIENJRFIgYmxvY2sgdG8gY2hlY2tcbiAqIEByZXR1cm5zIFRydWUgaWYgY2hpbGQgaXMgZnVsbHkgY29udGFpbmVkIGluIHBhcmVudFxuICpcbiAqIEBleGFtcGxlXG4gKiBjb25zdCBjb250YWluZWQgPSBpc0NvbnRhaW5lZChcIjEwLjAuMC4wLzE2XCIsIFwiMTAuMC4xLjAvMjRcIik7XG4gKiBjb25zb2xlLmxvZyhjb250YWluZWQpOyAvLyB0cnVlXG4gKi9cbmV4cG9ydCBmdW5jdGlvbiBpc0NvbnRhaW5lZChwYXJlbnRDaWRyOiBzdHJpbmcsIGNoaWxkQ2lkcjogc3RyaW5nKTogYm9vbGVhbiB7XG4gIGNvbnN0IHBhcmVudCA9IHBhcnNlQ2lkcihwYXJlbnRDaWRyKTtcbiAgY29uc3QgY2hpbGQgPSBwYXJzZUNpZHIoY2hpbGRDaWRyKTtcblxuICBjb25zdCBwYXJlbnRTdGFydCA9IGlwVG9OdW1iZXIocGFyZW50LmZpcnN0SXApO1xuICBjb25zdCBwYXJlbnRFbmQgPSBpcFRvTnVtYmVyKHBhcmVudC5sYXN0SXApO1xuICBjb25zdCBjaGlsZFN0YXJ0ID0gaXBUb051bWJlcihjaGlsZC5maXJzdElwKTtcbiAgY29uc3QgY2hpbGRFbmQgPSBpcFRvTnVtYmVyKGNoaWxkLmxhc3RJcCk7XG5cbiAgLy8gQ2hpbGQgbXVzdCBiZSBmdWxseSB3aXRoaW4gcGFyZW50IHJhbmdlXG4gIHJldHVybiBjaGlsZFN0YXJ0ID49IHBhcmVudFN0YXJ0ICYmIGNoaWxkRW5kIDw9IHBhcmVudEVuZDtcbn1cblxuLyoqXG4gKiBWYWxpZGF0ZSB0aGF0IG11bHRpcGxlIGNoaWxkIENJRFJzIGFyZSBhbGwgY29udGFpbmVkIHdpdGhpbiBhIHBhcmVudCBDSURSXG4gKlxuICogQHBhcmFtIHBhcmVudENpZHIgLSBQYXJlbnQgQ0lEUiBibG9ja1xuICogQHBhcmFtIGNoaWxkQ2lkcnMgLSBBcnJheSBvZiBjaGlsZCBDSURSIGJsb2Nrc1xuICogQHJldHVybnMgVmFsaWRhdGlvbiByZXN1bHQgd2l0aCBkZXRhaWxzIG9mIGFueSBjb250YWlubWVudCB2aW9sYXRpb25zXG4gKlxuICogQGV4YW1wbGVcbiAqIGNvbnN0IHJlc3VsdCA9IHZhbGlkYXRlQ29udGFpbm1lbnQoXCIxMC4wLjAuMC8xNlwiLCBbXG4gKiAgIFwiMTAuMC4xLjAvMjRcIixcbiAqICAgXCIxMC4wLjIuMC8yNFwiXG4gKiBdKTtcbiAqIGlmICghcmVzdWx0LnZhbGlkKSB7XG4gKiAgIGNvbnNvbGUuZXJyb3IoXCJDb250YWlubWVudCB2aW9sYXRpb25zOlwiLCByZXN1bHQuZXJyb3JzKTtcbiAqIH1cbiAqL1xuZXhwb3J0IGZ1bmN0aW9uIHZhbGlkYXRlQ29udGFpbm1lbnQoXG4gIHBhcmVudENpZHI6IHN0cmluZyxcbiAgY2hpbGRDaWRyczogc3RyaW5nW10sXG4pOiBDaWRyVmFsaWRhdGlvblJlc3VsdCB7XG4gIGNvbnN0IGVycm9yczogc3RyaW5nW10gPSBbXTtcbiAgY29uc3Qgd2FybmluZ3M6IHN0cmluZ1tdID0gW107XG5cbiAgLy8gVmFsaWRhdGUgcGFyZW50IENJRFJcbiAgY29uc3QgcGFyZW50VmFsaWRhdGlvbiA9IHZhbGlkYXRlQ2lkcihwYXJlbnRDaWRyKTtcbiAgaWYgKCFwYXJlbnRWYWxpZGF0aW9uLnZhbGlkKSB7XG4gICAgZXJyb3JzLnB1c2goXG4gICAgICBgSW52YWxpZCBwYXJlbnQgQ0lEUiAke3BhcmVudENpZHJ9OiAke3BhcmVudFZhbGlkYXRpb24uZXJyb3JzLmpvaW4oXCIsIFwiKX1gLFxuICAgICk7XG4gICAgcmV0dXJuIHsgdmFsaWQ6IGZhbHNlLCBlcnJvcnMsIHdhcm5pbmdzIH07XG4gIH1cblxuICAvLyBWYWxpZGF0ZSBlYWNoIGNoaWxkIENJRFJcbiAgZm9yIChjb25zdCBjaGlsZENpZHIgb2YgY2hpbGRDaWRycykge1xuICAgIGNvbnN0IGNoaWxkVmFsaWRhdGlvbiA9IHZhbGlkYXRlQ2lkcihjaGlsZENpZHIpO1xuICAgIGlmICghY2hpbGRWYWxpZGF0aW9uLnZhbGlkKSB7XG4gICAgICBlcnJvcnMucHVzaChcbiAgICAgICAgYEludmFsaWQgY2hpbGQgQ0lEUiAke2NoaWxkQ2lkcn06ICR7Y2hpbGRWYWxpZGF0aW9uLmVycm9ycy5qb2luKFwiLCBcIil9YCxcbiAgICAgICk7XG4gICAgICBjb250aW51ZTtcbiAgICB9XG5cbiAgICAvLyBDaGVjayBjb250YWlubWVudFxuICAgIGlmICghaXNDb250YWluZWQocGFyZW50Q2lkciwgY2hpbGRDaWRyKSkge1xuICAgICAgZXJyb3JzLnB1c2goXG4gICAgICAgIGBDaGlsZCBDSURSICR7Y2hpbGRDaWRyfSBpcyBub3QgY29udGFpbmVkIHdpdGhpbiBwYXJlbnQgQ0lEUiAke3BhcmVudENpZHJ9YCxcbiAgICAgICk7XG4gICAgfVxuICB9XG5cbiAgcmV0dXJuIHsgdmFsaWQ6IGVycm9ycy5sZW5ndGggPT09IDAsIGVycm9ycywgd2FybmluZ3MgfTtcbn1cblxuLyoqXG4gKiBDb252ZXJ0IGFuIElQIGFkZHJlc3Mgc3RyaW5nIHRvIGEgMzItYml0IG51bWJlclxuICpcbiAqIEBwYXJhbSBpcCAtIElQIGFkZHJlc3Mgc3RyaW5nIChlLmcuLCBcIjEwLjAuMC4xXCIpXG4gKiBAcmV0dXJucyAzMi1iaXQgbnVtYmVyIHJlcHJlc2VudGF0aW9uXG4gKlxuICogQGV4YW1wbGVcbiAqIGNvbnN0IG51bSA9IGlwVG9OdW1iZXIoXCIxMC4wLjAuMVwiKTtcbiAqIGNvbnNvbGUubG9nKG51bSk7IC8vIDE2Nzc3MjE2MVxuICovXG5leHBvcnQgZnVuY3Rpb24gaXBUb051bWJlcihpcDogc3RyaW5nKTogbnVtYmVyIHtcbiAgY29uc3Qgb2N0ZXRzID0gaXAuc3BsaXQoXCIuXCIpLm1hcChOdW1iZXIpO1xuICByZXR1cm4gKFxuICAgICgob2N0ZXRzWzBdIDw8IDI0KSA+Pj4gMCkgK1xuICAgICgob2N0ZXRzWzFdIDw8IDE2KSA+Pj4gMCkgK1xuICAgICgob2N0ZXRzWzJdIDw8IDgpID4+PiAwKSArXG4gICAgb2N0ZXRzWzNdXG4gICk7XG59XG5cbi8qKlxuICogQ29udmVydCBhIDMyLWJpdCBudW1iZXIgdG8gYW4gSVAgYWRkcmVzcyBzdHJpbmdcbiAqXG4gKiBAcGFyYW0gbnVtIC0gMzItYml0IG51bWJlciByZXByZXNlbnRhdGlvblxuICogQHJldHVybnMgSVAgYWRkcmVzcyBzdHJpbmdcbiAqXG4gKiBAZXhhbXBsZVxuICogY29uc3QgaXAgPSBudW1iZXJUb0lwKDE2Nzc3MjE2MSk7XG4gKiBjb25zb2xlLmxvZyhpcCk7IC8vIFwiMTAuMC4wLjFcIlxuICovXG5leHBvcnQgZnVuY3Rpb24gbnVtYmVyVG9JcChudW06IG51bWJlcik6IHN0cmluZyB7XG4gIHJldHVybiBbXG4gICAgKG51bSA+Pj4gMjQpICYgMHhmZixcbiAgICAobnVtID4+PiAxNikgJiAweGZmLFxuICAgIChudW0gPj4+IDgpICYgMHhmZixcbiAgICBudW0gJiAweGZmLFxuICBdLmpvaW4oXCIuXCIpO1xufVxuXG4vKipcbiAqIENvbnZlcnQgYSBwcmVmaXggbGVuZ3RoIHRvIGEgbmV0bWFzayBudW1iZXJcbiAqXG4gKiBAcGFyYW0gcHJlZml4IC0gUHJlZml4IGxlbmd0aCAoMC0zMilcbiAqIEByZXR1cm5zIDMyLWJpdCBuZXRtYXNrIG51bWJlclxuICpcbiAqIEBleGFtcGxlXG4gKiBjb25zdCBtYXNrID0gcHJlZml4VG9NYXNrKDI0KTtcbiAqIGNvbnN0IG1hc2tJcCA9IG51bWJlclRvSXAobWFzayk7XG4gKiBjb25zb2xlLmxvZyhtYXNrSXApOyAvLyBcIjI1NS4yNTUuMjU1LjBcIlxuICovXG5leHBvcnQgZnVuY3Rpb24gcHJlZml4VG9NYXNrKHByZWZpeDogbnVtYmVyKTogbnVtYmVyIHtcbiAgcmV0dXJuIHByZWZpeCA9PT0gMCA/IDAgOiAoMHhmZmZmZmZmZiA8PCAoMzIgLSBwcmVmaXgpKSA+Pj4gMDtcbn1cbiJdfQ==