@lossless.org/objectstorage 9.0.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/.smartconfig.json +71 -0
- package/dist_rust/ruststorage_linux_amd64 +0 -0
- package/dist_rust/ruststorage_linux_amd64.tsrust-build.json +14 -0
- package/dist_rust/ruststorage_linux_arm64 +0 -0
- package/dist_rust/ruststorage_linux_arm64.tsrust-build.json +14 -0
- package/dist_ts/00_commitinfo_data.d.ts +8 -0
- package/dist_ts/00_commitinfo_data.js +9 -0
- package/dist_ts/index.d.ts +741 -0
- package/dist_ts/index.js +885 -0
- package/dist_ts/paths.d.ts +4 -0
- package/dist_ts/paths.js +6 -0
- package/dist_ts/plugins.d.ts +9 -0
- package/dist_ts/plugins.js +13 -0
- package/license.md +21 -0
- package/package.json +77 -0
- package/readme.md +1430 -0
- package/third_party_licenses/base64-0.22.1/LICENSE-MIT +21 -0
- package/third_party_licenses/crc32fast-1.5.0/LICENSE-MIT +21 -0
- package/third_party_licenses/ring-0.17.14/INDEX.md +15 -0
- package/third_party_licenses/ring-0.17.14/LICENSE +9 -0
- package/third_party_licenses/ring-0.17.14/LICENSE-BoringSSL +272 -0
- package/third_party_licenses/ring-0.17.14/LICENSE-fiat +15 -0
- package/third_party_licenses/ring-0.17.14/LICENSE-once_cell-APACHE +201 -0
- package/third_party_licenses/ring-0.17.14/LICENSE-once_cell-MIT +23 -0
- package/third_party_licenses/ring-0.17.14/LICENSE-other-bits +13 -0
- package/ts/00_commitinfo_data.ts +8 -0
- package/ts/index.ts +1932 -0
- package/ts/paths.ts +11 -0
- package/ts/plugins.ts +17 -0
package/dist_ts/index.js
ADDED
|
@@ -0,0 +1,885 @@
|
|
|
1
|
+
import * as plugins from './plugins.js';
|
|
2
|
+
import * as paths from './paths.js';
|
|
3
|
+
const resourceFenceV1Limits = {
|
|
4
|
+
maximumObjects: 10_000,
|
|
5
|
+
maximumObjectBytes: 64 * 1024 * 1024,
|
|
6
|
+
maximumImportBytes: 64 * 1024 * 1024,
|
|
7
|
+
maximumKeyBytes: 1024,
|
|
8
|
+
maximumMetadataEntries: 64,
|
|
9
|
+
maximumMetadataBytes: 8 * 1024,
|
|
10
|
+
maximumTotalKeyBytes: 1024 * 1024,
|
|
11
|
+
maximumTotalMetadataBytes: 1024 * 1024,
|
|
12
|
+
maximumIdentifierBytes: 256,
|
|
13
|
+
};
|
|
14
|
+
const resourceFenceV1Uint64 = (valueArg) => {
|
|
15
|
+
if (!Number.isSafeInteger(valueArg) || valueArg < 0) {
|
|
16
|
+
throw new Error('ObjectStorage resource fence v1 integers must be non-negative safe integers.');
|
|
17
|
+
}
|
|
18
|
+
const encoded = Buffer.alloc(8);
|
|
19
|
+
encoded.writeBigUInt64BE(BigInt(valueArg));
|
|
20
|
+
return encoded;
|
|
21
|
+
};
|
|
22
|
+
const resourceFenceV1Utf8 = (valueArg, fieldArg) => {
|
|
23
|
+
if (typeof valueArg !== 'string') {
|
|
24
|
+
throw new Error(`${fieldArg} must be a string.`);
|
|
25
|
+
}
|
|
26
|
+
const encoded = Buffer.from(valueArg, 'utf8');
|
|
27
|
+
if (encoded.toString('utf8') !== valueArg) {
|
|
28
|
+
throw new Error(`${fieldArg} must contain valid Unicode scalar values.`);
|
|
29
|
+
}
|
|
30
|
+
return encoded;
|
|
31
|
+
};
|
|
32
|
+
const compareResourceFenceV1Utf8 = (leftArg, rightArg) => Buffer.compare(Buffer.from(leftArg, 'utf8'), Buffer.from(rightArg, 'utf8'));
|
|
33
|
+
const assertResourceFenceV1BucketName = (bucketNameArg) => {
|
|
34
|
+
const encoded = resourceFenceV1Utf8(bucketNameArg, 'bucketName');
|
|
35
|
+
const ipv4Parts = bucketNameArg.split('.');
|
|
36
|
+
const isIpv4Address = ipv4Parts.length === 4 && ipv4Parts.every((part) => /^[0-9]+$/.test(part) && Number(part) <= 255);
|
|
37
|
+
if (encoded.length < 3
|
|
38
|
+
|| encoded.length > 63
|
|
39
|
+
|| !/^[a-z0-9][a-z0-9.-]*[a-z0-9]$/.test(bucketNameArg)
|
|
40
|
+
|| bucketNameArg.includes('..')
|
|
41
|
+
|| isIpv4Address) {
|
|
42
|
+
throw new Error('bucketName is not a canonical S3 bucket name.');
|
|
43
|
+
}
|
|
44
|
+
};
|
|
45
|
+
const assertResourceFenceV1Identifier = (valueArg, fieldArg) => {
|
|
46
|
+
const encoded = resourceFenceV1Utf8(valueArg, fieldArg);
|
|
47
|
+
if (encoded.length === 0
|
|
48
|
+
|| encoded.length > resourceFenceV1Limits.maximumIdentifierBytes
|
|
49
|
+
|| !/^[A-Za-z0-9][A-Za-z0-9._:@-]*$/.test(valueArg)) {
|
|
50
|
+
throw new Error(`${fieldArg} is not a canonical identifier.`);
|
|
51
|
+
}
|
|
52
|
+
};
|
|
53
|
+
const canonicalResourceFenceV1Metadata = (metadataArg, objectKeyArg) => {
|
|
54
|
+
if (!metadataArg || typeof metadataArg !== 'object' || Array.isArray(metadataArg)) {
|
|
55
|
+
throw new Error(`Object '${objectKeyArg}' metadata must be an object.`);
|
|
56
|
+
}
|
|
57
|
+
const entries = Object.entries(metadataArg);
|
|
58
|
+
if (entries.length > resourceFenceV1Limits.maximumMetadataEntries) {
|
|
59
|
+
throw new Error(`Object '${objectKeyArg}' has too many metadata entries.`);
|
|
60
|
+
}
|
|
61
|
+
for (const [key, value] of entries) {
|
|
62
|
+
const keyBytes = resourceFenceV1Utf8(key, `Object '${objectKeyArg}' metadata key`);
|
|
63
|
+
const valueBytes = resourceFenceV1Utf8(value, `Object '${objectKeyArg}' metadata value`);
|
|
64
|
+
if (keyBytes.length === 0
|
|
65
|
+
|| keyBytes.length > 128
|
|
66
|
+
|| valueBytes.length > 2048
|
|
67
|
+
|| keyBytes.some((byte) => byte <= 0x1f || byte === 0x7f)
|
|
68
|
+
|| valueBytes.some((byte) => (byte <= 0x1f && byte !== 0x09) || byte === 0x7f)) {
|
|
69
|
+
throw new Error(`Object '${objectKeyArg}' metadata is invalid.`);
|
|
70
|
+
}
|
|
71
|
+
}
|
|
72
|
+
entries.sort(([left], [right]) => compareResourceFenceV1Utf8(left, right));
|
|
73
|
+
const encoded = Buffer.from(`{${entries.map(([key, value]) => `${JSON.stringify(key)}:${JSON.stringify(value)}`).join(',')}}`, 'utf8');
|
|
74
|
+
if (encoded.length > resourceFenceV1Limits.maximumMetadataBytes) {
|
|
75
|
+
throw new Error(`Object '${objectKeyArg}' metadata exceeds its size limit.`);
|
|
76
|
+
}
|
|
77
|
+
return encoded;
|
|
78
|
+
};
|
|
79
|
+
/**
|
|
80
|
+
* Computes the canonical provider-side effective payload digest for resource
|
|
81
|
+
* fencing v1. This intentionally excludes backup timestamps and source bucket
|
|
82
|
+
* names, matching the Rust engine's replacement and exact-delete profiles.
|
|
83
|
+
*/
|
|
84
|
+
export const computeObjectStorageResourceFenceEffectivePayloadSha256V1 = (payloadArg) => {
|
|
85
|
+
if (payloadArg.version !== 1) {
|
|
86
|
+
throw new Error('ObjectStorage resource fence effective payload version is unsupported.');
|
|
87
|
+
}
|
|
88
|
+
assertResourceFenceV1BucketName(payloadArg.bucketName);
|
|
89
|
+
const hash = plugins.crypto.createHash('sha256');
|
|
90
|
+
const digestField = (valueArg) => {
|
|
91
|
+
const value = typeof valueArg === 'string'
|
|
92
|
+
? resourceFenceV1Utf8(valueArg, 'resource fence digest field')
|
|
93
|
+
: valueArg;
|
|
94
|
+
hash.update(resourceFenceV1Uint64(value.length));
|
|
95
|
+
hash.update(value);
|
|
96
|
+
};
|
|
97
|
+
if (payloadArg.kind === 'smartstorage.bucket.delete.v1') {
|
|
98
|
+
assertResourceFenceV1Identifier(payloadArg.accessKeyId, 'accessKeyId');
|
|
99
|
+
digestField(payloadArg.kind);
|
|
100
|
+
digestField(payloadArg.bucketName);
|
|
101
|
+
digestField(payloadArg.accessKeyId);
|
|
102
|
+
if (payloadArg.holdPublication === true) {
|
|
103
|
+
digestField('smartstorage.resource-fence.hold-publication.v1');
|
|
104
|
+
digestField(Buffer.from([1]));
|
|
105
|
+
}
|
|
106
|
+
return hash.digest('hex');
|
|
107
|
+
}
|
|
108
|
+
if (payloadArg.kind === 'smartstorage.bucket.ensure.v1') {
|
|
109
|
+
assertResourceFenceV1Identifier(payloadArg.accessKeyId, 'accessKeyId');
|
|
110
|
+
const secretAccessKey = resourceFenceV1Utf8(payloadArg.secretAccessKey, 'secretAccessKey');
|
|
111
|
+
if (!payloadArg.secretAccessKey.trim()) {
|
|
112
|
+
throw new Error('secretAccessKey must not be empty.');
|
|
113
|
+
}
|
|
114
|
+
const region = resourceFenceV1Utf8(payloadArg.region, 'region');
|
|
115
|
+
if (!payloadArg.region.trim()) {
|
|
116
|
+
throw new Error('region must not be empty.');
|
|
117
|
+
}
|
|
118
|
+
digestField(payloadArg.kind);
|
|
119
|
+
digestField(payloadArg.bucketName);
|
|
120
|
+
digestField(payloadArg.accessKeyId);
|
|
121
|
+
digestField(plugins.crypto.createHash('sha256').update(secretAccessKey).digest('hex'));
|
|
122
|
+
digestField(region);
|
|
123
|
+
if (payloadArg.retention) {
|
|
124
|
+
if (payloadArg.retention.mode !== 'compliance'
|
|
125
|
+
|| !/^[A-Za-z0-9._-]{1,96}$/.test(payloadArg.retention.policyId)
|
|
126
|
+
|| !Number.isSafeInteger(payloadArg.retention.retentionDurationSeconds)
|
|
127
|
+
|| payloadArg.retention.retentionDurationSeconds <= 0
|
|
128
|
+
|| payloadArg.retention.retentionDurationSeconds > 100 * 366 * 24 * 60 * 60) {
|
|
129
|
+
throw new Error('retention must contain canonical compliance-retention intent.');
|
|
130
|
+
}
|
|
131
|
+
digestField('smartstorage.bucket.retention.v1');
|
|
132
|
+
digestField(payloadArg.retention.mode);
|
|
133
|
+
digestField(payloadArg.retention.policyId);
|
|
134
|
+
digestField(resourceFenceV1Uint64(payloadArg.retention.retentionDurationSeconds));
|
|
135
|
+
}
|
|
136
|
+
return hash.digest('hex');
|
|
137
|
+
}
|
|
138
|
+
if (payloadArg.kind !== 'smartstorage.bucket.replace.v1') {
|
|
139
|
+
throw new Error('ObjectStorage resource fence effective payload kind is unsupported.');
|
|
140
|
+
}
|
|
141
|
+
if (payloadArg.accessKeyId !== undefined) {
|
|
142
|
+
assertResourceFenceV1Identifier(payloadArg.accessKeyId, 'accessKeyId');
|
|
143
|
+
}
|
|
144
|
+
const source = payloadArg.source;
|
|
145
|
+
if (source.format !== 'smartstorage.bucket.v1') {
|
|
146
|
+
throw new Error('source format must be smartstorage.bucket.v1.');
|
|
147
|
+
}
|
|
148
|
+
assertResourceFenceV1BucketName(source.bucketName);
|
|
149
|
+
if (!Number.isSafeInteger(source.exportedAt) || source.exportedAt < 0) {
|
|
150
|
+
throw new Error('source exportedAt must be a non-negative safe integer.');
|
|
151
|
+
}
|
|
152
|
+
if (!Array.isArray(source.objects) || source.objects.length > resourceFenceV1Limits.maximumObjects) {
|
|
153
|
+
throw new Error(`source exceeds the ${resourceFenceV1Limits.maximumObjects}-object limit.`);
|
|
154
|
+
}
|
|
155
|
+
const logicalKeys = new Set();
|
|
156
|
+
let totalBytes = 0;
|
|
157
|
+
let totalKeyBytes = 0;
|
|
158
|
+
let totalMetadataBytes = 0;
|
|
159
|
+
const prepared = source.objects.map((object) => {
|
|
160
|
+
const keyBytes = resourceFenceV1Utf8(object.key, 'object key');
|
|
161
|
+
if (keyBytes.length === 0
|
|
162
|
+
|| keyBytes.length > resourceFenceV1Limits.maximumKeyBytes) {
|
|
163
|
+
throw new Error(`Object key '${object.key}' must contain between 1 and ${resourceFenceV1Limits.maximumKeyBytes} UTF-8 bytes.`);
|
|
164
|
+
}
|
|
165
|
+
if (logicalKeys.has(object.key)) {
|
|
166
|
+
throw new Error(`source contains duplicate object key '${object.key}'.`);
|
|
167
|
+
}
|
|
168
|
+
logicalKeys.add(object.key);
|
|
169
|
+
totalKeyBytes += keyBytes.length;
|
|
170
|
+
if (totalKeyBytes > resourceFenceV1Limits.maximumTotalKeyBytes) {
|
|
171
|
+
throw new Error(`source exceeds the ${resourceFenceV1Limits.maximumTotalKeyBytes}-byte aggregate key limit.`);
|
|
172
|
+
}
|
|
173
|
+
if (!Number.isSafeInteger(object.size)
|
|
174
|
+
|| object.size < 0
|
|
175
|
+
|| object.size > resourceFenceV1Limits.maximumObjectBytes
|
|
176
|
+
|| object.dataHex.length !== object.size * 2
|
|
177
|
+
|| !/^[0-9a-f]*$/.test(object.dataHex)) {
|
|
178
|
+
throw new Error(`Object '${object.key}' dataHex or size is not canonical.`);
|
|
179
|
+
}
|
|
180
|
+
const data = Buffer.from(object.dataHex, 'hex');
|
|
181
|
+
if (data.length !== object.size) {
|
|
182
|
+
throw new Error(`Object '${object.key}' size does not match dataHex.`);
|
|
183
|
+
}
|
|
184
|
+
if (!/^[0-9a-f]{32}$/.test(object.md5)) {
|
|
185
|
+
throw new Error(`Object '${object.key}' md5 is not canonical.`);
|
|
186
|
+
}
|
|
187
|
+
const actualMd5 = plugins.crypto.createHash('md5').update(data).digest('hex');
|
|
188
|
+
if (actualMd5 !== object.md5) {
|
|
189
|
+
throw new Error(`Object '${object.key}' md5 does not match payload.`);
|
|
190
|
+
}
|
|
191
|
+
const metadata = canonicalResourceFenceV1Metadata(object.metadata, object.key);
|
|
192
|
+
totalMetadataBytes += metadata.length;
|
|
193
|
+
if (totalMetadataBytes > resourceFenceV1Limits.maximumTotalMetadataBytes) {
|
|
194
|
+
throw new Error(`source exceeds the ${resourceFenceV1Limits.maximumTotalMetadataBytes}-byte aggregate metadata limit.`);
|
|
195
|
+
}
|
|
196
|
+
totalBytes += object.size;
|
|
197
|
+
if (totalBytes > resourceFenceV1Limits.maximumImportBytes) {
|
|
198
|
+
throw new Error(`source exceeds the ${resourceFenceV1Limits.maximumImportBytes}-byte import limit.`);
|
|
199
|
+
}
|
|
200
|
+
return {
|
|
201
|
+
key: object.key,
|
|
202
|
+
size: object.size,
|
|
203
|
+
md5: object.md5,
|
|
204
|
+
metadata,
|
|
205
|
+
dataSha256: plugins.crypto.createHash('sha256').update(data).digest('hex'),
|
|
206
|
+
};
|
|
207
|
+
});
|
|
208
|
+
prepared.sort((left, right) => compareResourceFenceV1Utf8(left.key, right.key));
|
|
209
|
+
digestField(payloadArg.kind);
|
|
210
|
+
digestField(payloadArg.bucketName);
|
|
211
|
+
if (payloadArg.accessKeyId !== undefined) {
|
|
212
|
+
digestField(payloadArg.accessKeyId);
|
|
213
|
+
}
|
|
214
|
+
digestField(resourceFenceV1Uint64(prepared.length));
|
|
215
|
+
for (const object of prepared) {
|
|
216
|
+
digestField(object.key);
|
|
217
|
+
digestField(resourceFenceV1Uint64(object.size));
|
|
218
|
+
digestField(object.md5);
|
|
219
|
+
digestField(object.metadata);
|
|
220
|
+
digestField(object.dataSha256);
|
|
221
|
+
}
|
|
222
|
+
if (payloadArg.holdPublication === true) {
|
|
223
|
+
digestField('smartstorage.resource-fence.hold-publication.v1');
|
|
224
|
+
digestField(Buffer.from([1]));
|
|
225
|
+
}
|
|
226
|
+
return hash.digest('hex');
|
|
227
|
+
};
|
|
228
|
+
export const objectStorageResourceFenceErrorCodes = [
|
|
229
|
+
'EFENCE_REQUIRED',
|
|
230
|
+
'EFENCE_STALE',
|
|
231
|
+
'EFENCE_SCOPE_MISMATCH',
|
|
232
|
+
'EFENCE_UNSUPPORTED',
|
|
233
|
+
'EFENCE_MUTATION_MISMATCH',
|
|
234
|
+
'EFENCE_STATE_CORRUPT',
|
|
235
|
+
'EFENCE_RECEIPT_LIMIT',
|
|
236
|
+
'EFENCE_BUSY',
|
|
237
|
+
'EFENCE_OWNERSHIP_MISMATCH',
|
|
238
|
+
'EFENCE_PUBLICATION_HELD',
|
|
239
|
+
'EFENCE_PUBLICATION_MISMATCH',
|
|
240
|
+
'EFENCE_PUBLICATION_CAPABILITY',
|
|
241
|
+
];
|
|
242
|
+
export class ObjectStorageResourceFenceError extends Error {
|
|
243
|
+
code;
|
|
244
|
+
constructor(codeArg, messageArg, causeArg) {
|
|
245
|
+
super(`${codeArg}: ${messageArg}`, { cause: causeArg });
|
|
246
|
+
this.name = 'ObjectStorageResourceFenceError';
|
|
247
|
+
this.code = codeArg;
|
|
248
|
+
}
|
|
249
|
+
}
|
|
250
|
+
const normalizeResourceFenceError = (errorArg) => {
|
|
251
|
+
const error = errorArg instanceof Error ? errorArg : new Error(String(errorArg));
|
|
252
|
+
const match = /(EFENCE_[A-Z_]+):\s*(.*)$/s.exec(error.message);
|
|
253
|
+
if (!match ||
|
|
254
|
+
!objectStorageResourceFenceErrorCodes.includes(match[1])) {
|
|
255
|
+
return error;
|
|
256
|
+
}
|
|
257
|
+
return new ObjectStorageResourceFenceError(match[1], match[2], error);
|
|
258
|
+
};
|
|
259
|
+
/**
|
|
260
|
+
* Default configuration values
|
|
261
|
+
*/
|
|
262
|
+
const DEFAULT_CONFIG = {
|
|
263
|
+
server: {
|
|
264
|
+
port: 3000,
|
|
265
|
+
address: '0.0.0.0',
|
|
266
|
+
silent: false,
|
|
267
|
+
region: 'us-east-1',
|
|
268
|
+
},
|
|
269
|
+
storage: {
|
|
270
|
+
directory: paths.bucketsDir,
|
|
271
|
+
cleanSlate: false,
|
|
272
|
+
},
|
|
273
|
+
auth: {
|
|
274
|
+
enabled: false,
|
|
275
|
+
credentials: [
|
|
276
|
+
{
|
|
277
|
+
accessKeyId: 'STORAGE',
|
|
278
|
+
secretAccessKey: 'STORAGE',
|
|
279
|
+
},
|
|
280
|
+
],
|
|
281
|
+
},
|
|
282
|
+
cors: {
|
|
283
|
+
enabled: false,
|
|
284
|
+
allowedOrigins: ['*'],
|
|
285
|
+
allowedMethods: ['GET', 'POST', 'PUT', 'DELETE', 'HEAD', 'OPTIONS'],
|
|
286
|
+
allowedHeaders: ['*'],
|
|
287
|
+
exposedHeaders: ['ETag', 'x-amz-request-id', 'x-amz-version-id'],
|
|
288
|
+
maxAge: 86400,
|
|
289
|
+
allowCredentials: false,
|
|
290
|
+
},
|
|
291
|
+
logging: {
|
|
292
|
+
level: 'info',
|
|
293
|
+
format: 'text',
|
|
294
|
+
enabled: true,
|
|
295
|
+
},
|
|
296
|
+
limits: {
|
|
297
|
+
maxObjectSize: 5 * 1024 * 1024 * 1024, // 5GB
|
|
298
|
+
maxMetadataSize: 2048,
|
|
299
|
+
requestTimeout: 300000, // 5 minutes
|
|
300
|
+
},
|
|
301
|
+
multipart: {
|
|
302
|
+
expirationDays: 7,
|
|
303
|
+
cleanupIntervalMinutes: 60,
|
|
304
|
+
},
|
|
305
|
+
};
|
|
306
|
+
// A fenced import may contain 64 MiB of raw object data encoded as hexadecimal.
|
|
307
|
+
// Keep this aligned with the bounded Rust management-line reader.
|
|
308
|
+
const MAXIMUM_MANAGEMENT_PAYLOAD_BYTES = 144 * 1024 * 1024;
|
|
309
|
+
function assertExactMountedDirectory(directoryArg, fieldArg) {
|
|
310
|
+
if (directoryArg.length === 0 ||
|
|
311
|
+
!plugins.path.isAbsolute(directoryArg) ||
|
|
312
|
+
plugins.path.normalize(directoryArg) !== directoryArg ||
|
|
313
|
+
plugins.path.resolve(directoryArg) !== directoryArg) {
|
|
314
|
+
throw new Error(`${fieldArg} must be a non-empty absolute lexically exact mountpoint without '.', '..', duplicate separators, or a trailing separator.`);
|
|
315
|
+
}
|
|
316
|
+
}
|
|
317
|
+
/**
|
|
318
|
+
* Merge user config with defaults (deep merge)
|
|
319
|
+
*/
|
|
320
|
+
function mergeConfig(userConfig) {
|
|
321
|
+
const userStorage = userConfig.storage || {};
|
|
322
|
+
const defaultDirectory = DEFAULT_CONFIG.storage.directory;
|
|
323
|
+
const legacyDirectory = userStorage.directory;
|
|
324
|
+
const configuredPool = userStorage.pool;
|
|
325
|
+
const configuredMountedPool = configuredPool?.backend.kind === 'mountedFs';
|
|
326
|
+
if (configuredMountedPool) {
|
|
327
|
+
assertExactMountedDirectory(configuredPool.directory, 'storage.pool.directory');
|
|
328
|
+
if (legacyDirectory !== undefined) {
|
|
329
|
+
assertExactMountedDirectory(legacyDirectory, 'storage.directory');
|
|
330
|
+
if (legacyDirectory !== configuredPool.directory) {
|
|
331
|
+
throw new Error('storage.directory and storage.pool.directory must be the same exact mountedFs path.');
|
|
332
|
+
}
|
|
333
|
+
}
|
|
334
|
+
}
|
|
335
|
+
const effectiveDirectory = configuredPool?.directory || legacyDirectory || defaultDirectory;
|
|
336
|
+
if (configuredPool &&
|
|
337
|
+
!configuredMountedPool &&
|
|
338
|
+
legacyDirectory &&
|
|
339
|
+
plugins.path.resolve(configuredPool.directory) !==
|
|
340
|
+
plugins.path.resolve(legacyDirectory)) {
|
|
341
|
+
throw new Error('storage.directory and storage.pool.directory must resolve to the same path.');
|
|
342
|
+
}
|
|
343
|
+
const pool = configuredPool
|
|
344
|
+
? {
|
|
345
|
+
...configuredPool,
|
|
346
|
+
directory: plugins.path.resolve(effectiveDirectory),
|
|
347
|
+
backend: { ...configuredPool.backend },
|
|
348
|
+
}
|
|
349
|
+
: {
|
|
350
|
+
id: 'default',
|
|
351
|
+
directory: plugins.path.resolve(effectiveDirectory),
|
|
352
|
+
backend: { kind: 'localFs' },
|
|
353
|
+
};
|
|
354
|
+
if (!/^[a-z][a-z0-9-]{0,62}$/.test(pool.id)) {
|
|
355
|
+
throw new Error('storage.pool.id must start with a lowercase letter and contain at most 63 lowercase letters, digits, or hyphens.');
|
|
356
|
+
}
|
|
357
|
+
if (pool.backend.kind === 'mountedFs' &&
|
|
358
|
+
pool.backend.expectedSource.trim().length === 0) {
|
|
359
|
+
throw new Error('storage.pool.backend.expectedSource must not be empty for mountedFs.');
|
|
360
|
+
}
|
|
361
|
+
if (pool.backend.kind === 'mountedFs' && userStorage.cleanSlate) {
|
|
362
|
+
throw new Error('storage.cleanSlate is not permitted for mountedFs pools.');
|
|
363
|
+
}
|
|
364
|
+
return {
|
|
365
|
+
server: {
|
|
366
|
+
...DEFAULT_CONFIG.server,
|
|
367
|
+
...(userConfig.server || {}),
|
|
368
|
+
},
|
|
369
|
+
storage: {
|
|
370
|
+
...DEFAULT_CONFIG.storage,
|
|
371
|
+
...userStorage,
|
|
372
|
+
directory: pool.directory,
|
|
373
|
+
pool,
|
|
374
|
+
},
|
|
375
|
+
auth: {
|
|
376
|
+
...DEFAULT_CONFIG.auth,
|
|
377
|
+
...(userConfig.auth || {}),
|
|
378
|
+
},
|
|
379
|
+
cors: {
|
|
380
|
+
...DEFAULT_CONFIG.cors,
|
|
381
|
+
...(userConfig.cors || {}),
|
|
382
|
+
},
|
|
383
|
+
logging: {
|
|
384
|
+
...DEFAULT_CONFIG.logging,
|
|
385
|
+
...(userConfig.logging || {}),
|
|
386
|
+
},
|
|
387
|
+
limits: {
|
|
388
|
+
...DEFAULT_CONFIG.limits,
|
|
389
|
+
...(userConfig.limits || {}),
|
|
390
|
+
},
|
|
391
|
+
multipart: {
|
|
392
|
+
...DEFAULT_CONFIG.multipart,
|
|
393
|
+
...(userConfig.multipart || {}),
|
|
394
|
+
},
|
|
395
|
+
...(userConfig.cluster ? { cluster: userConfig.cluster } : {}),
|
|
396
|
+
};
|
|
397
|
+
}
|
|
398
|
+
function createAccessKeyId() {
|
|
399
|
+
return `SS${plugins.crypto.randomBytes(10).toString('hex').toUpperCase()}`;
|
|
400
|
+
}
|
|
401
|
+
function createSecretAccessKey() {
|
|
402
|
+
return plugins.crypto.randomBytes(32).toString('hex');
|
|
403
|
+
}
|
|
404
|
+
function getRustStorageBinaryNames() {
|
|
405
|
+
const platformTargetMap = {
|
|
406
|
+
'linux:x64': 'linux_amd64',
|
|
407
|
+
'linux:arm64': 'linux_arm64',
|
|
408
|
+
'darwin:x64': 'macos_amd64',
|
|
409
|
+
'darwin:arm64': 'macos_arm64',
|
|
410
|
+
};
|
|
411
|
+
const platformTarget = platformTargetMap[`${process.platform}:${process.arch}`];
|
|
412
|
+
return [platformTarget ? `ruststorage_${platformTarget}` : null, 'ruststorage']
|
|
413
|
+
.filter((binaryNameArg) => Boolean(binaryNameArg));
|
|
414
|
+
}
|
|
415
|
+
/**
|
|
416
|
+
* Main ObjectStorage class - production-ready S3-compatible storage server
|
|
417
|
+
*/
|
|
418
|
+
export class ObjectStorage {
|
|
419
|
+
// STATIC
|
|
420
|
+
static async createAndStart(configArg = {}) {
|
|
421
|
+
const objectStorageInstance = new ObjectStorage(configArg);
|
|
422
|
+
await objectStorageInstance.start();
|
|
423
|
+
return objectStorageInstance;
|
|
424
|
+
}
|
|
425
|
+
static async probeStoragePool(poolArg) {
|
|
426
|
+
const objectStorageInstance = new ObjectStorage({
|
|
427
|
+
server: { silent: true },
|
|
428
|
+
storage: {
|
|
429
|
+
directory: poolArg.directory,
|
|
430
|
+
cleanSlate: false,
|
|
431
|
+
pool: poolArg,
|
|
432
|
+
},
|
|
433
|
+
});
|
|
434
|
+
await objectStorageInstance.ensureLocalBinariesExecutable();
|
|
435
|
+
const spawned = await objectStorageInstance.bridge.spawn();
|
|
436
|
+
if (!spawned) {
|
|
437
|
+
throw new Error('Failed to spawn ruststorage binary. Make sure it is compiled (pnpm build).');
|
|
438
|
+
}
|
|
439
|
+
try {
|
|
440
|
+
return await objectStorageInstance.bridge.sendCommand('probeStoragePool', {
|
|
441
|
+
pool: objectStorageInstance.config.storage
|
|
442
|
+
.pool,
|
|
443
|
+
});
|
|
444
|
+
}
|
|
445
|
+
finally {
|
|
446
|
+
await objectStorageInstance.bridge.terminate();
|
|
447
|
+
}
|
|
448
|
+
}
|
|
449
|
+
// INSTANCE
|
|
450
|
+
config;
|
|
451
|
+
localBinaryPaths;
|
|
452
|
+
bridge;
|
|
453
|
+
running = false;
|
|
454
|
+
completedStopResult;
|
|
455
|
+
startPromise;
|
|
456
|
+
stopPromise;
|
|
457
|
+
bridgeOwned = false;
|
|
458
|
+
stopRequested = false;
|
|
459
|
+
stopCompleted = false;
|
|
460
|
+
constructor(configArg = {}) {
|
|
461
|
+
this.config = mergeConfig(configArg);
|
|
462
|
+
this.localBinaryPaths = getRustStorageBinaryNames().map((binaryNameArg) => plugins.path.join(paths.packageDir, 'dist_rust', binaryNameArg));
|
|
463
|
+
this.bridge = new plugins.RustBridge({
|
|
464
|
+
binaryName: 'ruststorage',
|
|
465
|
+
localPaths: this.localBinaryPaths,
|
|
466
|
+
readyTimeoutMs: 30000,
|
|
467
|
+
requestTimeoutMs: 300000,
|
|
468
|
+
maxPayloadSize: MAXIMUM_MANAGEMENT_PAYLOAD_BYTES,
|
|
469
|
+
});
|
|
470
|
+
}
|
|
471
|
+
async ensureLocalBinariesExecutable() {
|
|
472
|
+
for (const localBinaryPath of this.localBinaryPaths) {
|
|
473
|
+
const binaryStat = await plugins.fs.stat(localBinaryPath).catch((errorArg) => {
|
|
474
|
+
if (errorArg.code !== 'ENOENT') {
|
|
475
|
+
throw errorArg;
|
|
476
|
+
}
|
|
477
|
+
return null;
|
|
478
|
+
});
|
|
479
|
+
if (!binaryStat || (binaryStat.mode & 0o111) !== 0) {
|
|
480
|
+
continue;
|
|
481
|
+
}
|
|
482
|
+
await plugins.fs.chmod(localBinaryPath, binaryStat.mode | 0o755);
|
|
483
|
+
}
|
|
484
|
+
}
|
|
485
|
+
start() {
|
|
486
|
+
if (this.stopRequested && !this.stopCompleted) {
|
|
487
|
+
return Promise.reject(new Error('Storage shutdown must complete before restarting.'));
|
|
488
|
+
}
|
|
489
|
+
if (this.startPromise) {
|
|
490
|
+
return this.startPromise;
|
|
491
|
+
}
|
|
492
|
+
if (this.running) {
|
|
493
|
+
return Promise.resolve();
|
|
494
|
+
}
|
|
495
|
+
if (this.bridgeOwned) {
|
|
496
|
+
return Promise.reject(new Error('Storage startup cleanup must complete before restarting.'));
|
|
497
|
+
}
|
|
498
|
+
this.stopRequested = false;
|
|
499
|
+
this.stopCompleted = false;
|
|
500
|
+
this.stopPromise = undefined;
|
|
501
|
+
this.completedStopResult = undefined;
|
|
502
|
+
const start = this.startInternal();
|
|
503
|
+
this.startPromise = start;
|
|
504
|
+
const releaseStart = () => {
|
|
505
|
+
if (this.startPromise === start) {
|
|
506
|
+
this.startPromise = undefined;
|
|
507
|
+
}
|
|
508
|
+
};
|
|
509
|
+
start.then(releaseStart, releaseStart);
|
|
510
|
+
return start;
|
|
511
|
+
}
|
|
512
|
+
async startInternal() {
|
|
513
|
+
try {
|
|
514
|
+
await this.ensureLocalBinariesExecutable();
|
|
515
|
+
// A failed spawn can still own a transport whose termination needs retrying.
|
|
516
|
+
this.bridgeOwned = true;
|
|
517
|
+
const spawned = await this.bridge.spawn();
|
|
518
|
+
if (!spawned) {
|
|
519
|
+
throw new Error('Failed to spawn ruststorage binary. Make sure it is compiled (pnpm build).');
|
|
520
|
+
}
|
|
521
|
+
await this.bridge.sendCommand('start', { config: this.config });
|
|
522
|
+
}
|
|
523
|
+
catch (error) {
|
|
524
|
+
try {
|
|
525
|
+
if (this.bridgeOwned) {
|
|
526
|
+
await this.bridge.terminate();
|
|
527
|
+
this.bridgeOwned = false;
|
|
528
|
+
}
|
|
529
|
+
}
|
|
530
|
+
catch (terminationError) {
|
|
531
|
+
throw new AggregateError([error, terminationError], 'Storage startup failed and Rust bridge termination could not be confirmed.', { cause: error });
|
|
532
|
+
}
|
|
533
|
+
throw error;
|
|
534
|
+
}
|
|
535
|
+
this.running = true;
|
|
536
|
+
if (!this.config.server.silent) {
|
|
537
|
+
console.log('storage server is running');
|
|
538
|
+
}
|
|
539
|
+
}
|
|
540
|
+
async getStorageDescriptor(optionsArg) {
|
|
541
|
+
const cred = this.config.auth.credentials[0] || {
|
|
542
|
+
accessKeyId: 'STORAGE',
|
|
543
|
+
secretAccessKey: 'STORAGE',
|
|
544
|
+
};
|
|
545
|
+
const descriptor = {
|
|
546
|
+
endpoint: this.config.server.address === '0.0.0.0' ? 'localhost' : this.config.server.address,
|
|
547
|
+
port: this.config.server.port,
|
|
548
|
+
useSsl: false,
|
|
549
|
+
accessKey: cred.accessKeyId,
|
|
550
|
+
accessSecret: cred.secretAccessKey,
|
|
551
|
+
bucketName: '',
|
|
552
|
+
};
|
|
553
|
+
return {
|
|
554
|
+
...descriptor,
|
|
555
|
+
...(optionsArg ? optionsArg : {}),
|
|
556
|
+
};
|
|
557
|
+
}
|
|
558
|
+
getEndpoint() {
|
|
559
|
+
return this.config.server.address === '0.0.0.0' ? 'localhost' : this.config.server.address;
|
|
560
|
+
}
|
|
561
|
+
buildBucketTenantDescriptor(credential, bucketNameArg) {
|
|
562
|
+
const bucketName = credential.bucketName || bucketNameArg;
|
|
563
|
+
const region = credential.region || this.config.server.region || 'us-east-1';
|
|
564
|
+
const endpoint = this.getEndpoint();
|
|
565
|
+
const port = this.config.server.port;
|
|
566
|
+
const useSsl = false;
|
|
567
|
+
const descriptor = {
|
|
568
|
+
endpoint,
|
|
569
|
+
port,
|
|
570
|
+
region,
|
|
571
|
+
bucket: bucketName,
|
|
572
|
+
bucketName,
|
|
573
|
+
accessKeyId: credential.accessKeyId,
|
|
574
|
+
secretAccessKey: credential.secretAccessKey,
|
|
575
|
+
accessKey: credential.accessKeyId,
|
|
576
|
+
accessSecret: credential.secretAccessKey,
|
|
577
|
+
useSsl,
|
|
578
|
+
ssl: useSsl,
|
|
579
|
+
env: {
|
|
580
|
+
S3_ENDPOINT: endpoint,
|
|
581
|
+
S3_PORT: String(port),
|
|
582
|
+
S3_REGION: region,
|
|
583
|
+
S3_BUCKET: bucketName,
|
|
584
|
+
S3_ACCESS_KEY_ID: credential.accessKeyId,
|
|
585
|
+
S3_SECRET_ACCESS_KEY: credential.secretAccessKey,
|
|
586
|
+
S3_USE_SSL: String(useSsl),
|
|
587
|
+
AWS_ACCESS_KEY_ID: credential.accessKeyId,
|
|
588
|
+
AWS_SECRET_ACCESS_KEY: credential.secretAccessKey,
|
|
589
|
+
AWS_REGION: region,
|
|
590
|
+
},
|
|
591
|
+
};
|
|
592
|
+
if (credential.resourceFence) {
|
|
593
|
+
descriptor.resourceFence = credential.resourceFence;
|
|
594
|
+
}
|
|
595
|
+
if (credential.retention) {
|
|
596
|
+
descriptor.retention = credential.retention;
|
|
597
|
+
}
|
|
598
|
+
return descriptor;
|
|
599
|
+
}
|
|
600
|
+
assertTenantAuthEnabled() {
|
|
601
|
+
if (!this.config.auth.enabled) {
|
|
602
|
+
throw new Error('Bucket tenant APIs require auth.enabled=true.');
|
|
603
|
+
}
|
|
604
|
+
}
|
|
605
|
+
async createBucket(bucketNameArg) {
|
|
606
|
+
try {
|
|
607
|
+
await this.bridge.sendCommand('createBucket', { name: bucketNameArg });
|
|
608
|
+
return { name: bucketNameArg };
|
|
609
|
+
}
|
|
610
|
+
catch (error) {
|
|
611
|
+
throw normalizeResourceFenceError(error);
|
|
612
|
+
}
|
|
613
|
+
}
|
|
614
|
+
async createBucketTenant(tenantArg) {
|
|
615
|
+
this.assertTenantAuthEnabled();
|
|
616
|
+
try {
|
|
617
|
+
const credential = await this.bridge.sendCommand('createBucketTenant', {
|
|
618
|
+
bucketName: tenantArg.bucketName,
|
|
619
|
+
accessKeyId: tenantArg.accessKeyId || createAccessKeyId(),
|
|
620
|
+
secretAccessKey: tenantArg.secretAccessKey || createSecretAccessKey(),
|
|
621
|
+
region: tenantArg.region || this.config.server.region,
|
|
622
|
+
...(tenantArg.retention ? { retention: tenantArg.retention } : {}),
|
|
623
|
+
});
|
|
624
|
+
return this.buildBucketTenantDescriptor(credential, tenantArg.bucketName);
|
|
625
|
+
}
|
|
626
|
+
catch (error) {
|
|
627
|
+
throw normalizeResourceFenceError(error);
|
|
628
|
+
}
|
|
629
|
+
}
|
|
630
|
+
async ensureBucketTenant(tenantArg) {
|
|
631
|
+
this.assertTenantAuthEnabled();
|
|
632
|
+
try {
|
|
633
|
+
const credential = await this.bridge.sendCommand('ensureBucketTenant', {
|
|
634
|
+
...tenantArg,
|
|
635
|
+
region: tenantArg.region || this.config.server.region,
|
|
636
|
+
});
|
|
637
|
+
return this.buildBucketTenantDescriptor(credential, tenantArg.bucketName);
|
|
638
|
+
}
|
|
639
|
+
catch (error) {
|
|
640
|
+
throw normalizeResourceFenceError(error);
|
|
641
|
+
}
|
|
642
|
+
}
|
|
643
|
+
async deleteBucketTenant(tenantArg) {
|
|
644
|
+
this.assertTenantAuthEnabled();
|
|
645
|
+
try {
|
|
646
|
+
await this.bridge.sendCommand('deleteBucketTenant', tenantArg);
|
|
647
|
+
}
|
|
648
|
+
catch (error) {
|
|
649
|
+
throw normalizeResourceFenceError(error);
|
|
650
|
+
}
|
|
651
|
+
}
|
|
652
|
+
async rotateBucketTenantCredentials(tenantArg) {
|
|
653
|
+
this.assertTenantAuthEnabled();
|
|
654
|
+
try {
|
|
655
|
+
const credential = await this.bridge.sendCommand('rotateBucketTenantCredentials', {
|
|
656
|
+
bucketName: tenantArg.bucketName,
|
|
657
|
+
accessKeyId: tenantArg.accessKeyId || createAccessKeyId(),
|
|
658
|
+
secretAccessKey: tenantArg.secretAccessKey || createSecretAccessKey(),
|
|
659
|
+
region: tenantArg.region || this.config.server.region,
|
|
660
|
+
});
|
|
661
|
+
return this.buildBucketTenantDescriptor(credential, tenantArg.bucketName);
|
|
662
|
+
}
|
|
663
|
+
catch (error) {
|
|
664
|
+
throw normalizeResourceFenceError(error);
|
|
665
|
+
}
|
|
666
|
+
}
|
|
667
|
+
async listBucketTenants() {
|
|
668
|
+
this.assertTenantAuthEnabled();
|
|
669
|
+
return this.bridge.sendCommand('listBucketTenants', {});
|
|
670
|
+
}
|
|
671
|
+
async getBucketTenantDescriptor(optionsArg) {
|
|
672
|
+
this.assertTenantAuthEnabled();
|
|
673
|
+
const credential = await this.bridge.sendCommand('getBucketTenantCredential', {
|
|
674
|
+
bucketName: optionsArg.bucketName,
|
|
675
|
+
});
|
|
676
|
+
return this.buildBucketTenantDescriptor(credential, optionsArg.bucketName);
|
|
677
|
+
}
|
|
678
|
+
async getBucketRetentionCapability() {
|
|
679
|
+
return this.bridge.sendCommand('getBucketRetentionCapability', {});
|
|
680
|
+
}
|
|
681
|
+
async getBucketRetentionReceipt(optionsArg) {
|
|
682
|
+
return this.bridge.sendCommand('getBucketRetentionReceipt', optionsArg);
|
|
683
|
+
}
|
|
684
|
+
async getBucketMigrationCapability() {
|
|
685
|
+
return this.bridge.sendCommand('getBucketMigrationCapability', {});
|
|
686
|
+
}
|
|
687
|
+
async createSourceMigration(optionsArg) {
|
|
688
|
+
return this.bridge.sendCommand('createSourceMigration', optionsArg);
|
|
689
|
+
}
|
|
690
|
+
async createDestinationMigration(optionsArg) {
|
|
691
|
+
return this.bridge.sendCommand('createDestinationMigration', optionsArg);
|
|
692
|
+
}
|
|
693
|
+
async transitionSourceMigration(optionsArg) {
|
|
694
|
+
return this.bridge.sendCommand('transitionSourceMigration', optionsArg);
|
|
695
|
+
}
|
|
696
|
+
async transitionDestinationMigration(optionsArg) {
|
|
697
|
+
return this.bridge.sendCommand('transitionDestinationMigration', optionsArg);
|
|
698
|
+
}
|
|
699
|
+
async cleanupSourceMigration(optionsArg) {
|
|
700
|
+
try {
|
|
701
|
+
return await this.bridge.sendCommand('cleanupSourceMigration', optionsArg);
|
|
702
|
+
}
|
|
703
|
+
catch (error) {
|
|
704
|
+
throw normalizeResourceFenceError(error);
|
|
705
|
+
}
|
|
706
|
+
}
|
|
707
|
+
async inspectBucketMigration(optionsArg) {
|
|
708
|
+
return this.bridge.sendCommand('inspectBucketMigration', optionsArg);
|
|
709
|
+
}
|
|
710
|
+
async exportBucket(optionsArg) {
|
|
711
|
+
return this.bridge.sendCommand('exportBucket', { bucketName: optionsArg.bucketName });
|
|
712
|
+
}
|
|
713
|
+
async importBucket(optionsArg) {
|
|
714
|
+
try {
|
|
715
|
+
return await this.bridge.sendCommand('importBucket', optionsArg);
|
|
716
|
+
}
|
|
717
|
+
catch (error) {
|
|
718
|
+
throw normalizeResourceFenceError(error);
|
|
719
|
+
}
|
|
720
|
+
}
|
|
721
|
+
async deleteBucket(optionsArg) {
|
|
722
|
+
try {
|
|
723
|
+
return await this.bridge.sendCommand('deleteBucket', optionsArg);
|
|
724
|
+
}
|
|
725
|
+
catch (error) {
|
|
726
|
+
throw normalizeResourceFenceError(error);
|
|
727
|
+
}
|
|
728
|
+
}
|
|
729
|
+
async commitResourcePublication(optionsArg) {
|
|
730
|
+
try {
|
|
731
|
+
return await this.bridge.sendCommand('commitResourcePublication', optionsArg);
|
|
732
|
+
}
|
|
733
|
+
catch (error) {
|
|
734
|
+
throw normalizeResourceFenceError(error);
|
|
735
|
+
}
|
|
736
|
+
}
|
|
737
|
+
async getResourceFencingCapability() {
|
|
738
|
+
return this.bridge.sendCommand('getResourceFencingCapability', {});
|
|
739
|
+
}
|
|
740
|
+
async getStorageStats() {
|
|
741
|
+
return this.bridge.sendCommand('getStorageStats', {});
|
|
742
|
+
}
|
|
743
|
+
async getStoragePoolDiagnostic() {
|
|
744
|
+
return this.bridge.sendCommand('getStoragePoolDiagnostic', {});
|
|
745
|
+
}
|
|
746
|
+
async listBucketSummaries() {
|
|
747
|
+
return this.bridge.sendCommand('listBucketSummaries', {});
|
|
748
|
+
}
|
|
749
|
+
async listCredentials() {
|
|
750
|
+
return this.bridge.sendCommand('listCredentials', {});
|
|
751
|
+
}
|
|
752
|
+
/**
|
|
753
|
+
* Trusted process-local administrative override. Resource lifecycle code
|
|
754
|
+
* must use fenced ensure/delete instead; completed exact receipts fail closed
|
|
755
|
+
* if this override introduces ownership or credential-material drift.
|
|
756
|
+
*/
|
|
757
|
+
async replaceCredentials(credentials) {
|
|
758
|
+
await this.bridge.sendCommand('replaceCredentials', { credentials });
|
|
759
|
+
this.config.auth.credentials = credentials.map((credential) => ({ ...credential }));
|
|
760
|
+
}
|
|
761
|
+
async getClusterHealth() {
|
|
762
|
+
return this.bridge.sendCommand('getClusterHealth', {});
|
|
763
|
+
}
|
|
764
|
+
async getHealth() {
|
|
765
|
+
if (!this.running) {
|
|
766
|
+
return {
|
|
767
|
+
ok: false,
|
|
768
|
+
running: false,
|
|
769
|
+
storageDirectory: this.config.storage.directory || paths.bucketsDir,
|
|
770
|
+
storageLocations: [],
|
|
771
|
+
auth: {
|
|
772
|
+
enabled: this.config.auth.enabled,
|
|
773
|
+
credentialCount: this.config.auth.credentials.length,
|
|
774
|
+
tenantCredentialCount: 0,
|
|
775
|
+
},
|
|
776
|
+
bucketCount: 0,
|
|
777
|
+
objectCount: 0,
|
|
778
|
+
totalBytes: 0,
|
|
779
|
+
cluster: { enabled: false },
|
|
780
|
+
resourceFencingVersion: 1,
|
|
781
|
+
resourceFencingSupported: false,
|
|
782
|
+
resourceFencingRequiresDrain: true,
|
|
783
|
+
resourcePublicationHoldVersion: 1,
|
|
784
|
+
resourcePublicationHoldSupported: false,
|
|
785
|
+
resourcePublicationHoldRequiresExternalDrain: false,
|
|
786
|
+
};
|
|
787
|
+
}
|
|
788
|
+
const [stats, credentials, tenants, cluster, resourceFencing] = await Promise.all([
|
|
789
|
+
this.getStorageStats(),
|
|
790
|
+
this.listCredentials(),
|
|
791
|
+
this.config.auth.enabled ? this.listBucketTenants() : Promise.resolve([]),
|
|
792
|
+
this.getClusterHealth(),
|
|
793
|
+
this.getResourceFencingCapability(),
|
|
794
|
+
]);
|
|
795
|
+
return {
|
|
796
|
+
ok: true,
|
|
797
|
+
running: true,
|
|
798
|
+
storageDirectory: stats.storageDirectory,
|
|
799
|
+
storageLocations: stats.storageLocations || [],
|
|
800
|
+
auth: {
|
|
801
|
+
enabled: this.config.auth.enabled,
|
|
802
|
+
credentialCount: credentials.length,
|
|
803
|
+
tenantCredentialCount: tenants.length,
|
|
804
|
+
},
|
|
805
|
+
bucketCount: stats.bucketCount,
|
|
806
|
+
objectCount: stats.totalObjectCount,
|
|
807
|
+
totalBytes: stats.totalStorageBytes,
|
|
808
|
+
cluster,
|
|
809
|
+
resourceFencingVersion: resourceFencing.version,
|
|
810
|
+
resourceFencingSupported: resourceFencing.supported,
|
|
811
|
+
resourceFencingRequiresDrain: resourceFencing.requiresDrain,
|
|
812
|
+
resourcePublicationHoldVersion: resourceFencing.publicationHoldVersion,
|
|
813
|
+
resourcePublicationHoldSupported: resourceFencing.publicationHoldSupported,
|
|
814
|
+
resourcePublicationHoldRequiresExternalDrain: resourceFencing.publicationHoldRequiresExternalDrain,
|
|
815
|
+
resourceFencingReason: resourceFencing.reason,
|
|
816
|
+
};
|
|
817
|
+
}
|
|
818
|
+
async getMetrics() {
|
|
819
|
+
const health = await this.getHealth();
|
|
820
|
+
const clusterEnabled = health.cluster.enabled;
|
|
821
|
+
return {
|
|
822
|
+
bucketCount: health.bucketCount,
|
|
823
|
+
objectCount: health.objectCount,
|
|
824
|
+
totalBytes: health.totalBytes,
|
|
825
|
+
authCredentialCount: health.auth.credentialCount,
|
|
826
|
+
tenantCredentialCount: health.auth.tenantCredentialCount,
|
|
827
|
+
clusterEnabled,
|
|
828
|
+
prometheusText: [
|
|
829
|
+
'# HELP objectstorage_buckets_total Runtime bucket count.',
|
|
830
|
+
'# TYPE objectstorage_buckets_total gauge',
|
|
831
|
+
`objectstorage_buckets_total ${health.bucketCount}`,
|
|
832
|
+
'# HELP objectstorage_objects_total Runtime object count.',
|
|
833
|
+
'# TYPE objectstorage_objects_total gauge',
|
|
834
|
+
`objectstorage_objects_total ${health.objectCount}`,
|
|
835
|
+
'# HELP objectstorage_storage_bytes_total Runtime storage bytes.',
|
|
836
|
+
'# TYPE objectstorage_storage_bytes_total gauge',
|
|
837
|
+
`objectstorage_storage_bytes_total ${health.totalBytes}`,
|
|
838
|
+
'# HELP objectstorage_tenant_credentials_total Scoped bucket tenant credential count.',
|
|
839
|
+
'# TYPE objectstorage_tenant_credentials_total gauge',
|
|
840
|
+
`objectstorage_tenant_credentials_total ${health.auth.tenantCredentialCount}`,
|
|
841
|
+
'# HELP objectstorage_cluster_enabled Cluster mode enabled.',
|
|
842
|
+
'# TYPE objectstorage_cluster_enabled gauge',
|
|
843
|
+
`objectstorage_cluster_enabled ${clusterEnabled ? 1 : 0}`,
|
|
844
|
+
].join('\n'),
|
|
845
|
+
};
|
|
846
|
+
}
|
|
847
|
+
/**
|
|
848
|
+
* Retains the Rust bridge when shutdown is incomplete so a later call can
|
|
849
|
+
* retry ownership draining. Completed terminal errors kill the bridge before
|
|
850
|
+
* they are rethrown.
|
|
851
|
+
*/
|
|
852
|
+
stop() {
|
|
853
|
+
if (this.stopPromise) {
|
|
854
|
+
return this.stopPromise;
|
|
855
|
+
}
|
|
856
|
+
this.stopRequested = true;
|
|
857
|
+
const stop = this.stopInternal();
|
|
858
|
+
this.stopPromise = stop;
|
|
859
|
+
stop.then(undefined, () => {
|
|
860
|
+
if (!this.stopCompleted && this.stopPromise === stop) {
|
|
861
|
+
this.stopPromise = undefined;
|
|
862
|
+
}
|
|
863
|
+
});
|
|
864
|
+
return stop;
|
|
865
|
+
}
|
|
866
|
+
async stopInternal() {
|
|
867
|
+
if (this.startPromise) {
|
|
868
|
+
// Startup keeps its own rejection; stop joins only its resource ownership.
|
|
869
|
+
await Promise.allSettled([this.startPromise]);
|
|
870
|
+
}
|
|
871
|
+
if (this.bridgeOwned) {
|
|
872
|
+
if (this.running) {
|
|
873
|
+
this.completedStopResult ??= await this.bridge.sendCommand('stop', {});
|
|
874
|
+
}
|
|
875
|
+
await this.bridge.terminate();
|
|
876
|
+
this.bridgeOwned = false;
|
|
877
|
+
this.running = false;
|
|
878
|
+
}
|
|
879
|
+
this.stopCompleted = true;
|
|
880
|
+
if (this.completedStopResult?.terminalError) {
|
|
881
|
+
throw new Error(`Storage server shutdown completed with terminal errors: ${this.completedStopResult.terminalError}`);
|
|
882
|
+
}
|
|
883
|
+
}
|
|
884
|
+
}
|
|
885
|
+
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiaW5kZXguanMiLCJzb3VyY2VSb290IjoiIiwic291cmNlcyI6WyIuLi90cy9pbmRleC50cyJdLCJuYW1lcyI6W10sIm1hcHBpbmdzIjoiQUFBQSxPQUFPLEtBQUssT0FBTyxNQUFNLGNBQWMsQ0FBQztBQUN4QyxPQUFPLEtBQUssS0FBSyxNQUFNLFlBQVksQ0FBQztBQTJpQnBDLE1BQU0scUJBQXFCLEdBQUc7SUFDNUIsY0FBYyxFQUFFLE1BQU07SUFDdEIsa0JBQWtCLEVBQUUsRUFBRSxHQUFHLElBQUksR0FBRyxJQUFJO0lBQ3BDLGtCQUFrQixFQUFFLEVBQUUsR0FBRyxJQUFJLEdBQUcsSUFBSTtJQUNwQyxlQUFlLEVBQUUsSUFBSTtJQUNyQixzQkFBc0IsRUFBRSxFQUFFO0lBQzFCLG9CQUFvQixFQUFFLENBQUMsR0FBRyxJQUFJO0lBQzlCLG9CQUFvQixFQUFFLElBQUksR0FBRyxJQUFJO0lBQ2pDLHlCQUF5QixFQUFFLElBQUksR0FBRyxJQUFJO0lBQ3RDLHNCQUFzQixFQUFFLEdBQUc7Q0FDbkIsQ0FBQztBQUVYLE1BQU0scUJBQXFCLEdBQUcsQ0FBQyxRQUFnQixFQUFVLEVBQUU7SUFDekQsSUFBSSxDQUFDLE1BQU0sQ0FBQyxhQUFhLENBQUMsUUFBUSxDQUFDLElBQUksUUFBUSxHQUFHLENBQUMsRUFBRSxDQUFDO1FBQ3BELE1BQU0sSUFBSSxLQUFLLENBQUMsOEVBQThFLENBQUMsQ0FBQztJQUNsRyxDQUFDO0lBQ0QsTUFBTSxPQUFPLEdBQUcsTUFBTSxDQUFDLEtBQUssQ0FBQyxDQUFDLENBQUMsQ0FBQztJQUNoQyxPQUFPLENBQUMsZ0JBQWdCLENBQUMsTUFBTSxDQUFDLFFBQVEsQ0FBQyxDQUFDLENBQUM7SUFDM0MsT0FBTyxPQUFPLENBQUM7QUFDakIsQ0FBQyxDQUFDO0FBRUYsTUFBTSxtQkFBbUIsR0FBRyxDQUFDLFFBQWdCLEVBQUUsUUFBZ0IsRUFBVSxFQUFFO0lBQ3pFLElBQUksT0FBTyxRQUFRLEtBQUssUUFBUSxFQUFFLENBQUM7UUFDakMsTUFBTSxJQUFJLEtBQUssQ0FBQyxHQUFHLFFBQVEsb0JBQW9CLENBQUMsQ0FBQztJQUNuRCxDQUFDO0lBQ0QsTUFBTSxPQUFPLEdBQUcsTUFBTSxDQUFDLElBQUksQ0FBQyxRQUFRLEVBQUUsTUFBTSxDQUFDLENBQUM7SUFDOUMsSUFBSSxPQUFPLENBQUMsUUFBUSxDQUFDLE1BQU0sQ0FBQyxLQUFLLFFBQVEsRUFBRSxDQUFDO1FBQzFDLE1BQU0sSUFBSSxLQUFLLENBQUMsR0FBRyxRQUFRLDRDQUE0QyxDQUFDLENBQUM7SUFDM0UsQ0FBQztJQUNELE9BQU8sT0FBTyxDQUFDO0FBQ2pCLENBQUMsQ0FBQztBQUVGLE1BQU0sMEJBQTBCLEdBQUcsQ0FBQyxPQUFlLEVBQUUsUUFBZ0IsRUFBVSxFQUFFLENBQy9FLE1BQU0sQ0FBQyxPQUFPLENBQUMsTUFBTSxDQUFDLElBQUksQ0FBQyxPQUFPLEVBQUUsTUFBTSxDQUFDLEVBQUUsTUFBTSxDQUFDLElBQUksQ0FBQyxRQUFRLEVBQUUsTUFBTSxDQUFDLENBQUMsQ0FBQztBQUU5RSxNQUFNLCtCQUErQixHQUFHLENBQUMsYUFBcUIsRUFBUSxFQUFFO0lBQ3RFLE1BQU0sT0FBTyxHQUFHLG1CQUFtQixDQUFDLGFBQWEsRUFBRSxZQUFZLENBQUMsQ0FBQztJQUNqRSxNQUFNLFNBQVMsR0FBRyxhQUFhLENBQUMsS0FBSyxDQUFDLEdBQUcsQ0FBQyxDQUFDO0lBQzNDLE1BQU0sYUFBYSxHQUFHLFNBQVMsQ0FBQyxNQUFNLEtBQUssQ0FBQyxJQUFJLFNBQVMsQ0FBQyxLQUFLLENBQUMsQ0FBQyxJQUFJLEVBQUUsRUFBRSxDQUN2RSxVQUFVLENBQUMsSUFBSSxDQUFDLElBQUksQ0FBQyxJQUFJLE1BQU0sQ0FBQyxJQUFJLENBQUMsSUFBSSxHQUFHLENBQzdDLENBQUM7SUFDRixJQUNFLE9BQU8sQ0FBQyxNQUFNLEdBQUcsQ0FBQztXQUNmLE9BQU8sQ0FBQyxNQUFNLEdBQUcsRUFBRTtXQUNuQixDQUFDLCtCQUErQixDQUFDLElBQUksQ0FBQyxhQUFhLENBQUM7V0FDcEQsYUFBYSxDQUFDLFFBQVEsQ0FBQyxJQUFJLENBQUM7V0FDNUIsYUFBYSxFQUNoQixDQUFDO1FBQ0QsTUFBTSxJQUFJLEtBQUssQ0FBQywrQ0FBK0MsQ0FBQyxDQUFDO0lBQ25FLENBQUM7QUFDSCxDQUFDLENBQUM7QUFFRixNQUFNLCtCQUErQixHQUFHLENBQUMsUUFBZ0IsRUFBRSxRQUFnQixFQUFRLEVBQUU7SUFDbkYsTUFBTSxPQUFPLEdBQUcsbUJBQW1CLENBQUMsUUFBUSxFQUFFLFFBQVEsQ0FBQyxDQUFDO0lBQ3hELElBQ0UsT0FBTyxDQUFDLE1BQU0sS0FBSyxDQUFDO1dBQ2pCLE9BQU8sQ0FBQyxNQUFNLEdBQUcscUJBQXFCLENBQUMsc0JBQXNCO1dBQzdELENBQUMsZ0NBQWdDLENBQUMsSUFBSSxDQUFDLFFBQVEsQ0FBQyxFQUNuRCxDQUFDO1FBQ0QsTUFBTSxJQUFJLEtBQUssQ0FBQyxHQUFHLFFBQVEsaUNBQWlDLENBQUMsQ0FBQztJQUNoRSxDQUFDO0FBQ0gsQ0FBQyxDQUFDO0FBRUYsTUFBTSxnQ0FBZ0MsR0FBRyxDQUN2QyxXQUFtQyxFQUNuQyxZQUFvQixFQUNaLEVBQUU7SUFDVixJQUFJLENBQUMsV0FBVyxJQUFJLE9BQU8sV0FBVyxLQUFLLFFBQVEsSUFBSSxLQUFLLENBQUMsT0FBTyxDQUFDLFdBQVcsQ0FBQyxFQUFFLENBQUM7UUFDbEYsTUFBTSxJQUFJLEtBQUssQ0FBQyxXQUFXLFlBQVksK0JBQStCLENBQUMsQ0FBQztJQUMxRSxDQUFDO0lBQ0QsTUFBTSxPQUFPLEdBQUcsTUFBTSxDQUFDLE9BQU8sQ0FBQyxXQUFXLENBQUMsQ0FBQztJQUM1QyxJQUFJLE9BQU8sQ0FBQyxNQUFNLEdBQUcscUJBQXFCLENBQUMsc0JBQXNCLEVBQUUsQ0FBQztRQUNsRSxNQUFNLElBQUksS0FBSyxDQUFDLFdBQVcsWUFBWSxrQ0FBa0MsQ0FBQyxDQUFDO0lBQzdFLENBQUM7SUFDRCxLQUFLLE1BQU0sQ0FBQyxHQUFHLEVBQUUsS0FBSyxDQUFDLElBQUksT0FBTyxFQUFFLENBQUM7UUFDbkMsTUFBTSxRQUFRLEdBQUcsbUJBQW1CLENBQUMsR0FBRyxFQUFFLFdBQVcsWUFBWSxnQkFBZ0IsQ0FBQyxDQUFDO1FBQ25GLE1BQU0sVUFBVSxHQUFHLG1CQUFtQixDQUFDLEtBQUssRUFBRSxXQUFXLFlBQVksa0JBQWtCLENBQUMsQ0FBQztRQUN6RixJQUNFLFFBQVEsQ0FBQyxNQUFNLEtBQUssQ0FBQztlQUNsQixRQUFRLENBQUMsTUFBTSxHQUFHLEdBQUc7ZUFDckIsVUFBVSxDQUFDLE1BQU0sR0FBRyxJQUFJO2VBQ3hCLFFBQVEsQ0FBQyxJQUFJLENBQUMsQ0FBQyxJQUFJLEVBQUUsRUFBRSxDQUFDLElBQUksSUFBSSxJQUFJLElBQUksSUFBSSxLQUFLLElBQUksQ0FBQztlQUN0RCxVQUFVLENBQUMsSUFBSSxDQUFDLENBQUMsSUFBSSxFQUFFLEVBQUUsQ0FBQyxDQUFDLElBQUksSUFBSSxJQUFJLElBQUksSUFBSSxLQUFLLElBQUksQ0FBQyxJQUFJLElBQUksS0FBSyxJQUFJLENBQUMsRUFDOUUsQ0FBQztZQUNELE1BQU0sSUFBSSxLQUFLLENBQUMsV0FBVyxZQUFZLHdCQUF3QixDQUFDLENBQUM7UUFDbkUsQ0FBQztJQUNILENBQUM7SUFDRCxPQUFPLENBQUMsSUFBSSxDQUFDLENBQUMsQ0FBQyxJQUFJLENBQUMsRUFBRSxDQUFDLEtBQUssQ0FBQyxFQUFFLEVBQUUsQ0FBQywwQkFBMEIsQ0FBQyxJQUFJLEVBQUUsS0FBSyxDQUFDLENBQUMsQ0FBQztJQUMzRSxNQUFNLE9BQU8sR0FBRyxNQUFNLENBQUMsSUFBSSxDQUN6QixJQUFJLE9BQU8sQ0FBQyxHQUFHLENBQUMsQ0FBQyxDQUFDLEdBQUcsRUFBRSxLQUFLLENBQUMsRUFBRSxFQUFFLENBQUMsR0FBRyxJQUFJLENBQUMsU0FBUyxDQUFDLEdBQUcsQ0FBQyxJQUFJLElBQUksQ0FBQyxTQUFTLENBQUMsS0FBSyxDQUFDLEVBQUUsQ0FBQyxDQUFDLElBQUksQ0FBQyxHQUFHLENBQUMsR0FBRyxFQUNqRyxNQUFNLENBQ1AsQ0FBQztJQUNGLElBQUksT0FBTyxDQUFDLE1BQU0sR0FBRyxxQkFBcUIsQ0FBQyxvQkFBb0IsRUFBRSxDQUFDO1FBQ2hFLE1BQU0sSUFBSSxLQUFLLENBQUMsV0FBVyxZQUFZLG9DQUFvQyxDQUFDLENBQUM7SUFDL0UsQ0FBQztJQUNELE9BQU8sT0FBTyxDQUFDO0FBQ2pCLENBQUMsQ0FBQztBQUVGOzs7O0dBSUc7QUFDSCxNQUFNLENBQUMsTUFBTSx5REFBeUQsR0FBRyxDQUN2RSxVQUF5RCxFQUNqRCxFQUFFO0lBQ1YsSUFBSSxVQUFVLENBQUMsT0FBTyxLQUFLLENBQUMsRUFBRSxDQUFDO1FBQzdCLE1BQU0sSUFBSSxLQUFLLENBQUMsd0VBQXdFLENBQUMsQ0FBQztJQUM1RixDQUFDO0lBQ0QsK0JBQStCLENBQUMsVUFBVSxDQUFDLFVBQVUsQ0FBQyxDQUFDO0lBQ3ZELE1BQU0sSUFBSSxHQUFHLE9BQU8sQ0FBQyxNQUFNLENBQUMsVUFBVSxDQUFDLFFBQVEsQ0FBQyxDQUFDO0lBQ2pELE1BQU0sV0FBVyxHQUFHLENBQUMsUUFBeUIsRUFBUSxFQUFFO1FBQ3RELE1BQU0sS0FBSyxHQUFHLE9BQU8sUUFBUSxLQUFLLFFBQVE7WUFDeEMsQ0FBQyxDQUFDLG1CQUFtQixDQUFDLFFBQVEsRUFBRSw2QkFBNkIsQ0FBQztZQUM5RCxDQUFDLENBQUMsUUFBUSxDQUFDO1FBQ2IsSUFBSSxDQUFDLE1BQU0sQ0FBQyxxQkFBcUIsQ0FBQyxLQUFLLENBQUMsTUFBTSxDQUFDLENBQUMsQ0FBQztRQUNqRCxJQUFJLENBQUMsTUFBTSxDQUFDLEtBQUssQ0FBQyxDQUFDO0lBQ3JCLENBQUMsQ0FBQztJQUVGLElBQUksVUFBVSxDQUFDLElBQUksS0FBSywrQkFBK0IsRUFBRSxDQUFDO1FBQ3hELCtCQUErQixDQUFDLFVBQVUsQ0FBQyxXQUFXLEVBQUUsYUFBYSxDQUFDLENBQUM7UUFDdkUsV0FBVyxDQUFDLFVBQVUsQ0FBQyxJQUFJLENBQUMsQ0FBQztRQUM3QixXQUFXLENBQUMsVUFBVSxDQUFDLFVBQVUsQ0FBQyxDQUFDO1FBQ25DLFdBQVcsQ0FBQyxVQUFVLENBQUMsV0FBVyxDQUFDLENBQUM7UUFDcEMsSUFBSSxVQUFVLENBQUMsZUFBZSxLQUFLLElBQUksRUFBRSxDQUFDO1lBQ3hDLFdBQVcsQ0FBQyxpREFBaUQsQ0FBQyxDQUFDO1lBQy9ELFdBQVcsQ0FBQyxNQUFNLENBQUMsSUFBSSxDQUFDLENBQUMsQ0FBQyxDQUFDLENBQUMsQ0FBQyxDQUFDO1FBQ2hDLENBQUM7UUFDRCxPQUFPLElBQUksQ0FBQyxNQUFNLENBQUMsS0FBSyxDQUFDLENBQUM7SUFDNUIsQ0FBQztJQUNELElBQUksVUFBVSxDQUFDLElBQUksS0FBSywrQkFBK0IsRUFBRSxDQUFDO1FBQ3hELCtCQUErQixDQUFDLFVBQVUsQ0FBQyxXQUFXLEVBQUUsYUFBYSxDQUFDLENBQUM7UUFDdkUsTUFBTSxlQUFlLEdBQUcsbUJBQW1CLENBQ3pDLFVBQVUsQ0FBQyxlQUFlLEVBQzFCLGlCQUFpQixDQUNsQixDQUFDO1FBQ0YsSUFBSSxDQUFDLFVBQVUsQ0FBQyxlQUFlLENBQUMsSUFBSSxFQUFFLEVBQUUsQ0FBQztZQUN2QyxNQUFNLElBQUksS0FBSyxDQUFDLG9DQUFvQyxDQUFDLENBQUM7UUFDeEQsQ0FBQztRQUNELE1BQU0sTUFBTSxHQUFHLG1CQUFtQixDQUFDLFVBQVUsQ0FBQyxNQUFNLEVBQUUsUUFBUSxDQUFDLENBQUM7UUFDaEUsSUFBSSxDQUFDLFVBQVUsQ0FBQyxNQUFNLENBQUMsSUFBSSxFQUFFLEVBQUUsQ0FBQztZQUM5QixNQUFNLElBQUksS0FBSyxDQUFDLDJCQUEyQixDQUFDLENBQUM7UUFDL0MsQ0FBQztRQUNELFdBQVcsQ0FBQyxVQUFVLENBQUMsSUFBSSxDQUFDLENBQUM7UUFDN0IsV0FBVyxDQUFDLFVBQVUsQ0FBQyxVQUFVLENBQUMsQ0FBQztRQUNuQyxXQUFXLENBQUMsVUFBVSxDQUFDLFdBQVcsQ0FBQyxDQUFDO1FBQ3BDLFdBQVcsQ0FBQyxPQUFPLENBQUMsTUFBTSxDQUFDLFVBQVUsQ0FBQyxRQUFRLENBQUMsQ0FBQyxNQUFNLENBQUMsZUFBZSxDQUFDLENBQUMsTUFBTSxDQUFDLEtBQUssQ0FBQyxDQUFDLENBQUM7UUFDdkYsV0FBVyxDQUFDLE1BQU0sQ0FBQyxDQUFDO1FBQ3BCLElBQUksVUFBVSxDQUFDLFNBQVMsRUFBRSxDQUFDO1lBQ3pCLElBQ0UsVUFBVSxDQUFDLFNBQVMsQ0FBQyxJQUFJLEtBQUssWUFBWTttQkFDdkMsQ0FBQyx3QkFBd0IsQ0FBQyxJQUFJLENBQUMsVUFBVSxDQUFDLFNBQVMsQ0FBQyxRQUFRLENBQUM7bUJBQzdELENBQUMsTUFBTSxDQUFDLGFBQWEsQ0FBQyxVQUFVLENBQUMsU0FBUyxDQUFDLHdCQUF3QixDQUFDO21CQUNwRSxVQUFVLENBQUMsU0FBUyxDQUFDLHdCQUF3QixJQUFJLENBQUM7bUJBQ2xELFVBQVUsQ0FBQyxTQUFTLENBQUMsd0JBQXdCLEdBQUcsR0FBRyxHQUFHLEdBQUcsR0FBRyxFQUFFLEdBQUcsRUFBRSxHQUFHLEVBQUUsRUFDM0UsQ0FBQztnQkFDRCxNQUFNLElBQUksS0FBSyxDQUFDLCtEQUErRCxDQUFDLENBQUM7WUFDbkYsQ0FBQztZQUNELFdBQVcsQ0FBQyxrQ0FBa0MsQ0FBQyxDQUFDO1lBQ2hELFdBQVcsQ0FBQyxVQUFVLENBQUMsU0FBUyxDQUFDLElBQUksQ0FBQyxDQUFDO1lBQ3ZDLFdBQVcsQ0FBQyxVQUFVLENBQUMsU0FBUyxDQUFDLFFBQVEsQ0FBQyxDQUFDO1lBQzNDLFdBQVcsQ0FBQyxxQkFBcUIsQ0FBQyxVQUFVLENBQUMsU0FBUyxDQUFDLHdCQUF3QixDQUFDLENBQUMsQ0FBQztRQUNwRixDQUFDO1FBQ0QsT0FBTyxJQUFJLENBQUMsTUFBTSxDQUFDLEtBQUssQ0FBQyxDQUFDO0lBQzVCLENBQUM7SUFDRCxJQUFJLFVBQVUsQ0FBQyxJQUFJLEtBQUssZ0NBQWdDLEVBQUUsQ0FBQztRQUN6RCxNQUFNLElBQUksS0FBSyxDQUFDLHFFQUFxRSxDQUFDLENBQUM7SUFDekYsQ0FBQztJQUNELElBQUksVUFBVSxDQUFDLFdBQVcsS0FBSyxTQUFTLEVBQUUsQ0FBQztRQUN6QywrQkFBK0IsQ0FBQyxVQUFVLENBQUMsV0FBVyxFQUFFLGFBQWEsQ0FBQyxDQUFDO0lBQ3pFLENBQUM7SUFFRCxNQUFNLE1BQU0sR0FBRyxVQUFVLENBQUMsTUFBTSxDQUFDO0lBQ2pDLElBQUksTUFBTSxDQUFDLE1BQU0sS0FBSyx3QkFBd0IsRUFBRSxDQUFDO1FBQy9DLE1BQU0sSUFBSSxLQUFLLENBQUMsK0NBQStDLENBQUMsQ0FBQztJQUNuRSxDQUFDO0lBQ0QsK0JBQStCLENBQUMsTUFBTSxDQUFDLFVBQVUsQ0FBQyxDQUFDO0lBQ25ELElBQUksQ0FBQyxNQUFNLENBQUMsYUFBYSxDQUFDLE1BQU0sQ0FBQyxVQUFVLENBQUMsSUFBSSxNQUFNLENBQUMsVUFBVSxHQUFHLENBQUMsRUFBRSxDQUFDO1FBQ3RFLE1BQU0sSUFBSSxLQUFLLENBQUMsd0RBQXdELENBQUMsQ0FBQztJQUM1RSxDQUFDO0lBQ0QsSUFBSSxDQUFDLEtBQUssQ0FBQyxPQUFPLENBQUMsTUFBTSxDQUFDLE9BQU8sQ0FBQyxJQUFJLE1BQU0sQ0FBQyxPQUFPLENBQUMsTUFBTSxHQUFHLHFCQUFxQixDQUFDLGNBQWMsRUFBRSxDQUFDO1FBQ25HLE1BQU0sSUFBSSxLQUFLLENBQUMsc0JBQXNCLHFCQUFxQixDQUFDLGNBQWMsZ0JBQWdCLENBQUMsQ0FBQztJQUM5RixDQUFDO0lBRUQsTUFBTSxXQUFXLEdBQUcsSUFBSSxHQUFHLEVBQVUsQ0FBQztJQUN0QyxJQUFJLFVBQVUsR0FBRyxDQUFDLENBQUM7SUFDbkIsSUFBSSxhQUFhLEdBQUcsQ0FBQyxDQUFDO0lBQ3RCLElBQUksa0JBQWtCLEdBQUcsQ0FBQyxDQUFDO0lBQzNCLE1BQU0sUUFBUSxHQUFHLE1BQU0sQ0FBQyxPQUFPLENBQUMsR0FBRyxDQUFDLENBQUMsTUFBTSxFQUFFLEVBQUU7UUFDN0MsTUFBTSxRQUFRLEdBQUcsbUJBQW1CLENBQUMsTUFBTSxDQUFDLEdBQUcsRUFBRSxZQUFZLENBQUMsQ0FBQztRQUMvRCxJQUNFLFFBQVEsQ0FBQyxNQUFNLEtBQUssQ0FBQztlQUNsQixRQUFRLENBQUMsTUFBTSxHQUFHLHFCQUFxQixDQUFDLGVBQWUsRUFDMUQsQ0FBQztZQUNELE1BQU0sSUFBSSxLQUFLLENBQ2IsZUFBZSxNQUFNLENBQUMsR0FBRyxnQ0FBZ0MscUJBQXFCLENBQUMsZUFBZSxlQUFlLENBQzlHLENBQUM7UUFDSixDQUFDO1FBQ0QsSUFBSSxXQUFXLENBQUMsR0FBRyxDQUFDLE1BQU0sQ0FBQyxHQUFHLENBQUMsRUFBRSxDQUFDO1lBQ2hDLE1BQU0sSUFBSSxLQUFLLENBQUMseUNBQXlDLE1BQU0sQ0FBQyxHQUFHLElBQUksQ0FBQyxDQUFDO1FBQzNFLENBQUM7UUFDRCxXQUFXLENBQUMsR0FBRyxDQUFDLE1BQU0sQ0FBQyxHQUFHLENBQUMsQ0FBQztRQUM1QixhQUFhLElBQUksUUFBUSxDQUFDLE1BQU0sQ0FBQztRQUNqQyxJQUFJLGFBQWEsR0FBRyxxQkFBcUIsQ0FBQyxvQkFBb0IsRUFBRSxDQUFDO1lBQy9ELE1BQU0sSUFBSSxLQUFLLENBQUMsc0JBQXNCLHFCQUFxQixDQUFDLG9CQUFvQiw0QkFBNEIsQ0FBQyxDQUFDO1FBQ2hILENBQUM7UUFFRCxJQUNFLENBQUMsTUFBTSxDQUFDLGFBQWEsQ0FBQyxNQUFNLENBQUMsSUFBSSxDQUFDO2VBQy9CLE1BQU0sQ0FBQyxJQUFJLEdBQUcsQ0FBQztlQUNmLE1BQU0sQ0FBQyxJQUFJLEdBQUcscUJBQXFCLENBQUMsa0JBQWtCO2VBQ3RELE1BQU0sQ0FBQyxPQUFPLENBQUMsTUFBTSxLQUFLLE1BQU0sQ0FBQyxJQUFJLEdBQUcsQ0FBQztlQUN6QyxDQUFDLGFBQWEsQ0FBQyxJQUFJLENBQUMsTUFBTSxDQUFDLE9BQU8sQ0FBQyxFQUN0QyxDQUFDO1lBQ0QsTUFBTSxJQUFJLEtBQUssQ0FBQyxXQUFXLE1BQU0sQ0FBQyxHQUFHLHFDQUFxQyxDQUFDLENBQUM7UUFDOUUsQ0FBQztRQUNELE1BQU0sSUFBSSxHQUFHLE1BQU0sQ0FBQyxJQUFJLENBQUMsTUFBTSxDQUFDLE9BQU8sRUFBRSxLQUFLLENBQUMsQ0FBQztRQUNoRCxJQUFJLElBQUksQ0FBQyxNQUFNLEtBQUssTUFBTSxDQUFDLElBQUksRUFBRSxDQUFDO1lBQ2hDLE1BQU0sSUFBSSxLQUFLLENBQUMsV0FBVyxNQUFNLENBQUMsR0FBRyxnQ0FBZ0MsQ0FBQyxDQUFDO1FBQ3pFLENBQUM7UUFDRCxJQUFJLENBQUMsZ0JBQWdCLENBQUMsSUFBSSxDQUFDLE1BQU0sQ0FBQyxHQUFHLENBQUMsRUFBRSxDQUFDO1lBQ3ZDLE1BQU0sSUFBSSxLQUFLLENBQUMsV0FBVyxNQUFNLENBQUMsR0FBRyx5QkFBeUIsQ0FBQyxDQUFDO1FBQ2xFLENBQUM7UUFDRCxNQUFNLFNBQVMsR0FBRyxPQUFPLENBQUMsTUFBTSxDQUFDLFVBQVUsQ0FBQyxLQUFLLENBQUMsQ0FBQyxNQUFNLENBQUMsSUFBSSxDQUFDLENBQUMsTUFBTSxDQUFDLEtBQUssQ0FBQyxDQUFDO1FBQzlFLElBQUksU0FBUyxLQUFLLE1BQU0sQ0FBQyxHQUFHLEVBQUUsQ0FBQztZQUM3QixNQUFNLElBQUksS0FBSyxDQUFDLFdBQVcsTUFBTSxDQUFDLEdBQUcsK0JBQStCLENBQUMsQ0FBQztRQUN4RSxDQUFDO1FBRUQsTUFBTSxRQUFRLEdBQUcsZ0NBQWdDLENBQUMsTUFBTSxDQUFDLFFBQVEsRUFBRSxNQUFNLENBQUMsR0FBRyxDQUFDLENBQUM7UUFDL0Usa0JBQWtCLElBQUksUUFBUSxDQUFDLE1BQU0sQ0FBQztRQUN0QyxJQUFJLGtCQUFrQixHQUFHLHFCQUFxQixDQUFDLHlCQUF5QixFQUFFLENBQUM7WUFDekUsTUFBTSxJQUFJLEtBQUssQ0FBQyxzQkFBc0IscUJBQXFCLENBQUMseUJBQXlCLGlDQUFpQyxDQUFDLENBQUM7UUFDMUgsQ0FBQztRQUNELFVBQVUsSUFBSSxNQUFNLENBQUMsSUFBSSxDQUFDO1FBQzFCLElBQUksVUFBVSxHQUFHLHFCQUFxQixDQUFDLGtCQUFrQixFQUFFLENBQUM7WUFDMUQsTUFBTSxJQUFJLEtBQUssQ0FBQyxzQkFBc0IscUJBQXFCLENBQUMsa0JBQWtCLHFCQUFxQixDQUFDLENBQUM7UUFDdkcsQ0FBQztRQUNELE9BQU87WUFDTCxHQUFHLEVBQUUsTUFBTSxDQUFDLEdBQUc7WUFDZixJQUFJLEVBQUUsTUFBTSxDQUFDLElBQUk7WUFDakIsR0FBRyxFQUFFLE1BQU0sQ0FBQyxHQUFHO1lBQ2YsUUFBUTtZQUNSLFVBQVUsRUFBRSxPQUFPLENBQUMsTUFBTSxDQUFDLFVBQVUsQ0FBQyxRQUFRLENBQUMsQ0FBQyxNQUFNLENBQUMsSUFBSSxDQUFDLENBQUMsTUFBTSxDQUFDLEtBQUssQ0FBQztTQUMzRSxDQUFDO0lBQ0osQ0FBQyxDQUFDLENBQUM7SUFDSCxRQUFRLENBQUMsSUFBSSxDQUFDLENBQUMsSUFBSSxFQUFFLEtBQUssRUFBRSxFQUFFLENBQUMsMEJBQTBCLENBQUMsSUFBSSxDQUFDLEdBQUcsRUFBRSxLQUFLLENBQUMsR0FBRyxDQUFDLENBQUMsQ0FBQztJQUVoRixXQUFXLENBQUMsVUFBVSxDQUFDLElBQUksQ0FBQyxDQUFDO0lBQzdCLFdBQVcsQ0FBQyxVQUFVLENBQUMsVUFBVSxDQUFDLENBQUM7SUFDbkMsSUFBSSxVQUFVLENBQUMsV0FBVyxLQUFLLFNBQVMsRUFBRSxDQUFDO1FBQ3pDLFdBQVcsQ0FBQyxVQUFVLENBQUMsV0FBVyxDQUFDLENBQUM7SUFDdEMsQ0FBQztJQUNELFdBQVcsQ0FBQyxxQkFBcUIsQ0FBQyxRQUFRLENBQUMsTUFBTSxDQUFDLENBQUMsQ0FBQztJQUNwRCxLQUFLLE1BQU0sTUFBTSxJQUFJLFFBQVEsRUFBRSxDQUFDO1FBQzlCLFdBQVcsQ0FBQyxNQUFNLENBQUMsR0FBRyxDQUFDLENBQUM7UUFDeEIsV0FBVyxDQUFDLHFCQUFxQixDQUFDLE1BQU0sQ0FBQyxJQUFJLENBQUMsQ0FBQyxDQUFDO1FBQ2hELFdBQVcsQ0FBQyxNQUFNLENBQUMsR0FBRyxDQUFDLENBQUM7UUFDeEIsV0FBVyxDQUFDLE1BQU0sQ0FBQyxRQUFRLENBQUMsQ0FBQztRQUM3QixXQUFXLENBQUMsTUFBTSxDQUFDLFVBQVUsQ0FBQyxDQUFDO0lBQ2pDLENBQUM7SUFDRCxJQUFJLFVBQVUsQ0FBQyxlQUFlLEtBQUssSUFBSSxFQUFFLENBQUM7UUFDeEMsV0FBVyxDQUFDLGlEQUFpRCxDQUFDLENBQUM7UUFDL0QsV0FBVyxDQUFDLE1BQU0sQ0FBQyxJQUFJLENBQUMsQ0FBQyxDQUFDLENBQUMsQ0FBQyxDQUFDLENBQUM7SUFDaEMsQ0FBQztJQUNELE9BQU8sSUFBSSxDQUFDLE1BQU0sQ0FBQyxLQUFLLENBQUMsQ0FBQztBQUM1QixDQUFDLENBQUM7QUF3SUYsTUFBTSxDQUFDLE1BQU0sb0NBQW9DLEdBQUc7SUFDbEQsaUJBQWlCO0lBQ2pCLGNBQWM7SUFDZCx1QkFBdUI7SUFDdkIsb0JBQW9CO0lBQ3BCLDBCQUEwQjtJQUMxQixzQkFBc0I7SUFDdEIsc0JBQXNCO0lBQ3RCLGFBQWE7SUFDYiwyQkFBMkI7SUFDM0IseUJBQXlCO0lBQ3pCLDZCQUE2QjtJQUM3QiwrQkFBK0I7Q0FDdkIsQ0FBQztBQUtYLE1BQU0sT0FBTywrQkFBZ0MsU0FBUSxLQUFLO0lBQ3hDLElBQUksQ0FBdUM7SUFFM0QsWUFDRSxPQUE2QyxFQUM3QyxVQUFrQixFQUNsQixRQUFrQjtRQUVsQixLQUFLLENBQUMsR0FBRyxPQUFPLEtBQUssVUFBVSxFQUFFLEVBQUUsRUFBRSxLQUFLLEVBQUUsUUFBUSxFQUFFLENBQUMsQ0FBQztRQUN4RCxJQUFJLENBQUMsSUFBSSxHQUFHLGlDQUFpQyxDQUFDO1FBQzlDLElBQUksQ0FBQyxJQUFJLEdBQUcsT0FBTyxDQUFDO0lBQ3RCLENBQUM7Q0FDRjtBQUVELE1BQU0sMkJBQTJCLEdBQUcsQ0FBQyxRQUFpQixFQUFTLEVBQUU7SUFDL0QsTUFBTSxLQUFLLEdBQUcsUUFBUSxZQUFZLEtBQUssQ0FBQyxDQUFDLENBQUMsUUFBUSxDQUFDLENBQUMsQ0FBQyxJQUFJLEtBQUssQ0FBQyxNQUFNLENBQUMsUUFBUSxDQUFDLENBQUMsQ0FBQztJQUNqRixNQUFNLEtBQUssR0FBRyw0QkFBNEIsQ0FBQyxJQUFJLENBQUMsS0FBSyxDQUFDLE9BQU8sQ0FBQyxDQUFDO0lBQy9ELElBQ0UsQ0FBQyxLQUFLO1FBQ04sQ0FBQyxvQ0FBb0MsQ0FBQyxRQUFRLENBQzVDLEtBQUssQ0FBQyxDQUFDLENBQXlDLENBQ2pELEVBQ0QsQ0FBQztRQUNELE9BQU8sS0FBSyxDQUFDO0lBQ2YsQ0FBQztJQUNELE9BQU8sSUFBSSwrQkFBK0IsQ0FDeEMsS0FBSyxDQUFDLENBQUMsQ0FBeUMsRUFDaEQsS0FBSyxDQUFDLENBQUMsQ0FBQyxFQUNSLEtBQUssQ0FDTixDQUFDO0FBQ0osQ0FBQyxDQUFDO0FBOEVGOztHQUVHO0FBQ0gsTUFBTSxjQUFjLEdBQXlCO0lBQzNDLE1BQU0sRUFBRTtRQUNOLElBQUksRUFBRSxJQUFJO1FBQ1YsT0FBTyxFQUFFLFNBQVM7UUFDbEIsTUFBTSxFQUFFLEtBQUs7UUFDYixNQUFNLEVBQUUsV0FBVztLQUNwQjtJQUNELE9BQU8sRUFBRTtRQUNQLFNBQVMsRUFBRSxLQUFLLENBQUMsVUFBVTtRQUMzQixVQUFVLEVBQUUsS0FBSztLQUNsQjtJQUNELElBQUksRUFBRTtRQUNKLE9BQU8sRUFBRSxLQUFLO1FBQ2QsV0FBVyxFQUFFO1lBQ1g7Z0JBQ0UsV0FBVyxFQUFFLFNBQVM7Z0JBQ3RCLGVBQWUsRUFBRSxTQUFTO2FBQzNCO1NBQ0Y7S0FDRjtJQUNELElBQUksRUFBRTtRQUNKLE9BQU8sRUFBRSxLQUFLO1FBQ2QsY0FBYyxFQUFFLENBQUMsR0FBRyxDQUFDO1FBQ3JCLGNBQWMsRUFBRSxDQUFDLEtBQUssRUFBRSxNQUFNLEVBQUUsS0FBSyxFQUFFLFFBQVEsRUFBRSxNQUFNLEVBQUUsU0FBUyxDQUFDO1FBQ25FLGNBQWMsRUFBRSxDQUFDLEdBQUcsQ0FBQztRQUNyQixjQUFjLEVBQUUsQ0FBQyxNQUFNLEVBQUUsa0JBQWtCLEVBQUUsa0JBQWtCLENBQUM7UUFDaEUsTUFBTSxFQUFFLEtBQUs7UUFDYixnQkFBZ0IsRUFBRSxLQUFLO0tBQ3hCO0lBQ0QsT0FBTyxFQUFFO1FBQ1AsS0FBSyxFQUFFLE1BQU07UUFDYixNQUFNLEVBQUUsTUFBTTtRQUNkLE9BQU8sRUFBRSxJQUFJO0tBQ2Q7SUFDRCxNQUFNLEVBQUU7UUFDTixhQUFhLEVBQUUsQ0FBQyxHQUFHLElBQUksR0FBRyxJQUFJLEdBQUcsSUFBSSxFQUFFLE1BQU07UUFDN0MsZUFBZSxFQUFFLElBQUk7UUFDckIsY0FBYyxFQUFFLE1BQU0sRUFBRSxZQUFZO0tBQ3JDO0lBQ0QsU0FBUyxFQUFFO1FBQ1QsY0FBYyxFQUFFLENBQUM7UUFDakIsc0JBQXNCLEVBQUUsRUFBRTtLQUMzQjtDQUNGLENBQUM7QUFFRixnRkFBZ0Y7QUFDaEYsa0VBQWtFO0FBQ2xFLE1BQU0sZ0NBQWdDLEdBQUcsR0FBRyxHQUFHLElBQUksR0FBRyxJQUFJLENBQUM7QUFFM0QsU0FBUywyQkFBMkIsQ0FBQyxZQUFvQixFQUFFLFFBQWdCO0lBQ3pFLElBQ0UsWUFBWSxDQUFDLE1BQU0sS0FBSyxDQUFDO1FBQ3pCLENBQUMsT0FBTyxDQUFDLElBQUksQ0FBQyxVQUFVLENBQUMsWUFBWSxDQUFDO1FBQ3RDLE9BQU8sQ0FBQyxJQUFJLENBQUMsU0FBUyxDQUFDLFlBQVksQ0FBQyxLQUFLLFlBQVk7UUFDckQsT0FBTyxDQUFDLElBQUksQ0FBQyxPQUFPLENBQUMsWUFBWSxDQUFDLEtBQUssWUFBWSxFQUNuRCxDQUFDO1FBQ0QsTUFBTSxJQUFJLEtBQUssQ0FDYixHQUFHLFFBQVEsNEhBQTRILENBQ3hJLENBQUM7SUFDSixDQUFDO0FBQ0gsQ0FBQztBQUVEOztHQUVHO0FBQ0gsU0FBUyxXQUFXLENBQUMsVUFBZ0M7SUFDbkQsTUFBTSxXQUFXLEdBQUcsVUFBVSxDQUFDLE9BQU8sSUFBSSxFQUFFLENBQUM7SUFDN0MsTUFBTSxnQkFBZ0IsR0FBRyxjQUFjLENBQUMsT0FBUSxDQUFDLFNBQVUsQ0FBQztJQUM1RCxNQUFNLGVBQWUsR0FBRyxXQUFXLENBQUMsU0FBUyxDQUFDO0lBQzlDLE1BQU0sY0FBYyxHQUFHLFdBQVcsQ0FBQyxJQUFJLENBQUM7SUFDeEMsTUFBTSxxQkFBcUIsR0FBRyxjQUFjLEVBQUUsT0FBTyxDQUFDLElBQUksS0FBSyxXQUFXLENBQUM7SUFFM0UsSUFBSSxxQkFBcUIsRUFBRSxDQUFDO1FBQzFCLDJCQUEyQixDQUN6QixjQUFjLENBQUMsU0FBUyxFQUN4Qix3QkFBd0IsQ0FDekIsQ0FBQztRQUNGLElBQUksZUFBZSxLQUFLLFNBQVMsRUFBRSxDQUFDO1lBQ2xDLDJCQUEyQixDQUFDLGVBQWUsRUFBRSxtQkFBbUIsQ0FBQyxDQUFDO1lBQ2xFLElBQUksZUFBZSxLQUFLLGNBQWMsQ0FBQyxTQUFTLEVBQUUsQ0FBQztnQkFDakQsTUFBTSxJQUFJLEtBQUssQ0FDYixxRkFBcUYsQ0FDdEYsQ0FBQztZQUNKLENBQUM7UUFDSCxDQUFDO0lBQ0gsQ0FBQztJQUVELE1BQU0sa0JBQWtCLEdBQ3RCLGNBQWMsRUFBRSxTQUFTLElBQUksZUFBZSxJQUFJLGdCQUFnQixDQUFDO0lBRW5FLElBQ0UsY0FBYztRQUNkLENBQUMscUJBQXFCO1FBQ3RCLGVBQWU7UUFDZixPQUFPLENBQUMsSUFBSSxDQUFDLE9BQU8sQ0FBQyxjQUFjLENBQUMsU0FBUyxDQUFDO1lBQzVDLE9BQU8sQ0FBQyxJQUFJLENBQUMsT0FBTyxDQUFDLGVBQWUsQ0FBQyxFQUN2QyxDQUFDO1FBQ0QsTUFBTSxJQUFJLEtBQUssQ0FDYiw2RUFBNkUsQ0FDOUUsQ0FBQztJQUNKLENBQUM7SUFFRCxNQUFNLElBQUksR0FBdUIsY0FBYztRQUM3QyxDQUFDLENBQUM7WUFDRSxHQUFHLGNBQWM7WUFDakIsU0FBUyxFQUFFLE9BQU8sQ0FBQyxJQUFJLENBQUMsT0FBTyxDQUFDLGtCQUFrQixDQUFDO1lBQ25ELE9BQU8sRUFBRSxFQUFFLEdBQUcsY0FBYyxDQUFDLE9BQU8sRUFBRTtTQUN2QztRQUNILENBQUMsQ0FBQztZQUNFLEVBQUUsRUFBRSxTQUFTO1lBQ2IsU0FBUyxFQUFFLE9BQU8sQ0FBQyxJQUFJLENBQUMsT0FBTyxDQUFDLGtCQUFrQixDQUFDO1lBQ25ELE9BQU8sRUFBRSxFQUFFLElBQUksRUFBRSxTQUFTLEVBQUU7U0FDN0IsQ0FBQztJQUVOLElBQUksQ0FBQyx3QkFBd0IsQ0FBQyxJQUFJLENBQUMsSUFBSSxDQUFDLEVBQUUsQ0FBQyxFQUFFLENBQUM7UUFDNUMsTUFBTSxJQUFJLEtBQUssQ0FDYixrSEFBa0gsQ0FDbkgsQ0FBQztJQUNKLENBQUM7SUFDRCxJQUNFLElBQUksQ0FBQyxPQUFPLENBQUMsSUFBSSxLQUFLLFdBQVc7UUFDakMsSUFBSSxDQUFDLE9BQU8sQ0FBQyxjQUFjLENBQUMsSUFBSSxFQUFFLENBQUMsTUFBTSxLQUFLLENBQUMsRUFDL0MsQ0FBQztRQUNELE1BQU0sSUFBSSxLQUFLLENBQ2Isc0VBQXNFLENBQ3ZFLENBQUM7SUFDSixDQUFDO0lBQ0QsSUFBSSxJQUFJLENBQUMsT0FBTyxDQUFDLElBQUksS0FBSyxXQUFXLElBQUksV0FBVyxDQUFDLFVBQVUsRUFBRSxDQUFDO1FBQ2hFLE1BQU0sSUFBSSxLQUFLLENBQUMsMERBQTBELENBQUMsQ0FBQztJQUM5RSxDQUFDO0lBRUQsT0FBTztRQUNMLE1BQU0sRUFBRTtZQUNOLEdBQUcsY0FBYyxDQUFDLE1BQU87WUFDekIsR0FBRyxDQUFDLFVBQVUsQ0FBQyxNQUFNLElBQUksRUFBRSxDQUFDO1NBQzdCO1FBQ0QsT0FBTyxFQUFFO1lBQ1AsR0FBRyxjQUFjLENBQUMsT0FBUTtZQUMxQixHQUFHLFdBQVc7WUFDZCxTQUFTLEVBQUUsSUFBSSxDQUFDLFNBQVM7WUFDekIsSUFBSTtTQUNMO1FBQ0QsSUFBSSxFQUFFO1lBQ0osR0FBRyxjQUFjLENBQUMsSUFBSztZQUN2QixHQUFHLENBQUMsVUFBVSxDQUFDLElBQUksSUFBSSxFQUFFLENBQUM7U0FDM0I7UUFDRCxJQUFJLEVBQUU7WUFDSixHQUFHLGNBQWMsQ0FBQyxJQUFLO1lBQ3ZCLEdBQUcsQ0FBQyxVQUFVLENBQUMsSUFBSSxJQUFJLEVBQUUsQ0FBQztTQUMzQjtRQUNELE9BQU8sRUFBRTtZQUNQLEdBQUcsY0FBYyxDQUFDLE9BQVE7WUFDMUIsR0FBRyxDQUFDLFVBQVUsQ0FBQyxPQUFPLElBQUksRUFBRSxDQUFDO1NBQzlCO1FBQ0QsTUFBTSxFQUFFO1lBQ04sR0FBRyxjQUFjLENBQUMsTUFBTztZQUN6QixHQUFHLENBQUMsVUFBVSxDQUFDLE1BQU0sSUFBSSxFQUFFLENBQUM7U0FDN0I7UUFDRCxTQUFTLEVBQUU7WUFDVCxHQUFHLGNBQWMsQ0FBQyxTQUFVO1lBQzVCLEdBQUcsQ0FBQyxVQUFVLENBQUMsU0FBUyxJQUFJLEVBQUUsQ0FBQztTQUNoQztRQUNELEdBQUcsQ0FBQyxVQUFVLENBQUMsT0FBTyxDQUFDLENBQUMsQ0FBQyxFQUFFLE9BQU8sRUFBRSxVQUFVLENBQUMsT0FBTyxFQUFFLENBQUMsQ0FBQyxDQUFDLEVBQUUsQ0FBQztLQUM3QixDQUFDO0FBQ3RDLENBQUM7QUFFRCxTQUFTLGlCQUFpQjtJQUN4QixPQUFPLEtBQUssT0FBTyxDQUFDLE1BQU0sQ0FBQyxXQUFXLENBQUMsRUFBRSxDQUFDLENBQUMsUUFBUSxDQUFDLEtBQUssQ0FBQyxDQUFDLFdBQVcsRUFBRSxFQUFFLENBQUM7QUFDN0UsQ0FBQztBQUVELFNBQVMscUJBQXFCO0lBQzVCLE9BQU8sT0FBTyxDQUFDLE1BQU0sQ0FBQyxXQUFXLENBQUMsRUFBRSxDQUFDLENBQUMsUUFBUSxDQUFDLEtBQUssQ0FBQyxDQUFDO0FBQ3hELENBQUM7QUFFRCxTQUFTLHlCQUF5QjtJQUNoQyxNQUFNLGlCQUFpQixHQUEyQjtRQUNoRCxXQUFXLEVBQUUsYUFBYTtRQUMxQixhQUFhLEVBQUUsYUFBYTtRQUM1QixZQUFZLEVBQUUsYUFBYTtRQUMzQixjQUFjLEVBQUUsYUFBYTtLQUM5QixDQUFDO0lBQ0YsTUFBTSxjQUFjLEdBQUcsaUJBQWlCLENBQUMsR0FBRyxPQUFPLENBQUMsUUFBUSxJQUFJLE9BQU8sQ0FBQyxJQUFJLEVBQUUsQ0FBQyxDQUFDO0lBQ2hGLE9BQU8sQ0FBQyxjQUFjLENBQUMsQ0FBQyxDQUFDLGVBQWUsY0FBYyxFQUFFLENBQUMsQ0FBQyxDQUFDLElBQUksRUFBRSxhQUFhLENBQUM7U0FDNUUsTUFBTSxDQUFDLENBQUMsYUFBYSxFQUEyQixFQUFFLENBQUMsT0FBTyxDQUFDLGFBQWEsQ0FBQyxDQUFDLENBQUM7QUFDaEYsQ0FBQztBQTZHRDs7R0FFRztBQUNILE1BQU0sT0FBTyxhQUFhO0lBQ3hCLFNBQVM7SUFDRixNQUFNLENBQUMsS0FBSyxDQUFDLGNBQWMsQ0FBQyxZQUFrQyxFQUFFO1FBQ3JFLE1BQU0scUJBQXFCLEdBQUcsSUFBSSxhQUFhLENBQUMsU0FBUyxDQUFDLENBQUM7UUFDM0QsTUFBTSxxQkFBcUIsQ0FBQyxLQUFLLEVBQUUsQ0FBQztRQUNwQyxPQUFPLHFCQUFxQixDQUFDO0lBQy9CLENBQUM7SUFFTSxNQUFNLENBQUMsS0FBSyxDQUFDLGdCQUFnQixDQUNsQyxPQUFrQztRQUVsQyxNQUFNLHFCQUFxQixHQUFHLElBQUksYUFBYSxDQUFDO1lBQzlDLE1BQU0sRUFBRSxFQUFFLE1BQU0sRUFBRSxJQUFJLEVBQUU7WUFDeEIsT0FBTyxFQUFFO2dCQUNQLFNBQVMsRUFBRSxPQUFPLENBQUMsU0FBUztnQkFDNUIsVUFBVSxFQUFFLEtBQUs7Z0JBQ2pCLElBQUksRUFBRSxPQUFPO2FBQ2Q7U0FDRixDQUFDLENBQUM7UUFDSCxNQUFNLHFCQUFxQixDQUFDLDZCQUE2QixFQUFFLENBQUM7UUFDNUQsTUFBTSxPQUFPLEdBQUcsTUFBTSxxQkFBcUIsQ0FBQyxNQUFNLENBQUMsS0FBSyxFQUFFLENBQUM7UUFDM0QsSUFBSSxDQUFDLE9BQU8sRUFBRSxDQUFDO1lBQ2IsTUFBTSxJQUFJLEtBQUssQ0FDYiw0RUFBNEUsQ0FDN0UsQ0FBQztRQUNKLENBQUM7UUFDRCxJQUFJLENBQUM7WUFDSCxPQUFPLE1BQU0scUJBQXFCLENBQUMsTUFBTSxDQUFDLFdBQVcsQ0FBQyxrQkFBa0IsRUFBRTtnQkFDeEUsSUFBSSxFQUFFLHFCQUFxQixDQUFDLE1BQU0sQ0FBQyxPQUFPO3FCQUN2QyxJQUFpQzthQUNyQyxDQUFDLENBQUM7UUFDTCxDQUFDO2dCQUFTLENBQUM7WUFDVCxNQUFNLHFCQUFxQixDQUFDLE1BQU0sQ0FBQyxTQUFTLEVBQUUsQ0FBQztRQUNqRCxDQUFDO0lBQ0gsQ0FBQztJQUVELFdBQVc7SUFDSixNQUFNLENBQWlDO0lBQ3RDLGdCQUFnQixDQUFXO0lBQzNCLE1BQU0sQ0FBZ0U7SUFDdEUsT0FBTyxHQUFHLEtBQUssQ0FBQztJQUNoQixtQkFBbUIsQ0FBaUM7SUFDcEQsWUFBWSxDQUE0QjtJQUN4QyxXQUFXLENBQTRCO0lBQ3ZDLFdBQVcsR0FBRyxLQUFLLENBQUM7SUFDcEIsYUFBYSxHQUFHLEtBQUssQ0FBQztJQUN0QixhQUFhLEdBQUcsS0FBSyxDQUFDO0lBRTlCLFlBQVksWUFBa0MsRUFBRTtRQUM5QyxJQUFJLENBQUMsTUFBTSxHQUFHLFdBQVcsQ0FBQyxTQUFTLENBQUMsQ0FBQztRQUNyQyxJQUFJLENBQUMsZ0JBQWdCLEdBQUcseUJBQXlCLEVBQUUsQ0FBQyxHQUFHLENBQUMsQ0FBQyxhQUFhLEVBQUUsRUFBRSxDQUN4RSxPQUFPLENBQUMsSUFBSSxDQUFDLElBQUksQ0FBQyxLQUFLLENBQUMsVUFBVSxFQUFFLFdBQVcsRUFBRSxhQUFhLENBQUMsQ0FDaEUsQ0FBQztRQUNGLElBQUksQ0FBQyxNQUFNLEdBQUcsSUFBSSxPQUFPLENBQUMsVUFBVSxDQUF1QjtZQUN6RCxVQUFVLEVBQUUsYUFBYTtZQUN6QixVQUFVLEVBQUUsSUFBSSxDQUFDLGdCQUFnQjtZQUNqQyxjQUFjLEVBQUUsS0FBSztZQUNyQixnQkFBZ0IsRUFBRSxNQUFNO1lBQ3hCLGNBQWMsRUFBRSxnQ0FBZ0M7U0FDakQsQ0FBQyxDQUFDO0lBQ0wsQ0FBQztJQUVPLEtBQUssQ0FBQyw2QkFBNkI7UUFDekMsS0FBSyxNQUFNLGVBQWUsSUFBSSxJQUFJLENBQUMsZ0JBQWdCLEVBQUUsQ0FBQztZQUNwRCxNQUFNLFVBQVUsR0FBRyxNQUFNLE9BQU8sQ0FBQyxFQUFFLENBQUMsSUFBSSxDQUFDLGVBQWUsQ0FBQyxDQUFDLEtBQUssQ0FBQyxDQUFDLFFBQStCLEVBQUUsRUFBRTtnQkFDbEcsSUFBSSxRQUFRLENBQUMsSUFBSSxLQUFLLFFBQVEsRUFBRSxDQUFDO29CQUMvQixNQUFNLFFBQVEsQ0FBQztnQkFDakIsQ0FBQztnQkFDRCxPQUFPLElBQUksQ0FBQztZQUNkLENBQUMsQ0FBQyxDQUFDO1lBQ0gsSUFBSSxDQUFDLFVBQVUsSUFBSSxDQUFDLFVBQVUsQ0FBQyxJQUFJLEdBQUcsS0FBSyxDQUFDLEtBQUssQ0FBQyxFQUFFLENBQUM7Z0JBQ25ELFNBQVM7WUFDWCxDQUFDO1lBQ0QsTUFBTSxPQUFPLENBQUMsRUFBRSxDQUFDLEtBQUssQ0FBQyxlQUFlLEVBQUUsVUFBVSxDQUFDLElBQUksR0FBRyxLQUFLLENBQUMsQ0FBQztRQUNuRSxDQUFDO0lBQ0gsQ0FBQztJQUVNLEtBQUs7UUFDVixJQUFJLElBQUksQ0FBQyxhQUFhLElBQUksQ0FBQyxJQUFJLENBQUMsYUFBYSxFQUFFLENBQUM7WUFDOUMsT0FBTyxPQUFPLENBQUMsTUFBTSxDQUFDLElBQUksS0FBSyxDQUFDLG1EQUFtRCxDQUFDLENBQUMsQ0FBQztRQUN4RixDQUFDO1FBQ0QsSUFBSSxJQUFJLENBQUMsWUFBWSxFQUFFLENBQUM7WUFDdEIsT0FBTyxJQUFJLENBQUMsWUFBWSxDQUFDO1FBQzNCLENBQUM7UUFDRCxJQUFJLElBQUksQ0FBQyxPQUFPLEVBQUUsQ0FBQztZQUNqQixPQUFPLE9BQU8sQ0FBQyxPQUFPLEVBQUUsQ0FBQztRQUMzQixDQUFDO1FBQ0QsSUFBSSxJQUFJLENBQUMsV0FBVyxFQUFFLENBQUM7WUFDckIsT0FBTyxPQUFPLENBQUMsTUFBTSxDQUFDLElBQUksS0FBSyxDQUFDLDBEQUEwRCxDQUFDLENBQUMsQ0FBQztRQUMvRixDQUFDO1FBQ0QsSUFBSSxDQUFDLGFBQWEsR0FBRyxLQUFLLENBQUM7UUFDM0IsSUFBSSxDQUFDLGFBQWEsR0FBRyxLQUFLLENBQUM7UUFDM0IsSUFBSSxDQUFDLFdBQVcsR0FBRyxTQUFTLENBQUM7UUFDN0IsSUFBSSxDQUFDLG1CQUFtQixHQUFHLFNBQVMsQ0FBQztRQUNyQyxNQUFNLEtBQUssR0FBRyxJQUFJLENBQUMsYUFBYSxFQUFFLENBQUM7UUFDbkMsSUFBSSxDQUFDLFlBQVksR0FBRyxLQUFLLENBQUM7UUFDMUIsTUFBTSxZQUFZLEdBQUcsR0FBRyxFQUFFO1lBQ3hCLElBQUksSUFBSSxDQUFDLFlBQVksS0FBSyxLQUFLLEVBQUUsQ0FBQztnQkFDaEMsSUFBSSxDQUFDLFlBQVksR0FBRyxTQUFTLENBQUM7WUFDaEMsQ0FBQztRQUNILENBQUMsQ0FBQztRQUNGLEtBQUssQ0FBQyxJQUFJLENBQUMsWUFBWSxFQUFFLFlBQVksQ0FBQyxDQUFDO1FBQ3ZDLE9BQU8sS0FBSyxDQUFDO0lBQ2YsQ0FBQztJQUVPLEtBQUssQ0FBQyxhQUFhO1FBQ3pCLElBQUksQ0FBQztZQUNILE1BQU0sSUFBSSxDQUFDLDZCQUE2QixFQUFFLENBQUM7WUFDM0MsNkVBQTZFO1lBQzdFLElBQUksQ0FBQyxXQUFXLEdBQUcsSUFBSSxDQUFDO1lBQ3hCLE1BQU0sT0FBTyxHQUFHLE1BQU0sSUFBSSxDQUFDLE1BQU0sQ0FBQyxLQUFLLEVBQUUsQ0FBQztZQUMxQyxJQUFJLENBQUMsT0FBTyxFQUFFLENBQUM7Z0JBQ2IsTUFBTSxJQUFJLEtBQUssQ0FBQyw0RUFBNEUsQ0FBQyxDQUFDO1lBQ2hHLENBQUM7WUFDRCxNQUFNLElBQUksQ0FBQyxNQUFNLENBQUMsV0FBVyxDQUFDLE9BQU8sRUFBRSxFQUFFLE1BQU0sRUFBRSxJQUFJLENBQUMsTUFBTSxFQUFFLENBQUMsQ0FBQztRQUNsRSxDQUFDO1FBQUMsT0FBTyxLQUFLLEVBQUUsQ0FBQztZQUNmLElBQUksQ0FBQztnQkFDSCxJQUFJLElBQUksQ0FBQyxXQUFXLEVBQUUsQ0FBQztvQkFDckIsTUFBTSxJQUFJLENBQUMsTUFBTSxDQUFDLFNBQVMsRUFBRSxDQUFDO29CQUM5QixJQUFJLENBQUMsV0FBVyxHQUFHLEtBQUssQ0FBQztnQkFDM0IsQ0FBQztZQUNILENBQUM7WUFBQyxPQUFPLGdCQUFnQixFQUFFLENBQUM7Z0JBQzFCLE1BQU0sSUFBSSxjQUFjLENBQ3RCLENBQUMsS0FBSyxFQUFFLGdCQUFnQixDQUFDLEVBQ3pCLDRFQUE0RSxFQUM1RSxFQUFFLEtBQUssRUFBRSxLQUFLLEVBQUUsQ0FDakIsQ0FBQztZQUNKLENBQUM7WUFDRCxNQUFNLEtBQUssQ0FBQztRQUNkLENBQUM7UUFDRCxJQUFJLENBQUMsT0FBTyxHQUFHLElBQUksQ0FBQztRQUVwQixJQUFJLENBQUMsSUFBSSxDQUFDLE1BQU0sQ0FBQyxNQUFNLENBQUMsTUFBTSxFQUFFLENBQUM7WUFDL0IsT0FBTyxDQUFDLEdBQUcsQ0FBQywyQkFBMkIsQ0FBQyxDQUFDO1FBQzNDLENBQUM7SUFDSCxDQUFDO0lBRU0sS0FBSyxDQUFDLG9CQUFvQixDQUMvQixVQUEyRDtRQUUzRCxNQUFNLElBQUksR0FBRyxJQUFJLENBQUMsTUFBTSxDQUFDLElBQUksQ0FBQyxXQUFXLENBQUMsQ0FBQyxDQUFDLElBQUk7WUFDOUMsV0FBVyxFQUFFLFNBQVM7WUFDdEIsZUFBZSxFQUFFLFNBQVM7U0FDM0IsQ0FBQztRQUVGLE1BQU0sVUFBVSxHQUEwQztZQUN4RCxRQUFRLEVBQUUsSUFBSSxDQUFDLE1BQU0sQ0FBQyxNQUFNLENBQUMsT0FBTyxLQUFLLFNBQVMsQ0FBQyxDQUFDLENBQUMsV0FBVyxDQUFDLENBQUMsQ0FBQyxJQUFJLENBQUMsTUFBTSxDQUFDLE1BQU0sQ0FBQyxPQUFRO1lBQzlGLElBQUksRUFBRSxJQUFJLENBQUMsTUFBTSxDQUFDLE1BQU0sQ0FBQyxJQUFLO1lBQzlCLE1BQU0sRUFBRSxLQUFLO1lBQ2IsU0FBUyxFQUFFLElBQUksQ0FBQyxXQUFXO1lBQzNCLFlBQVksRUFBRSxJQUFJLENBQUMsZUFBZTtZQUNsQyxVQUFVLEVBQUUsRUFBRTtTQUNmLENBQUM7UUFFRixPQUFPO1lBQ0wsR0FBRyxVQUFVO1lBQ2IsR0FBRyxDQUFDLFVBQVUsQ0FBQyxDQUFDLENBQUMsVUFBVSxDQUFDLENBQUMsQ0FBQyxFQUFFLENBQUM7U0FDbEMsQ0FBQztJQUNKLENBQUM7SUFFTyxXQUFXO1FBQ2pCLE9BQU8sSUFBSSxDQUFDLE1BQU0sQ0FBQyxNQUFNLENBQUMsT0FBTyxLQUFLLFNBQVMsQ0FBQyxDQUFDLENBQUMsV0FBVyxDQUFDLENBQUMsQ0FBQyxJQUFJLENBQUMsTUFBTSxDQUFDLE1BQU0sQ0FBQyxPQUFRLENBQUM7SUFDOUYsQ0FBQztJQUVPLDJCQUEyQixDQUNqQyxVQUdDLEVBQ0QsYUFBcUI7UUFFckIsTUFBTSxVQUFVLEdBQUcsVUFBVSxDQUFDLFVBQVUsSUFBSSxhQUFhLENBQUM7UUFDMUQsTUFBTSxNQUFNLEdBQUcsVUFBVSxDQUFDLE1BQU0sSUFBSSxJQUFJLENBQUMsTUFBTSxDQUFDLE1BQU0sQ0FBQyxNQUFNLElBQUksV0FBVyxDQUFDO1FBQzdFLE1BQU0sUUFBUSxHQUFHLElBQUksQ0FBQyxXQUFXLEVBQUUsQ0FBQztRQUNwQyxNQUFNLElBQUksR0FBRyxJQUFJLENBQUMsTUFBTSxDQUFDLE1BQU0sQ0FBQyxJQUFLLENBQUM7UUFDdEMsTUFBTSxNQUFNLEdBQUcsS0FBSyxDQUFDO1FBRXJCLE1BQU0sVUFBVSxHQUE0QjtZQUMxQyxRQUFRO1lBQ1IsSUFBSTtZQUNKLE1BQU07WUFDTixNQUFNLEVBQUUsVUFBVTtZQUNsQixVQUFVO1lBQ1YsV0FBVyxFQUFFLFVBQVUsQ0FBQyxXQUFXO1lBQ25DLGVBQWUsRUFBRSxVQUFVLENBQUMsZUFBZTtZQUMzQyxTQUFTLEVBQUUsVUFBVSxDQUFDLFdBQVc7WUFDakMsWUFBWSxFQUFFLFVBQVUsQ0FBQyxlQUFlO1lBQ3hDLE1BQU07WUFDTixHQUFHLEVBQUUsTUFBTTtZQUNYLEdBQUcsRUFBRTtnQkFDSCxXQUFXLEVBQUUsUUFBUTtnQkFDckIsT0FBTyxFQUFFLE1BQU0sQ0FBQyxJQUFJLENBQUM7Z0JBQ3JCLFNBQVMsRUFBRSxNQUFNO2dCQUNqQixTQUFTLEVBQUUsVUFBVTtnQkFDckIsZ0JBQWdCLEVBQUUsVUFBVSxDQUFDLFdBQVc7Z0JBQ3hDLG9CQUFvQixFQUFFLFVBQVUsQ0FBQyxlQUFlO2dCQUNoRCxVQUFVLEVBQUUsTUFBTSxDQUFDLE1BQU0sQ0FBQztnQkFDMUIsaUJBQWlCLEVBQUUsVUFBVSxDQUFDLFdBQVc7Z0JBQ3pDLHFCQUFxQixFQUFFLFVBQVUsQ0FBQyxlQUFlO2dCQUNqRCxVQUFVLEVBQUUsTUFBTTthQUNuQjtTQUNGLENBQUM7UUFDRixJQUFJLFVBQVUsQ0FBQyxhQUFhLEVBQUUsQ0FBQztZQUM3QixVQUFVLENBQUMsYUFBYSxHQUFHLFVBQVUsQ0FBQyxhQUFhLENBQUM7UUFDdEQsQ0FBQztRQUNELElBQUksVUFBVSxDQUFDLFNBQVMsRUFBRSxDQUFDO1lBQ3pCLFVBQVUsQ0FBQyxTQUFTLEdBQUcsVUFBVSxDQUFDLFNBQVMsQ0FBQztRQUM5QyxDQUFDO1FBQ0QsT0FBTyxVQUFVLENBQUM7SUFDcEIsQ0FBQztJQUVPLHVCQUF1QjtRQUM3QixJQUFJLENBQUMsSUFBSSxDQUFDLE1BQU0sQ0FBQyxJQUFJLENBQUMsT0FBTyxFQUFFLENBQUM7WUFDOUIsTUFBTSxJQUFJLEtBQUssQ0FBQywrQ0FBK0MsQ0FBQyxDQUFDO1FBQ25FLENBQUM7SUFDSCxDQUFDO0lBRU0sS0FBSyxDQUFDLFlBQVksQ0FBQyxhQUFxQjtRQUM3QyxJQUFJLENBQUM7WUFDSCxNQUFNLElBQUksQ0FBQyxNQUFNLENBQUMsV0FBVyxDQUFDLGNBQWMsRUFBRSxFQUFFLElBQUksRUFBRSxhQUFhLEVBQUUsQ0FBQyxDQUFDO1lBQ3ZFLE9BQU8sRUFBRSxJQUFJLEVBQUUsYUFBYSxFQUFFLENBQUM7UUFDakMsQ0FBQztRQUFDLE9BQU8sS0FBSyxFQUFFLENBQUM7WUFDZixNQUFNLDJCQUEyQixDQUFDLEtBQUssQ0FBQyxDQUFDO1FBQzNDLENBQUM7SUFDSCxDQUFDO0lBRU0sS0FBSyxDQUFDLGtCQUFrQixDQUM3QixTQUE2QjtRQUU3QixJQUFJLENBQUMsdUJBQXVCLEVBQUUsQ0FBQztRQUMvQixJQUFJLENBQUM7WUFDSCxNQUFNLFVBQVUsR0FBRyxNQUFNLElBQUksQ0FBQyxNQUFNLENBQUMsV0FBVyxDQUFDLG9CQUFvQixFQUFFO2dCQUNyRSxVQUFVLEVBQUUsU0FBUyxDQUFDLFVBQVU7Z0JBQ2hDLFdBQVcsRUFBRSxTQUFTLENBQUMsV0FBVyxJQUFJLGlCQUFpQixFQUFFO2dCQUN6RCxlQUFlLEVBQUUsU0FBUyxDQUFDLGVBQWUsSUFBSSxxQkFBcUIsRUFBRTtnQkFDckUsTUFBTSxFQUFFLFNBQVMsQ0FBQyxNQUFNLElBQUksSUFBSSxDQUFDLE1BQU0sQ0FBQyxNQUFNLENBQUMsTUFBTTtnQkFDckQsR0FBRyxDQUFDLFNBQVMsQ0FBQyxTQUFTLENBQUMsQ0FBQyxDQUFDLEVBQUUsU0FBUyxFQUFFLFNBQVMsQ0FBQyxTQUFTLEVBQUUsQ0FBQyxDQUFDLENBQUMsRUFBRSxDQUFDO2FBQ25FLENBQUMsQ0FBQztZQUNILE9BQU8sSUFBSSxDQUFDLDJCQUEyQixDQUFDLFVBQVUsRUFBRSxTQUFTLENBQUMsVUFBVSxDQUFDLENBQUM7UUFDNUUsQ0FBQztRQUFDLE9BQU8sS0FBSyxFQUFFLENBQUM7WUFDZixNQUFNLDJCQUEyQixDQUFDLEtBQUssQ0FBQyxDQUFDO1FBQzNDLENBQUM7SUFDSCxDQUFDO0lBRU0sS0FBSyxDQUFDLGtCQUFrQixDQUM3QixTQUFtQztRQUVuQyxJQUFJLENBQUMsdUJBQXVCLEVBQUUsQ0FBQztRQUMvQixJQUFJLENBQUM7WUFDSCxNQUFNLFVBQVUsR0FBRyxNQUFNLElBQUksQ0FBQyxNQUFNLENBQUMsV0FBVyxDQUFDLG9CQUFvQixFQUFFO2dCQUNyRSxHQUFHLFNBQVM7Z0JBQ1osTUFBTSxFQUFFLFNBQVMsQ0FBQyxNQUFNLElBQUksSUFBSSxDQUFDLE1BQU0sQ0FBQyxNQUFNLENBQUMsTUFBTTthQUN0RCxDQUFDLENBQUM7WUFDSCxPQUFPLElBQUksQ0FBQywyQkFBMkIsQ0FBQyxVQUFVLEVBQUUsU0FBUyxDQUFDLFVBQVUsQ0FBQyxDQUFDO1FBQzVFLENBQUM7UUFBQyxPQUFPLEtBQUssRUFBRSxDQUFDO1lBQ2YsTUFBTSwyQkFBMkIsQ0FBQyxLQUFLLENBQUMsQ0FBQztRQUMzQyxDQUFDO0lBQ0gsQ0FBQztJQUVNLEtBQUssQ0FBQyxrQkFBa0IsQ0FBQyxTQUFtQztRQUNqRSxJQUFJLENBQUMsdUJBQXVCLEVBQUUsQ0FBQztRQUMvQixJQUFJLENBQUM7WUFDSCxNQUFNLElBQUksQ0FBQyxNQUFNLENBQUMsV0FBVyxDQUFDLG9CQUFvQixFQUFFLFNBQVMsQ0FBQyxDQUFDO1FBQ2pFLENBQUM7UUFBQyxPQUFPLEtBQUssRUFBRSxDQUFDO1lBQ2YsTUFBTSwyQkFBMkIsQ0FBQyxLQUFLLENBQUMsQ0FBQztRQUMzQyxDQUFDO0lBQ0gsQ0FBQztJQUVNLEtBQUssQ0FBQyw2QkFBNkIsQ0FDeEMsU0FBNkI7UUFFN0IsSUFBSSxDQUFDLHVCQUF1QixFQUFFLENBQUM7UUFDL0IsSUFBSSxDQUFDO1lBQ0gsTUFBTSxVQUFVLEdBQUcsTUFBTSxJQUFJLENBQUMsTUFBTSxDQUFDLFdBQVcsQ0FBQywrQkFBK0IsRUFBRTtnQkFDaEYsVUFBVSxFQUFFLFNBQVMsQ0FBQyxVQUFVO2dCQUNoQyxXQUFXLEVBQUUsU0FBUyxDQUFDLFdBQVcsSUFBSSxpQkFBaUIsRUFBRTtnQkFDekQsZUFBZSxFQUFFLFNBQVMsQ0FBQyxlQUFlLElBQUkscUJBQXFCLEVBQUU7Z0JBQ3JFLE1BQU0sRUFBRSxTQUFTLENBQUMsTUFBTSxJQUFJLElBQUksQ0FBQyxNQUFNLENBQUMsTUFBTSxDQUFDLE1BQU07YUFDdEQsQ0FBQyxDQUFDO1lBQ0gsT0FBTyxJQUFJLENBQUMsMkJBQTJCLENBQUMsVUFBVSxFQUFFLFNBQVMsQ0FBQyxVQUFVLENBQUMsQ0FBQztRQUM1RSxDQUFDO1FBQUMsT0FBTyxLQUFLLEVBQUUsQ0FBQztZQUNmLE1BQU0sMkJBQTJCLENBQUMsS0FBSyxDQUFDLENBQUM7UUFDM0MsQ0FBQztJQUNILENBQUM7SUFFTSxLQUFLLENBQUMsaUJBQWlCO1FBQzVCLElBQUksQ0FBQyx1QkFBdUIsRUFBRSxDQUFDO1FBQy9CLE9BQU8sSUFBSSxDQUFDLE1BQU0sQ0FBQyxXQUFXLENBQUMsbUJBQW1CLEVBQUUsRUFBRSxDQUFDLENBQUM7SUFDMUQsQ0FBQztJQUVNLEtBQUssQ0FBQyx5QkFBeUIsQ0FBQyxVQUV0QztRQUNDLElBQUksQ0FBQyx1QkFBdUIsRUFBRSxDQUFDO1FBQy9CLE1BQU0sVUFBVSxHQUFHLE1BQU0sSUFBSSxDQUFDLE1BQU0sQ0FBQyxXQUFXLENBQUMsMkJBQTJCLEVBQUU7WUFDNUUsVUFBVSxFQUFFLFVBQVUsQ0FBQyxVQUFVO1NBQ2xDLENBQUMsQ0FBQztRQUNILE9BQU8sSUFBSSxDQUFDLDJCQUEyQixDQUFDLFVBQVUsRUFBRSxVQUFVLENBQUMsVUFBVSxDQUFDLENBQUM7SUFDN0UsQ0FBQztJQUVNLEtBQUssQ0FBQyw0QkFBNEI7UUFDdkMsT0FBTyxJQUFJLENBQUMsTUFBTSxDQUFDLFdBQVcsQ0FBQyw4QkFBOEIsRUFBRSxFQUFFLENBQUMsQ0FBQztJQUNyRSxDQUFDO0lBRU0sS0FBSyxDQUFDLHlCQUF5QixDQUFDLFVBRXRDO1FBQ0MsT0FBTyxJQUFJLENBQUMsTUFBTSxDQUFDLFdBQVcsQ0FBQywyQkFBMkIsRUFBRSxVQUFVLENBQUMsQ0FBQztJQUMxRSxDQUFDO0lBRU0sS0FBSyxDQUFDLDRCQUE0QjtRQUN2QyxPQUFPLElBQUksQ0FBQyxNQUFNLENBQUMsV0FBVyxDQUFDLDhCQUE4QixFQUFFLEVBQUUsQ0FBQyxDQUFDO0lBQ3JFLENBQUM7SUFFTSxLQUFLLENBQUMscUJBQXFCLENBQ2hDLFVBQThDO1FBRTlDLE9BQU8sSUFBSSxDQUFDLE1BQU0sQ0FBQyxXQUFXLENBQUMsdUJBQXVCLEVBQUUsVUFBVSxDQUFDLENBQUM7SUFDdEUsQ0FBQztJQUVNLEtBQUssQ0FBQywwQkFBMEIsQ0FDckMsVUFBOEM7UUFFOUMsT0FBTyxJQUFJLENBQUMsTUFBTSxDQUFDLFdBQVcsQ0FBQyw0QkFBNEIsRUFBRSxVQUFVLENBQUMsQ0FBQztJQUMzRSxDQUFDO0lBRU0sS0FBSyxDQUFDLHlCQUF5QixDQUNwQyxVQUF3RDtRQUV4RCxPQUFPLElBQUksQ0FBQyxNQUFNLENBQUMsV0FBVyxDQUFDLDJCQUEyQixFQUFFLFVBQVUsQ0FBQyxDQUFDO0lBQzFFLENBQUM7SUFFTSxLQUFLLENBQUMsOEJBQThCLENBQ3pDLFVBQTZEO1FBRTdELE9BQU8sSUFBSSxDQUFDLE1BQU0sQ0FBQyxXQUFXLENBQUMsZ0NBQWdDLEVBQUUsVUFBVSxDQUFDLENBQUM7SUFDL0UsQ0FBQztJQUVNLEtBQUssQ0FBQyxzQkFBc0IsQ0FDakMsVUFBcUQ7UUFFckQsSUFBSSxDQUFDO1lBQ0gsT0FBTyxNQUFNLElBQUksQ0FBQyxNQUFNLENBQUMsV0FBVyxDQUFDLHdCQUF3QixFQUFFLFVBQVUsQ0FBQyxDQUFDO1FBQzdFLENBQUM7UUFBQyxPQUFPLEtBQUssRUFBRSxDQUFDO1lBQ2YsTUFBTSwyQkFBMkIsQ0FBQyxLQUFLLENBQUMsQ0FBQztRQUMzQyxDQUFDO0lBQ0gsQ0FBQztJQUVNLEtBQUssQ0FBQyxzQkFBc0IsQ0FBQyxVQUVuQztRQUNDLE9BQU8sSUFBSSxDQUFDLE1BQU0sQ0FBQyxXQUFXLENBQUMsd0JBQXdCLEVBQUUsVUFBVSxDQUFDLENBQUM7SUFDdkUsQ0FBQztJQUVNLEtBQUssQ0FBQyxZQUFZLENBQUMsVUFBOEI7UUFDdEQsT0FBTyxJQUFJLENBQUMsTUFBTSxDQUFDLFdBQVcsQ0FBQyxjQUFjLEVBQUUsRUFBRSxVQUFVLEVBQUUsVUFBVSxDQUFDLFVBQVUsRUFBRSxDQUFDLENBQUM7SUFDeEYsQ0FBQztJQUVNLEtBQUssQ0FBQyxZQUFZLENBQ3ZCLFVBQThCO1FBRTlCLElBQUksQ0FBQztZQUNILE9BQU8sTUFBTSxJQUFJLENBQUMsTUFBTSxDQUFDLFdBQVcsQ0FBQyxjQUFjLEVBQUUsVUFBVSxDQUFDLENBQUM7UUFDbkUsQ0FBQztRQUFDLE9BQU8sS0FBSyxFQUFFLENBQUM7WUFDZixNQUFNLDJCQUEyQixDQUFDLEtBQUssQ0FBQyxDQUFDO1FBQzNDLENBQUM7SUFDSCxDQUFDO0lBRU0sS0FBSyxDQUFDLFlBQVksQ0FDdkIsVUFBOEI7UUFFOUIsSUFBSSxDQUFDO1lBQ0gsT0FBTyxNQUFNLElBQUksQ0FBQyxNQUFNLENBQUMsV0FBVyxDQUFDLGNBQWMsRUFBRSxVQUFVLENBQUMsQ0FBQztRQUNuRSxDQUFDO1FBQUMsT0FBTyxLQUFLLEVBQUUsQ0FBQztZQUNmLE1BQU0sMkJBQTJCLENBQUMsS0FBSyxDQUFDLENBQUM7UUFDM0MsQ0FBQztJQUNILENBQUM7SUFFTSxLQUFLLENBQUMseUJBQXlCLENBQ3BDLFVBQXdEO1FBRXhELElBQUksQ0FBQztZQUNILE9BQU8sTUFBTSxJQUFJLENBQUMsTUFBTSxDQUFDLFdBQVcsQ0FBQywyQkFBMkIsRUFBRSxVQUFVLENBQUMsQ0FBQztRQUNoRixDQUFDO1FBQUMsT0FBTyxLQUFLLEVBQUUsQ0FBQztZQUNmLE1BQU0sMkJBQTJCLENBQUMsS0FBSyxDQUFDLENBQUM7UUFDM0MsQ0FBQztJQUNILENBQUM7SUFFTSxLQUFLLENBQUMsNEJBQTRCO1FBQ3ZDLE9BQU8sSUFBSSxDQUFDLE1BQU0sQ0FBQyxXQUFXLENBQUMsOEJBQThCLEVBQUUsRUFBRSxDQUFDLENBQUM7SUFDckUsQ0FBQztJQUVNLEtBQUssQ0FBQyxlQUFlO1FBQzFCLE9BQU8sSUFBSSxDQUFDLE1BQU0sQ0FBQyxXQUFXLENBQUMsaUJBQWlCLEVBQUUsRUFBRSxDQUFDLENBQUM7SUFDeEQsQ0FBQztJQUVNLEtBQUssQ0FBQyx3QkFBd0I7UUFDbkMsT0FBTyxJQUFJLENBQUMsTUFBTSxDQUFDLFdBQVcsQ0FBQywwQkFBMEIsRUFBRSxFQUFFLENBQUMsQ0FBQztJQUNqRSxDQUFDO0lBRU0sS0FBSyxDQUFDLG1CQUFtQjtRQUM5QixPQUFPLElBQUksQ0FBQyxNQUFNLENBQUMsV0FBVyxDQUFDLHFCQUFxQixFQUFFLEVBQUUsQ0FBQyxDQUFDO0lBQzVELENBQUM7SUFFTSxLQUFLLENBQUMsZUFBZTtRQUMxQixPQUFPLElBQUksQ0FBQyxNQUFNLENBQUMsV0FBVyxDQUFDLGlCQUFpQixFQUFFLEVBQUUsQ0FBQyxDQUFDO0lBQ3hELENBQUM7SUFFRDs7OztPQUlHO0lBQ0ksS0FBSyxDQUFDLGtCQUFrQixDQUFDLFdBQWlDO1FBQy9ELE1BQU0sSUFBSSxDQUFDLE1BQU0sQ0FBQyxXQUFXLENBQUMsb0JBQW9CLEVBQUUsRUFBRSxXQUFXLEVBQUUsQ0FBQyxDQUFDO1FBQ3JFLElBQUksQ0FBQyxNQUFNLENBQUMsSUFBSSxDQUFDLFdBQVcsR0FBRyxXQUFXLENBQUMsR0FBRyxDQUFDLENBQUMsVUFBVSxFQUFFLEVBQUUsQ0FBQyxDQUFDLEVBQUUsR0FBRyxVQUFVLEVBQUUsQ0FBQyxDQUFDLENBQUM7SUFDdEYsQ0FBQztJQUVNLEtBQUssQ0FBQyxnQkFBZ0I7UUFDM0IsT0FBTyxJQUFJLENBQUMsTUFBTSxDQUFDLFdBQVcsQ0FBQyxrQkFBa0IsRUFBRSxFQUFFLENBQUMsQ0FBQztJQUN6RCxDQUFDO0lBRU0sS0FBSyxDQUFDLFNBQVM7UUFDcEIsSUFBSSxDQUFDLElBQUksQ0FBQyxPQUFPLEVBQUUsQ0FBQztZQUNsQixPQUFPO2dCQUNMLEVBQUUsRUFBRSxLQUFLO2dCQUNULE9BQU8sRUFBRSxLQUFLO2dCQUNkLGdCQUFnQixFQUFFLElBQUksQ0FBQyxNQUFNLENBQUMsT0FBTyxDQUFDLFNBQVMsSUFBSSxLQUFLLENBQUMsVUFBVTtnQkFDbkUsZ0JBQWdCLEVBQUUsRUFBRTtnQkFDcEIsSUFBSSxFQUFFO29CQUNKLE9BQU8sRUFBRSxJQUFJLENBQUMsTUFBTSxDQUFDLElBQUksQ0FBQyxPQUFPO29CQUNqQyxlQUFlLEVBQUUsSUFBSSxDQUFDLE1BQU0sQ0FBQyxJQUFJLENBQUMsV0FBVyxDQUFDLE1BQU07b0JBQ3BELHFCQUFxQixFQUFFLENBQUM7aUJBQ3pCO2dCQUNELFdBQVcsRUFBRSxDQUFDO2dCQUNkLFdBQVcsRUFBRSxDQUFDO2dCQUNkLFVBQVUsRUFBRSxDQUFDO2dCQUNiLE9BQU8sRUFBRSxFQUFFLE9BQU8sRUFBRSxLQUFLLEVBQUU7Z0JBQzNCLHNCQUFzQixFQUFFLENBQUM7Z0JBQ3pCLHdCQUF3QixFQUFFLEtBQUs7Z0JBQy9CLDRCQUE0QixFQUFFLElBQUk7Z0JBQ2xDLDhCQUE4QixFQUFFLENBQUM7Z0JBQ2pDLGdDQUFnQyxFQUFFLEtBQUs7Z0JBQ3ZDLDRDQUE0QyxFQUFFLEtBQUs7YUFDcEQsQ0FBQztRQUNKLENBQUM7UUFFRCxNQUFNLENBQUMsS0FBSyxFQUFFLFdBQVcsRUFBRSxPQUFPLEVBQUUsT0FBTyxFQUFFLGVBQWUsQ0FBQyxHQUFHLE1BQU0sT0FBTyxDQUFDLEdBQUcsQ0FBQztZQUNoRixJQUFJLENBQUMsZUFBZSxFQUFFO1lBQ3RCLElBQUksQ0FBQyxlQUFlLEVBQUU7WUFDdEIsSUFBSSxDQUFDLE1BQU0sQ0FBQyxJQUFJLENBQUMsT0FBTyxDQUFDLENBQUMsQ0FBQyxJQUFJLENBQUMsaUJBQWlCLEVBQUUsQ0FBQyxDQUFDLENBQUMsT0FBTyxDQUFDLE9BQU8sQ0FBQyxFQUFFLENBQUM7WUFDekUsSUFBSSxDQUFDLGdCQUFnQixFQUFFO1lBQ3ZCLElBQUksQ0FBQyw0QkFBNEIsRUFBRTtTQUNwQyxDQUFDLENBQUM7UUFDSCxPQUFPO1lBQ0wsRUFBRSxFQUFFLElBQUk7WUFDUixPQUFPLEVBQUUsSUFBSTtZQUNiLGdCQUFnQixFQUFFLEtBQUssQ0FBQyxnQkFBZ0I7WUFDeEMsZ0JBQWdCLEVBQUUsS0FBSyxDQUFDLGdCQUFnQixJQUFJLEVBQUU7WUFDOUMsSUFBSSxFQUFFO2dCQUNKLE9BQU8sRUFBRSxJQUFJLENBQUMsTUFBTSxDQUFDLElBQUksQ0FBQyxPQUFPO2dCQUNqQyxlQUFlLEVBQUUsV0FBVyxDQUFDLE1BQU07Z0JBQ25DLHFCQUFxQixFQUFFLE9BQU8sQ0FBQyxNQUFNO2FBQ3RDO1lBQ0QsV0FBVyxFQUFFLEtBQUssQ0FBQyxXQUFXO1lBQzlCLFdBQVcsRUFBRSxLQUFLLENBQUMsZ0JBQWdCO1lBQ25DLFVBQVUsRUFBRSxLQUFLLENBQUMsaUJBQWlCO1lBQ25DLE9BQU87WUFDUCxzQkFBc0IsRUFBRSxlQUFlLENBQUMsT0FBTztZQUMvQyx3QkFBd0IsRUFBRSxlQUFlLENBQUMsU0FBUztZQUNuRCw0QkFBNEIsRUFBRSxlQUFlLENBQUMsYUFBYTtZQUMzRCw4QkFBOEIsRUFBRSxlQUFlLENBQUMsc0JBQXNCO1lBQ3RFLGdDQUFnQyxFQUFFLGVBQWUsQ0FBQyx3QkFBd0I7WUFDMUUsNENBQTRDLEVBQzFDLGVBQWUsQ0FBQyxvQ0FBb0M7WUFDdEQscUJBQXFCLEVBQUUsZUFBZSxDQUFDLE1BQU07U0FDOUMsQ0FBQztJQUNKLENBQUM7SUFFTSxLQUFLLENBQUMsVUFBVTtRQUNyQixNQUFNLE1BQU0sR0FBRyxNQUFNLElBQUksQ0FBQyxTQUFTLEVBQUUsQ0FBQztRQUN0QyxNQUFNLGNBQWMsR0FBRyxNQUFNLENBQUMsT0FBTyxDQUFDLE9BQU8sQ0FBQztRQUM5QyxPQUFPO1lBQ0wsV0FBVyxFQUFFLE1BQU0sQ0FBQyxXQUFXO1lBQy9CLFdBQVcsRUFBRSxNQUFNLENBQUMsV0FBVztZQUMvQixVQUFVLEVBQUUsTUFBTSxDQUFDLFVBQVU7WUFDN0IsbUJBQW1CLEVBQUUsTUFBTSxDQUFDLElBQUksQ0FBQyxlQUFlO1lBQ2hELHFCQUFxQixFQUFFLE1BQU0sQ0FBQyxJQUFJLENBQUMscUJBQXFCO1lBQ3hELGNBQWM7WUFDZCxjQUFjLEVBQUU7Z0JBQ2QsMERBQTBEO2dCQUMxRCwwQ0FBMEM7Z0JBQzFDLCtCQUErQixNQUFNLENBQUMsV0FBVyxFQUFFO2dCQUNuRCwwREFBMEQ7Z0JBQzFELDBDQUEwQztnQkFDMUMsK0JBQStCLE1BQU0sQ0FBQyxXQUFXLEVBQUU7Z0JBQ25ELGlFQUFpRTtnQkFDakUsZ0RBQWdEO2dCQUNoRCxxQ0FBcUMsTUFBTSxDQUFDLFVBQVUsRUFBRTtnQkFDeEQsc0ZBQXNGO2dCQUN0RixxREFBcUQ7Z0JBQ3JELDBDQUEwQyxNQUFNLENBQUMsSUFBSSxDQUFDLHFCQUFxQixFQUFFO2dCQUM3RSw0REFBNEQ7Z0JBQzVELDRDQUE0QztnQkFDNUMsaUNBQWlDLGNBQWMsQ0FBQyxDQUFDLENBQUMsQ0FBQyxDQUFDLENBQUMsQ0FBQyxDQUFDLEVBQUU7YUFDMUQsQ0FBQyxJQUFJLENBQUMsSUFBSSxDQUFDO1NBQ2IsQ0FBQztJQUNKLENBQUM7SUFFRDs7OztPQUlHO0lBQ0ksSUFBSTtRQUNULElBQUksSUFBSSxDQUFDLFdBQVcsRUFBRSxDQUFDO1lBQ3JCLE9BQU8sSUFBSSxDQUFDLFdBQVcsQ0FBQztRQUMxQixDQUFDO1FBQ0QsSUFBSSxDQUFDLGFBQWEsR0FBRyxJQUFJLENBQUM7UUFDMUIsTUFBTSxJQUFJLEdBQUcsSUFBSSxDQUFDLFlBQVksRUFBRSxDQUFDO1FBQ2pDLElBQUksQ0FBQyxXQUFXLEdBQUcsSUFBSSxDQUFDO1FBQ3hCLElBQUksQ0FBQyxJQUFJLENBQUMsU0FBUyxFQUFFLEdBQUcsRUFBRTtZQUN4QixJQUFJLENBQUMsSUFBSSxDQUFDLGFBQWEsSUFBSSxJQUFJLENBQUMsV0FBVyxLQUFLLElBQUksRUFBRSxDQUFDO2dCQUNyRCxJQUFJLENBQUMsV0FBVyxHQUFHLFNBQVMsQ0FBQztZQUMvQixDQUFDO1FBQ0gsQ0FBQyxDQUFDLENBQUM7UUFDSCxPQUFPLElBQUksQ0FBQztJQUNkLENBQUM7SUFFTyxLQUFLLENBQUMsWUFBWTtRQUN4QixJQUFJLElBQUksQ0FBQyxZQUFZLEVBQUUsQ0FBQztZQUN0QiwyRUFBMkU7WUFDM0UsTUFBTSxPQUFPLENBQUMsVUFBVSxDQUFDLENBQUMsSUFBSSxDQUFDLFlBQVksQ0FBQyxDQUFDLENBQUM7UUFDaEQsQ0FBQztRQUNELElBQUksSUFBSSxDQUFDLFdBQVcsRUFBRSxDQUFDO1lBQ3JCLElBQUksSUFBSSxDQUFDLE9BQU8sRUFBRSxDQUFDO2dCQUNqQixJQUFJLENBQUMsbUJBQW1CLEtBQUssTUFBTSxJQUFJLENBQUMsTUFBTSxDQUFDLFdBQVcsQ0FBQyxNQUFNLEVBQUUsRUFBRSxDQUFDLENBQUM7WUFDekUsQ0FBQztZQUNELE1BQU0sSUFBSSxDQUFDLE1BQU0sQ0FBQyxTQUFTLEVBQUUsQ0FBQztZQUM5QixJQUFJLENBQUMsV0FBVyxHQUFHLEtBQUssQ0FBQztZQUN6QixJQUFJLENBQUMsT0FBTyxHQUFHLEtBQUssQ0FBQztRQUN2QixDQUFDO1FBQ0QsSUFBSSxDQUFDLGFBQWEsR0FBRyxJQUFJLENBQUM7UUFDMUIsSUFBSSxJQUFJLENBQUMsbUJBQW1CLEVBQUUsYUFBYSxFQUFFLENBQUM7WUFDNUMsTUFBTSxJQUFJLEtBQUssQ0FDYiwyREFBMkQsSUFBSSxDQUFDLG1CQUFtQixDQUFDLGFBQWEsRUFBRSxDQUNwRyxDQUFDO1FBQ0osQ0FBQztJQUNILENBQUM7Q0FDRiJ9
|