@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.
- package/.jsii +10617 -7822
- package/API.md +25592 -20586
- package/lib/azure-actiongroup/lib/action-group.js +1 -1
- package/lib/azure-activitylogalert/lib/activity-log-alert.js +1 -1
- package/lib/azure-aks/lib/aks-cluster.js +1 -1
- package/lib/azure-diagnosticsettings/lib/diagnostic-settings.js +1 -1
- package/lib/azure-dnsforwardingruleset/lib/dns-forwarding-ruleset.js +1 -1
- package/lib/azure-dnsforwardingruleset/lib/forwarding-rule.js +1 -1
- package/lib/azure-dnsforwardingruleset/lib/virtual-network-link.js +1 -1
- package/lib/azure-dnsresolver/lib/dns-resolver.js +1 -1
- package/lib/azure-dnsresolver/lib/inbound-endpoint.js +1 -1
- package/lib/azure-dnsresolver/lib/outbound-endpoint.js +1 -1
- package/lib/azure-dnszone/lib/dns-zone.js +1 -1
- package/lib/azure-metricalert/lib/metric-alert.js +1 -1
- package/lib/azure-networkinterface/lib/network-interface.js +1 -1
- package/lib/azure-networksecuritygroup/lib/network-security-group.js +1 -1
- package/lib/azure-policyassignment/lib/policy-assignment.js +1 -1
- package/lib/azure-policydefinition/lib/policy-definition.js +1 -1
- package/lib/azure-privatednszone/lib/private-dns-zone.js +1 -1
- package/lib/azure-privatednszonelink/lib/private-dns-zone-link.js +1 -1
- package/lib/azure-publicipaddress/lib/public-ip-address.js +1 -1
- package/lib/azure-resourcegroup/lib/resource-group.js +1 -1
- package/lib/azure-roleassignment/lib/role-assignment.js +1 -1
- package/lib/azure-roledefinition/lib/role-definition.js +1 -1
- package/lib/azure-storageaccount/lib/storage-account.js +1 -1
- package/lib/azure-subnet/lib/subnet.js +1 -1
- package/lib/azure-virtualmachine/lib/virtual-machine.js +1 -1
- package/lib/azure-virtualnetwork/lib/virtual-network.js +1 -1
- package/lib/azure-virtualnetworkmanager/lib/connectivity-configuration.js +1 -1
- package/lib/azure-virtualnetworkmanager/lib/index.d.ts +5 -0
- package/lib/azure-virtualnetworkmanager/lib/index.js +6 -1
- package/lib/azure-virtualnetworkmanager/lib/ipam-pool-schemas.d.ts +24 -0
- package/lib/azure-virtualnetworkmanager/lib/ipam-pool-schemas.js +169 -0
- package/lib/azure-virtualnetworkmanager/lib/ipam-pool-static-cidr-schemas.d.ts +32 -0
- package/lib/azure-virtualnetworkmanager/lib/ipam-pool-static-cidr-schemas.js +206 -0
- package/lib/azure-virtualnetworkmanager/lib/ipam-pool-static-cidr.d.ts +170 -0
- package/lib/azure-virtualnetworkmanager/lib/ipam-pool-static-cidr.js +214 -0
- package/lib/azure-virtualnetworkmanager/lib/ipam-pool.d.ts +175 -0
- package/lib/azure-virtualnetworkmanager/lib/ipam-pool.js +206 -0
- package/lib/azure-virtualnetworkmanager/lib/network-group-static-member.js +1 -1
- package/lib/azure-virtualnetworkmanager/lib/network-group.js +1 -1
- package/lib/azure-virtualnetworkmanager/lib/security-admin-configuration.js +1 -1
- package/lib/azure-virtualnetworkmanager/lib/security-admin-rule-collection.js +1 -1
- package/lib/azure-virtualnetworkmanager/lib/security-admin-rule.js +1 -1
- package/lib/azure-virtualnetworkmanager/lib/utils/cidr-validator.d.ts +225 -0
- package/lib/azure-virtualnetworkmanager/lib/utils/cidr-validator.js +389 -0
- package/lib/azure-virtualnetworkmanager/lib/virtual-network-manager.d.ts +56 -0
- package/lib/azure-virtualnetworkmanager/lib/virtual-network-manager.js +29 -2
- package/lib/azure-virtualnetworkmanager/test/cidr-validator.spec.d.ts +6 -0
- package/lib/azure-virtualnetworkmanager/test/cidr-validator.spec.js +292 -0
- package/lib/azure-virtualnetworkmanager/test/ipam-pool-static-cidr.spec.d.ts +6 -0
- package/lib/azure-virtualnetworkmanager/test/ipam-pool-static-cidr.spec.js +430 -0
- package/lib/azure-virtualnetworkmanager/test/ipam-pool.spec.d.ts +6 -0
- package/lib/azure-virtualnetworkmanager/test/ipam-pool.spec.js +372 -0
- package/lib/azure-virtualnetworkmanager/test/virtual-network-manager.integ.d.ts +2 -1
- package/lib/azure-virtualnetworkmanager/test/virtual-network-manager.integ.js +30 -3
- package/lib/azure-virtualnetworkmanager/test/virtual-network-manager.spec.js +105 -1
- package/lib/azure-vmss/lib/virtual-machine-scale-set.js +1 -1
- package/lib/core-azure/lib/azapi/azapi-resource.js +2 -2
- package/lib/core-azure/lib/azapi/providers-azapi/data-azapi-client-config/index.js +2 -2
- package/lib/core-azure/lib/azapi/providers-azapi/data-azapi-resource/index.js +5 -5
- package/lib/core-azure/lib/azapi/providers-azapi/provider/index.js +1 -1
- package/lib/core-azure/lib/azapi/providers-azapi/resource/index.js +5 -5
- package/lib/core-azure/lib/azapi/providers-azapi/resource-action/index.js +3 -3
- package/lib/core-azure/lib/azapi/providers-azapi/update-resource/index.js +3 -3
- package/lib/core-azure/lib/azapi/schema-mapper/schema-mapper.js +1 -1
- package/lib/core-azure/lib/version-manager/api-version-manager.js +1 -1
- package/lib/core-azure/lib/version-manager/interfaces/version-interfaces.js +7 -7
- package/lib/testing/index.js +2 -2
- package/lib/testing/lib/cleanup.js +1 -1
- package/lib/testing/lib/metadata.js +1 -1
- 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==
|