@serve.zone/interfaces 31.3.0 → 31.5.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/changelog.md +72 -0
- package/dist_ts/00_commitinfo_data.js +1 -1
- package/dist_ts/data/cluster.d.ts +44 -9
- package/dist_ts/data/cluster.js +35 -12
- package/dist_ts/data/clusteringress.d.ts +137 -0
- package/dist_ts/data/clusteringress.js +302 -0
- package/dist_ts/data/coremail.runtime.js +4 -8
- package/dist_ts/data/index.d.ts +1 -0
- package/dist_ts/data/index.js +2 -1
- package/dist_ts/data/networkprotection.d.ts +18 -0
- package/dist_ts/data/networkprotection.js +24 -3
- package/dist_ts/private/networkcontract.d.ts +1 -1
- package/dist_ts/private/networkcontract.js +7 -3
- package/dist_ts/private/utccalendarday.d.ts +17 -0
- package/dist_ts/private/utccalendarday.js +27 -0
- package/dist_ts/requests/cluster.d.ts +17 -0
- package/dist_ts/requests/clusteringress.d.ts +120 -0
- package/dist_ts/requests/clusteringress.js +3 -0
- package/dist_ts/requests/index.d.ts +2 -1
- package/dist_ts/requests/index.js +3 -2
- package/package.json +1 -1
- package/readme.md +106 -6
- package/ts/00_commitinfo_data.ts +1 -1
- package/ts/data/cluster.ts +76 -17
- package/ts/data/clusteringress.ts +425 -0
- package/ts/data/coremail.runtime.ts +3 -10
- package/ts/data/index.ts +1 -0
- package/ts/data/networkprotection.ts +34 -2
- package/ts/private/networkcontract.ts +8 -2
- package/ts/private/utccalendarday.ts +30 -0
- package/ts/requests/cluster.ts +21 -0
- package/ts/requests/clusteringress.ts +140 -0
- package/ts/requests/index.ts +2 -0
|
@@ -0,0 +1,302 @@
|
|
|
1
|
+
import { isPlausibleUnicastIpv4 } from '../private/ipv4.js';
|
|
2
|
+
import { isUtcCalendarDay } from '../private/utccalendarday.js';
|
|
3
|
+
/**
|
|
4
|
+
* The cluster ingress contracts.
|
|
5
|
+
*
|
|
6
|
+
* A cluster's ingress terminates TLS for the hostnames its services publish and
|
|
7
|
+
* forwards to the host ports Pallet published for the workloads behind them.
|
|
8
|
+
* Cloudly computes the table — it is the only party that knows every service,
|
|
9
|
+
* every ready endpoint and every certificate — and the relay carries it to the
|
|
10
|
+
* ingress and the ingress's counters back.
|
|
11
|
+
*
|
|
12
|
+
* Names here carry no version suffix and no schema field: the protocol version
|
|
13
|
+
* is the installed `@serve.zone/interfaces` version.
|
|
14
|
+
*/
|
|
15
|
+
export const clusterIngressLimits = Object.freeze({
|
|
16
|
+
minimumIngressBearerLength: 16,
|
|
17
|
+
maximumIngressBearerLength: 4096,
|
|
18
|
+
maximumHostNameLength: 253,
|
|
19
|
+
maximumRoutes: 4096,
|
|
20
|
+
maximumPortRoutes: 1024,
|
|
21
|
+
maximumDestinationsPerRoute: 64,
|
|
22
|
+
maximumStatisticsPageSize: 1000,
|
|
23
|
+
maximumCursorLength: 512,
|
|
24
|
+
maximumIdentifierLength: 200,
|
|
25
|
+
maximumIngressCapabilities: 32,
|
|
26
|
+
});
|
|
27
|
+
const isRecord = (valueArg) => (Boolean(valueArg)
|
|
28
|
+
&& typeof valueArg === 'object'
|
|
29
|
+
&& !Array.isArray(valueArg)
|
|
30
|
+
&& (Object.getPrototypeOf(valueArg) === Object.prototype
|
|
31
|
+
|| Object.getPrototypeOf(valueArg) === null));
|
|
32
|
+
const hasExactKeys = (valueArg, keysArg, optionalKeysArg = []) => {
|
|
33
|
+
const actualKeys = Object.keys(valueArg);
|
|
34
|
+
return keysArg.every((keyArg) => actualKeys.includes(keyArg))
|
|
35
|
+
&& actualKeys.every((keyArg) => keysArg.includes(keyArg) || optionalKeysArg.includes(keyArg));
|
|
36
|
+
};
|
|
37
|
+
const isCounter = (valueArg) => (Number.isSafeInteger(valueArg) && valueArg >= 0);
|
|
38
|
+
const isPort = (valueArg) => (Number.isSafeInteger(valueArg) && valueArg >= 1 && valueArg <= 65_535);
|
|
39
|
+
const hostLabelPattern = /^[a-z0-9](?:[a-z0-9-]{0,61}[a-z0-9])?$/;
|
|
40
|
+
// Built from the limit rather than beside it, so the two can never disagree.
|
|
41
|
+
const identifierPattern = new RegExp(`^[A-Za-z0-9][A-Za-z0-9:._-]{0,${clusterIngressLimits.maximumIdentifierLength - 1}}$`);
|
|
42
|
+
/**
|
|
43
|
+
* A hostname a certificate can be issued for and a browser can present.
|
|
44
|
+
*
|
|
45
|
+
* Lowercase labels only, because a certificate names the lowercase form and a
|
|
46
|
+
* table that carried both would serve one of them without a certificate.
|
|
47
|
+
*/
|
|
48
|
+
export const isClusterIngressHostName = (valueArg) => {
|
|
49
|
+
if (typeof valueArg !== 'string'
|
|
50
|
+
|| valueArg.length === 0
|
|
51
|
+
|| valueArg.length > clusterIngressLimits.maximumHostNameLength) {
|
|
52
|
+
return false;
|
|
53
|
+
}
|
|
54
|
+
const labels = valueArg.split('.');
|
|
55
|
+
return labels.length >= 2 && labels.every((labelArg) => hostLabelPattern.test(labelArg));
|
|
56
|
+
};
|
|
57
|
+
const validateDestinations = (valueArg, pathArg) => {
|
|
58
|
+
if (!Array.isArray(valueArg)
|
|
59
|
+
|| valueArg.length === 0
|
|
60
|
+
|| valueArg.length > clusterIngressLimits.maximumDestinationsPerRoute) {
|
|
61
|
+
return [`${pathArg} must list 1 to `
|
|
62
|
+
+ `${clusterIngressLimits.maximumDestinationsPerRoute} destinations`];
|
|
63
|
+
}
|
|
64
|
+
const errors = [];
|
|
65
|
+
for (const [index, destination] of valueArg.entries()) {
|
|
66
|
+
const destinationPath = `${pathArg}[${index}]`;
|
|
67
|
+
if (!isRecord(destination) || !hasExactKeys(destination, ['address', 'port'])) {
|
|
68
|
+
errors.push(`${destinationPath} must use its exact schema`);
|
|
69
|
+
continue;
|
|
70
|
+
}
|
|
71
|
+
if (!isPlausibleUnicastIpv4(destination.address)) {
|
|
72
|
+
errors.push(`${destinationPath} address must be a unicast IPv4 literal`);
|
|
73
|
+
}
|
|
74
|
+
if (!isPort(destination.port)) {
|
|
75
|
+
errors.push(`${destinationPath} port must be an integer from 1 to 65535`);
|
|
76
|
+
}
|
|
77
|
+
}
|
|
78
|
+
return errors;
|
|
79
|
+
};
|
|
80
|
+
const validateCertificate = (valueArg, pathArg) => {
|
|
81
|
+
// Presence and shape only. The material itself is never described in a reason:
|
|
82
|
+
// a validation message travels into logs, and this one would carry a key.
|
|
83
|
+
if (!isRecord(valueArg) || !hasExactKeys(valueArg, ['publicKey', 'privateKey'])) {
|
|
84
|
+
return [`${pathArg} must use its exact schema`];
|
|
85
|
+
}
|
|
86
|
+
const errors = [];
|
|
87
|
+
if (typeof valueArg.publicKey !== 'string' || valueArg.publicKey.length === 0) {
|
|
88
|
+
errors.push(`${pathArg} publicKey must be a non-empty PEM string`);
|
|
89
|
+
}
|
|
90
|
+
if (typeof valueArg.privateKey !== 'string' || valueArg.privateKey.length === 0) {
|
|
91
|
+
errors.push(`${pathArg} privateKey must be a non-empty PEM string`);
|
|
92
|
+
}
|
|
93
|
+
return errors;
|
|
94
|
+
};
|
|
95
|
+
const validateAuthentication = (valueArg, pathArg) => {
|
|
96
|
+
if (!isRecord(valueArg) || !hasExactKeys(valueArg, ['type', 'user', 'pass'])) {
|
|
97
|
+
return [`${pathArg} must use its exact schema`];
|
|
98
|
+
}
|
|
99
|
+
return valueArg.type === 'Basic'
|
|
100
|
+
&& typeof valueArg.user === 'string' && valueArg.user.length > 0
|
|
101
|
+
&& typeof valueArg.pass === 'string' && valueArg.pass.length > 0
|
|
102
|
+
? []
|
|
103
|
+
: [`${pathArg} must be Basic authentication with a user and a pass`];
|
|
104
|
+
};
|
|
105
|
+
const validateHttpRoute = (valueArg, pathArg) => {
|
|
106
|
+
if (!isRecord(valueArg)
|
|
107
|
+
|| !hasExactKeys(valueArg, ['hostName', 'destinations', 'certificate'], ['authentication'])) {
|
|
108
|
+
return [`${pathArg} must use its exact schema`];
|
|
109
|
+
}
|
|
110
|
+
const errors = [];
|
|
111
|
+
if (!isClusterIngressHostName(valueArg.hostName)) {
|
|
112
|
+
errors.push(`${pathArg} hostName must be a lowercase dotted hostname a certificate can name`);
|
|
113
|
+
}
|
|
114
|
+
errors.push(...validateDestinations(valueArg.destinations, `${pathArg} destinations`));
|
|
115
|
+
errors.push(...validateCertificate(valueArg.certificate, `${pathArg} certificate`));
|
|
116
|
+
if (valueArg.authentication !== undefined) {
|
|
117
|
+
errors.push(...validateAuthentication(valueArg.authentication, `${pathArg} authentication`));
|
|
118
|
+
}
|
|
119
|
+
return errors;
|
|
120
|
+
};
|
|
121
|
+
const validatePortRoute = (valueArg, pathArg) => {
|
|
122
|
+
if (!isRecord(valueArg)
|
|
123
|
+
|| !hasExactKeys(valueArg, ['name', 'listenPort', 'transport', 'destinations'])) {
|
|
124
|
+
return [`${pathArg} must use its exact schema`];
|
|
125
|
+
}
|
|
126
|
+
const errors = [];
|
|
127
|
+
if (typeof valueArg.name !== 'string' || !identifierPattern.test(valueArg.name)) {
|
|
128
|
+
errors.push(`${pathArg} name must be a bounded identifier`);
|
|
129
|
+
}
|
|
130
|
+
if (!isPort(valueArg.listenPort)) {
|
|
131
|
+
errors.push(`${pathArg} listenPort must be an integer from 1 to 65535`);
|
|
132
|
+
}
|
|
133
|
+
if (valueArg.transport !== 'tcp' && valueArg.transport !== 'udp') {
|
|
134
|
+
errors.push(`${pathArg} transport must be tcp or udp`);
|
|
135
|
+
}
|
|
136
|
+
errors.push(...validateDestinations(valueArg.destinations, `${pathArg} destinations`));
|
|
137
|
+
return errors;
|
|
138
|
+
};
|
|
139
|
+
/**
|
|
140
|
+
* Validates one route table and names every reason it is refused.
|
|
141
|
+
*
|
|
142
|
+
* Shape only. Whether a hostname belongs to the cluster, whether a certificate
|
|
143
|
+
* matches it, and whether a destination is a node of this cluster are decisions
|
|
144
|
+
* for the party that computed the table; a table that passes here has claimed
|
|
145
|
+
* none of them. No reason ever quotes certificate material.
|
|
146
|
+
*/
|
|
147
|
+
export const validateClusterRouteTable = (valueArg, pathArg = 'cluster route table') => {
|
|
148
|
+
if (!isRecord(valueArg)
|
|
149
|
+
|| !hasExactKeys(valueArg, ['revision', 'issuedAt', 'routes', 'portRoutes'])) {
|
|
150
|
+
return [`${pathArg} must use its exact schema`];
|
|
151
|
+
}
|
|
152
|
+
const errors = [];
|
|
153
|
+
if (!isCounter(valueArg.revision)) {
|
|
154
|
+
errors.push(`${pathArg} revision must be a non-negative safe integer`);
|
|
155
|
+
}
|
|
156
|
+
if (!Number.isSafeInteger(valueArg.issuedAt) || valueArg.issuedAt <= 0) {
|
|
157
|
+
errors.push(`${pathArg} issuedAt must be a positive epoch millisecond timestamp`);
|
|
158
|
+
}
|
|
159
|
+
if (!Array.isArray(valueArg.routes)
|
|
160
|
+
|| valueArg.routes.length > clusterIngressLimits.maximumRoutes) {
|
|
161
|
+
errors.push(`${pathArg} routes must list at most ${clusterIngressLimits.maximumRoutes} routes`);
|
|
162
|
+
}
|
|
163
|
+
else {
|
|
164
|
+
const seenHostNames = new Set();
|
|
165
|
+
for (const [index, route] of valueArg.routes.entries()) {
|
|
166
|
+
errors.push(...validateHttpRoute(route, `${pathArg} routes[${index}]`));
|
|
167
|
+
const hostName = isRecord(route) ? route.hostName : undefined;
|
|
168
|
+
if (typeof hostName === 'string') {
|
|
169
|
+
if (seenHostNames.has(hostName)) {
|
|
170
|
+
errors.push(`${pathArg} routes[${index}] repeats hostName ${hostName}`);
|
|
171
|
+
}
|
|
172
|
+
seenHostNames.add(hostName);
|
|
173
|
+
}
|
|
174
|
+
}
|
|
175
|
+
}
|
|
176
|
+
if (!Array.isArray(valueArg.portRoutes)
|
|
177
|
+
|| valueArg.portRoutes.length > clusterIngressLimits.maximumPortRoutes) {
|
|
178
|
+
errors.push(`${pathArg} portRoutes must list at most `
|
|
179
|
+
+ `${clusterIngressLimits.maximumPortRoutes} port routes`);
|
|
180
|
+
}
|
|
181
|
+
else {
|
|
182
|
+
const seenPorts = new Set();
|
|
183
|
+
for (const [index, portRoute] of valueArg.portRoutes.entries()) {
|
|
184
|
+
errors.push(...validatePortRoute(portRoute, `${pathArg} portRoutes[${index}]`));
|
|
185
|
+
if (!isRecord(portRoute)) {
|
|
186
|
+
continue;
|
|
187
|
+
}
|
|
188
|
+
const key = `${String(portRoute.transport)}:${String(portRoute.listenPort)}`;
|
|
189
|
+
if (seenPorts.has(key)) {
|
|
190
|
+
errors.push(`${pathArg} portRoutes[${index}] repeats ${key}`);
|
|
191
|
+
}
|
|
192
|
+
seenPorts.add(key);
|
|
193
|
+
}
|
|
194
|
+
}
|
|
195
|
+
return errors;
|
|
196
|
+
};
|
|
197
|
+
/** Validates one day bucket of ingress counters. */
|
|
198
|
+
export const validateClusterTrafficStatistics = (valueArg, pathArg = 'cluster traffic statistics') => {
|
|
199
|
+
if (!isRecord(valueArg) || !hasExactKeys(valueArg, [
|
|
200
|
+
'serviceId', 'hostName', 'nodeId', 'dayUtc', 'requests', 'bytesIn', 'bytesOut',
|
|
201
|
+
'connections', 'backendErrors', 'updatedAt',
|
|
202
|
+
])) {
|
|
203
|
+
return [`${pathArg} must use its exact schema`];
|
|
204
|
+
}
|
|
205
|
+
const errors = [];
|
|
206
|
+
for (const key of ['serviceId', 'nodeId']) {
|
|
207
|
+
const value = valueArg[key];
|
|
208
|
+
if (typeof value !== 'string' || !identifierPattern.test(value)) {
|
|
209
|
+
errors.push(`${pathArg} ${key} must be a bounded identifier`);
|
|
210
|
+
}
|
|
211
|
+
}
|
|
212
|
+
if (!isClusterIngressHostName(valueArg.hostName)) {
|
|
213
|
+
errors.push(`${pathArg} hostName must be a lowercase dotted hostname`);
|
|
214
|
+
}
|
|
215
|
+
if (!isUtcCalendarDay(valueArg.dayUtc)) {
|
|
216
|
+
errors.push(`${pathArg} dayUtc must be a real YYYY-MM-DD UTC calendar day`);
|
|
217
|
+
}
|
|
218
|
+
for (const key of ['requests', 'bytesIn', 'bytesOut', 'connections']) {
|
|
219
|
+
if (!isCounter(valueArg[key])) {
|
|
220
|
+
errors.push(`${pathArg} ${key} must be a non-negative safe integer`);
|
|
221
|
+
}
|
|
222
|
+
}
|
|
223
|
+
const backendErrors = valueArg.backendErrors;
|
|
224
|
+
if (!isRecord(backendErrors)
|
|
225
|
+
|| !hasExactKeys(backendErrors, ['connect', 'handshake', 'request'])
|
|
226
|
+
|| !isCounter(backendErrors.connect)
|
|
227
|
+
|| !isCounter(backendErrors.handshake)
|
|
228
|
+
|| !isCounter(backendErrors.request)) {
|
|
229
|
+
errors.push(`${pathArg} backendErrors must count connect, handshake and request failures`);
|
|
230
|
+
}
|
|
231
|
+
if (!Number.isSafeInteger(valueArg.updatedAt) || valueArg.updatedAt <= 0) {
|
|
232
|
+
errors.push(`${pathArg} updatedAt must be a positive epoch millisecond timestamp`);
|
|
233
|
+
}
|
|
234
|
+
return errors;
|
|
235
|
+
};
|
|
236
|
+
/**
|
|
237
|
+
* Validates one page of ingress counters, as a reader receives it.
|
|
238
|
+
*
|
|
239
|
+
* The cursor is opaque to everyone but the ingress that issued it: bounded and
|
|
240
|
+
* printable here, meaningful only there.
|
|
241
|
+
*/
|
|
242
|
+
export const validateClusterTrafficStatisticsPage = (valueArg, pathArg = 'cluster traffic statistics page') => {
|
|
243
|
+
if (!isRecord(valueArg) || !hasExactKeys(valueArg, ['statistics'], ['nextCursor'])) {
|
|
244
|
+
return [`${pathArg} must use its exact schema`];
|
|
245
|
+
}
|
|
246
|
+
const errors = [];
|
|
247
|
+
if (!Array.isArray(valueArg.statistics)
|
|
248
|
+
|| valueArg.statistics.length > clusterIngressLimits.maximumStatisticsPageSize) {
|
|
249
|
+
errors.push(`${pathArg} statistics must list at most `
|
|
250
|
+
+ `${clusterIngressLimits.maximumStatisticsPageSize} entries`);
|
|
251
|
+
}
|
|
252
|
+
else {
|
|
253
|
+
for (const [index, item] of valueArg.statistics.entries()) {
|
|
254
|
+
errors.push(...validateClusterTrafficStatistics(item, `${pathArg} statistics[${index}]`));
|
|
255
|
+
}
|
|
256
|
+
}
|
|
257
|
+
if (valueArg.nextCursor !== undefined
|
|
258
|
+
&& (typeof valueArg.nextCursor !== 'string'
|
|
259
|
+
|| valueArg.nextCursor.length === 0
|
|
260
|
+
|| valueArg.nextCursor.length > clusterIngressLimits.maximumCursorLength)) {
|
|
261
|
+
errors.push(`${pathArg} nextCursor must be 1 to `
|
|
262
|
+
+ `${clusterIngressLimits.maximumCursorLength} characters when present`);
|
|
263
|
+
}
|
|
264
|
+
return errors;
|
|
265
|
+
};
|
|
266
|
+
/**
|
|
267
|
+
* Validates one cluster ingress registration and names every reason it is refused.
|
|
268
|
+
*
|
|
269
|
+
* Shape only, and deliberately so: whether the bearer is the one Cloudly minted
|
|
270
|
+
* for this cluster's ingress is Cloudly's to verify against its own records, and
|
|
271
|
+
* which cluster is speaking comes from the identity tag on the relay's session.
|
|
272
|
+
* A registration that passes here has proven nothing — and no reason it returns
|
|
273
|
+
* ever quotes the bearer, because a reason travels into logs.
|
|
274
|
+
*/
|
|
275
|
+
export const validateClusterIngressRegistration = (valueArg, pathArg = 'cluster ingress registration') => {
|
|
276
|
+
if (!isRecord(valueArg)
|
|
277
|
+
|| !hasExactKeys(valueArg, ['bearer', 'version', 'nodeId'], ['capabilities'])) {
|
|
278
|
+
return [`${pathArg} must use its exact schema`];
|
|
279
|
+
}
|
|
280
|
+
const errors = [];
|
|
281
|
+
if (typeof valueArg.bearer !== 'string'
|
|
282
|
+
|| valueArg.bearer.length < clusterIngressLimits.minimumIngressBearerLength
|
|
283
|
+
|| valueArg.bearer.length > clusterIngressLimits.maximumIngressBearerLength) {
|
|
284
|
+
errors.push(`${pathArg} bearer must be ${clusterIngressLimits.minimumIngressBearerLength} to `
|
|
285
|
+
+ `${clusterIngressLimits.maximumIngressBearerLength} characters`);
|
|
286
|
+
}
|
|
287
|
+
if (typeof valueArg.version !== 'string' || !identifierPattern.test(valueArg.version)) {
|
|
288
|
+
errors.push(`${pathArg} version must be a bounded identifier`);
|
|
289
|
+
}
|
|
290
|
+
if (typeof valueArg.nodeId !== 'string' || !identifierPattern.test(valueArg.nodeId)) {
|
|
291
|
+
errors.push(`${pathArg} nodeId must be a bounded identifier`);
|
|
292
|
+
}
|
|
293
|
+
if (valueArg.capabilities !== undefined
|
|
294
|
+
&& (!Array.isArray(valueArg.capabilities)
|
|
295
|
+
|| valueArg.capabilities.length > clusterIngressLimits.maximumIngressCapabilities
|
|
296
|
+
|| !valueArg.capabilities.every((capabilityArg) => (typeof capabilityArg === 'string' && identifierPattern.test(capabilityArg))))) {
|
|
297
|
+
errors.push(`${pathArg} capabilities must be at most `
|
|
298
|
+
+ `${clusterIngressLimits.maximumIngressCapabilities} bounded identifiers when present`);
|
|
299
|
+
}
|
|
300
|
+
return errors;
|
|
301
|
+
};
|
|
302
|
+
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiY2x1c3RlcmluZ3Jlc3MuanMiLCJzb3VyY2VSb290IjoiIiwic291cmNlcyI6WyIuLi8uLi90cy9kYXRhL2NsdXN0ZXJpbmdyZXNzLnRzIl0sIm5hbWVzIjpbXSwibWFwcGluZ3MiOiJBQUFBLE9BQU8sRUFBRSxzQkFBc0IsRUFBRSxNQUFNLG9CQUFvQixDQUFDO0FBQzVELE9BQU8sRUFBRSxnQkFBZ0IsRUFBRSxNQUFNLDhCQUE4QixDQUFDO0FBRWhFOzs7Ozs7Ozs7OztHQVdHO0FBRUgsTUFBTSxDQUFDLE1BQU0sb0JBQW9CLEdBQUcsTUFBTSxDQUFDLE1BQU0sQ0FBQztJQUNoRCwwQkFBMEIsRUFBRSxFQUFFO0lBQzlCLDBCQUEwQixFQUFFLElBQUk7SUFDaEMscUJBQXFCLEVBQUUsR0FBRztJQUMxQixhQUFhLEVBQUUsSUFBSTtJQUNuQixpQkFBaUIsRUFBRSxJQUFJO0lBQ3ZCLDJCQUEyQixFQUFFLEVBQUU7SUFDL0IseUJBQXlCLEVBQUUsSUFBSTtJQUMvQixtQkFBbUIsRUFBRSxHQUFHO0lBQ3hCLHVCQUF1QixFQUFFLEdBQUc7SUFDNUIsMEJBQTBCLEVBQUUsRUFBRTtDQUMvQixDQUFDLENBQUM7QUFzRkgsTUFBTSxRQUFRLEdBQUcsQ0FBQyxRQUFpQixFQUF1QyxFQUFFLENBQUMsQ0FDM0UsT0FBTyxDQUFDLFFBQVEsQ0FBQztPQUNkLE9BQU8sUUFBUSxLQUFLLFFBQVE7T0FDNUIsQ0FBQyxLQUFLLENBQUMsT0FBTyxDQUFDLFFBQVEsQ0FBQztPQUN4QixDQUFDLE1BQU0sQ0FBQyxjQUFjLENBQUMsUUFBUSxDQUFDLEtBQUssTUFBTSxDQUFDLFNBQVM7V0FDbkQsTUFBTSxDQUFDLGNBQWMsQ0FBQyxRQUFRLENBQUMsS0FBSyxJQUFJLENBQUMsQ0FDL0MsQ0FBQztBQUVGLE1BQU0sWUFBWSxHQUFHLENBQ25CLFFBQWlDLEVBQ2pDLE9BQTBCLEVBQzFCLGtCQUFxQyxFQUFFLEVBQzlCLEVBQUU7SUFDWCxNQUFNLFVBQVUsR0FBRyxNQUFNLENBQUMsSUFB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
|